diff --git a/components/cmd_nvs/cmd_nvs.c b/components/cmd_nvs/cmd_nvs.c index 90071a87..3c763757 100644 --- a/components/cmd_nvs/cmd_nvs.c +++ b/components/cmd_nvs/cmd_nvs.c @@ -26,26 +26,8 @@ extern "C" { #include "nvs.h" #include "nvs_utilities.h" -typedef struct { - nvs_type_t type; - const char *str; -} type_str_pair_t; -static const type_str_pair_t type_str_pair[] = { - { NVS_TYPE_I8, "i8" }, - { NVS_TYPE_U8, "u8" }, - { NVS_TYPE_U16, "u16" }, - { NVS_TYPE_I16, "i16" }, - { NVS_TYPE_U32, "u32" }, - { NVS_TYPE_I32, "i32" }, - { NVS_TYPE_U64, "u64" }, - { NVS_TYPE_I64, "i64" }, - { NVS_TYPE_STR, "str" }, - { NVS_TYPE_BLOB, "blob" }, - { NVS_TYPE_ANY, "any" }, -}; -static const size_t TYPE_STR_PAIR_SIZE = sizeof(type_str_pair) / sizeof(type_str_pair[0]); static const char *ARG_TYPE_STR = "type can be: i8, u8, i16, u16 i32, u32 i64, u64, str, blob"; static const char * TAG = "platform_esp32"; @@ -80,28 +62,7 @@ static struct { } list_args; -static nvs_type_t str_to_type(const char *type) -{ - for (int i = 0; i < TYPE_STR_PAIR_SIZE; i++) { - const type_str_pair_t *p = &type_str_pair[i]; - if (strcmp(type, p->str) == 0) { - return p->type; - } - } - return NVS_TYPE_ANY; -} -const char *type_to_str(nvs_type_t type) -{ - for (int i = 0; i < TYPE_STR_PAIR_SIZE; i++) { - const type_str_pair_t *p = &type_str_pair[i]; - if (p->type == type) { - return p->str; - } - } - - return "Unknown"; -} static esp_err_t store_blob(nvs_handle nvs, const char *key, const char *str_values) { uint8_t value; @@ -149,14 +110,6 @@ static esp_err_t store_blob(nvs_handle nvs, const char *key, const char *str_val return err; } -static void print_blob(const char *blob, size_t len) -{ - for (int i = 0; i < len; i++) { - printf("%02x", blob[i]); - } - printf("\n"); -} - static esp_err_t set_value_in_nvs(const char *key, const char *str_type, const char *str_value) { esp_err_t err; diff --git a/components/cmd_nvs/cmd_nvs.h b/components/cmd_nvs/cmd_nvs.h index 7b1c3350..21f7a4d3 100644 --- a/components/cmd_nvs/cmd_nvs.h +++ b/components/cmd_nvs/cmd_nvs.h @@ -15,7 +15,7 @@ extern "C" { // Register NVS functions void register_nvs(); -const char *type_to_str(nvs_type_t type); + #ifdef __cplusplus } #endif diff --git a/components/squeezelite-ota/component.mk b/components/squeezelite-ota/component.mk index 61351b55..35902feb 100644 --- a/components/squeezelite-ota/component.mk +++ b/components/squeezelite-ota/component.mk @@ -10,4 +10,4 @@ COMPONENT_EXTRA_INCLUDES += $(PROJECT_PATH)/main/ COMPONENT_EXTRA_INCLUDES += $(PROJECT_PATH)/components/tools CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_INFO -DCONFIG_OTA_ALLOW_HTTP=1 #CFLAGS += -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG -DCONFIG_OTA_ALLOW_HTTP=1 -COMPONENT_EMBED_TXTFILES := ${PROJECT_PATH}/server_certs/github.pem + diff --git a/components/squeezelite-ota/squeezelite-ota.c b/components/squeezelite-ota/squeezelite-ota.c index c5208a57..51aeebf3 100644 --- a/components/squeezelite-ota/squeezelite-ota.c +++ b/components/squeezelite-ota/squeezelite-ota.c @@ -14,7 +14,6 @@ #include "esp_system.h" #include "esp_event.h" #include "esp_log.h" -#include "esp_ota_ops.h" #include "esp_https_ota.h" #include "string.h" #include @@ -28,20 +27,17 @@ #include #include #include -#include "esp_image_format.h" #include "esp_secure_boot.h" #include "esp_flash_encrypt.h" #include "esp_spi_flash.h" #include "sdkconfig.h" #include "esp_ota_ops.h" +extern const char * get_certificate(); static const char *TAG = "squeezelite-ota"; -extern const uint8_t server_cert_pem_start[] asm("_binary_github_pem_start"); -extern const uint8_t server_cert_pem_end[] asm("_binary_github_pem_end"); -char * cert=NULL; char * ota_write_data = NULL; - +esp_http_client_handle_t ota_http_client = NULL; #define IMAGE_HEADER_SIZE sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t) + 1 #define BUFFSIZE 4096 #define HASH_LEN 32 /* SHA-256 digest length */ @@ -53,7 +49,6 @@ static struct { uint32_t ota_total_len; char * redirected_url; char * current_url; - bool bRedirectFound; bool bOTAStarted; bool bInitialized; uint8_t lastpct; @@ -67,14 +62,14 @@ static esp_http_client_config_t ota_config; extern void wifi_manager_refresh_ota_json(); -void RECOVERY_IRAM_FUNCTION _printMemStats(){ +void _printMemStats(){ ESP_LOGD(TAG,"Heap internal:%zu (min:%zu) external:%zu (min:%zu)", heap_caps_get_free_size(MALLOC_CAP_INTERNAL), heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL), heap_caps_get_free_size(MALLOC_CAP_SPIRAM), heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM)); } -void RECOVERY_IRAM_FUNCTION triggerStatusJsonRefresh(bool bDelay,const char * status, ...){ +void triggerStatusJsonRefresh(bool bDelay,const char * status, ...){ va_list args; va_start(args, status); vsnprintf(ota_status.status_text,sizeof(ota_status.status_text)-1,status, args); @@ -86,13 +81,12 @@ void RECOVERY_IRAM_FUNCTION triggerStatusJsonRefresh(bool bDelay,const char * st vTaskDelay(200 / portTICK_PERIOD_MS); // wait here for a short amount of time. This will help with refreshing the UI status ESP_LOGD(TAG,"Done holding task..."); } - else - { + else { ESP_LOGI(TAG,"%s",ota_status.status_text); taskYIELD(); } } -const char * RECOVERY_IRAM_FUNCTION ota_get_status(){ +const char * ota_get_status(){ if(!ota_status.bInitialized) { memset(ota_status.status_text, 0x00,sizeof(ota_status.status_text)); @@ -100,12 +94,12 @@ const char * RECOVERY_IRAM_FUNCTION ota_get_status(){ } return ota_status.status_text; } -uint8_t RECOVERY_IRAM_FUNCTION ota_get_pct_complete(){ +uint8_t ota_get_pct_complete(){ return ota_status.ota_total_len==0?0: (uint8_t)((float)ota_status.ota_actual_len/(float)ota_status.ota_total_len*100.0f); } -static void __attribute__((noreturn)) RECOVERY_IRAM_FUNCTION task_fatal_error(void) +static void __attribute__((noreturn)) task_fatal_error(void) { ESP_LOGE(TAG, "Exiting task due to fatal error..."); (void)vTaskDelete(NULL); @@ -115,7 +109,7 @@ static void __attribute__((noreturn)) RECOVERY_IRAM_FUNCTION task_fatal_error(vo } } #define FREE_RESET(p) if(p!=NULL) { free(p); p=NULL; } -esp_err_t RECOVERY_IRAM_FUNCTION _http_event_handler(esp_http_client_event_t *evt) +esp_err_t _http_event_handler(esp_http_client_event_t *evt) { // -------------- // Received parameters @@ -159,7 +153,6 @@ esp_err_t RECOVERY_IRAM_FUNCTION _http_event_handler(esp_http_client_event_t *ev FREE_RESET(ota_status.redirected_url); ota_status.redirected_url=strdup(evt->header_value); ESP_LOGW(TAG,"OTA will redirect to url: %s",ota_status.redirected_url); - ota_status.bRedirectFound= true; } if (strcasecmp(evt->header_key, "content-length") == 0) { ota_status.ota_total_len = atol(evt->header_value); @@ -170,19 +163,6 @@ esp_err_t RECOVERY_IRAM_FUNCTION _http_event_handler(esp_http_client_event_t *ev if(!ota_status.bOTAStarted) { ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, status_code=%d, len=%d",esp_http_client_get_status_code(evt->client), evt->data_len); } - else if(ota_status.bOTAStarted && esp_http_client_get_status_code(evt->client) == 200 ){ - ota_status.ota_actual_len+=evt->data_len; - if(ota_get_pct_complete()%5 == 0) ota_status.newpct = ota_get_pct_complete(); - if(ota_status.lastpct!=ota_status.newpct ) - { - gettimeofday(&tv, NULL); - uint32_t elapsed_ms= (tv.tv_sec-ota_status.OTA_start.tv_sec )*1000+(tv.tv_usec-ota_status.OTA_start.tv_usec)/1000; - ESP_LOGI(TAG,"OTA progress : %d/%d (%d pct), %d KB/s", ota_status.ota_actual_len, ota_status.ota_total_len, ota_status.newpct, elapsed_ms>0?ota_status.ota_actual_len*1000/elapsed_ms/1024:0); - wifi_manager_refresh_ota_json(); - - ota_status.lastpct=ota_status.newpct; - } - } break; case HTTP_EVENT_ON_FINISH: ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH"); @@ -194,17 +174,23 @@ esp_err_t RECOVERY_IRAM_FUNCTION _http_event_handler(esp_http_client_event_t *ev return ESP_OK; } -esp_err_t RECOVERY_IRAM_FUNCTION init_config(const char * url){ +esp_err_t init_config(char * url){ memset(&ota_config, 0x00, sizeof(ota_config)); - - ota_config.cert_pem =cert==NULL?(char *)server_cert_pem_start:cert; + ota_status.bInitialized = true; + triggerStatusJsonRefresh(true,"Initializing..."); + if(url==NULL || strlen(url)==0){ + ESP_LOGE(TAG,"HTTP OTA called without a url"); + return ESP_FAIL; + } + ota_status.current_url= url; + ota_config.cert_pem =get_certificate(); ota_config.event_handler = _http_event_handler; ota_config.buffer_size = BUFFSIZE; - ota_config.disable_auto_redirect=true; - //conf->disable_auto_redirect=false; + //ota_config.disable_auto_redirect=true; + ota_config.disable_auto_redirect=false; ota_config.skip_cert_common_name_check = false; ota_config.url = strdup(url); - ota_config.max_redirection_count = 0; + ota_config.max_redirection_count = 3; //ota_write_data = heap_caps_malloc(ota_config.buffer_size+1 , MALLOC_CAP_INTERNAL); ota_write_data = malloc(ota_config.buffer_size+1); if(ota_write_data== NULL){ @@ -213,7 +199,7 @@ esp_err_t RECOVERY_IRAM_FUNCTION init_config(const char * url){ } return ESP_OK; } -esp_partition_t * RECOVERY_IRAM_FUNCTION _get_ota_partition(esp_partition_subtype_t subtype){ +esp_partition_t * _get_ota_partition(esp_partition_subtype_t subtype){ esp_partition_t *ota_partition=NULL; ESP_LOGI(TAG, "Looking for OTA partition."); @@ -237,7 +223,7 @@ esp_partition_t * RECOVERY_IRAM_FUNCTION _get_ota_partition(esp_partition_subtyp -esp_err_t RECOVERY_IRAM_FUNCTION _erase_last_boot_app_partition(esp_partition_t *ota_partition) +esp_err_t _erase_last_boot_app_partition(esp_partition_t *ota_partition) { uint16_t num_passes=0; uint16_t remain_size=0; @@ -270,26 +256,21 @@ esp_err_t RECOVERY_IRAM_FUNCTION _erase_last_boot_app_partition(esp_partition_t err=esp_partition_erase_range(ota_partition, i*single_pass_size, single_pass_size); if(err!=ESP_OK) return err; // triggerStatusJsonRefresh(i%10==0?true:false,"Erasing flash (%u/%u)",i,num_passes); - if(i%10) { + if(i%2) { triggerStatusJsonRefresh(false,"Erasing flash (%u/%u)",i,num_passes); } - taskYIELD(); + vTaskDelay(200/ portTICK_PERIOD_MS); // wait here for a short amount of time. This will help with reducing WDT errors } if(remain_size>0){ err=esp_partition_erase_range(ota_partition, ota_partition->size-remain_size, remain_size); if(err!=ESP_OK) return err; } - triggerStatusJsonRefresh(false,"Erasing flash (100%%)"); + triggerStatusJsonRefresh(true,"Erasing flash complete."); taskYIELD(); return ESP_OK; } -static void RECOVERY_IRAM_FUNCTION http_cleanup(esp_http_client_handle_t client) -{ - esp_http_client_close(client); - esp_http_client_cleanup(client); -} -static bool RECOVERY_IRAM_FUNCTION process_again(int status_code) +static bool process_again(int status_code) { switch (status_code) { case HttpStatus_MovedPermanently: @@ -301,7 +282,7 @@ static bool RECOVERY_IRAM_FUNCTION process_again(int status_code) } return false; } -static esp_err_t RECOVERY_IRAM_FUNCTION _http_handle_response_code(esp_http_client_handle_t http_client, int status_code) +static esp_err_t _http_handle_response_code(esp_http_client_handle_t http_client, int status_code) { esp_err_t err; if (status_code == HttpStatus_MovedPermanently || status_code == HttpStatus_Found) { @@ -341,7 +322,7 @@ static esp_err_t RECOVERY_IRAM_FUNCTION _http_handle_response_code(esp_http_clie return err; } -static esp_err_t RECOVERY_IRAM_FUNCTION _http_connect(esp_http_client_handle_t http_client) +static esp_err_t _http_connect(esp_http_client_handle_t http_client) { esp_err_t err = ESP_FAIL; int status_code, header_ret; @@ -361,6 +342,9 @@ static esp_err_t RECOVERY_IRAM_FUNCTION _http_connect(esp_http_client_handle_t h ESP_LOGD(TAG, "HTTP Header fetch completed, found content length of %d",header_ret); status_code = esp_http_client_get_status_code(http_client); ESP_LOGD(TAG, "HTTP status code was %d",status_code); + + + err = _http_handle_response_code(http_client, status_code); if (err != ESP_OK) { return err; @@ -368,39 +352,51 @@ static esp_err_t RECOVERY_IRAM_FUNCTION _http_connect(esp_http_client_handle_t h } while (process_again(status_code)); return err; } +void ota_task_cleanup(const char * message, ...){ + ota_status.bOTAThreadStarted=false; + if(message!=NULL){ -void RECOVERY_IRAM_FUNCTION ota_task(void *pvParameter) + va_list args; + va_start(args, message); + triggerStatusJsonRefresh(true,message, args); + va_end(args); + ESP_LOGE(TAG, "%s",ota_status.status_text); + } + FREE_RESET(ota_status.redirected_url); + FREE_RESET(ota_status.current_url); + FREE_RESET(ota_write_data); + if(ota_http_client!=NULL) { + esp_http_client_cleanup(ota_http_client); + ota_http_client=NULL; + } + ota_status.bOTAStarted = false; + task_fatal_error(); +} +void ota_task(void *pvParameter) { - char * passedURL=(char *)pvParameter; esp_err_t err = ESP_OK; - int status_code, header_ret; - size_t buffer_size = BUFFSIZE; + ESP_LOGD(TAG, "HTTP ota Thread started"); const esp_partition_t *configured = esp_ota_get_boot_partition(); const esp_partition_t *running = esp_ota_get_running_partition(); + const esp_partition_t * update_partition = esp_ota_get_next_update_partition(NULL); + ESP_LOGI(TAG, "esp_ota_get_next_update_partition returned : partition [%s] subtype %d at offset 0x%x", + update_partition->label, update_partition->subtype, update_partition->address); if (configured != running) { - ESP_LOGW(TAG, "Configured OTA boot partition at offset 0x%08x, but running from offset 0x%08x", - configured->address, running->address); + ESP_LOGW(TAG, "Configured OTA boot partition at offset 0x%08x, but running from offset 0x%08x", configured->address, running->address); ESP_LOGW(TAG, "(This can happen if either the OTA boot data or preferred boot image become corrupted somehow.)"); } - ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08x)", - running->type, running->subtype, running->address); + ESP_LOGI(TAG, "Running partition [%s] type %d subtype %d (offset 0x%08x)", running->label, running->type, running->subtype, running->address); _printMemStats(); - ota_status.bInitialized = true; - ESP_LOGD(TAG, "HTTP ota Thread started"); - triggerStatusJsonRefresh(true,"Initializing..."); - ota_status.bRedirectFound=false; - if(passedURL==NULL || strlen(passedURL)==0){ - ESP_LOGE(TAG,"HTTP OTA called without a url"); - triggerStatusJsonRefresh(true,"Error: Updating needs a URL!"); - ota_status.bOTAThreadStarted=false; - FREE_RESET(ota_write_data); - vTaskDelete(NULL); - return ; + + + ESP_LOGI(TAG,"Initializing OTA configuration"); + err = init_config(pvParameter); + if(err!=ESP_OK){ + ota_task_cleanup("Error: Failed to initialize OTA."); + return; } - ota_status.current_url= strdup(passedURL); - FREE_RESET(pvParameter); /* Locate and erase ota application partition */ ESP_LOGW(TAG,"**************** Expecting WATCHDOG errors below during flash erase. This is OK and not to worry about **************** "); @@ -408,75 +404,44 @@ void RECOVERY_IRAM_FUNCTION ota_task(void *pvParameter) esp_partition_t *ota_partition = _get_ota_partition(ESP_PARTITION_SUBTYPE_APP_OTA_0); if(ota_partition == NULL){ ESP_LOGE(TAG,"Unable to locate OTA application partition. "); - FREE_RESET(ota_status.current_url); - FREE_RESET(ota_write_data); - triggerStatusJsonRefresh(true,"Error: OTA application partition not found. (%s)",esp_err_to_name(err)); - ota_status.bOTAThreadStarted=false; - vTaskDelete(NULL); + ota_task_cleanup("Error: OTA application partition not found. (%s)",esp_err_to_name(err)); + return; } _printMemStats(); err=_erase_last_boot_app_partition(ota_partition); if(err!=ESP_OK){ - ESP_LOGE(TAG,"Unable to erase last APP partition. (%s)",esp_err_to_name(err)); - FREE_RESET(ota_status.current_url); - FREE_RESET(ota_status.redirected_url); - triggerStatusJsonRefresh(true,"Error: Unable to erase last APP partition. (%s)",esp_err_to_name(err)); - ota_status.bOTAThreadStarted=false; - FREE_RESET(ota_write_data); - vTaskDelete(NULL); + ota_task_cleanup("Error: Unable to erase last APP partition. (%s)",esp_err_to_name(err)); + return; } - _printMemStats(); - ESP_LOGI(TAG,"Initializing http client configuration"); - if(init_config(ota_status.bRedirectFound?ota_status.redirected_url:ota_status.current_url)!=ESP_OK){ - ESP_LOGE(TAG, "Failed to initialise HTTP configuration"); - triggerStatusJsonRefresh(true,"Error: Failed to initialize OTA."); - ota_status.bOTAThreadStarted=false; - FREE_RESET(ota_write_data); - task_fatal_error(); - } _printMemStats(); ota_status.bOTAStarted = true; triggerStatusJsonRefresh(true,"Starting OTA..."); - esp_http_client_handle_t client = esp_http_client_init(&ota_config); - if (client == NULL) { - ESP_LOGE(TAG, "Failed to initialise HTTP connection"); - triggerStatusJsonRefresh(true,"Error: Failed to initialize HTTP connection."); - ota_status.bOTAThreadStarted=false; - FREE_RESET(ota_write_data); - task_fatal_error(); + ota_http_client = esp_http_client_init(&ota_config); + if (ota_http_client == NULL) { + ota_task_cleanup("Error: Failed to initialize HTTP connection."); + return; } _printMemStats(); // Open the http connection and follow any redirection - err = _http_connect(client); + err = _http_connect(ota_http_client); if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed start http read: %s", esp_err_to_name(err)); - triggerStatusJsonRefresh(true,"Error: HTTP Start read failed. (%s)",esp_err_to_name(err)); - ota_status.bOTAThreadStarted=false; - esp_http_client_cleanup(client); - FREE_RESET(ota_write_data); - task_fatal_error(); + ota_task_cleanup("Error: HTTP Start read failed. (%s)",esp_err_to_name(err)); + return; } _printMemStats(); -// update_partition = esp_ota_get_next_update_partition(NULL); -// ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x", -// update_partition->subtype, update_partition->address); -// assert(update_partition != NULL); esp_ota_handle_t update_handle = 0 ; 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, buffer_size); + int data_read = esp_http_client_read(ota_http_client, ota_write_data, buffer_size); if (data_read < 0) { - ESP_LOGE(TAG, "Error: SSL data read error"); - triggerStatusJsonRefresh(true,"Error: Data read error"); - ota_status.bOTAThreadStarted=false; - http_cleanup(client); - FREE_RESET(ota_write_data); - task_fatal_error(); + ota_task_cleanup("Error: Data read error"); + return; } else if (data_read > 0) { if (image_header_was_checked == false) { esp_app_desc_t new_app_info; @@ -490,11 +455,11 @@ void RECOVERY_IRAM_FUNCTION ota_task(void *pvParameter) ESP_LOGI(TAG, "Running recovery version: %s", running_app_info.version); } -// 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); -// } + 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 version with last invalid partition // if (last_invalid_app != NULL) { @@ -502,15 +467,12 @@ void RECOVERY_IRAM_FUNCTION ota_task(void *pvParameter) // 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); -// infinite_loop(); +// ota_task_cleanup("esp_ota_begin failed (%s)", esp_err_to_name(err)); // } // } if (memcmp(new_app_info.version, running_app_info.version, sizeof(new_app_info.version)) == 0) { ESP_LOGW(TAG, "Current running version is the same as a new."); -// http_cleanup(client); -// infinite_loop(); } image_header_was_checked = true; @@ -518,33 +480,33 @@ void RECOVERY_IRAM_FUNCTION ota_task(void *pvParameter) // Call OTA Begin with a small partition size - this drives the erase operation which was already done; err = esp_ota_begin(ota_partition, 512, &update_handle); if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err)); - http_cleanup(client); - FREE_RESET(ota_write_data); - task_fatal_error(); - } - else { - ESP_LOGI(TAG, "esp_ota_begin succeeded"); + ota_task_cleanup("esp_ota_begin failed (%s)", esp_err_to_name(err)); + return; } + ESP_LOGD(TAG, "esp_ota_begin succeeded"); } else { - ESP_LOGE(TAG, "received package is not fit len"); - triggerStatusJsonRefresh(true,"Error: Binary file too large for the current partition"); - ota_status.bOTAThreadStarted=false; - http_cleanup(client); - FREE_RESET(ota_write_data); - task_fatal_error(); + ota_task_cleanup("Error: Binary file too large for the current partition"); + return; } } err = esp_ota_write( update_handle, (const void *)ota_write_data, data_read); if (err != ESP_OK) { - triggerStatusJsonRefresh(true,"Error: OTA Partition write failure. (%s)",esp_err_to_name(err)); - ota_status.bOTAThreadStarted=false; - http_cleanup(client); - FREE_RESET(ota_write_data); - task_fatal_error(); + ota_task_cleanup("Error: OTA Partition write failure. (%s)",esp_err_to_name(err)); + return; } binary_file_length += data_read; ESP_LOGD(TAG, "Written image length %d", binary_file_length); + ota_status.ota_actual_len=binary_file_length; + if(ota_get_pct_complete()%5 == 0) ota_status.newpct = ota_get_pct_complete(); + if(ota_status.lastpct!=ota_status.newpct ) { + gettimeofday(&tv, NULL); + uint32_t elapsed_ms= (tv.tv_sec-ota_status.OTA_start.tv_sec )*1000+(tv.tv_usec-ota_status.OTA_start.tv_usec)/1000; + ESP_LOGI(TAG,"OTA progress : %d/%d (%d pct), %d KB/s", ota_status.ota_actual_len, ota_status.ota_total_len, ota_status.newpct, elapsed_ms>0?ota_status.ota_actual_len*1000/elapsed_ms/1024:0); + triggerStatusJsonRefresh(true,"Downloading & writing update."); + ota_status.lastpct=ota_status.newpct; + } + taskYIELD(); + } else if (data_read == 0) { ESP_LOGI(TAG, "Connection closed"); break; @@ -553,40 +515,29 @@ void RECOVERY_IRAM_FUNCTION ota_task(void *pvParameter) ESP_LOGI(TAG, "Total Write binary data length: %d", binary_file_length); if (ota_status.ota_total_len != binary_file_length) { - ESP_LOGE(TAG, "Error in receiving complete file"); - triggerStatusJsonRefresh(true,"Error: Error in receiving complete file"); - ota_status.bOTAThreadStarted=false; - http_cleanup(client); - FREE_RESET(ota_write_data); - task_fatal_error(); + ota_task_cleanup("Error: Error in receiving complete file"); + return; } _printMemStats(); err = esp_ota_end(update_handle); if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_ota_end failed (%s)!", esp_err_to_name(err)); - triggerStatusJsonRefresh(true,"Error: %s",esp_err_to_name(err)); - ota_status.bOTAThreadStarted=false; - http_cleanup(client); - FREE_RESET(ota_write_data); - task_fatal_error(); - } + ota_task_cleanup("Error: %s",esp_err_to_name(err)); + return; + } _printMemStats(); err = esp_ota_set_boot_partition(ota_partition); if (err == ESP_OK) { + ESP_LOGI(TAG,"OTA Process completed successfully!"); triggerStatusJsonRefresh(true,"Success!"); + vTaskDelay(1500/ portTICK_PERIOD_MS); // wait here to give the UI a chance to refresh esp_restart(); } else { - triggerStatusJsonRefresh(true,"Error: %s",esp_err_to_name(err)); - wifi_manager_refresh_ota_json(); - ESP_LOGE(TAG, "Unable to set boot partition. %s", esp_err_to_name(err)); - ota_status.bOTAThreadStarted=false; + ota_task_cleanup("Error: Unable to update boot partition [%s]",esp_err_to_name(err)); + return; } - FREE_RESET(ota_status.current_url); - FREE_RESET(ota_status.redirected_url); - FREE_RESET(ota_write_data); - vTaskDelete(NULL); - + ota_task_cleanup(NULL); + return; } esp_err_t process_recovery_ota(const char * bin_url){ diff --git a/components/squeezelite-ota/squeezelite-ota.h b/components/squeezelite-ota/squeezelite-ota.h index b2fd5667..8e51ee74 100644 --- a/components/squeezelite-ota/squeezelite-ota.h +++ b/components/squeezelite-ota/squeezelite-ota.h @@ -7,6 +7,9 @@ #pragma once #include "esp_attr.h" +#include "esp_image_format.h" +#include "esp_ota_ops.h" + #if RECOVERY_APPLICATION #define CODE_RAM_LOCATION #define RECOVERY_IRAM_FUNCTION IRAM_ATTR @@ -17,10 +20,21 @@ -// ERASE BLOCK needs to be a multiple of wear leveling's sector size -#define OTA_FLASH_ERASE_BLOCK (uint32_t)512000 -#define OTA_STACK_SIZE 5120 +// ERASE BLOCK needs to be a multiple of sector size. If a different multiple is passed +// the OTA process will adjust. Here, we need to strike the balance between speed and +// stability. The larger the blocks, the faster the erase will be, but the more likely +// the system will throw WDT while the flash chip is locked and the more likely +// the OTA process will derail +#define OTA_FLASH_ERASE_BLOCK (uint32_t)249856 + +// We're running the OTA without squeezelite in the background, so we can set a comfortable +// amount of stack to avoid overflows. +#define OTA_STACK_SIZE 10240 + +// To speed up processing, we set this priority to a number that is higher than normal +// tasks #define OTA_TASK_PRIOTITY 6 + esp_err_t start_ota(const char * bin_url); const char * ota_get_status(); uint8_t ota_get_pct_complete(); diff --git a/main/component.mk b/main/component.mk index 719925ae..1b631af2 100644 --- a/main/component.mk +++ b/main/component.mk @@ -10,4 +10,5 @@ CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_INFO COMPONENT_ADD_INCLUDEDIRS += $(COMPONENT_PATH)/../tools COMPONENT_EXTRA_INCLUDES += $(PROJECT_PATH)/components/tools/ -LDFLAGS += -s \ No newline at end of file +LDFLAGS += -s +COMPONENT_EMBED_TXTFILES := ${PROJECT_PATH}/server_certs/github.pem \ No newline at end of file diff --git a/main/config.c b/main/config.c index a311b1c5..9b46c1de 100644 --- a/main/config.c +++ b/main/config.c @@ -61,7 +61,16 @@ bool config_set_group_bit(int bit_num,bool flag); cJSON * config_set_value_safe(nvs_type_t nvs_type, const char *key, void * value); static void vCallbackFunction( TimerHandle_t xTimer ); void config_set_entry_changed_flag(cJSON * entry, cJSON_bool flag); - +#define IMPLEMENT_SET_DEFAULT(t,nt) void config_set_default_## t (const char *key, t value){\ + void * pval = malloc(sizeof(value));\ + *((t *) pval) = value;\ + config_set_default(nt, key,pval,0);\ + free(pval); } +#define IMPLEMENT_GET_NUM(t,nt) esp_err_t config_get_## t (const char *key, t * value){\ + void * pval = config_alloc_get(nt, key);\ + if(pval!=NULL){ *value = *(t * )pval; free(pval); return ESP_OK; }\ + return ESP_FAIL;} +#ifdef RECOVERY_APPLICATION static void * malloc_fn(size_t sz){ void * ptr = heap_caps_malloc(sz, MALLOC_CAP_SPIRAM); @@ -79,6 +88,7 @@ static void * free_fn(void * ptr){ } return NULL; } +#endif void init_cJSON(){ static cJSON_Hooks hooks; // initialize cJSON hooks it uses SPIRAM memory @@ -118,52 +128,71 @@ void config_start_timer(){ } } + +nvs_type_t config_get_item_type(cJSON * entry){ + if(entry==NULL){ + ESP_LOGE(TAG,"null pointer received!"); + return true; + } + cJSON * item_type = cJSON_GetObjectItemCaseSensitive(entry, "type"); + if(item_type ==NULL ) { + ESP_LOGE(TAG, "Item type not found! "); + return 0; + } + ESP_LOGD(TAG,"Found item type %f",item_type->valuedouble); + return item_type->valuedouble; +} + + cJSON * config_set_value_safe(nvs_type_t nvs_type, const char *key, void * value){ - char * num_buffer = NULL; - num_buffer = malloc(NUM_BUFFER_LEN); - memset(num_buffer,0x00,NUM_BUFFER_LEN); cJSON * entry = cJSON_CreateObject(); + double numvalue = 0; if(entry == NULL) { ESP_LOGE(TAG, "Unable to allocate memory for entry %s",key); return NULL; } - cJSON_AddNumberToObject(entry,"type", nvs_type ); - switch (nvs_type) { - case NVS_TYPE_I8: - snprintf(num_buffer, NUM_BUFFER_LEN-1, "%i", *(int8_t*)value); - cJSON_AddNumberToObject(entry,"value", *(int8_t*)value ); - break; - case NVS_TYPE_I16: - snprintf(num_buffer, NUM_BUFFER_LEN-1, "%i", *(int16_t*)value); - cJSON_AddNumberToObject(entry,"value", *(int16_t*)value ); - break; - case NVS_TYPE_I32: - snprintf(num_buffer, NUM_BUFFER_LEN-1, "%i", *(int32_t*)value); - cJSON_AddNumberToObject(entry,"value", *(int32_t*)value ); - break; - case NVS_TYPE_U8: - snprintf(num_buffer, NUM_BUFFER_LEN-1, "%u", *(uint8_t*)value); - cJSON_AddNumberToObject(entry,"value", *(uint8_t*)value ); - break; - case NVS_TYPE_U16: - snprintf(num_buffer, NUM_BUFFER_LEN-1, "%u", *(uint16_t*)value); - cJSON_AddNumberToObject(entry,"value", *(uint16_t*)value ); - break; - case NVS_TYPE_U32: - snprintf(num_buffer, NUM_BUFFER_LEN-1, "%u", *(uint32_t*)value); - cJSON_AddNumberToObject(entry,"value", *(uint32_t*)value ); - break; - case NVS_TYPE_STR: - cJSON_AddStringToObject(entry, "value", (char *)value); - break; - case NVS_TYPE_I64: - case NVS_TYPE_U64: - default: - ESP_LOGE(TAG, "nvs type %u not supported", nvs_type); - break; - } + cJSON * existing = cJSON_GetObjectItemCaseSensitive(nvs_json, key); + if(existing !=NULL && nvs_type == NVS_TYPE_STR && config_get_item_type(existing) != NVS_TYPE_STR ) { + ESP_LOGW(TAG, "Storing numeric value from string"); + numvalue = atof((char *)value); + cJSON_AddNumberToObject(entry,"value", numvalue ); + nvs_type_t exist_type = config_get_item_type(existing); + ESP_LOGW(TAG, "Stored value %f from string %s as type %d",numvalue, (char *)value,exist_type); + cJSON_AddNumberToObject(entry,"type", exist_type); + } + else { + cJSON_AddNumberToObject(entry,"type", nvs_type ); + switch (nvs_type) { + case NVS_TYPE_I8: + cJSON_AddNumberToObject(entry,"value", *(int8_t*)value ); + break; + case NVS_TYPE_I16: + cJSON_AddNumberToObject(entry,"value", *(int16_t*)value ); + break; + case NVS_TYPE_I32: + cJSON_AddNumberToObject(entry,"value", *(int32_t*)value ); + break; + case NVS_TYPE_U8: + cJSON_AddNumberToObject(entry,"value", *(uint8_t*)value ); + break; + case NVS_TYPE_U16: + cJSON_AddNumberToObject(entry,"value", *(uint16_t*)value ); + break; + case NVS_TYPE_U32: + cJSON_AddNumberToObject(entry,"value", *(uint32_t*)value ); + break; + case NVS_TYPE_STR: + cJSON_AddStringToObject(entry, "value", (char *)value); + break; + case NVS_TYPE_I64: + case NVS_TYPE_U64: + default: + ESP_LOGE(TAG, "nvs type %u not supported", nvs_type); + break; + } + } if(existing!=NULL ) { ESP_LOGV(TAG, "Changing existing entry [%s].", key); char * exist_str = cJSON_PrintUnformatted(existing); @@ -207,7 +236,6 @@ cJSON * config_set_value_safe(nvs_type_t nvs_type, const char *key, void * value config_set_entry_changed_flag(entry,true); cJSON_AddItemToObject(nvs_json, key, entry); } - free(num_buffer); return entry; } @@ -222,6 +250,7 @@ nvs_type_t config_get_entry_type(cJSON * entry){ ESP_LOGE(TAG, "Entry type not found in nvs cache for existing setting."); return 0; } + ESP_LOGV(TAG,"Found type %s",type_to_str(entry_type->valuedouble)); return entry_type->valuedouble; } void config_set_entry_changed_flag(cJSON * entry, cJSON_bool flag){ @@ -267,6 +296,10 @@ cJSON_bool config_is_entry_changed(cJSON * entry){ } return cJSON_IsTrue(changed); } + + + + void * config_safe_alloc_get_entry_value(nvs_type_t nvs_type, cJSON * entry){ void * value=NULL; if(entry==NULL){ @@ -300,7 +333,6 @@ void * config_safe_alloc_get_entry_value(nvs_type_t nvs_type, cJSON * entry){ return NULL; } - if (nvs_type == NVS_TYPE_I8) { value=malloc(sizeof(int8_t)); *(int8_t *)value = (int8_t)entry_value->valuedouble; @@ -507,7 +539,7 @@ bool config_set_group_bit(int bit_num,bool flag){ } return result; } -//void config_set_default_uint16(const char *key, uint16_t value) { } + void config_set_default(nvs_type_t type, const char *key, void * default_value, size_t blob_size) { if(!config_lock(LOCK_MAX_WAIT/portTICK_PERIOD_MS)){ ESP_LOGE(TAG, "Unable to lock config"); @@ -672,3 +704,16 @@ esp_err_t config_set_value(nvs_type_t nvs_type, const char *key, void * value){ return result; } +IMPLEMENT_SET_DEFAULT(uint8_t,NVS_TYPE_U8); +IMPLEMENT_SET_DEFAULT(int8_t,NVS_TYPE_I8); +IMPLEMENT_SET_DEFAULT(uint16_t,NVS_TYPE_U16); +IMPLEMENT_SET_DEFAULT(int16_t,NVS_TYPE_I16); +IMPLEMENT_SET_DEFAULT(uint32_t,NVS_TYPE_U32); +IMPLEMENT_SET_DEFAULT(int32_t,NVS_TYPE_I32); + +IMPLEMENT_GET_NUM(uint8_t,NVS_TYPE_U8); +IMPLEMENT_GET_NUM(int8_t,NVS_TYPE_I8); +IMPLEMENT_GET_NUM(uint16_t,NVS_TYPE_U16); +IMPLEMENT_GET_NUM(int16_t,NVS_TYPE_I16); +IMPLEMENT_GET_NUM(uint32_t,NVS_TYPE_U32); +IMPLEMENT_GET_NUM(int32_t,NVS_TYPE_I32); diff --git a/main/config.h b/main/config.h index 2d2e8bcb..d0861321 100644 --- a/main/config.h +++ b/main/config.h @@ -10,6 +10,23 @@ extern "C" { #ifdef __cplusplus } #endif +#define DECLARE_SET_DEFAULT(t) void config_set_default_## t (const char *key, t value); +#define DECLARE_GET_NUM(t) esp_err_t config_get_## t (const char *key, t * value); + + +DECLARE_SET_DEFAULT(uint8_t); +DECLARE_SET_DEFAULT(uint16_t); +DECLARE_SET_DEFAULT(uint32_t); +DECLARE_SET_DEFAULT(int8_t); +DECLARE_SET_DEFAULT(int16_t); +DECLARE_SET_DEFAULT(int32_t); +DECLARE_GET_NUM(uint8_t); +DECLARE_GET_NUM(uint16_t); +DECLARE_GET_NUM(uint32_t); +DECLARE_GET_NUM(int8_t); +DECLARE_GET_NUM(int16_t); +DECLARE_GET_NUM(int32_t); + bool config_has_changes(); void config_commit_to_nvs(); void config_start_timer(); diff --git a/main/esp_app_main.c b/main/esp_app_main.c index dcf144fe..ef4d485e 100644 --- a/main/esp_app_main.c +++ b/main/esp_app_main.c @@ -50,6 +50,9 @@ extern bool enable_bt_sink; extern bool enable_airplay; extern bool jack_mutes_amp; +static const char certs_namespace[] = "certificates"; +static const char certs_key[] = "blob"; +static const char certs_version[] = "version"; EventGroupHandle_t wifi_event_group; @@ -69,6 +72,8 @@ char * fwurl = NULL; #define LED_RED_GPIO -1 #endif static bool bWifiConnected=false; +extern const uint8_t server_cert_pem_start[] asm("_binary_github_pem_start"); +extern const uint8_t server_cert_pem_end[] asm("_binary_github_pem_end"); @@ -125,6 +130,108 @@ esp_log_level_t get_log_level_from_char(char * level){ void set_log_level(char * tag, char * level){ esp_log_level_set(tag, get_log_level_from_char(level)); } +esp_err_t update_certificates(){ +// server_cert_pem_start +// server_cert_pem_end + + nvs_handle handle; + esp_err_t esp_err; + esp_app_desc_t running_app_info; + + ESP_LOGI(TAG, "About to check if certificates need to be updated in flash"); + esp_err = nvs_open_from_partition(settings_partition, certs_namespace, NVS_READWRITE, &handle); + if (esp_err != ESP_OK) { + ESP_LOGE(TAG, "Unable to open name namespace %s. Error %s", certs_namespace, esp_err_to_name(esp_err)); + return esp_err; + } + + const esp_partition_t *running = esp_ota_get_running_partition(); + if(running->subtype !=ESP_PARTITION_SUBTYPE_APP_FACTORY ){ + ESP_LOGI(TAG, "Running partition [%s] type %d subtype %d (offset 0x%08x)", running->label, running->type, running->subtype, running->address); + + } + + if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) { + ESP_LOGI(TAG, "Running version: %s", running_app_info.version); + } + + + size_t len=0; + char *str=NULL; + bool changed=false; + if ( (esp_err= nvs_get_str(handle, certs_version, NULL, &len)) == ESP_OK) { + str=(char *)malloc(len); + if ( (esp_err = nvs_get_str(handle, certs_version, str, &len)) == ESP_OK) { + printf("String associated with key '%s' is %s \n", certs_version, str); + } + } + if(str!=NULL){ + if(strcmp((char *)running_app_info.version,(char *)str )){ + // Versions are different + ESP_LOGW(TAG,"Found a different software version. Updating certificates"); + changed=true; + } + free(str); + } + else { + ESP_LOGW(TAG,"No certificate found. Adding certificates"); + changed=true; + } + + if(changed){ + + esp_err = nvs_set_blob(handle, certs_key, server_cert_pem_start, (server_cert_pem_end-server_cert_pem_start)); + if(esp_err!=ESP_OK){ + ESP_LOGE(TAG, "Failed to store certificate data: %s", esp_err_to_name(esp_err)); + } + else { + ESP_LOGI(TAG,"Updated stored https certificates"); + esp_err = nvs_set_str(handle, certs_version, running_app_info.version); + if(esp_err!=ESP_OK){ + ESP_LOGE(TAG, "Failed to store app version: %s", esp_err_to_name(esp_err)); + } + else { + esp_err = nvs_commit(handle); + if(esp_err!=ESP_OK){ + ESP_LOGE(TAG, "Failed to commit certificate changes: %s", esp_err_to_name(esp_err)); + } + } + } + } + + nvs_close(handle); + return ESP_OK; +} +const char * get_certificate(){ + nvs_handle handle; + esp_err_t esp_err; + char *blob =NULL; +// + ESP_LOGD(TAG, "Fetching certificate."); + esp_err = nvs_open_from_partition(settings_partition, certs_namespace, NVS_READONLY, &handle); + if(esp_err == ESP_OK){ + size_t len; + esp_err = nvs_get_blob(handle, certs_key, NULL, &len); + if( esp_err == ESP_OK) { + blob = (char *)malloc(len); + esp_err = nvs_get_blob(handle, certs_key, blob, &len); + if ( esp_err == ESP_OK) { + printf("Blob associated with key '%s' is %d bytes long: \n", certs_key, len); + } + } + else{ + ESP_LOGE(TAG, "Unable to get the existing blob from namespace %s. [%s]", certs_namespace, esp_err_to_name(esp_err)); + } + nvs_close(handle); + } + else{ + ESP_LOGE(TAG, "Unable to open name namespace %s. [%s]", certs_namespace, esp_err_to_name(esp_err)); + } + return blob; +} + + + //CONFIG_SDIF_NUM=0 @@ -207,8 +314,7 @@ void register_default_nvs(){ ESP_LOGD(TAG,"Registering default value for key %s, value %s", "bypass_wm", "0"); config_set_default(NVS_TYPE_STR, "bypass_wm", "0", 0); -// ESP_LOGD(TAG,"Registering default value for key %s, value %s", "test_num", "0"); -// config_set_default(NVS_TYPE_U16, "test_num", (uint16_t)2, 0); + ESP_LOGD(TAG,"Registering default value for key %s, value %s", "test_num", "0"); char number_buffer[101] = {}; @@ -260,7 +366,7 @@ void register_default_nvs(){ void app_main() { char * fwurl = NULL; - + esp_err_t update_certificates(); ESP_LOGD(TAG,"Creating event group for wifi"); wifi_event_group = xEventGroupCreate(); ESP_LOGD(TAG,"Clearing CONNECTED_BIT from wifi group"); @@ -268,12 +374,18 @@ void app_main() ESP_LOGI(TAG,"Starting app_main"); initialize_nvs(); + ESP_LOGI(TAG,"Setting up config subsystem."); config_init(); - ESP_LOGD(TAG,"Registering default values"); + ESP_LOGI(TAG,"Registering default values"); register_default_nvs(); +#if !RECOVERY_APPLICATION + ESP_LOGI(TAG,"Checking if certificates need to be updated"); + update_certificates(); +#endif + ESP_LOGD(TAG,"Getting firmware OTA URL (if any)"); fwurl = process_ota_url(); diff --git a/main/nvs_utilities.c b/main/nvs_utilities.c index 73ec4e43..691b946c 100644 --- a/main/nvs_utilities.c +++ b/main/nvs_utilities.c @@ -20,6 +20,55 @@ const char current_namespace[] = "config"; const char settings_partition[] = "settings"; static const char * TAG = "nvs_utilities"; +typedef struct { + nvs_type_t type; + const char *str; +} type_str_pair_t; + +static const type_str_pair_t type_str_pair[] = { + { NVS_TYPE_I8, "i8" }, + { NVS_TYPE_U8, "u8" }, + { NVS_TYPE_U16, "u16" }, + { NVS_TYPE_I16, "i16" }, + { NVS_TYPE_U32, "u32" }, + { NVS_TYPE_I32, "i32" }, + { NVS_TYPE_U64, "u64" }, + { NVS_TYPE_I64, "i64" }, + { NVS_TYPE_STR, "str" }, + { NVS_TYPE_BLOB, "blob" }, + { NVS_TYPE_ANY, "any" }, +}; + +static const size_t TYPE_STR_PAIR_SIZE = sizeof(type_str_pair) / sizeof(type_str_pair[0]); +void print_blob(const char *blob, size_t len) +{ + for (int i = 0; i < len; i++) { + printf("%02x", blob[i]); + } + printf("\n"); +} +nvs_type_t str_to_type(const char *type) +{ + for (int i = 0; i < TYPE_STR_PAIR_SIZE; i++) { + const type_str_pair_t *p = &type_str_pair[i]; + if (strcmp(type, p->str) == 0) { + return p->type; + } + } + + return NVS_TYPE_ANY; +} +const char *type_to_str(nvs_type_t type) +{ + for (int i = 0; i < TYPE_STR_PAIR_SIZE; i++) { + const type_str_pair_t *p = &type_str_pair[i]; + if (p->type == type) { + return p->str; + } + } + + return "Unknown"; +} void initialize_nvs() { ESP_LOGI(TAG, "Initializing flash nvs "); esp_err_t err = nvs_flash_init(); diff --git a/main/nvs_utilities.h b/main/nvs_utilities.h index d54faea6..154d66ad 100644 --- a/main/nvs_utilities.h +++ b/main/nvs_utilities.h @@ -16,6 +16,9 @@ esp_err_t store_nvs_value(nvs_type_t type, const char *key, void * data); esp_err_t get_nvs_value(nvs_type_t type, const char *key, void*value, const uint8_t buf_size); void * get_nvs_value_alloc(nvs_type_t type, const char *key); esp_err_t erase_nvs(const char *key); +void print_blob(const char *blob, size_t len); +const char *type_to_str(nvs_type_t type); +nvs_type_t str_to_type(const char *type); #ifdef __cplusplus } #endif