Преглед изворни кода

OTA - redirection support + bootloader

Sebastien пре 5 година
родитељ
комит
deab7950c2

+ 2 - 1
components/squeezelite-ota/component.mk

@@ -5,5 +5,6 @@
 
 # todo: add support for https
 COMPONENT_ADD_INCLUDEDIRS := .
-CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_DEBUG -D CONFIG_OTA_ALLOW_HTTP=1
+COMPONENT_ADD_INCLUDEDIRS += include
+CFLAGS += -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG -DCONFIG_OTA_ALLOW_HTTP=1
 COMPONENT_EMBED_TXTFILES :=  ${PROJECT_PATH}/server_certs/github.pem

+ 202 - 32
components/squeezelite-ota/squeezelite-ota.c

@@ -12,7 +12,6 @@
 #include "esp_event.h"
 #include "esp_log.h"
 #include "esp_ota_ops.h"
-#include "esp_http_client.h"
 #include "esp_https_ota.h"
 #include "string.h"
 #include <stdbool.h>
@@ -23,22 +22,59 @@
 #include "tcpip_adapter.h"
 #include "squeezelite-ota.h"
 
+
 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");
 extern bool wait_for_wifi();
+extern char current_namespace[];
+static struct {
+	char status_text[31];
+	uint32_t ota_actual_len;
+	uint32_t ota_total_len;
+	char * actual_url;
+	bool bOTA_started;
+} ota_status;
+
+static esp_http_client_config_t config;
+static esp_http_client_config_t ota_config;
+static esp_http_client_handle_t client;
 
-#define OTA_URL_SIZE 256
-static char ota_status[31]={0};
-static uint8_t ota_pct=0;
 const char * ota_get_status(){
-	return ota_status;
+	return ota_status.status_text;
 }
 uint8_t ota_get_pct_complete(){
-	return ota_pct;
+	return ota_status.ota_total_len==0?0:
+			(uint8_t)((uint32_t)ota_status.ota_actual_len/(uint32_t)ota_status.ota_total_len*100);
+}
+
+static void __attribute__((noreturn)) task_fatal_error(void)
+{
+    ESP_LOGE(TAG, "Exiting task due to fatal error...");
+    (void)vTaskDelete(NULL);
+
+    while (1) {
+        ;
+    }
 }
+
 esp_err_t _http_event_handler(esp_http_client_event_t *evt)
 {
+// --------------
+//	Received parameters
+//
+//	esp_http_client_event_id_tevent_id event_id, to know the cause of the event
+//	esp_http_client_handle_t client
+//	esp_http_client_handle_t context
+
+//	void *data data of the event
+
+//	int data_len - data length of data
+//	void *user_data -- user_data context, from esp_http_client_config_t user_data
+
+//	char *header_key For HTTP_EVENT_ON_HEADER event_id, it’s store current http header key
+//	char *header_value For HTTP_EVENT_ON_HEADER event_id, it’s store current http header value
+// --------------
     switch (evt->event_id) {
     case HTTP_EVENT_ERROR:
         ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
@@ -46,58 +82,134 @@ esp_err_t _http_event_handler(esp_http_client_event_t *evt)
         break;
     case HTTP_EVENT_ON_CONNECTED:
         ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
-       // strncpy(ota_status,"HTTP_EVENT_ON_CONNECTED",sizsffeof(ota_status)-1);
-                break;
+        if(!ota_status.bOTA_started){
+        	ESP_LOGW(TAG, "Resetting the OTA stats");
+			ota_status.ota_total_len=0;
+			ota_status.ota_actual_len=0;
+			if(ota_status.actual_url!=NULL){
+				free(ota_status.actual_url);
+				ota_status.actual_url=NULL;
+			}
+        }
+       break;
     case HTTP_EVENT_HEADER_SENT:
         ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
        /// strncpy(ota_status,"HTTP_EVENT_HEADER_SENT",sizeof(ota_status)-1);
         break;
     case HTTP_EVENT_ON_HEADER:
         ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, status_code=%d, key=%s, value=%s",esp_http_client_get_status_code(evt->client),evt->header_key, evt->header_value);
-        // check for header Content-Length:2222240
-        // convert to numeric and store in total bin size
-
-        //snprintf(ota_status,sizeof(ota_status)-1,"HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
+        if (strcasecmp(evt->header_key, "location") == 0) {
+        	if(ota_status.actual_url!=NULL) {
+        		free(ota_status.actual_url);
+        		ota_status.actual_url = NULL;
+        	}
+        	ota_status.actual_url=strdup(evt->header_value);
+        	ESP_LOGW(TAG,"OTA will redirect to url: %s",ota_status.actual_url);
+        	ota_status.bOTA_started = true;
+        }
+        if (strcasecmp(evt->header_key, "content-length") == 0) {
+        	ota_status.ota_total_len = atol(evt->header_value);
+        	 ESP_LOGW(TAG, "Content length found: %s, parsed to %d", evt->header_value, ota_status.ota_total_len);
+        }
         break;
     case HTTP_EVENT_ON_DATA:
-        ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, status_code=%d, len=%d",esp_http_client_get_status_code(evt->client), evt->data_len);
-        //increment the total data received, then divide by the bin size and store as ota_pct
-
-        //snprintf(ota_status,sizeof(ota_status)-1, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
+    	if(!ota_status.bOTA_started) ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, status_code=%d, len=%d",esp_http_client_get_status_code(evt->client), evt->data_len);
+        if(esp_http_client_get_status_code(evt->client) == 302){
+                	// This is an indication of a redirect.  Let's follow it
+                	return ESP_OK;
+        }
+        if(esp_http_client_get_status_code(evt->client) == 200 ){
+        	if(!ota_status.bOTA_started) return ESP_OK;
+			ota_status.ota_actual_len+=evt->data_len;
+			ESP_LOGD(TAG,"Receiving OTA data chunk len: %d, %d of %d (%d pct)", evt->data_len, ota_status.ota_actual_len, ota_status.ota_total_len, ota_get_pct_complete());
+        }
         break;
     case HTTP_EVENT_ON_FINISH:
         ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
         break;
     case HTTP_EVENT_DISCONNECTED:
         ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
-        //strncpy(ota_status,"HTTP_EVENT_DISCONNECTED",sizeof(ota_status)-1);
         break;
     }
     return ESP_OK;
 }
 
