Fork me on GitHub
0%

OTA

简介

概述

OTA(Over-the-Air)直译来讲就是空中下载,是指通过网络将设备固件更新到最新版本,而无需物理连接。对于不同厂家的不同的产品有着不同的OTA升级策略,但是在OTA升级中关键步骤几乎都是一致的。

相应的,通过有线方式进行升级,叫本地升级,比如通过UART,USB或者SPI通信接口来升级设备固件

需要解决的问题

  1. 如何将固件下载到本地?
  2. 如何保证下载到的固件是正确且完整的?
  3. 如何烧录新的固件且过程中出现问题可以正常恢复?

整体流程

  1. 服务器端制作升级包
  2. 设备端下载升级包
    • 建立与服务器的网络连接
    • 从服务器下载新的固件版本,确认是否需要升级
    • 下载升级包
  3. 设备端检验升级包
    • 解压升级包
    • 校验固件版本的完整性、准确性、安全性
  4. 升级
    • 将固件写入设备的存储介质
    • 升级成功写入新的版本号
  5. 设备重启

OTA升级策略

固件完整性

全包升级

全包升级(Full Update):全包升级是指将完整的固件包发送给设备进行升级。这意味着设备需要下载整个固件包并进行完全的替换,无论设备当前运行的是哪个版本。全包升级适用于较小的固件更新或在设备上进行了较大的改动。

差分升级

差分升级(Differential Update):差分升级是指只发送变更部分的固件包给设备进行升级。这样,设备只需要下载和应用固件的变化部分,而不是整个固件包。差分升级适用于较大的固件更新,尤其是当设备已经处于较新的版本上时。

这种方式可以大大减少升级所需的时间和数据量,提高升级的效率。但是,差分升级可能需要额外的计算资源来处理固件的合并和应用,因为设备需要将变更部分与当前的固件进行合并,并且可能会出现不兼容的情况。

固件覆盖模式

单区模式

在单区覆盖模式下,设备固件的存储空间被分成一个单一的区域。当进行OTA固件升级时,新的固件包会完全替换原有的固件,覆盖在同一个存储区域上。这意味着在升级过程中,设备的固件会被擦除并用新的固件替代。但是由于程序执行的代码已经从flash拷贝到ram中进行执行了,所以OTA的流程还是可以进行完成的,当新的固件覆写到旧固件的存储区域完成重启设备即可进入新版本

单区覆盖模式的优点是实现简单,固件更新相对直接,但缺点是如果升级失败,可能需要进行完全的固件恢复,而且升级过程中无法回滚到原有固件版本,甚至最后变成“板砖”

双区模式

在双区覆盖模式下,设备固件的存储空间被分为两个独立的区域,假设称为”A区”和”B区”。设备当前运行的固件版本存储在其中一个区域(例如A区),而进行OTA固件升级时,新的固件包将被下载并写入另一个区域(例如B区)。

在升级过程中,设备将保持运行在当前固件版本所在的区域(例如A区),而新的固件被下载到另一个区域(例如B区)。下载完成后,设备会进行固件切换,将运行环境从当前区域切换到新的固件所在的区域。这样,如果升级失败,设备可以回滚到原有的固件版本,通过切换回之前的区域。

双区覆盖模式的优点是在升级过程中提供了冗余和回滚的能力,减少了升级失败的风险。然而,相对于单区覆盖模式,双区覆盖模式需要更多的存储空间来维护两个固件区域。

其它策略

解压策略

在工作的一个项目中(freertos系统、8M nor flash),由于flash存储空间有限,不能将升级包下载解压到flash文件系统上。

此时可以使用边解压变升级的方式,使用minizip、zlib等开源库的API将升级包的zip包解压到RAM当中,无论是使用堆空间还是栈空间都是可以的,按照固定大小解压到buffer,将buffer内容升级或者是烧录进flash即可。

这样做的好处很明显:节省flash空间、减少了一次数据的写flash和读flash,理论上按照同样大小的块烧录,速度更快

完整性策略

完整性指的是:我们下载download下来的数据包与网络服务器端的数据包是一致的

这个相对简单,我们对升级包做hash或者md5值,将这个值写入文件放到服务器上。当我们下载压缩包的时候,将这个hash/md5一起下载,在本地对压缩包做hash/md5,再与下载下来的值比较。这样对于数据包是否正确是否完整我们就有了准确的判断依据。

合法性策略

合法性策略指的是:对于服务器来讲,它所存放的升级包不能给规定产品以外的设备来获取到压缩包,或者说即使客户端下载下来了也不能正常解压压缩包拿到数据。

工作中我所涉及的加密的方法是使用非对称加密RSA算法,这里可能涉及到密码学的相关知识,后面我也需要再进一步学习整理出专门的文章来做解释。在OTA的场景下,可以概括成如下一些步骤:

  • 生成RSA密钥对(公钥+私钥)
  • 将公钥提供给设备端。可以将公钥作为固件的一部分或单独提供给设备。
  • 将私钥提供给服务端。在OTA服务器端,使用私钥对固件文件进行数字签名。
  • OTA下载流程。将固件通过HTTP/HTTP等协议传输到设备端。
  • 设备端验签。OTA设备接收到加密的固件后,使用嵌入在设备中的公钥对固件进行解密和验证。
  • 验证成功进入固件升级流程

伪码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
bool download_firmware(void);
bool verify_firmware(void);
bool write_firmware(void);
bool update_firmware_version(void);
bool restart_device(void);

int main() {
if(download_firmware() &&
verify_firmware() &&
write_firmware() &&
update_firmware_version()) {
printf("OTA upgrade succeeded.\n");
}
else {
printf("OTA upgrade failed.\n");
}
reboot(); // 设备重启
return 0;
}

bool download_firmware() {
// 从服务器端下载固件
// 工具:wget、curl
// 在lwip工程中提供了代码http_client_download_to_disk接口来下载文件
}

bool verify_firmware() {
// 校验固件(完整性、安全性、准确性)
}

bool write_firmware() {
// 将固件写入非易失性存储器当中(write to flash)
}

bool update_firmware_version() {
// 将系统的版本号进行更新
}

参考资料