zoukankan      html  css  js  c++  java
  • ESP32-OTA升级

    基于ESP-IDF4.1

      1 #include <string.h>
      2 #include "freertos/FreeRTOS.h"
      3 #include "freertos/task.h"
      4 #include "esp_system.h"
      5 #include "esp_event.h"
      6 #include "esp_log.h"
      7 #include "esp_ota_ops.h"
      8 #include "esp_http_client.h"
      9 #include "esp_flash_partitions.h"
     10 #include "esp_partition.h"
     11 #include "nvs.h"
     12 #include "nvs_flash.h"
     13 #include "driver/gpio.h"
     14 #include "protocol_examples_common.h"
     15 #include "errno.h"
     16 
     17 #if CONFIG_EXAMPLE_CONNECT_WIFI
     18 #include "esp_wifi.h"
     19 #endif
     20 
     21 #define BUFFSIZE 1024
     22 #define HASH_LEN 32 //sha256摘要长度
     23 
     24 static const char *TAG = "native_ota_example";
     25 //准备写入闪存的OTA数据写入缓冲区
     26 static char ota_write_data[BUFFSIZE + 1] = { 0 };
     27 extern const uint8_t server_cert_pem_start[] asm("_binary_ca_cert_pem_start");
     28 extern const uint8_t server_cert_pem_end[] asm("_binary_ca_cert_pem_end");
     29 
     30 #define OTA_URL_SIZE 256
     31 
     32 static void http_cleanup(esp_http_client_handle_t client)
     33 {
     34     esp_http_client_close(client);
     35     esp_http_client_cleanup(client);
     36 }
     37 
     38 //__attribute__((noreturn)) 这个属性告诉编译器函数不会返回,这可以用来抑制关于未达到代码路径的错误。
     39 static void __attribute__((noreturn)) task_fatal_error(void)
     40 {
     41     ESP_LOGE(TAG, "Exiting task due to fatal error...");
     42     (void)vTaskDelete(NULL);
     43 
     44     while (1) {
     45         ;
     46     }
     47 }
     48 
     49 static void print_sha256 (const uint8_t *image_hash, const char *label)
     50 {
     51     char hash_print[HASH_LEN * 2 + 1];
     52     hash_print[HASH_LEN * 2] = 0;
     53     for (int i = 0; i < HASH_LEN; ++i) {
     54         sprintf(&hash_print[i * 2], "%02x", image_hash[i]);
     55     }
     56     ESP_LOGI(TAG, "%s: %s", label, hash_print);
     57 }
     58 
     59 static void infinite_loop(void)
     60 {
     61     int i = 0;
     62     ESP_LOGI(TAG, "When a new firmware is available on the server, press the reset button to download it");
     63     while(1) {
     64         ESP_LOGI(TAG, "Waiting for a new firmware ... %d", ++i);
     65         vTaskDelay(2000 / portTICK_PERIOD_MS);
     66     }
     67 }
     68 
     69 //OTA升级任务
     70 static void ota_example_task(void *pvParameter)
     71 {
     72     esp_err_t err;
     73     //更新处理程序,通过esp_ota_begin()设置,必须通过esp_ota_end()释放
     74     esp_ota_handle_t update_handle = 0 ;
     75     const esp_partition_t *update_partition = NULL;
     76 
     77     ESP_LOGI(TAG, "Starting OTA example");
     78 
     79     const esp_partition_t *configured = esp_ota_get_boot_partition();
     80     const esp_partition_t *running = esp_ota_get_running_partition();
     81 
     82     if (configured != running) {
     83         ESP_LOGW(TAG, "Configured OTA boot partition at offset 0x%08x, but running from offset 0x%08x",
     84                  configured->address, running->address);
     85         ESP_LOGW(TAG, "(This can happen if either the OTA boot data or preferred boot image become corrupted somehow.)");
     86     }
     87     ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08x)",
     88              running->type, running->subtype, running->address);
     89 
     90     esp_http_client_config_t config = {
     91         .url = CONFIG_EXAMPLE_FIRMWARE_UPG_URL,
     92         .cert_pem = (char *)server_cert_pem_start,
     93         .timeout_ms = CONFIG_EXAMPLE_OTA_RECV_TIMEOUT,
     94     };
     95 
     96 #ifdef CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL_FROM_STDIN
     97     char url_buf[OTA_URL_SIZE];
     98     if (strcmp(config.url, "FROM_STDIN") == 0) {
     99         example_configure_stdin_stdout();
    100         fgets(url_buf, OTA_URL_SIZE, stdin);
    101         int len = strlen(url_buf);
    102         url_buf[len - 1] = '';
    103         config.url = url_buf;
    104     } else {
    105         ESP_LOGE(TAG, "Configuration mismatch: wrong firmware upgrade image url");
    106         abort();
    107     }
    108 #endif
    109 
    110 #ifdef CONFIG_EXAMPLE_SKIP_COMMON_NAME_CHECK
    111     config.skip_cert_common_name_check = true;
    112 #endif
    113 
    114     esp_http_client_handle_t client = esp_http_client_init(&config);
    115     if (client == NULL) {
    116         ESP_LOGE(TAG, "Failed to initialise HTTP connection");
    117         task_fatal_error();
    118     }
    119     err = esp_http_client_open(client, 0);
    120     if (err != ESP_OK) {
    121         ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
    122         esp_http_client_cleanup(client);
    123         task_fatal_error();
    124     }
    125     esp_http_client_fetch_headers(client);
    126 
    127     update_partition = esp_ota_get_next_update_partition(NULL);
    128     ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x",
    129              update_partition->subtype, update_partition->address);
    130     assert(update_partition != NULL);
    131 
    132     int binary_file_length = 0;
    133     //处理所有接收数据包
    134     bool image_header_was_checked = false;
    135     while (1) {
    136         int data_read = esp_http_client_read(client, ota_write_data, BUFFSIZE);
    137         if (data_read < 0) {
    138             ESP_LOGE(TAG, "Error: SSL data read error");
    139             http_cleanup(client);
    140             task_fatal_error();
    141         } else if (data_read > 0) {
    142             if (image_header_was_checked == false) {
    143                 esp_app_desc_t new_app_info;
    144                 if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) {
    145                     //通过下载检查当前版本
    146                     memcpy(&new_app_info, &ota_write_data[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t));
    147                     ESP_LOGI(TAG, "New firmware version: %s", new_app_info.version);
    148 
    149                     esp_app_desc_t running_app_info;
    150                     if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) {
    151                         ESP_LOGI(TAG, "Running firmware version: %s", running_app_info.version);
    152                     }
    153 
    154                     const esp_partition_t* last_invalid_app = esp_ota_get_last_invalid_partition();
    155                     esp_app_desc_t invalid_app_info;
    156                     if (esp_ota_get_partition_description(last_invalid_app, &invalid_app_info) == ESP_OK) {
    157                         ESP_LOGI(TAG, "Last invalid firmware version: %s", invalid_app_info.version);
    158                     }
    159 
    160                     //通过最新无效分区检查当前版本
    161                     if (last_invalid_app != NULL) {
    162                         if (memcmp(invalid_app_info.version, new_app_info.version, sizeof(new_app_info.version)) == 0) {
    163                             ESP_LOGW(TAG, "New version is the same as invalid version.");
    164                             ESP_LOGW(TAG, "Previously, there was an attempt to launch the firmware with %s version, but it failed.", invalid_app_info.version);
    165                             ESP_LOGW(TAG, "The firmware has been rolled back to the previous version.");
    166                             http_cleanup(client);
    167                             infinite_loop();
    168                         }
    169                     }
    170 #ifndef CONFIG_EXAMPLE_SKIP_VERSION_CHECK
    171                     if (memcmp(new_app_info.version, running_app_info.version, sizeof(new_app_info.version)) == 0) {
    172                         ESP_LOGW(TAG, "Current running version is the same as a new. We will not continue the update.");
    173                         http_cleanup(client);
    174                         infinite_loop();
    175                     }
    176 #endif
    177 
    178                     image_header_was_checked = true;
    179 
    180                     err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
    181                     if (err != ESP_OK) {
    182                         ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err));
    183                         http_cleanup(client);
    184                         task_fatal_error();
    185                     }
    186                     ESP_LOGI(TAG, "esp_ota_begin succeeded");
    187                 } else {
    188                     ESP_LOGE(TAG, "received package is not fit len");
    189                     http_cleanup(client);
    190                     task_fatal_error();
    191                 }
    192             }
    193             err = esp_ota_write( update_handle, (const void *)ota_write_data, data_read);
    194             if (err != ESP_OK) {
    195                 http_cleanup(client);
    196                 task_fatal_error();
    197             }
    198             binary_file_length += data_read;
    199             ESP_LOGD(TAG, "Written image length %d", binary_file_length);
    200         } else if (data_read == 0) {
    201            //由于esp_http_client_read从不返回负错误代码,因此我们依赖“errno”来检查底层传输连接关闭(如果有)
    202             if (errno == ECONNRESET || errno == ENOTCONN) {
    203                 ESP_LOGE(TAG, "Connection closed, errno = %d", errno);
    204                 break;
    205             }
    206             if (esp_http_client_is_complete_data_received(client) == true) {
    207                 ESP_LOGI(TAG, "Connection closed");
    208                 break;
    209             }
    210         }
    211     }
    212     ESP_LOGI(TAG, "Total Write binary data length: %d", binary_file_length);
    213     if (esp_http_client_is_complete_data_received(client) != true) {
    214         ESP_LOGE(TAG, "Error in receiving complete file");
    215         http_cleanup(client);
    216         task_fatal_error();
    217     }
    218 
    219     err = esp_ota_end(update_handle);
    220     if (err != ESP_OK) {
    221         if (err == ESP_ERR_OTA_VALIDATE_FAILED) {
    222             ESP_LOGE(TAG, "Image validation failed, image is corrupted");
    223         }
    224         ESP_LOGE(TAG, "esp_ota_end failed (%s)!", esp_err_to_name(err));
    225         http_cleanup(client);
    226         task_fatal_error();
    227     }
    228 
    229     err = esp_ota_set_boot_partition(update_partition);
    230     if (err != ESP_OK) {
    231         ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err));
    232         http_cleanup(client);
    233         task_fatal_error();
    234     }
    235     ESP_LOGI(TAG, "Prepare to restart system!");
    236     esp_restart();
    237     return ;
    238 }
    239 
    240 //诊断
    241 static bool diagnostic(void)
    242 {
    243     gpio_config_t io_conf;
    244     io_conf.intr_type    = GPIO_PIN_INTR_DISABLE;
    245     io_conf.mode         = GPIO_MODE_INPUT;
    246     io_conf.pin_bit_mask = (1ULL << CONFIG_EXAMPLE_GPIO_DIAGNOSTIC);
    247     io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
    248     io_conf.pull_up_en   = GPIO_PULLUP_ENABLE;
    249     gpio_config(&io_conf);
    250 
    251     ESP_LOGI(TAG, "Diagnostics (5 sec)...");
    252     vTaskDelay(5000 / portTICK_PERIOD_MS);
    253 
    254     bool diagnostic_is_ok = gpio_get_level(CONFIG_EXAMPLE_GPIO_DIAGNOSTIC);
    255 
    256     gpio_reset_pin(CONFIG_EXAMPLE_GPIO_DIAGNOSTIC);
    257     return diagnostic_is_ok;
    258 }
    259 
    260 void app_main(void)
    261 {
    262     uint8_t sha_256[HASH_LEN] = { 0 };
    263     esp_partition_t partition;
    264 
    265     //获取分区表的sha256摘要
    266     partition.address   = ESP_PARTITION_TABLE_OFFSET;
    267     partition.size      = ESP_PARTITION_TABLE_MAX_LEN;
    268     partition.type      = ESP_PARTITION_TYPE_DATA;
    269     esp_partition_get_sha256(&partition, sha_256);
    270     print_sha256(sha_256, "SHA-256 for the partition table: ");
    271 
    272     //获取引导加载程序的sha256摘要
    273     partition.address   = ESP_BOOTLOADER_OFFSET;
    274     partition.size      = ESP_PARTITION_TABLE_OFFSET;
    275     partition.type      = ESP_PARTITION_TYPE_APP;
    276     esp_partition_get_sha256(&partition, sha_256);
    277     print_sha256(sha_256, "SHA-256 for bootloader: ");
    278 
    279     //获取运行分区的sha256摘要
    280     esp_partition_get_sha256(esp_ota_get_running_partition(), sha_256);
    281     print_sha256(sha_256, "SHA-256 for current firmware: ");
    282 
    283     const esp_partition_t *running = esp_ota_get_running_partition();
    284     esp_ota_img_states_t ota_state;
    285     if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
    286         if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) {
    287             // 运行诊断功能 ...
    288             bool diagnostic_is_ok = diagnostic();
    289             if (diagnostic_is_ok) {
    290                 ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution ...");
    291                 esp_ota_mark_app_valid_cancel_rollback();
    292             } else {
    293                 ESP_LOGE(TAG, "Diagnostics failed! Start rollback to the previous version ...");
    294                 esp_ota_mark_app_invalid_rollback_and_reboot();
    295             }
    296         }
    297     }
    298 
    299     // 初始化NVS
    300     esp_err_t err = nvs_flash_init();
    301     if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
    302         //OTA应用程序分区表有一个比非OTA分区表更小的NVS分区。大小不匹配可能导致NVS初始化失败,如果发生这种情况,我们擦除NVS分区并在此初始化NVS
    303         ESP_ERROR_CHECK(nvs_flash_erase());
    304         err = nvs_flash_init();
    305     }
    306     ESP_ERROR_CHECK( err );
    307 
    308     ESP_ERROR_CHECK(esp_netif_init());
    309     ESP_ERROR_CHECK(esp_event_loop_create_default());
    310 
    311     //连接网络
    312     ESP_ERROR_CHECK(example_connect());
    313 
    314 #if CONFIG_EXAMPLE_CONNECT_WIFI
    315     //确保禁用低功耗模式,这样可以提供最佳的吞吐量从而节省OTA操作的时间
    316     esp_wifi_set_ps(WIFI_PS_NONE);
    317 #endif
    318     xTaskCreate(&ota_example_task, "ota_example_task", 8192, NULL, 5, NULL);
    319 }

    原文:https://gitee.com/EspressifSystems/esp-idf/tree/master/examples/system/ota

  • 相关阅读:
    分布式事务解决方案1--使用Atomikos分布式事务(事务强一致方案)
    SringBoot集成Sharding-Jdbc 实践
    Sharding-Jdbc简介
    Mycat+haproxy中使用keepalived保障haproxy的高可用
    Angular CLI
    背压(Backpressure)机制
    Function.identity()
    解决Error: ENOENT: no such file or directory, scandir 安装node-sass报错
    Reactor flatMap
    Reactor map
  • 原文地址:https://www.cnblogs.com/kerwincui/p/13968497.html
Copyright © 2011-2022 走看看