squeezelite-ota.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  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. #ifndef LOG_LOCAL_LEVEL
  8. #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
  9. #endif
  10. #include "freertos/FreeRTOS.h"
  11. #include "freertos/task.h"
  12. #include "esp_system.h"
  13. #include "esp_event.h"
  14. #include "esp_log.h"
  15. #include "esp_ota_ops.h"
  16. #include "esp_https_ota.h"
  17. #include "string.h"
  18. #include <stdbool.h>
  19. #include "nvs.h"
  20. #include "nvs_flash.h"
  21. #include "cmd_system.h"
  22. #include "esp_err.h"
  23. #include "tcpip_adapter.h"
  24. #include "squeezelite-ota.h"
  25. #include "nvs_utilities.h"
  26. #include <time.h>
  27. #include <sys/time.h>
  28. #include <stdarg.h>
  29. #include "esp_image_format.h"
  30. #include "esp_secure_boot.h"
  31. #include "esp_flash_encrypt.h"
  32. #include "esp_spi_flash.h"
  33. #include "sdkconfig.h"
  34. #include "esp_ota_ops.h"
  35. static const char *TAG = "squeezelite-ota";
  36. extern const uint8_t server_cert_pem_start[] asm("_binary_github_pem_start");
  37. extern const uint8_t server_cert_pem_end[] asm("_binary_github_pem_end");
  38. char * cert=NULL;
  39. char * ota_write_data = NULL;
  40. #define IMAGE_HEADER_SIZE sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t) + 1
  41. #define BUFFSIZE 4096
  42. #define HASH_LEN 32 /* SHA-256 digest length */
  43. static struct {
  44. char status_text[81];
  45. uint32_t ota_actual_len;
  46. uint32_t ota_total_len;
  47. char * redirected_url;
  48. char * current_url;
  49. bool bRedirectFound;
  50. bool bOTAStarted;
  51. bool bInitialized;
  52. uint8_t lastpct;
  53. uint8_t newpct;
  54. struct timeval OTA_start;
  55. bool bOTAThreadStarted;
  56. } ota_status;
  57. struct timeval tv;
  58. static esp_http_client_config_t ota_config;
  59. extern void wifi_manager_refresh_ota_json();
  60. void RECOVERY_IRAM_FUNCTION _printMemStats(){
  61. ESP_LOGD(TAG,"Heap internal:%zu (min:%zu) external:%zu (min:%zu)",
  62. heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
  63. heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
  64. heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
  65. heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM));
  66. }
  67. void RECOVERY_IRAM_FUNCTION triggerStatusJsonRefresh(bool bDelay,const char * status, ...){
  68. va_list args;
  69. va_start(args, status);
  70. vsnprintf(ota_status.status_text,sizeof(ota_status.status_text)-1,status, args);
  71. va_end(args);
  72. _printMemStats();
  73. wifi_manager_refresh_ota_json();
  74. if(bDelay){
  75. ESP_LOGD(TAG,"Holding task...");
  76. vTaskDelay(200 / portTICK_PERIOD_MS); // wait here for a short amount of time. This will help with refreshing the UI status
  77. ESP_LOGD(TAG,"Done holding task...");
  78. }
  79. else
  80. {
  81. ESP_LOGI(TAG,"%s",ota_status.status_text);
  82. taskYIELD();
  83. }
  84. }
  85. const char * RECOVERY_IRAM_FUNCTION ota_get_status(){
  86. if(!ota_status.bInitialized)
  87. {
  88. memset(ota_status.status_text, 0x00,sizeof(ota_status.status_text));
  89. ota_status.bInitialized = true;
  90. }
  91. return ota_status.status_text;
  92. }
  93. uint8_t RECOVERY_IRAM_FUNCTION ota_get_pct_complete(){
  94. return ota_status.ota_total_len==0?0:
  95. (uint8_t)((float)ota_status.ota_actual_len/(float)ota_status.ota_total_len*100.0f);
  96. }
  97. static void __attribute__((noreturn)) RECOVERY_IRAM_FUNCTION task_fatal_error(void)
  98. {
  99. ESP_LOGE(TAG, "Exiting task due to fatal error...");
  100. (void)vTaskDelete(NULL);
  101. while (1) {
  102. ;
  103. }
  104. }
  105. #define FREE_RESET(p) if(p!=NULL) { free(p); p=NULL; }
  106. esp_err_t RECOVERY_IRAM_FUNCTION _http_event_handler(esp_http_client_event_t *evt)
  107. {
  108. // --------------
  109. // Received parameters
  110. //
  111. // esp_http_client_event_id_tevent_id event_id, to know the cause of the event
  112. // esp_http_client_handle_t client
  113. // esp_http_client_handle_t context
  114. // void *data data of the event
  115. // int data_len - data length of data
  116. // void *user_data -- user_data context, from esp_http_client_config_t user_data
  117. // char *header_key For HTTP_EVENT_ON_HEADER event_id, it’s store current http header key
  118. // char *header_value For HTTP_EVENT_ON_HEADER event_id, it’s store current http header value
  119. // --------------
  120. switch (evt->event_id) {
  121. case HTTP_EVENT_ERROR:
  122. ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
  123. _printMemStats();
  124. //strncpy(ota_status,"HTTP_EVENT_ERROR",sizeof(ota_status)-1);
  125. break;
  126. case HTTP_EVENT_ON_CONNECTED:
  127. ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
  128. if(ota_status.bOTAStarted) triggerStatusJsonRefresh(true,"Installing...");
  129. ota_status.ota_total_len=0;
  130. ota_status.ota_actual_len=0;
  131. ota_status.lastpct=0;
  132. ota_status.newpct=0;
  133. gettimeofday(&ota_status.OTA_start, NULL);
  134. break;
  135. case HTTP_EVENT_HEADER_SENT:
  136. ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
  137. break;
  138. case HTTP_EVENT_ON_HEADER:
  139. ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s",evt->header_key, evt->header_value);
  140. if (strcasecmp(evt->header_key, "location") == 0) {
  141. FREE_RESET(ota_status.redirected_url);
  142. ota_status.redirected_url=strdup(evt->header_value);
  143. ESP_LOGW(TAG,"OTA will redirect to url: %s",ota_status.redirected_url);
  144. ota_status.bRedirectFound= true;
  145. }
  146. if (strcasecmp(evt->header_key, "content-length") == 0) {
  147. ota_status.ota_total_len = atol(evt->header_value);
  148. ESP_LOGW(TAG, "Content length found: %s, parsed to %d", evt->header_value, ota_status.ota_total_len);
  149. }
  150. break;
  151. case HTTP_EVENT_ON_DATA:
  152. if(!ota_status.bOTAStarted) {
  153. ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, status_code=%d, len=%d",esp_http_client_get_status_code(evt->client), evt->data_len);
  154. }
  155. else if(ota_status.bOTAStarted && esp_http_client_get_status_code(evt->client) == 200 ){
  156. ota_status.ota_actual_len+=evt->data_len;
  157. if(ota_get_pct_complete()%5 == 0) ota_status.newpct = ota_get_pct_complete();
  158. if(ota_status.lastpct!=ota_status.newpct )
  159. {
  160. gettimeofday(&tv, NULL);
  161. uint32_t elapsed_ms= (tv.tv_sec-ota_status.OTA_start.tv_sec )*1000+(tv.tv_usec-ota_status.OTA_start.tv_usec)/1000;
  162. 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);
  163. wifi_manager_refresh_ota_json();
  164. ota_status.lastpct=ota_status.newpct;
  165. }
  166. }
  167. break;
  168. case HTTP_EVENT_ON_FINISH:
  169. ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
  170. break;
  171. case HTTP_EVENT_DISCONNECTED:
  172. ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
  173. break;
  174. }
  175. return ESP_OK;
  176. }
  177. esp_err_t RECOVERY_IRAM_FUNCTION init_config(const char * url){
  178. memset(&ota_config, 0x00, sizeof(ota_config));
  179. ota_config.cert_pem =cert==NULL?(char *)server_cert_pem_start:cert;
  180. ota_config.event_handler = _http_event_handler;
  181. ota_config.buffer_size = BUFFSIZE;
  182. ota_config.disable_auto_redirect=true;
  183. //conf->disable_auto_redirect=false;
  184. ota_config.skip_cert_common_name_check = false;
  185. ota_config.url = strdup(url);
  186. ota_config.max_redirection_count = 0;
  187. //ota_write_data = heap_caps_malloc(ota_config.buffer_size+1 , MALLOC_CAP_INTERNAL);
  188. ota_write_data = malloc(ota_config.buffer_size+1);
  189. if(ota_write_data== NULL){
  190. ESP_LOGE(TAG,"Error allocating the ota buffer");
  191. return ESP_ERR_NO_MEM;
  192. }
  193. return ESP_OK;
  194. }
  195. esp_partition_t * RECOVERY_IRAM_FUNCTION _get_ota_partition(esp_partition_subtype_t subtype){
  196. esp_partition_t *ota_partition=NULL;
  197. ESP_LOGI(TAG, "Looking for OTA partition.");
  198. esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_APP, subtype , NULL);
  199. if(it == NULL){
  200. ESP_LOGE(TAG,"Unable initialize partition iterator!");
  201. }
  202. else {
  203. ota_partition = (esp_partition_t *) esp_partition_get(it);
  204. if(ota_partition != NULL){
  205. ESP_LOGI(TAG, "Found OTA partition: %s.",ota_partition->label);
  206. }
  207. else {
  208. ESP_LOGE(TAG,"OTA partition not found! Unable update application.");
  209. }
  210. esp_partition_iterator_release(it);
  211. }
  212. return ota_partition;
  213. }
  214. esp_err_t RECOVERY_IRAM_FUNCTION _erase_last_boot_app_partition(esp_partition_t *ota_partition)
  215. {
  216. uint16_t num_passes=0;
  217. uint16_t remain_size=0;
  218. uint32_t single_pass_size=0;
  219. esp_err_t err=ESP_OK;
  220. char * ota_erase_size=config_alloc_get(NVS_TYPE_STR, "ota_erase_blk");
  221. if(ota_erase_size!=NULL) {
  222. single_pass_size = atol(ota_erase_size);
  223. ESP_LOGD(TAG,"OTA Erase block size is %d (from string: %s)",single_pass_size, ota_erase_size );
  224. free(ota_erase_size);
  225. }
  226. else {
  227. ESP_LOGW(TAG,"OTA Erase block config not found");
  228. single_pass_size = OTA_FLASH_ERASE_BLOCK;
  229. }
  230. if(single_pass_size % SPI_FLASH_SEC_SIZE !=0){
  231. uint32_t temp_single_pass_size = single_pass_size-(single_pass_size % SPI_FLASH_SEC_SIZE);
  232. 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);
  233. single_pass_size=temp_single_pass_size;
  234. }
  235. ESP_LOGI(TAG,"Erasing flash partition of size %u in blocks of %d bytes", ota_partition->size, single_pass_size);
  236. num_passes=ota_partition->size/single_pass_size;
  237. remain_size=ota_partition->size-(num_passes*single_pass_size);
  238. ESP_LOGI(TAG,"Erasing in %d passes with blocks of %d bytes ", num_passes,single_pass_size);
  239. for(uint16_t i=0;i<num_passes;i++){
  240. ESP_LOGD(TAG,"Erasing flash (%u%%)",i/num_passes);
  241. 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);
  242. err=esp_partition_erase_range(ota_partition, i*single_pass_size, single_pass_size);
  243. if(err!=ESP_OK) return err;
  244. // triggerStatusJsonRefresh(i%10==0?true:false,"Erasing flash (%u/%u)",i,num_passes);
  245. if(i%10) {
  246. triggerStatusJsonRefresh(false,"Erasing flash (%u/%u)",i,num_passes);
  247. }
  248. taskYIELD();
  249. }
  250. if(remain_size>0){
  251. err=esp_partition_erase_range(ota_partition, ota_partition->size-remain_size, remain_size);
  252. if(err!=ESP_OK) return err;
  253. }
  254. triggerStatusJsonRefresh(false,"Erasing flash (100%%)");
  255. taskYIELD();
  256. return ESP_OK;
  257. }
  258. static void RECOVERY_IRAM_FUNCTION http_cleanup(esp_http_client_handle_t client)
  259. {
  260. esp_http_client_close(client);
  261. esp_http_client_cleanup(client);
  262. }
  263. static bool RECOVERY_IRAM_FUNCTION process_again(int status_code)
  264. {
  265. switch (status_code) {
  266. case HttpStatus_MovedPermanently:
  267. case HttpStatus_Found:
  268. case HttpStatus_Unauthorized:
  269. return true;
  270. default:
  271. return false;
  272. }
  273. return false;
  274. }
  275. static esp_err_t RECOVERY_IRAM_FUNCTION _http_handle_response_code(esp_http_client_handle_t http_client, int status_code)
  276. {
  277. esp_err_t err;
  278. if (status_code == HttpStatus_MovedPermanently || status_code == HttpStatus_Found) {
  279. ESP_LOGW(TAG, "Handling HTTP redirection. ");
  280. err = esp_http_client_set_redirection(http_client);
  281. if (err != ESP_OK) {
  282. ESP_LOGE(TAG, "URL redirection Failed. %s", esp_err_to_name(err));
  283. return err;
  284. }
  285. } else if (status_code == HttpStatus_Unauthorized) {
  286. ESP_LOGW(TAG, "Handling Unauthorized. ");
  287. esp_http_client_add_auth(http_client);
  288. }
  289. ESP_LOGD(TAG, "Redirection done, checking if we need to read the data. ");
  290. if (process_again(status_code)) {
  291. //char * local_buff = heap_caps_malloc(ota_config.buffer_size, MALLOC_CAP_INTERNAL);
  292. char * local_buff = malloc(ota_config.buffer_size+1);
  293. if(local_buff==NULL){
  294. ESP_LOGE(TAG,"Failed to allocate internal memory buffer for http processing");
  295. return ESP_ERR_NO_MEM;
  296. }
  297. while (1) {
  298. ESP_LOGD(TAG, "Reading data chunk. ");
  299. int data_read = esp_http_client_read(http_client, local_buff, ota_config.buffer_size);
  300. if (data_read < 0) {
  301. ESP_LOGE(TAG, "Error: SSL data read error");
  302. err= ESP_FAIL;
  303. break;
  304. } else if (data_read == 0) {
  305. ESP_LOGD(TAG, "No more data. ");
  306. err= ESP_OK;
  307. break;
  308. }
  309. }
  310. FREE_RESET(local_buff);
  311. }
  312. return err;
  313. }
  314. static esp_err_t RECOVERY_IRAM_FUNCTION _http_connect(esp_http_client_handle_t http_client)
  315. {
  316. esp_err_t err = ESP_FAIL;
  317. int status_code, header_ret;
  318. do {
  319. ESP_LOGD(TAG, "connecting the http client. ");
  320. err = esp_http_client_open(http_client, 0);
  321. if (err != ESP_OK) {
  322. ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
  323. return err;
  324. }
  325. ESP_LOGD(TAG, "Fetching headers");
  326. header_ret = esp_http_client_fetch_headers(http_client);
  327. if (header_ret < 0) {
  328. // Error found
  329. return header_ret;
  330. }
  331. ESP_LOGD(TAG, "HTTP Header fetch completed, found content length of %d",header_ret);
  332. status_code = esp_http_client_get_status_code(http_client);
  333. ESP_LOGD(TAG, "HTTP status code was %d",status_code);
  334. err = _http_handle_response_code(http_client, status_code);
  335. if (err != ESP_OK) {
  336. return err;
  337. }
  338. } while (process_again(status_code));
  339. return err;
  340. }
  341. void RECOVERY_IRAM_FUNCTION ota_task(void *pvParameter)
  342. {
  343. char * passedURL=(char *)pvParameter;
  344. esp_err_t err = ESP_OK;
  345. int status_code, header_ret;
  346. size_t buffer_size = BUFFSIZE;
  347. const esp_partition_t *configured = esp_ota_get_boot_partition();
  348. const esp_partition_t *running = esp_ota_get_running_partition();
  349. if (configured != running) {
  350. ESP_LOGW(TAG, "Configured OTA boot partition at offset 0x%08x, but running from offset 0x%08x",
  351. configured->address, running->address);
  352. ESP_LOGW(TAG, "(This can happen if either the OTA boot data or preferred boot image become corrupted somehow.)");
  353. }
  354. ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08x)",
  355. running->type, running->subtype, running->address);
  356. _printMemStats();
  357. ota_status.bInitialized = true;
  358. ESP_LOGD(TAG, "HTTP ota Thread started");
  359. triggerStatusJsonRefresh(true,"Initializing...");
  360. ota_status.bRedirectFound=false;
  361. if(passedURL==NULL || strlen(passedURL)==0){
  362. ESP_LOGE(TAG,"HTTP OTA called without a url");
  363. triggerStatusJsonRefresh(true,"Error: Updating needs a URL!");
  364. ota_status.bOTAThreadStarted=false;
  365. FREE_RESET(ota_write_data);
  366. vTaskDelete(NULL);
  367. return ;
  368. }
  369. ota_status.current_url= strdup(passedURL);
  370. FREE_RESET(pvParameter);
  371. /* Locate and erase ota application partition */
  372. ESP_LOGW(TAG,"**************** Expecting WATCHDOG errors below during flash erase. This is OK and not to worry about **************** ");
  373. triggerStatusJsonRefresh(true,"Erasing OTA partition");
  374. esp_partition_t *ota_partition = _get_ota_partition(ESP_PARTITION_SUBTYPE_APP_OTA_0);
  375. if(ota_partition == NULL){
  376. ESP_LOGE(TAG,"Unable to locate OTA application partition. ");
  377. FREE_RESET(ota_status.current_url);
  378. FREE_RESET(ota_write_data);
  379. triggerStatusJsonRefresh(true,"Error: OTA application partition not found. (%s)",esp_err_to_name(err));
  380. ota_status.bOTAThreadStarted=false;
  381. vTaskDelete(NULL);
  382. }
  383. _printMemStats();
  384. err=_erase_last_boot_app_partition(ota_partition);
  385. if(err!=ESP_OK){
  386. ESP_LOGE(TAG,"Unable to erase last APP partition. (%s)",esp_err_to_name(err));
  387. FREE_RESET(ota_status.current_url);
  388. FREE_RESET(ota_status.redirected_url);
  389. triggerStatusJsonRefresh(true,"Error: Unable to erase last APP partition. (%s)",esp_err_to_name(err));
  390. ota_status.bOTAThreadStarted=false;
  391. FREE_RESET(ota_write_data);
  392. vTaskDelete(NULL);
  393. }
  394. _printMemStats();
  395. ESP_LOGI(TAG,"Initializing http client configuration");
  396. if(init_config(ota_status.bRedirectFound?ota_status.redirected_url:ota_status.current_url)!=ESP_OK){
  397. ESP_LOGE(TAG, "Failed to initialise HTTP configuration");
  398. triggerStatusJsonRefresh(true,"Error: Failed to initialize OTA.");
  399. ota_status.bOTAThreadStarted=false;
  400. FREE_RESET(ota_write_data);
  401. task_fatal_error();
  402. }
  403. _printMemStats();
  404. ota_status.bOTAStarted = true;
  405. triggerStatusJsonRefresh(true,"Starting OTA...");
  406. esp_http_client_handle_t client = esp_http_client_init(&ota_config);
  407. if (client == NULL) {
  408. ESP_LOGE(TAG, "Failed to initialise HTTP connection");
  409. triggerStatusJsonRefresh(true,"Error: Failed to initialize HTTP connection.");
  410. ota_status.bOTAThreadStarted=false;
  411. FREE_RESET(ota_write_data);
  412. task_fatal_error();
  413. }
  414. _printMemStats();
  415. // Open the http connection and follow any redirection
  416. err = _http_connect(client);
  417. if (err != ESP_OK) {
  418. ESP_LOGE(TAG, "Failed start http read: %s", esp_err_to_name(err));
  419. triggerStatusJsonRefresh(true,"Error: HTTP Start read failed. (%s)",esp_err_to_name(err));
  420. ota_status.bOTAThreadStarted=false;
  421. esp_http_client_cleanup(client);
  422. FREE_RESET(ota_write_data);
  423. task_fatal_error();
  424. }
  425. _printMemStats();
  426. // update_partition = esp_ota_get_next_update_partition(NULL);
  427. // ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x",
  428. // update_partition->subtype, update_partition->address);
  429. // assert(update_partition != NULL);
  430. esp_ota_handle_t update_handle = 0 ;
  431. int binary_file_length = 0;
  432. /*deal with all receive packet*/
  433. bool image_header_was_checked = false;
  434. while (1) {
  435. int data_read = esp_http_client_read(client, ota_write_data, buffer_size);
  436. if (data_read < 0) {
  437. ESP_LOGE(TAG, "Error: SSL data read error");
  438. triggerStatusJsonRefresh(true,"Error: Data read error");
  439. ota_status.bOTAThreadStarted=false;
  440. http_cleanup(client);
  441. FREE_RESET(ota_write_data);
  442. task_fatal_error();
  443. } else if (data_read > 0) {
  444. _printMemStats();
  445. if (image_header_was_checked == false) {
  446. esp_app_desc_t new_app_info;
  447. if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) {
  448. // check current version with downloading
  449. memcpy(&new_app_info, &ota_write_data[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t));
  450. ESP_LOGI(TAG, "New firmware version: %s", new_app_info.version);
  451. esp_app_desc_t running_app_info;
  452. if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) {
  453. ESP_LOGI(TAG, "Running recovery version: %s", running_app_info.version);
  454. }
  455. // const esp_partition_t* last_invalid_app = esp_ota_get_last_invalid_partition();
  456. // esp_app_desc_t invalid_app_info;
  457. // if (esp_ota_get_partition_description(last_invalid_app, &invalid_app_info) == ESP_OK) {
  458. // ESP_LOGI(TAG, "Last invalid firmware version: %s", invalid_app_info.version);
  459. // }
  460. // check current version with last invalid partition
  461. // if (last_invalid_app != NULL) {
  462. // if (memcmp(invalid_app_info.version, new_app_info.version, sizeof(new_app_info.version)) == 0) {
  463. // ESP_LOGW(TAG, "New version is the same as invalid version.");
  464. // ESP_LOGW(TAG, "Previously, there was an attempt to launch the firmware with %s version, but it failed.", invalid_app_info.version);
  465. // ESP_LOGW(TAG, "The firmware has been rolled back to the previous version.");
  466. // http_cleanup(client);
  467. // infinite_loop();
  468. // }
  469. // }
  470. if (memcmp(new_app_info.version, running_app_info.version, sizeof(new_app_info.version)) == 0) {
  471. ESP_LOGW(TAG, "Current running version is the same as a new.");
  472. // http_cleanup(client);
  473. // infinite_loop();
  474. }
  475. image_header_was_checked = true;
  476. // Call OTA Begin with a small partition size - this drives the erase operation which was already done;
  477. err = esp_ota_begin(ota_partition, 512, &update_handle);
  478. if (err != ESP_OK) {
  479. ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err));
  480. http_cleanup(client);
  481. FREE_RESET(ota_write_data);
  482. task_fatal_error();
  483. }
  484. else {
  485. ESP_LOGI(TAG, "esp_ota_begin succeeded");
  486. }
  487. } else {
  488. ESP_LOGE(TAG, "received package is not fit len");
  489. triggerStatusJsonRefresh(true,"Error: Binary file too large for the current partition");
  490. ota_status.bOTAThreadStarted=false;
  491. http_cleanup(client);
  492. FREE_RESET(ota_write_data);
  493. task_fatal_error();
  494. }
  495. }
  496. _printMemStats();
  497. err = esp_ota_write( update_handle, (const void *)ota_write_data, data_read);
  498. if (err != ESP_OK) {
  499. triggerStatusJsonRefresh(true,"Error: OTA Partition write failure. (%s)",esp_err_to_name(err));
  500. ota_status.bOTAThreadStarted=false;
  501. http_cleanup(client);
  502. FREE_RESET(ota_write_data);
  503. task_fatal_error();
  504. }
  505. binary_file_length += data_read;
  506. ESP_LOGD(TAG, "Written image length %d", binary_file_length);
  507. } else if (data_read == 0) {
  508. ESP_LOGI(TAG, "Connection closed");
  509. break;
  510. }
  511. }
  512. ESP_LOGI(TAG, "Total Write binary data length: %d", binary_file_length);
  513. if (ota_status.ota_total_len != binary_file_length) {
  514. ESP_LOGE(TAG, "Error in receiving complete file");
  515. triggerStatusJsonRefresh(true,"Error: Error in receiving complete file");
  516. ota_status.bOTAThreadStarted=false;
  517. http_cleanup(client);
  518. FREE_RESET(ota_write_data);
  519. task_fatal_error();
  520. }
  521. _printMemStats();
  522. err = esp_ota_end(update_handle);
  523. if (err != ESP_OK) {
  524. ESP_LOGE(TAG, "esp_ota_end failed (%s)!", esp_err_to_name(err));
  525. triggerStatusJsonRefresh(true,"Error: %s",esp_err_to_name(err));
  526. ota_status.bOTAThreadStarted=false;
  527. http_cleanup(client);
  528. FREE_RESET(ota_write_data);
  529. task_fatal_error();
  530. }
  531. _printMemStats();
  532. err = esp_ota_set_boot_partition(ota_partition);
  533. if (err == ESP_OK) {
  534. triggerStatusJsonRefresh(true,"Success!");
  535. esp_restart();
  536. } else {
  537. triggerStatusJsonRefresh(true,"Error: %s",esp_err_to_name(err));
  538. wifi_manager_refresh_ota_json();
  539. ESP_LOGE(TAG, "Unable to set boot partition. %s", esp_err_to_name(err));
  540. ota_status.bOTAThreadStarted=false;
  541. }
  542. FREE_RESET(ota_status.current_url);
  543. FREE_RESET(ota_status.redirected_url);
  544. FREE_RESET(ota_write_data);
  545. vTaskDelete(NULL);
  546. }
  547. esp_err_t process_recovery_ota(const char * bin_url){
  548. int ret = 0;
  549. uint16_t stack_size, task_priority;
  550. if(ota_status.bOTAThreadStarted){
  551. ESP_LOGE(TAG,"OTA Already started. ");
  552. return ESP_FAIL;
  553. }
  554. memset(&ota_status, 0x00, sizeof(ota_status));
  555. ota_status.bOTAThreadStarted=true;
  556. char * urlPtr=strdup(bin_url);
  557. // the first thing we need to do here is to erase the firmware url
  558. // to avoid a boot loop
  559. #ifdef CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1
  560. #define OTA_CORE 0
  561. #warning "OTA will run on core 0"
  562. #else
  563. #pragma message "OTA will run on core 1"
  564. #define OTA_CORE 1
  565. #endif
  566. ESP_LOGI(TAG, "Starting ota on core %u for : %s", OTA_CORE,urlPtr);
  567. char * num_buffer=config_alloc_get(NVS_TYPE_STR, "ota_stack");
  568. if(num_buffer!=NULL) {
  569. stack_size= atol(num_buffer);
  570. free(num_buffer);
  571. num_buffer=NULL;
  572. }
  573. else {
  574. ESP_LOGW(TAG,"OTA stack size config not found");
  575. stack_size = OTA_STACK_SIZE;
  576. }
  577. num_buffer=config_alloc_get(NVS_TYPE_STR, "ota_prio");
  578. if(num_buffer!=NULL) {
  579. task_priority= atol(num_buffer);
  580. free(num_buffer);
  581. num_buffer=NULL;
  582. }
  583. else {
  584. ESP_LOGW(TAG,"OTA task priority not found");
  585. task_priority= OTA_TASK_PRIOTITY;
  586. }
  587. 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");
  588. ret=xTaskCreatePinnedToCore(&ota_task, "ota_task", stack_size , (void *)urlPtr, task_priority, NULL, OTA_CORE);
  589. //ret=xTaskCreate(&ota_task, "ota_task", 1024*20, (void *)urlPtr, ESP_TASK_MAIN_PRIO+2, NULL);
  590. if (ret != pdPASS) {
  591. ESP_LOGI(TAG, "create thread %s failed", "ota_task");
  592. return ESP_FAIL;
  593. }
  594. return ESP_OK;
  595. }
  596. esp_err_t start_ota(const char * bin_url, bool bFromAppMain)
  597. {
  598. // uint8_t * config_alloc_get_default(NVS_TYPE_BLOB, "certs", server_cert_pem_start , server_cert_pem_end-server_cert_pem_start);
  599. #if RECOVERY_APPLICATION
  600. return process_recovery_ota(bin_url);
  601. #else
  602. ESP_LOGW(TAG, "Called to update the firmware from url: %s",bin_url);
  603. if(config_set_value(NVS_TYPE_STR, "fwurl", bin_url) != ESP_OK){
  604. ESP_LOGE(TAG,"Failed to save the OTA url into nvs cache");
  605. return ESP_FAIL;
  606. }
  607. if(!wait_for_commit()){
  608. ESP_LOGW(TAG,"Unable to commit configuration. ");
  609. }
  610. ESP_LOGW(TAG, "Rebooting to recovery to complete the installation");
  611. return guided_factory();
  612. return ESP_OK;
  613. #endif
  614. }