main/updates.c

changeset 0
88d965579617
child 26
8a3696620c0a
--- /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();
+}
+
+

mercurial