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] = '\0';
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

posted @ 2020-11-13 11:34  kerwin cui  阅读(1307)  评论(0编辑  收藏  举报