ESP-IDF保姆级入门21|OTA空中升级全解:双分区原理/断点续传/升级校验/故障回滚/批量升级,掌握物联网设备远程固件升级工程化方案
专栏前言
上一篇我们掌握了Flash存储与文件系统,实现了参数、文件的可靠持久化,解决了设备掉电数据丢失的问题。
对于量产部署的物联网设备而言,另一个核心刚需就是固件远程升级——设备安装到现场后,不可能逐一拆壳烧录固件,OTA(Over-The-Air)空中升级是所有联网设备的标配功能。
很多新手对OTA的理解停留在“下载固件写到Flash里”,但实际工程化的OTA是一套完整的可靠性体系:双分区备份保证升级失败不砖机、断点续传适配弱网场景、签名校验保障固件安全、故障自动回滚兜底,每一个环节出问题都可能导致设备现场瘫痪。
ESP-IDF 提供了原生的OTA框架,从底层分区切换到高层网络升级都有成熟的组件支持,无需自己实现底层逻辑。
本篇一次性讲透ESP-IDF下的完整OTA方案:
双分区启动原理 → 标准升级流程 → HTTPS OTA基础实战 → 断点续传与签名校验 → 故障自动回滚机制 → 批量升级策略 → 最佳实践与踩坑汇总
全程配可编译源码+架构图解,零基础也能实现工业级可靠的远程固件升级。
一、OTA核心基础与双分区架构
1. 什么是OTA空中升级
OTA 是通过无线网络远程更新设备固件的技术,无需物理接触设备、无需现场烧录器,即可完成功能迭代、Bug修复、配置更新。
核心价值:大幅降低设备运维成本,支持产品快速迭代,是量产物联网设备的必备功能。
2. 双分区启动原理
ESP-IDF 的 OTA 采用双分区交替升级架构,是其可靠性的核心保障。Flash 中划分两个大小完全相同的应用分区(ota_0、ota_1),两者地位对等,交替作为“运行分区”和“升级分区”。
工作机制:
- 设备启动时,Bootloader 读取专门的
ota_data数据分区,根据其中的启动标记,决定从ota_0还是ota_1加载固件 - 执行升级时,新固件只写入当前未运行的空闲分区,全程不触碰正在运行的固件
- 新固件写入并校验通过后,修改
ota_data的启动标记,下次重启自动切换到新分区 - 升级过程中任何环节失败(断电、下载失败、校验失败),都不影响当前运行的系统,设备永远保留一个可正常启动的固件
下面是双分区交替启动的完整流程:
3. 完整OTA分区组成
一个标准的OTA分区表包含以下核心区域:
| 分区 | 作用 | 说明 |
|---|---|---|
| Bootloader | 启动引导 | 负责校验分区、选择启动分区、加载固件 |
| ota_0 | 应用分区1 | 存放固件,可作为运行分区或升级分区 |
| ota_1 | 应用分区2 | 与ota_0大小完全一致,对等交替使用 |
| ota_data | 启动配置区 | 存储启动选择标记、版本信息、升级状态 |
| factory(可选) | 出厂固件分区 | 出厂默认固件,作为最终保底回滚分区 |
| NVS / SPIFFS | 数据分区 | 参数、文件存储,升级时不改动,数据保留 |

