main/updates.c

Wed, 10 Jun 2020 09:43:51 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Wed, 10 Jun 2020 09:43:51 +0200
changeset 87
47253f294a9f
parent 70
d6838a268020
child 119
1cef3c25426b
permissions
-rw-r--r--

SDK settings to reduce bin size. Some log messages to debug level. Added KWH usage registration. Added equipment power usage for HLT and MLT. Equipment database upgraded to version 2, expandable. Fixed some screen errors during temperature mash steps.

/**
 * @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";

extern sButton                  Buttons[MAXBUTTONS];
extern int                      Main_Screen;


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/ap1/fw/brewboard.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_LOGD(TAG, "Continue upgrade application");
		    TFT_print((char *)"Download new version.\r\n", 0, LASTY);
                } 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;
}



/**
 * @brief Update /spiffs filesystem
 */
void spiffs_update(void)
{
    int		rc;
    FILE	*f;
    char	v1[12], v2[12], fn[41];

    TFT_setFont(DEJAVU18_FONT, NULL);
    _fg = TFT_CYAN;
    TFT_print((char *)"Update /spiffs ", 0, 25);

    rc = rename("/spiffs/version.txt", "/spiffs/version.old");
    if ((rc != 0) && (errno == ENOENT)) {
	/* No old file. */
	ESP_LOGI(TAG, "No old /spiffs/version.txt");
	/* Download, install old and new */
	DownloadSpiffs((char *)"version.txt");
	rename("/spiffs/version.txt", "/spiffs/version.old");
	DownloadSpiffs((char *)"version.txt");
	goto spiffs_update;
    }

    if (DownloadSpiffs((char *)"version.txt") < 0)
	goto spiffs_error;

    /* Compare spiffs/version.old and /spiffs/version.txt */
    v1[0] = '\0';
    v2[0] = '\0';
    f = fopen("/spiffs/version.old", "r");
    if (f) {
	fgets(v1, 11, f);
	fclose(f);
    }
    f = fopen("/spiffs/version.txt", "r");
    if (f) {
	fgets(v2, 11, f);
	fclose(f);
    }
    if (strcmp(v1, v2) == 0) {
	ESP_LOGI(TAG, "/spiffs is up to date");
	TFT_print((char *)"Ok\r\n", LASTX, LASTY);
	unlink("/spiffs/version.old");
	return;
    }

spiffs_update:
    /*
     * Run the update, get the filelist.
     */
    ESP_LOGI(TAG, "Full /spiffs update");
    rc = DownloadSpiffs((char *)"files.list");
    if (rc < 0) {
	unlink("/spiffs/version.txt");
	rename("/spiffs/version.old", "/spiffs/version.txt");	// So next time we try again.
	goto spiffs_error;
    }

    f = fopen("/spiffs/files.list", "r");
    while (fgets(fn, 40, f)) {
	fn[strlen(fn)-1] = '\0';
	rc = DownloadSpiffs(fn);
	if (rc < 0) {
	    ESP_LOGE(TAG, "Updates failed");
	    fclose(f);
	    goto spiffs_error;
	}
    }
    fclose(f);
    unlink("/spiffs/version.old");
    TFT_print((char *)"updated\r\n", LASTX, LASTY);
    return;

spiffs_error:
    _fg = TFT_RED;
    TFT_print((char *)"error\r\n", LASTX, LASTY);
}



/*
 * Files init function, only runs once a new screen is entered.
 */
void Updates_Init(void)
{
    switch (Main_Screen) {
	case MAIN_TOOLS_UPDATES:
			_bg = TFT_BLACK;
			TFT_fillScreen(_bg);
			TopMessage((char *)"Update");
			break;

	default:	break;
    }
}



/*
 * Updates management loop, non-blocking.
 */
void Updates_Loop(void)
{
    switch (Main_Screen) {

	case MAIN_TOOLS_UPDATES:
			spiffs_update();
			bin_update();
			Main_Screen = MAIN_TOOLS;
			break;

	default:	break;
    }
}

mercurial