一、概述
- 最近做了一下ESP32的IAP升级(基于Ymodem),所以需要剥离一下ESP32的OTA
二、SDK准备工作
-
SDK版本:esp-idf-4.4.x
-
修改分区表,我这里采用OTA0+OTA1,各1.5M,无Factory区域
# Name, Type, SubType, Offset, Size, Flags # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap nvs, data, nvs, , 0x4000, otadata, data, ota, , 0x2000, phy_init, data, phy, , 0x1000, ota_0, app, ota_0, , 0x180000, ota_1, app, ota_1, , 0x180000,
-
设置板子的FLASH为4M(这里需要根据硬件和分区表在menuconfig进行调整)
# CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_2MB is not set CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y # CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set
三、整体流程
-
需要用到的变量
volatile esp_ota_handle_t update_handle = 0 ;//update的hdl,用于识别当前是哪个OTA,这里应该允许多个OTA同时进行? volatile const esp_partition_t *update_partition = NULL;//拿分区的指针 volatile int binary_file_length = 0;//文件长度 volatile bool image_header_was_checked = false;//是否检测到头了
-
初始化时需要调用(准备开始OTA了)
///>ESP32 需要处理的逻辑 update_partition = esp_ota_get_next_update_partition(NULL);//获取下一个更新的分区 assert(update_partition != NULL); ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x", update_partition->subtype, update_partition->address); binary_file_length = 0;//清空写入总长度 image_header_was_checked = false;//获取到头标志清空
-
每接收到一包数据,进行调用
esp_err_t err; if (image_header_was_checked == false) {//固件第一包数据 头检查 esp_app_desc_t new_app_info; if (dat_len > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) { // check current version with downloading memcpy(&new_app_info, &dat[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t)); ESP_LOGI(TAG, "New firmware version: %s", new_app_info.version); esp_app_desc_t running_app_info; if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) { ESP_LOGI(TAG, "Running firmware version: %s", running_app_info.version); } const esp_partition_t* last_invalid_app = esp_ota_get_last_invalid_partition(); esp_app_desc_t invalid_app_info; if (esp_ota_get_partition_description(last_invalid_app, &invalid_app_info) == ESP_OK) { ESP_LOGI(TAG, "Last invalid firmware version: %s", invalid_app_info.version); } // check current version with last invalid partition // 这部分可以不需要管,通常我们不用理这个版本。。。。。。 if (last_invalid_app != NULL) { if (memcmp(invalid_app_info.version, new_app_info.version, sizeof(new_app_info.version)) == 0) { ESP_LOGW(TAG, "New version is the same as invalid version."); ESP_LOGW(TAG, "Previously, there was an attempt to launch the firmware with %s version, but it failed.", invalid_app_info.version); ESP_LOGW(TAG, "The firmware has been rolled back to the previous version."); } } image_header_was_checked = true;//表示已经收到头包数据了,并且检查正常 err = esp_ota_begin(update_partition, OTA_WITH_SEQUENTIAL_WRITES, &update_handle);//开始OTA if (err != ESP_OK) { ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err)); //http_cleanup(client); esp_ota_abort(update_handle); return COM_ERROR; } ESP_LOGI(TAG, "esp_ota_begin succeeded"); } else { ESP_LOGE(TAG, "received package is not fit len"); //http_cleanup(client); esp_ota_abort(update_handle); return COM_ERROR; } } //开始写入数据,注意头包检查成功后,esp_ota_begin后, 也要通过下面esp_ota_write进行写入 //之后每一包都会进行写入 err = esp_ota_write( update_handle, (const void *)dat, dat_len); if (err != ESP_OK) { // http_cleanup(client); esp_ota_abort(update_handle); } binary_file_length += dat_len; ESP_LOGI(TAG, "Written image length %d|%d", binary_file_length);
-
OTA结束后调用
///>esp32 esp_err_t err; ESP_LOGI(TAG, "Total Write binary data length: %d", binary_file_length); err = esp_ota_end(update_handle);//结束调用 if (err != ESP_OK) { if (err == ESP_ERR_OTA_VALIDATE_FAILED) { ESP_LOGE(TAG, "Image validation failed, image is corrupted"); } else { ESP_LOGE(TAG, "esp_ota_end failed (%s)!", esp_err_to_name(err)); } // http_cleanup(client); // task_fatal_error(); } ///>下面进行设置启动分区 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)); //http_cleanup(client); //task_fatal_error(); } ESP_LOGI(TAG, "Prepare to restart system!"); ///>成功复位 esp_restart();
四、注意事项
- 获取头包时,注意适当增加长度,通常1K字节是没问题的,否则上面的实现中,检查头包长度时,会不过,若是收数据单包比较少,建议组包后才调用上面函数进行处理