← 返回教程库

OTA 空中升级:不拔线给 ESP32-S3 远程刷固件

最后更新 2026-06-22
L3 · 联网与 IoT ⏱ 约 22 分钟 🟢 软件/低风险
你将学到
  • 理解 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 monitorESP_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

升级流程是这样的:

  1. 设备正常跑在 ota_0,新固件下载并写到空着的 ota_1——这期间老固件照常跑,没受任何影响。
  2. 写完先校验(ESP-IDF 自动校验固件头的 magic、SHA256 摘要、芯片型号是否对得上),合法才更新 otadata,把启动指针指向 ota_1
  3. 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_1esp_ota_get_next_update_partition 会返回 NULLesp_https_ota 直接失败。两种配法:

配法一(推荐):menuconfig 选现成的。 idf.py menuconfigPartition TablePartition TableFactory app, two OTA definitions。这个内置方案就带 factory + ota_0 + ota_1 + otadata,开箱即用。

配法二:自己写 partitions.csv 在工程根目录放一个,menuconfig 里 Partition TableCustom 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_otaPartition 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_attachconfig.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_otaESP_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 自动检查更新 + 版本号管理

前面两段都是"调一次就升级"。量产几十上百台设备时,得让设备自己定时去服务器查有没有新固件,发现更新了才升。思路是:

  1. 你把新固件 bin 和一个版本号文件(如 version.txt1.2.0)放到服务器/对象存储上。
  2. 设备开机或定时(比如每天一次)先 esp_http_client GET 那个版本号文件,和自己当前 #define FW_VERSION "1.1.0" 比。
  3. 服务器版本更高,才调 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 跑成一个完整闭环:

  1. menuconfig 选 Factory app, two OTA definitions 分区表,写一个让板载 LED 每 1 秒闪一次的程序(复用你 L1 的闪灯逻辑),数据线 idf.py flash 烧进去。
  2. 在另一台机器/本机用 Python 起个最简单的服务器托管固件:python -m http.server 8000(自测用,走 http:// 那条路就行)。
  3. 改代码让 LED 每 0.2 秒闪一次idf.py build 生成新 build/工程名.bin,拷到服务器目录。
  4. 完整代码二(esp_ota_ops) 那段,把 .url 指向 http://你的电脑IP:8000/工程名.bin,触发 OTA。全程不碰数据线,看着灯从慢闪变快闪——你就完成了第一次真正的远程固件更新,还亲眼看到了"已写入 N 字节"和分区从 ota_0 切到 ota_1
  5. 进阶:在新固件 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_opsget_next_update_partition / begin / 循环 write 边下边写 / end 校验 / set_boot_partition)细粒度控制,把 OTA 的底层流程看得明明白白。
  • 你知道了 OTA 的硬性前提(带 OTA slot 的分区表 + 4MB+ Flash)和必踩的坑(HTTPS 证书、固件大小、签名校验、回滚报平安)。

设备能远程升级了,但前面代码里 WiFi 名和密码还是写死的——换个网络又得重烧。下一步学SmartConfig 与配网,让用户用手机就能把 WiFi 信息传给设备,彻底告别硬编码密码。想看 L3 整条进阶路线,回L3 关卡总览完整路线图

📄 来源 / 自校链接

本文为公开资料整理,非亲测。关键参数与代码请结合实物与下列官方来源验证。

内容有错、看不懂、或想看下一期?告诉我们 →

本文为公开资料的学习整理,非亲测。涉接线/花钱/合规的步骤请结合实物与官方最新资料验证,风险自负。见免责声明