【配图1:ESP32 OTA双分区启动架构图】
配图说明:展示Flash内各分区的分布,Bootloader、ota_data、两个OTA应用分区的关系,标注启动选择、升级写入的数据流方向,直观呈现双分区交替工作的原理。
二、标准升级流程与核心API
1. OTA标准五步流程
一次完整可靠的OTA升级,严格遵循以下流程,任何一步失败都终止并保留原系统:
- 版本检测:设备连接升级服务器,查询最新固件版本,对比自身版本判断是否需要升级
- 固件下载:通过HTTP/HTTPS通道接收固件数据,支持流式下载边下边写
- 分区写入:将接收的固件数据按顺序写入空闲的OTA分区
- 完整性校验:写入完成后,校验固件的CRC、SHA256或数字签名,确保完整合法
- 启动切换:校验通过后更新ota_data启动标记,重启设备从新固件启动
下面是 OTA 标准五步流程的完整链路:
2. 核心API
ESP-IDF 提供了两层OTA接口:底层原生OTA API和高层封装组件。
- 底层原生API:灵活可控,适合自定义升级流程
// 开始OTA,获取目标空闲分区
esp_err_t esp_ota_begin(const esp_partition_t *partition, size_t image_size, esp_ota_handle_t *out_handle);
// 写入固件数据
esp_err_t esp_ota_write(esp_ota_handle_t handle, const void *data, size_t size);
// 结束OTA,校验固件
esp_err_t esp_ota_end(esp_ota_handle_t handle);
// 设置该分区为下次启动分区
esp_err_t esp_ota_set_boot_partition(const esp_partition_t *partition);
- 高层HTTPS OTA组件:封装了网络下载、证书校验、分区写入全流程,一行调用即可完成升级,是绝大多数场景的首选。
三、实战1:HTTPS OTA最简实现
这是最常用的入门方案,使用ESP-IDF原生的esp_https_ota组件,自动完成网络下载、分区写入、校验全流程,代码简洁、可靠性高。
1. 实现功能
- 连接WiFi网络
- 连接指定HTTPS升级服务器,下载最新固件
- 自动写入空闲OTA分区并校验
- 校验通过自动切换启动分区,重启运行新固件
2. 完整可编译源码
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_https_ota.h"
#include "nvs_flash.h"
#define TAG "OTA_DEMO"
// WiFi配置
#define WIFI_SSID "你的WiFi名称"
#define WIFI_PASS "你的WiFi密码"
// 升级固件URL
#define FIRMWARE_URL "https://你的服务器地址/firmware.bin"
// WiFi连接事件处理
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
}
}
// WiFi初始化
static void wifi_init(void)
{
esp_netif_init();
esp_event_loop_create_default();
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&cfg);
esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL);
wifi_config_t wifi_config = {
.sta = {
.ssid = WIFI_SSID,
.password = WIFI_PASS,
},
};
esp_wifi_set_mode(WIFI_MODE_STA);
esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
esp_wifi_start();
// 等待连接成功
uint8_t retry = 0;
while(retry < 10) {
vTaskDelay(pdMS_TO_TICKS(1000));
if(esp_wifi_connect() == ESP_OK) break;
retry++;
}
ESP_LOGI(TAG, "WiFi连接完成");
}
// OTA升级任务
void ota_task(void *pvParameters)
{
ESP_LOGI(TAG, "开始OTA升级,固件地址:%s", FIRMWARE_URL);
esp_http_client_config_t config = {
.url = FIRMWARE_URL,
.timeout_ms = 10000,
};
esp_https_ota_config_t ota_config = {
.http_config = &config,
};
// 执行HTTPS OTA升级,阻塞直到完成或失败
esp_err_t ret = esp_https_ota(&ota_config);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "OTA升级成功,准备重启");
vTaskDelay(pdMS_TO_TICKS(2000));
esp_restart();
} else {
ESP_LOGE(TAG, "OTA升级失败,错误码:%d", ret);
}
vTaskDelete(NULL);
}
void app_main(void)
{
nvs_flash_init();
wifi_init();
// 创建OTA升级任务
xTaskCreate(ota_task, "ota_task", 8192, NULL, 5, NULL);
}
3. 关键说明
- 分区表需要选择包含双OTA分区的配置,如
partition_table_double,或自定义分区表 - 量产场景建议配置服务器证书校验,避免中间人攻击
- 升级任务栈建议设大一些,网络协议栈需要一定的栈空间

