From 9d6f5ea4124e8b70dd6517b5695ff07813a7630a Mon Sep 17 00:00:00 2001 From: wangfq Date: Thu, 20 Aug 2026 11:37:38 +0800 Subject: [PATCH] =?UTF-8?q?docs(vd=5F960):=20MQTT=20OTA=20=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE=E8=AE=BE=E8=AE=A1=E7=A8=BF=E6=8C=89=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E6=84=8F=E8=A7=81=E4=BF=AE=E8=AE=A2=20=E2=86=92=20V1.01?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Slot A/B 容量 60KB→100KB (镜像上限 96KB, 为 DBN CH32V208 镜像预留) - 删除继电器 GPIO 默认态待验证项, 维持 Loop 现状 (与 BLE OTA 行为一致, 不额外干预) - 新增会话期间静默: 暂停 MQTT event_report 发送 (队列积压结束补发) + offlog/快照落盘 (开始/结果日志除外) - 刷写调度明确非阻塞 tick 驱动, 刷写窗口内 MQTT 保活 (PINGREQ/心跳/IWDG 喂狗) 不受影响 - 待板上验证项重新编号 (删 1 项, 保活项升级为实现约束+验证) --- docs/DLD960_MQTT_OTA协议.md | 45 +++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/docs/DLD960_MQTT_OTA协议.md b/docs/DLD960_MQTT_OTA协议.md index 02b96ef..e5e07ff 100644 --- a/docs/DLD960_MQTT_OTA协议.md +++ b/docs/DLD960_MQTT_OTA协议.md @@ -1,6 +1,6 @@ # DLD960 MQTT 远程 OTA 协议(Loop MCU 先存后刷) -> 文档版本 V1.00(设计稿)· 2026-08-20 · 适用范围:并入《DLD960 IoT 接口协议(MQTT + JSON)》V1.08 后生效 +> 文档版本 V1.01(设计稿·按修改意见修订)· 2026-08-20 · 适用范围:并入《DLD960 IoT 接口协议(MQTT + JSON)》V1.08 后生效 > 依据:`docs/ROADMAP.md` P1.4 ① —— Loop MCU (AT32F421) 远程 OTA,v1.2.x 落地 > 设计原则:**下载阶段纯 MQTT 分片 → W25Qxx 暂存;刷写阶段纯本地 ISP 透传(复用 BLE OTA 0x9F 状态机)**;传输层无关,4G 通道原样复用。 @@ -25,7 +25,7 @@ | 刷写窗口 | 与传输时间重合(网络慢则窗口长) | 下载完成后刷写纯本地,窗口短且可控 | | 校验 | 依赖传输层(BLE 无 CRC) | 单片 CRC32 + 全镜像 CRC32 双重校验 | | 回滚 | 无 | 暂存区 Slot A/B 双槽,保留上一版重刷 | -| 安全窗口 | 无检查 | 刷写前检查无车压线圈、继电器强制断开 | +| 安全窗口 | 无检查 | 刷写前检查无车压线圈;继电器维持 Loop 现状(与 BLE OTA 一致) | ## 1.3 范围 @@ -62,7 +62,7 @@ | 下载单片原始字节 | **256B** | 2 的幂 + W25Qxx 页(256B)对齐;hex 512 字符 + JSON 外壳 ≈ 640B < `IOT_MQTT_RECV_BUF_LEN=1024` | | 单片 CRC | **CRC32**(256B 独立计算) | 与全镜像同算法,查表实现复用 | | 全镜像 CRC | **CRC32**(offset 0 ~ size-1 连续) | 标准 CRC-32/ISO-HDLC | -| 镜像上限 | **60KB** | Loop APP 区 0x08003400~0x08010000 = 51KB 硬上限;暂存 Slot 数据区 60KB 留余量 | +| 镜像上限 | **96KB** | Slot 数据区 100KB 预留 4KB 边界余量;Loop APP 区 0x08003400~0x08010000 = 51KB 硬上限绰绰有余,100KB 槽为 DBN (CH32V208) 自身镜像(~100KB APP 区)预留 | | 下载片序号语义 | offset 绝对字节偏移(256 对齐) | 支持乱序/重复片幂等处理 | | 断点续传 | ota_begin 返回已接收字节数(片对齐) | 设备持久化 received 到元数据 | @@ -72,8 +72,9 @@ |------|-----|------| | A7 数据块 | **≤254B**(实现取 248B) | bootloader `LEN` 为 uint8 → DATA = LEN-1 ≤ 254(iap.c `CMD_9F_DATA_LEN`) | | 块间超时 | 1s(可配) | 0x9F 停等协议,超时重发该块 | +| 刷写调度 | **非阻塞 tick 驱动** | 刷写状态机挂主循环轮询,每轮最多发送 1~2 块并检查 ACK,绝不阻塞主循环 → 刷写窗口内 MQTT PINGREQ/心跳/IWDG 喂狗节奏完全不受影响 | | 刷写重试 | 每块失败重发 ×3,整体失败重试 ×3 | ROADMAP 安全底线 | -| 刷写速率 | 248B/帧 ≈ 13ms + ACK,~20ms/帧 | 64KB ≈ 265 帧 ≈ **6~8s 窗口** | +| 刷写速率 | 248B/帧 ≈ 13ms + ACK,~20ms/帧 | 64KB ≈ 265 帧 ≈ **6~8s 窗口**(Loop 典型 40~50KB 更快) | | 升级触发 | DBN 发 `9F 01 00 01 A5 A7` → Loop APP 写 `IAP_UPGRADE_FLAG_9F=0x444C4439` → 复位 | 复用 BLE OTA 启动帧(dbn_ble_srv.c check_pkg 同款) | ## 3.3 CRC32 算法定义(必须双方一致) @@ -90,15 +91,19 @@ ``` 0x010000 ┌─────────────────────────┐ │ OTA 元数据扇区 (4KB) │ SlotA/SlotB 头 + 会话状态 + 审计 -0x010400 ├─────────────────────────┤ - │ Slot A 数据区 (60KB) │ 当前新镜像(bin 原始字节) -0x020400 ├─────────────────────────┤ - │ Slot B 数据区 (60KB) │ 上一版镜像(回滚重刷) -0x030400 ├─────────────────────────┤ - │ 预留 384KB │ DBN 自身镜像 / 4G 扩展 +0x011000 ├─────────────────────────┤ + │ Slot A 数据区 (100KB) │ 当前新镜像(bin 原始字节) +0x02A000 ├─────────────────────────┤ + │ Slot B 数据区 (100KB) │ 上一版镜像(回滚重刷) +0x043000 ├─────────────────────────┤ + │ 预留 308KB │ DBN 自身镜像 / 4G 扩展 0x090000 └─────────────────────────┘ ``` +- Slot A/B 各 **100KB 数据区**(`0x011000`/`0x02A000` 起,4KB 对齐),镜像上限 96KB(预留 4KB 边界余量) +- 100KB 槽位容量同时覆盖 DBN (CH32V208) 自身镜像(APP 区约 100KB),为 P1.4 ② 预留 +- 4KB 边界余量:防止镜像写满后越界擦到下一槽;元数据/数据区起始均 4KB 扇区对齐 + 元数据结构(建议,实现可调,不进入协议字段): ```c @@ -165,7 +170,7 @@ typedef struct { /* 64B */ | data 字段 | 类型 | 说明 | |-----------|------|------| | `target` | string | `loop`(当前支持);`dbn` 预留 | -| `size` | uint32 | 镜像 bin 字节数(≤ 61440) | +| `size` | uint32 | 镜像 bin 字节数(≤ 98304 = 96KB) | | `crc32` | uint32 | 全镜像 CRC32(十进制) | | `version` | string | 目标固件版本(写入元数据,审计用;bootloader 不校验版本) | | `force` | bool | `true` = 覆盖现有暂存镜像 / 忽略冲突(默认 false) | @@ -309,7 +314,8 @@ typedef struct { /* 64B */ 2. 元数据非 ready → `code=3`(先 `ota_end` 完成校验) 3. 通过 → 立即回 `code=0`(异步),进入刷写: - 写 offlog 事件日志:`固件升级开始`(target/slot/version/size) - - 下发 0x7F 命令尝试关闭 Loop 全部继电器(失败不阻塞——bootloader 复位后 GPIO 默认态=断开安全侧,**待板上验证**) + - **暂停事件上报与脱机日志(会话期间)**:MQTT `event_report` 暂停发送(入队积压,16 深溢出丢最旧,会话结束恢复后补发);offlog/快照落盘暂停——理由:刷写期间 Loop 复位不产生检测事件,DBN 让出 SPI 总线/主循环给刷写状态机,保证保活与喂狗 + - 维持 Loop 现状:复位进 bootloader 后的 GPIO/继电器状态与现网 BLE OTA 升级完全一致(该通道已验证可用),不额外干预 - 发 `9F 01 00 01 A5 A7` 启动帧 → Loop APP 写 flag 复位 → bootloader 回 pre_ok - 发 A6 地址帧(`0x08003400` 4 字节大端)→ addr_ok - 从 W25Qxx 读镜像,按 248B/块发 A7(停等 ACK,1s 超时重发 ×3) @@ -425,13 +431,14 @@ typedef struct { /* 64B */ | # | 措施 | 说明 | |---|------|------| | 1 | **升级窗口检查** | `ota_flash` 前检查 4 通道无车;有车 → `code=3`,平台提示延迟。`force=true` 可跳过(高风险,需平台权限控制) | -| 2 | **继电器安全侧** | 刷写前尝试 0x7F 关闭全部继电器;bootloader 复位后 GPIO 默认态应为断开(常开安全侧)——**待板上验证** | +| 2 | **维持 Loop 现状** | 刷写期间 Loop 行为与现网 BLE OTA 升级一致(复位进 bootloader → ISP 刷写 → 复位跑新 APP),继电器 GPIO 状态按当前硬件实测状态接受,不额外干预(BLE 通道已验证可用) | | 3 | **双重 CRC** | 单片 CRC32(传输层抓错)+ 全镜像 CRC32(落盘完整性);刷写前读回复核 | | 4 | **失败可重入** | 下载失败 → 断点续传;刷写失败 → 重试 ×3 → 保留镜像可重刷;AT32 ISP bootloader 兜底,不会真砖 | | 5 | **版本回滚** | Slot A/B 双槽;平台可 `ota_begin` 指定槽刷旧版(`force=true` 覆盖) | | 6 | **审计留痕** | 升级开始/结果写 offlog 事件日志(含目标版本、slot、结果码);平台侧 version 字段归档 | | 7 | **能力探测** | 老固件 `ota_*` 回 `code=4`;平台按 `ota_status`/`dev_info_query.soft_ver` 判断,不盲目下发 | | 8 | **幂等与防呆** | 重复片幂等 ACK;offset 乱序拒绝并要求续传;同版本默认拒绝重刷(force 绕过) | +| 9 | **会话期间静默** | OTA 会话期间(begin ~ 结束)暂停 MQTT `event_report` 发送(队列积压,结束后补发)与 offlog/快照落盘("升级开始"日志在暂停前写入、"升级结果"在恢复后补记);避免刷写窗口与上报/日志抢 SPI 总线与主循环 | --- @@ -466,12 +473,11 @@ typedef struct { /* 64B */ | # | 项 | 影响 | |---|-----|------| -| 1 | Loop 复位后继电器 GPIO 默认态是否断开(安全侧) | 安全底线 2 | -| 2 | DBN UART2 TX 缓冲容量(能否容纳 254B A7 帧;现 BLE 透传块 ≤94B) | 刷写块大小 | -| 3 | W25Qxx 写 256B 页 + 4KB 扇区擦除在 MQTT 接收回调内的耗时(阻塞窗口 vs MQTT 保活) | 单片落盘是否需移主循环 | -| 4 | DBN RAM 预算:镜像块缓冲(248B)+ CRC 查表(1KB)是否挤占现有栈余量(历史 .bss 事故) | 内存方案 | -| 5 | Loop APP 当前固件实际 bin 大小(验证 60KB 槽余量) | 槽位容量 | -| 6 | 刷写期间 MQTT 保活(6~8s 窗口内 PINGREQ/心跳是否正常,IWDG 喂狗节奏) | 刷写中断安全 | +| 1 | DBN UART2 TX 缓冲容量(能否容纳 254B A7 帧;现 BLE 透传块 ≤94B) | 刷写块大小 | +| 2 | W25Qxx 写 256B 页 + 4KB 扇区擦除在 MQTT 接收回调内的耗时(阻塞窗口 vs MQTT 保活) | 单片落盘是否需移主循环 | +| 3 | DBN RAM 预算:镜像块缓冲(248B)+ CRC 查表(1KB)是否挤占现有栈余量(历史 .bss 事故) | 内存方案 | +| 4 | Loop APP 当前固件实际 bin 大小(验证 100KB 槽余量) | 槽位容量 | +| 5 | 刷写期间 MQTT 保活验证(设计已保证非阻塞 tick 驱动,PINGREQ/心跳/IWDG 喂狗不受阻塞;板级抓包确认 6~8s 窗口内无 PINGREQ 超时断连、IWDG 不复位) | 刷写中断安全 | --- @@ -498,3 +504,4 @@ typedef struct { /* 64B */ | 版本 | 时间 | 说明 | |------|------|------| | V1.00 | 2026-08-20 | 设计稿:Loop MCU MQTT 远程 OTA 先存后刷协议(依据 ROADMAP P1.4 ①) | +| V1.01 | 2026-08-20 | 按修改意见修订:①Slot A/B 容量 60KB→**100KB**(镜像上限 96KB,为 DBN 镜像预留)②删除"继电器 GPIO 默认态"待验证项,维持 Loop 现状(与 BLE OTA 行为一致,不额外干预)③新增"OTA 会话期间静默"约束:暂停 MQTT `event_report` 发送(队列积压结束后补发)+ offlog/快照落盘(开始/结果日志除外)④刷写调度明确**非阻塞 tick 驱动**,刷写窗口内 MQTT 保活(PINGREQ/心跳/IWDG 喂狗)不受影响 |