Files
vd_960/docs/DLD960_BLE日志查询交互.md
T
wangfq ef3316f2b3 docs(vd960DBN): BLE 日志查询交互过程文档 + 协议 V1.01 动态分包 + 周期心跳
- 新增 docs/DLD960_BLE日志查询交互.md: 0x25/0x26/0x27 完整交互示例
  (真实日志逐字节解析, 分包规则, 第二分包未发问题记录)
- DLD960_BLE协议.md V1.01: 分包上限改为随协商 MTU 动态 (min(MTU-9,94))
- peripheral.c: performPeriodicTask 加 500ms 心跳打印 (确认 TMOS 周期存活
  + resp 队列状态, 定位第二分包为何不续传)
2026-08-12 13:51:32 +08:00

8.3 KiB
Raw Blame History

DLD960 BLE 日志查询交互过程(实测示例)

版本: V1.002026-08-12 适用: vd960DBNCH32V208+ 微信小程序 目的: 以真实串口日志为样例,完整描述"小程序发指令 → 设备处理 → 设备回包"的交互过程,供固件/小程序两端排查对照


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 = 0x8FMAGIC_BYTE_DBN_DEFAULT
  • Header 字节分包语义(pkg_amount << 4) | pkg_seq
    • 单包:0x00amount=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
185iOS 常见) 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 ckbXor=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:2525 = 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 = 1LE32,含)
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 > 单包上限 87BMTU=96)→ 必须分包 2 包

header 整包长 dat 内容
第 1 包 0x21amount=2, seq=1 93B status+count + 记录1(32B) + 记录2(32B) + 记录3前21B = 87B
第 2 包 0x22amount=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 0x22amount=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 但发送周期未到/被阻塞

下一步排查

  1. performPeriodicTask 周期心跳打印(每 500ms),确认 TMOS 周期事件是否持续执行
  2. 核对 SBP_PERIODIC_EVT_PERIOD=50 的 TMOS 时间单位(tick vs ms
  3. 检查 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
  • datstatus(1)=00OK
  • 注意:擦除 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)