J2534-Spezifikation und Funktionsbeschreibung ›
ELM327-Spezifikation und Befehlsbeschreibung ›
Die ELM327-Schnittstelle steht beim Nano ET zur Verfügung. Die übrigen ScanDoc-Adapter nutzen das J2534-PassThru-Protokoll.
Änderungen an der J2534 DLL, an ELM327 und an der Firmware der ScanDoc-Adapter, die die Integration betreffen: neue Funktionen, Protokolle und Parameter - mit Anwendungsbeispielen.
J2534-Bibliotheken 2.0.0.213 herunterladen - Windows x86/x64/ARM64 (separate Builds für Windows 7), macOS (universal), Linux (x64, x86, ARM, ARM64), Android (arm64-v8a, armeabi-v7a, x86, x86_64), iOS (XCFramework); im Ordner docs/ liegt die SDK-Dokumentation (Erste Schritte, API-Referenz, Konfiguration, Fehlerbehandlung, DoIP, Firmware-Update, Logformat, Android, iOS). Statische .a-Archive werden nur für iOS und den Linux-x64-Server-Build geliefert; alle anderen Plattformen laden die Bibliothek dynamisch.
Behoben
CAN_PS, ISO15765_PS, J1939_PS und Pin-Einstellung auf _PS-Kanälen - beim Verbinden dieser drei Protokolle antwortete das Gerät mit einer Ablehnung; jetzt arbeiten sie wie TP2_0_PS und ISO9141_PS. Die Pin-Einstellung wurde in drei Punkten an J2534-2 angeglichen:
PassThruConnect auf den Standard-Pins hochgefahren, obwohl ein _PS-Kanal bis SET_CONFIG(J1962_PINS) schweigen muss. Jetzt geht der Kanal erst nach der Pin-Einstellung auf den Bus.SET_CONFIG(J1962_PINS) schaltete einen aktiven Kanal mitten in der Sitzung auf andere Kontakte um. Laut Standard werden die Pins pro Kanal einmal gesetzt: ein wiederholter Aufruf liefert ERR_CHANNEL_IN_USE, andere Pins sind erst nach PassThruDisconnect möglich.ERR_PIN_NOT_SUPPORTED abgelehnt.uint32_t ch;
pt_config_t pins = { J1962_PINS, 0x0000060EU }; /* Pins 6 und 14 */
pt_config_list_t cfg = { 1, &pins };
PassThruConnect(dev, ISO15765_PS, 0, 500000, &ch);
/* der Kanal ist noch nicht auf dem Bus */
if (PassThruIoctl(ch, SET_CONFIG, &cfg, NULL) != STATUS_NOERROR) {
/* ERR_PIN_NOT_SUPPORTED - diese Kombination gibt es in der Verdrahtung nicht */
}
/* erst jetzt PassThruWriteMsgs / PassThruReadMsgs;
wiederholtes SET_CONFIG(J1962_PINS) - ERR_CHANNEL_IN_USE */
PassThruWriteMsgs Erfolg meldete, PassThruReadMsgs leer blieb und das Flag CONNECTION_LOST nie gesetzt wurde. Die Steuergeräte-Programmierung über TP2.0 wurde auf dem Prüfstand bestätigt.REQUEST_CONNECTION nicht entfernt: ein Dutzend Versuche gegen ein stummes Steuergerät belegte alle Filterplätze und legte den Kanal lahm; TEARDOWN_CONNECTION auf TP1_6_PS wurde abgelehnt - die Verbindung ließ sich von der Anwendung aus nicht schließen; TP2.0 nahm keine vom Steuergerät aufgebaute Verbindung an und reichte Rahmen außerhalb einer Verbindung nicht an die Anwendung weiter (§19.3.1 J2534-2).PassThruReadMsgs nie. Die Verbindung ist hier Punkt-zu-Punkt, die Testeradresse ist bei der Routing Activation registriert, es gibt nichts auszusortieren: der Kanal reicht jede Nachricht an die Anwendung weiter, PassThruStartMsgFilter antwortet mit ERR_NOT_SUPPORTED. Ein Fehler im CAN-Treiber startete das Gerät mitten in einer DoIP-Sitzung neu. Der Watchdog der Empfangsaufgabe wurde von 5 auf 30 s erhöht - der Aufbau einer DoIP-Verbindung dauert regulär bis zu 20 s.PassThruConnect bekam schon bei der ersten Nachricht einen Warteschlangenüberlauf. Die Basisprotokolle CAN und ISO15765 gingen auf den anderen CAN-Controller und erreichten den Bus nicht. Das Lesen der Empfangswarteschlange erholte sich nach einem beschädigten Eintrag nicht - das Kennzeichen wurde in allen CAN-basierten Protokollen falsch geprüft.GET_NDIS_ADAPTER_INFO - unter STATUS_NOERROR wurden nicht initialisierte Daten zurückgegeben. Jetzt kommen Adapterkennung, MAC, die IPv4-Adresse, unter der das Steuergerät das Gerät sieht, und der Zustand der Aktivierungsleitung; auf einem Gerät ohne Ethernet - ERR_NOT_SUPPORTED.GET_PROTOCOL_INFO - antwortete nur für einen Teil der Protokolle und im falschen Format. Jetzt funktioniert es auf jedem geöffneten Kanal: Auflösung des Zeitstempels (1 µs), unterstützte Parität, UART-Datenbits. Ein Parameter, den das Gerät nicht beantworten kann, wird im Feld supported markiert, der Aufruf selbst liefert STATUS_NOERROR.PassThruDisconnect - der Zugriff auf eine bereits beendete Kanalaufgabe beschädigte den Speicher des Geräts.Neu
libj2534.xcframework enthält Slices für Gerät (arm64) und Simulator (arm64/x86_64), Mindestversion iOS 12.0. Jeder Slice bringt die Header j2534.h und j2534_ota.h, eine Module Map (Swift import J2534, CoreBluetooth wird automatisch gelinkt) und ein Privacy Manifest mit. Die Prototypen der Pass-Thru-API sind in j2534.h selbst deklariert - auf allen Plattformen. mbedTLS ist einkompiliert, externe Abhängigkeiten gibt es keine. Einrichtung in Xcode: Embed = Do Not Embed (die Bibliothek ist statisch), -lc++ in Other Linker Flags, die Schlüssel NSBluetoothAlwaysUsageDescription (BLE) und NSLocalNetworkUsageDescription (WLAN) in Info.plist - ohne sie beendet iOS die App beim ersten Zugriff auf den Transport.
import J2534
var deviceId: UInt32 = 0
// PassThruOpen erwartet ein veränderbares char* - eine Kopie des Strings übergeben
var cstr = Array("ScanDoc;b:N4999".utf8CString) // BLE nach Namenspräfix
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)
}
/* Protokoll- und IOCTL-IDs sind Makros mit Typumwandlung und werden in Swift nicht importiert:
als Zahl angeben, let CAN: UInt32 = 5, let ISO15765: UInt32 = 6 */
ptOtaUpdate(devId, firmwarePath, callback) und ptOtaAbort(devId); zuvor waren OtaUpdate/OtaAbort nur über die C-API erreichbar. Der Fortschritt kommt in onProgress(current, total) - Blöcke, Zählung ab 1.
// update.bin wurde vorher in den App-Speicher kopiert.
// Der Aufruf blockiert - außerhalb des Main Threads ausführen.
val res = j2534.ptOtaUpdate(devId, file.absolutePath,
object : OtaProgressListener {
override fun onProgress(current: Int, total: Int) { /* Fortschrittsbalken */ }
})
if (res.status == 0) {
// Firmware geschrieben, das Gerät startet neu: devId ist ungültig,
// Verbindung mit neuem ptOpen wiederherstellen (über BLE - ~10 s warten)
}
// res.status < 0 - ota_result_t-Code (siehe j2534_ota.h)
// ptOtaAbort(devId) bricht das Update ohne Neustart des Geräts ab
log_level in j2534.json gesetzt: -1 aus, 0 Fehler, 1 +Warnungen, 2 +Info, 3 +Debug, 4 +Verbose; Standard ist 3, ohne Konfiguration wird das Log also vollständig geschrieben. Bei -1 werden weder der Ordner sdlogs noch die Datei .qlog angelegt, es wird nichts auf die Platte geschrieben. Auf Android lässt sich der Level auch aus dem Code setzen - ptSetLogLevel(int); ein so gesetzter Level hat Vorrang vor der Konfigurationsdatei, sodass sich das Log in einem Release-Build nicht von außen einschalten lässt.
// Android: vor ptOpen aufrufen
j2534.ptSetLogLevel(-1) // Release-Build - Log aus
j2534.ptSetLogLevel(3) // Support-Fall - vollständiges Log
// Andere Plattformen: j2534.json im Konfigurationsordner
// macOS ~/Library/Application Support/Quantex/
// Linux ~/.config/quantex/
// Windows %APPDATA%\Quantex\
{ "log_level": -1, "devices": [] }
Behoben
PassThruReadVersion und der .qlog-Kopf lieferten 2.0.0.0: die Build-Nummer wurde nicht an den Android-Build übergeben. Die Version wird jetzt nach derselben Regel wie auf den anderen Plattformen bestimmt; diese Nummer ist bei Support-Anfragen anzugeben.serial_*-Symbolen: der USB-Transport ist aus dem iOS-Build ausgeschlossen, die Aufrufe blieben aber bestehen. Der Transport ist durch einen Stub ersetzt - eine Verbindung mit c:-String liefert einen regulären Fehler beim Öffnen des Ports..qlog-Log - Nachrichtendaten wurden nur bis 125 Byte geschrieben, und der Eintrag einer Nachrichtengruppe aus einem einzelnen PassThruReadMsgs- oder PassThruWriteMsgs-Aufruf war durch einen festen Puffer begrenzt. Nachricht und gesamte Gruppe werden jetzt vollständig geschrieben - wichtig bei langen Antworten wie einer DTC-Liste..qlog-Log - Bibliothek und Gerät erzeugten den Logtext mit zwei unabhängigen Implementierungen, und die Dekodierung derselben Werte wich voneinander ab. Das Flag der aufgebauten Verbindung bei TP2.0 und TP1.6 im Feld RxStatus druckte die Bibliothek als CONNECTION_ESTABLISHED, das Gerät als CONN_OK; die IOCTL-Namen unterschieden sich an 22 Stellen. Die Namen von Protokollen, TxFlags- und RxStatus-Flags, IOCTLs und deren Parametern stammen jetzt aus einer Implementierung, sodass sich Anwendungs- und Gerätelog desselben Austauschs nebeneinander lesen lassen.J2534-Bibliotheken 2.0.0.200 herunterladen - Windows x86/x64/ARM64 (separate Builds für Windows 7), macOS (universal), Linux (x64, x86, ARM, ARM64), Android (arm64-v8a, armeabi-v7a, x86, x86_64), iOS.
Neu
ISO13400_PS (0x8FFD) und HSFZ_PS (0x8FFC). Sie sind nicht Teil des SAE-J2534-Standards - eine proprietäre ScanDoc-Erweiterung: Diagnose über Ethernet - Fahrzeugerkennung im Netzwerk (VIN, logische Adresse), TCP-Verbindung, Routing Activation, UDS-Austausch. Die Tester-Adresse ist standardmäßig 0 - setzen Sie ISO13400_SOURCE_ADDR vor der Routing Activation, sonst lehnt das Gateway ab; die ECU-Adresse wird in jeder Nachricht übertragen ([TA][SA][UDS]), ISO13400_TARGET_ADDR wird nicht über Set/GetConfig gesetzt. Das Senden wird per P2 serialisiert: jeweils nur eine offene UDS-Anfrage, NRC 7F xx 78 verlängert die Wartezeit auf P2*max (6 s). Neuer Kanalparameter ISO13400_P3_DOIP (0x8108) - Pause zwischen Nachrichten.
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 - vor der Routing Activation */
PassThruIoctl(ch, ISO13400_DISCOVER_VEHICLES, NULL, NULL); /* die ECU-IP wird automatisch übernommen */
PassThruIoctl(ch, ISO13400_CONNECT_TCP, NULL, NULL);
PassThruIoctl(ch, ISO13400_ACTIVATE_ROUTING, NULL, &code); /* 0x10 = Erfolg */
/* danach PassThruWriteMsgs / PassThruReadMsgs - normales UDS */
0x55 (J2534-Rahmenmarker) - J2534, alles andere (textuelles AT-Kommando) - ELM327.Behoben
PassThruStartMsgFilter verglich nur die 4 Bytes der CAN-ID und ignorierte die angegebene Filterlänge. Jetzt wird der Rahmen über die volle Länge verglichen, wie es der Standard verlangt: PASS/BLOCK nach Rahmeninhalt funktioniert.
/* TesterPresent-Antworten (07E8 02 7E ...) in der Empfangswarteschlange unterdrücken */
pt_msg_t mask = {0}, pattern = {0};
mask.protocol_id = pattern.protocol_id = CAN;
mask.data_size = pattern.data_size = 6; /* 4 Bytes CAN-ID + 2 Datenbytes */
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 bei aktivem CAN-Kanal zerstörte die Flow Control (das FC ging ohne Padding raus, DLC=3 - das Gateway sendete keine Consecutive Frames) und überschrieb den Empfangsfilter mit der eigenen TX-ID (der Empfang ohne AT CRA brach ab). Laut Datenblatt setzt AT SH nur den Sende-Header - den Empfangsfilter steuern jetzt ausschließlich AT CRA/CF/CM.PassThruStopPeriodicMsg konnte nach dem Stoppen einen zusätzlichen Rahmen senden._PS - die Pin-Auswahl über SET_CONFIG(J1962_PINS) wurde nicht übernommen, Rahmen erreichten den Bus nicht.Behoben