2
0

squeezelite-ota.c 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. /* OTA example
  2. This example code is in the Public Domain (or CC0 licensed, at your option.)
  3. Unless required by applicable law or agreed to in writing, this
  4. software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  5. CONDITIONS OF ANY KIND, either express or implied.
  6. */
  7. #include "freertos/FreeRTOS.h"
  8. #include "freertos/task.h"
  9. #include "esp_system.h"
  10. #include "esp_event.h"
  11. #include "esp_log.h"
  12. #include "esp_ota_ops.h"
  13. #include "esp_https_ota.h"
  14. #include "string.h"
  15. #include <stdbool.h>
  16. #include "nvs.h"
  17. #include "nvs_flash.h"
  18. #include "cmd_system.h"
  19. #include "esp_err.h"
  20. #include "tcpip_adapter.h"
  21. #include "squeezelite-ota.h"
  22. #include "nvs_utilities.h"
  23. #include <time.h>
  24. #include <sys/time.h>
  25. #include <stdarg.h>
  26. static const char *TAG = "squeezelite-ota";
  27. extern const uint8_t server_cert_pem_start[] asm("_binary_github_pem_start");
  28. extern const uint8_t server_cert_pem_end[] asm("_binary_github_pem_end");
  29. extern bool wait_for_wifi();
  30. extern char current_namespace[];
  31. static struct {
  32. char status_text[31];
  33. uint32_t ota_actual_len;
  34. uint32_t ota_total_len;
  35. char * redirected_url;
  36. char * current_url;
  37. bool bRedirectFound;
  38. bool bOTAStarted;
  39. bool bInitialized;
  40. uint8_t lastpct;
  41. uint8_t newpct;
  42. struct timeval OTA_start;
  43. bool bOTAThreadStarted;
  44. } ota_status;
  45. struct timeval tv;
  46. static esp_http_client_config_t ota_config;
  47. extern void CODE_RAM_LOCATION wifi_manager_refresh_ota_json();
  48. void CODE_RAM_LOCATION _printMemStats(){
  49. ESP_LOGI(TAG,"Heap internal:%zu (min:%zu) external:%zu (min:%zu)\n",
  50. heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
  51. heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
  52. heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
  53. heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM));
  54. }
  55. void triggerStatusJsonRefresh(const char * status, ...){
  56. va_list args;
  57. va_start(args, status);
  58. snprintf(ota_status.status_text,sizeof(ota_status.status_text)-1,status, args);
  59. va_end(args);
  60. _printMemStats();
  61. wifi_manager_refresh_ota_json();
  62. }
  63. const char * CODE_RAM_LOCATION ota_get_status(){
  64. if(!ota_status.bInitialized)
  65. {
  66. memset(ota_status.status_text, 0x00,sizeof(ota_status.status_text));
  67. ota_status.bInitialized = true;
  68. }
  69. return ota_status.status_text;
  70. }
  71. uint8_t CODE_RAM_LOCATION ota_get_pct_complete(){
  72. return ota_status.ota_total_len==0?0:
  73. (uint8_t)((float)ota_status.ota_actual_len/(float)ota_status.ota_total_len*100.0f);
  74. }
  75. static void __attribute__((noreturn)) task_fatal_error(void)
  76. {
  77. ESP_LOGE(TAG, "Exiting task due to fatal error...");
  78. (void)vTaskDelete(NULL);
  79. while (1) {
  80. ;
  81. }
  82. }
  83. #define FREE_RESET(p) if(p!=NULL) { free(p); p=NULL; }
  84. esp_err_t CODE_RAM_LOCATION _http_event_handler(esp_http_client_event_t *evt)
  85. {
  86. // --------------
  87. // Received parameters
  88. //
  89. // esp_http_client_event_id_tevent_id event_id, to know the cause of the event
  90. // esp_http_client_handle_t client
  91. // esp_http_client_handle_t context
  92. // void *data data of the event
  93. // int data_len - data length of data
  94. // void *user_data -- user_data context, from esp_http_client_config_t user_data
  95. // char *header_key For HTTP_EVENT_ON_HEADER event_id, it’s store current http header key
  96. // char *header_value For HTTP_EVENT_ON_HEADER event_id, it’s store current http header value
  97. // --------------
  98. switch (evt->event_id) {
  99. case HTTP_EVENT_ERROR:
  100. ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
  101. _printMemStats();
  102. //strncpy(ota_status,"HTTP_EVENT_ERROR",sizeof(ota_status)-1);
  103. break;
  104. case HTTP_EVENT_ON_CONNECTED:
  105. ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
  106. if(ota_status.bOTAStarted) triggerStatusJsonRefresh("Installing...");
  107. ota_status.ota_total_len=0;
  108. ota_status.ota_actual_len=0;
  109. ota_status.lastpct=0;
  110. ota_status.newpct=0;
  111. gettimeofday(&ota_status.OTA_start, NULL);
  112. break;
  113. case HTTP_EVENT_HEADER_SENT:
  114. ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
  115. break;
  116. case HTTP_EVENT_ON_HEADER:
  117. 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);
  118. if (strcasecmp(evt->header_key, "location") == 0) {
  119. FREE_RESET(ota_status.redirected_url);
  120. ota_status.redirected_url=strdup(evt->header_value);
  121. ESP_LOGW(TAG,"OTA will redirect to url: %s",ota_status.redirected_url);
  122. ota_status.bRedirectFound= true;
  123. }
  124. if (strcasecmp(evt->header_key, "content-length") == 0) {
  125. ota_status.ota_total_len = atol(evt->header_value);
  126. ESP_LOGW(TAG, "Content length found: %s, parsed to %d", evt->header_value, ota_status.ota_total_len);
  127. }
  128. break;
  129. case HTTP_EVENT_ON_DATA:
  130. vTaskDelay(5/ portTICK_RATE_MS);
  131. if(!ota_status.bOTAStarted) {
  132. ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, status_code=%d, len=%d",esp_http_client_get_status_code(evt->client), evt->data_len);
  133. }
  134. else if(ota_status.bOTAStarted && esp_http_client_get_status_code(evt->client) == 200 ){
  135. ota_status.ota_actual_len+=evt->data_len;
  136. if(ota_get_pct_complete()%2 == 0) ota_status.newpct = ota_get_pct_complete();
  137. if(ota_status.lastpct!=ota_status.newpct )
  138. {
  139. gettimeofday(&tv, NULL);
  140. uint32_t elapsed_ms= (tv.tv_sec-ota_status.OTA_start.tv_sec )*1000+(tv.tv_usec-ota_status.OTA_start.tv_usec)/1000;
  141. wifi_manager_refresh_ota_json();
  142. ota_status.lastpct=ota_status.newpct;
  143. ESP_LOGI(TAG,"OTA chunk : %d bytes (%d of %d) (%d pct), %d KB/s", evt->data_len, 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);
  144. ESP_LOGD(TAG,"Heap internal:%zu (min:%zu) external:%zu (min:%zu)\n",
  145. heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
  146. heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
  147. heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
  148. heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM));
  149. }
  150. }
  151. break;
  152. case HTTP_EVENT_ON_FINISH:
  153. ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
  154. ESP_LOGD(TAG,"Heap internal:%zu (min:%zu) external:%zu (min:%zu)\n",
  155. heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
  156. heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
  157. heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
  158. heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM));
  159. break;
  160. case HTTP_EVENT_DISCONNECTED:
  161. ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
  162. break;
  163. }
  164. return ESP_OK;
  165. }
  166. esp_err_t CODE_RAM_LOCATION init_config(esp_http_client_config_t * conf, const char * url){
  167. memset(conf, 0x00, sizeof(esp_http_client_config_t));
  168. conf->cert_pem = (char *)server_cert_pem_start;
  169. conf->event_handler = _http_event_handler;
  170. conf->buffer_size = 4096;
  171. conf->disable_auto_redirect=true;
  172. conf->skip_cert_common_name_check = false;
  173. conf->url = strdup(url);
  174. conf->max_redirection_count = 0;
  175. return ESP_OK;
  176. }
  177. void CODE_RAM_LOCATION ota_task(void *pvParameter)
  178. {
  179. char * passedURL=(char *)pvParameter;
  180. ota_status.bInitialized = true;
  181. ESP_LOGD(TAG, "HTTP ota Thread started");
  182. triggerStatusJsonRefresh("Initializing...");
  183. ota_status.bRedirectFound=false;
  184. if(passedURL==NULL || strlen(passedURL)==0){
  185. ESP_LOGE(TAG,"HTTP OTA called without a url");
  186. ota_status.bOTAThreadStarted=false;
  187. vTaskDelete(NULL);
  188. return ;
  189. }
  190. ota_status.current_url= strdup(passedURL);
  191. FREE_RESET(pvParameter);
  192. ESP_LOGD(TAG,"Calling esp_https_ota");
  193. init_config(&ota_config,ota_status.bRedirectFound?ota_status.redirected_url:ota_status.current_url);
  194. ota_status.bOTAStarted = true;
  195. triggerStatusJsonRefresh("Starting OTA...");
  196. esp_err_t err = esp_https_ota(&ota_config);
  197. if (err == ESP_OK) {
  198. triggerStatusJsonRefresh("Success!");
  199. esp_restart();
  200. } else {
  201. triggerStatusJsonRefresh("Error: %s",esp_err_to_name(err));
  202. wifi_manager_refresh_ota_json();
  203. ESP_LOGE(TAG, "Firmware upgrade failed with error : %s", esp_err_to_name(err));
  204. ota_status.bOTAThreadStarted=false;
  205. }
  206. FREE_RESET(ota_status.current_url);
  207. FREE_RESET(ota_status.redirected_url);
  208. vTaskDelete(NULL);
  209. }
  210. esp_err_t process_recovery_ota(const char * bin_url){
  211. #if RECOVERY_APPLICATION
  212. // Initialize NVS.
  213. int ret=0;
  214. nvs_handle nvs;
  215. esp_err_t err = nvs_flash_init();
  216. if(ota_status.bOTAThreadStarted){
  217. ESP_LOGE(TAG,"OTA Already started. ");
  218. return ESP_FAIL;
  219. }
  220. memset(&ota_status, 0x00, sizeof(ota_status));
  221. ota_status.bOTAThreadStarted=true;
  222. if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
  223. // todo: If we ever change the size of the nvs partition, we need to figure out a mechanism to enlarge the nvs.
  224. // 1.OTA app partition table has a smaller NVS partition size than the non-OTA
  225. // partition table. This size mismatch may cause NVS initialization to fail.
  226. // 2.NVS partition contains data in new format and cannot be recognized by this version of code.
  227. // If this happens, we erase NVS partition and initialize NVS again.
  228. ESP_LOGW(TAG,"NVS flash size has changed. Formatting nvs");
  229. ESP_ERROR_CHECK(nvs_flash_erase());
  230. err = nvs_flash_init();
  231. }
  232. ESP_ERROR_CHECK(err);
  233. char * urlPtr=strdup(bin_url);
  234. // the first thing we need to do here is to erase the firmware url
  235. // to avoid a boot loop
  236. #ifdef CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1
  237. #define OTA_CORE 0
  238. #warning "OTA will run on core 0"
  239. #else
  240. #warning "OTA will run on core 1"
  241. #define OTA_CORE 1
  242. #endif
  243. ESP_LOGI(TAG, "Starting ota on core %u for : %s", OTA_CORE,urlPtr);
  244. ret=xTaskCreatePinnedToCore(&ota_task, "ota_task", 1024*20, (void *)urlPtr, ESP_TASK_MAIN_PRIO+4, NULL, OTA_CORE);
  245. if (ret != pdPASS) {
  246. ESP_LOGI(TAG, "create thread %s failed", "ota_task");
  247. return ESP_FAIL;
  248. }
  249. #endif
  250. return ESP_OK;
  251. }
  252. esp_err_t start_ota(const char * bin_url, bool bFromAppMain)
  253. {
  254. #if RECOVERY_APPLICATION
  255. return process_recovery_ota(bin_url);
  256. #else
  257. ESP_LOGW(TAG, "Called to update the firmware from url: %s",bin_url);
  258. store_nvs_value(NVS_TYPE_STR, "fwurl", bin_url);
  259. ESP_LOGW(TAG, "Rebooting to recovery to complete the installation");
  260. return guided_factory();
  261. return ESP_OK;
  262. #endif
  263. }