ESP32 OTA升级之 https ota详解

1. 前言

本文以 ESP32 官方demo例程 native_ota_example 为例,详细阐述如何采用https实现esp32的ota升级。

第一章节,为本文的前言部分,对文章内容进行大体概述;第二章节,主要描述了如何在本地将demo例程跑起来,并附带了关于使用demo例程中遇到的相关报错的具体解决措施;第三章节,主要描述了ESP32的 flash 扇区布局;第四章节,主要描述了OTA下载的镜像文件的头部数据结构,并对其进行详细分析,镜像文件的头部与OTA息息相关;第五、六章节,详细描述了OTA升级过程中应用程序的状态切换逻辑以及相关软件版本的使用;

接下来,让我们一起研究ESP32的 OTA 升级是如何实现的吧!相信你一定有所收获!

2. 例程体验

例程目录:esp-idf/examples/system/ota/native_ota_example/ 工程地址:https://github.com/espressif/esp-idf/tree/release/v5.1/examples/system/ota/native_ota_example IDF版本: v5.1

2.1 采用直接跳过ca根证书验证方案

进入 native_ota_example 工程 运行 idf.py menuconfig 配置wifi及服务器地址 配置 wifi 配置https服务器地址 如果没有https服务器,填写http服务器地址也可以,http服务器搭建参考:ESP32 OTA升级之HTTP OTA https服务器,大家可以尝试把升级文件往github、gitee上发布也可以啦,免费的https测试服务器对吧 运行 idf.py build 编译工程 运行 idf.py flash monitor 下载工程并开启监控 网络连接成功之后https下载时会报错,提示 mbdtls ssl 握手失败,错误码 -0x2700 I (5888) native_ota_example: Starting OTA example task

I (5888) native_ota_example: Running partition type 0 subtype 0 (offset 0x00010000)

E (6808) esp-tls-mbedtls: mbedtls_ssl_handshake returned -0x2700

I (6808) esp-tls-mbedtls: Failed to verify peer certificate!

E (6808) esp-tls: Failed to open new connection

E (6818) transport_base: Failed to open a new connection

E (6828) HTTP_CLIENT: Connection failed, sock < 0

E (6828) native_ota_example: Failed to open HTTP connection: ESP_ERR_HTTP_CONNECT

E (6838) native_ota_example: Exiting task due to fatal error...

打开 native_ota_example.c 文件,屏蔽 .cert_pem 配置 运行 idf.py build 编译工程,之后运行 idf.py flash monitor 开启监控 网络连接成功之后https下载时会报错,提示 mbdtls 服务器验证选项 esp_tls_cfg_t 结构体配置失败 I (5888) native_ota_example: Starting OTA example task

I (5888) native_ota_example: Running partition type 0 subtype 0 (offset 0x00010000)

E (6368) esp-tls-mbedtls: No server verification option set in esp_tls_cfg_t structure. Check esp_tls API reference

E (6368) esp-tls-mbedtls: Failed to set client configurations, returned [0x8017] (ESP_ERR_MBEDTLS_SSL_SETUP_FAILED)

E (6378) esp-tls: create_ssl_handle failed

E (6388) esp-tls: Failed to open new connection

E (6388) transport_base: Failed to open a new connection

E (6398) HTTP_CLIENT: Connection failed, sock < 0

E (6398) native_ota_example: Failed to open HTTP connection: ESP_ERR_HTTP_CONNECT

E (6408) native_ota_example: Exiting task due to fatal error...

修改 menuconfig ,跳过服务器证书验证 运行 idf.py build 编译工程,之后运行 idf.py flash monitor 开启监控 网络连接成功之后https下载成功

2.2 采用mbedtls内自带的ca根证书方案

修改 native_ota_example.c 文件中.cert_pem 配置

esp_http_client_config_t config = {

.url = CONFIG_EXAMPLE_FIRMWARE_UPG_URL,

// .cert_pem = (char *)server_cert_pem_start,

.crt_bundle_attach = esp_crt_bundle_attach,

.timeout_ms = CONFIG_EXAMPLE_OTA_RECV_TIMEOUT,

.keep_alive_enable = true,

};

2.3 采用对应服务器的根证书文件

