native_ota_example.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  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 <string.h>
  8. #include "freertos/FreeRTOS.h"
  9. #include "freertos/task.h"
  10. #include "esp_system.h"
  11. #include "esp_event.h"
  12. #include "esp_log.h"
  13. #include "esp_ota_ops.h"
  14. #include "esp_http_client.h"
  15. #include "esp_flash_partitions.h"
  16. #include "esp_partition.h"
  17. #include "nvs.h"
  18. #include "nvs_flash.h"
  19. #include "driver/gpio.h"
  20. #include "protocol_examples_common.h"
  21. #if CONFIG_EXAMPLE_CONNECT_WIFI
  22. #include "esp_wifi.h"
  23. #endif
  24. #define BUFFSIZE 1024
  25. #define HASH_LEN 32 /* SHA-256 digest length */
  26. static const char *TAG = "native_ota_example";
  27. /*an ota data write buffer ready to write to the flash*/
  28. static char ota_write_data[BUFFSIZE + 1] = { 0 };
  29. extern const uint8_t server_cert_pem_start[] asm("_binary_ca_cert_pem_start");
  30. extern const uint8_t server_cert_pem_end[] asm("_binary_ca_cert_pem_end");
  31. static void http_cleanup(esp_http_client_handle_t client)
  32. {
  33. esp_http_client_close(client);
  34. esp_http_client_cleanup(client);
  35. }
  36. static void __attribute__((noreturn)) task_fatal_error(void)
  37. {
  38. ESP_LOGE(TAG, "Exiting task due to fatal error...");
  39. (void)vTaskDelete(NULL);
  40. while (1) {
  41. ;
  42. }
  43. }
  44. static void print_sha256 (const uint8_t *image_hash, const char *label)
  45. {
  46. char hash_print[HASH_LEN * 2 + 1];
  47. hash_print[HASH_LEN * 2] = 0;
  48. for (int i = 0; i < HASH_LEN; ++i) {
  49. sprintf(&hash_print[i * 2], "%02x", image_hash[i]);
  50. }
  51. ESP_LOGI(TAG, "%s: %s", label, hash_print);
  52. }
  53. static void infinite_loop(void)
  54. {
  55. int i = 0;
  56. ESP_LOGI(TAG, "When a new firmware is available on the server, press the reset button to download it");
  57. while(1) {
  58. ESP_LOGI(TAG, "Waiting for a new firmware ... %d", ++i);
  59. vTaskDelay(2000 / portTICK_PERIOD_MS);
  60. }
  61. }
  62. static void ota_example_task(void *pvParameter)
  63. {
  64. esp_err_t err;
  65. /* update handle : set by esp_ota_begin(), must be freed via esp_ota_end() */
  66. esp_ota_handle_t update_handle = 0 ;
  67. const esp_partition_t *update_partition = NULL;
  68. ESP_LOGI(TAG, "Starting OTA example");
  69. const esp_partition_t *configured = esp_ota_get_boot_partition();
  70. const esp_partition_t *running = esp_ota_get_running_partition();
  71. if (configured != running) {
  72. ESP_LOGW(TAG, "Configured OTA boot partition at offset 0x%08x, but running from offset 0x%08x",
  73. configured->address, running->address);
  74. ESP_LOGW(TAG, "(This can happen if either the OTA boot data or preferred boot image become corrupted somehow.)");
  75. }
  76. ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08x)",
  77. running->type, running->subtype, running->address);
  78. esp_http_client_config_t config = {
  79. .url = CONFIG_EXAMPLE_FIRMWARE_UPG_URL,
  80. .cert_pem = (char *)server_cert_pem_start,
  81. };
  82. esp_http_client_handle_t client = esp_http_client_init(&config);
  83. if (client == NULL) {
  84. ESP_LOGE(TAG, "Failed to initialise HTTP connection");
  85. task_fatal_error();
  86. }
  87. err = esp_http_client_open(client, 0);
  88. if (err != ESP_OK) {
  89. ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
  90. esp_http_client_cleanup(client);
  91. task_fatal_error();
  92. }
  93. esp_http_client_fetch_headers(client);
  94. update_partition = esp_ota_get_next_update_partition(NULL);
  95. ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x",
  96. update_partition->subtype, update_partition->address);
  97. assert(update_partition != NULL);
  98. int binary_file_length = 0;
  99. /*deal with all receive packet*/
  100. bool image_header_was_checked = false;
  101. while (1) {
  102. int data_read = esp_http_client_read(client, ota_write_data, BUFFSIZE);
  103. if (data_read < 0) {
  104. ESP_LOGE(TAG, "Error: SSL data read error");
  105. http_cleanup(client);
  106. task_fatal_error();
  107. } else if (data_read > 0) {
  108. if (image_header_was_checked == false) {
  109. esp_app_desc_t new_app_info;
  110. if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) {
  111. // check current version with downloading
  112. memcpy(&new_app_info, &ota_write_data[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t));
  113. ESP_LOGI(TAG, "New firmware version: %s", new_app_info.version);
  114. esp_app_desc_t running_app_info;
  115. if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) {
  116. ESP_LOGI(TAG, "Running firmware version: %s", running_app_info.version);
  117. }
  118. const esp_partition_t* last_invalid_app = esp_ota_get_last_invalid_partition();
  119. esp_app_desc_t invalid_app_info;
  120. if (esp_ota_get_partition_description(last_invalid_app, &invalid_app_info) == ESP_OK) {
  121. ESP_LOGI(TAG, "Last invalid firmware version: %s", invalid_app_info.version);
  122. }
  123. // check current version with last invalid partition
  124. if (last_invalid_app != NULL) {
  125. if (memcmp(invalid_app_info.version, new_app_info.version, sizeof(new_app_info.version)) == 0) {
  126. ESP_LOGW(TAG, "New version is the same as invalid version.");
  127. ESP_LOGW(TAG, "Previously, there was an attempt to launch the firmware with %s version, but it failed.", invalid_app_info.version);
  128. ESP_LOGW(TAG, "The firmware has been rolled back to the previous version.");
  129. http_cleanup(client);
  130. infinite_loop();
  131. }
  132. }
  133. if (memcmp(new_app_info.version, running_app_info.version, sizeof(new_app_info.version)) == 0) {
  134. ESP_LOGW(TAG, "Current running version is the same as a new. We will not continue the update.");
  135. http_cleanup(client);
  136. infinite_loop();
  137. }
  138. image_header_was_checked = true;
  139. err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
  140. if (err != ESP_OK) {
  141. ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err));
  142. http_cleanup(client);
  143. task_fatal_error();
  144. }
  145. ESP_LOGI(TAG, "esp_ota_begin succeeded");
  146. } else {
  147. ESP_LOGE(TAG, "received package is not fit len");
  148. http_cleanup(client);
  149. task_fatal_error();
  150. }
  151. }
  152. err = esp_ota_write( update_handle, (const void *)ota_write_data, data_read);
  153. if (err != ESP_OK) {
  154. http_cleanup(client);
  155. task_fatal_error();
  156. }
  157. binary_file_length += data_read;
  158. ESP_LOGD(TAG, "Written image length %d", binary_file_length);
  159. } else if (data_read == 0) {
  160. ESP_LOGI(TAG, "Connection closed");
  161. break;
  162. }
  163. }
  164. ESP_LOGI(TAG, "Total Write binary data length: %d", binary_file_length);
  165. if (esp_http_client_is_complete_data_received(client) != true) {
  166. ESP_LOGE(TAG, "Error in receiving complete file");
  167. http_cleanup(client);
  168. task_fatal_error();
  169. }
  170. err = esp_ota_end(update_handle);
  171. if (err != ESP_OK) {
  172. ESP_LOGE(TAG, "esp_ota_end failed (%s)!", esp_err_to_name(err));
  173. http_cleanup(client);
  174. task_fatal_error();
  175. }
  176. err = esp_ota_set_boot_partition(update_partition);
  177. if (err != ESP_OK) {
  178. ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err));
  179. http_cleanup(client);
  180. task_fatal_error();
  181. }
  182. ESP_LOGI(TAG, "Prepare to restart system!");
  183. esp_restart();
  184. return ;
  185. }
  186. static bool diagnostic(void)
  187. {
  188. gpio_config_t io_conf;
  189. io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
  190. io_conf.mode = GPIO_MODE_INPUT;
  191. io_conf.pin_bit_mask = (1ULL << CONFIG_EXAMPLE_GPIO_DIAGNOSTIC);
  192. io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
  193. io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
  194. gpio_config(&io_conf);
  195. ESP_LOGI(TAG, "Diagnostics (5 sec)...");
  196. vTaskDelay(5000 / portTICK_PERIOD_MS);
  197. bool diagnostic_is_ok = gpio_get_level(CONFIG_EXAMPLE_GPIO_DIAGNOSTIC);
  198. gpio_reset_pin(CONFIG_EXAMPLE_GPIO_DIAGNOSTIC);
  199. return diagnostic_is_ok;
  200. }
  201. void app_main(void)
  202. {
  203. uint8_t sha_256[HASH_LEN] = { 0 };
  204. esp_partition_t partition;
  205. // get sha256 digest for the partition table
  206. partition.address = ESP_PARTITION_TABLE_OFFSET;
  207. partition.size = ESP_PARTITION_TABLE_MAX_LEN;
  208. partition.type = ESP_PARTITION_TYPE_DATA;
  209. esp_partition_get_sha256(&partition, sha_256);
  210. print_sha256(sha_256, "SHA-256 for the partition table: ");
  211. // get sha256 digest for bootloader
  212. partition.address = ESP_BOOTLOADER_OFFSET;
  213. partition.size = ESP_PARTITION_TABLE_OFFSET;
  214. partition.type = ESP_PARTITION_TYPE_APP;
  215. esp_partition_get_sha256(&partition, sha_256);
  216. print_sha256(sha_256, "SHA-256 for bootloader: ");
  217. // get sha256 digest for running partition
  218. esp_partition_get_sha256(esp_ota_get_running_partition(), sha_256);
  219. print_sha256(sha_256, "SHA-256 for current firmware: ");
  220. const esp_partition_t *running = esp_ota_get_running_partition();
  221. esp_ota_img_states_t ota_state;
  222. if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
  223. if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) {
  224. // run diagnostic function ...
  225. bool diagnostic_is_ok = diagnostic();
  226. if (diagnostic_is_ok) {
  227. ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution ...");
  228. esp_ota_mark_app_valid_cancel_rollback();
  229. } else {
  230. ESP_LOGE(TAG, "Diagnostics failed! Start rollback to the previous version ...");
  231. esp_ota_mark_app_invalid_rollback_and_reboot();
  232. }
  233. }
  234. }
  235. // Initialize NVS.
  236. esp_err_t err = nvs_flash_init();
  237. if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
  238. // OTA app partition table has a smaller NVS partition size than the non-OTA
  239. // partition table. This size mismatch may cause NVS initialization to fail.
  240. // If this happens, we erase NVS partition and initialize NVS again.
  241. ESP_ERROR_CHECK(nvs_flash_erase());
  242. err = nvs_flash_init();
  243. }
  244. ESP_ERROR_CHECK( err );
  245. tcpip_adapter_init();
  246. ESP_ERROR_CHECK(esp_event_loop_create_default());
  247. /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
  248. * Read "Establishing Wi-Fi or Ethernet Connection" section in
  249. * examples/protocols/README.md for more information about this function.
  250. */
  251. ESP_ERROR_CHECK(example_connect());
  252. #if CONFIG_EXAMPLE_CONNECT_WIFI
  253. /* Ensure to disable any WiFi power save mode, this allows best throughput
  254. * and hence timings for overall OTA operation.
  255. */
  256. esp_wifi_set_ps(WIFI_PS_NONE);
  257. #endif // CONFIG_EXAMPLE_CONNECT_WIFI
  258. xTaskCreate(&ota_example_task, "ota_example_task", 8192, NULL, 5, NULL);
  259. }