/** ****************************************************************************** * @file ota_srv.h * @author wangfq * @version V1.0 * @date 2026-08-20 * @brief DLD960 Loop MCU 远程 OTA 服务 (MQTT 协议 V1.08, ROADMAP P1.4 ①) * * 先存后刷: MQTT 分片下载 → W25Qxx 暂存区 (0x010000 起 512KB) → * 全镜像 CRC32 复核 → 本地 0x9F ISP 透传刷写 Loop MCU * * 协议依据: docs/DLD960_IoT_MQTT协议.md §4.19~4.24 / §5.5 * 设计稿: docs/DLD960_MQTT_OTA协议.md V1.01 ****************************************************************************** */ #ifndef __OTA_SRV_H__ #define __OTA_SRV_H__ #include /*=========================================================================== * 分区常量 (与 offlog.h: OFFLOG_PARAM_SIZE=64KB + OFFLOG_OTA_SIZE=512KB) * OTA 区 = 0x010000 ~ 0x090000 (512KB) * 布局: 元数据扇区(4KB) + Slot A(100KB) + Slot B(100KB) + 预留(308KB) *===========================================================================*/ #define OTA_AREA_BASE 0x10000UL /* 0x010000 (参数区 64KB 后) */ #define OTA_AREA_SIZE 0x80000UL /* 512KB */ #define OTA_META_BASE (OTA_AREA_BASE + 0x0000UL) /* 元数据主备份 */ #define OTA_META_BACKUP (OTA_AREA_BASE + 0x0200UL) /* 元数据备份 (同扇区) */ #define OTA_META_SECTOR (OTA_AREA_BASE / 4096UL) /* 元数据所在扇区号 */ #define OTA_SLOT_A_BASE (OTA_AREA_BASE + 0x1000UL) /* 0x011000 */ #define OTA_SLOT_B_BASE (OTA_AREA_BASE + 0x1A000UL) /* 0x02A000 */ #define OTA_SLOT_SIZE (100UL * 1024UL) /* 100KB */ #define OTA_MAX_IMAGE (96UL * 1024UL) /* 镜像上限 96KB (留 4KB 余量) */ /* 下载单片 (协议常量, 256B 页对齐) */ #define OTA_CHUNK_SIZE 256 /* 刷写块 (0x9F LEN 为 uint8 → DATA ≤ 254B; 取 248B) */ #define OTA_FLASH_BLOCK 248 /*=========================================================================== * 会话状态 *===========================================================================*/ enum { OTA_STATE_IDLE = 0, /* 空闲 */ OTA_STATE_DOWNLOADING, /* 下载中 */ OTA_STATE_READY, /* 校验通过, 可刷写 */ OTA_STATE_FLASHING, /* 刷写中 */ OTA_STATE_FLASH_FAILED, /* 刷写失败 (保留镜像可重试) */ OTA_STATE_ABORTED, /* 已中止 */ }; /* 刷写子状态 (非阻塞 tick 驱动) */ enum { OTA_FLASH_IDLE = 0, OTA_FLASH_START, /* 发 A5 启动帧 */ OTA_FLASH_WAIT_READY, /* 等 pre_ok */ OTA_FLASH_ADDR, /* 发 A6 地址帧 */ OTA_FLASH_WAIT_ADDR, /* 等 addr_ok */ OTA_FLASH_DATA, /* 发 A7 数据块 */ OTA_FLASH_WAIT_ACK, /* 等 data ACK */ }; /* OTA 错误码 (event_report type=ota_error 的 value, 协议 §5.3) */ #define OTA_ERR_READY_TIMEOUT 0x1001 /* 启动帧无响应 (pre_ok 超时) */ #define OTA_ERR_ADDR_FAIL 0x1002 /* 地址帧错误 (addr_err / 超时) */ #define OTA_ERR_DATA_ACK_LIMIT 0x1003 /* 数据块 ACK 超限 (重试×3 耗尽) */ #define OTA_ERR_CRC_FAIL 0x1004 /* 全镜像校验失败 */ #define OTA_ERR_FORCE_FAIL 0x1005 /* 安全窗口拒绝后强制失败 */ /*=========================================================================== * 元数据结构 (56B, 写 64B 槽) *===========================================================================*/ typedef struct { uint32_t magic; /* OTA_MAGIC 'DLD9' */ uint32_t state; /* OTA_STATE_* */ uint8_t target; /* 0=loop (1=dbn 预留) */ uint8_t slot; /* 0=A, 1=B */ uint16_t rsv; uint32_t size; /* 镜像字节数 */ uint32_t crc32; /* 全镜像 CRC32 */ uint32_t received; /* 已下载字节数 (片对齐, 断点续传依据) */ uint32_t last_result; /* 上次刷写结果: 0=无/成功, 非0=错误码 */ uint32_t begin_ts; /* 会话开始 (同步后 Unix 秒, 0=未同步) */ char version[16]; /* 目标固件版本 (审计) */ uint32_t flash_cnt; /* 刷写尝试次数 */ uint32_t rsv2; } OtaMeta; /* 56B */ #define OTA_MAGIC 0x39444C44UL /* 'DLD9' */ /*=========================================================================== * API *===========================================================================*/ /* 初始化: 读元数据 (双备份校验), 刷写状态机复位 */ void ota_init(void); /* 主循环轮询: 刷写状态机 (非阻塞 tick 驱动, 绝不阻塞主循环) */ void ota_poll(void); /* OTA 会话期间静默标志: downloading/flashing 时置位 → event_report 暂停发送 (队列积压) + offlog/快照落盘暂停 */ uint8_t ota_silent_active(void); /* 当前会话元数据 (只读, MQTT 响应组装用) */ const OtaMeta *ota_meta(void); /* 状态字符串 (JSON state 字段) */ const char *ota_state_str(uint8_t state); /*=========================================================================== * MQTT 命令入口 (iot_mqtt_srv.c 调用, 主循环上下文) * 返回 0=成功, 非0=错误码 (1=参数 3=忙 5=内部; data.err_code 用 OTA_ERR_*) *===========================================================================*/ int ota_cmd_begin(uint32_t size, uint32_t crc32, const char *version, uint8_t force); int ota_cmd_data(uint32_t offset, uint32_t crc32, const char *hex); int ota_cmd_end(uint32_t crc32); int ota_cmd_abort(void); int ota_cmd_flash(uint8_t slot, uint8_t force); /* 分片落盘用: hex → bin (n 字节), 成功 0 */ int ota_hex_decode(const char *hex, uint8_t *out, uint32_t n); /* CRC-32/ISO-HDLC (逐位实现, 零查表 RAM) */ uint32_t ota_crc32(const uint8_t *data, uint32_t len); /*=========================================================================== * 本地刷写与 UART2 联动 (usart_biz.c 使用) *===========================================================================*/ /* 1 = 本地刷写进行中: uart_srv OTA 分支把 0x9F ACK 交给 ota_flash_feed_ack, 而非透传回 BLE (extern 定义于 ota_srv.c) */ extern uint8_t g_ota_flash_active; /* usart_srv 收到 bootloader 0x9F ACK 帧时调用 (主循环上下文) */ void ota_flash_feed_ack(const uint8_t *pkg, uint16_t len); /* 刷写进度 (已送字节 / 总量, ota_report 用) */ uint32_t ota_flash_sent(void); uint32_t ota_flash_total(void); #endif /* __OTA_SRV_H__ */