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。
新增
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 连接时,库会关闭文件描述符 0,而 BLE 模式下根本没有套接字。通常这只是悄无声息地关闭了 stdin,但如果此时 fd 0 正被 JVM 的对象占用,fdsan 就会终止进程:attempted to close file descriptor 0 … owned by native object。因此崩溃并不规律。ptClose 都会多耗时一秒。库丢弃了设备对关闭命令的应答,等满 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 的要求,输入限定为一个地址字节;此前接受任意长度的数据块,传入更多内容的应用会得到成功码而不是拒绝。.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 通道的引脚设置 - 连接这三种协议时设备返回拒绝;现已与 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 对 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 接受可变 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_* 符号失败:USB 传输已从 iOS 构建中排除,但对它的调用仍在。该传输已替换为桩实现 - 用 c: 字符串连接返回常规的端口打开错误。.qlog 日志中数据被截断 - 消息数据最多只写入 125 字节,单次 PassThruReadMsgs 或 PassThruWriteMsgs 调用传入的一组消息的记录受固定缓冲区限制。现在消息和整组均完整写入 - 对 DTC 列表等长响应尤为重要。.qlog 日志中的字段解析 - 库和设备用两套独立实现生成日志文本,同一数值的解析结果不一致。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 交换。tester 地址默认为 0 - 请在 routing activation 之前设置 ISO13400_SOURCE_ADDR,否则网关会拒绝;ECU 地址随每条消息传递([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); /* 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; /* 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 会破坏 Flow Control(FC 无填充发送,DLC=3 - 网关不发送 Consecutive Frames),并用自己的 TX ID 覆盖接收过滤器(不使用 AT CRA 时接收损坏)。按数据手册 AT SH 只设置发送标头 - 接收过滤器现在仅由 AT CRA/CF/CM 控制。PassThruStopPeriodicMsg 停止后可能多发送一帧。_PS - 通过 SET_CONFIG(J1962_PINS) 选择引脚未生效,帧未上总线。修复