- BluetoothManager: per-characteristic isolated channels (per-UUID read queue/timeout/dispatch, no cross-char write blocking), 10007 delayed retry for read/write, 300ms link settle after discovery, full tx/rx hex logging - McmCli/McmInfo/McmOta: command/response, TLV parse and OTA frame logs - home.vue: Auto refresh switch (polling off by default, no GATT interference with OTA), MTU-then-load sequencing - ota.vue: SAF file picker via plus.android (ACTION_GET_CONTENT) with Scanner whole-stream read + magic self-check + per-byte fallback; default path moved to _doc (Android 10+ scoped storage) - docs: change logs for 2026-09-04 protocol adaptation and 2026-09-05 field debugging; AGENTS.md updated
79 lines
4.2 KiB
Markdown
79 lines
4.2 KiB
Markdown
# 修改记录 2026-09-05:真机联调——BLE 通道按特征值隔离 + SAF 选包 + 全链路日志
|
||
|
||
## 背景
|
||
|
||
V1.00.23 协议适配(见 2026-09-04 修改记录)后真机联调,依次踩到并解决:
|
||
连接后读超时/写 10007、OTA 页读包失败(Android 10+ 分区存储)、
|
||
plus 桥 byte[] 不回写导致读包内容错误等。本轮全部修复并加了可定位问题的日志。
|
||
|
||
## 改动清单
|
||
|
||
### src/BluetoothManager.ts(核心:读写按特征值隔离成独立通道)
|
||
|
||
- **读改为按特征 UUID 分键的独立队列**(`readQueues`/`readActiveMap`/`readTimers`):
|
||
同一特征上的读串行(e0003 分包读不与 e0004 轮询读交错);不同特征互不阻塞,
|
||
一路超时/失败不影响其他通道;`onBLECharacteristicValueChange` 按
|
||
characteristicId 直接分发到对应通道
|
||
- **写无任何跨特征等待**(`writeChar` 直接发);断连时逐通道排空各自队列
|
||
- **10007 "property not support" 延时重试**:读和写各一次(1600ms)——实测安卓
|
||
链路未稳定时会误报 10007(特征属性明明支持写),参考 DCloud 社区已知问题
|
||
- **链路稳定期**:`scanServices` 成功后 300ms 内不发出 GATT 操作(连接级一次性)
|
||
- 全链路日志:连接/断开事件、`tx/rx <特征短名> <长度> <hex>`(截断标注总长)、
|
||
读发起/超时、10007 重试提示
|
||
|
||
实测根因记录:home 页曾在 `bleState=0` 时**并发**调 `setMtu` 与读 e0004,
|
||
读被安卓无声丢弃(3s 超时),后续写全部被拖进 10007;通道隔离 + 时序修正后消失。
|
||
|
||
### src/mcm/McmCli.ts
|
||
|
||
- 日志:`[CLI] >> 命令` / `[CLI] << (NB, status=0) <JSON 化应答>`,失败带底层
|
||
errMsg 原文;`CliResult` 新增 `errMsg` 字段
|
||
|
||
### src/mcm/McmInfo.ts
|
||
|
||
- 日志:`[INFO] TLV NB parsed: {...}`;读失败打 `[INFO] read failed`
|
||
|
||
### src/mcm/McmOta.ts
|
||
|
||
- 帧日志:BEGIN/END/ABORT 全帧 hex;每条 OTA_RSP 打 `echo/status/offset + hex`;
|
||
坏帧内容一并打出(DATA 帧体不逐条打,避免刷屏)
|
||
|
||
### pages/tabbar/home.vue
|
||
|
||
- **Auto refresh (2s) 开关,缺省关闭**:不再自动周期读 e0004,避免轮询占用
|
||
GATT 干扰 OTA/CLI;开关打开才轮询(立即查一次 + 2s 周期);
|
||
连接就绪回调与 onShow 两个自动启动点都已按开关门控
|
||
- 连接初始化改串行:先 `setMtu(247)`,回调里再 `loadDeviceInfo()`(消除
|
||
GATT 并发撞车)
|
||
|
||
### pages/tabbar/ota.vue(选包/读包重做)
|
||
|
||
Android 10+ 分区存储(targetSdk≥29)禁止直读 `/sdcard/Download`(plus 报
|
||
code 15),`uni.chooseFile` 在 App 端 vue3 又不存在,最终方案:
|
||
|
||
- **Choose File 按钮**:plus.android 起 `Intent.ACTION_GET_CONTENT` 系统 SAF
|
||
选择器(免权限,可直接选 Download 里的包)
|
||
- **读包**:`ContentResolver.openInputStream(uri)` → `java.util.Scanner` 按
|
||
ISO-8859-1 整流读成 String 过桥(字节 1:1 映射,规避 **byte[] 参数按值拷贝、
|
||
`read(buf)` 写不回 JS 侧**的桥限制——曾读出整块填充字节 0x41)→ 读回自验
|
||
magic(`11 BA 10 CA`)→ **不符自动重开流落逐字节 `read()` 直读**(int 返回值
|
||
过桥,二进制绝对精确,每 16KB 打进度)
|
||
- 全程日志:picker 拉起/onActivityResult 返回码/uri 原文/读取模式/字节数/
|
||
文件头 hex/magic 校验结论;失败分支带异常原文
|
||
- 手动路径方式保留:默认 `_doc/mothercup_ble_ota.bin`(应用私有目录,可 adb
|
||
push 进去);解析失败时日志带实际读到的长度+头部 hex,便于区分"读错"与"格式错"
|
||
- `window.plus` 改 `globalThis.plus`(App 端 vue3 无 window 对象)
|
||
|
||
## 验证(真机)
|
||
|
||
- e0004 参数特征读通:TLV 33B 解析正确(V1.00.23 / APP1 / 21.2℃ / 3335mV /
|
||
uptime / 堆剩余),10007 与读超时不再出现
|
||
- SAF 选包读通:111168B 与包文件一致
|
||
- parseCombo 逻辑在 PC 侧 node 用真实 `mothercup_ble_ota.bin` 逐字节验证:
|
||
magic/hdr_len/total_size/payload_crc/逐 bank crc 全对(协议符合性确认无误)
|
||
|
||
## 待办
|
||
|
||
- BLE OTA 全流程真机(Start Upgrade → 复位 → 回连复核 PASS)
|
||
- 修改记录 2026-09-04 的实机验证项随本轮一并闭环
|