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