3. 版本号与元数据规范

在OTA体系中,版本号不是给用户看的装饰,而是设备端、云端、产线和售后共同依赖的“身份契约”。如果版本号规则混乱,就会出现设备误升级、回滚失败、灰度判断错误、兼容性事故等连锁问题。本节围绕四个核心对象展开:语义化版本(SemVer)、构建号(Build Number)、硬件兼容标识(Hardware Compatibility ID)以及版本清单文件(Manifest)与依赖关系表达。

一、语义化版本:表达“兼容性承诺”

语义化版本采用 MAJOR.MINOR.PATCH 三段式,例如 2.4.1。它的核心不是数字大小,而是每一段变化所隐含的兼容性含义:

  • MAJOR(主版本):出现不兼容的接口、协议或数据结构变更。例如设备从 MQTT 3.1.1 迁移到自定义二进制协议,或配置存储格式不向后兼容。
  • MINOR(次版本):向后兼容地新增功能。例如新增一个传感器上报字段、新增一条 OTA 断点续传能力。
  • PATCH(修订号):向后兼容的问题修复。例如修复内存泄漏、修正 CRC 校验边界错误。

在设备端,语义化版本应作为“升级决策”的第一依据。云端在决定是否向某设备推送固件时,先比较目标版本与当前版本的 MAJOR 是否一致;若 MAJOR 不同,通常意味着需要走特殊迁移流程,而不是普通灰度升级。预发布版本可追加 -beta.1、-rc.2 等标识,例如 2.4.1-rc.2,用于区分测试固件与正式固件。

二、构建号:唯一标识“这一次编译产物”

语义化版本描述的是“功能与兼容性语义”,而构建号描述的是“具体哪一次构建”。同一个 2.4.1 可能对应多次编译,因此必须用构建号保证二进制唯一性。常见形式包括:

  • 递增整数:build=20240612.3,便于排序和追溯。
  • Git 短哈希:build=a1b2c3d,直接关联代码提交。
  • 时间戳+流水线号:build=20240612-1147-3,适合 CI/CD 自动生成。

设备端上报版本时应同时上报 version 与 build。云端判断“是否需要升级”时,若语义化版本相同但构建号不同,通常视为“同版本不同产物”,是否升级取决于策略:正式发布场景下不应重复升级,而调试或产线场景下可能需要强制刷入指定构建。

三、硬件兼容标识:防止“固件跑错板”

同一套固件源码可能编译出面向不同硬件板型的产物。硬件兼容标识用于回答一个关键问题:这个固件能不能在这块板子上运行? 常见做法是定义 hw_model、hw_rev 与 compatible 列表:

{
  "hw_model": "GW-200",
  "hw_rev": "C",
  "compatible": ["GW-200-A", "GW-200-B", "GW-200-C"],
  "flash_layout": "layout_v3",
  "peripherals": ["wifi", "ble", "rs485"]
}

设备端在升级前必须校验自身硬件标识是否落在固件的 compatible 列表中。若不在,应拒绝升级并上报 HW_INCOMPATIBLE 错误。这一步是防止“刷错板导致变砖”的最后一道闸门。

四、版本清单文件与依赖关系表达

版本清单文件(Manifest)是OTA升级的“说明书+合同”。它把固件包、版本号、构建号、硬件兼容性、依赖关系和校验信息组织成一个可被设备解析的结构。一个典型的清单文件如下:

{
  "manifest_version": "1.0",
  "firmware": {
    "version": "2.4.1",
    "build": "20240612.3",
    "url": "https://ota.example.com/fw/gw200/2.4.1.bin",
    "size": 1048576,
    "sha256": "9f2c...e1a7"
  },
  "hardware": {
    "compatible": ["GW-200-B", "GW-200-C"]
  },
  "dependencies": [
    {"name": "bootloader", "min_version": "1.2.0"},
    {"name": "radio_fw", "version": "3.1.4", "required": true}
  ],
  "release_notes": "修复RS485丢包问题,新增BLE配网超时重试。"
}

依赖关系表达是清单文件中最容易被忽视、却最容易引发事故的部分。设备升级主固件前,必须检查依赖项是否满足:

  • bootloader 最低版本:若当前 bootloader 低于 min_version,必须先升级 bootloader,否则新固件无法启动。
  • 协处理器/射频固件版本:若 required=true 且版本不匹配,应拒绝升级或先升级依赖项。
  • 依赖顺序:多个依赖项之间可能存在先后关系,清单中可用 depends_on 字段表达拓扑顺序。

下面这张图展示了从设备上报版本、云端匹配清单、校验硬件兼容性与依赖关系,到最终决定升级或拒绝的完整判断链路。图中同时标出了语义化版本、构建号、硬件标识和依赖项在链路中的位置,便于对照理解。

OTA版本匹配与依赖校验链路 设备端上报 version: 2.3.0 build: 20240501.1 hw_model: GW-200-C 云端匹配清单 目标版本: 2.4.1 构建号: 20240612.3 SHA256 校验 硬件兼容校验 compatible 列表 GW-200-B / GW-200-C 匹配成功 ✓ 依赖关系校验 bootloader ≥ 1.2.0 ? radio_fw = 3.1.4 ? depends_on 拓扑顺序检查 全部满足 → 允许升级 允许升级 下载固件 → 校验 → 写入 拒绝升级 上报错误码,保持当前版本 版本/构建号传递 校验通过路径 校验失败路径 依赖关系校验

五、版本清单文件的工程落地要点

清单文件本身也需要版本管理。建议在清单中固定 manifest_version,设备端解析时先判断清单格式版本是否支持,避免因云端升级清单结构导致旧设备解析失败。同时,清单中的 url 应使用 HTTPS,并配合 sha256 做完整性校验;设备端在下载完成后先校验哈希,再写入 Flash。

对于依赖关系,建议遵循“最小必要”原则:只声明真正影响升级成败的依赖项,不要把无关模块全部写入。依赖项应包含 name、min_version 或 version、required 三个基本字段,必要时增加 depends_on 表达拓扑顺序。设备端在升级前按拓扑顺序逐项检查,任一必需依赖不满足即终止升级并上报明确错误码。

实践建议:在产线烧录阶段就把 hw_model、hw_rev 写入设备安全区,OTA 时直接读取,避免运行时被篡改。版本号与构建号应同时上报,云端日志中保留两者,便于问题追溯时精确定位到某一次编译产物。
常见错误:只比较语义化版本而忽略构建号,导致同一版本号下不同构建产物被误判为“已是最新”;或清单中 compatible 列表写错,使固件被推送到不兼容的板型上。这两类问题在灰度阶段往往难以发现,必须在清单生成环节加入自动化校验。

总结来说,语义化版本回答“功能兼容性”,构建号回答“哪一次编译”,硬件兼容标识回答“能不能跑”,清单文件与依赖关系回答“升级前还要满足什么条件”。四者共同构成 OTA 版本管理的元数据基础,缺一不可。