Pour le développement de votre propre logiciel de diagnostic, les adaptateurs ScanDoc prennent en charge deux protocoles d'échange de données : J2534 PassThru et ELM327. L'ensemble des protocoles pris en charge dépend du modèle de l'adaptateur - choisissez celui qui convient le mieux à votre tâche.
Spécification et description des fonctions J2534 ›
Spécification et description des commandes ELM327 ›
L'interface ELM327 est disponible dans l'adaptateur Nano ET. Les autres adaptateurs ScanDoc utilisent le protocole J2534 PassThru.
Changements dans la DLL J2534, ELM327 et le firmware des adaptateurs ScanDoc concernant l'intégration : nouvelles fonctions, protocoles et paramètres - avec des exemples d'utilisation.
Les bibliothèques sont livrées dans une seule archive. Plateformes : Windows x86/x64/ARM64 (builds séparés pour Windows 7), macOS (universal), Linux (x64, x86, ARM, ARM64), Android (arm64-v8a, armeabi-v7a, x86, x86_64), iOS (XCFramework). Le dossier docs/ contient la documentation du SDK : prise en main, référence de l'API, configuration, gestion des erreurs, DoIP, mise à jour du firmware, format du journal, Android, iOS.
Télécharger les bibliothèques J2534 2.0.0.225
Nouveautés
ConfigRead, et ConfigWrite supprime un appairage. Aucun nouvel appel n'a été ajouté à la bibliothèque pour cela.
Exemple
char json[2048];
if (ConfigRead(dev, json, sizeof(json)) == STATUS_NOERROR) {
/* dans la réponse, parmi les autres réglages :
"ble_bonds":[{"name":"WS-07","mac":"A4:C1:38:11:22:33"}] */
}
ConfigWrite(dev, "{\"ble_bond_del\":\"A4:C1:38:11:22:33\"}");
/* effet immédiat, ConfigReboot inutile */
Corrections
reciv ack end single msg timeout SG, puis reciv ack counter error SG.TX_FAILED, et seule une reconnexion du canal rétablissait le fonctionnement.Nouveautés
ConfigRead, ConfigWrite, ConfigReboot et ConfigReset (ordinaux @52-@55 sous Windows) étaient déjà exportés, mais absents de la documentation : impossible de s'en servir depuis l'extérieur. docs/API_REFERENCE.md décrit désormais les prototypes et le comportement, et docs/CONFIGURATION.md donne le tableau des clés avec les plages que le firmware contrôle. L'appareil traite ces commandes avant le routage J2534, elles fonctionnent donc sur tous les transports : LAN/WLAN, BLE et USB.
Exemple
char json[2048];
ConfigRead(dev, json, sizeof(json)); /* tous les réglages en un seul objet JSON */
ConfigWrite(dev, "{\"ble_name\":\"WS-07\"}"); /* seules les clés transmises changent */
ConfigReboot(dev); /* les réglages prennent effet au redémarrage ;
device_id devient invalide, rouvrir après ~10 s */
ptConfigRead, ptConfigWrite, ptConfigReboot et ptConfigReset.
Exemple
external fun ptConfigRead(devId: Int): String? // null en cas d'erreur
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 // cause : ptGetLastError()
j2534.ptConfigWrite(devId, """{"ble_name":"WS-07"}""")
j2534.ptConfigReboot(devId)
PassThruOpen ne vérifie volontairement pas la version, afin de ne pas ajouter un échange avec l'appareil à chaque ouverture : le build installé est renvoyé par PassThruReadVersion, et l'écart apparaît dans le .qlog sous la forme firmware build N is older than build 85 required by DLL ….Corrections
ERR_FAILED, et PassThruGetLastError fournit le texte BLE pairing rejected (wrong PIN or not paired). ERR_FAILED a été retenu à dessein, car les applications demandent rarement le texte d'erreur sur ERR_DEVICE_NOT_CONNECTED. Cela vaut pour toutes les plateformes : Android, macOS, Windows et Linux. Les autres défaillances BLE renvoient toujours ERR_DEVICE_NOT_CONNECTED, mais avec le code du transport : ce code était auparavant affiché comme un numéro d'erreur POSIX, et une écriture refusée ressortait sous la forme 0x7 - Argument list too long.ptClose en BLE. À la fermeture d'une connexion BLE, la bibliothèque fermait le descripteur de fichier 0, alors qu'il n'y a pas de socket en mode BLE. En général, cela fermait stdin sans bruit, mais si fd 0 appartenait à ce moment-là à un objet de la JVM, fdsan mettait fin au processus : attempted to close file descriptor 0 … owned by native object. D'où le caractère intermittent du plantage.ptClose en BLE prenait une seconde de trop. La bibliothèque rejetait la réponse de l'appareil à la fermeture, attendait l'expiration du délai de réception de 1 s et écrivait 0x6E - Connection timed out dans le journal, comme si l'appareil n'avait pas répondu. La fermeture se termine désormais dès la réponse de l'appareil. macOS, Windows et Linux n'étaient pas concernés.ptOpen en BLE lorsque l'appareil ne répondait pas à l'ouverture. L'application se terminait par SIGSEGV. Lors des autres échecs d'ouverture (appairage refusé, délai de connexion dépassé), la bibliothèque ne libérait pas sa référence JNI globale vers BLEManager, et les références s'accumulaient au fil des tentatives.FAST_INIT. La réponse de l'appareil était copiée dans la structure pt_msg_t de l'application sans contrôle de longueur : une réponse trop courte entraînait la lecture de mémoire non initialisée, et une longueur surévaluée dans la réponse une écriture au-delà de la structure transmise par l'application. Sous Android, le même appel envoyait une trame altérée sur le bus et, en cas d'échec, terminait l'application. Le contrôle de la taille des données d'entrée a lui aussi été corrigé : il acceptait des valeurs de NumOfBytes de l'ordre de 0x20000000.FIVE_BAUD_INIT. L'entrée est limitée à un seul octet d'adresse, comme l'exige le §11.3.3.3 ; un bloc de longueur quelconque était auparavant accepté, et une application qui en transmettait davantage recevait un code de succès au lieu d'un refus..qlog n'était pas créé. Seul PassThruOpen ouvrait le journal, alors que l'appel ptOpen le contourne. Pour une application Android tierce, le dossier sdlogs restait donc vide quel que soit le niveau de journalisation, les enregistrements n'allaient que dans logcat, et il était impossible d'obtenir un journal de session auprès du client.FIVE_BAUD_INIT et FAST_INIT n'affichaient que > ok : ni l'adresse d'initialisation ni la réponse du calculateur n'atteignaient le journal, et une initialisation manquée ne pouvait pas y être analysée. La ligne contient désormais la requête et la réponse : io 1 FIVE_BAUD_INIT 33 > 8F6F 2850ms.Télécharger les bibliothèques J2534 2.0.0.213 - Windows x86/x64/ARM64 (builds séparés pour Windows 7), macOS (universal), Linux (x64, x86, ARM, ARM64), Android (arm64-v8a, armeabi-v7a, x86, x86_64), iOS (XCFramework) ; le dossier docs/ contient la documentation du SDK (prise en main, référence de l'API, configuration, gestion des erreurs, DoIP, mise à jour du firmware, format du journal, Android, iOS). Les archives statiques .a ne sont fournies que pour iOS et le build serveur Linux x64 ; les autres plateformes chargent la bibliothèque dynamiquement.
Corrections
CAN_PS, ISO15765_PS, J1939_PS et réglage des broches sur les canaux _PS - la connexion de ces trois protocoles était refusée par l'appareil ; ils fonctionnent désormais comme TP2_0_PS et ISO9141_PS. Le réglage des broches a été mis en conformité avec J2534-2 sur trois points :
PassThruConnect sur les broches par défaut, alors qu'un canal _PS doit rester muet jusqu'à SET_CONFIG(J1962_PINS). Le canal n'entre désormais sur le bus qu'après le réglage des broches.SET_CONFIG(J1962_PINS) répété basculait un canal actif sur d'autres contacts en pleine session. Selon la norme, les broches se règlent une seule fois par canal : un appel répété renvoie ERR_CHANNEL_IN_USE, d'autres broches ne sont possibles qu'après PassThruDisconnect.ERR_PIN_NOT_SUPPORTED.uint32_t ch;
pt_config_t pins = { J1962_PINS, 0x0000060EU }; /* broches 6 et 14 */
pt_config_list_t cfg = { 1, &pins };
PassThruConnect(dev, ISO15765_PS, 0, 500000, &ch);
/* le canal n'est pas encore sur le bus */
if (PassThruIoctl(ch, SET_CONFIG, &cfg, NULL) != STATUS_NOERROR) {
/* ERR_PIN_NOT_SUPPORTED - cette combinaison n'existe pas dans le câblage de l'appareil */
}
/* seulement maintenant PassThruWriteMsgs / PassThruReadMsgs ;
un SET_CONFIG(J1962_PINS) répété - ERR_CHANNEL_IN_USE */
PassThruWriteMsgs renvoyait un succès, PassThruReadMsgs restait vide et l'indicateur CONNECTION_LOST n'était jamais levé. La programmation d'un calculateur via TP2.0 a été vérifiée sur banc.REQUEST_CONNECTION échoué : une dizaine de tentatives vers un calculateur muet occupait tous les emplacements de filtres et rendait le canal muet ; TEARDOWN_CONNECTION sur TP1_6_PS était refusé - impossible de fermer la connexion côté application ; TP2.0 n'acceptait pas une connexion établie par le calculateur et ne transmettait pas à l'application les trames reçues hors connexion (§19.3.1 J2534-2).PassThruReadMsgs. La connexion est ici point à point, l'adresse du testeur est enregistrée lors du routing activation, il n'y a rien à filtrer : le canal transmet chaque message à l'application et PassThruStartMsgFilter répond ERR_NOT_SUPPORTED. Une défaillance du pilote CAN redémarrait l'appareil en pleine session DoIP. Le watchdog de la tâche de réception passe de 5 à 30 s - l'établissement d'une connexion DoIP prend légitimement jusqu'à 20 s.PassThruConnect suivant recevait un débordement de file dès le premier message. Les protocoles de base CAN et ISO15765 partaient vers l'autre contrôleur CAN et n'atteignaient pas le bus. La lecture de la file de réception ne se rétablissait pas après une entrée corrompue - l'indicateur était vérifié incorrectement dans tous les protocoles basés sur CAN.GET_NDIS_ADAPTER_INFO - des données non initialisées étaient renvoyées sous STATUS_NOERROR. La réponse contient désormais l'identifiant de l'adaptateur, le MAC, l'adresse IPv4 sous laquelle le calculateur voit l'appareil et l'état de la ligne d'activation ; sur un appareil sans Ethernet - ERR_NOT_SUPPORTED.GET_PROTOCOL_INFO - ne répondait que pour une partie des protocoles et dans un mauvais format. Il fonctionne désormais sur tout canal ouvert : résolution de l'horodatage (1 µs), parité prise en charge, bits de données UART. Un paramètre auquel l'appareil ne peut pas répondre est marqué dans le champ supported, l'appel lui-même renvoie STATUS_NOERROR.PassThruDisconnect - l'accès à une tâche de canal déjà terminée corrompait la mémoire de l'appareil.Nouveautés
libj2534.xcframework contient des tranches pour l'appareil (arm64) et le simulateur (arm64/x86_64), version minimale iOS 12.0. Chaque tranche embarque les en-têtes j2534.h et j2534_ota.h, une module map (Swift import J2534, CoreBluetooth lié automatiquement) et un privacy manifest. Les prototypes de l'API Pass-Thru sont déclarés dans j2534.h lui-même - sur toutes les plateformes. mbedTLS est compilé dans la bibliothèque, aucune dépendance externe. Configuration Xcode : Embed = Do Not Embed (bibliothèque statique), -lc++ dans Other Linker Flags, les clés NSBluetoothAlwaysUsageDescription (BLE) et NSLocalNetworkUsageDescription (WLAN) dans Info.plist - sans elles, iOS termine l'application au premier accès au transport.
import J2534
var deviceId: UInt32 = 0
// PassThruOpen attend un char* mutable - passer une copie de la chaîne
var cstr = Array("ScanDoc;b:N4999".utf8CString) // BLE par préfixe de nom
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)
}
/* les ID de protocoles et d'IOCTL sont des macros avec transtypage, non importées en Swift :
utiliser les nombres, let CAN: UInt32 = 5, let ISO15765: UInt32 = 6 */
ptOtaUpdate(devId, firmwarePath, callback) et ptOtaAbort(devId) sont ajoutés à la couche JNI ; auparavant OtaUpdate/OtaAbort n'étaient accessibles que via l'API C. La progression est transmise dans onProgress(current, total) - blocs, numérotés à partir de 1.
// update.bin a été copié au préalable dans le stockage de l'application.
// L'appel est bloquant - à exécuter hors du main thread.
val res = j2534.ptOtaUpdate(devId, file.absolutePath,
object : OtaProgressListener {
override fun onProgress(current: Int, total: Int) { /* barre de progression */ }
})
if (res.status == 0) {
// firmware écrit, l'appareil redémarre : devId n'est plus valide,
// reconnexion par un nouveau ptOpen (en BLE - attendre ~10 s)
}
// res.status < 0 - code ota_result_t (voir j2534_ota.h)
// ptOtaAbort(devId) interrompt la mise à jour sans redémarrer l'appareil
log_level dans j2534.json : -1 désactivé, 0 erreurs, 1 +avertissements, 2 +info, 3 +debug, 4 +verbose ; la valeur par défaut est 3, c'est-à-dire que sans configuration le journal est écrit en entier. Au niveau -1, ni le dossier sdlogs ni le fichier .qlog ne sont créés, rien n'est écrit sur le disque. Sur Android, le niveau se règle aussi depuis le code - ptSetLogLevel(int) ; un niveau ainsi fixé a priorité sur le fichier de configuration, si bien qu'en build release le journal ne peut pas être activé de l'extérieur.
// Android : appeler avant ptOpen
j2534.ptSetLogLevel(-1) // build release - journal désactivé
j2534.ptSetLogLevel(3) // demande au support - journal complet
// Autres plateformes : j2534.json dans le dossier de configuration
// macOS ~/Library/Application Support/Quantex/
// Linux ~/.config/quantex/
// Windows %APPDATA%\Quantex\
{ "log_level": -1, "devices": [] }
Corrections
PassThruReadVersion et l'en-tête .qlog renvoyaient 2.0.0.0 : le numéro de build n'était pas transmis au build Android. La version est désormais déterminée selon la même règle que sur les autres plateformes ; c'est ce numéro qu'il faut indiquer lors d'une demande au support.serial_* non résolus : le transport USB est exclu du build iOS, mais les appels subsistaient. Le transport est remplacé par un stub - une connexion par chaîne c: renvoie une erreur normale d'ouverture de port..qlog - les données d'un message n'étaient écrites que jusqu'à 125 octets, et l'enregistrement d'un groupe de messages passé en un seul appel PassThruReadMsgs ou PassThruWriteMsgs était limité par un tampon fixe. Le message et le groupe entier sont désormais écrits en totalité - important pour les réponses longues, par exemple une liste de DTC..qlog - la bibliothèque et l'appareil produisaient le texte du journal avec deux implémentations indépendantes, et le décodage des mêmes valeurs différait. L'indicateur de connexion établie TP2.0 et TP1.6 dans le champ RxStatus était imprimé CONNECTION_ESTABLISHED par la bibliothèque et CONN_OK par l'appareil ; les noms d'IOCTL différaient à 22 endroits. Les noms des protocoles, des indicateurs TxFlags et RxStatus, des IOCTL et de leurs paramètres proviennent désormais d'une seule implémentation, de sorte que le journal de l'application et celui de l'appareil pour un même échange se lisent côte à côte.Télécharger les bibliothèques J2534 2.0.0.200 - Windows x86/x64/ARM64 (builds séparés pour Windows 7), macOS (universal), Linux (x64, x86, ARM, ARM64), Android (arm64-v8a, armeabi-v7a, x86, x86_64), iOS.
Nouveautés
ISO13400_PS (0x8FFD) et HSFZ_PS (0x8FFC). Ils ne font pas partie du standard SAE J2534 - c'est une extension propriétaire ScanDoc : diagnostic par Ethernet - découverte des véhicules sur le réseau (VIN, adresse logique), connexion TCP, routing activation, échange UDS. L'adresse du testeur vaut 0 par défaut - définissez ISO13400_SOURCE_ADDR avant le routing activation, sinon la passerelle refusera ; l'adresse de l'ECU est transmise dans chaque message ([TA][SA][UDS]), ISO13400_TARGET_ADDR ne se définit pas via Set/GetConfig. L'émission est sérialisée selon P2 : une seule requête UDS en cours à la fois, le NRC 7F xx 78 prolonge l'attente jusqu'à P2*max (6 s). Nouveau paramètre de canal ISO13400_P3_DOIP (0x8108) - pause entre les messages.
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 - avant le routing activation */
PassThruIoctl(ch, ISO13400_DISCOVER_VEHICLES, NULL, NULL); /* l'IP de l'ECU est mémorisée automatiquement */
PassThruIoctl(ch, ISO13400_CONNECT_TCP, NULL, NULL);
PassThruIoctl(ch, ISO13400_ACTIVATE_ROUTING, NULL, &code); /* 0x10 = succès */
/* ensuite PassThruWriteMsgs / PassThruReadMsgs - UDS classique */
0x55 (marqueur de trame J2534) - J2534, tout autre octet (commande AT texte) - ELM327.Corrections
PassThruStartMsgFilter ne comparait que les 4 octets du CAN ID, en ignorant la longueur de filtre indiquée. La trame est désormais comparée sur toute la longueur, comme l'exige le standard : PASS/BLOCK selon le contenu de la trame fonctionnent.
/* Supprimer les réponses TesterPresent (07E8 02 7E ...) de la file de réception */
pt_msg_t mask = {0}, pattern = {0};
mask.protocol_id = pattern.protocol_id = CAN;
mask.data_size = pattern.data_size = 6; /* 4 octets CAN ID + 2 octets de données */
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 sur un canal CAN actif cassait le Flow Control (le FC partait sans padding, DLC=3 - la passerelle n'envoyait pas de Consecutive Frames) et écrasait le filtre de réception avec son propre TX ID (la réception sans AT CRA était cassée). Selon la datasheet, AT SH ne définit que l'en-tête d'émission - le filtre de réception n'est plus contrôlé que par AT CRA/CF/CM.PassThruStopPeriodicMsg pouvait envoyer une trame supplémentaire après l'arrêt._PS - la sélection des broches via SET_CONFIG(J1962_PINS) n'était pas appliquée, les trames n'atteignaient pas le bus.Corrections