【配图2:OTA标准升级流程图】
配图说明:按步骤展示版本检测→固件下载→分区写入→校验→切换启动→重启运行的完整流程,标注每个环节的失败处理路径,直观呈现升级的可靠性逻辑。
四、进阶工程化:断点续传、签名校验与故障回滚
基础OTA只能满足简单场景,量产工业级设备还需要解决弱网升级、安全校验、失败兜底三大核心问题。
1. 断点续传
针对大固件、低带宽、网络不稳定的场景,断点续传可以在下载中断后,下次升级从中断的位置继续写入,无需从头开始,大幅节省流量和时间。
- 实现原理:每次写入固件后,将当前写入偏移量保存到NVS中
- 升级中断后,下次启动先读取已写入偏移量,跳过已下载部分,从断点继续接收
- 配合Flash写入校验,确保已写入部分数据正确
- 适用场景:固件大小超过1MB、移动网络/低功耗广域网、网络信号不稳定的现场设备
2. 固件签名与完整性校验
这是量产设备的安全底线,防止恶意固件、篡改固件、损坏固件写入设备。
- 完整性校验:使用SHA256算法校验固件整体哈希,确保下载过程中没有损坏、截断
- 数字签名校验:服务器端用私钥对固件签名,设备端用公钥验证签名,只有官方发布的固件才能被升级
- ESP-IDF 原生支持安全启动(Secure Boot)功能,硬件级校验固件签名,非法固件无法启动也无法写入
3. 故障自动回滚机制
这是OTA的最后一道保障,确保即使新固件有Bug、启动失败,设备也能自动恢复,不会变砖。
回滚工作流程
- 升级完成后,新固件标记为待确认状态
- 设备重启运行新固件,业务逻辑正常运行后,调用API确认升级成功
- 如果指定时间内没有确认(新固件死机、崩溃、业务异常),Bootloader 在下一次启动时自动回滚到上一个正常的旧固件
- 极端情况下,两个OTA分区都损坏,自动回滚到factory出厂分区保底
下面是故障自动回滚的完整状态流转:
核心API
// 确认当前运行的固件正常,取消回滚
esp_err_t esp_ota_mark_app_valid_cancel_rollback(void);
// 标记当前固件无效,立即回滚
esp_err_t esp_ota_mark_app_invalid_rollback_and_reboot(void);
工程建议:不要一启动就确认成功,要等核心业务都运行正常、关键外设都初始化完成后再确认,确保异常能触发回滚。

【配图3:OTA故障自动回滚机制示意图】
配图说明:展示升级→待确认→运行验证→确认成功/超时回滚的完整状态流转,标注不同状态下的启动分区选择,直观呈现回滚兜底的工作逻辑。
五、工程最佳实践
1. 版本与升级策略
- 语义化版本号:采用
主版本.次版本.修订号的规范,明确兼容关系,禁止跨大版本强制升级 - 灰度升级:先升级小批量设备验证稳定性,没问题再逐步全量推送,避免批量故障
- 差分升级:大固件场景使用差分升级,只传输新旧固件的差异部分,流量可减少90%以上
- 定时升级:避开业务高峰期、网络高峰期,选择闲时自动升级,降低对业务的影响
2. 可靠性设计
- 升级前检查:检查Flash剩余空间、电源电压、WiFi信号强度,条件不满足不发起升级
- 状态持久化:升级进度、版本信息存入NVS,断电重启后可恢复
- 多重保底:保留factory出厂分区,双OTA分区+出厂分区三重备份
- 日志记录:完整记录升级过程、失败原因、版本切换记录,便于现场排查
3. 安全设计
- 必须使用HTTPS加密传输,禁止明文HTTP升级,防止固件泄露和篡改
- 量产设备开启安全启动与固件签名校验,硬件级拒绝非法固件
- 升级服务器增加设备身份认证,只有授权设备才能获取固件
- 敏感设备增加升级白名单,指定设备才能升级特定版本
六、新手高频踩坑汇总
- 升级后设备变砖,无法启动
- OTA分区大小设置不足,固件大小超出分区容量
- 编译固件的分区表和设备实际Flash分区不匹配
- 升级中途意外断电,分区数据损坏,且未开启自动回滚
- 新固件本身有致命Bug,启动直接死机,没有确认回滚机制
- OTA升级频繁失败
- 网络不稳定,大固件下载中途断开,没有断点续传
- HTTPS服务器证书过期、域名不匹配,连接校验失败
- Flash擦写异常,或剩余空间不足
- 固件校验不通过,下载的文件不完整
- 提示升级成功,但重启还是旧版本
- 写入完成后忘记调用
esp_ota_set_boot_partition,启动标记未更新 ota_data分区异常,Bootloader仍读取旧的启动配置- 新固件启动失败,自动回滚到旧版本,误以为升级未生效
- 固件版本号判断逻辑错误,重复升级同一个版本
- 写入完成后忘记调用
- 量产批量升级出事故
- 全量设备同时升级,服务器带宽不足,大量设备下载失败
- 没有灰度验证,新固件有Bug导致大面积设备异常
- 没有回滚机制,升级失败后设备全部现场瘫痪
- 未开启签名校验,存在恶意固件攻击风险
下一篇预告(22篇)
ESP-IDF保姆级入门22|WiFi联网与网络编程全解:STA/AP双模式、TCP/UDP Socket编程、HTTP客户端服务端、网络调试与优化,掌握物联网设备的网络通信基础,实现设备与云端的可靠数据交互!
更多推荐



所有评论(0)