## 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、签名验证和回滚策略,构建健壮的升级链路。