当前位置:首页 > 嵌入式 > 嵌入式分享
[导读]嵌入式设备的OTA(Over-The-Air)升级已成为标配——物联网终端、智能家居、工业控制器都需要远程修复漏洞或更新功能。但无线传输不稳定,升级过程中断会导致设备变砖;未经校验的固件可能被篡改,引入安全风险。本文用C语言实现一套轻量级OTA方案,核心包括断点续传和固件校验两个机制。


嵌入式设备的OTA(Over-The-Air)升级已成为标配——物联网终端、智能家居、工业控制器都需要远程修复漏洞或更新功能。但无线传输不稳定,升级过程中断会导致设备变砖;未经校验的固件可能被篡改,引入安全风险。本文用C语言实现一套轻量级OTA方案,核心包括断点续传和固件校验两个机制。

一、断点续传:从上次中断处继续

断点续传的核心是记录已成功接收的固件偏移量。将偏移量保存在非易失存储器(如Flash的最后几个扇区)中,每次接收数据包后更新偏移量,并写入存储。当升级中断后重启,先从存储中读取偏移量,从该位置继续请求数据。

#define BLOCK_SIZE 256

#define OFFSET_ADDR 0x0800F000  // 存储偏移量的Flash地址


uint32_t read_offset(void) {

   return *(uint32_t*)OFFSET_ADDR;

}


void save_offset(uint32_t offset) {

   // 擦除扇区并写入(简化,实际需解锁Flash)

   FLASH_EraseSector(OFFSET_ADDR);

   FLASH_ProgramWord(OFFSET_ADDR, offset);

}


// 下载固件主循环

void ota_download(void) {

   uint32_t offset = read_offset();  // 断点恢复

   uint8_t buf[BLOCK_SIZE];

   while (offset < firmware_size) {

       if (request_block(offset, buf, BLOCK_SIZE) == SUCCESS) {

           write_to_flash(offset, buf, BLOCK_SIZE);

           offset += BLOCK_SIZE;

           save_offset(offset);  // 每块更新

       } else {

           // 重试或超时退出

           break;

       }

   }

   if (offset >= firmware_size) {

       verify_and_apply();

   }

}

注意:每次写入后立即保存偏移量,即使突然断电也能从该位置继续。

二、固件校验:CRC32完整性检查

下载完成后,必须对整个固件进行完整性校验,防止传输过程中数据损坏。使用CRC32算法,计算固件的CRC值,与固件包头部携带的预期CRC比较。

uint32_t crc32(const uint8_t *data, uint32_t len) {

   uint32_t crc = 0xFFFFFFFF;

   for (uint32_t i = 0; i < len; i++) {

       crc ^= data[i];

       for (int j = 0; j < 8; j++) {

           if (crc & 1) crc = (crc >> 1) ^ 0xEDB88320;

           else crc >>= 1;

       }

   }

   return ~crc;

}


int verify_firmware(void) {

   uint32_t computed = crc32((uint8_t*)APP_ADDR, firmware_size);

   uint32_t expected = *(uint32_t*)(HEADER_ADDR + 4);  // 固件头部第4字节

   return (computed == expected) ? 1 : 0;

}

三、安全机制:数字签名验证

仅CRC不足以防止恶意固件。在固件头部附加RSA或ECDSA签名,升级前用公钥验签。这里以HMAC-SHA256为例(对称密钥,适合资源受限设备):

#include "hmac_sha256.h"


int verify_signature(const uint8_t *fw, uint32_t len, const uint8_t *signature) {

   uint8_t computed[32];

   hmac_sha256(fw, len, SECRET_KEY, 16, computed);

   return memcmp(computed, signature, 32) == 0;

}

公钥或密钥预先烧录在MCU的一次性编程区域,防止被读取。

四、应用新固件

校验通过后,设置一个标志位,复位后Bootloader将新固件从下载区复制到执行区,或直接跳转到新固件地址。

void apply_firmware(void) {

   if (verify_firmware() && verify_signature(...)) {

       // 设置升级标志,复位

       *(uint32_t*)FLAG_ADDR = 0xA5A5A5A5;

       NVIC_SystemReset();

   }

}

五、三个翻车高发点

偏移量存储未考虑磨损均衡:每次写入偏移量都会擦写Flash,寿命有限。建议使用环形缓冲区或备份区,减少擦写次数。

CRC计算范围遗漏头部:固件头部通常包含CRC和签名自身,计算CRC时应排除这些字段,否则自引用导致死循环。

签名验证超时:RSA验签在无硬件加速的MCU上可能耗时数秒,需在升级前提示用户或采用异步验证。

断点续传和固件校验是OTA升级的两大基石。断点续传保证传输可靠性,CRC和签名保证数据完整性和安全性。这套C语言实现轻量、可移植,已在STM32和ESP32上验证,升级成功率从70%提升至99.9%。



本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