-void ota_task(void *pvParameter)
+static void check_http_redirect(void)
 {
-	char * bin_url=(char *)pvParameter;
+	esp_err_t err=ESP_OK;
+	ESP_LOGD(TAG, "Checking for http redirects. initializing http client");
+    client = esp_http_client_init(&config);
+    if (client == NULL) {
+		ESP_LOGE(TAG, "Failed to initialise HTTP connection");
+		task_fatal_error();
+	}
+    ESP_LOGD(TAG, "opening http connection");
+	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);
+		task_fatal_error();
+	}
+	ESP_LOGD(TAG, "fetching headers");
+	esp_http_client_fetch_headers(client);
+    if (err == ESP_OK) {
+        ESP_LOGI(TAG, "redirect check returned success");
+    } else {
+        ESP_LOGE(TAG, "redirect check returned %s", esp_err_to_name(err));
+    }
+}
+esp_err_t init_config(esp_http_client_config_t * conf, const char * url){
+	memset(&ota_status, 0x00, sizeof(ota_status));
+	if(url==NULL || strlen(url)==0){
+		ESP_LOGE(TAG,"HTTP OTA called without a url");
+		return ESP_ERR_INVALID_ARG;
+	}
+	memset(conf, 0x00, sizeof(esp_http_client_config_t));
+	ota_status.actual_url= strdup(url);
+	ota_status.bOTA_started=false;
+	conf->cert_pem = (char *)server_cert_pem_start;
+	conf->event_handler = _http_event_handler;
+	conf->buffer_size = 1024*4;
+	conf->disable_auto_redirect=true;
+	conf->skip_cert_common_name_check = false;
+	conf->url = strdup(ota_status.actual_url);
+	conf->max_redirection_count = 0;
 
-     esp_http_client_config_t config = {
-        .url = bin_url,
-        .cert_pem = (char *)server_cert_pem_start,
-        .event_handler = _http_event_handler,
-		.buffer_size = 1024*50,
+	return ESP_OK;
+}
+void ota_task(void *pvParameter)
+{
+	ESP_LOGD(TAG, "HTTP ota Thread started");
 
-    };
+	if(init_config(&config,(char *)pvParameter)!=ESP_OK){
+		if(pvParameter!=NULL) free(pvParameter);
+		vTaskDelete(NULL);
+		return;
+	}
+	free(pvParameter);
 
-    config.skip_cert_common_name_check = false;
+	check_http_redirect();
+	if(!ota_status.bOTA_started || ota_status.actual_url == NULL){
+		// OTA Failed miserably.  Errors would have been logged somewhere
+		ESP_LOGE(TAG,"Redirect check failed. Bailing out");
+		vTaskDelete(NULL);
+	}
+	ESP_LOGD(TAG,"Calling esp_https_ota");
+	if(init_config(&ota_config,ota_status.actual_url)!=ESP_OK){
+		if(pvParameter!=NULL) free(pvParameter);
+		vTaskDelete(NULL);
+		return;
+	}
+	free(ota_status.actual_url);
+	ota_status.actual_url=NULL;
 
-    esp_err_t ret = esp_https_ota(&config);
-    if (ret == ESP_OK) {
+	esp_err_t err = esp_https_ota(&config);
+    if (err == ESP_OK) {
         esp_restart();
     } else {
-        ESP_LOGE(TAG, "Firmware upgrade failed");
+        ESP_LOGE(TAG, "Firmware upgrade failed with error : %s", esp_err_to_name(err));
     }
-    free(pvParameter);
-    return;
+    vTaskDelete(NULL);
 }
 
 void start_ota(const char * bin_url)
@@ -105,6 +217,7 @@ void start_ota(const char * bin_url)
 	ESP_LOGW(TAG, "Called to update the firmware from url: %s",bin_url);
 #if RECOVERY_APPLICATION
 	// Initialize NVS.
+	int ret=0;
 	esp_err_t err = nvs_flash_init();
     if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
     	// todo: If we ever change the size of the nvs partition, we need to figure out a mechanism to enlarge the nvs.
@@ -118,12 +231,69 @@ void start_ota(const char * bin_url)
     ESP_ERROR_CHECK(err);
     char * urlPtr=malloc((strlen(bin_url)+1)*sizeof(char));
     strcpy(urlPtr,bin_url);
+	// the first thing we need to do here is to erase the firmware url
+	// to avoid a boot loop
+    nvs_handle nvs;
+
+    err = nvs_open(current_namespace, NVS_READWRITE, &nvs);
+    if (err == ESP_OK) {
+        err = nvs_erase_key(nvs, "fwurl");
+        if (err == ESP_OK) {
+            err = nvs_commit(nvs);
+            if (err == ESP_OK) {
+                ESP_LOGI(TAG, "Value with key '%s' erased", "fwurl");
+            }
+        }
+        nvs_close(nvs);
+    }
+
 
     ESP_LOGI(TAG, "Starting ota: %s", urlPtr);
-    xTaskCreate(&ota_task, "ota_task", 8192*3,(void *) urlPtr, 5, NULL);
+    ret=xTaskCreate(&ota_task, "ota_task", 1024*8,(void *) urlPtr, 3, NULL);
+    if (ret != pdPASS)  {
+            ESP_LOGI(TAG, "create thread %s failed", "ota_task");
+        }
 #else
     ESP_LOGW(TAG, "Rebooting to recovery to complete the installation");
     guided_factory();
 #endif
 
 }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 3 - 9
