/****************************************************************************** * 作者:kerwincui * 时间:2021-06-08 * 邮箱:164770707@qq.com * 源码地址:https://gitee.com/kerwincui/wumei-smart * author: kerwincui * create: 2021-06-08 * email:164770707@qq.com * source:https://github.com/kerwincui/wumei-smart ******************************************************************************/ #include "native_ota.h" static const char *TAG = "native_ota"; /*准备写入Flash的OTA数据写入缓冲区*/ static char ota_write_data[BUFFSIZE + 1] = { 0 }; // extern const uint8_t server_cert_pem_start[] asm("_binary_ca_cert_pem_start"); //extern const uint8_t server_cert_pem_end[] asm("_binary_ca_cert_pem_end"); static void http_cleanup(esp_http_client_handle_t client) { esp_http_client_close(client); esp_http_client_cleanup(client); } static void __attribute__((noreturn)) task_fatal_error(void) { ESP_LOGE(TAG, "Exiting task due to fatal error..."); (void)vTaskDelete(NULL); while (1) { ; } } static void print_sha256 (const uint8_t *image_hash, const char *label) { char hash_print[HASH_LEN * 2 + 1]; hash_print[HASH_LEN * 2] = 0; for (int i = 0; i < HASH_LEN; ++i) { sprintf(&hash_print[i * 2], "%02x", image_hash[i]); } ESP_LOGI(TAG, "%s: %s", label, hash_print); } static void infinite_loop(void) { int i = 0; ESP_LOGI(TAG, "When a new firmware is available on the server, press the reset button to download it"); while(1) { ESP_LOGI(TAG, "Waiting for a new firmware ... %d", ++i); vTaskDelay(2000 / portTICK_PERIOD_MS); } } static void ota_task(void *pvParameter) { esp_err_t err; /* 更新句柄 : 由 esp_ota_begin()设置, 必须通过esp_ota_end()释放 */ esp_ota_handle_t update_handle = 0 ; const esp_partition_t *update_partition = NULL; ESP_LOGI(TAG, "Starting OTA..."); const esp_partition_t *configured = esp_ota_get_boot_partition(); const esp_partition_t *running = esp_ota_get_running_partition(); if (configured != running) { ESP_LOGW(TAG, "Configured OTA boot partition at offset 0x%08x, but running from offset 0x%08x", configured->address, running->address); ESP_LOGW(TAG, "(This can happen if either the OTA boot data or preferred boot image become corrupted somehow.)"); } ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08x)", running->type, running->subtype, running->address); esp_http_client_config_t config = { .url = CONFIG_FIRMWARE_UPG_URL, //.cert_pem = (char *)server_cert_pem_start, .timeout_ms = CONFIG_OTA_RECV_TIMEOUT, }; #ifdef CONFIG_SKIP_COMMON_NAME_CHECK config.skip_cert_common_name_check = true; #endif esp_http_client_handle_t client = esp_http_client_init(&config); if (client == NULL) { ESP_LOGE(TAG, "Failed to initialise HTTP connection"); task_fatal_error(); } 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)); esp_http_client_cleanup(client); task_fatal_error(); } esp_http_client_fetch_headers(client); update_partition = esp_ota_get_next_update_partition(NULL); ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x", update_partition->subtype, update_partition->address); assert(update_partition != NULL); int binary_file_length = 0; /*处理所有接收到的数据包*/ bool image_header_was_checked = false; while (1) { int data_read = esp_http_client_read(client, ota_write_data, BUFFSIZE); if (data_read < 0) { ESP_LOGE(TAG, "Error: SSL data read error"); http_cleanup(client); task_fatal_error(); } else if (data_read > 0) { if (image_header_was_checked == false) { esp_app_desc_t new_app_info; if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) { // 通过下载检查当前版本 memcpy(&new_app_info, &ota_write_data[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); } // 使用最新的无效分区检查当前版本 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."); http_cleanup(client); infinite_loop(); } } #ifndef CONFIG_SKIP_VERSION_CHECK if (memcmp(new_app_info.version, running_app_info.version, sizeof(new_app_info.version)) == 0) { ESP_LOGW(TAG, "Current running version is the same as a new. We will not continue the update."); http_cleanup(client); infinite_loop(); } #endif image_header_was_checked = true; 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)); http_cleanup(client); task_fatal_error(); } ESP_LOGI(TAG, "esp_ota_begin succeeded"); } else { ESP_LOGE(TAG, "received package is not fit len"); http_cleanup(client); task_fatal_error(); } } err = esp_ota_write( update_handle, (const void *)ota_write_data, data_read); if (err != ESP_OK) { http_cleanup(client); task_fatal_error(); } binary_file_length += data_read; ESP_LOGD(TAG, "Written image length %d", binary_file_length); } else if (data_read == 0) { /* *由于esp_http_client_read永远不会返回负错误代码,因此我们依靠`errno`来检查基础传输连接是否关闭 */ if (errno == ECONNRESET || errno == ENOTCONN) { ESP_LOGE(TAG, "Connection closed, errno = %d", errno); break; } if (esp_http_client_is_complete_data_received(client) == true) { ESP_LOGI(TAG, "Connection closed"); break; } } } ESP_LOGI(TAG, "Total Write binary data length: %d", binary_file_length); if (esp_http_client_is_complete_data_received(client) != true) { ESP_LOGE(TAG, "Error in receiving complete file"); http_cleanup(client); task_fatal_error(); } 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"); } 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(); return ; } static bool diagnostic(void) { gpio_config_t io_conf; io_conf.intr_type = GPIO_PIN_INTR_DISABLE; io_conf.mode = GPIO_MODE_INPUT; io_conf.pin_bit_mask = (1ULL << CONFIG_GPIO_DIAGNOSTIC); io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; io_conf.pull_up_en = GPIO_PULLUP_ENABLE; gpio_config(&io_conf); ESP_LOGI(TAG, "Diagnostics (5 sec)..."); vTaskDelay(5000 / portTICK_PERIOD_MS); bool diagnostic_is_ok = gpio_get_level(CONFIG_GPIO_DIAGNOSTIC); gpio_reset_pin(CONFIG_GPIO_DIAGNOSTIC); return diagnostic_is_ok; } void native_ota_start(void) { uint8_t sha_256[HASH_LEN] = { 0 }; esp_partition_t partition; // 获取分区表的sha256摘要 partition.address = ESP_PARTITION_TABLE_OFFSET; partition.size = ESP_PARTITION_TABLE_MAX_LEN; partition.type = ESP_PARTITION_TYPE_DATA; esp_partition_get_sha256(&partition, sha_256); print_sha256(sha_256, "SHA-256 for the partition table: "); // 获取引导加载程序的sha256摘要 partition.address = ESP_BOOTLOADER_OFFSET; partition.size = ESP_PARTITION_TABLE_OFFSET; partition.type = ESP_PARTITION_TYPE_APP; esp_partition_get_sha256(&partition, sha_256); print_sha256(sha_256, "SHA-256 for bootloader: "); // 获取运行分区的sha256摘要 esp_partition_get_sha256(esp_ota_get_running_partition(), sha_256); print_sha256(sha_256, "SHA-256 for current firmware: "); const esp_partition_t *running = esp_ota_get_running_partition(); esp_ota_img_states_t ota_state; if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) { if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) { // 运行诊断功能 bool diagnostic_is_ok = diagnostic(); if (diagnostic_is_ok) { ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution ..."); esp_ota_mark_app_valid_cancel_rollback(); } else { ESP_LOGE(TAG, "Diagnostics failed! Start rollback to the previous version ..."); esp_ota_mark_app_invalid_rollback_and_reboot(); } } } // 初始化 NVS. esp_err_t err = nvs_flash_init(); if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { // OTA app分区表比非OTA分区表具有更小的NVS分区大小。这种大小不匹配可能会导致NVS初始化失败。 //如果发生这种情况,我们擦除NVS分区并再次初始化NVS ESP_ERROR_CHECK(nvs_flash_erase()); err = nvs_flash_init(); } ESP_ERROR_CHECK( err ); ESP_ERROR_CHECK(esp_netif_init()); /* 确保禁用任何WiFi省电模式,这将允许最佳吞吐量,从而为整个OTA操作计时*/ esp_wifi_set_ps(WIFI_PS_NONE); xTaskCreate(&ota_task, "ota_task", 8192, NULL, 5, NULL); }