OTA 升级
一、什么是 OTA 升级
OTA 是指设备在远程状态下完成软件、固件或模块升级,不需要人工到现场刷机。
在本平台中,OTA 不只是"发一个升级指令"这么简单,而是完整覆盖以下过程:
- 升级包管理
- 升级任务创建
- 设备接收升级通知
- 设备下载和安装升级包
- 上报升级进度和结果
- 平台统计成功、失败、超时和回滚情况
二、推荐的 OTA 工作方式
本平台推荐的标准 OTA 模式是:
- 平台向设备发送升级通知
- 设备拿到升级包地址
- 设备自行下载升级包
- 设备校验、安装、重启
- 设备上报进度、结果和新版本
推荐方式
建议设备通过 HTTP、HTTPS 或对象存储地址下载升级包。
也就是说,平台默认更适合做"升级控制面",而不是直接通过消息链路去传整个大文件。
这套方式适合 MQTT、TCP、UDP 以及其他自定义协议设备。协议不同,只会影响"指令怎么发、上报怎么解",不会改变 OTA 的核心业务逻辑。
三、平台侧与设备侧职责
平台侧负责
- 管理升级包
- 创建升级任务
- 控制灰度、分批、定时和回滚策略
- 向设备发送升级通知
- 记录设备升级明细
- 判断成功、失败和超时
设备侧负责
- 接收升级通知
- 获取并校验升级包完整性
- 执行安装、必要时自动重启
- 上报进度、最终结果和最新版本号
注意
如果设备本身不具备"下载、校验、安装、重启、结果上报"这些能力,平台只能创建任务和发送通知,不能替设备完成真正的升级。
四、使用前准备
开始使用 OTA 前,建议先确认:
- 产品和设备都已经创建完成
- 设备已经能正常接入平台
- 已经准备好升级包文件
- 设备端已经实现升级流程
- 已经明确升级失败后的恢复方案
建议优先整理好以下升级包信息:升级包名称、产品、版本号、升级类型、模块、文件大小、校验值、发布说明、标签。
五、标准 OTA 任务流程
第一步:上传升级包
在升级包管理中上传升级包,并完善版本、校验值和发布说明。
第二步:创建升级任务
创建任务时,一般需要确定:选择哪个升级包、升级哪些设备、是否立即/定时执行、是否灰度/分批推送、是否失败自动回滚、超时时间和重试次数。
第三步:平台发送升级通知
任务开始后,平台会把标准 OTA 内容下发到协议层,由协议层再转换成设备真正能识别的报文。
第四步:设备执行升级
设备收到通知后,开始下载、校验、安装和重启。
第五步:设备回传进度和结果
设备在升级过程中持续上报当前进度、当前状态、当前版本、目标版本,以及最终成功或失败结果。
第六步:平台汇总结果
在任务中心里,平台会统计总设备数、成功数、失败数、超时数、取消数和跳过数。
六、平台下发给设备的标准 OTA 内容
这部分是设备研发和协议研发最关心的内容。
平台在下发 OTA 时,会先形成一份统一的 OTA 标准内容,然后由具体协议去编码成 MQTT、TCP、UDP 或厂商私有报文。
下发升级通知示例
下面是协议编解码层通常能拿到的标准 OTA 内容示例:
{
"messageType": "FUNCTIONS",
"function": "otaUpgrade",
"data": {
"action": "start",
"taskId": "1024",
"taskNo": "OTA202603220001",
"taskDeviceId": "50001",
"packageId": "2",
"packageName": "主程序升级包 1.0.2",
"packageType": "FIRMWARE",
"customType": "default",
"module": "APPLICATION",
"currentVersion": "1.0.1",
"targetVersion": "1.0.2",
"downloadUrl": "https://example.com/ota/firmware-1.0.2.bin",
"fileName": "firmware-1.0.2.bin",
"fileSize": 1048576,
"checksum": "8d1c7f8ef8d6a0ce4f13f6c9c2aef001",
"checksumAlgorithm": "MD5",
"signatureUrl": "https://example.com/ota/firmware-1.0.2.sign",
"forceUpgrade": false,
"releaseNotes": "修复已知问题并优化稳定性",
"strategy": {
"maxRetryCount": 3,
"timeoutSeconds": 1800,
"batchSize": 100,
"batchIntervalSeconds": 0,
"requireVersionCheck": true,
"autoDispatch": true
}
}
}编解码脚本要注意
当前平台在执行 DEV_FUNCTION 编码时,传给协议层 encode() 的就是上面这段函数 JSON 本身,而不是整个 UnifiedDownlinkCommand。
也就是说,你在 magic/js/java/spring 编码器里通常直接处理的是 messageType、function、data,而不是再去取一层 function.data。
设备建议识别哪些字段
平台下发的 OTA 内容会尽量完整,但并不是要求所有设备一次性把所有字段都"硬编码支持"。更合理的做法是按设备能力分层实现:
| 分类 | 字段 | 说明 |
|---|---|---|
| 必需字段 | action、targetVersion、downloadUrl | 设备启动升级流程的最核心内容,建议所有 OTA 设备都支持 |
| 条件必需 | module、packageType、fileSize、checksum、checksumAlgorithm | 存在多模块升级、多包类型或需要自行做完整性校验时必须支持;如固件包自带签名/厂商校验流程,可作为辅助信息而非强制依赖 |
| 推荐字段 | taskId、taskDeviceId、strategy.maxRetryCount、strategy.timeoutSeconds | 不是"能不能升级"的前提,但会明显提升任务追踪、超时判定和问题排查能力 |
推荐做法
设备尽量回传 taskDeviceId。如果实在不能回,也建议回 taskId。如果这两个都没有,平台只能根据设备、模块、目标版本等信息做兜底匹配,准确性会下降。
平台推荐"字段尽量完整",但真正的最低要求仍然是:设备能拿到升级地址,完成下载、校验、安装,并且能把进度和结果回传上来。
七、设备上报给平台的标准 OTA 消息
设备上报时,推荐统一成三类消息:
- 版本确认使用
messageType = PROPERTIES - 升级进度使用
messageType = EVENT, event = otaProgress - 升级结果使用
messageType = EVENT, event = otaResult - OTA 扩展字段直接放在
data节点中,不再额外包一层ota
1. 设备上报当前版本
适合设备日常上报版本信息,或者升级完成重启后再次确认版本。
{
"messageType": "PROPERTIES",
"data": {
"reportType": "FIRMWARE_INFO",
"taskId": "1024",
"taskDeviceId": "50001",
"packageType": "FIRMWARE",
"module": "APPLICATION",
"currentVersion": "1.0.2",
"targetVersion": "1.0.2"
},
"properties": {
"firmwareVersion": "1.0.2"
}
}2. 设备上报升级进度
升级中建议定期上报进度,例如 10%、30%、60%、90%。
{
"messageType": "EVENT",
"event": "otaProgress",
"data": {
"reportType": "UPGRADE_PROGRESS",
"taskId": "1024",
"taskDeviceId": "50001",
"packageType": "FIRMWARE",
"module": "APPLICATION",
"currentVersion": "1.0.1",
"targetVersion": "1.0.2",
"upgradeStatus": "DOWNLOADING",
"progress": 45,
"statusDetail": "已下载 45%"
}
}3. 设备上报升级成功
升级完成后,建议显式上报成功结果。
{
"messageType": "EVENT",
"event": "otaResult",
"data": {
"reportType": "UPGRADE_RESULT",
"taskId": "1024",
"taskDeviceId": "50001",
"packageType": "FIRMWARE",
"module": "APPLICATION",
"currentVersion": "1.0.2",
"targetVersion": "1.0.2",
"upgradeStatus": "SUCCESS",
"progress": 100,
"statusDetail": "升级完成并重启成功"
}
}4. 设备上报升级失败
失败时建议把失败原因一起带回来。
{
"messageType": "EVENT",
"event": "otaResult",
"data": {
"reportType": "UPGRADE_RESULT",
"taskId": "1024",
"taskDeviceId": "50001",
"packageType": "FIRMWARE",
"module": "APPLICATION",
"currentVersion": "1.0.1",
"targetVersion": "1.0.2",
"upgradeStatus": "FAILED",
"progress": 65,
"errorCode": "VERIFY_FAILED",
"errorMessage": "校验失败"
}
}八、TCP 私有报文怎么做 OTA
如果设备不是直接收发 JSON,也完全可以做 OTA。思路很简单:
- 平台先生成上面的标准 OTA 内容
- 你的
encode把标准内容翻译成设备私有帧 - 设备上报时,你的
decode再把私有帧翻译回标准 OTA 消息
下行可以是 HEX,上行也可以是 HEX,但进入平台统一处理前,最好被转换成标准 OTA 语义。
例如,设备上报的原始 TCP 帧里即使只有状态码、进度、版本号、错误码,只要在协议脚本里把它们映射成 reportType、upgradeStatus、progress、currentVersion、targetVersion、errorCode、errorMessage,平台就能继续识别。
九、平台如何判断升级成功、失败和超时
判断升级成功
平台一般通过以下方式判断升级成功:
- 设备明确上报
SUCCESS - 设备进度达到
100 - 设备升级后再次上报的新版本已经等于目标版本
最稳妥的方式是同时满足两件事:设备上报升级结果为成功,且设备重启后再次上报当前版本号。
判定失败的常见场景
- 下载失败、校验失败、存储空间不足
- 安装失败、重启失败
- 设备主动返回失败状态
判定超时的常见场景
平台下发升级任务后,如果设备长时间没有继续反馈,平台应判定为超时。例如:下发后一直没有任何回应、上报到 30% 后长时间不再更新、下载中断后一直没有后续状态。
建议
超时时间建议按设备能力和文件大小设置,一般可以从 30 分钟 起步。
十、灰度、分批、定时、回滚
灰度发布
建议先选择少量代表性设备试升级,例如不同地区、不同网络环境、不同硬件批次。
分批发布
不建议第一次就全量升级,推荐先发 5%~10% 观察结果,再逐步扩大范围,最后再全量发布。
定时发布
适合夜间、低峰期或指定运维窗口执行升级,减少业务影响。
失败回滚
建议提前准备稳定版本作为回滚包。当失败率达到阈值后,可以停止继续放量,并对已经升级成功但需要回退的设备执行回滚。
十一、为什么推荐 URL 下载而不是消息分片传包
原因主要有这些:
- 升级包通常比较大
- 消息链路更适合控制指令
- 分片传输要处理丢包、重传、排序、续传和整包校验
- 大规模同时升级时,对 Broker 和设备压力都更大
所以本平台默认推荐消息下发升级通知,设备通过 URL 拉取升级包。如果设备无法访问 URL,再考虑私有分片传包方案。
十二、日常使用建议
- 升级包命名尽量规范,发布说明写清楚变更内容
- 先灰度,再逐步放量
- 对核心设备启用回滚策略
- 尽量让设备回传
taskDeviceId - 升级完成后再上报一次版本信息
- 对失败和超时设备保留人工复核和重试空间
十三、常见问题
设备只上报进度,不上报结果,可以吗?
可以作为过程信息,但不建议长期只报进度。最好补充最终结果和最终版本。
设备不回 taskDeviceId 可以吗?
可以,但不推荐。最好至少回 taskId。如果两个都不回,平台只能根据设备、模块、目标版本等信息做关联。
平台下发成功了,为什么不代表升级成功?
因为"下发成功"只代表设备收到了升级通知,不代表设备已经下载、校验、安装并重启成功。
升级结果上报成功后,还要不要再报版本?
建议报。结果上报是"过程结束",版本上报是"最终状态确认",两者一起会更稳。
总结
如果你要把 OTA 做得稳定可用,建议记住这三点:
- 平台负责任务和流程控制
- 设备负责真正的升级执行
- 协议层负责把私有报文转换成统一 OTA 语义
只要把"下发通知、进度上报、结果上报、版本确认"这四个环节打通,OTA 就可以真正落地,而不是停留在"只能发指令"的阶段。