components/wifi-manager/wifi_manager.c

@@ -214,9 +214,7 @@ char * wifi_manager_alloc_get_config(char * name, size_t * l){
 	}
 	else
 	{
-		char szErrorDesc[101]={0};
-		esp_err_to_name_r(esp_err , szErrorDesc, sizeof(szErrorDesc)-1);
-		ESP_LOGE(TAG,"Unable to open nvs namespace %s. Error: %d, %s", wifi_manager_nvs_namespace,esp_err, szErrorDesc);
+		ESP_LOGE(TAG,"Unable to open nvs namespace %s. Error: %d, %s", wifi_manager_nvs_namespace,esp_err, esp_err_to_name(esp_err));
 	}
 	return value;
 
@@ -231,9 +229,7 @@ esp_err_t wifi_manager_save_autoexec_flag(uint8_t flag){
 		ESP_LOGE(TAG,"Unable to open nvs namespace %s. nvs is not initialized.",wifi_manager_nvs_namespace);
 	}
 	else if (esp_err != ESP_OK) {
-		char szErrorDesc[101]={0};
-		esp_err_to_name_r(esp_err , szErrorDesc, sizeof(szErrorDesc)-1);
-		ESP_LOGE(TAG,"Unable to open nvs namespace %s. Error: %s", wifi_manager_nvs_namespace, szErrorDesc);
+		ESP_LOGE(TAG,"Unable to open nvs namespace %s. Error: %s", wifi_manager_nvs_namespace, esp_err_to_name(esp_err));
 		return esp_err;
 	}
 
@@ -266,9 +262,7 @@ esp_err_t wifi_manager_save_autoexec_config(char * value, char * name, int len){
 			ESP_LOGE(TAG,"Unable to open nvs namespace %s. nvs is not initialized.",wifi_manager_nvs_namespace);
 	}
 	else if (esp_err != ESP_OK) {
-			char szErrorDesc[101]={0};
-			esp_err_to_name_r(esp_err , szErrorDesc, sizeof(szErrorDesc)-1);
-			ESP_LOGE(TAG,"Unable to open nvs namespace %s. Error: %d, %s", wifi_manager_nvs_namespace, esp_err, szErrorDesc);
+			ESP_LOGE(TAG,"Unable to open nvs namespace %s. Error: %d, %s", wifi_manager_nvs_namespace, esp_err, esp_err_to_name(esp_err));
 			return esp_err;
 		}
     esp_err = nvs_set_str(handle, name, value);

+ 0 - 3
main/esp_app_main.c

@@ -103,9 +103,6 @@ void app_main()
 
 	char * fwurl = get_nvs_value_alloc(NVS_TYPE_STR, "fwurl");
 	if(fwurl){
-		// the first thing we need to do here is to erase the firmware url
-		// to avoid a boot loop
-		erase_nvs("fwurl");
 		while(!bWifiConnected){
 			wait_for_wifi();
 		}