#include "esp_log.h" #include "esp_partition.h" #include "esp_ota_ops.h" #include "esp_http_client.h" #include "esp_app_format.h" #include "constants.h" #include "ota.h" #include "storage.h" #include "secure_api.h" #include const char *TAG = "ota"; #define BUFF_SIZE 1024 static void http_cleanup(esp_http_client_handle_t client) { esp_http_client_close(client); esp_http_client_cleanup(client); } bool ota_perform_update(const char *version) { 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%08" PRIx32 ", but running from offset 0x%08" PRIx32, 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%08" PRIx32 ")", running->type, running->subtype, running->address); // Determine firmware download URL char *update_url = calloc(256, 1); assert(update_url != NULL); assert(storage_get_secure_origin(update_url) > 0); strcat(update_url, "/devices_api/ota/Wt32-Eth01/"); strcat(update_url, version); ESP_LOGI(TAG, "Firmware URL: %s", update_url); char *root_ca = calloc(1, ROOT_CA_MAX_BYTES); assert(root_ca); assert(storage_get_root_ca(root_ca) > 0); esp_http_client_config_t config = { .url = update_url, .cert_pem = root_ca, .timeout_ms = OTA_REC_TIMEOUT, .keep_alive_enable = true, }; esp_http_client_handle_t client = esp_http_client_init(&config); if (client == NULL) { ESP_LOGE(TAG, "Failed to initialise HTTP connection"); free(update_url); free(root_ca); return false; } int err = esp_http_client_open(client, 0); free(update_url); free(root_ca); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err)); esp_http_client_cleanup(client); return false; } esp_http_client_fetch_headers(client); const esp_partition_t *update_partition = esp_ota_get_next_update_partition(NULL); assert(update_partition != NULL); ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%" PRIx32, update_partition->subtype, update_partition->address); // OTA update loop int binary_file_length = 0; esp_ota_handle_t update_handle = 0; /*deal with all receive packet*/ bool image_header_was_checked = false; char *ota_write_data = calloc(BUFF_SIZE + 1, 1); while (1) { int data_read = esp_http_client_read(client, ota_write_data, BUFF_SIZE); if (data_read < 0) { ESP_LOGE(TAG, "Error: SSL data read error"); http_cleanup(client); free(ota_write_data); return false; } if (data_read == 0) { /* * As esp_http_client_read never returns negative error code, we rely on * `errno` to check for underlying transport connectivity closure if any */ 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; } // No data received yet continue; } 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)) { // check current version with downloading 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); } // 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."); http_cleanup(client); free(ota_write_data); secure_api_report_log_message(Error, "New version is the same as last invalid version. Could not perform update!"); return false; } } image_header_was_checked = true; err = esp_ota_begin(update_partition, OTA_WITH_SEQUENTIAL_WRITES, &update_handle); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err)); http_cleanup(client); free(ota_write_data); esp_ota_abort(update_handle); return false; } ESP_LOGI(TAG, "esp_ota_begin succeeded"); } else { ESP_LOGE(TAG, "received package is not fit len"); http_cleanup(client); free(ota_write_data); esp_ota_abort(update_handle); return false; } } err = esp_ota_write(update_handle, (const void *)ota_write_data, data_read); if (err != ESP_OK) { http_cleanup(client); free(ota_write_data); esp_ota_abort(update_handle); return false; } binary_file_length += data_read; ESP_LOGD(TAG, "Written image length %d", binary_file_length); } free(ota_write_data); 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); esp_ota_abort(update_handle); return false; } 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); return false; } 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); return false; } ESP_LOGI(TAG, "End of OTA procedure!"); return true; }