main/updates.c

Sun, 14 Jun 2020 14:53:06 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sun, 14 Jun 2020 14:53:06 +0200
changeset 57
232f318a6b51
parent 53
20c14b06f255
child 74
34da2d2b12d5
permissions
-rw-r--r--

Code cleanup, doxygen comments added.

/**
 * @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";
int				update_running = 0;	///< Not zero if update is running.

extern u8g2_t			u8g2;			///< Structure for the display.
extern int			Main_Loop1;
extern uint32_t			AlarmTimer;


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)
{
    esp_err_t                   err;
    const esp_partition_t       *update_partition = NULL;
    esp_ota_handle_t            update_handle = 0;
    int				timeout = 600;

    ESP_LOGI(TAG, "Update begin");
    update_running = 1;
    AlarmTimer = 0;
    screen_updating("Stop meten", NULL);

    for (;;) {
	vTaskDelay(100 / portTICK_PERIOD_MS);
	if (Main_Loop1 == ML1_DONE)
	    break;
	if (timeout)
	    timeout--;
	else {
	    ESP_LOGE(TAG, "Timout request stop");
	    goto updateerr;
	}
    }

    screen_updating("Stop meten", "Start WiFi");
    timeout = 600;
    for (;;) {
        vTaskDelay(100 / portTICK_PERIOD_MS);
        if (ready_WiFi())
            break;
	if (timeout)
            timeout--;
        else {
            ESP_LOGE(TAG, "Timout request WiFi");
            goto updateerr;
        }
    }
    ESP_LOGI(TAG, "System is ready for update");
    screen_updating("Verbonden", NULL);

    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;
    }

    screen_updating("Start download", NULL);
    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) {
			screen_updating("Geen nieuwe versie", NULL);
                        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");
		    screen_updating("Start download", "Nieuwe versie!");
                } 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;
    }

    /*
     * 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!");
    screen_updating("Herstart ...", "... Herstart");
    vTaskDelay(1000 / portTICK_PERIOD_MS);
    update_running = 0;
    esp_restart();
    return;

updateerr:
    screen_updating("** FOUT **", "Update mislukt");

updateok:
    update_running = 0;
    vTaskDelay(3000 / portTICK_PERIOD_MS);
}

mercurial