
为什么要做OTA你往客户的工厂发了500台设备每台设备跑着固件V1.0。三天后发现一个bug——串口数据解析在高湿度环境下偶尔丢包。没有OTA的话派工程师去现场一台台用USB烧录。500台设备假设每台5分钟加上路上时间一周也搞不完。有OTA的话服务器上点一下推送500台设备自动下载新固件5分钟全部升级完毕。OTA不是锦上添花是物联网设备的运维刚需。STM32 OTA的技术难点STM32做OTA比ESP32麻烦得多。ESP32有现成的分区表和OTA库几行代码搞定。STM32需要自己处理所有事情Bootloader上电后先跑Bootloader判断是否需要升级双Bank设计Flash分两个区一个跑当前固件一个写新固件升级时切换Flash写入STM32写Flash要先擦除再写擦除最小单位是页2KB/4KB4G模组通信通过AT指令下载固件文件断点续传网络不稳定时能续传校验机制下载完成后CRC校验防止刷入坏固件变砖硬件方案[STM32F407] --UART-- [EC25 4G模组] --4G网络-- [OTA服务器] | [SPI Flash W25Q128] -- 存储新固件 | [内部Flash Bank1/Bank2] -- 运行固件选STM32F407的原因1MB Flash支持双BankBank1: 512KB, Bank2: 512KB192KB RAM固件下载缓冲够用支持SPI外挂W25Q12816MB存固件文件价格合适批量约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代码#includestm32f4xx_hal.h#includeflash_if.h#includeota_config.h#defineBANK1_ADDR0x08008000#defineBANK2_ADDR0x08040000#defineCONFIG_ADDR0x08078000typedefstruct{uint32_tmagic;uint32_tactive_bank;// 1 or 2uint32_tfirmware_size;uint32_tfirmware_crc;uint32_tupgrade_flag;// 0xFFFFFFFFno upgrade, 0x55555555upgrading}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_bank1){app_addrBANK1_ADDR;}else{app_addrBANK2_ADDR;}// 检查是否有升级完成待切换if(config.upgrade_flag0xAAAAAAAA){// 新固件已写入并校验通过切换Bankconfig.active_bank(config.active_bank1)?2:1;config.upgrade_flag0xFFFFFFFF;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_addr4));app_main();}// 如果跳转失败停在Bootloaderwhile(1){// 可以在这里加串口下载模式作为fallback}}应用固件中的OTA下载#defineFIRMWARE_MAX_SIZE(224*1024)// 224KBexternUART_HandleTypeDef huart2;// 4G模组UARTuint8_tfirmware_buf[FIRMWARE_MAX_SIZE];uint32_tfirmware_offset0;voidota_check_and_download(void){// 1. HTTP请求检查是否有新版本uint8_thttp_cmd[256];snprintf((char*)http_cmd,sizeof(http_cmd),ATQHTTPURL\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,ATQHTTPGET80\r\n);// 解析返回的JSON获取version和size// 3. 下载固件uint32_ttotal_sizefirmware_info.size;uint32_tchunk_size4096;firmware_offset0;while(firmware_offsettotal_size){uint32_tremaintotal_size-firmware_offset;uint32_tread_size(remainchunk_size)?chunk_size:remain;// 通过HTTP GET Range下载分片snprintf((char*)http_cmd,sizeof(http_cmd),ATQHTTPGET80,\Range: bytes%d-%d\\r\n,firmware_offset,firmware_offsetread_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_buffirmware_offset,buf,read_size);firmware_offsetread_size;}// 4. CRC校验uint32_tcrccrc32(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.magic0xDEADBEEF;config.active_bank1;// 当前在Bank1切到Bank2config.firmware_sizetotal_size;config.firmware_crccrc;config.upgrade_flag0xAAAAAAAA;// 待切换标志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($latestversion_compare($latest[version],$device[firmware_version])0){echojson_encode([has_updatetrue,version$latest[version],size$latest[file_size],crc$latest[file_crc],urlhttp://ota.heicat.com/firmware/.$latest[filename]]);}else{echojson_encode([has_updatefalse]);}断点续传4G网络不稳定下载16MB固件中途断开是常有的事。解决方案分片下载每次请求4KB用HTTP Range头本地记录进度写入Flash配置区重启后从断点继续失败重试单次下载失败重试3次3次失败则等待5分钟后重试防变砖设计OTA最怕的是刷入坏固件导致设备变砖。防砖措施CRC校验下载完成后必须校验不通过不写入双Bank切换新固件写到备用Bank不覆盖当前运行固件Bootloader回退如果新固件启动失败看门狗复位Bootloader自动回退到旧Bank硬件恢复接口预留UART或SWD接口最坏情况下可物理烧录看门狗回退逻辑// Bootloader中if(config.upgrade_flag0xAAAAAAAA){// 新固件第一次启动config.upgrade_flag0x55555555;// 标记试运行flash_write_config(config);// 跳转到新固件// 如果新固件正常会在运行时把flag清为0xFFFFFFFF// 如果新固件崩溃看门狗复位回到Bootloader}if(config.upgrade_flag0x55555555){// 试运行期间看门狗复位了说明新固件有问题回退config.active_bank(config.active_bank1)?2:1;// 切回旧Bankconfig.upgrade_flag0xFFFFFFFF;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每个设备都要手动升级一遍那就真的痛不欲生了。沧州虎王科技专注物联网软硬件开发、通信设备、嵌入式系统与设备管理平台。