当前位置: 首页 > news >正文

STM32+4G模组远程OTA升级方案:双Bank设计与断点续传实践

为什么要做OTA

你往客户的工厂发了500台设备,每台设备跑着固件V1.0。三天后发现一个bug——串口数据解析在高湿度环境下偶尔丢包。

没有OTA的话:派工程师去现场,一台台用USB烧录。500台设备,假设每台5分钟,加上路上时间,一周也搞不完。

有OTA的话:服务器上点一下"推送",500台设备自动下载新固件,5分钟全部升级完毕。

OTA不是锦上添花,是物联网设备的运维刚需。

STM32 OTA的技术难点

STM32做OTA比ESP32麻烦得多。ESP32有现成的分区表和OTA库,几行代码搞定。STM32需要自己处理所有事情:

  1. Bootloader:上电后先跑Bootloader,判断是否需要升级
  2. 双Bank设计:Flash分两个区,一个跑当前固件,一个写新固件,升级时切换
  3. Flash写入:STM32写Flash要先擦除再写,擦除最小单位是页(2KB/4KB)
  4. 4G模组通信:通过AT指令下载固件文件
  5. 断点续传:网络不稳定时能续传
  6. 校验机制:下载完成后CRC校验,防止刷入坏固件变砖

硬件方案

[STM32F407] --UART--> [EC25 4G模组] --4G网络--> [OTA服务器] | [SPI Flash W25Q128] -- 存储新固件 | [内部Flash Bank1/Bank2] -- 运行固件

选STM32F407的原因:

  • 1MB Flash,支持双Bank(Bank1: 512KB, Bank2: 512KB)
  • 192KB RAM,固件下载缓冲够用
  • 支持SPI,外挂W25Q128(16MB)存固件文件
  • 价格合适,批量约15元

Flash分区设计

STM32F407VE的512KB Flash分区:

0x08000000 - 0x08007FFF Bootloader (32KB) 0x08008000 - 0x0803FFFF Bank1: 应用固件 (224KB) 0x08040000 - 0x0807FFFF Bank2: 备用固件 (224KB) 0x08078000 - 0x0807FFFF 配置参数区 (32KB)

STM32F407实际上只有512KB单Bank Flash(双Bank是F42x/F46x系列才有),但可以通过软件模拟双Bank逻辑:升级时把新固件写到Bank2区域,校验通过后修改Bootloader标志位,下次重启从Bank2启动。

Bootloader代码

#include"stm32f4xx_hal.h"#include"flash_if.h"#include"ota_config.h"#defineBANK1_ADDR0x08008000#defineBANK2_ADDR0x08040000#defineCONFIG_ADDR0x08078000typedefstruct{uint32_tmagic;uint32_tactive_bank;// 1 or 2uint32_tfirmware_size;uint32_tfirmware_crc;uint32_tupgrade_flag;// 0xFFFFFFFF=no upgrade, 0x55555555=upgrading}ota_config_t;intmain(void){HAL_Init();// 读取配置区ota_config_tconfig;flash_read(CONFIG_ADDR,(uint8_t*)&config,sizeof(config));void(*app_main)(void);uint32_tapp_addr;if(config.active_bank==1){app_addr=BANK1_ADDR;}else{app_addr=BANK2_ADDR;}// 检查是否有升级完成待切换if(config.upgrade_flag==0xAAAAAAAA){// 新固件已写入并校验通过,切换Bankconfig.active_bank=(config.active_bank==1)?2:1;config.upgrade_flag=0xFFFFFFFF;flash_erase_config();flash_write_config((uint8_t*)&config,sizeof(config));}// 跳转到应用固件if(((*(__IOuint32_t*)app_addr)&0x2FFE0000)==0x20000000){// 检查栈指针合法性__set_MSP(*(__IOuint32_t*)app_addr);app_main=(void(*)(void))(*(__IOuint32_t*)(app_addr+4));app_main();}// 如果跳转失败,停在Bootloaderwhile(1){// 可以在这里加串口下载模式作为fallback}}

应用固件中的OTA下载

