1. OTA 升级原理与分区表
ESP32 的 OTA 功能依赖于其 Flash 分区表。默认分区表通常包含两个 ota_0 和 ota_1 应用分区,以及一个 otadata 分区。升级时,新固件被写入当前未运行的 OTA 分区(例如当前运行在 ota_0,则写入 ota_1),写入完成后,通过设置 otadata 中的引导计数器,使系统在下次重启时切换到新分区。若新固件启动失败,引导程序会自动回滚到旧分区,保证设备不“变砖”。
分区表示例(partitions.csv):
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x4000,
otadata, data, ota, 0xd000, 0x2000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 0x200000,
ota_0, app, ota_0, 0x210000, 0x200000,
ota_1, app, ota_1, 0x410000, 0x200000,
- 每个 OTA 分区大小需一致,且足够容纳固件(本例为 2MB)。
-
factory分区可选,若存在,首次启动会运行它,之后 OTA 分区优先。
2. HTTP OTA 实现步骤
2.1 硬件与软件准备
- 硬件:ESP32 开发板(如 ESP32-DevKitC)、USB 线。
- 软件:ESP-IDF v5.x 或 Arduino ESP32 核心(本文以 ESP-IDF 为例)。
- 服务器:任意 HTTP 服务器(如 Python
http.server),存放编译好的.bin固件文件。
2.2 配置工程
在 menuconfig 中启用 OTA 支持:
-
Component config → ESP HTTPS OTA→ 勾选Allow HTTP for OTA(若用 HTTP 而非 HTTPS)。 - 设置分区表为自定义 CSV(
Partition Table → Custom partition CSV file)。
2.3 编写 OTA 代码
核心逻辑:连接 Wi-Fi → 发起 HTTP GET 请求 → 流式写入 OTA 分区 → 校验 → 重启。
#include <stdio.h>
#include <string.h>
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_ota_ops.h"
#include "esp_http_client.h"
#include "esp_https_ota.h"
static const char *TAG = "OTA_HTTP";
// Wi-Fi 配置
void wifi_init(void) {
// 初始化 NVS、Wi-Fi 等(省略详细代码)
// 连接你的 AP
}
// HTTP OTA 任务
void ota_task(void *pvParameter) {
esp_http_client_config_t http_config = {
.url = "http://192.168.1.100:8000/firmware.bin",
.timeout_ms = 10000,
};
esp_https_ota_config_t ota_config = {
.http_config = &http_config,
};
ESP_LOGI(TAG, "Starting OTA...");
esp_err_t ret = esp_https_ota(&ota_config);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "OTA success, restarting...");
esp_restart();
} else {
ESP_LOGE(TAG, "OTA failed: %s", esp_err_to_name(ret));
}
vTaskDelete(NULL);
}
void app_main(void) {
wifi_init();
xTaskCreate(&ota_task, "ota_task", 8192, NULL, 5, NULL);
}
2.4 编译与生成固件
- 编译工程:
idf.py build。 - 生成的固件位于
build/your_project.bin。 - 将该
.bin文件复制到 HTTP 服务器目录,并启动服务器:python3 -m http.server 8000
3. 完整示例:带进度显示与错误处理
以下代码增加进度回调,并处理网络错误:
// 进度回调
void ota_progress_cb(int progress, int total) {
static int last_percent = -1;
int percent = (progress * 100) / total;
if (percent != last_percent) {
ESP_LOGI(TAG, "Progress: %d%%", percent);
last_percent = percent;
}
}
void ota_task(void *pvParameter) {
esp_http_client_config_t http_config = {
.url = "http://192.168.1.100:8000/firmware.bin",
.timeout_ms = 15000,
.keep_alive_enable = true,
};
esp_https_ota_config_t ota_config = {
.http_config = &http_config,
.progress_cb = ota_progress_cb,
};
ESP_LOGI(TAG, "Starting OTA...");
esp_err_t ret = esp_https_ota(&ota_config);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "OTA success, restarting...");
esp_restart();
} else {
ESP_LOGE(TAG, "OTA failed: %s", esp_err_to_name(ret));
// 可在此处添加重试逻辑或进入安全模式
}
vTaskDelete(NULL);
}
4. 注意事项与最佳实践
-
固件大小:确保固件
.bin小于 OTA 分区大小,否则写入失败。 -
网络稳定性:HTTP 下载中断会导致 OTA 失败,建议使用 HTTPS 或添加断点续传(ESP-IDF 支持
esp_http_client的 Range 请求)。 -
回滚机制:新固件启动后,建议调用
esp_ota_mark_app_valid_cancel_rollback()标记固件有效,否则多次重启后会自动回滚。 - 安全:生产环境务必使用 HTTPS,并验证服务器证书,防止固件被篡改。
-
测试:在升级前,先备份当前固件(可通过
esptool.py read_flash导出)。 - 日志:OTA 期间避免大量日志输出,以免阻塞网络任务。
5. 总结
本文介绍了基于 HTTP 的 ESP32 OTA 升级方案,从分区表原理到代码实现,并提供了带进度显示的完整示例。通过 OTA,你可以远程修复 bug、推送新功能,极大提升设备维护效率。实际产品中,建议结合 HTTPS、签名验证和回滚策略,构建健壮的升级链路。