STM32+ESP8266在线OTA升级实战:腾讯云物联网平台固件远程更新方案 简介这份资源面向嵌入式与物联网开发者聚焦STM32与ESP8266组合下的在线OTA升级方案借助腾讯云物联网平台完成固件远程分发与更新。内容围绕设备上线、固件分发、下载校验、存储管理、烧录更新与状态反馈等关键环节展开适合已具备单片机基础、希望掌握远程固件升级技能的工程师与学习者参考。压缩包共288个文件约6.64MB以54个c源文件与52个h头文件为核心配合45个d依赖文件、45个o目标文件、42个crf交叉引用文件以及uvprojx、uvoptx工程配置、hex与bin固件、sct分散加载文件、map映射文件等完整呈现BootLoader与STM32F103C8_MD双工程的构建脉络。资源已有5980人学习下载可帮助读者理解串口通信、MQTT连接、固件分区与烧录验证的实现思路并对照工程结构排查编译与下载问题。1. STM32ESP8266在线OTA为什么你的设备出厂后还能“换脑子”设备卖出去之后发现固件有 bug或者客户想要加个新功能传统做法是派人到现场用 J-Link 重新烧录成本高得离谱。STM32 搭配 ESP8266 走腾讯云物联网平台做在线 OTA 升级解决的正是这个痛点设备联网后自己从云端拉取新固件校验、写入、重启全程不用拆机。这套方案适合做物联网终端、共享设备、远程采集节点的嵌入式开发者尤其是那些已经用 STM32 做主力 MCU、用 ESP8266 做联网模块的项目。核心思路是把 STM32 的 IAP 升级能力和 ESP8266 的 Wi-Fi 通信能力拼在一起云端负责存固件、发通知设备端负责下载和搬运。听起来简单但固件怎么分片、写到哪个 Flash 地址、升级标志位怎么设计、断网了怎么办每一步都有翻车的可能。下面按实际落地顺序拆开讲。2. 先搞懂 IAP 和 OTA 的分工STM32 管搬运ESP8266 管通信2.1 IAP 是地基OTA 是上层建筑STM32 的 IAPIn-Application Programming指的是程序自己擦写自己的 Flash把新固件写到指定区域然后跳转执行。没有 IAPOTA 就是空中楼阁。STM32 的 Flash 通常从 0x08000000 开始我们一般把它切成三块Bootloader 区、APP 运行区、下载暂存区。Bootloader 放在最前面负责判断“要不要升级”和“跳到哪个区执行”APP 区放当前运行的固件暂存区用来接收新固件等校验通过后再搬运到 APP 区。这里有个关键选择Bootloader 和 APP 的地址怎么分。以 STM32F103C8T6 为例64KB Flash常见分法是 Bootloader 占 0x08000000~0x08003FFF16KBAPP 从 0x08004000 开始暂存区放在 0x0800C000 往后。分的时候要算清楚 APP 编译出来的 bin 文件大小别让 APP 区装不下。我一般会在 Bootloader 里留一个升级标志位存在 Flash 最后一页或者备份寄存器里上电先读这个标志决定是跳 APP 还是进升级流程。OTA 则是在 IAP 基础上加了“远程”两个字。ESP8266 通过 Wi-Fi 连到腾讯云物联网平台订阅固件升级的 topic收到通知后把固件分片下载下来通过串口发给 STM32STM32 再按 IAP 的逻辑写 Flash。所以整个链路是云端 → ESP8266 → 串口 → STM32 → Flash。2.2 腾讯云物联网平台的 OTA 能力怎么用腾讯云物联网平台提供固件升级服务你需要在控制台创建产品、设备然后上传固件包。固件包一般是一个 bin 文件平台会生成版本号和 MD5 校验值。设备端通过 MQTT 协议订阅$ota/update/${productId}/${deviceName}这个 topic平台下发升级通知时会带上固件大小、版本号、MD5 等信息。ESP8266 收到通知后通过 HTTP 或者 MQTT 分片请求固件数据。常见做法是 ESP8266 用 AT 指令连腾讯云或者直接烧写支持 MQTT 的固件。如果你用的是 ESP8266 NonOS 开发环境可以自己写 MQTT 客户端如果图省事用 AT 固件也行但 AT 固件对 OTA 分片下载的支持有限需要自己拼 HTTP 请求。我一般会选 ESP8266 跑一个轻量 MQTT 客户端直接和腾讯云交互拿到固件数据后通过串口透传给 STM32。提示腾讯云物联网平台的 OTA 固件包有大小限制分片下载时每片建议不超过 4KB否则 ESP8266 的 RAM 扛不住。2.3 串口协议设计STM32 和 ESP8266 怎么对话STM32 和 ESP8266 之间用串口通信协议要简单可靠。我一般定义几种帧类型升级通知帧、数据帧、结束帧、确认帧。升级通知帧告诉 STM32 固件大小和 MD5数据帧携带固件分片每片带偏移地址结束帧表示传输完成确认帧由 STM32 回复告诉 ESP8266 这一片写成功了还是失败了。帧格式可以这样定帧头2 字节比如 0xAA 0x55 帧类型1 字节 数据长度2 字节 数据N 字节 CRC162 字节。STM32 收到数据帧后先校验 CRC再擦写对应 Flash 地址然后回确认帧。如果校验失败或者写失败回 NACKESP8266 重发这一片。重发次数超过 3 次就终止升级保留旧固件继续运行。这个协议看起来简单但实际调试时最容易出问题的是串口波特率和流控。ESP8266 在下载固件时数据量较大如果波特率太高比如 921600STM32 的串口中断处理不过来会丢数据。我一般用 115200 或者 230400并且开启硬件流控RTS/CTS或者用软件流控XON/XOFF。如果硬件不支持流控就在协议里加窗口机制STM32 每收到几帧就暂停一下等处理完再让 ESP8266 继续发。3. 从零搭建Bootloader 分区、串口协议和云端固件上传的完整步骤3.1 Bootloader 工程配置与 Flash 分区先建一个 Bootloader 工程用 STM32CubeMX 生成 HAL 库基础代码。关键在链接脚本.ld 文件里把 Bootloader 的 Flash 起始地址设为 0x08000000长度设为 16KB。APP 工程的链接脚本则把起始地址改成 0x08004000长度根据芯片剩余空间调整。下面是一个典型的 APP 工程 .ld 文件片段/* APP 工程链接脚本片段STM32F103C8T6APP 从 0x08004000 开始 */ MEMORY { RAM (xrw) : ORIGIN 0x20000000, LENGTH 20K FLASH (rx) : ORIGIN 0x08004000, LENGTH 48K }逻辑说明ORIGIN 改成 0x08004000 后编译出来的中断向量表偏移也要改。在 APP 的 main 函数开头调用SCB-VTOR 0x08004000;把向量表重定位到 APP 区。参数说明LENGTH 根据芯片实际 Flash 大小减去 Bootloader 和暂存区后得出别写超了否则编译会报错或者运行时跑飞。Bootloader 里要实现几个函数Flash 擦除、Flash 写入、跳转到 APP。跳转前先关中断设置主堆栈指针为 APP 区的第一个字然后跳转到 APP 区的第二个字复位向量。代码大概长这样/* Bootloader 跳转到 APP 的核心代码 */ typedef void (*pFunction)(void); void jump_to_app(uint32_t app_addr) { uint32_t jump_addr *(volatile uint32_t *)(app_addr 4); pFunction app_entry (pFunction)jump_addr; __disable_irq(); MSR_MSP(*(volatile uint32_t *)app_addr); app_entry(); }逻辑说明app_addr 是 APP 区的起始地址第一个字是栈顶地址第二个字是复位向量。MSR_MSP 是一个宏用来设置主堆栈指针。参数说明调用前要确保 APP 区已经有有效固件否则跳过去会 HardFault。3.2 ESP8266 端 MQTT 连接与固件分片下载ESP8266 这边如果用 NonOS 开发环境需要自己移植 MQTT 客户端。腾讯云物联网平台的 MQTT 接入信息包括设备三元组ProductId、DeviceName、DeviceSecret连接时用 HMAC-SHA1 算密码。连接成功后订阅 OTA topic收到升级通知后解析 JSON拿到固件大小和 MD5。固件下载可以用 HTTP也可以用 MQTT。腾讯云支持通过 MQTT 分片下发固件每片最大 4KB。ESP8266 收到分片后先存到自己的 Flash 或者直接通过串口转发给 STM32。如果 ESP8266 的 Flash 够大比如 4MB可以先存下来校验完再统一发给 STM32如果不够就边收边转发。下面是一个简化的 MQTT 消息处理伪代码/* ESP8266 收到 OTA 通知后的处理逻辑 */ void ota_notify_handler(char *json) { int firmware_size parse_json_int(json, size); char *md5 parse_json_string(json, md5); uart_send_ota_start(firmware_size, md5); // 通知 STM32 准备升级 int offset 0; while (offset firmware_size) { int chunk_size (firmware_size - offset) 4096 ? 4096 : (firmware_size - offset); char *chunk mqtt_request_firmware_chunk(offset, chunk_size); uart_send_data_frame(offset, chunk, chunk_size); // 通过串口发给 STM32 if (!wait_for_ack()) { // 等 STM32 确认 retry_count; if (retry_count 3) break; continue; } offset chunk_size; } uart_send_ota_end(); }逻辑说明先通知 STM32 升级开始然后循环请求固件分片每片通过串口发出去并等待确认。参数说明chunk_size 建议 2048 或 4096太大 ESP8266 内存不够太小传输效率低。retry_count 是重试次数超过 3 次就放弃避免死循环。3.3 STM32 端 Flash 写入与升级标志位管理STM32 收到数据帧后先判断帧类型。如果是升级开始帧擦除暂存区记录固件大小和 MD5如果是数据帧把数据写到暂存区的对应偏移如果是结束帧计算暂存区数据的 MD5和云端下发的比对一致就把升级标志位置位然后重启。Flash 写入要注意对齐。STM32 的 Flash 按半字16 位或字32 位编程写入前必须先擦除。擦除按页进行STM32F103 每页 1KB。写的时候用 HAL_FLASH_Program 函数参数选 FLASH_TYPEPROGRAM_HALFWORD 或 FLASH_TYPEPROGRAM_WORD。升级标志位我一般存在 Flash 的最后一页或者用备份寄存器BKP_DRx。存 Flash 的好处是不怕掉电坏处是每次升级都要擦写一页。存备份寄存器更简单但需要纽扣电池供电否则掉电丢失。下面是一个写标志位的例子/* 在 Flash 最后一页写入升级标志 */ #define OTA_FLAG_ADDR 0x0800FC00 // 最后一页起始地址 #define OTA_FLAG_VALUE 0x5A5A5A5A // 自定义标志值 void set_ota_flag(void) { HAL_FLASH_Unlock(); FLASH_EraseInitTypeDef erase; erase.TypeErase FLASH_TYPEERASE_PAGES; erase.PageAddress OTA_FLAG_ADDR; erase.NbPages 1; uint32_t page_error; HAL_FLASHEx_Erase(erase, page_error); HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, OTA_FLAG_ADDR, OTA_FLAG_VALUE); HAL_FLASH_Lock(); }逻辑说明先解锁 Flash擦除最后一页写入标志值再上锁。参数说明OTA_FLAG_ADDR 要根据芯片 Flash 大小调整别写到 APP 区去了。OTA_FLAG_VALUE 可以自定义只要 Bootloader 和 APP 约定好就行。Bootloader 启动时先读这个标志如果是 OTA_FLAG_VALUE就把暂存区的固件搬运到 APP 区搬运完清除标志再跳转到 APP。搬运过程也要校验 MD5防止暂存区数据损坏。3.4 腾讯云物联网平台固件上传与版本管理登录腾讯云物联网控制台进入产品详情找到“固件升级”模块。先创建固件版本填写版本号比如 1.0.1上传编译好的 bin 文件。平台会自动计算 MD5 和文件大小。然后选择要升级的设备下发升级任务。设备端收到通知后开始下载。版本号管理有个坑APP 固件里要有一个地方记录当前版本号Bootloader 搬运完新固件后APP 启动时上报版本号给云端云端才能知道升级成功了。我一般把版本号存在 APP 区的固定偏移或者用宏定义编译进去。上报版本号的 topic 是$ota/update/${productId}/${deviceName}/reportpayload 里带版本号和升级结果。注意腾讯云物联网平台的固件升级任务下发后设备如果不在线任务会保留一段时间。设备上线后重新订阅 topic 就能收到。但保留时间有限别指望离线太久还能收到。4. 避坑指南OTA 升级中最容易翻车的 5 个地方4.1 串口丢数据导致固件损坏现象升级完成后设备重启APP 跑不起来或者 HardFault。原因ESP8266 发送固件分片时STM32 串口中断处理不及时数据被覆盖写入 Flash 的固件不完整。解决降低波特率到 115200开启硬件流控或者在协议里加序号和重传机制STM32 每收到一帧就回 ACKESP8266 收到 ACK 才发下一帧。如果还是丢就在 STM32 端加一个环形缓冲区中断里只存数据主循环再处理。4.2 Flash 擦除地址算错把 Bootloader 擦了现象升级后设备彻底变砖连 Bootloader 都跑不起来。原因暂存区或者 APP 区的地址计算错误擦除时覆盖了 Bootloader 区。解决在 Bootloader 里加地址检查擦除前确认目标地址在合法范围内。APP 工程的链接脚本和 Bootloader 里的分区定义要一致最好用一个头文件统一定义。我一般会在 Bootloader 里硬编码一个分区表擦除前断言地址在暂存区范围内。4.3 升级标志位没清设备反复进 Bootloader现象升级成功后设备重启又进 Bootloader反复循环。原因Bootloader 搬运完固件后忘记清除升级标志或者清除失败。解决搬运完成后立即擦除标志页或者在跳转 APP 前把标志改成非 OTA_FLAG_VALUE 的值。另外APP 启动后也可以主动清一次标志双保险。4.4 MD5 校验不通过但没重试现象升级过程中网络抖动固件分片下载不完整MD5 校验失败设备直接停在 Bootloader。原因没有重试机制或者重试次数太少。解决ESP8266 端在 MD5 校验失败后重新请求整个固件或者缺失的分片。STM32 端在暂存区 MD5 校验失败后不要置升级标志保留旧固件继续运行同时通过 ESP8266 上报升级失败。腾讯云平台支持升级失败重试可以在控制台配置重试策略。4.5 电源不稳导致 Flash 写入失败现象升级过程中设备突然重启固件损坏。原因Flash 写入时电流较大如果电源供电不足电压跌落会导致写入失败甚至芯片复位。解决升级前确保电源稳定最好加一个大电容。另外Bootloader 里可以在写入前检查电压低于阈值就拒绝升级。如果设备用电池供电升级时建议插上充电器。5. 进阶技巧差分升级、断点续传和升级成功率统计5.1 差分升级只传变化的部分全量包升级每次都要传整个固件流量和时间都浪费。差分升级只传新旧固件的差异部分设备端用差分算法合并出完整固件。常见做法是用 bsdiff 或者 hdiffpatch 生成差分包设备端集成对应的合并库。STM32 的 RAM 和 Flash 有限合并时可能需要外部 Flash 做缓存。我一般会在 ESP8266 端做差分合并因为 ESP8266 的 Flash 和 RAM 比 STM32 宽裕合并完再通过串口发给 STM32。这样 STM32 端不用改还是收全量固件。5.2 断点续传网络断了不用从头来设备下载固件到一半断网了重新连上后如果从头下载浪费流量和时间。断点续传的思路是记录已下载的偏移重新连接后从断点继续。ESP8266 端可以在 Flash 里记录当前下载进度每次请求固件分片时带上偏移。腾讯云物联网平台的 OTA 接口支持按偏移请求所以实现起来不难。STM32 端也要配合暂存区里已经写入的数据不要重复擦除只写缺失的部分。5.3 升级成功率统计用数据驱动优化升级成功率是衡量 OTA 方案好坏的核心指标。我一般会在设备端记录每次升级的结果成功、失败原因、耗时通过 MQTT 上报到云端。腾讯云物联网平台有数据统计功能可以看升级成功率曲线。如果发现某个版本失败率突然升高就要排查是固件问题还是网络问题。下面是一个简单的上报格式{ version: 1.0.1, result: success, duration: 45, retry_count: 0, error_code: 0 }逻辑说明version 是目标版本号result 是升级结果duration 是耗时秒retry_count 是重试次数error_code 是错误码。参数说明error_code 可以自定义比如 1 表示串口超时2 表示 MD5 校验失败3 表示 Flash 写入失败。云端收到后可以按版本、按设备分组统计。5.4 一个具体技巧用双备份区实现无缝升级如果 Flash 够大可以搞双备份区APP 区放当前运行固件备份区放新固件。升级时新固件写到备份区校验通过后修改启动地址下次重启直接跑备份区。这样升级过程中设备不用停机旧固件继续运行新固件准备好后一键切换。STM32F103 的 Flash 通常不够但 STM32F407 或者带外部 Flash 的型号可以这么玩。我做过一个项目用 STM32F407W25Q128双备份区加外部 Flash升级成功率从 85% 提到了 99%。最后说个血泪教训OTA 升级的测试一定要覆盖异常场景比如升级中断电、断网、串口拔线。我早期做的一个项目实验室测试全过现场升级时因为电源纹波导致 Flash 写入失败设备变砖了几十台。后来加了电压检测和双备份区才解决。做 OTA永远要留后悔药。希望帮到你。本文还有配套的精品资源点击获取