单片机
返回首页

ESP32-OTA升级

2025-09-10 来源:cnblogs

基于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] = '';

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 }


进入单片机查看更多内容>>
相关视频
  • 【TI MSPM0 应用实战】智能小车+工业角度编码器+血氧仪+烟雾探测器!硬核参考设计详解!

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

  • 直播回放: Microchip Timberwolf™ 音频处理器在线研讨会

  • 基于灵动MM32W0系列MCU的指夹血氧仪控制及OTA升级应用方案分享

精选电路图
  • 1瓦线性调频增强器

  • 1瓦四级调频发射机

  • 500W MOS场效应管电源逆变器,12V转110V/220V

  • 红外开关

  • LM317过压保护

  • 0-30V/20A 大功率稳压电源(采用LM338)

    相关电子头条文章