(ESP-IDF)OTA
双分区
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
# app offset must 64k 0x10000
nvs ,data ,nvs ,0x9000 ,0x4000,
otadata ,data ,ota ,0xd000 ,0x2000,
phy_init ,data ,phy ,0xf000 ,0x1000,
ota_0 ,app ,ota_0 ,0x10000 ,0xf0000,
ota_1 ,app ,ota_1 ,0x100000 ,0xf0000,
ESP32 OTA 步骤
// 1. 预先需要的变量
// esp_ota_handle_t update_handle = 0;
// update_partition = esp_ota_get_next_update_partition(NULL);
// 2. esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
// 3. esp_ota_write(update_handle, download_static_buffer, data_read);
// 4. esp_ota_end(update_handle);
// 5. esp_ota_set_boot_partition(update_partition);
实际案例 HTTP OTA
#include "module_ota.h"
#include <esp_err.h>
#include <esp_ota_ops.h>
#include <esp_http_client.h>
#include <esp_crt_bundle.h>
#include "esp_log.h"
#include "stdbool.h"
#include "event_bus.h"
static const char *TAG = "module_ota";
#define DOWNLOAD_CHUNK_SIZE 2048
static uint8_t download_static_buffer[DOWNLOAD_CHUNK_SIZE];
void module_ota_task(void *pt) {
char *ota_url = (char *) pt;
ota_url[256] = '\0';
if (strlen(ota_url) == 0) {
ESP_LOGE(TAG, "Invalid ota_url\r\n");
return;
}
esp_err_t err = ESP_OK;
esp_ota_handle_t update_handle = 0;
const esp_partition_t *update_partition = NULL;
esp_http_client_handle_t client = NULL;
// 重试机制相关变量
int retry_count = 0;
const int max_retries = 3; // 最大重试次数
const int retry_delay_ms = 2000; // 重试延迟2秒
// 1. 获取OTA分区
update_partition = esp_ota_get_next_update_partition(NULL);
if (update_partition == NULL) {
ESP_LOGE(TAG, "Failed to get next update partition");
err = ESP_FAIL;
goto cleanup;
}
ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%"PRIx32,
update_partition->subtype, update_partition->address);
// 2. 配置HTTP客户端(优化配置)
esp_http_client_config_t config = {
.url = ota_url,
.transport_type = HTTP_TRANSPORT_OVER_SSL,
.timeout_ms = 60000, // 增加到60秒超时
.keep_alive_enable = true,
.crt_bundle_attach = esp_crt_bundle_attach,
.buffer_size = DOWNLOAD_CHUNK_SIZE, // 设置缓冲区大小
.buffer_size_tx = DOWNLOAD_CHUNK_SIZE, // 发送缓冲区大小
};
// 3. 初始化HTTP客户端
client = esp_http_client_init(&config);
if (client == NULL) {
ESP_LOGE(TAG, "Failed to initialize HTTP client");
err = ESP_FAIL;
goto cleanup;
}
esp_http_client_set_method(client, HTTP_METHOD_GET);
err = esp_http_client_open(client, 0);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
goto cleanup;
}
esp_http_client_fetch_headers(client);
// 4. 开始OTA操作
err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_begin failed: %s", esp_err_to_name(err));
goto cleanup;
}
// 5. 循环下载数据(使用静态缓冲区,包含重试机制)
bool download_complete = false;
uint32_t start_ms = pdTICKS_TO_MS(xTaskGetTickCount());
while (!download_complete) {
// 读取数据到静态缓冲区
int data_read = esp_http_client_read(client, (char *) download_static_buffer, DOWNLOAD_CHUNK_SIZE);
if (data_read < 0) {
ESP_LOGE(TAG, "Error reading data: %d, retry count: %d/%d", data_read, retry_count, max_retries);
// 检查是否是连接断开错误
if (esp_http_client_is_complete_data_received(client) == false) {
retry_count++;
if (retry_count <= max_retries) {
ESP_LOGW(TAG, "Retrying connection...");
vTaskDelay(retry_delay_ms / portTICK_PERIOD_MS); // 等待后重试
// 重新建立连接
esp_http_client_close(client);
err = esp_http_client_open(client, 0);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to reopen HTTP connection: %s", esp_err_to_name(err));
continue; // 继续重试
}
esp_http_client_fetch_headers(client);
// 修复:重新开始OTA操作,重置写入位置
if (update_handle != 0) {
esp_ota_abort(update_handle);
update_handle = 0;
}
err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to restart OTA operation: %s", esp_err_to_name(err));
continue; // 继续重试
}
continue; // 重新开始读取
} else {
ESP_LOGE(TAG, "Max retries exceeded, aborting download");
err = ESP_FAIL;
goto cleanup;
}
} else {
// 数据传输已完成但读取错误,可能是正常结束
download_complete = true;
break;
}
} else if (data_read > 0) {
// 重置重试计数器
retry_count = 0;
// 写入OTA分区
err = esp_ota_write(update_handle, download_static_buffer, data_read);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_write failed: %s", esp_err_to_name(err));
goto cleanup;
}
} else if (data_read == 0) {
// 数据传输完成
if (esp_http_client_is_complete_data_received(client) == true) {
ESP_LOGI(TAG, "Download completed successfully");
download_complete = true;
break;
}
}
// 超时检查(10分钟)
if (pdTICKS_TO_MS(xTaskGetTickCount()) - start_ms > 60000 * 10) {
ESP_LOGE(TAG, "Download timeout");
err = ESP_FAIL;
goto cleanup;
}
}
// 8. 结束OTA操作
err = esp_ota_end(update_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_end failed: %s", esp_err_to_name(err));
goto cleanup;
}
// 9. 设置启动分区
err = esp_ota_set_boot_partition(update_partition);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed: %s", esp_err_to_name(err));
goto cleanup;
}
ESP_LOGI(TAG, "Firmware download and update completed successfully");
cleanup:
// 统一的资源清理
if (client) {
esp_http_client_cleanup(client);
}
if (update_handle != 0 && err != ESP_OK) {
esp_ota_abort(update_handle);
}
esp_restart();
}
TaskHandle_t module_ota_task_handle = NULL;
void module_ota_init(char *ota_url) {
if (module_ota_task_handle == NULL) {
xTaskCreate(module_ota_task, "module_ota_task", 1024*4, ota_url, 10, &module_ota_task_handle);
}
}
// OTA 步骤
// 1. 预先需要的变量
// esp_ota_handle_t update_handle = 0;
// update_partition = esp_ota_get_next_update_partition(NULL);
// 2. esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
// 3. esp_ota_write(update_handle, download_static_buffer, data_read);
// 4. esp_ota_end(update_handle);
// 5. esp_ota_set_boot_partition(update_partition);
License:
CC BY 4.0