Спецификация и описание функций J2534 ›
Спецификация и описание команд ELM327 ›
Интерфейс ELM327 доступен в адаптере Nano ET. Остальные адаптеры ScanDoc используют протокол J2534 PassThru.
Изменения в J2534 DLL, ELM327 и прошивках адаптеров ScanDoc, которые касаются интеграции: новые функции, протоколы и параметры - с примерами использования.
Библиотеки поставляются одним архивом. Платформы: Windows x86/x64/ARM64 (отдельные сборки для Windows 7), macOS (universal), Linux (x64, x86, ARM, ARM64), Android (arm64-v8a, armeabi-v7a, x86, x86_64), iOS (XCFramework). В папке docs/ лежит документация SDK: начало работы, справочник API, конфигурация, обработка ошибок, DoIP, обновление прошивки, формат лога, Android, iOS.
Скачать библиотеки J2534 2.0.0.225
Новое
ConfigRead, а удаляет пару ConfigWrite. Новых вызовов в библиотеке для этого не появилось.
Пример
char json[2048];
if (ConfigRead(dev, json, sizeof(json)) == STATUS_NOERROR) {
/* в ответе, среди прочих настроек:
"ble_bonds":[{"name":"WS-07","mac":"A4:C1:38:11:22:33"}] */
}
ConfigWrite(dev, "{\"ble_bond_del\":\"A4:C1:38:11:22:33\"}");
/* применяется сразу, ConfigReboot не требуется */
Исправлено
reciv ack end single msg timeout SG, затем reciv ack counter error SG.TX_FAILED, и вернуть канал в работу можно было только переподключением.Новое
ConfigRead, ConfigWrite, ConfigReboot и ConfigReset (на Windows ординалы @52-@55) библиотека экспортировала и раньше, но в документации их не было, и воспользоваться ими со стороны было нельзя. Теперь в docs/API_REFERENCE.md описаны прототипы и поведение, а в docs/CONFIGURATION.md приведена таблица ключей с диапазонами, которые проверяет прошивка. Эти команды прибор обрабатывает до маршрутизации J2534, поэтому они работают на любом транспорте: LAN/WLAN, BLE и USB.
Пример
char json[2048];
ConfigRead(dev, json, sizeof(json)); /* все настройки одним объектом JSON */
ConfigWrite(dev, "{\"ble_name\":\"WS-07\"}"); /* изменяются только переданные ключи */
ConfigReboot(dev); /* настройки вступают в силу после перезагрузки;
device_id недействителен, переоткрыть через ~10 с */
ptConfigRead, ptConfigWrite, ptConfigReboot и ptConfigReset.
Пример
external fun ptConfigRead(devId: Int): String? // null при ошибке
external fun ptConfigWrite(devId: Int, json: String): Int
external fun ptConfigReboot(devId: Int): Int
external fun ptConfigReset(devId: Int): Int
val json = j2534.ptConfigRead(devId) ?: return // причина в ptGetLastError()
j2534.ptConfigWrite(devId, """{"ble_name":"WS-07"}""")
j2534.ptConfigReboot(devId)
PassThruOpen версия намеренно не проверяется, чтобы не тратить на это обмен с прибором при каждом открытии: установленную сборку возвращает PassThruReadVersion, а расхождение видно в .qlog по строке firmware build N is older than build 85 required by DLL ….Исправлено
ERR_FAILED, а PassThruGetLastError отдаёт текст BLE pairing rejected (wrong PIN or not paired). Код взят именно ERR_FAILED, потому что при ERR_DEVICE_NOT_CONNECTED приложения обычно текст ошибки не запрашивают. Работает на всех транспортах: Android, macOS, Windows и Linux. Остальные сбои BLE по-прежнему дают ERR_DEVICE_NOT_CONNECTED, но теперь с кодом транспорта: раньше этот код печатался как номер ошибки POSIX, и отказ записи выходил наружу строкой 0x7 - Argument list too long.ptClose по BLE. При закрытии BLE-соединения библиотека закрывала файловый дескриптор 0, хотя сокета в BLE-режиме нет. Обычно так молча закрывался stdin, но если fd 0 к этому моменту занимал объект JVM, fdsan завершал процесс: attempted to close file descriptor 0 … owned by native object. Поэтому падение и было нерегулярным.ptClose по BLE занимал лишнюю секунду. Ответ прибора на закрытие библиотека выбрасывала, выжидала таймаут приёма 1 с и писала в лог 0x6E - Connection timed out, будто прибор не ответил. Теперь закрытие завершается по ответу прибора. На macOS, Windows и Linux этой ошибки не было.ptOpen по BLE, когда прибор не ответил на открытие. Приложение завершалось с SIGSEGV. При других отказах открытия (отвергнутый пайринг, таймаут подключения) библиотека не освобождала глобальную JNI-ссылку на BLEManager, и при повторных попытках ссылки копились.FAST_INIT. Ответ прибора копировался в структуру pt_msg_t приложения без проверки длины: укороченный ответ приводил к чтению неинициализированной памяти, а завышенная длина в ответе приводила к записи за границы структуры, которую передало приложение. На Android тот же вызов отправлял в шину искажённый кадр, а при отказе аварийно завершал приложение. Исправлена и проверка объёма входных данных: значения NumOfBytes порядка 0x20000000 она считала допустимыми.FIVE_BAUD_INIT. Вход ограничен одним байтом адреса, как требует §11.3.3.3; раньше принимался блок произвольной длины, и приложение, передававшее больше, получало успешный код вместо отказа..qlog. Лог открывал только PassThruOpen, а вызов ptOpen идёт в обход него. Поэтому у стороннего приложения под Android папка sdlogs оставалась пустой при любом уровне логирования, записи попадали только в logcat, и получить от клиента лог сессии было нельзя.FIVE_BAUD_INIT и FAST_INIT печаталось только > ok: ни адрес инициализации, ни ответ ЭБУ в лог не попадали, и разобрать по логу неудачную инициализацию было невозможно. Теперь строка содержит и запрос, и ответ: io 1 FIVE_BAUD_INIT 33 > 8F6F 2850ms.Скачать библиотеки J2534 2.0.0.213 - Windows x86/x64/ARM64 (отдельные сборки для Windows 7), macOS (universal), Linux (x64, x86, ARM, ARM64), Android (arm64-v8a, armeabi-v7a, x86, x86_64), iOS (XCFramework); в папке docs/ - документация SDK (начало работы, справочник API, конфигурация, обработка ошибок, DoIP, обновление прошивки, формат лога, Android, iOS). Статические .a поставляются только для iOS и серверной сборки Linux x64, остальные платформы загружают библиотеку динамически.
Исправлено
CAN_PS, ISO15765_PS, J1939_PS и установка выводов на каналах _PS - при подключении этих трёх протоколов прибор отвечал отказом; теперь они работают наравне с TP2_0_PS и ISO9141_PS. Установка выводов приведена к J2534-2 по трём пунктам:
PassThruConnect на выводах по умолчанию, хотя _PS-канал обязан молчать до SET_CONFIG(J1962_PINS). Теперь на шину канал выходит только после установки выводов.SET_CONFIG(J1962_PINS) переключал живой канал на другие контакты посреди сеанса. По стандарту выводы задаются на канал один раз: повторный вызов возвращает ERR_CHANNEL_IN_USE, другие выводы - только после PassThruDisconnect.ERR_PIN_NOT_SUPPORTED.uint32_t ch;
pt_config_t pins = { J1962_PINS, 0x0000060EU }; /* выводы 6 и 14 */
pt_config_list_t cfg = { 1, &pins };
PassThruConnect(dev, ISO15765_PS, 0, 500000, &ch);
/* канал ещё не на шине */
if (PassThruIoctl(ch, SET_CONFIG, &cfg, NULL) != STATUS_NOERROR) {
/* ERR_PIN_NOT_SUPPORTED - такого сочетания нет в разводке прибора */
}
/* только теперь PassThruWriteMsgs / PassThruReadMsgs;
повторный SET_CONFIG(J1962_PINS) - ERR_CHANNEL_IN_USE */
PassThruWriteMsgs возвращал успех, PassThruReadMsgs - пусто, а признак CONNECTION_LOST не выставлялся. Программирование ЭБУ по TP2.0 проверено на стенде.REQUEST_CONNECTION: десяток попыток к молчащему ЭБУ занимал все слоты фильтров и глушил канал; TEARDOWN_CONNECTION на TP1_6_PS отвечал отказом - закрыть соединение со стороны приложения было нельзя; TP2.0 не принимал соединение, установленное со стороны ЭБУ, и не отдавал приложению кадры, пришедшие вне соединения (§19.3.1 J2534-2).PassThruReadMsgs. Соединение здесь точка-точка, адрес тестера зарегистрирован при routing activation, отбирать нечего: канал отдаёт приложению все сообщения, PassThruStartMsgFilter отвечает ERR_NOT_SUPPORTED. Отказ в драйвере CAN перезагружал прибор посреди сеанса DoIP. Сторожевой таймер приёмной задачи поднят с 5 до 30 с - установление соединения DoIP штатно занимает до 20 с.PassThruConnect получал переполнение очереди на первом же сообщении. Базовые CAN и ISO15765 уходили на другой CAN-контроллер и не доходили до шины. Чтение приёмной очереди не восстанавливалось после испорченной записи - признак проверялся неверно во всех протоколах на базе CAN.GET_NDIS_ADAPTER_INFO - под кодом STATUS_NOERROR возвращались неинициализированные данные. Теперь приходят идентификатор адаптера, MAC, адрес IPv4, по которому прибор виден ЭБУ, и состояние линии активации; если Ethernet на приборе нет - ERR_NOT_SUPPORTED.GET_PROTOCOL_INFO - отвечал не за все протоколы и не тем форматом. Теперь работает на любом открытом канале: разрешение метки времени (1 мкс), поддерживаемая чётность, разрядность данных UART. Параметр, на который прибор ответить не может, помечается в поле supported, вызов при этом возвращает STATUS_NOERROR.PassThruDisconnect - обращение к уже завершившейся задаче канала портило память прибора.Новое
libj2534.xcframework содержит срезы для устройства (arm64) и симулятора (arm64/x86_64), минимальная версия iOS 12.0. В каждом срезе - заголовки j2534.h и j2534_ota.h, модульная карта (Swift import J2534, автолинковка CoreBluetooth) и privacy manifest. Прототипы Pass-Thru API объявлены в самом j2534.h - на всех платформах. mbedTLS вкомпилирован, внешних зависимостей нет. Подключение в Xcode: Embed = Do Not Embed (библиотека статическая), -lc++ в Other Linker Flags, ключи NSBluetoothAlwaysUsageDescription (BLE) и NSLocalNetworkUsageDescription (WLAN) в Info.plist - без них iOS завершает приложение при первом обращении к транспорту.
import J2534
var deviceId: UInt32 = 0
// PassThruOpen принимает mutable char* - передаём копию строки
var cstr = Array("ScanDoc;b:N4999".utf8CString) // BLE по префиксу имени
let ret = cstr.withUnsafeMutableBufferPointer { PassThruOpen($0.baseAddress, &deviceId) }
if ret == 0 {
var fw = [CChar](repeating: 0, count: 80)
var dll = [CChar](repeating: 0, count: 80)
var api = [CChar](repeating: 0, count: 80)
PassThruReadVersion(deviceId, &fw, &dll, &api)
PassThruClose(deviceId)
}
/* ID протоколов и IOCTL - макросы с приведением типа, в Swift не импортируются:
задавайте числом, let CAN: UInt32 = 5, let ISO15765: UInt32 = 6 */
ptOtaUpdate(devId, firmwarePath, callback) и ptOtaAbort(devId); ранее OtaUpdate/OtaAbort были доступны только через C API. Прогресс передаётся в onProgress(current, total) - блоки, нумерация с единицы.
// update.bin заранее скопирован в хранилище приложения.
// Вызов блокирующий - выполнять вне main thread.
val res = j2534.ptOtaUpdate(devId, file.absolutePath,
object : OtaProgressListener {
override fun onProgress(current: Int, total: Int) { /* прогресс-бар */ }
})
if (res.status == 0) {
// прошивка записана, прибор перезагружается: devId недействителен,
// подключение восстанавливается новым ptOpen (по BLE - ожидание ~10 с)
}
// res.status < 0 - код ota_result_t (см. j2534_ota.h)
// ptOtaAbort(devId) прерывает обновление без перезагрузки прибора
log_level в j2534.json: -1 выключено, 0 ошибки, 1 +предупреждения, 2 +info, 3 +debug, 4 +verbose; по умолчанию - 3, то есть без настройки лог пишется полностью. На уровне -1 папка sdlogs и файл .qlog не создаются, данные на диск не записываются. На Android уровень задаётся и из кода - ptSetLogLevel(int); заданный так уровень имеет приоритет над файлом конфигурации, поэтому в релизной сборке лог не может быть включён извне.
// Android: вызвать до ptOpen
j2534.ptSetLogLevel(-1) // релизная сборка - лог выключен
j2534.ptSetLogLevel(3) // обращение в поддержку - полный лог
// Остальные платформы: j2534.json в папке конфигурации
// macOS ~/Library/Application Support/Quantex/
// Linux ~/.config/quantex/
// Windows %APPDATA%\Quantex\
{ "log_level": -1, "devices": [] }
Исправлено
PassThruReadVersion и заголовок .qlog возвращали 2.0.0.0: номер сборки не передавался в сборку под Android. Версия определяется тем же правилом, что и на остальных платформах; этот номер указывается при обращении в поддержку.serial_*: USB-транспорт из iOS-сборки исключён, а вызовы к нему оставались. Транспорт заменён заглушкой - подключение строкой c: возвращает штатную ошибку открытия порта..qlog - данные сообщения записывались не далее 125 байт, а запись группы сообщений, переданных одним вызовом PassThruReadMsgs или PassThruWriteMsgs, ограничивалась фиксированным буфером. Сообщение и вся группа записываются полностью - это важно для длинных ответов, например списка DTC..qlog - библиотека и прибор формировали текст лога двумя независимыми реализациями, и расшифровка одних и тех же значений различалась. Флаг установленного соединения TP2.0 и TP1.6 в поле RxStatus библиотека печатала как CONNECTION_ESTABLISHED, прибор - как CONN_OK; имена IOCTL различались в 22 позициях. Теперь имена протоколов, флагов TxFlags и RxStatus, IOCTL и их параметров формируются единой реализацией, поэтому лог приложения и лог прибора по одному и тому же обмену читаются рядом.Скачать библиотеки J2534 2.0.0.200 - Windows x86/x64/ARM64 (отдельные сборки для Windows 7), macOS (universal), Linux (x64, x86, ARM, ARM64), Android (arm64-v8a, armeabi-v7a, x86, x86_64), iOS.
Новое
ISO13400_PS (0x8FFD) и HSFZ_PS (0x8FFC). В стандарт SAE J2534 они не входят - это собственное расширение ScanDoc: диагностика по Ethernet - обнаружение автомобилей в сети (VIN, логический адрес), TCP-подключение, routing activation, обмен UDS. Тестерный адрес по умолчанию 0 - задайте ISO13400_SOURCE_ADDR до активации маршрутизации, иначе шлюз ответит отказом; адрес ЭБУ передаётся в каждом сообщении ([TA][SA][UDS]), ISO13400_TARGET_ADDR через Set/GetConfig не задаётся. Отправка сериализуется по P2: один незакрытый UDS-запрос за раз, NRC 7F xx 78 продлевает ожидание до P2*max (6 с). Новый параметр канала ISO13400_P3_DOIP (0x8108) - пауза между сообщениями.
uint32_t ch, code = 0;
pt_config_t sa = { ISO13400_SOURCE_ADDR, 0x0E80 };
pt_config_list_t cfg = { 1, &sa };
PassThruConnect(dev, ISO13400_PS, 0, 0, &ch);
PassThruIoctl(ch, SET_CONFIG, &cfg, NULL); /* SA - до routing activation */
PassThruIoctl(ch, ISO13400_DISCOVER_VEHICLES, NULL, NULL); /* IP ЭБУ запомнится сам */
PassThruIoctl(ch, ISO13400_CONNECT_TCP, NULL, NULL);
PassThruIoctl(ch, ISO13400_ACTIVATE_ROUTING, NULL, &code); /* 0x10 = success */
/* дальше PassThruWriteMsgs / PassThruReadMsgs - обычный UDS */
0x55 (маркер кадра J2534) - J2534, любой другой (текстовая AT-команда) - ELM327.Исправлено
PassThruStartMsgFilter сравнивал только 4 байта CAN ID, игнорируя заданную длину фильтра. Теперь кадр сравнивается на всю длину, как требует стандарт: PASS/BLOCK по содержимому кадра работают.
/* Подавить ответы TesterPresent (07E8 02 7E ...) в приёмной очереди */
pt_msg_t mask = {0}, pattern = {0};
mask.protocol_id = pattern.protocol_id = CAN;
mask.data_size = pattern.data_size = 6; /* 4 байта CAN ID + 2 байта данных */
memcpy(mask.data, "\xFF\xFF\xFF\xFF\xFF\xFF", 6);
memcpy(pattern.data, "\x00\x00\x07\xE8\x02\x7E", 6);
uint32_t fid;
PassThruStartMsgFilter(ch, BLOCK_FILTER, &mask, &pattern, NULL, &fid);
AT SH при активном CAN-канале ломала Flow Control (FC уходил без паддинга, DLC=3 - шлюз не слал Consecutive Frames) и затирала приёмный фильтр своим TX ID (приём ответов без AT CRA ломался). По даташиту AT SH задаёт только заголовок передачи - приёмный фильтр теперь управляют только AT CRA/CF/CM.PassThruStopPeriodicMsg мог отправить лишний кадр после остановки._PS - выбор пинов через SET_CONFIG(J1962_PINS) не применялся, кадры не уходили на шину.Исправлено