# mothercup(母乳杯)设计说明书 V1.0 / 2026-09-05 本文整合当前系统的架构、协议、约定与扩展指南,供后续功能扩展时查阅。 细则以 `docs/ble_protocol.md`(协议权威)、`docs/开发日志.md`(踩坑根因)、 `AGENTS.md`(工程约定)为准;本文是三者 + 真机联调经验的汇总索引。 --- ## 1. 系统组成 ``` ┌─────────────┐ BLE (GATT) ┌──────────────────────┐ │ 手机 App │ ◄─────────────► │ 设备 N32WB031 │ │ SmartAssiter│ │ Boot + 双bank APP │ └─────────────┘ │ USART1 (PB6/PB7) │ ┌─────────────┐ BLE / UART │ ↑ │ │ PC 工具 │ ◄─────────────► │ └─ SWD (PA4/PA5) │ │ tools/ │ └──────────────────────┘ └─────────────┘ ``` | 端 | 位置 | 技术栈 | 职责 | |---|---|---|---| | Bootloader | `Boot/` | C / ARMCC / 16KB @0x01000000 | 校验 bootsetting 记录 → 按 active bank 跳转 | | 应用固件 | `mcm-ddc-ble/` | C / FreeRTOS V9.0.0 原生 API | BLE GATT 服务、CLI、参数、OTA | | 手机 App | `SmartAssiter/` | uni-app Vue3 + TS(HBuilderX 工程) | 参数展示、CLI 终端、参数读写、OTA | | PC 工具 | `tools/` | Python(无三方依赖的出包脚本 + bleak/pyserial 测试客户端) | 出包、烧录、OTA/CLI 参考实现 | 厂商 SDK/PDF 在 `nations-tec/`(**只读**)。SDK 源码经相对路径被工程引用, 各工程目录不能脱离仓库根单独移动。 ## 2. 硬件平台与资源约束 - SoC:国民技术 N32WB031(板载硅片实测 **256KB Flash**,Cortex-M0 64MHz, RAM 48KB+16KB) - **RAM 铁律**:低 16KB(0x20000000~0x20003FFF)被芯片 ROM/BLE 子系统占用, 任何工程 IRAM 执行区必须从 **0x20004000** 起(否则 BLE init 必 HardFault) - **SWD 引脚 SWCLK=PA4 / SWDIO=PA5,应用程序绝对禁止占用** - 板载 LED:LED1=PB0、LED2=PA6;USART1:TX=PB6 / RX=PB7(AF4,115200 8N1) - 新增 GPIO 前核对开发日志 §4 引脚分配表 - **不要直接写寄存器**;勘误修复必须先在 SDK 源码交叉验证地址用途 (曾按勘误表写 AFEC 寄存器导致 BLE 射频挂死,开发日志 §28) ## 3. Flash 布局与启动 | 区域 | 地址 | 大小 | 说明 | |---|---|---|---| | Bootloader | 0x01000000 | 16KB | 只校验 bootsetting 记录自身(magic+CRC32)后跳转 | | bootsetting | 0x01004000 | 8KB | active bank、双 bank 的 addr/size/crc32/version | | APP_DATA | 0x01006000 | 8KB | 区头 `caiic_params_t` 参数记录(magic `0xCA12DA7A`+CRC32) | | APP1 | 0x01008000 | 112KB | bank1 | | APP2 | 0x01024000 | 112KB | bank2 | - Boot **不校验镜像 CRC**(CRC 在 OTA 过程中完成);记录无效/地址越界回退 APP1 - **VTOR**:Cortex-M0 无 SCB->VTOR,用 `PWR->VTOR_REG`。BLE init 前指向 APP 自身 向量表(`0x80000000|0x01008000`);`ns_ble_stack_init()` 之后必须为 0, **绝对不要在 BLE init 后重设 VTOR**(开发日志 §18) - 固件不接 ns_sleep 低功耗(与 FreeRTOS tick 冲突),main 里永久锁睡眠保 SWD ## 4. 版本号与出包约定 - **每改固件必递增** `mcm-ddc-ble/inc/app_version.h` 的 `APP_FW_VERSION` / `APP_FW_VERSION_NUM`(`0x00MMmmpp`,补丁位 +1) - 构建:Keil µVision5(ARMCC V5.06u6),要求 **0 Error 0 Warning** - **出包必须 `UV4 -r` 全量重建**(增量构建产生过砖包,开发日志 §24), 一键 `tools\make_package.bat`,产物在 `tools/out/`: - `mothercup_ble_prod.hex/.bin` — 生产整片包(Boot + 缺省 bootsetting + APP1) - `mothercup_ble_ota.bin` — OTA 发布单文件(52B 头 + 双 bank 载荷,格式见 ble_protocol.md §7) - 烧录:`tools\flash_package.bat`(NSpyocd 整片);Keil 调试下载必须 **Erase Sectors**(整片擦除会杀掉 Boot) - `merge_image.py` 手动调用必须显式传参(默认路径指向已删除的旧工程) ## 5. BLE 接口(详见 ble_protocol.md §1/§2/§5) 广播名 `CAIIC-MCM-20260902`;服务 `00002760-08C2-11E1-9073-0E8AC72E1001`: | 特征尾段 | 属性 | 通道定位 | |---|---|---| | e0001 | Write NoRsp | 帧协议下行(0xCA 字节流) | | e0002 | Notify | 帧协议上行(V1.00.19 修复后可用) | | e0003 | Read+Write | **CLI 通道**:写命令行(≤63B) → 分包读回文本(0 长度包=结束,上限 1024B) | | e0004 | Read | **参数通道**:全信息项 TLV(同 INFO_RSP all) | | e0005 | Read+Write | **OTA 通道**:写一传输帧 → 轮询读回 OTA_RSP(至非空) | - MTU:连接后设备主动发起 247 交换;单次写入上限 = att_mtu − 3 - 帧格式:`CA | TYPE | SEQ | LEN(LE16) | PAYLOAD | CRC16-CCITT(LE)`, **CRC 覆盖 TYPE..PAYLOAD 共 4+payLen 字节**(新旧实现必须统一) - 信息项:0x01 版本 / 0x02 温度(0.1℃) / 0x03 风扇 / 0x04 电压(mV) / 0x05 运行时长(s) / 0x06 堆剩余(B) / 0x07 当前 bank(OTA 选包依据) ## 6. CLI 命令集(UART 与 BLE e0003 完全一致) `help / version / sysinfo / devinfo / blelog / led / appget [json] / appset / bsdump / bsset / appsw [1|2] / ota / reset / factory / uartrst / uartinfo` - 维护区(bootsetting / APP_DATA)**只暴露结构化命令,无裸读写** - `appsw` 切 bank 前校验目标镜像 CRC 与记录一致,不匹配拒绝 - `ota` 命令使 UART 进入二进制 OTA 模式(握手标记行 `[ota] binary mode ON`) - `reset`/`factory`/`appsw` 均先应答、延时后复位(保证应答送达) ## 7. OTA 设计(详见 ble_protocol.md §6/§6.6/§7) - **双 bank 直写**,无中转区;惰性擦扇区;仅 4B 对齐暂存 - **传输层通道无关**:同一 0xCA 帧会话(BEGIN/DATA/END/ABORT → OTA_RSP) 可跑在 BLE e0005 / UART 二进制模式 / 旧帧协议通道;设备侧 OTA 引擎经 通道注册的 sink 出口应答,会话全程只允许一个通道占用 - **逐帧锁步**:每帧等 ack(ack.offset = 设备期望的下一字节);ack 超时直接 重发同一帧(≤3 次);重复帧回 BAD_STATE+期望 offset,恰好等于本帧结束位置 则视为 ack(丢 ack 容错);status=2 按其 offset 重发即再同步 - **END 可靠送达**(V1.00.20):设备先发 ok 应答,等主机取走 + 300ms 宽限 才复位,2s 兜底 - **选包规则**:读 CUR_BANK → 目标 = 对侧 bank → 从头取对应载荷;三重防呆: 客户端向量表预检 + 固件 OTA_END bank_mismatch(status=6) + `appsw` CRC 校验 - 主机兜底:END 应答丢失不等于失败,复位后重连复核 CUR_BANK+FW_VERSION 定论 ## 8. 手机 App 设计(SmartAssiter) ### 8.1 分层 ``` pages/ 页面(home / ota / my / bleTest 终端 / 云端账号页) src/mcm/ 设备业务层(只碰业务,不碰 BLE 细节) McmCli.ts CLI 通道(Promise 链串行;写命令→分包读回) McmInfo.ts 参数通道(TLV 解析,含 CUR_BANK) McmOta.ts OTA 通道(combo 包解析 + 锁步会话 + 重发/再同步/中止) McmCommands.ts 业务命令封装(sysinfo/devinfo/appget/appset…) src/BluetoothManager.ts 蓝牙管理(扫描/连接/服务发现/读写/MTU) common/ api/ token、已连设备记忆、云端 HTTP ``` ### 8.2 通道隔离约定(重要) - **每个特征值一条独立通道**:读请求按 UUID 分键排队/超时(3s)/分发; 写无跨特征阻塞;CLI 与参数轮询与 OTA 可并行,一路故障不扩散 - 安卓兼容(实测踩坑):服务发现后 300ms 稳定期;读/写被误报 10007 "property not support" 时各延时 1600ms 重试一次;连接初始化 **先 setMtu 再发起读写**(并发 GATT 操作会被安卓无声丢弃) - home 页参数轮询由 **Auto refresh 开关**控制,**缺省关闭**(避免干扰 OTA) - App 侧错误码:0 成功;10000 连接失败;10001 断开;10002 蓝牙未开; 10003 无权限;10004/10005 服务/特征缺失;10008 读写/MTU/超时 ### 8.3 OTA 页文件读取(Android 10+ 分区存储) - `/sdcard/Download` 直读被禁(targetSdk≥29);`uni.chooseFile` App 端不存在 - 现行方案:**SAF 选择器**(plus.android `ACTION_GET_CONTENT`,免权限)→ ContentResolver 流 → Scanner 整流读 String(ISO-8859-1 字节 1:1)→ **magic 自验**,不过则自动逐字节 `read()` 直读(二进制精确兜底) - plus 桥注意:**byte[] 参数按值拷贝,Java 侧写入不会回传 JS**; Java 对象方法一律用 `plus.android.invoke(obj, name, args...)` 调用; App 端 vue3 无 `window`,plus 挂在 `globalThis.plus` - 备用:adb push 到 `_doc/`(应用私有目录)后走手动路径 ## 9. PC 工具(tools/) | 工具 | 用途 | |---|---| | `make_package.bat` | 一键出包(UV4 -r 全量重建 Boot+APP → merge_image.py) | | `merge_image.py` | 合并整片包 + 生成 OTA 发布件(自动取版本号);**手动调用必须显式传参** | | `flash_package.bat` | NSpyocd 整片烧录(需 NS-LINK,先关 Keil) | | `ble_ota_update.py` / `ble_ota.bat` | **OTA 参考实现**(BLE 默认,`--uart COMx` 串口);锁步/重发/回连复核的标准实现,新主机端照此移植 | | `ble_cli_test.py` / `ble_cli.bat` | BLE CLI 测试客户端(帧协议 + `-rw` 读写特征两种模式) | | `ble_temp_watch.py` | 温度监测 | Python 环境:`tools/.venv-ble`(bleak/pyserial);本机 `python` 可能是商店占位 stub,脚本内已处理全路径查找。 ## 10. 扩展指南 ### 10.1 加一条 CLI 命令 1. `mcm-ddc-ble/src/cli_core.c` 的 `s_cmds[]` 加 `{name, help, handler}`,实现 handler (输出经 `cli_printf`,自动同时可用 UART 与 BLE e0003) 2. 递增版本号 → 出包 → 烧录验证 3. App 侧如需业务封装,在 `McmCommands.ts` 加函数(命令名常量集中在顶部) ### 10.2 加一个信息项(温度/转速类遥测) 1. 固件 `app_info.c` 加数据源,`app_ble_proto.c` 的 TLV 组装加一项(新 id) 2. 协议文档 §5 表格登记;App `McmInfo.ts` 的 `parseTlv` 加分支 + `DeviceInfo` 加字段(旧 App 会按 len 跳过未知 id,天然兼容) 3. 版本号 + 出包 + 修改记录 ### 10.3 加一个 GATT 特征 1. 固件 `app_rdtss.c` 的 `app_rdts_att_db` 加声明+值条目(注意 `RDTSS_IDX_NB`), UUID 取 `...e0006` 起顺延;读写回调在 `app_ble_proto.c` 按 att_idx 分发 2. App `BluetoothManager.ts` 加 UUID 常量与读写封装;`scanServices` 校验清单 按需更新(**注意:校验清单变严会让旧固件连不上,考虑兼容性**) 3. 手机端 GATT 缓存注意:特征表变更后若手机行为异常,系统蓝牙关开/ 忽略设备清缓存 ### 10.4 加一条 OTA 传输通道 设备侧 OTA 引擎与通道解耦:新通道只需①把收到的字节流喂给帧重组器、 ②注册应答 sink。参考 UART 通道(`ota` 命令模式切换)与 BLE e0005 通道 (写帧→读应答轮询)。主机端参考 `tools/ble_ota_update.py` 与 `SmartAssiter/src/mcm/McmOta.ts`。 ### 10.5 加 APP_DATA 参数字段 1. `app_params.h` 的 `caiic_params_t` 加字段(保持 magic+CRC 结构;reserved 有余量,优先占用 reserved 保持记录长度 52B 不变) 2. `appget/appset` 命令加 key;JSON 模式同步 3. App `McmCommands.ts` 的 `AppParams` 类型与解析同步 ### 10.6 App 加一个页面/业务 - BLE 一律走 `BluetoothManager` 的 `MyApiResult` 回调约定;设备业务封装进 `src/mcm/`;不改 `uni_modules` 官方组件;页面注意**缩进用 tab** - HBuilderX 运行调试;云端接口走 `api/httpInstance.ts`(SERVER_LIST 三地址) ## 11. 验证与排障手段 1. Keil 命令行构建 0 Error/0 Warning;出包核对 map 的 `__initial_sp` 与 bin 向量表初始 SP 一致 2. 串口 banner/`caiic->`/`devinfo` 人工确认;`blelog on` 抓 BLE 帧(走 printf 不经 BLE,避免自激) 3. BLE 链路对照:PC `ble_cli_test.py` / `ble_ota_update.py` 与手机 App 互验 4. App 侧控制台日志体系:`[MCM]`(连接/tx/rx hex/超时/重试)、`[CLI]`、 `[INFO]`、`[OTA]`(帧)、`[OTA-PAGE]`(选包/升级流程)、`[HOME]` 5. 变砖恢复:SWD 重烧整片包 ## 12. 铁律清单(变更前必读) 1. IRAM 从 0x20004000 起;BLE init 后 VTOR 必须为 0 且不再重设 2. PA4/PA5(SWD)禁止占用;不用 cmsis_os 封装;SysTick 由 FreeRTOS port 接管 3. USART1_IRQn 优先级 3(ISR 内调 FromISR API 的硬性要求);BLE_SW/FIFO IRQ 优先级 0 且禁止 FromISR API 4. ke_msg/ke_timer 只允许在 BLE 调度任务上下文运行;CLI 输出经环形缓冲由 BLE 任务发上行 5. 出包必须 UV4 -r 全量;Keil 下载只能 Erase Sectors;每改固件必升版本号 6. 帧 CRC16 覆盖 4+payLen 字节;OTA 逐帧锁步;END 应答送达后才复位 7. 安卓 BLE:初始化先 MTU 后读写;10007 重试;通道隔离;OTA 期间不开轮询 8. 固件重大改动追加 `docs/开发日志.md`;协议改动同步 `docs/ble_protocol.md`; App 改动写 `SmartAssiter/docs/修改记录_日期_主题.md`;同步更新 `AGENTS.md`