- 新增 docs/DLD960_BLE日志查询交互.md: 0x25/0x26/0x27 完整交互示例 (真实日志逐字节解析, 分包规则, 第二分包未发问题记录) - DLD960_BLE协议.md V1.01: 分包上限改为随协商 MTU 动态 (min(MTU-9,94)) - peripheral.c: performPeriodicTask 加 500ms 心跳打印 (确认 TMOS 周期存活 + resp 队列状态, 定位第二分包为何不续传)
8.3 KiB
8.3 KiB
DLD960 BLE 日志查询交互过程(实测示例)
版本: V1.00(2026-08-12) 适用: vd960DBN(CH32V208)+ 微信小程序 目的: 以真实串口日志为样例,完整描述"小程序发指令 → 设备处理 → 设备回包"的交互过程,供固件/小程序两端排查对照
1 帧格式回顾(与 DLD960_BLE协议.md 一致)
Magic | Header | Data | CheckByte
| Addr/Sub Len CMD | | Xor Sum
1 Byte | 1B 1B 1B | xx | 1B 1B
Len = len(CMD) + len(Data),即pkg[2] = 1 + data_len- 校验:
Xor = XOR(pkg[1..Len+2]),Sum = SUM(pkg[1..Len+2])(覆盖 header+cmd+data,不含 magic) - Magic =
0x8F(MAGIC_BYTE_DBN_DEFAULT) - Header 字节分包语义:
(pkg_amount << 4) | pkg_seq- 单包:
0x00(amount=0, seq=0) - 分包:高 4 位 = 总包数,低 4 位 = 当前包序号(从 1 递增),最后一片
amount == seq
- 单包:
- 单包数据上限(动态,V1.01+):
chunk = min(peripheralMTU - 9, 94)(整包 = 帧头4 + dat + ckb2,须 ≤ MTU-3 且 ≤ 本地缓冲 100B)
| 协商 MTU | chunk(单包最大 dat) | 整包最大长度 |
|---|---|---|
| 23(未协商) | 14 | 20 |
| 96(本样例手机) | 87 | 93 |
| 185(iOS 常见) | 94 | 100 |
2 命令码
| 命令码 | 名称 | 方向 | 说明 |
|---|---|---|---|
0x25 |
OFFLOG_STAT |
APP→设备 | 查询脱机事件日志统计 |
0x26 |
OFFLOG_QUERY |
APP→设备 | 按全局序号分页拉取日志记录 |
0x27 |
OFFLOG_CLEAR |
APP→设备 | 清除日志(审计留痕) |
3 交互示例一:查询日志统计 OFFLOG_STAT (0x25)
3.1 小程序发送(请求帧)
8F 00 01 25 24 26
| 字节 | 值 | 含义 |
|---|---|---|
| pkg[0] | 8F |
magic |
| pkg[1] | 00 |
header:单包(amount=0, seq=0) |
| pkg[2] | 01 |
Len = 0 data + 1 cmd = 1 |
| pkg[3] | 25 |
cmd = OFFLOG_STAT(查询统计,无 data) |
| pkg[4..5] | 24 26 |
ckb:Xor=0x24, Sum=0x26 |
3.2 设备处理日志
[Rx][13:45:41.577] profile ChangeCB CHAR1?..:�, len:6
[Rx][13:45:41.577] 8F 00 01 25 24 26
[Rx][13:45:41.577] BLE: offlog_stat count=162 seq_first=1 seq_last=162
[Rx][13:45:41.588] BLE notify OK, len:25, MTU:96
3.3 设备响应(25B = 帧头4 + dat 19 + ckb 2,单包)
8F 00 14 25 <19B data> <ckb2>
响应 data(19B,全小端)与设备日志字段对照:
| data 偏移 | 长度 | 字段 | 本次值 | 说明 |
|---|---|---|---|---|
| 0 | 1 | status |
00 |
OK |
| 1 | 2 | boot_seq |
待补(需原始 hex) | 启动序号 |
| 3 | 4 | count |
A2 00 00 00 = 162 |
有效记录条数 |
| 7 | 4 | capacity |
80 1F 00 00 = 8064 |
容量上限 |
| 11 | 4 | seq_first |
01 00 00 00 = 1 |
逻辑首条全局序号 |
| 15 | 4 | seq_last |
A2 00 00 00 = 162 |
最新全局序号 |
单包响应无分包,
notify OK len:25(25 = 4+19+2),MTU=96 下 25 ≤ 93,正常。
4 交互示例二:读取日志记录 OFFLOG_QUERY (0x26)
4.1 小程序发送(请求帧)
8F 00 06 26 01 00 00 00 04 25 31
| 字节 | 值 | 含义 |
|---|---|---|
| pkg[0] | 8F |
magic |
| pkg[1] | 00 |
header:单包请求 |
| pkg[2] | 06 |
Len = 5 data + 1 cmd = 6 |
| pkg[3] | 26 |
cmd = OFFLOG_QUERY |
| pkg[4..7] | 01 00 00 00 |
start_seq = 1(LE32,含) |
| pkg[8] | 04 |
count = 4(≤ OFFLOG_MAX_QUERY_RECORDS=4) |
| pkg[9..10] | 25 31 |
ckb |
4.2 设备处理日志
[Rx][13:45:47.827] profile ChangeCB CHAR1?..:�, len:11
[Rx][13:45:47.827] 8F 00 06 26 01 00 00 00 04 25 31
[Rx][13:45:47.827] BLE: offlog_query start_seq=1 req=4 fetched=4
[Rx][13:45:47.848] BLE notify OK, len:93, MTU:96
[Rx][13:45:47.848] 8F 21 58 26 00 04 A5 01 04 00 01 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 18 00 00 00 00 00 00 00 00 00 00 00 A5 01 04 00 02 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 A5 01 04 00 03 00 00 00 00 00 00 00 00 00 00 00 03 00 00 00 18 F3 E5
4.3 响应分包说明
响应 dat 总量 = status(1) + count(1) + 4×32B = 130B > 单包上限 87B(MTU=96)→ 必须分包 2 包:
| 包 | header | 整包长 | dat 内容 |
|---|---|---|---|
| 第 1 包 | 0x21(amount=2, seq=1) |
93B | status+count + 记录1(32B) + 记录2(32B) + 记录3前21B = 87B |
| 第 2 包 | 0x22(amount=2, seq=2) |
49B(预期) | 记录3剩余11B + 记录4(32B) = 43B |
4.4 第一包逐字段解析(93B)
8F 21 58 26 00 04 <记录1 32B> <记录2 32B> <记录3 前21B> F3 E5
-- -- -- -- -- -- --------------------------------------- ----
| | | | | | | +-- ckb
| | | | | | +-- dat: status(1)=00, count(1)=04, 然后记录
| | | | | +-- status=00 OK
| | | | +-- cmd=0x26
| | | +-- Len=0x58=88 = 87 data + 1 cmd
| | +-- header=0x21: amount=2, seq=1
| +-- magic=0x8F
记录结构(OfflogEvt 32B,小端)——本次 3 条记录均为 type=0x01 04 00(事件类型,待对照 offlog.h 事件表):
记录1: A5 01 04 00 | 01 00 00 00 | 00×12 | 01 00 00 00 | 18 00 00 00
记录2: A5 01 04 00 | 02 00 00 00 | 00×12 | 02 00 00 00 | 08 00 00 00
记录3: A5 01 04 00 | 03 00 00 00 | 00×12 | 03 00 00 00 | 18(截断于第 21B)
4.5 第二包(预期,未收到——当前问题)
8F 22 2C 26 <记录3剩余 11B> <记录4 32B> <ckb2>
- header
0x22:amount=2, seq=2(最后一片) - Len =
0x2C= 44 = 43 data + 1 cmd - 整包 49B
4.6 当前问题(2026-08-12 排查中)
现象:设备只发出了第 1 包(93B),第 2 包(49B)未发出;小程序按 amount=2 等第 2 包永远等不到。
已确认:
- MTU 分包粒度动态化已生效(93B ≤ 96-3=93,无
Too large noti) performPeriodicTask已加主动拉包逻辑(不依赖收包事件)- 已加
BLE pull FAIL诊断打印,但本次日志未出现 → 说明拉包时g_buf_ble_response.flag==0(队列已被清)或_pull==1但发送周期未到/被阻塞
下一步排查:
- 加
performPeriodicTask周期心跳打印(每 500ms),确认 TMOS 周期事件是否持续执行 - 核对
SBP_PERIODIC_EVT_PERIOD=50的 TMOS 时间单位(tick vs ms) - 检查
g_buf_ble_response在第 1 包发出后是否被意外 clear(unpack_packs/set_response_buf调用路径)
5 交互示例三:清除日志 OFFLOG_CLEAR (0x27)
5.1 小程序发送
8F 00 01 27 XX XX
| 字节 | 值 | 含义 |
|---|---|---|
| pkg[0] | 8F |
magic |
| pkg[1] | 00 |
header:单包 |
| pkg[2] | 01 |
Len = 1 |
| pkg[3] | 27 |
cmd = OFFLOG_CLEAR |
| pkg[4..5] | XX XX |
ckb |
5.2 设备响应
8F 00 02 27 00 XX XX
- dat:
status(1)=00(OK) - 注意:擦除 W25Q32 阻塞约 2.8s,期间 BLE 无响应、网络短时失服务(MQTT keepalive 60s 可吸收)
6 小程序端分包重组规则(伪代码)
// 订阅 onBLECharacteristicValueChange, 按 cmd 维护重组缓冲
let frag = { cmd: null, amount: 0, seq: 0, data: [] };
function onNotify(buf) {
const magic = buf[0], header = buf[1], len = buf[2], cmd = buf[3];
const amount = header >> 4, seq = header & 0x0F;
const data = buf.slice(4, 4 + (len - 1)); // 去掉 cmd 的净数据
const ckb = buf.slice(-2); // 校验可选
if (amount === 0) { // 单包
dispatch(cmd, data);
} else { // 分包
if (seq === 1) { frag = { cmd, amount, seq, data: [] }; }
if (frag.cmd !== cmd) return; // 乱序/新响应, 丢弃
frag.data = frag.data.concat(data);
frag.seq = seq;
if (seq === amount) { // 收齐
dispatch(cmd, frag.data);
frag = null;
}
}
}
关键点:收到第 1 包(seq=1, amount=2)后必须继续等待 seq=2 的第二个通知,不要在第 1 包到达时判完成。
7 排查时间线
| 时间 | 现象 | 结论 |
|---|---|---|
| 08:15 | Too large noti, len:102, MTU:96 |
分包粒度写死 96B > MTU-3=93,丢包 |
| 11:51 | 修复后第一包 93B 正常发出 | MTU 动态分包生效 |
| 11:51~13:45 | 第二包 49B 未发出,无 pull FAIL 打印 | 分包续传/发送周期链路待查(见 §4.6) |