main/updates.c

Wed, 24 Oct 2018 23:15:04 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Wed, 24 Oct 2018 23:15:04 +0200
changeset 13
8f01b74bf1dd
parent 4
6d1f512cd074
child 42
c6a1a6ca5437
permissions
-rw-r--r--

Update /spiffs via internet. http://update.mbse.eu is now the update server.

/**
 * @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();
    snprintf(temp, 63, "Running part.type %d sub %d,\r\nat offset 0x%08x\r\n",
		running->type, running->subtype, running->address);
    TFT_print(temp, 0, LASTY);

    /*
     * 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("Begin 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*/
    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) {
	    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 (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);

    if (esp_partition_check_identity(esp_ota_get_running_partition(), update_partition) == true) {
	ESP_LOGI(TAG, "Already the latest version");
	TFT_print("Already the latest version.\r\n", LASTX, LASTY);
	goto updateok;
    }

    /*
     * Here we have a different and hopefully newer 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("Rebooting ...", 0, LASTY);
    vTaskDelay(1000 / portTICK_PERIOD_MS);
    esp_restart();
    return ;

updateerr:
    _fg = TFT_RED;
    TFT_print("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(5 / 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("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("version.txt");
	rename("/spiffs/version.txt", "/spiffs/version.old");
	DownloadSpiffs("version.txt");
	goto spiffs_update;
    }

    if (DownloadSpiffs("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);
    }
//    ESP_LOG_BUFFER_HEXDUMP(TAG, v1, strlen(v1), ESP_LOG_INFO);
//    ESP_LOG_BUFFER_HEXDUMP(TAG, v2, strlen(v2), ESP_LOG_INFO);
    if (strcmp(v1, v2) == 0) {
	ESP_LOGI(TAG, "/spiffs is up to date");
	TFT_print("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("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;
	}
//	vTaskDelay(10 / portTICK_PERIOD_MS);
    }
    fclose(f);
    unlink("/spiffs/version.old");
    TFT_print("updated\r\n", LASTX, LASTY);
    return;

spiffs_error:
    _fg = TFT_RED;
    TFT_print("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("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