J2534 の仕様と機能の説明 ›
ELM327 の仕様とコマンドの説明 ›
ELM327 インターフェースは Nano ET アダプターで利用できます。その他の ScanDoc アダプターは J2534 PassThru プロトコルを使用します。
J2534 DLL、ELM327、ScanDoc アダプターのファームウェアにおける、インテグレーションに関わる変更:新機能、プロトコル、パラメーター - 使用例付き。
ライブラリは 1 つのアーカイブで配布されます。対応プラットフォーム: 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。
新機能
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)); /* 全設定が 1 つの 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 を閉じていました。通常は stdin が黙って閉じられるだけですが、その時点で fd 0 が JVM のオブジェクトに使われていると、fdsan がプロセスを終了させていました: attempted to close file descriptor 0 … owned by native object。クラッシュが不定期だったのはこのためです。ptClose が毎回 1 秒余計にかかる。 ライブラリはクローズに対する機器の応答を破棄し、1 秒の受信タイムアウトを待ったうえで、機器が応答しなかったかのようにログへ 0x6E - Connection timed out を書き込んでいました。現在は機器の応答を受けた時点でクローズが完了します。macOS、Windows、Linux ではこの不具合は発生していませんでした。ptOpen がクラッシュする。 アプリは SIGSEGV で終了していました。その他のオープン失敗時 (ペアリング拒否、接続タイムアウト) には、ライブラリが BLEManager へのグローバル JNI 参照を解放せず、試行を繰り返すたびに参照が蓄積していました。FAST_INIT。デバイスの応答が長さの検査なしにアプリの pt_msg_t 構造体へコピーされていました。応答が短いと未初期化メモリーの読み取りになり、応答中の長さが過大だとアプリが渡した構造体の外への書き込みになります。Android では同じ呼び出しが不正なフレームをバスへ送出し、失敗時にはアプリを異常終了させていました。入力サイズの検査も修正しました。0x20000000 程度の NumOfBytes を通してしまっていたためです。FIVE_BAUD_INIT。入力は §11.3.3.3 の要求どおりアドレス 1 バイトに制限されます。従来は任意長のブロックを受け付け、それ以上を渡したアプリには拒否ではなく成功コードが返っていました。.qlog ファイルが作成されない。ログを開くのは PassThruOpen だけで、ptOpen はこれを経由しません。そのためサードパーティーの Android アプリではログレベルに関係なく sdlogs フォルダーが空のままで、記録は logcat にしか残らず、顧客からセッションのログを受け取ることができませんでした。FIVE_BAUD_INIT と FAST_INIT の行には > ok しか出力されず、初期化アドレスも ECU の応答もログに残らないため、失敗した初期化をログから解析できませんでした。現在は要求と応答の両方が含まれます: 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 チャネルのピン設定 - これら 3 つのプロトコルの接続をデバイスが拒否していました。現在は TP2_0_PS、ISO9141_PS と同様に動作します。ピン設定を J2534-2 に合わせて 3 点修正しました:
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 経由の ECU プログラミングをベンチで確認済みです。REQUEST_CONNECTION 失敗後にチャネル内部のフィルターが解除されず、無応答の ECU への十数回の試行でフィルタースロットを使い切りチャネルが沈黙していました。TP1_6_PS の TEARDOWN_CONNECTION が拒否され、アプリ側から接続を閉じられませんでした。TP2.0 は ECU 側から確立された接続を受け付けず、接続外で受信したフレームをアプリへ渡していませんでした(J2534-2 §19.3.1)。PassThruReadMsgs に届きません。ここはポイント・ツー・ポイント接続で、テスターアドレスは routing activation で登録済みのため、選別するものがありません:チャネルは全メッセージをアプリへ渡し、PassThruStartMsgFilter は ERR_NOT_SUPPORTED を返します。CAN ドライバーの障害で DoIP セッション中にデバイスが再起動していました。受信タスクのウォッチドッグを 5 s から 30 s に延長 - DoIP 接続の確立は正常でも最大 20 s かかります。PassThruConnect が最初のメッセージでキューあふれになっていました。基本の CAN と ISO15765 が別の CAN コントローラーへ向かい、バスに届いていませんでした。破損したエントリの後、受信キューの読み出しが回復しませんでした - CAN ベースの全プロトコルでフラグの判定が誤っていました。GET_NDIS_ADAPTER_INFO - STATUS_NOERROR のもとで未初期化データが返されていました。現在はアダプター識別子、MAC、ECU から見たデバイスの IPv4 アドレス、アクティベーション線の状態を返します。Ethernet のないデバイスでは ERR_NOT_SUPPORTED。GET_PROTOCOL_INFO - 一部のプロトコルにしか応答せず、形式も誤っていました。現在は開いている任意のチャネルで動作します:タイムスタンプ分解能(1 µs)、対応パリティ、UART データビット数。デバイスが応答できないパラメーターは supported フィールドで示され、呼び出し自体は STATUS_NOERROR を返します。PassThruDisconnect - すでに終了したチャネルタスクへのアクセスがデバイスのメモリを破壊していました。新機能
libj2534.xcframework はデバイス(arm64)とシミュレーター(arm64/x86_64)のスライスを含み、最低 iOS 12.0。各スライスにヘッダー j2534.h、j2534_ota.h、module map(Swift import J2534、CoreBluetooth を自動リンク)、privacy manifest を同梱。Pass-Thru API のプロトタイプは j2534.h 自体で宣言されます - 全プラットフォーム共通。mbedTLS は組み込み済みで外部依存はありません。Xcode 設定:Embed = Do Not Embed(静的ライブラリ)、Other Linker Flags に -lc++、Info.plist に NSBluetoothAlwaysUsageDescription(BLE)と NSLocalNetworkUsageDescription(WLAN)- これらがないと 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)
}
/* プロトコルと IOCTL の ID は型キャスト付きマクロで Swift にインポートされない:
数値で指定する、let CAN: UInt32 = 5, let ISO15765: UInt32 = 6 */
ptOtaUpdate(devId, firmwarePath, callback) と ptOtaAbort(devId) を追加。従来 OtaUpdate/OtaAbort は C API からのみ利用可能でした。進捗は onProgress(current, total) で通知 - ブロック単位、1 始まり。
// 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 s 待つ)
}
// res.status < 0 - ota_result_t コード(j2534_ota.h 参照)
// ptOtaAbort(devId) はデバイスを再起動せずに更新を中止する
j2534.json の log_level キーで設定:-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_* シンボル 9 個でアプリのビルドが失敗していました:USB トランスポートは iOS ビルドから除外されていましたが、呼び出しが残っていました。トランスポートをスタブに置き換え - c: 文字列での接続は通常のポートオープンエラーを返します。.qlog ログのデータ切り詰め - メッセージデータは 125 バイトまでしか記録されず、1 回の PassThruReadMsgs または PassThruWriteMsgs で渡されたメッセージ群の記録も固定バッファで制限されていました。メッセージも群全体も完全に記録されます - DTC 一覧などの長い応答で重要です。.qlog ログのフィールド解読 - ライブラリとデバイスが独立した 2 つの実装でログ文字列を生成しており、同じ値の解読が食い違っていました。RxStatus フィールドの TP2.0/TP1.6 接続確立フラグをライブラリは 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 - routing activation の前に ISO13400_SOURCE_ADDR を設定してください。設定しないとゲートウェイが拒否します。ECU アドレスは各メッセージで渡されます([TA][SA][UDS])。ISO13400_TARGET_ADDR は Set/GetConfig では設定しません。送信は P2 で直列化されます:未完了の UDS 要求は同時に 1 つ、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); /* ECU の IP は自動的に記憶される */
PassThruIoctl(ch, ISO13400_CONNECT_TCP, NULL, NULL);
PassThruIoctl(ch, ISO13400_ACTIVATE_ROUTING, NULL, &code); /* 0x10 = 成功 */
/* 以降は PassThruWriteMsgs / PassThruReadMsgs - 通常の UDS */
0x55(J2534 フレームマーカー)- J2534、それ以外(テキスト AT コマンド)- ELM327。修正
PassThruStartMsgFilter は CAN ID の 4 バイトのみを比較し、指定されたフィルター長を無視していました。標準の要求どおり全長で比較するようになり、フレーム内容による 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; /* CAN ID 4 バイト + データ 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 コマンドが Flow Control を壊し(FC がパディングなし DLC=3 で送信され、ゲートウェイが Consecutive Frames を送らない)、受信フィルターを自身の TX ID で上書きしていました(AT CRA なしの受信が壊れる)。データシートどおり AT SH は送信ヘッダーのみを設定し、受信フィルターは AT CRA/CF/CM のみで制御されます。PassThruStopPeriodicMsg が停止後に余分なフレームを 1 つ送ることがありました。_PS - SET_CONFIG(J1962_PINS) によるピン選択が適用されず、フレームがバスに出ませんでした。修正