#defineFIRMWARE_MAX_SIZE(224*1024)// 224KBexternUART_HandleTypeDef huart2;// 4G模组UARTuint8_tfirmware_buf[FIRMWARE_MAX_SIZE];uint32_tfirmware_offset=0;voidota_check_and_download(void){// 1. HTTP请求检查是否有新版本uint8_thttp_cmd[256];snprintf((char*)http_cmd,sizeof(http_cmd),"AT+QHTTPURL=\"http://ota.heicat.com/api/check?device_id=%s\"\r\n",get_device_id());if(at_send_wait(huart2,http_cmd,"OK",3000)!=0)return;// 2. 获取版本号和固件大小at_send(huart2,"AT+QHTTPGET=80\r\n");// 解析返回的JSON,获取version和size// 3. 下载固件uint32_ttotal_size=firmware_info.size;uint32_tchunk_size=4096;firmware_offset=0;while(firmware_offset<total_size){uint32_tremain=total_size-firmware_offset;uint32_tread_size=(remain>chunk_size)?chunk_size:remain;// 通过HTTP GET Range下载分片snprintf((char*)http_cmd,sizeof(http_cmd),"AT+QHTTPGET=80,\"Range: bytes=%d-%d\"\r\n",firmware_offset,firmware_offset+read_size-1);if(at_send_wait(huart2,http_cmd,"CONNECT",5000)!=0){// 重试HAL_Delay(1000);continue;}// 接收数据uint8_tbuf[4096];at_receive_data(huart2,buf,read_size,10000);memcpy(firmware_buf+firmware_offset,buf,read_size);firmware_offset+=read_size;}// 4. CRC校验uint32_tcrc=crc32(firmware_buf,total_size);if(crc!=firmware_info.crc){return;// CRC不匹配,放弃升级}// 5. 写入Bank2flash_erase_bank2();flash_write_bank2(firmware_buf,total_size);// 6. 更新配置,通知Bootloader切换ota_config_tconfig;config.magic=0xDEADBEEF;config.active_bank=1;// 当前在Bank1,切到Bank2config.firmware_size=total_size;config.firmware_crc=crc;config.upgrade_flag=0xAAAAAAAA;// 待切换标志flash_erase_config();flash_write_config((uint8_t*)&config,sizeof(config));// 7. 重启NVIC_SystemReset();}

服务器端API

简单的PHP版本:

// check.php - 检查更新$device_id=$_GET['device_id'];$stmt=$pdo->prepare("SELECT * FROM devices WHERE device_id = ?");$stmt->execute([$device_id]);$device=$stmt->fetch();$latest=$pdo->query("SELECT * FROM firmware_versions WHERE product_id = ? AND status = 'published' ORDER BY created_at DESC LIMIT 1",[$device['product_id']]);if($latest&&version_compare($latest['version'],$device['firmware_version'])>0){echojson_encode(['has_update'=>true,'version'=>$latest['version'],'size'=>$latest['file_size'],'crc'=>$latest['file_crc'],'url'=>"http://ota.heicat.com/firmware/".$latest['filename']]);}else{echojson_encode(['has_update'=>false]);}

断点续传

4G网络不稳定,下载16MB固件中途断开是常有的事。解决方案:

  1. 分片下载:每次请求4KB,用HTTP Range头
  2. 本地记录进度:写入Flash配置区,重启后从断点继续
  3. 失败重试:单次下载失败重试3次,3次失败则等待5分钟后重试

防变砖设计

OTA最怕的是刷入坏固件导致设备变砖。防砖措施:

  1. CRC校验:下载完成后必须校验,不通过不写入
  2. 双Bank切换:新固件写到备用Bank,不覆盖当前运行固件
  3. Bootloader回退:如果新固件启动失败(看门狗复位),Bootloader自动回退到旧Bank
  4. 硬件恢复接口:预留UART或SWD接口,最坏情况下可物理烧录

看门狗回退逻辑:

