|
@@ -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)
|
|
|
{
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
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");
|
|
|
-
|
|
|
- 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");
|
|
|
|
|
|
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);
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
+ 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);
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
+ 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){
|
|
|
+
|
|
|
+ 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");
|
|
|
-
|
|
|
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){
|
|
|
+
|
|
|
+ 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
|
|
|
|
|
|
+ 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) {
|
|
|
|
|
@@ -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);
|
|
|
+
|
|
|
+
|
|
|
+ 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
|
|
|
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|