ESP32-OTA升级
阅读原文时间:2023年07月10日阅读:6

基于ESP-IDF4.1

1 #include
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