尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

嵌入式 OTA 空中升级:从“为什么要升级“到 ESP32 实战

嵌入式 OTA 空中升级:从“为什么要升级“到 ESP32 实战 适用人群刚学完 STM32/ESP32 基础、听说过远程升级但没真正跑通过的同学读完你能得到① 搞懂 OTA 到底在解决什么问题② 看懂 ESP32 怎么几十行代码就跑通 OTA③ 避开新手最容易把设备刷成砖的 5 个坑。一、先别管技术看个你一定会遇到的场景假设你做了个智能温湿度传感器卖了 1000 台给客户装在人家家里、仓库里。两周后你发现自己代码里有个 bug某次浮点计算溢出温度高于 40℃ 时读数变成负数。现在问题来了——你怎么把这 1000 台设备修好❌ 方案 A挨家挨户上门拆壳、接串口、用 ST-Link 重新烧录。1000 台人先废了。❌ 方案 B让客户自己拆壳烧录。客户不是工程师大概率把设备搞坏还来找你退货。✅ 方案 COTA。设备本身连着 Wi-Fi你在全国任何地方把修好的固件往服务器一传设备半夜自己下载、自己刷进去、重启bug 没了客户毫无感知。OTA Over-The-Air空中升级设备通过网络把新固件下载到本地 Flash自己完成擦写、校验、重启全程不需要你人到现场、不需要拆机接线。一句话记住它传统升级是人带着线去设备跟前OTA 是新固件自己飞到设备里。二、前置概念先搞懂这 6 个词否则后面代码看天书名词白话解释固件 Firmware跑在 MCU 上的程序就是你idf.py build/keil编出来的那个.bin文件BootloaderMCU 上电后第一个跑的一小段程序。它决定接下来跑哪份 app“要不要升级”。ESP32 的 bootloader 出厂就带STM32 要自己写分区表 Partition Table整颗 Flash 被切成好几块每块规定干啥放 app、放 Wi-Fi 配置、放空的备用区。相当于 Flash 的土地使用规划A/B 双分区双 BankFlash 里放两份 app一份正在跑A一份接新固件B。下载完校验 OK下次启动切到 B回滚 Rollback新固件跑起来发现起不来 / 有 bug自动或手动切回 A设备不死机、不报废校验 / 签名防止下载到一半断电或被人篡改的固件被刷进去。常用 CRC、SHA256、RSA 签名 为什么要用 A/B 双分区因为设备正在跑的 app 不能自己擦自己你不能在脚底下抽砖。所以新固件必须写到另一块空地写好了再切换过去。三、OTA 的原理一张图说清全流程Flash 里简化布局长这样┌─────────────────────────────────────────┐ ← Flash 顶端 │ bootloader 出厂引导不可轻易动 │ ├─────────────────────────────────────────┤ │ partition table 记录下面每块是啥 │ ├─────────────────────────────────────────┤ │ ota_0 (app) ← 当前正在跑的固件 │ ├─────────────────────────────────────────┤ │ ota_1 (app) ← 备用区新固件写这里 │ ├─────────────────────────────────────────┤ │ ota_data ← 记着下次启动跑 ota_0 │ │ 还是 ota_1 │ └─────────────────────────────────────────┘ ← Flash 底端标准 OTA 流程建议背下来面试常问1. 设备联网 → 问服务器我有 v1.0有没有新版 2. 服务器有 v1.1地址在这 3. 设备下载 v1.1 的 .bin一般走 HTTP/HTTPS或 MQTT 透传 4. 设备把 .bin 写进 ota_1当前没在跑的那块 5. 写完做校验长度对不对、SHA256/签名对不对 6. 校验通过 → 改 ota_data标记下次启动跑 ota_1 7. 设备重启 → bootloader 读 ota_data → 跑 ota_1v1.1 8. 新固件自检 OK → 提交从此 ota_1 成为正式版本 │ 失败/崩溃重启 ↓ bootloader 发现没提交 → 自动回滚跑 ota_0v1.0第 8 步的提交/回滚是 OTA 的命门下文实战会重点讲。四、ESP32 ESP-IDF 实战学生最省事的上手路径为什么用 ESP32 讲因为乐鑫把 OTA 整套都封装好了bootloader 自带、分区表改个配置就行、esp_ota_ops.h把擦写校验全包了。你几乎不用碰底层寄存器适合先跑通建立信心。4.1 改分区表让 Flash 支持双 appESP-IDF 工程里找到partitions.csv或partitions_two_ota.csv改成下面这样# Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, factory, app, factory, 0x10000, 1M, ota_0, app, ota_0, , 1M, ota_1, app, ota_1, , 1M, otadata, data, ota, , 0x2000,逐列解释Name/Type/SubType名字、类型app 还是 data、子类型。Offset 留空让烧录工具自动往后排不容易算错地址。Size 1M每块给 1MB。⚠️ 必须≥ 你编译出的 app 实际大小建议留 10%~20% 余量否则 OTA 写入直接失败。otadata 分区Typedata, SubTypeota必须显式声明ESP-IDF 的 OTA 流程依赖它记录下次从哪个 ota 分区启动。官方文档明确要求分区表至少包含ota_0、ota_1和一个ota_data分区并非自动生成。大小 0x2000 足够。然后在menuconfig里Partition Table → Partition Table选你这个 csvExample Configuration里把 OTA 用的 Wi-Fi SSID/密码填好实操部分以 ESP-IDF 编程指南的 OTA 章节为准里面明确写了分区表必须包含 ota_data 分区。4.2 核心代码下载新固件并写入下面这段代码就是把 HTTP 下载到的字节流边收边写到 ota_1的关键逻辑基于 ESP-IDF 官方例程system/ota精简#includeesp_ota_ops.h#includeesp_http_client.h#includeesp_log.hstaticconstchar*TAGOTA;staticconstchar*urlhttp://192.168.1.100:8080/firmware.bin;esp_err_tdo_ota_update(void){/* 1. 建一个 HTTP 客户端指向固件地址 */esp_http_client_config_tconfig{.urlurl,.timeout_ms5000,};esp_http_client_handle_tclientesp_http_client_init(config);if(esp_http_client_open(client,0)!ESP_OK){esp_http_client_cleanup(client);returnESP_FAIL;}/* 2. 拿到当前没在跑的那个分区新固件写这里 */constesp_partition_t*update_partesp_ota_get_next_update_partition(NULL);/* 3. 开写ota_handle 是这次升级的句柄 */esp_ota_handle_tota_handle;if(esp_ota_begin(update_part,OTA_SIZE_UNKNOWN,ota_handle)!ESP_OK){returnESP_FAIL;}/* 4. 边下载边写每次读 1KB立刻 esp_ota_write 进 Flash */intcontent_lenesp_http_client_fetch_headers(client);char*bufmalloc(1024);inttotal0,read_len;while((read_lenesp_http_client_read(client,buf,1024))0){esp_ota_write(ota_handle,buf,read_len);totalread_len;}free(buf);/* 5. 关键校验下载字节数 ! 服务器说的长度 → 半截固件放弃 */if(total!content_len){ESP_LOGE(TAG,下载不完整 %d/%d,total,content_len);esp_ota_abort(ota_handle);esp_http_client_cleanup(client);returnESP_FAIL;}/* 6. 收尾校验内部会算 SHA256 */if(esp_ota_end(ota_handle)!ESP_OK){esp_http_client_cleanup(client);returnESP_FAIL;}/* 7. 切换启动分区下次重启就跑新固件 */esp_err_terresp_ota_set_boot_partition(update_part);esp_http_client_cleanup(client);returnerr;// 返回 ESP_OK 后调用处执行 esp_restart() 即可}逐行要点新手必看esp_ota_get_next_update_partition(NULL)自动挑当前没在跑的分区不用你手动判断 A 还是 B。esp_ota_begin / write / end这三件套是原子操作——没end成功之前bootloader 不会切过去。total ! content_len这步是提前中止优化网络抖一下、固件只下了 80%先拦下来避免后面无谓写入。⚠️ 注意若服务器用chunked 分块传输esp_http_client_fetch_headers可能返回 -1此时长度比对会误判失败——所以这一行只是优化真正的完整性校验在下一步。esp_ota_end内部会做完整性校验校验镜像头里的 SHA256这才是兜底长度对但数据被篡改照样拦下。二者配合最稳不要只依赖长度。4.3 回滚新固件起不来怎么办ESP-IDF 用的是待提交机制——这是 OTA 最关键的安全设计esp_ota_set_boot_partition()之后新固件处于未提交状态。新固件正常跑起来、确认自己没问题调用下面这行提交// 新固件 main() 里确认一切正常后再调用esp_ota_mark_app_valid_cancel_rollback();如果新固件有 bug、起不来、反复崩溃重启bootloader 发现它一直没提交自动回滚到旧版本。设备照样能跑不会成砖。⚠️血泪经验一定要在确认能干活之后再mark valid。比如在连上服务器 传感器初始化成功 业务线程起来了之后。千万别app_main第一句就 mark——万一后面崩了已经提交就回不去了。五、新手最容易把设备刷成砖的 5 个坑#坑后果正确做法1ota_0/ota_1 分区比 app 小写入失败 / 烧录报错分区 Size ≥ app 实际大小留 20% 余量2忘了 ota_data 分区bootloader 不知道启动哪个永远跑 factory用 ESP-IDF 默认分区模板ota_data 自动生成3不校验就切换半截固件被当真刷进去 →变砖必须esp_ota_end 长度比对4一启动就 mark valid新固件其实有 bug但已提交回滚不了 →变砖业务自检 OK 后再 mark5用 HTTP 明文传固件被中间人篡改刷进恶意固件量产上 HTTPS 固件签名校验记住一个总原则OTA 的底线不是升得上是升坏了也不死。A/B 未提交回滚就是为了这条底线。六、进阶方向知道有这些面试能接话STM32 自己写 bootloader用 Ymodem/串口或自定义协议收固件写内部 FlashCRC 校验后跳转到 app。难点在中断向量表偏移__set_MSP 地址重映射后面可以单独写一篇。Nordic nRF MCUbootZephyr 生态的成熟 OTA 方案原生支持签名、加密、A/B。差分升级Delta OTA只传新旧固件的差异包流量能省 90%适合 4G 流量贵、NB-IoT 窄带的场景。安全启动Secure Boot硬件级防刷非法固件配合 Flash 加密防止别人把你设备刷成别家的。七、动手练一练照着做一遍才真的会拿一块 ESP32打开 ESP-IDF 例程system/ota用idf.py menuconfig配好 Wi-Fi 和分区表先build通过。在电脑起一个本地 HTTP 服务器python -m http.server 8080把一个改过版本号的.bin放进去。烧录旧固件观察日志设备联网 → 下载 → 重启 → 跑新固件打印出新版本号 ✅。故意搞破坏放一个损坏/截断的.bin看设备会不会因为校验失败而拒绝升级、老固件照常跑。验证回滚在新固件main()里故意vTaskDelay(10000)之后再mark valid期间手动复位观察会不会回滚到旧版本。这 5 步全跑通你对 OTA 的理解就已经超过 80% 的初学者了。小结OTA 不是什么高深黑科技本质就三件事下载到备用区 → 校验 → 切换启动。ESP32 帮你把底层全包了重点是把校验和回滚这两道安全锁理解透。先把例程跑通再回头看本文的 5 个坑你会发现自己已经能接住量产级的升级需求了。下一篇预告《STM32 自己写 Bootloader实现串口升级》
返回列表