此方案,首先需要下载对应服务器的证书文件才可以,下载服务器的证书可以采用以下方案:

首先使用浏览器打开对应的服务器,之后浏览器的地址栏旁边有一个,点击它 按照上述方式,导出对应服务器的证书文件 .pem,之后替换掉工程目录下 server_certs 目录下的ca_cert.pem 文件里面的内容即可

采用此方案有一个需要特别注意的地方是:通过此方案下载的证书为此网站的证书,通常有效期为1年,过了一年之后就无效了!!!

这个时间在我们下载证书的时候其实就可以看到: 因此使用此方案时,需要注意特别处理,推荐两种解决方案:

1)采用升级的手段,定期更新设备端证书文件;

2)使用发证机构(注意选择大品牌的)的根证书文件,通常此类证书的有效期会比较长,我们上一小节中 mbedtls里面的证书其实就是存的一部分发证机构的ca跟证书

参考文档:ESP32空中升级 OTA

3. ESP32 flash分区结构

OTA是基于 Flash 进行的,想要弄清楚 ESP32 的 OTA,首先肯定得了解 ESP32 的 flash 布局结构,关于此部分内容我整理在了另外一篇博客,请查阅:ESP32 分区表(点击跳转),对 ESP32 的 flash 分区布局有所了解的可以直接跳过。

4. 应用程序镜像段结构

应用程序镜像段结构如下,运行命令 esptool.py --chip esp32 image_info build/app.bin

esptool.py v2.3.1

Image version: 1

Entry point: 40080ea4

13 segments

Segment 1: len 0x13ce0 load 0x3f400020 file_offs 0x00000018 SOC_DROM

Segment 2: len 0x00000 load 0x3ff80000 file_offs 0x00013d00 SOC_RTC_DRAM

Segment 3: len 0x00000 load 0x3ff80000 file_offs 0x00013d08 SOC_RTC_DRAM

Segment 4: len 0x028e0 load 0x3ffb0000 file_offs 0x00013d10 DRAM

Segment 5: len 0x00000 load 0x3ffb28e0 file_offs 0x000165f8 DRAM

Segment 6: len 0x00400 load 0x40080000 file_offs 0x00016600 SOC_IRAM

Segment 7: len 0x09600 load 0x40080400 file_offs 0x00016a08 SOC_IRAM

Segment 8: len 0x62e4c load 0x400d0018 file_offs 0x00020010 SOC_IROM

Segment 9: len 0x06cec load 0x40089a00 file_offs 0x00082e64 SOC_IROM

Segment 10: len 0x00000 load 0x400c0000 file_offs 0x00089b58 SOC_RTC_IRAM

Segment 11: len 0x00004 load 0x50000000 file_offs 0x00089b60 SOC_RTC_DATA

Segment 12: len 0x00000 load 0x50000004 file_offs 0x00089b6c SOC_RTC_DATA

Segment 13: len 0x00000 load 0x50000004 file_offs 0x00089b74 SOC_RTC_DATA

Checksum: e8 (valid)

Validation Hash: 407089ca0eae2bbf83b4120979d3354b1c938a49cb7a0c997f240474ef2ec76b (valid)

4.1 镜像文件头格式

镜像在最后一段之后有一个校验和字节(如上述中的 Checksum: e8 (valid) )。此字节写入十六字节填充边界,因此应用程序镜像可能需要填充。如果esp_image_header_t 结构体(见下图)的hash_appended字段被设置,则将附加 SHA256 校验和。SHA256 哈希的值是在从第一个字节到该字段的范围内计算的,该字段的长度为 32 个字节。

应用程序映像由以下结构组成:

首先是 镜像头 esp_image_header_t 结构数据,描述了 SPI 闪存的模式和内存段的数量。 之后是 数据段头 esp_image_segment_header_t 结构数据,此结构数据描述了每个段、其长度及其在 ESP32 内存中的位置,后跟长度为data_len,图像中每个片段的数据偏移量按以下方式计算:

第 0 段的偏移量 = sizeof( esp_image_header_t) + sizeof( esp_image_segment_header_t))。第 1 Segment 的偏移量 = 0 Segment 的偏移量 + 0 Segment 的长度 + sizeof( esp_image_segment_header_t)。第 2 Segment 的偏移量 = 1 Segment 的偏移量 + 1 Segment 的长度 + sizeof( esp_image_segment_header_t)。 之后是 应用头 esp_app_desc_t 结构数据:

