squeezelite-ota.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816
  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_https_ota.h"
  13. #include "string.h"
  14. #include <stdbool.h>
  15. #include "nvs.h"
  16. #include "nvs_flash.h"
  17. #include "cmd_system.h"
  18. #include "esp_err.h"
  19. #include "tcpip_adapter.h"
  20. #include "squeezelite-ota.h"
  21. #include "platform_config.h"
  22. #include <time.h>
  23. #include <sys/time.h>
  24. #include <stdarg.h>
  25. #include "esp_secure_boot.h"
  26. #include "esp_flash_encrypt.h"
  27. #include "esp_spi_flash.h"
  28. #include "sdkconfig.h"
  29. #include "messaging.h"
  30. #include "trace.h"
  31. #include "esp_ota_ops.h"
  32. #include "display.h"
  33. #include "gds.h"
  34. #include "gds_text.h"
  35. #include "gds_draw.h"
  36. #include "platform_esp32.h"
  37. extern const char * get_certificate();
  38. #ifdef CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1
  39. #define OTA_CORE 0
  40. #else
  41. #define OTA_CORE 1
  42. #endif
  43. static const char *TAG = "squeezelite-ota";
  44. esp_http_client_handle_t ota_http_client = NULL;
  45. #define IMAGE_HEADER_SIZE sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t) + 1
  46. #define BUFFSIZE 4096
  47. #define HASH_LEN 32 /* SHA-256 digest length */
  48. typedef struct {
  49. char * url;
  50. char * bin;
  51. uint32_t length;
  52. } ota_thread_parms_t ;
  53. static ota_thread_parms_t ota_thread_parms;
  54. typedef enum {
  55. OTA_TYPE_HTTP,
  56. OTA_TYPE_BUFFER,
  57. OTA_TYPE_INVALID
  58. } ota_type_t;
  59. static struct {
  60. uint32_t actual_image_len;
  61. uint32_t total_image_len;
  62. uint32_t remain_image_len;
  63. ota_type_t ota_type;
  64. char * ota_write_data;
  65. char * bin_data;
  66. bool bOTAStarted;
  67. size_t buffer_size;
  68. uint8_t lastpct;
  69. uint8_t newpct;
  70. struct timeval OTA_start;
  71. bool bOTAThreadStarted;
  72. const esp_partition_t *configured;
  73. const esp_partition_t *running;
  74. const esp_partition_t * update_partition;
  75. const esp_partition_t* last_invalid_app ;
  76. const esp_partition_t * ota_partition;
  77. } ota_status;
  78. struct timeval tv;
  79. static esp_http_client_config_t http_client_config;
  80. void _printMemStats(){
  81. ESP_LOGD(TAG,"Heap internal:%zu (min:%zu) external:%zu (min:%zu)",
  82. heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
  83. heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
  84. heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
  85. heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM));
  86. }
  87. uint8_t ota_get_pct_complete(){
  88. return ota_status.total_image_len==0?0:
  89. (uint8_t)((float)ota_status.actual_image_len/(float)ota_status.total_image_len*100.0f);
  90. }
  91. typedef struct {
  92. int x1,y1,x2,y2,width,height;
  93. } rect_t;
  94. typedef struct _progress {
  95. int border_thickness;
  96. int sides_margin;
  97. int vertical_margin;
  98. int bar_tot_height;
  99. int bar_fill_height;
  100. rect_t border;
  101. rect_t filler;
  102. } progress_t;
  103. static progress_t * loc_displayer_get_progress_dft(){
  104. if(!display){
  105. return;
  106. }
  107. int start_coord_offset=0;
  108. static progress_t def={
  109. .border_thickness = 2,
  110. .sides_margin = 2,
  111. .bar_tot_height = 7,
  112. };
  113. def.bar_fill_height= def.bar_tot_height-(def.border_thickness*2);
  114. def.border.x1=start_coord_offset+def.sides_margin;
  115. def.border.x2=GDS_GetWidth(display)-def.sides_margin;
  116. // progress bar will be drawn at the bottom of the display
  117. def.border.y2= GDS_GetHeight(display)-def.border_thickness;
  118. def.border.y1= def.border.y2-def.bar_tot_height;
  119. def.border.width=def.border.x2-def.border.x1;
  120. def.border.height=def.border.y2-def.border.y1;
  121. def.filler.x1= def.border.x1+def.border_thickness;
  122. def.filler.x2= def.border.x2-def.border_thickness;
  123. def.filler.y1= def.border.y1+def.border_thickness;
  124. def.filler.y2= def.border.y2-def.border_thickness;
  125. def.filler.width=def.filler.x2-def.filler.x1;
  126. def.filler.height=def.filler.y2-def.filler.y1;
  127. assert(def.filler.width>0);
  128. assert(def.filler.height>0);
  129. assert(def.border.width>0);
  130. assert(def.border.height>0);
  131. assert(def.border.width>def.filler.width);
  132. assert(def.border.height>def.filler.height);
  133. return &def;
  134. }
  135. static void loc_displayer_progressbar(uint8_t pct){
  136. static progress_t * progress_coordinates;
  137. if(!display){
  138. return;
  139. }
  140. if(!progress_coordinates) progress_coordinates = loc_displayer_get_progress_dft();
  141. int filler_x=progress_coordinates->filler.x1+(int)((float)progress_coordinates->filler.width*(float)pct/(float)100);
  142. ESP_LOGD(TAG,"Drawing %d,%d,%d,%d",progress_coordinates->border.x1,progress_coordinates->border.y1,progress_coordinates->border.x2,progress_coordinates->border.y2);
  143. GDS_DrawBox(display,progress_coordinates->border.x1,progress_coordinates->border.y1,progress_coordinates->border.x2,progress_coordinates->border.y2,GDS_COLOR_WHITE,false);
  144. ESP_LOGD(TAG,"Drawing %d,%d,%d,%d",progress_coordinates->filler.x1,progress_coordinates->filler.y1,filler_x,progress_coordinates->filler.y2);
  145. if(filler_x > progress_coordinates->filler.x1){
  146. GDS_DrawBox(display,progress_coordinates->filler.x1,progress_coordinates->filler.y1,filler_x,progress_coordinates->filler.y2,GDS_COLOR_WHITE,true);
  147. }
  148. else {
  149. // Clear the inner box
  150. GDS_DrawBox(display,progress_coordinates->filler.x1,progress_coordinates->filler.y1,progress_coordinates->filler.x2,progress_coordinates->filler.y2,GDS_COLOR_BLACK,true);
  151. }
  152. ESP_LOGD(TAG,"Updating Display");
  153. GDS_Update(display);
  154. }
  155. void sendMessaging(messaging_types type,const char * fmt, ...){
  156. va_list args;
  157. cJSON * msg = cJSON_CreateObject();
  158. size_t str_len=0;
  159. char * msg_str=NULL;
  160. va_start(args, fmt);
  161. str_len = vsnprintf(NULL,0,fmt,args)+1;
  162. if(str_len>0){
  163. msg_str = malloc(str_len);
  164. vsnprintf(msg_str,str_len,fmt,args);
  165. if(type == MESSAGING_WARNING){
  166. ESP_LOGW(TAG,"%s",msg_str);
  167. }
  168. else if (type == MESSAGING_ERROR){
  169. ESP_LOGE(TAG,"%s",msg_str);
  170. }
  171. else
  172. ESP_LOGI(TAG,"%s",msg_str);
  173. }
  174. else {
  175. ESP_LOGW(TAG, "Sending empty string message");
  176. }
  177. va_end(args);
  178. if(type!=MESSAGING_INFO){
  179. if(display) {
  180. GDS_TextLine(display, 2, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, msg_str);
  181. }
  182. }
  183. cJSON_AddStringToObject(msg,"ota_dsc",str_or_unknown(msg_str));
  184. free(msg_str);
  185. cJSON_AddNumberToObject(msg,"ota_pct", ota_get_pct_complete() );
  186. char * json_msg = cJSON_PrintUnformatted(msg);
  187. messaging_post_message(type, MESSAGING_CLASS_OTA, json_msg);
  188. free(json_msg);
  189. cJSON_free(msg);
  190. _printMemStats();
  191. }
  192. //esp_err_t decode_alloc_ota_message(single_message_t * message, char * ota_dsc, uint8_t * ota_pct ){
  193. // if(!message || !message->message) return ESP_ERR_INVALID_ARG;
  194. // cJSON * json = cJSON_Parse(message->message);
  195. // if(!json) return ESP_FAIL;
  196. // if(ota_dsc) {
  197. // ota_dsc = strdup(cJSON_GetObjectItem(json, "ota_dsc")?cJSON_GetStringValue(cJSON_GetObjectItem(json, "ota_dsc")):"");
  198. // }
  199. // if(ota_pct){
  200. // *ota_pct = cJSON_GetObjectItem(json, "ota_pct")?cJSON_GetObjectItem(json, "ota_pct")->valueint:0;
  201. // }
  202. // cJSON_free(json);
  203. // return ESP_OK;
  204. //}
  205. static void __attribute__((noreturn)) task_fatal_error(void)
  206. {
  207. ESP_LOGE(TAG, "Exiting task due to fatal error...");
  208. (void)vTaskDelete(NULL);
  209. while (1) {
  210. ;
  211. }
  212. }
  213. esp_err_t _http_event_handler(esp_http_client_event_t *evt)
  214. {
  215. // --------------
  216. // Received parameters
  217. //
  218. // esp_http_client_event_id_tevent_id event_id, to know the cause of the event
  219. // esp_http_client_handle_t client
  220. // esp_http_client_handle_t context
  221. // void *data data of the event
  222. // int data_len - data length of data
  223. // void *user_data -- user_data context, from esp_http_client_config_t user_data
  224. // char *header_key For HTTP_EVENT_ON_HEADER event_id, it’s store current http header key
  225. // char *header_value For HTTP_EVENT_ON_HEADER event_id, it’s store current http header value
  226. // --------------
  227. switch (evt->event_id) {
  228. case HTTP_EVENT_ERROR:
  229. ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
  230. _printMemStats();
  231. //strncpy(ota_status,"HTTP_EVENT_ERROR",sizeof(ota_status)-1);
  232. break;
  233. case HTTP_EVENT_ON_CONNECTED:
  234. ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
  235. if(ota_status.bOTAStarted) sendMessaging(MESSAGING_INFO,"Connecting to URL...");
  236. ota_status.total_image_len=0;
  237. ota_status.actual_image_len=0;
  238. ota_status.lastpct=0;
  239. ota_status.remain_image_len=0;
  240. ota_status.newpct=0;
  241. gettimeofday(&ota_status.OTA_start, NULL);
  242. break;
  243. case HTTP_EVENT_HEADER_SENT:
  244. ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
  245. break;
  246. case HTTP_EVENT_ON_HEADER:
  247. ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s",evt->header_key, evt->header_value);
  248. if (strcasecmp(evt->header_key, "location") == 0) {
  249. ESP_LOGW(TAG,"OTA will redirect to url: %s",evt->header_value);
  250. }
  251. if (strcasecmp(evt->header_key, "content-length") == 0) {
  252. ota_status.total_image_len = atol(evt->header_value);
  253. ESP_LOGW(TAG, "Content length found: %s, parsed to %d", evt->header_value, ota_status.total_image_len);
  254. }
  255. break;
  256. case HTTP_EVENT_ON_DATA:
  257. if(!ota_status.bOTAStarted) {
  258. ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, status_code=%d, len=%d",esp_http_client_get_status_code(evt->client), evt->data_len);
  259. }
  260. break;
  261. case HTTP_EVENT_ON_FINISH:
  262. ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
  263. break;
  264. case HTTP_EVENT_DISCONNECTED:
  265. ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
  266. break;
  267. }
  268. return ESP_OK;
  269. }
  270. esp_err_t init_config(ota_thread_parms_t * p_ota_thread_parms){
  271. memset(&http_client_config, 0x00, sizeof(http_client_config));
  272. sendMessaging(MESSAGING_INFO,"Initializing...");
  273. loc_displayer_progressbar(0);
  274. ota_status.ota_type= OTA_TYPE_INVALID;
  275. if(p_ota_thread_parms->url !=NULL && strlen(p_ota_thread_parms->url)>0 ){
  276. ota_status.ota_type= OTA_TYPE_HTTP;
  277. }
  278. else if(p_ota_thread_parms->bin!=NULL && p_ota_thread_parms->length > 0) {
  279. ota_status.ota_type= OTA_TYPE_BUFFER;
  280. }
  281. if( ota_status.ota_type== OTA_TYPE_INVALID ){
  282. ESP_LOGE(TAG,"HTTP OTA called without a url or a binary buffer");
  283. return ESP_ERR_INVALID_ARG;
  284. }
  285. ota_status.buffer_size = BUFFSIZE;
  286. ota_status.ota_write_data = heap_caps_malloc(ota_status.buffer_size+1 , (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT));
  287. if(ota_status.ota_write_data== NULL){
  288. ESP_LOGE(TAG,"Error allocating the ota buffer");
  289. return ESP_ERR_NO_MEM;
  290. }
  291. switch (ota_status.ota_type) {
  292. case OTA_TYPE_HTTP:
  293. http_client_config.cert_pem =get_certificate();
  294. http_client_config.event_handler = _http_event_handler;
  295. http_client_config.disable_auto_redirect=true;
  296. http_client_config.skip_cert_common_name_check = false;
  297. http_client_config.url = strdup(p_ota_thread_parms->url);
  298. http_client_config.max_redirection_count = 3;
  299. // buffer size below is for http read chunks
  300. http_client_config.buffer_size = 1024 ;
  301. break;
  302. case OTA_TYPE_BUFFER:
  303. ota_status.bin_data = p_ota_thread_parms->bin;
  304. ota_status.total_image_len = p_ota_thread_parms->length;
  305. break;
  306. default:
  307. return ESP_FAIL;
  308. break;
  309. }
  310. return ESP_OK;
  311. }
  312. esp_partition_t * _get_ota_partition(esp_partition_subtype_t subtype){
  313. esp_partition_t *ota_partition=NULL;
  314. ESP_LOGI(TAG, "Looking for OTA partition.");
  315. esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_APP, subtype , NULL);
  316. if(it == NULL){
  317. ESP_LOGE(TAG,"Unable initialize partition iterator!");
  318. }
  319. else {
  320. ota_partition = (esp_partition_t *) esp_partition_get(it);
  321. if(ota_partition != NULL){
  322. ESP_LOGI(TAG, "Found OTA partition: %s.",ota_partition->label);
  323. }
  324. else {
  325. ESP_LOGE(TAG,"OTA partition not found! Unable update application.");
  326. }
  327. esp_partition_iterator_release(it);
  328. }
  329. return ota_partition;
  330. }
  331. esp_err_t _erase_last_boot_app_partition(const esp_partition_t *ota_partition)
  332. {
  333. uint16_t num_passes=0;
  334. uint16_t remain_size=0;
  335. uint32_t single_pass_size=0;
  336. esp_err_t err=ESP_OK;
  337. char * ota_erase_size=config_alloc_get(NVS_TYPE_STR, "ota_erase_blk");
  338. if(ota_erase_size!=NULL) {
  339. single_pass_size = atol(ota_erase_size);
  340. ESP_LOGD(TAG,"OTA Erase block size is %d (from string: %s)",single_pass_size, ota_erase_size );
  341. free(ota_erase_size);
  342. }
  343. else {
  344. ESP_LOGW(TAG,"OTA Erase block config not found");
  345. single_pass_size = OTA_FLASH_ERASE_BLOCK;
  346. }
  347. if(single_pass_size % SPI_FLASH_SEC_SIZE !=0){
  348. uint32_t temp_single_pass_size = single_pass_size-(single_pass_size % SPI_FLASH_SEC_SIZE);
  349. ESP_LOGW(TAG,"Invalid erase block size of %u. Value should be a multiple of %d and will be adjusted to %u.", single_pass_size, SPI_FLASH_SEC_SIZE,temp_single_pass_size);
  350. single_pass_size=temp_single_pass_size;
  351. }
  352. ESP_LOGI(TAG,"Erasing flash partition of size %u in blocks of %d bytes", ota_partition->size, single_pass_size);
  353. num_passes=ota_partition->size/single_pass_size;
  354. remain_size=ota_partition->size-(num_passes*single_pass_size);
  355. ESP_LOGI(TAG,"Erasing in %d passes with blocks of %d bytes ", num_passes,single_pass_size);
  356. for(uint16_t i=0;i<num_passes;i++){
  357. ESP_LOGD(TAG,"Erasing flash (%u%%)",i/num_passes);
  358. ESP_LOGD(TAG,"Pass %d of %d, with chunks of %d bytes, from %d to %d", i+1, num_passes,single_pass_size,i*single_pass_size,i*single_pass_size+single_pass_size);
  359. err=esp_partition_erase_range(ota_partition, i*single_pass_size, single_pass_size);
  360. if(err!=ESP_OK) return err;
  361. if(i%2) {
  362. loc_displayer_progressbar((int)(((float)i/(float)num_passes)*100.0f));
  363. sendMessaging(MESSAGING_INFO,"Erasing flash (%u/%u)",i,num_passes);
  364. }
  365. vTaskDelay(100/ portTICK_PERIOD_MS); // wait here for a short amount of time. This will help with reducing WDT errors
  366. }
  367. if(remain_size>0){
  368. err=esp_partition_erase_range(ota_partition, ota_partition->size-remain_size, remain_size);
  369. if(err!=ESP_OK) return err;
  370. }
  371. sendMessaging(MESSAGING_INFO,"Erasing flash complete.");
  372. loc_displayer_progressbar(100);
  373. vTaskDelay(200/ portTICK_PERIOD_MS);
  374. return ESP_OK;
  375. }
  376. static bool process_again(int status_code)
  377. {
  378. switch (status_code) {
  379. case HttpStatus_MovedPermanently:
  380. case HttpStatus_Found:
  381. case HttpStatus_Unauthorized:
  382. return true;
  383. default:
  384. return false;
  385. }
  386. return false;
  387. }
  388. static esp_err_t _http_handle_response_code(esp_http_client_handle_t http_client, int status_code)
  389. {
  390. esp_err_t err=ESP_OK;
  391. if (status_code == HttpStatus_MovedPermanently || status_code == HttpStatus_Found ) {
  392. ESP_LOGW(TAG, "Handling HTTP redirection. ");
  393. err = esp_http_client_set_redirection(http_client);
  394. if (err != ESP_OK) {
  395. ESP_LOGE(TAG, "URL redirection Failed. %s", esp_err_to_name(err));
  396. return err;
  397. }
  398. ESP_LOGW(TAG, "Done Handling HTTP redirection. ");
  399. } else if (status_code == HttpStatus_Unauthorized) {
  400. ESP_LOGW(TAG, "Handling Unauthorized. ");
  401. esp_http_client_add_auth(http_client);
  402. }
  403. ESP_LOGD(TAG, "Redirection done, checking if we need to read the data. ");
  404. if (process_again(status_code)) {
  405. //ESP_LOGD(TAG, "We have to read some more data. Allocating buffer size %u",ota_config.buffer_size+1);
  406. //char * local_buff = heap_caps_malloc(ota_status.buffer_size+1, (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT));
  407. // if(local_buff==NULL){
  408. // ESP_LOGE(TAG,"Failed to allocate internal memory buffer for http processing");
  409. // return ESP_ERR_NO_MEM;
  410. // }
  411. while (1) {
  412. ESP_LOGD(TAG, "Reading data chunk. ");
  413. int data_read = esp_http_client_read(http_client, ota_status.ota_write_data, ota_status.buffer_size);
  414. if (data_read < 0) {
  415. ESP_LOGE(TAG, "Error: SSL data read error");
  416. err= ESP_FAIL;
  417. break;
  418. } else if (data_read == 0) {
  419. ESP_LOGD(TAG, "No more data. ");
  420. err= ESP_OK;
  421. break;
  422. }
  423. }
  424. //FREE_RESET(local_buff);
  425. }
  426. return err;
  427. }
  428. static esp_err_t _http_connect(esp_http_client_handle_t http_client)
  429. {
  430. esp_err_t err = ESP_FAIL;
  431. int status_code, header_ret;
  432. do {
  433. ESP_LOGI(TAG, "connecting the http client. ");
  434. err = esp_http_client_open(http_client, 0);
  435. if (err != ESP_OK) {
  436. ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
  437. sendMessaging(MESSAGING_ERROR,"Failed to open HTTP connection: %s", esp_err_to_name(err));
  438. return err;
  439. }
  440. ESP_LOGI(TAG, "Fetching headers");
  441. header_ret = esp_http_client_fetch_headers(http_client);
  442. if (header_ret < 0) {
  443. // Error found
  444. sendMessaging(MESSAGING_ERROR,"Header fetch failed");
  445. return header_ret;
  446. }
  447. ESP_LOGI(TAG, "HTTP Header fetch completed, found content length of %d",header_ret);
  448. status_code = esp_http_client_get_status_code(http_client);
  449. ESP_LOGD(TAG, "HTTP status code was %d",status_code);
  450. err = _http_handle_response_code(http_client, status_code);
  451. if (err != ESP_OK) {
  452. sendMessaging(MESSAGING_ERROR,"HTTP connect error: %s", esp_err_to_name(err));
  453. return err;
  454. }
  455. } while (process_again(status_code));
  456. if(status_code >=400 && status_code <=900){
  457. sendMessaging(MESSAGING_ERROR,"Error: HTTP Status %d",status_code);
  458. err=ESP_FAIL;
  459. }
  460. return err;
  461. }
  462. void ota_task_cleanup(const char * message, ...){
  463. ota_status.bOTAThreadStarted=false;
  464. loc_displayer_progressbar(0);
  465. if(message!=NULL){
  466. va_list args;
  467. va_start(args, message);
  468. sendMessaging(MESSAGING_ERROR,message, args);
  469. va_end(args);
  470. }
  471. FREE_RESET(ota_status.ota_write_data);
  472. FREE_RESET(ota_status.bin_data);
  473. if(ota_http_client!=NULL) {
  474. esp_http_client_cleanup(ota_http_client);
  475. ota_http_client=NULL;
  476. }
  477. ota_status.bOTAStarted = false;
  478. task_fatal_error();
  479. }
  480. esp_err_t ota_buffer_all(){
  481. int data_read=0;
  482. esp_err_t err=ESP_OK;
  483. if (ota_status.ota_type == OTA_TYPE_HTTP){
  484. GDS_TextLine(display, 2, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "Downloading file");
  485. ota_http_client = esp_http_client_init(&http_client_config);
  486. if (ota_http_client == NULL) {
  487. sendMessaging(MESSAGING_ERROR,"Error: Failed to initialize HTTP connection.");
  488. return ESP_FAIL;
  489. }
  490. _printMemStats();
  491. // Open the http connection and follow any redirection
  492. err = _http_connect(ota_http_client);
  493. if (err != ESP_OK) {
  494. return err;
  495. }
  496. if(ota_status.total_image_len<=0){
  497. sendMessaging(MESSAGING_ERROR,"Error: Invalid image length");
  498. return ESP_FAIL;
  499. }
  500. ota_status.bin_data= malloc(ota_status.total_image_len);
  501. if(ota_status.bin_data==NULL){
  502. sendMessaging(MESSAGING_ERROR,"Error: buffer alloc error");
  503. return ESP_FAIL;
  504. }
  505. data_read = esp_http_client_read(ota_http_client, ota_status.bin_data, ota_status.total_image_len);
  506. if(data_read != ota_status.total_image_len){
  507. sendMessaging(MESSAGING_ERROR,"Error: Binary incomplete");
  508. return ESP_FAIL;
  509. }
  510. }
  511. else {
  512. gettimeofday(&ota_status.OTA_start, NULL);
  513. }
  514. ota_status.remain_image_len=ota_status.total_image_len;
  515. return err;
  516. }
  517. int ota_buffer_read(){
  518. int data_read=0;
  519. if(ota_status.remain_image_len >ota_status.buffer_size){
  520. data_read = ota_status.buffer_size;
  521. } else {
  522. data_read = ota_status.remain_image_len;
  523. }
  524. memcpy(ota_status.ota_write_data, &ota_status.bin_data[ota_status.actual_image_len], data_read);
  525. ota_status.actual_image_len += data_read;
  526. ota_status.remain_image_len -= data_read;
  527. return data_read;
  528. }
  529. esp_err_t ota_header_check(){
  530. esp_app_desc_t new_app_info;
  531. esp_app_desc_t running_app_info;
  532. ota_status.configured = esp_ota_get_boot_partition();
  533. ota_status.running = esp_ota_get_running_partition();
  534. ota_status.last_invalid_app= esp_ota_get_last_invalid_partition();
  535. ota_status.ota_partition = _get_ota_partition(ESP_PARTITION_SUBTYPE_APP_OTA_0);
  536. ESP_LOGI(TAG, "Running partition [%s] type %d subtype %d (offset 0x%08x)", ota_status.running->label, ota_status.running->type, ota_status.running->subtype, ota_status.running->address);
  537. if (ota_status.total_image_len > ota_status.ota_partition->size){
  538. ota_task_cleanup("Error: Image size too large to fit in partition.");
  539. return ESP_FAIL;
  540. }
  541. if(ota_status.ota_partition == NULL){
  542. ESP_LOGE(TAG,"Unable to locate OTA application partition. ");
  543. ota_task_cleanup("Error: OTA partition not found");
  544. return ESP_FAIL;
  545. }
  546. if (ota_status.configured != ota_status.running) {
  547. ESP_LOGW(TAG, "Configured OTA boot partition at offset 0x%08x, but running from offset 0x%08x", ota_status.configured->address, ota_status.running->address);
  548. ESP_LOGW(TAG, "(This can happen if either the OTA boot data or preferred boot image become corrupted somehow.)");
  549. }
  550. ESP_LOGI(TAG, "Next ota update partition is: [%s] subtype %d at offset 0x%x",
  551. ota_status.update_partition->label, ota_status.update_partition->subtype, ota_status.update_partition->address);
  552. if (ota_status.total_image_len >= IMAGE_HEADER_SIZE) {
  553. // check current version with downloading
  554. memcpy(&new_app_info, &ota_status.bin_data[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t));
  555. ESP_LOGI(TAG, "New firmware version: %s", new_app_info.version);
  556. if (esp_ota_get_partition_description(ota_status.running, &running_app_info) == ESP_OK) {
  557. ESP_LOGI(TAG, "Running recovery version: %s", running_app_info.version);
  558. }
  559. esp_app_desc_t invalid_app_info;
  560. if (esp_ota_get_partition_description(ota_status.last_invalid_app, &invalid_app_info) == ESP_OK) {
  561. ESP_LOGI(TAG, "Last invalid firmware version: %s", invalid_app_info.version);
  562. }
  563. if (memcmp(new_app_info.version, running_app_info.version, sizeof(new_app_info.version)) == 0) {
  564. ESP_LOGW(TAG, "Current running version is the same as a new.");
  565. }
  566. return ESP_OK;
  567. }
  568. else{
  569. ota_task_cleanup("Error: Binary file too small");
  570. }
  571. return ESP_FAIL;
  572. }
  573. void ota_task(void *pvParameter)
  574. {
  575. esp_err_t err = ESP_OK;
  576. int data_read = 0;
  577. GDS_TextSetFont(display,2,GDS_GetHeight(display)>32?&Font_droid_sans_fallback_15x17:&Font_droid_sans_fallback_11x13,-2);
  578. GDS_ClearExt(display, true);
  579. GDS_TextLine(display, 1, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "Firmware update");
  580. GDS_TextLine(display, 2, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "Initializing");
  581. loc_displayer_progressbar(0);
  582. ESP_LOGD(TAG, "HTTP ota Thread started");
  583. _printMemStats();
  584. ota_status.update_partition = esp_ota_get_next_update_partition(NULL);
  585. ESP_LOGI(TAG,"Initializing OTA configuration");
  586. err = init_config(pvParameter);
  587. if(err!=ESP_OK){
  588. ota_task_cleanup("Error: Failed to initialize OTA.");
  589. return;
  590. }
  591. _printMemStats();
  592. ota_status.bOTAStarted = true;
  593. sendMessaging(MESSAGING_INFO,"Starting OTA...");
  594. err=ota_buffer_all();
  595. if(err!=ESP_OK){
  596. ota_task_cleanup(NULL);
  597. return;
  598. }
  599. if(ota_header_check()!=ESP_OK){
  600. ota_task_cleanup(NULL);
  601. return;
  602. }
  603. /* Locate and erase ota application partition */
  604. ESP_LOGW(TAG,"**************** Expecting WATCHDOG errors below during flash erase. This is OK and not to worry about **************** ");
  605. GDS_TextLine(display, 2, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "Formatting partition");
  606. sendMessaging(MESSAGING_INFO,"Formatting OTA partition");
  607. _printMemStats();
  608. err=_erase_last_boot_app_partition(ota_status.ota_partition);
  609. if(err!=ESP_OK){
  610. ota_task_cleanup("Error: Unable to erase last APP partition. (%s)",esp_err_to_name(err));
  611. return;
  612. }
  613. loc_displayer_progressbar(0);
  614. _printMemStats();
  615. // Call OTA Begin with a small partition size - this minimizes the time spent in erasing partition,
  616. // which was already done above
  617. esp_ota_handle_t update_handle = 0 ;
  618. err = esp_ota_begin(ota_status.ota_partition, 512, &update_handle);
  619. if (err != ESP_OK) {
  620. ota_task_cleanup("esp_ota_begin failed (%s)", esp_err_to_name(err));
  621. return;
  622. }
  623. ESP_LOGD(TAG, "esp_ota_begin succeeded");
  624. GDS_TextLine(display, 2, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "Writing image...");
  625. while (ota_status.remain_image_len>0) {
  626. data_read = ota_buffer_read();
  627. if (data_read <= 0) {
  628. ota_task_cleanup("Error: Data read error");
  629. return;
  630. } else if (data_read > 0) {
  631. err = esp_ota_write( update_handle, (const void *)ota_status.ota_write_data, data_read);
  632. if (err != ESP_OK) {
  633. ota_task_cleanup("Error: OTA Partition write failure. (%s)",esp_err_to_name(err));
  634. return;
  635. }
  636. ESP_LOGD(TAG, "Written image length %d", ota_status.actual_image_len);
  637. if(ota_get_pct_complete()%5 == 0) ota_status.newpct = ota_get_pct_complete();
  638. if(ota_status.lastpct!=ota_status.newpct ) {
  639. loc_displayer_progressbar(ota_status.newpct);
  640. gettimeofday(&tv, NULL);
  641. uint32_t elapsed_ms= (tv.tv_sec-ota_status.OTA_start.tv_sec )*1000+(tv.tv_usec-ota_status.OTA_start.tv_usec)/1000;
  642. ESP_LOGI(TAG,"OTA progress : %d/%d (%d pct), %d KB/s", ota_status.actual_image_len, ota_status.total_image_len, ota_status.newpct, elapsed_ms>0?ota_status.actual_image_len*1000/elapsed_ms/1024:0);
  643. sendMessaging(MESSAGING_INFO,"Writing binary file.");
  644. ota_status.lastpct=ota_status.newpct;
  645. }
  646. taskYIELD();
  647. } else if (data_read == 0) {
  648. ESP_LOGI(TAG, "End of OTA data stream");
  649. break;
  650. }
  651. }
  652. ESP_LOGI(TAG, "Total Write binary data length: %d", ota_status.actual_image_len);
  653. if (ota_status.total_image_len != ota_status.actual_image_len) {
  654. ota_task_cleanup("Error: Error in receiving complete file");
  655. return;
  656. }
  657. _printMemStats();
  658. loc_displayer_progressbar(100);
  659. err = esp_ota_end(update_handle);
  660. if (err != ESP_OK) {
  661. ota_task_cleanup("Error: %s",esp_err_to_name(err));
  662. return;
  663. }
  664. _printMemStats();
  665. err = esp_ota_set_boot_partition(ota_status.ota_partition);
  666. if (err == ESP_OK) {
  667. ESP_LOGI(TAG,"OTA Process completed successfully!");
  668. sendMessaging(MESSAGING_INFO,"Success!");
  669. GDS_TextLine(display, 2, GDS_TEXT_LEFT, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "Success!");
  670. vTaskDelay(1500/ portTICK_PERIOD_MS); // wait here to give the UI a chance to refresh
  671. GDS_Clear(display,GDS_COLOR_BLACK);
  672. esp_restart();
  673. } else {
  674. ota_task_cleanup("Error: Unable to update boot partition [%s]",esp_err_to_name(err));
  675. return;
  676. }
  677. ota_task_cleanup(NULL);
  678. return;
  679. }
  680. esp_err_t process_recovery_ota(const char * bin_url, char * bin_buffer, uint32_t length){
  681. int ret = 0;
  682. uint16_t stack_size, task_priority;
  683. if(ota_status.bOTAThreadStarted){
  684. ESP_LOGE(TAG,"OTA Already started. ");
  685. return ESP_FAIL;
  686. }
  687. memset(&ota_status, 0x00, sizeof(ota_status));
  688. ota_status.bOTAThreadStarted=true;
  689. if(bin_url){
  690. ota_thread_parms.url =strdup(bin_url);
  691. ESP_LOGI(TAG, "Starting ota on core %u for : %s", OTA_CORE,ota_thread_parms.url);
  692. }
  693. else {
  694. ota_thread_parms.bin = bin_buffer;
  695. ota_thread_parms.length = length;
  696. ESP_LOGI(TAG, "Starting ota on core %u for file upload", OTA_CORE);
  697. }
  698. char * num_buffer=config_alloc_get(NVS_TYPE_STR, "ota_stack");
  699. if(num_buffer!=NULL) {
  700. stack_size= atol(num_buffer);
  701. FREE_AND_NULL(num_buffer);
  702. }
  703. else {
  704. ESP_LOGW(TAG,"OTA stack size config not found");
  705. stack_size = OTA_STACK_SIZE;
  706. }
  707. num_buffer=config_alloc_get(NVS_TYPE_STR, "ota_prio");
  708. if(num_buffer!=NULL) {
  709. task_priority= atol(num_buffer);
  710. FREE_AND_NULL(num_buffer);
  711. }
  712. else {
  713. ESP_LOGW(TAG,"OTA task priority not found");
  714. task_priority= OTA_TASK_PRIOTITY;
  715. }
  716. ESP_LOGD(TAG,"OTA task stack size %d, priority %d (%d %s ESP_TASK_MAIN_PRIO)",stack_size , task_priority, abs(task_priority-ESP_TASK_MAIN_PRIO), task_priority-ESP_TASK_MAIN_PRIO>0?"above":"below");
  717. ret=xTaskCreatePinnedToCore(&ota_task, "ota_task", stack_size , (void *)&ota_thread_parms, task_priority, NULL, OTA_CORE);
  718. if (ret != pdPASS) {
  719. ESP_LOGI(TAG, "create thread %s failed", "ota_task");
  720. return ESP_FAIL;
  721. }
  722. return ESP_OK;
  723. }
  724. esp_err_t start_ota(const char * bin_url, char * bin_buffer, uint32_t length)
  725. {
  726. if(is_recovery_running){
  727. return process_recovery_ota(bin_url,bin_buffer,length);
  728. }
  729. if(!bin_url){
  730. ESP_LOGE(TAG,"missing URL parameter. Unable to start OTA");
  731. return ESP_ERR_INVALID_ARG;
  732. }
  733. ESP_LOGW(TAG, "Called to update the firmware from url: %s",bin_url);
  734. if(config_set_value(NVS_TYPE_STR, "fwurl", bin_url) != ESP_OK){
  735. ESP_LOGE(TAG,"Failed to save the OTA url into nvs cache");
  736. return ESP_FAIL;
  737. }
  738. if(!wait_for_commit()){
  739. ESP_LOGW(TAG,"Unable to commit configuration. ");
  740. }
  741. ESP_LOGW(TAG, "Rebooting to recovery to complete the installation");
  742. return guided_factory();
  743. return ESP_OK;
  744. }