引言

为什么需要OTA?如果你的嵌入式产品量产并大卖,产品发往世界各地,当你想给产品进行软件升级的时候怎么办呢?这个时候就需要OTA(Over-the-Air)空中下载技术。那嵌入式产品往往还伴随着以下问题:

  • 存储资源受限
  • 网络环境恶劣,丢包率高
  • 存在断电风险
  • 无人工干预,需要设备100%自主完成

接下来让我们看看如何设计OTA让设备软件成功升级,而不变砖的。

一、OTA核心架构

1.1 A/B分区系统

双分区系统这是避免变砖的关键设计。假设eMMC布局如下

/dev/mmcblk0
├─p1: bootloader (U-Boot)   # 引导程序
├─p2: boot_a                # 内核A分区
├─p3: rootfs_a              # 根文件系统A
├─p4: boot_b                # 内核B分区
├─p5: rootfs_b              # 根文件系统B
├─p6: ota_data              # OTA元数据分区
└─p7: user_data             # 用户数据分区
  • 分区大小规划:每个根文件系统分区应预留20-30%的额外空间,用于存放临时文件、日志和未来版本增量
  • OTA元数据分区:这个分区通常很小(4-16MB),存放OTA状态、回滚计数、升级日志等关键信息(存储空间有限的嵌入式设备可以没有此分区,并将其中部分状态写入uboot环境变量)
  • 数据分区独立:user_data分区存储应用数据,避免OTA时用户数据丢失

更新时的操作:

  1. 如果当前运行在A分区,则将新的版本下载更新到B分区
  2. 验证通过后,只更新B分区的内容
  3. 引导程序切换到B分区启动
  4. 如果B分区启动失败(可设置多次判断,如3次重启失败),自动回退到A分区

在U-Boot中,需要设置启动计数器和回滚逻辑。U-Boot中的关键环境变量:

# 查看当前启动分区
fw_printenv bootpart

# 设置从B分区启动
fw_setenv bootpart 2
fw_setenv bootargs "root=/dev/mmcblk0p5 rootwait rw"

# 安全回退机制:启动计数器
fw_setenv bootcount 0
fw_setenv bootlimit 3
# 每次启动递增bootcount,如果超过bootlimit仍失败,则切换回A分区

1.2 OTA客户端
这是常驻后台的守护进程,核心职责有以下三个:

  1. 定期心跳,上报状态
  2. 检查更新
  3. 触发更新流程

开发要点:

  • 必须轻量,内存占用通常控制在5MB以内
  • 实现指数退避的重试机制,避免网络风暴
  • 心跳协议要包含设备健康状态(电池、存储、信号强度)

1.3 更新引擎
这是最复杂的部分,必须处理所有异常情况。以下是简化流程:
脚本

  1. 准备阶段:创建文件夹
  2. 下载更新包(支持断点续传)
  3. 安全验证:验证数字签名、验证哈希值
  4. 应用更新
  5. 原子性提交:标记更新成功、标记更新失败,触发回滚
    原子性的核心是:一个操作要么完全执行,要么完全不执行,不会出现"执行一半"的状态。这也是减少升级过程中出现问题的关键。

1.4 安全验证体系

嵌入式设备最怕被植入恶意固件。数字签名流程:

# 服务器端生成签名
openssl dgst -sha256 -sign private_key.pem \
    -out update.bin.sig \
    update.bin

# 设备端验证签名(公钥已预置在固件中)
openssl dgst -sha256 -verify /etc/ota/public_key.pem \
    -signature update.bin.sig \
    update.bin

实际开发中的增强措施:

  • 双密钥轮转:生产密钥和恢复密钥分离

  • 版本防回滚:防止攻击者用旧版本漏洞

// 版本检查逻辑
if (new_version <= current_version) {
    log_warn("Version rollback attempt detected!");
    return -EPERM;
}

二、开发中的挑战

断电处理:状态机+原子操作的设计,让每次状态变化都立即持久化,确保在任何时刻断电都不变砖

存储空间不足的解决方案:差分升级包,压缩升级包

健康检查机制:确保新系统能工作,比如在新版本运行到指定阶段或者指定时长后,再清除升级失败计数器

三、自研vs开源

自研方案的优势与代价

何时应该自研?

  • 硬件极其特殊(MCU+外置Flash,无标准MMC)

  • 更新逻辑特别简单(只更新一个应用)

  • 公司已有深厚的基础设施

  • 对安全有特殊合规要求

自研的隐藏成本:

  • 测试成本:需要模拟各种异常(断电、断网、存储坏块)

  • 维护成本:每个新硬件平台都要适配

  • 生态成本:缺少社区支持,所有问题自己解决

开源工具

SWUpdate、 RAUC、Mender

更多推荐