此数据结构位于DORM 段起始位置当前应用程序可以通过调用esp_app_get_description() 获取应用头结构其他应用程序可通过调用esp_ota_get_partition_description() 获取其他OTA分区应用头结构此结构的偏移地址计算为:offset = sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) 再往后是 用户自定义的应用结构头,如有需要可自行查阅 Adding a Custom Structure to an Application

综上,一个应用程序镜像的头部结构为:

首先是 esp_image_header_t其次是 esp_image_segment_header_t之后是 esp_app_desc_t如果有用户自定义的结构custom_app_desc 则之后是用户自定义结构 custom_app_desc之后就是实际的程序了

参考文档:App Image Format

5. 应用程序状态切换

应用程序通过一个OTA状态对齐进行维护,通过此状态决定应用程序是否运行。

应用程序可通过调用 esp_err_t esp_ota_get_state_partition(const esp_partition_t *partition, esp_ota_img_states_t *ota_state) 获取对应扇区应用的状态。

应用程序的状态有以下几大类:

状态引导加载程序选取启动应用程序的限制ESP_OTA_IMG_VALID没有限制,可以选取。ESP_OTA_IMG_UNDEFINED没有限制,可以选取。ESP_OTA_IMG_INVALID不会选取。ESP_OTA_IMG_ABORTED不会选取。ESP_OTA_IMG_NEW如使能 CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE, 则仅会选取一次。在引导加载程序中,状态立即变为 ESP_OTA_IMG_PENDING_VERIFY。ESP_OTA_IMG_PENDING_VERIFY如使能 CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE, 则不会选取,状态变为ESP_OTA_IMG_ABORTED。

关于状态的使用,需要注意一个配置项: CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE

当 CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE 未使能时,应用程序状态通常只会使用 ESP_OTA_IMG_UNDEFINED 这一个状态

(官方IDF文档只是说ESP_OTA_IMG_NEW 和 ESP_OTA_IMG_PENDING_VERIFY 不会使用,但实测发现基本只会使用 ESP_OTA_IMG_UNDEFINED ) 当 CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE 使能时,应用程序状态通常会使用除 ESP_OTA_IMG_UNDEFINED 状态之外的其他所有状态

CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE 配置项可通过 menuconfig 配置:

当 CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE 未使能时:

当CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE 使能时:

Pass: 程序分析小技巧,可以通过python在工程目录下搭建本地http服务器,配置升级地址为服务器地址,这样可以循环升级,并在例程中添加我们需要的打印信息,即可快速分析ota程序!

参考文档:Over The Air Updates (OTA)

6. 应用程序版本修改

应用程序版本存储在 esp_app_desc_t 结构体中。该结构体位于 DROM 扇区,有一个从二进制文件头部计算的固定偏移值。该结构体位于 esp_image_header_t 和 esp_image_segment_header_t 结构体之后。字段 Version 类型为字符串,最大长度为 32 字节。

关于应用程序的版本设置,总共有以下几种方案:

在 menuconfig 中设置对应配置项(Application manager选项内)

在 CMakeLists.txt 文件中设置 PROJECT_VER 变量,注意需要在 包含project.cmake 之前添加,格式如 set(PROJECT_VER "0.1.0.1")

在工程目录下新建 version.txt 文档,在此文档中输入对应版本信息

使用 git describe 检索(实测命令无效) 若以上均未有设置,则会将版本变量 PROJECT_VER 默认设置为 1

7. 总结

以上就是关于 ESP32 使用 https 进行 OTA 升级的全部内容了,欢迎大家一同探讨关于ESP32的更多知识!

创作不易,转载请注明出处!

关注、点赞+收藏,可快速查收博主有关分享!

8. 补充学习

   强烈推荐:

1) ESP32 OTA升级之HTTP OTA(点击跳转!!!)2) ESP32 bin文件生成及多个bin文件合并指南 (点击跳转!!!)3) ESP32 分区表(点击跳转!!!)4) ESP32 专栏(点击跳转!!!)

精彩链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: