3、固件版本管理:版本号规范、版本兼容性策略、版本回滚机制设计

说到固件版本管理,我脑子里第一个蹦出来的画面,是几年前一个凌晨的生产线。当时新固件推送上去,结果有一批设备直接变砖了。嗯,从那以后,我对版本管理这件事,就再也不敢马虎了。

说白了,版本管理就是OTA升级的“安全带”。没有它,你飞得再高,摔下来也疼。今天我就把这块的核心内容,掰开了跟你聊聊。

3.1 版本号规范:别让数字骗了你

版本号这东西,看着简单,但坑特别多。我个人习惯用三段式版本号主版本号.次版本号.修订号

举个例子:2.1.3

  • 主版本号:大版本迭代,比如架构重写、协议变更。不兼容旧版时,就加这个。
  • 次版本号:新增功能,但向下兼容。比如加了个传感器校准算法。
  • 修订号:修bug、优化性能。功能不变,只是更稳了。

重要原则:版本号一旦发布,就不能再改。同一个版本号,必须对应同一份固件二进制。这是底线。

我在项目中遇到过,有人把版本号写成 20240301 这种日期格式。结果呢?同一天发布了两个修复版本,根本分不清哪个是哪个。你想想看,这要是回滚,你选哪个日期?

所以,我建议你直接用三段式。如果非要加额外信息,比如测试版、内部版,可以用后缀:2.1.3-beta2.1.3-rc1

3.2 版本兼容性策略:向前看,也要向后看

兼容性,是OTA升级里最头疼的事。为什么?因为设备可能跳过好几个版本,直接从 1.0.0 升到 2.1.3

我一般把兼容性分成两类:

  • 向前兼容:新固件能处理旧固件产生的数据。比如旧版保存的配置参数,新版能正常读取。
  • 向后兼容:旧固件能识别新固件发来的指令。这个在网关类设备里特别重要。

怎么保证?我的做法是:定义一份“兼容性矩阵”

当前版本 可升级版本 是否支持直接升级 备注
1.0.0 1.1.0 小版本升级
1.0.0 2.0.0 需先升到1.1.0
1.1.0 2.0.0 支持跨大版本
2.0.0 2.1.0 正常升级

你看,不是所有版本都能直接跳。我曾经遇到一个项目,设备从 1.0.0 直接升到 2.0.0,结果因为Flash分区表变了,配置全丢了。从那以后,我强制要求:跨大版本升级,必须经过中间版本

小技巧:在固件里加一个“最低兼容版本号”字段。升级前,先检查当前版本是否低于这个值。如果是,就拒绝升级,或者提示用户先升到中间版本。

3.3 版本回滚机制设计:给自己留条后路

回滚,是OTA升级的最后一道防线。说白了,就是“升坏了,还能退回去”。

我常用的方案是双区备份。设备里有两个固件分区:ActiveBackup

// 伪代码示例:回滚逻辑
if (new_firmware_ok) {
    swap_partitions();  // 交换分区,新固件变Active
} else {
    rollback();         // 直接启动Backup分区
}

具体流程是这样的:

  1. 新固件下载到 Backup 分区。
  2. 校验通过后,标记为“待激活”。
  3. 重启设备,从 Backup 分区启动。
  4. 如果启动成功,并且运行稳定(比如5分钟内无异常),就把 Backup 设为 Active,原来的 Active 变成新的 Backup
  5. 如果启动失败,或者用户手动触发回滚,就切回原来的 Active 分区。

注意:回滚不是万能的。如果新固件修改了非易失性存储(比如NVM、EEPROM)的结构,回滚后旧固件可能无法正确读取。所以,我建议在回滚时,也把配置数据恢复到升级前的版本。

我曾经在一个智能家居项目里,就因为没做配置回滚,导致回滚后设备连不上网。用户气得直接退货。嗯,从那以后,我每次升级前都会备份一份完整的配置快照

另外,回滚次数也要有限制。我一般设置最多回滚3次。超过这个次数,就锁定设备,必须通过售后处理。为什么?因为反复回滚说明固件本身有问题,不能靠回滚来“续命”。

3.4 版本管理的其他细节

最后,再补充几个我踩过的坑:

  • 版本号要写在固件头部。这样Bootloader不用解压就能读到版本信息,方便做升级决策。
  • 记录升级历史。在设备里维护一个“升级日志”,记录每次升级的版本号、时间、结果。排查问题时,这东西能救命。
  • 服务器端也要做版本管理。别让设备能升到比当前还低的版本。我见过有人误操作,把设备从 2.0.0 降级到 1.0.0,结果功能全丢了。

好了,版本管理这块,核心就是这些。记住一句话:版本号是身份证,兼容性是通行证,回滚是后悔药。三者缺一不可。

下一章,我们聊聊升级包的签名和校验。那可是防篡改的关键,别错过。