diff -r 000000000000 -r 88d965579617 main/updates.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main/updates.c Tue Oct 08 12:00:31 2019 +0200 @@ -0,0 +1,305 @@ +/** + * @file updates.c + * @brief Updates management. It can download and install new firmware + * downloaded from the internet. + */ + +#include "config.h" + + +#define BUFFSIZE 1024 ///< Download buffer size +static char ota_write_data[BUFFSIZE + 1] = { 0 }; +static const char *TAG = "update"; + + + +static void http_cleanup(esp_http_client_handle_t client) +{ + esp_http_client_close(client); + esp_http_client_cleanup(client); +} + + + +/** + * @brief Run binary update procedure + */ +void bin_update(void) +{ + char temp[64]; + esp_err_t err; + const esp_partition_t *update_partition = NULL; + esp_ota_handle_t update_handle = 0; + +// TFT_setFont(DEJAVU18_FONT, NULL); +// _fg = TFT_CYAN; + const esp_partition_t *running = esp_ota_get_running_partition(); + + /* + * Don't use https because it costs more then 100K memory. + */ + esp_http_client_config_t update = { + .url = "http://update.mbse.eu/ap2/fw/co2meter.bin", + }; + + esp_http_client_handle_t client = esp_http_client_init(&update); + if (client == NULL) { + ESP_LOGI(TAG, "Failed to init HTTP connection"); + goto updateerr; + } + + 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); + goto updateerr; + } + + int content_length = esp_http_client_fetch_headers(client); + int status_code = esp_http_client_get_status_code(client); + if (status_code != 200) { + ESP_LOGE(TAG, "GET %s error %d", update.url, status_code); + esp_http_client_cleanup(client); + goto updateerr; + } + update_partition = esp_ota_get_next_update_partition(NULL); + if (update_partition == NULL) { + ESP_LOGE(TAG, "No update partition"); + esp_http_client_cleanup(client); + goto updateerr; + } + ESP_LOGI(TAG, "Update to partition subtype %d at offset 0x%x", update_partition->subtype, update_partition->address); + + 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); + goto updateerr; + } + +// TFT_print((char *)"Begin firmware download.\r\n", 0, LASTY); + ESP_LOGI(TAG, "Download update %s size %d", update.url, content_length); + int binary_file_length = 0; + /*deal with all receive packet*/ + 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: data read error"); + http_cleanup(client); + goto updateerr;; + } 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)) { + // 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_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, %s %s", running_app_info.version, running_app_info.date, running_app_info.time); + } + + 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 sha256 with last invalid partition + if (last_invalid_app != NULL) { + if (memcmp(invalid_app_info.app_elf_sha256, new_app_info.app_elf_sha256, 32) == 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); + goto updateok; + } + } + + if (memcmp(new_app_info.app_elf_sha256, running_app_info.app_elf_sha256, 32) == 0) { + ESP_LOGI(TAG, "Current running version is the same as a new."); + http_cleanup(client); + goto updateok; + } + + 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); + goto updateerr; + } + ESP_LOGI(TAG, "Continue upgrade application"); + } else { + ESP_LOGE(TAG, "Received package is not fit len"); + http_cleanup(client); + goto updateerr; + } + } + err = esp_ota_write( update_handle, (const void *)ota_write_data, data_read); + if (err != ESP_OK) { + http_cleanup(client); + goto updateerr;; + } + binary_file_length += data_read; + ESP_LOGD(TAG, "Written image length %d", binary_file_length); + } else if (data_read == 0) { + break; + } + } + + ESP_LOGI(TAG, "Download complete, binary data length: %d", binary_file_length); + http_cleanup(client); + + if (binary_file_length != content_length) { + ESP_LOGE(TAG, "Incomplete file"); + goto updateerr; + } + if (esp_ota_end(update_handle) != ESP_OK) { + ESP_LOGE(TAG, "esp_ota_end failed!"); + goto updateerr; + } + snprintf(temp, 63, "Received image %d bytes\r\n", binary_file_length); +// TFT_print(temp, 0, LASTY); + + /* + * Here we have new version, install and boot it. + */ + 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 updateerr; + } + + ESP_LOGI(TAG, "Prepare to restart system!"); + // TFT_print((char *)"Rebooting ...", 0, LASTY); + vTaskDelay(1000 / portTICK_PERIOD_MS); + esp_restart(); + return; + +updateerr: +// _fg = TFT_RED; +// TFT_print((char *)"Error\r\n", 0, LASTY); + +updateok: + vTaskDelay(3000 / portTICK_PERIOD_MS); +} + + + +/** + * @brief Download a file to /spiffs + * @param filename The name and path of the file to download. + * @return Return 0 if ok, negative if errors. + */ +int DownloadSpiffs(char *filename) +{ + esp_err_t err; + static char theurl[73], thefile[41]; + FILE *f; + +// static char todel[41]; +// snprintf(todel, 40, "/spiffs//%s", filename); +// unlink(todel); +// return 0; + + snprintf(theurl, 72, "http://update.mbse.eu/ap1/image/%s", filename); + snprintf(thefile, 40, "/spiffs/%s", filename); + + esp_http_client_config_t update = { + .url = theurl, + }; + + esp_http_client_handle_t client = esp_http_client_init(&update); + if (client == NULL) { + ESP_LOGE(TAG, "Failed to init HTTP connection"); + return -1; + } + + 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); + return -1; + } + + int content_length = esp_http_client_fetch_headers(client); + int status_code = esp_http_client_get_status_code(client); + if (status_code != 200) { + ESP_LOGE(TAG, "GET %s error %d", update.url, status_code); + esp_http_client_cleanup(client); + return -1; + } + + /* + * Remove a possible stale download. + */ + unlink("/spiffs/tmpfile"); + f = fopen("/spiffs/tmpfile", "w"); + if (f == NULL) { + ESP_LOGE(TAG, "Cannot create /spiffs/tmpfile"); + esp_http_client_cleanup(client); + return -1; + } + + int read_length = 0; + int write_length = 0; + while (1) { + int data_read = esp_http_client_read(client, ota_write_data, BUFFSIZE); + if (data_read < 0) { + ESP_LOGE(TAG, "Error: data read error %s", theurl); + http_cleanup(client); + return -1; + } else if (data_read > 0) { + size_t bytes = fwrite(ota_write_data, 1, data_read, f); + if (bytes != data_read) { + ESP_LOGE(TAG, "fwrite %s %d/%d at %d", theurl, bytes, data_read, write_length); + } + write_length += bytes; + read_length += data_read; + } else if (data_read == 0) { + break; + } + vTaskDelay(10 / portTICK_PERIOD_MS); + } + fclose(f); + + if (content_length != write_length) { + ESP_LOGE(TAG, "Download %s size %d but got %d bytes", theurl, content_length, write_length); + unlink("/spiffs/tmpfile"); + return -1; + } + + ESP_LOGI(TAG, "Download %s size %d Ok", theurl, content_length); + unlink(thefile); + rename("/spiffs/tmpfile", thefile); + esp_http_client_cleanup(client); + return 0; +} + + + +/* + * Files init function, only runs once a new screen is entered. + */ +void Updates_Init(void) +{ +// _bg = TFT_BLACK; +// TFT_fillScreen(_bg); +// TopMessage((char *)"Update"); + +} + + + +/* + * Updates management loop, non-blocking. + */ +void Updates_Loop(void) +{ + bin_update(); +} + +