ESP32 OTA 固件升级实战:基于 HTTP 的远程更新方案
👁 2 阅读 · 2026-08-20 · ESP32 OTA
OTA(Over-The-Air)升级是物联网设备必备的能力,它允许设备在运行中通过网络获取新固件,无需物理接触。本文以 ESP32 为例,深入讲解基于 HTTP 的 OTA 升级原理、分区表配置、代码实现及注意事项。你将学会如何搭建一个简易的 HTTP 服务器,并通过 ESP32 的 esp_https_ota 组件安全地完成固件下载与切换,为产品迭代打下坚实基础。
## 1. OTA 升级原理与分区表
ESP32 的 OTA 功能依赖于其 Flash 分区表。默认分区表通常包含两个 `ota_0` 和 `ota_1` 应用分区,以及一个 `otadata` 分区。升级时,新固件被写入当前未运行的 OTA 分区(例如当前运行在 ota_0,则写入 ota_1),写入完成后,通过设置 `otadata` 中的引导计数器,使系统在下次重启时切换到新分区。若新固件启动失败,引导程序会自动回滚到旧分区,保证设备不“变砖”。
**分区表示例(partitions.csv)**:
```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 分区 → 校验 → 重启。
```c
#include
#include
#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 服务器目录,并启动服务器:
```bash
python3 -m http.server 8000
```
## 3. 完整示例:带进度显示与错误处理
以下代码增加进度回调,并处理网络错误:
```c
// 进度回调
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、签名验证和回滚策略,构建健壮的升级链路。