Skip to content

OTA 升级架构

设备固件远程升级的端到端架构设计,涵盖固件存储、分发、升级流程和异常处理。

时序图见 02.核心业务流程 第 5 节,MQTT Topic 见 06.MQTT通信协议 3.3 节,API 接口见 08.API接口规范 固件升级部分。


1. 整体架构

数据流: 运维人员通过 Web 后台上传固件 → 存储到 OSS → 后端通过 MQTT 通知设备 → 设备通过 HTTPS 下载 → 校验安装 → 重启上报新版本。

四个参与方:

参与方职责
Web 后台固件上传入口、升级任务管理、进度展示
后端服务固件存储代理、升级任务调度、MQTT 通知下发、进度汇总
OSS/MinIO固件文件持久化存储,提供 HTTPS 下载
设备 ESP32接收升级通知、HTTPS 下载固件、MD5 校验、写入 OTA 分区、重启

2. 固件存储方案

方案优点缺点结论
阿里云 OSS高可用、CDN 加速、免运维有费用、依赖公网生产环境推荐
MinIO自托管、S3 兼容、无流量费需自行维护高可用内网/开发环境
本地文件系统零依赖不支持分布式、扩容困难仅单机测试

选型建议: 开发/内网用 MinIO,生产用阿里云 OSS。二者均为 S3 兼容 API,后端代码可无缝切换。

存储路径规范: firmware/{productKey}/{version}/{filename}.bin

例如:firmware/p_relay_4ch/1.1.0/relay_4ch_v1.1.0.bin


3. 升级流程拆解

分五个阶段,补充时序图中未展开的技术细节。

阶段一:固件上传

  1. Web 后台上传 .bin 文件
  2. 后端校验文件格式:检查 ELF 头/magic bytes,确认是否为有效 ESP32 固件
  3. 计算文件 MD5 和大小
  4. 上传到 OSS/MinIO,获取文件 URL
  5. 在 PostgreSQL 记录固件版本元数据:
字段说明
productKey适用产品
version语义化版本号
urlOSS 下载 URL
size文件大小(字节)
md5文件 MD5 校验值
changelog版本更新说明
uploadTime上传时间

阶段二:升级通知

  1. 运维选择目标设备:单设备 / 批量 / 按产品全量
  2. 创建升级任务,后端通过 MQTT 向每个目标设备发布 sys/{pid}/{did}/thing/ota/upgrade
  3. Payload 含固件 URL、版本号、大小、MD5
  4. 离线设备处理: 标记任务为 pending,设备下次上线首次属性上报时,后端检查是否有待执行 OTA 任务,有则补发通知

阶段三:固件下载

  1. 设备收到通知,通过 HTTPS GET 从 OSS 下载固件
  2. 断点续传: 使用 HTTP Range 请求头,ESP-IDF 的 esp_https_ota 组件原生支持。下载中断后下次从断点继续,不需要重新下载
  3. 每下载 10% 通过 MQTT 上报进度到 thing/ota/progress(Payload 格式见 MQTT 协议文档 3.3 节)
  4. 后端通过 WebSocket 实时推送进度到 Web 后台

阶段四:校验与安装

  1. 下载完成后,设备计算文件 MD5,与通知中的值比对
  2. 校验通过:
    • 写入 OTA 分区(ESP32 双分区机制:当前运行 active 分区,OTA 写入 inactive 分区)
    • 设置启动标记(bootloader 识别下次启动使用哪个分区)
    • 上报 progress=100, desc="flashing"desc="done"
  3. 校验失败:
    • 上报 desc="verify_failed"
    • 终止升级,可选自动重试(最多 2 次,每次重新下载)

阶段五:重启与确认

  1. 设备调用 esp_restart() 重启
  2. Bootloader 检测启动标记,切换到新固件分区
  3. 连接 WiFi + MQTT,属性上报时携带新固件版本号
  4. 后端比对版本号:
    • 匹配目标版本 → 标记升级任务成功
    • 不匹配(自动回滚到旧版)→ 标记任务失败
  5. ESP32 自带回滚机制: 新固件启动后若在 watchdog 时间内未确认稳定运行,自动回滚到旧版本,无需人工介入

4. 固件版本管理

版本号规范

采用语义化版本:主版本.次版本.修订号(如 1.1.0)。设备当前版本记录在 PostgreSQL Device 表的 firmwareVersion 字段中。

升级类型

类型说明示例
强制升级安全漏洞修复、协议不兼容变更旧版本设备必须升级,否则无法连接
可选升级功能新增、Bug 修复用户自行决定是否升级

版本回滚

  • 设备端: ESP32 双分区自动回滚(新固件启动失败 → bootloader 切回旧分区)
  • 云端主动回滚: 发起一次"升级到旧版本号"的 OTA 任务即可,流程完全相同

5. 批量升级与灰度

批量升级

参数限制说明
单次任务最大设备数500 台避免瞬时带宽冲击
分批间隔30 秒逐批下发通知,防止 OSS 带宽被打满
Web 后台展示总设备数 / 成功 / 失败 / 进行中 / 等待中全局进度实时刷新

灰度策略(预留 v0.5+)

  • 按设备比例分批:第一批 10% → 观察 1 小时无异常 → 第二批 30% → 全量
  • 灰度期间出现异常(失败率 > 5%)→ 自动暂停任务,防止影响全部设备

6. 异常处理

异常场景处理方式
设备下载中断(WiFi 断开)支持断点续传(HTTP Range),ESP-IDF 原生支持;设备下次上线后从断点继续
MD5 校验失败设备上报 verify_failed,后端自动重试下发 2 次,仍失败则标记任务失败
新固件启动后崩溃ESP32 bootloader watchdog 检测,自动回滚到旧固件;设备重新上线后上报旧版本号
设备存储空间不足下载前先检查剩余 OTA 分区空间,不足则拒绝下载并上报错误(ESP32 OTA 分区大小由编译时决定,正常不会出现)
OSS 不可用设备下载失败 → 上报错误 → 后端记录异常 → 运维检查 OSS 状态后重试
升级期间设备断电重启后 bootloader 检测 OTA 分区未完整写入 → 自动回滚到旧固件 → 云端收到旧版本号,判定任务失败

7. 安全考虑

措施说明优先级
HTTPS 下载设备必须通过 HTTPS 从 OSS 下载固件,防止中间人篡改P0
MD5 校验设备端验证固件完整性P0
预签名 URL固件文件默认私有,设备通过预签名 URL 下载(默认 24 小时过期),避免暴露 OSS BucketP1
固件签名上传固件时后端用私钥签名,设备端用内置公钥验签,防止恶意固件刷入P2(预留 v0.5+)

8. 与 MQTT Topic 的对应

流程阶段MQTT Topic方向
通知设备sys/{pid}/{did}/thing/ota/upgrade云端 → 设备
进度上报sys/{pid}/{did}/thing/ota/progress设备 → 云端

Payload 格式详见 06.MQTT通信协议 3.3 节。