OTA 空中升级:不拔线给 ESP32-S3 远程刷固件
- 理解 ESP-IDF 的 factory/ota_0/ota_1 + otadata 双分区与 A/B 回滚机制,知道升级失败为什么不会变砖
- 用 esp_https_ota 一把梭实现最简单的 HTTPS OTA,成功后 esp_restart 重启进新固件
- 用 esp_ota_ops(get_next_update_partition / begin / write / end / set_boot_partition)细粒度地边下边写,看透 OTA 的底层流程
- 知道分区表怎么选带 OTA 的方案、固件大小 vs 分区大小、HTTPS 证书、断电中断和签名校验这几个必踩的坑
设备做完了,外壳合上、螺丝拧紧、装上墙——这时候你发现固件里有个灯的闪烁逻辑要改。总不能把它从墙上拆下来、撬开外壳、接上数据线、idf.py flash 一遍再装回去吧?要是这样的设备有 50 台呢?
这就是 OTA 要解决的事。OTA = Over-The-Air,空中升级——不接任何线,通过 WiFi 把新固件远程推给 ESP32-S3。装在天花板上的传感器、出货到客户家的开关、车间里几十台联网的机器,全靠它远程维护。这一节让你的设备具备这个能力。
读这篇前,你的 ESP32-S3 得已经能连上 WiFi——OTA 全程走 WiFi,连不上网就谈不上空中升级;本篇直接复用那套 wifi_init_sta() 骨架。本篇不接任何线,纯软件,调试同样靠 idf.py monitor 看 ESP_LOGI 打出来的日志。
先把一句话放最前面,这是 ESP-IDF 跟 Arduino 在 OTA 上最大的不同:Arduino 的 ArduinoOTA / Update.begin/write/end 把"双分区切换"这层全藏起来了,ESP-IDF 反过来把它摊在你面前。 这一层恰恰是 OTA 不变砖的命根子——理解了 factory/ota_0/ota_1 这套分区机制,你才真敢往远程设备上推固件。
为什么需要 OTA,以及它凭什么不会把板子刷成砖
数据线烧录的前提是"板子在你手边、能插上线"。可一旦设备出了门,这个前提就没了:
- 装进了封闭空间——嵌墙开关、吊顶传感器,拆一次比写代码还累。
- 已经出货——发给客户、铺到现场,你根本碰不到那块板子。
- 批量太多——50 台、500 台,逐个插线
idf.py flash是不可能完成的体力活。
OTA 把"烧录"这件事从物理动作变成了一次网络传输。但这里有个让人不安的问题:烧到一半断电了怎么办?固件传错了怎么办?设备不就变砖了吗? ESP-IDF 的设计早就想到了这点,靠的就是分区表里那套 OTA slot。
factory + ota_0 + ota_1 + otadata:双分区 A/B 回滚的核心
这是全篇的眼,也是 Arduino 替你藏起来的东西。ESP-IDF 的 Flash 不是只存一份固件,带 OTA 的分区表会划出好几块 app 分区:
factory:出厂固件分区(可选)。第一次用数据线烧进去的程序默认跑在这。ota_0/ota_1:两个 OTA slot,A/B 双分区。运行时只有一个是"当前固件",另一个空着备用。otadata:一块很小的数据分区,记录"下次该从哪个 slot 启动"。Bootloader 开机先读它,决定跳进factory还是ota_0还是ota_1。
升级流程是这样的:
- 设备正常跑在
ota_0,新固件下载并写到空着的ota_1——这期间老固件照常跑,没受任何影响。 - 写完先校验(ESP-IDF 自动校验固件头的 magic、SHA256 摘要、芯片型号是否对得上),合法才更新
otadata,把启动指针指向ota_1。 esp_restart()重启,Bootloader 读otadata,从ota_1启动新固件。
关键在于:下载和写入全程不碰正在运行的那份固件。如果下载途中断电,otadata 还没更新,下次开机 Bootloader 照样从老分区 ota_0 启动,设备照常工作,只是这次升级没成功而已。
更狠的是 ESP-IDF 的回滚(rollback):新固件启动后,如果它在规定时间内没主动调 esp_ota_mark_app_valid_cancel_rollback()"报平安"(比如新固件一跑就 crash 重启),Bootloader 下次会自动退回上一个能用的分区。这就是 OTA 不会轻易变砖的底气——它永远留着一条退路。明白了这层,你就敢放心给远程设备推固件了。
用过 Arduino ArduinoOTA 的话,对比一下就懂差别了:Arduino 那套你只调 Update.begin/write/end,分区切换、otadata、回滚全在库里黑箱跑,你看不见也控制不了。ESP-IDF 把这几步全交到你手上——刚上手是麻烦点,但产品级设备出了问题你能精确知道卡在哪一步。
两条路线,先看清楚再动手
ESP-IDF 做 OTA 有两条路线,别一上来就抄代码,先搞清楚区别:
| 路线 | API | 你要操心什么 | 适合 |
|---|---|---|---|
| esp_https_ota 一把梭 | esp_https_ota() |
几乎啥都不用,填个 URL 和证书 | 入门、绝大多数 HTTPS 拉固件场景 |
| esp_ota_ops 细粒度 | esp_ota_begin/write/end/set_boot_partition |
自己控制下载、边下边写、进度、校验 | 进阶、要自定义传输(如 MQTT/BLE 推固件)或讲透原理 |
简单说:esp_https_ota 是把"下载 + 写分区 + 切 boot"打包成一个函数,esp_ota_ops 是把这几步拆开让你自己拼。 这一节两条都给完整可跑代码——先用 esp_https_ota 快速跑通,再用 esp_ota_ops 把底层流程看透。固件本身放在一台 HTTP(S) 服务器上(你自己的服务器、对象存储、或本机起个简易 server 都行)。
完整代码一:esp_https_ota,一把梭最省事
最简单的 OTA:给一个固件的 HTTPS 地址,esp_https_ota() 一个函数把下载、写 ota_1、切 boot 全干完,返回 ESP_OK 你就 esp_restart()。把下面这段放进工程的 main/main.c(前提是 wifi_init_sta() 已经把网连上):
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_https_ota.h"
#include "esp_http_client.h"
static const char *TAG = "ota";
// 把目标服务器的 CA 根证书在编译期嵌进固件(见下方说明)
extern const char server_cert_pem_start[] asm("_binary_server_cert_pem_start");
extern const char server_cert_pem_end[] asm("_binary_server_cert_pem_end");
void do_https_ota(void) {
ESP_LOGI(TAG, "开始 OTA,从服务器拉固件…");
esp_http_client_config_t http_cfg = {
.url = "https://你的服务器/firmware.bin", // 新固件地址
.cert_pem = server_cert_pem_start, // HTTPS 必须带证书
.timeout_ms = 15000,
.keep_alive_enable = true,
};
esp_https_ota_config_t ota_cfg = {
.http_config = &http_cfg,
};
esp_err_t ret = esp_https_ota(&ota_cfg); // 下载 + 写 ota_1 + 切 boot,一把梭
if (ret == ESP_OK) {
ESP_LOGI(TAG, "OTA 成功,3 秒后重启进新固件");
vTaskDelay(pdMS_TO_TICKS(3000));
esp_restart(); // 重启,Bootloader 从新分区启动
} else {
ESP_LOGE(TAG, "OTA 失败: %s", esp_err_to_name(ret)); // 失败照样跑老固件,没变砖
}
}
void app_main(void) {
ESP_ERROR_CHECK(nvs_flash_init());
wifi_init_sta(); // 复用上一篇的骨架,阻塞到连上 WiFi
do_https_ota(); // 连上网之后再 OTA,顺序别反
}
几个关键点讲透:
esp_http_client_config_t http_cfg是底层传输的配置:填url(固件地址)、cert_pem(服务器证书)、超时。esp_https_ota内部就是用esp_http_client去下载的——这跟你上一篇 HTTP 请求 用的是同一套客户端。esp_https_ota_config_t ota_cfg把 http 配置包一层:最少只要填.http_config。它还有bulk_flash_erase、回调等高级项,入门用不上。esp_https_ota(&ota_cfg)一句话干完三件事:下载固件 → 写到esp_ota_get_next_update_partition选出的空闲 slot(通常是ota_1)→ 写完校验通过就更新otadata切 boot。返回ESP_OK代表全程成功。- 成功后必须自己
esp_restart():esp_https_ota只负责把新固件准备好、把 boot 指针切过去,它不会自动重启,得你来。重启后 Bootloader 才会真正跳进新固件。 - 失败了也不慌:返回非
ESP_OK(网断了、证书不对、固件太大写不下),老固件原封不动还在ota_0跑着,这次没升成而已——这就是双分区的安全网。
你应该看到什么
idf.py monitor 的日志里依次滚出(中间夹着系统打的 esp_https_ota 底层日志,正常):
I (3210) ota: 开始 OTA,从服务器拉固件…
I (3450) esp_https_ota: Starting OTA...
I (8120) esp_image: segment 0: ...
I (9530) ota: OTA 成功,3 秒后重启进新固件
... 设备重启,进入新固件 ...
- 看到
Starting OTA...说明连上服务器、开始下载了。 - 中间
esp_image: segment那几行是 ESP-IDF 在校验下载下来的固件镜像——这一步过了才说明固件合法、没传错。 - 最后
OTA 成功+ 重启,再起来跑的就是新固件了,全程没碰过数据线。 - 想确认真的换了新固件?在固件里
#define FW_VERSION "1.1.0",app_main开头ESP_LOGI(TAG, "固件版本 %s", FW_VERSION),重启后看打印的版本号变没变。
嫌折腾证书?自测阶段可以先用 http://(明文):把 .url 改成 http://...,.cert_pem 删掉,菜单里 Component config → ESP HTTPS OTA → Allow HTTP for OTA 打开就能跑。但上线务必换回 HTTPS 带证书——OTA 走明文等于谁都能往你设备塞固件,下面"坑"那节会专门说。
完整代码二:esp_ota_ops,把底层流程拆开看透
esp_https_ota 好用,但它是个黑箱——你不知道里面怎么选分区、怎么边下边写、怎么切 boot。真做产品(比如固件不是走 HTTP 而是走 MQTT 推送、或者要自己显示精确下载进度),你得用 esp_ota_ops 这套底层 API 自己拼。它把 OTA 拆成清清楚楚的几步,正好让你看透原理:
#include "esp_ota_ops.h"
#include "esp_http_client.h"
#include "esp_log.h"
#include "esp_system.h"
static const char *TAG = "ota";
#define OTA_BUF_SIZE 1024
void do_ota_ops(void) {
// ① 选出"下一个该写"的分区——当前跑 ota_0,它就返回 ota_1,反之亦然
const esp_partition_t *update_part = esp_ota_get_next_update_partition(NULL);
if (update_part == NULL) {
ESP_LOGE(TAG, "找不到可写的 OTA 分区——分区表没配 OTA?");
return;
}
ESP_LOGI(TAG, "新固件将写入分区: %s (offset 0x%lx)",
update_part->label, (unsigned long)update_part->address);
// ② 打开 HTTP 连接准备下载固件
esp_http_client_config_t http_cfg = {
.url = "http://你的服务器/firmware.bin",
.timeout_ms = 15000,
};
esp_http_client_handle_t client = esp_http_client_init(&http_cfg);
if (esp_http_client_open(client, 0) != ESP_OK) { // 0 = 这次没有请求 body
ESP_LOGE(TAG, "连不上服务器");
esp_http_client_cleanup(client);
return;
}
esp_http_client_fetch_headers(client); // 读响应头,拿到 Content-Length
// ③ begin:在目标分区上开一个 OTA 写入会话
esp_ota_handle_t ota_handle = 0;
esp_err_t err = esp_ota_begin(update_part, OTA_SIZE_UNKNOWN, &ota_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_begin 失败: %s", esp_err_to_name(err));
esp_http_client_cleanup(client);
return;
}
// ④ 循环:边从网络读、边写进分区(这就是"边下边写",不占大内存)
char buf[OTA_BUF_SIZE];
int total = 0;
while (1) {
int n = esp_http_client_read(client, buf, OTA_BUF_SIZE);
if (n < 0) { ESP_LOGE(TAG, "下载出错"); esp_ota_abort(ota_handle); goto cleanup; }
if (n == 0) break; // 读到头了,下载完成
err = esp_ota_write(ota_handle, buf, n); // 把这一块写进 ota_1
if (err != ESP_OK) { ESP_LOGE(TAG, "写分区失败"); esp_ota_abort(ota_handle); goto cleanup; }
total += n;
ESP_LOGI(TAG, "已写入 %d 字节", total); // 这里能算精确进度
}
// ⑤ end:收尾并校验整个固件镜像(校验不过这里就报错,不会切 boot)
err = esp_ota_end(ota_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "固件校验失败: %s", esp_err_to_name(err)); // 镜像损坏/不合法
goto cleanup;
}
// ⑥ 切 boot:更新 otadata,让下次从新分区启动
err = esp_ota_set_boot_partition(update_part);
if (err != ESP_OK) { ESP_LOGE(TAG, "切换启动分区失败"); goto cleanup; }
ESP_LOGI(TAG, "OTA 完成,重启进新固件");
esp_http_client_cleanup(client);
esp_restart();
cleanup:
esp_http_client_cleanup(client);
}
对着代码把六步走一遍,你就彻底懂 OTA 在干嘛了:
- ①
esp_ota_get_next_update_partition(NULL):这是双分区的灵魂。它自动算出"当前没在跑的那个 slot"——你跑在ota_0,它返回ota_1;你跑在ota_1,它返回ota_0。你永远不会把新固件写到正在运行的分区上,老固件全程安全。返回NULL就是分区表里压根没 OTA 分区(见下方"坑")。 - ③
esp_ota_begin:在选出的分区上开一个写入会话,会先擦除目标分区。OTA_SIZE_UNKNOWN表示固件大小未知(让它擦整个分区)。 - ④
esp_ota_write循环——这就是"边下边写":固件可能有 1MB 多,ESP32-S3 的 RAM 才几百 KB,绝不能整个下载到内存再写。所以是读一小块(1KB)就写一小块进 Flash,内存里永远只占一个小 buffer。这层 Arduino 的Update.write也是这么干的,只是你看不见循环。 - ⑤
esp_ota_end:收尾,并对刚写进去的整个镜像做 SHA256 校验。校验不过就在这里报错返回——这一步是"固件传错/传坏不会被启动"的保险,它通过了才允许切 boot。 - ⑥
esp_ota_set_boot_partition:更新otadata分区,把"下次启动指针"指向新 slot。注意:到这一步之前otadata都没动,所以前面任何一步失败、断电,下次开机还是从老固件起——退路一直在。 esp_ota_abort:中途出错时调它放弃这次会话、释放资源,别让半截固件占着分区。
你应该看到什么
I (3210) ota: 新固件将写入分区: ota_1 (offset 0x110000)
I (3260) ota: 已写入 1024 字节
I (3265) ota: 已写入 2048 字节
...
I (9800) ota: 已写入 1048576 字节
I (9950) ota: OTA 完成,重启进新固件
- 第一行打出
ota_1——证明它确实选了"另一个" slot(当前跑在ota_0)。重启后再跑这段,这里会变成ota_0,两个 slot 轮换,这就是 A/B。 - "已写入 N 字节"一路涨到固件总大小——这是你自己掌控进度的好处,
esp_https_ota黑箱里你看不到这个。 - 如果
esp_ota_end打了"固件校验失败",说明下载的 bin 损坏或不是合法 ESP32-S3 镜像,它不会切 boot,重启后还是老固件——校验这关把住了。
分区表:先满足这个硬性前提
上面两段代码能跑的大前提是:你的工程用了带 OTA slot 的分区表。 默认的 partitions_singleapp.csv 只有一个 factory 分区,没有 ota_0/ota_1,esp_ota_get_next_update_partition 会返回 NULL、esp_https_ota 直接失败。两种配法:
配法一(推荐):menuconfig 选现成的。 idf.py menuconfig → Partition Table → Partition Table 选 Factory app, two OTA definitions。这个内置方案就带 factory + ota_0 + ota_1 + otadata,开箱即用。
配法二:自己写 partitions.csv。 在工程根目录放一个,menuconfig 里 Partition Table 选 Custom partition table CSV,内容长这样:
# Name, Type, SubType, Offset, Size
nvs, data, nvs, , 0x4000
otadata, data, ota, , 0x2000
phy_init, data, phy, , 0x1000
factory, app, factory, , 1M
ota_0, app, ota_0, , 1M
ota_1, app, ota_1, , 1M
otadata那行(data, ota)必须有,它记录启动指针,没它回滚和切 boot 都无从谈起。ota_0/ota_1两个 app 分区是 A/B 的本体,大小要装得下你的固件。- 改完分区表第一次得用数据线
idf.py flash烧一遍(分区表本身要写进 Flash),之后才能走 OTA。
还要盯两件事:
- Flash 至少 4MB。 三个 app 分区各 1MB 就吃掉 3MB 了,ESP32-S3 常见的是 4MB/8MB/16MB,够用;老的 2MB 板子塞不下。
- 单个固件不能超过单个 OTA 分区的大小。 你的固件编译后大小(
idf.py build末尾会打印 app 用了多少字节,或看idf.py size)必须小于ota_0分区的 1MB,否则esp_ota_write写到一半空间不够直接失败。固件偏大就把分区调大、或精简代码/关掉用不到的组件。
头号翻车:代码写得没错,但分区表还是默认的单 app 方案,跑起来 esp_ota_get_next_update_partition 返回 NULL、或 esp_https_ota 报 Partition not found。OTA 的前置不是代码、是分区表——动代码前先把 Factory app, two OTA definitions 选上、烧一遍。
几个绕不过的坑
OTA 比普通联网代码更容易翻车,因为它在改 Flash、还涉及远程和安全。这几个必须心里有数:
- HTTPS 证书。 上线必须走
https://,得把目标服务器的 CA 根证书编进固件:把.pem证书文件用target_add_binary_data()(在CMakeLists.txt里)嵌进去,运行时填到cert_pem;或者用证书包esp_crt_bundle_attach(config.crt_bundle_attach = esp_crt_bundle_attach)省去手动管证书。证书过期/换了,固件里的也得跟着更新——这是 OTA 长期运维的隐性成本。 - 断电中断。 下载/写分区途中断电是常态。放心:只要没走到
esp_ota_set_boot_partition/esp_https_ota成功返回那步,otadata没动,下次开机还是老固件——双分区天然抗断电。你要做的只是失败后重试。 - 固件大小 vs 分区大小。 前面说过,固件一旦超过
ota_0的容量,esp_ota_write必然失败。发版前用idf.py size看一眼 app 大小留足余量。 - 签名校验(安全)。 默认 ESP-IDF 只校验固件镜像"完整、是合法 ESP32-S3 镜像",不校验"是不是你发的"。生产环境要开 Secure Boot + 固件签名:设备只接受用你私钥签过的固件,防止有人往设备塞恶意固件。个人项目可以先不开,但联网产品出货前这是必做项。
- 回滚要主动"报平安"。 开了 rollback(
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE)后,新固件启动起来必须在确认自己正常后调esp_ota_mark_app_valid_cancel_rollback(),否则下次重启会被判定"升级后跑挂了"自动退回老固件。别忘了加这一句,不然好固件也会被莫名回滚。
最危险也最容易忽略的是安全:OTA 接口等于给设备开了一扇"远程改写大脑"的门。不设防、走明文、不验签名出货,等于谁连上你的服务器/中间人劫持,就能把任意固件刷进你的设备。个人练手没关系,真出货前 HTTPS + 固件签名一个都不能少,更系统的设备安全见安全基线。
故障排查:OTA 不灵,按这个顺序查
| 现象 / 日志 | 最可能的原因 | 怎么办 |
|---|---|---|
esp_ota_get_next_update_partition 返回 NULL / Partition not found |
分区表没带 OTA slot | menuconfig 选 Factory app, two OTA definitions,数据线烧一遍 |
esp_https_ota 报 ESP_ERR_HTTPS_OTA_* 或连接失败 |
URL 不对 / 服务器没起 / 网没就绪 | 确认 IP_EVENT_STA_GOT_IP 那条日志出来了;URL 复制到浏览器能不能下到 bin |
HTTPS 报证书相关错误(mbedtls -0x2700 之类) |
证书没配 / 配错 / 站点证书换了 | 核对 cert_pem 是该服务器的 CA 根证书;自测先用 http:// 排除证书干扰 |
esp_ota_write 写到一半失败、空间不足 |
固件超过单个 OTA 分区大小 | idf.py size 看 app 大小;把 ota_0/ota_1 分区调大或精简固件 |
esp_ota_end 报校验失败 / image header 错 |
下载的 bin 损坏 / 不是合法 ESP32-S3 镜像 | 重新生成 bin(build/工程名.bin,别传 bootloader/partition-table 的 bin);确认 set-target 是 esp32s3 |
| 升级成功重启后跑了一阵又退回老固件 | 开了 rollback 但新固件没"报平安" | 新固件正常后调 esp_ota_mark_app_valid_cancel_rollback() |
| 下载途中断电,重启后还是老固件 | 这是正常且正确的行为 | 双分区保护,没切 boot 就回老固件;联网后重试 OTA 即可 |
真的"变砖"在 ESP32-S3 上很难发生——只要 Bootloader 没被破坏,永远能用数据线重新 idf.py flash 救回来。OTA 的双分区 + 校验 + 回滚更是叠了三层保险。所以大胆试,刷坏了数据线烧一次就回来了。
变体:HTTP OTA 自动检查更新 + 版本号管理
前面两段都是"调一次就升级"。量产几十上百台设备时,得让设备自己定时去服务器查有没有新固件,发现更新了才升。思路是:
- 你把新固件 bin 和一个版本号文件(如
version.txt写1.2.0)放到服务器/对象存储上。 - 设备开机或定时(比如每天一次)先
esp_http_clientGET 那个版本号文件,和自己当前#define FW_VERSION "1.1.0"比。 - 服务器版本更高,才调
esp_https_ota/esp_ota_ops拉 bin 升级,否则跳过——别每次开机都重刷一遍,既费流量又折腾 Flash 寿命。
// 连好 WiFi 后,先比版本,再决定要不要 OTA
char *latest = http_get_text("https://你的服务器/version.txt"); // 见 l3-http 那套 GET
if (strcmp(latest, FW_VERSION) > 0) {
ESP_LOGI(TAG, "发现新版本 %s(当前 %s),开始升级", latest, FW_VERSION);
do_https_ota();
} else {
ESP_LOGI(TAG, "已是最新版 %s,无需升级", FW_VERSION);
}
版本号管理是规模化 OTA 的灵魂:固件里写死 FW_VERSION、每次发版手动加一;服务器放最新版本号供比对。规模化的远程固件维护,本质就是"版本号比对 + HTTPS 拉取 + 双分区升级 + 回滚兜底"这几件事的自动化,固件工程化那一级会把它做成完整的发布流程。
动手挑战
别只看,动手把 OTA 跑成一个完整闭环:
- menuconfig 选
Factory app, two OTA definitions分区表,写一个让板载 LED 每 1 秒闪一次的程序(复用你 L1 的闪灯逻辑),数据线idf.py flash烧进去。 - 在另一台机器/本机用 Python 起个最简单的服务器托管固件:
python -m http.server 8000(自测用,走http://那条路就行)。 - 改代码让 LED 每 0.2 秒闪一次,
idf.py build生成新build/工程名.bin,拷到服务器目录。 - 用 完整代码二(esp_ota_ops) 那段,把
.url指向http://你的电脑IP:8000/工程名.bin,触发 OTA。全程不碰数据线,看着灯从慢闪变快闪——你就完成了第一次真正的远程固件更新,还亲眼看到了"已写入 N 字节"和分区从ota_0切到ota_1。 - 进阶:在新固件
app_main里加esp_ota_mark_app_valid_cancel_rollback(),再开 menuconfig 的 rollback,理解"报平安"机制。
卡住了?把代码、idf.py monitor 的完整日志和"我看到了什么、期望看到什么"一起发给 AI,它定位会准得多。
本篇代码为参考实现。OTA 直接改写 Flash、风险比一般联网更高,esp_https_ota/esp_ota_ops 的函数签名、分区表字段、回滚接口随 ESP-IDF 版本可能微调——上线前务必对照官方 OTA 与分区表文档自校一遍,先用本地 http:// 把流程跑通、确认回滚生效,再上 HTTPS + 签名正式部署。
小结 · 你现在掌握了什么
- 你理解了 OTA 为什么是出货后维护设备的刚需,以及 ESP-IDF 的 factory/ota_0/ota_1 + otadata 双分区 + A/B 回滚 为什么让升级失败、断电、新固件跑挂都不会变砖。
- 你能用
esp_https_ota一把梭快速跑通 HTTPS OTA,成功后esp_restart()进新固件。 - 你能用
esp_ota_ops(get_next_update_partition/begin/ 循环write边下边写 /end校验 /set_boot_partition)细粒度控制,把 OTA 的底层流程看得明明白白。 - 你知道了 OTA 的硬性前提(带 OTA slot 的分区表 + 4MB+ Flash)和必踩的坑(HTTPS 证书、固件大小、签名校验、回滚报平安)。
设备能远程升级了,但前面代码里 WiFi 名和密码还是写死的——换个网络又得重烧。下一步学SmartConfig 与配网,让用户用手机就能把 WiFi 信息传给设备,彻底告别硬编码密码。想看 L3 整条进阶路线,回L3 关卡总览或完整路线图。
本文为公开资料整理,非亲测。关键参数与代码请结合实物与下列官方来源验证。