// Bootloader中if(config.upgrade_flag==0xAAAAAAAA){// 新固件第一次启动config.upgrade_flag=0x55555555;// 标记"试运行"flash_write_config(&config);// 跳转到新固件// 如果新固件正常,会在运行时把flag清为0xFFFFFFFF// 如果新固件崩溃,看门狗复位回到Bootloader}if(config.upgrade_flag==0x55555555){// 试运行期间看门狗复位了,说明新固件有问题,回退config.active_bank=(config.active_bank==1)?2:1;// 切回旧Bankconfig.upgrade_flag=0xFFFFFFFF;flash_write_config(&config);}

实际项目中的经验

在虎王科技的工业物联网项目中,这套OTA方案已经部署在300+台设备上。几个经验数据:

  • 平均升级时间:220KB固件,4G网络下约90秒
  • 升级成功率:97%一次成功,3%需要重试,重试后100%成功
  • 变砖率:0。双Bank+看门狗回退策略下,从未变砖
  • 最常见失败原因:4G信号弱(工厂角落信号差),不是固件本身的问题

总结

STM32 OTA的核心就是双Bank + Bootloader + CRC校验 + 看门狗回退。这套组合拳打下来,设备在恶劣网络环境下也能安全升级。

4G模组选型上,移远EC25/EC20的AT指令最规范,文档最好。固件分片用HTTP Range是最简单可靠的方案。

OTA是物联网设备运维的基础设施,早做早受益。等到设备量上去了再补OTA,每个设备都要手动升级一遍,那就真的痛不欲生了。


沧州虎王科技,专注物联网软硬件开发、通信设备、嵌入式系统与设备管理平台。

http://www.jsqmd.com/news/1356757/

相关文章:

  • skbuild-docs-l10n
  • 2026年近期全国中秋包装盒供应厂家选择实战指南 - 装修教育财税推荐2026
  • T5 模型将所有 NLP 任务统一为“文本到文本“格式的意义是什么?
  • 开源项目二次开发中的Git代码同步策略与实践
  • 开源鸿蒙PC版开发指南与生态建设
  • 2026年8月湖南省怀化市移动单宽带避坑与办理指南 - 找卡家园
  • 抓包历史为什么从 sql.js 迁到原生 SQLite
  • GIS矢量数据处理:分散要素合并技术全解析
  • IEEE39节点Simulink建模与电力系统仿真实践
  • 智慧景区小程序开发实战:技术架构与性能优化
  • Unity中实现MuJoCo式位置控制:KV参数调优与插件开发实战
  • 2025届学术党必备的十大降重复率助手推荐
  • 蕉岭脱硫系统铜镍管件/海水循环铜镍合金管/铜镍带材联系方式-欣茂安钢业 - 企业推荐官【认证】
  • 昆泰芯 KTH5761|2.8~5.5V/-40~85℃三轴高精度数字 3D 线性霍尔 DFN2×2.5 微型封装
  • 工业气缸HD 6600 TK 50-28在严苛工况下的应用与优化
  • 生产级Java代码的线程安全与内存管理实战
  • Docker部署AiShort:构建私有提示词管理平台,提升AI协作效率
  • 医学论文解读:Reliable Multi-Prototypical Contrastive Learning for Semi-Supervised Heterogeneous and Multi-
  • fre:ac音频转换器终极指南:从技术原理到实战应用深度解析
  • PCIe CAN接口卡实战指南:从选型、部署到集成开发
  • 多智能体协作视频理解:基于“先猜后验”框架的长视频分析技术解析
  • 别只盯着640 TOPS:从SA8775P到SA8797P,高通真正升级的是整车计算架构
  • SolidWorks_标准零件库15_标准件与配置表
  • 自演化智体概览:在通往超级人工智能的道路上,应该演化什么、何时演化、如何演化以及在哪里演
  • 【STM32入门项目】DHT11温湿度监测与声光报警系统
  • 做了8年BI报表,2025年底我第一次怕:AI十分钟搭的大屏,我以前要熬两天
  • Flux模型+Krea风格+深度图ControlNet:AI图像生成全流程实战指南
  • Markdown语法详解
  • HashMap遍历性能优化:entrySet vs keySet深度解析
  • 从ReAct到Multi-Agent:AI智能体架构演进与实战指南