适用人群:把前四篇(全量 / Bootloader / 差分 / 签名)都看过的同学
读完你能得到:一份把四篇串起来的大规模 OTA 上线清单,以及一份"故障场景 → 怎么扛"的对照表。照着做,你的 OTA 才敢推给 10 万台设备。
一、前四篇各解决了一个问题,但量产要"全用上"
| 篇 | 解决什么 | 不用会怎样 |
|---|---|---|
| OTA_01 全量 | 设备能远程升级(A/B + 回滚) | 只能抱着线去现场刷 |
| OTA_02 Bootloader | STM32 自己跳过去 | 依赖出厂 Bootloader,业务绑死 |
| OTA_03 差分 | 升级省 80%~95% 流量 | 4G 上每月烧几万到百万 |
| OTA_04 签名 | 拒绝非法/篡改固件 | 别人随便给你刷木马 |
但真有 10 万台设备在线时,光有这四样还不够:版本怎么管理?推全量还是灰度?网络断了怎么续传?一台变砖你能不能发现?
这篇就是把它们组装成一套可上线的方案,并补上前面没展开的运营细节。
二、一套完整的大规模 OTA 长啥样
┌──────────────────── 服务器 / CI ────────────────────┐ │ 编译 → 签名(OTA_04) → 差分(OTA_03) → 生成版本清单 │ │ 版本清单: {ver, full_url, delta_url(基于上一版), sig} │ │ 灰度策略: 按 device_id 哈希决定谁能升、升到哪版 │ └───────────────────────────┬──────────────────────────┘ │ HTTPS + 签名 ┌───────────────────────────┴──────────────────────────┐ │ 设备端(每台) │ │ 1. 上报当前版本 + 设备ID │ │ 2. 问服务器: 我有 v1.2,能升到哪版?(灰度判定) │ │ 3. 选 full 或 delta(需本地有匹配旧版) │ │ 4. 断点续传下载(Range) + 边下边验签(OTA_04) │ │ 5. 写非活跃分区 A/B(OTA_01/02) │ │ 6. 切换启动 + 自检 + mark valid(防回滚) │ │ 7. 上报结果: 成功/失败/回滚 + 错误码 │ └───────────────────────────────────────────────────────┘四篇的技术,全部嵌进第 2~6 步了。下面按主题给可勾选的清单。
三、量产 Checklist(照着逐条打勾)
3.1 版本号与防回滚(来自 OTA_04 第 6 坑)
- 每个固件嵌入单调版本号(如
0x010002= v1.2),写在固定偏移、参与签名 - 设备把"已接受的最高版本"存eFuse / 写保护 Flash,不存普通区
- 升级前比版本:服务器版 ≤ 当前版 或 ≤ 最高已接受版 → 直接拒绝,杜绝降级攻击
- 新固件启动成功后才更新最高已接受版本(和
mark valid一起做)
3.2 灰度发布(Canary / 分批)
- 绝不 100% 一把推。分级:内部 1% → 5% → 20% → 50% → 100%
- 每级之间设健康闸门:砖化率 / 崩溃率超阈值(如 >0.5%)→自动熔断暂停
- 谁能升由服务器按
hash(device_id) % 100或白名单决定,设备端不自己判断 - 保留"紧急全量回退"开关(一键把清单指回旧版)
3.3 断点续传(来自 4G/NB-IoT 必现的断连)
- 下载用HTTP
Range: bytes=N-,设备记录已收字节数,断线从 N 续,不重头下 - 差分场景:记录"已写到第几个块",续传从最后一块继续
- 下载中途断电:重启后旧固件完好(A/B),重新走下载流程即可
3.4 安全(来自 OTA_04)
- 传输走HTTPS(明文 HTTP 在生产环境不可接受)
- 固件签名验签(ECDSA P-256),验不过硬拒绝、不下跳
- 公钥锁eFuse/OTP,配合 RDP/WRP/PCROP
- 有密钥轮换预案:私钥泄露怎么作废、怎么发新公钥固件
3.5 A/B 与回滚(来自 OTA_01/02)
- 新固件"未提交"状态启动,自检 OK 才 mark valid,否则自动回滚
- 设回滚计数器:连续回滚 N 次 → 放弃本次版本,避免无限重启环路
- 看门狗兜底:规定时间内没起来 → 复位并回滚
3.6 可观测性(生产最容易被忘)
- 设备上报:当前版本、升级结果(成功/失败/回滚)、错误码
- 服务器端有仪表盘 + 告警:砖化率、失败率突增立即通知
- 这是"灰度健康闸门"的数据来源——没它你就是瞎推
3.7 重试与限流(防"重启风暴")
- 重试用随机退避(如随机 1~10 分钟),避免 10 万台同时重连把服务器打挂
- 服务器对下发出错峰调度,不在业务高峰强推
3.8 测试(上线前必做)
- 断电测试(最重要):下载中、烧写中各拔电 N 次 → 设备必须不砖、重启能恢复
- 篡改测试:下发的固件改 1 字节 → 验签必须拒
- 回滚测试:故意发崩溃固件 → 必须自动回旧版
- canary:先在真实设备上小批跑,再放大
四、设备端主流程(把四篇串成一段伪代码)
voidota_task(void){uint32_tcur=read_version();// 当前版本uint32_tmax=read_max_accepted();// eFuse 里的最高已接受版本manifest_tm=server_query(cur,device_id);// 问: 我能升到哪版?(灰度判定)if(m.version<=cur||m.version<=max)return;// 防回滚: 不降版本// 选 full 还是 delta: 本地有匹配旧版才用 delta(省流量)constchar*url=have_old_for(m)?m.delta_url:m.full_url;uint32_treceived=0;do{chunk_tc=https_download_range(url,received);// 断点续传if(verify_signature(c.data,c.len,m.sig)!=0)// OTA_04 验签return;// 验不过硬拒flash_write_inactive(c.data,c.offset);// OTA_01/02 写非活跃区received+=c.len;}while(!download_done());if(app_is_valid(INACTIVE)&&set_boot_partition(INACTIVE)==OK){esp_restart();// 或 jump_to_app,取决于平台}}// 新固件起来后voidapp_main_postboot(void){self_check();// 连服务器/传感器初始化等mark_valid();// 提交, 不再回滚write_max_accepted(read_version());// 更新最高已接受版本(防回滚)report_status(SUCCESS);// 上报, 供灰度闸门判断}这段几乎就是前四篇的"总装图":防回滚(3.1) + 灰度(server_query) + 断点(Range) + 验签(OTA_04) + A/B 写(OTA_01/02) + 提交(OTA_01) 全在里面。
五、故障场景 → 怎么扛(对照表)
| 故障 | 会发生什么 | 本文哪条兜住 |
|---|---|---|
| 下载中断电 | 旧固件完好 | A/B(OTA_01) + 断点续传(3.3) |
| 烧写中断电 | 非活跃区半截,激活区(old)仍好 | app_is_valid 失败 → 仍跑 old(OTA_02) |
| 固件被篡改 | 验签失败 | 签名(3.4) |
| 中间人下木马 | 验签失败 | 签名 + HTTPS(3.4) |
| 新固件崩溃 | 未提交 → 自动回滚 | 回滚(3.5) |
| 攻击者推旧漏洞版 | 版本号 ≤ 最高已接受 → 拒 | 防回滚(3.1) |
| 10 万台同时重试 | 服务器被打挂 | 随机退避 + 错峰(3.7) |
| 新版本有 bug | 砖化率超阈 → 灰度熔断 | 灰度闸门(3.2) + 上报(3.6) |
六、生产最易翻车的 5 件事
| # | 翻车点 | 后果 | 正确做法 |
|---|---|---|---|
| 1 | 没做断电测试 | 大量设备在升级中断电变砖 | 下载/烧写各拔电 N 次,必须可恢复 |
| 2 | 一把推 100% | 一个 bug 瞬间砖化整 fleet | 灰度分级 + 健康闸门 |
| 3 | 无遥测上报 | 砖化率飙升你不知道 | 设备上报 + 仪表盘告警 |
| 4 | 无单调版本号 | 可被降级攻击 | eFuse 存最高已接受版本(3.1) |
| 5 | 回滚无限环路 | 坏固件反复重启耗死设备 | 回滚计数器 + 看门狗(3.5) |
七、动手:给你的 OTA 做一次自检
拿前四篇你写的代码,对照第三节 8 张清单逐条打勾。重点问自己三个问题:
- 拔一次电,设备还能起来吗?(测断电)
- 下个旧版本,设备会拒绝吗?(测防回滚)
- 推给 1% 设备,你能看到成功/失败数字吗?(测遥测)
三问全过,才敢说"能上线"。
小结 & 系列收官
五篇 OTA 串起来就是一句话:能升级(01/02)→ 省流量(03)→ 保安全(04)→ 能规模运营(05)。
- OTA_01 全量升级(ESP32)
- OTA_STM32 自写 Bootloader
- OTA_03 差分升级省流量
- OTA_04 安全启动与固件签名
- OTA_05 量产落地 Checklist(本篇)
从"让灯闪一下"到"敢推给十万台设备且半夜不被叫醒",你差的不是某一招,而是这五篇拼起来的完整闭环。把这系列发出去,就是一份含金量很高的"我真的懂嵌入式 OTA"的作品集。
下个方向建议(任选):① 无线通信系列(Wi-Fi 配网 / BLE 透传 / LoRa 入门);② RTOS 进阶(内存泄漏排查 / 栈溢出定位);③ 低功耗系列(STM32 Stop 模式 / ESP32 深度睡眠)。想写哪个告诉我。