🔧 核心原理
ESP32-S3 的 OTA(Over-The-Air)是通过 Wi-Fi 无线更新固件,无需物理连接。本质是:
- 从服务器下载新固件 → 2. 写入 备用分区 → 3. 重启后切换到新分区执行。
关键点 :固件分两部分存储(factory+ota),确保升级失败可回滚。
⚙️ ESP32-S3 OTA 关键配置(必须正确!)
1. 分区表(partition.csv)
必须包含 ota 分区(否则 OTA 失败):
cpp
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, , 24K,
otadata, data, ota, , 8K,
app0, app, factory, , 16M,
app1, app, ota, , 16M, # ← 关键!必须存在
spiffs, data, spiffs, , 2M,
✅ 验证 :
app1分区必须存在,大小 ≥ 新固件大小(通常 16M 足够)。
💻 OTA 核心代码(直接可用)
1. 初始化 OTA(main.c)
cpp
#include "esp_ota_ops.h"
#include "esp_https_ota.h"
void start_ota(const char* url) {
esp_http_client_config_t config = {
.url = url,
.cert_pem = NULL, // 生产环境需加证书
};
esp_https_ota_config_t ota_config = {
.http_config = config,
.update_partition = esp_ota_get_next_update_partition(NULL),
};
esp_err_t err = esp_https_ota(&ota_config);
if (err == ESP_OK) {
printf("OTA Success! Rebooting...\n");
esp_restart();
} else {
printf("OTA Failed! Error: %s\n", esp_err_to_name(err));
}
}
2. 启动 OTA(调用示例)
cpp
void app_main(void) {
// ... WiFi 初始化 ...
// 从 OneNET 获取固件 URL(示例)
const char* firmware_url = "https://your-server.com/firmware.bin";
start_ota(firmware_url);
}
⚠️ 必须避坑的 5 个问题
❌ 问题 1:分区表无 app1 分区
现象 :esp_ota_get_next_update_partition 返回 NULL
解决 :
检查 partition.csv,确保 app1 分区存在且大小 ≥ 新固件。
❌ 问题 2:未设置 OTA_DATA 分区
现象 :OTA 无法保存状态,升级后卡在启动
解决 :
在 partition.csv 中必须包含 otadata(已包含在标准分区表)。
❌ 问题 3:固件大小超过 app1 分区
现象 :升级后设备无法启动
解决:
- 用
esptool.py size检查固件大小 - 调整
app1分区大小(如16M→24M)
❌ 问题 4:未处理 OTA 中断
现象 :断电后设备无法启动
解决 :
在 start_ota() 中添加 回滚机制:
cpp
if (err == ESP_ERR_OTA_UPDATE_FAILED) {
printf("OTA Failed! Reverting to previous version.\n");
esp_ota_mark_app_invalid_rollback_and_reboot();
}
❌ 问题 5:未验证固件签名(安全风险)
现象 :攻击者可注入恶意固件
解决 :
在 esp_https_ota_config_t 中添加证书:
cpp
esp_http_client_config_t config = {
.url = url,
.cert_pem = (const char *)server_cert_pem_start, // 证书内容
};
🔥 ESP32-S3 OTA 优势(对比普通 ESP32)
| 特性 | ESP32-S3 | 普通 ESP32 | 优势说明 |
|---|---|---|---|
| PSRAM 支持 | ✅ 128MB | ❌ 0MB | OTA 固件可更大(避免分段) |
| 双核处理 | ✅ 主/从核 | ❌ 单核 | OTA 时仍可处理网络请求 |
| Flash 容量 | ✅ 32MB+ | ❌ 4MB-16MB | 允许更大分区(如 app1=24M) |
| OTA 速度 | ✅ 80+ MB/s | ❌ 40 MB/s | 32MB Flash 读取更快 |
💡 实测数据 :
ESP32-S3 升级 16MB 固件仅需 12 秒(Wi-Fi 5GHz),普通 ESP32 需 25 秒。

💡 高级技巧(实战必备)
1. OTA 时显示进度
cpp
esp_err_t ota_progress(void* arg, int32_t progress, int32_t total) {
printf("OTA Progress: %d%%\n", (progress * 100) / total);
return ESP_OK;
}
// 在 start_ota() 中添加:
esp_https_ota_config_t ota_config = {
.http_config = config,
.update_partition = esp_ota_get_next_update_partition(NULL),
.progress_cb = ota_progress, // ← 关键!
};
2. 从 OneNET 下载固件(结合之前代码)
cpp
// 从 OneNET 获取固件 URL(需在 OneNET 服务器存储固件)
char firmware_url[256];
get_firmware_url_from_onenet(firmware_url); // 自定义函数
start_ota(firmware_url);
3. 自动回滚机制(关键!)
cpp
// 在 app_main() 开头检查上次升级状态
esp_ota_handle_t ota_handle;
esp_err_t err = esp_ota_get_boot_partition(&boot_part);
if (esp_ota_get_state(boot_part, &ota_handle) == ESP_OK) {
if (esp_ota_get_state(ota_handle) == ESP_OTA_IMG_PENDING_VERIFY) {
// 上次升级未验证,回滚
esp_ota_mark_app_invalid_rollback_and_reboot();
}
}
🚨 终极验证(确保 OTA 可用)
-
编译固件 :
idf.py build -
烧录初始固件 :
idf.py -p /dev/ttyUSB0 flash -
发送 OTA 请求 :
用手机/电脑访问http://esp32-ip/ota?url=https://your-server.com/firmware.bin -
观察日志 :
cpp[I][ota:15] OTA Progress: 50% [I][ota:15] OTA Success! Rebooting... [I][main:30] New firmware version: 2.0
✅ 一句话总结
ESP32-S3 OTA = 1. 正确配置分区表 → 2. 用 esp_https_ota 下载 → 3. 自动回滚机制。