设备 OTA 实践

最近更新时间:2024-11-01 09:43:32

我的收藏

概述

OTA(Over-The-Air)是一种无线下载并安装新软件或更新现有软件的技术。在物联网(IoT)领域,OTA 技术被广泛应用于设备固件的远程更新,使得设备可以在不需要物理接触的情况下,接收并安装新的固件版本。
腾讯云物联网开发平台支持了完善的设备 OTA 能力,本实践以 ESP32S3-BOX3 硬件为载体,详细介绍 OTA 的实现细节。通过本实践可以快速理解腾讯云物联网开发平台的 OTA 升级步骤和实现流程,使目标设备可以快速集成 OTA 能力。

硬件简介




ESP-BOX 是乐鑫科技推出的面向 AIoT、边缘 AI 和 IIoT 应用的开发平台。ESP32-S3-BOX、ESP32-S3-BOX-Lite 和 ESP32-S3-BOX-3 是为该平台设计的系列开发板,它们基于乐鑫强大的 ESP32-S3 Wi-Fi + Bluetooth 5(LE) SoC 构建,并拥有紧凑美观的外壳结构。借助多功能配件和 ESP-BOX 项目内的可靠例程,这些开发板既适用于构建新项目原型,也适用于打造复杂的物联网系统,提供了形式上和功能上的平衡。
关于此硬件的更多资料,请访问GitHub仓库获取。

OTA实现原理&升级流程概述

设备在通过MQTT成功连接到云平台后,需要订阅 ota 的 topic 用来接收 ota 升级请求和上报升级进度。

OTA topic


下发的固件信息中包含固件的下载地址、MD5 校验值和固件大小。拿到这些信息后就可以通过 HTTP 来下载固件并校验固件的完整性。一个完整的设备升级流程如下图所示:

OTA 时序图


说明:
这里的升级流程执行的前提是设备成功接入腾讯云物联网开发平台,并订阅了 OTA 的 topic。

实践步骤

1. 静默升级

1. 联系腾讯云物联网开发平台的销售或 提交工单,获取本实践的相关代码(基于乐鑫的 esp-box 开发,客户也可以自行适配)
2. 登录 腾讯云物联网开发平台,新建设备并获取到设备的三元组信息,相关操作可以参考 使用MQTT物模型接入平台



3. 将上一步生成的三元组信息,替换工程中的三元组,并填入当前网络环境的路由器信息,让设备正常接入云平台。
// 腾讯云物联网开发平台三元组信息
#define YOUR_PRODUCT_ID "产品ID"
#define YOUR_DEVICE_NAME "设备名称"
#define YOUR_DEVICE_SECRET "设备密钥"
#define YOUR_PRODUCT_SECRET "PRODUCT_SECRET"

// 路由器信息
#define YOUR_SSID "L-004"
#define YOUR_PASSWORD "iot2021$"
4. 设备正常上线后,会上报当前的版本号。



5. 修改工程的系统版本号,重新编译生成一个待升级的固件。
#define FW_RUNNING_MCU_VERSION "mcu_v1.0.1" // 待升级的固件号
重新编译后,生成我们需要的上传云平台的固件:
$ idf.py build
Executing action: all (aliases: build)
Running ninja in directory E:\\GitHub\\tc-iot-explorer-for-esp32-box3\\examples\\tc-iot-explorer-ota\\build
Executing "ninja all"...
[2/5] cmd.exe /C "cd /D E:\\GitHub\\tc-iot-explorer-for-esp32-box3\\examp...er-for-esp32-box3/examples/tc-iot-explorer-ota/build/explorer_ota.bin
explorer_ota.bin binary size 0x135500 bytes. Smallest app partition is 0x200000 bytes. 0xcab00 bytes (40%) free.
[1/1] cmd.exe /C "cd /D E:\\GitHub\\tc-iot-explorer-for-esp32-box3\\examp...p32-box3/examples/tc-iot-explorer-ota/build/bootloader/bootloader.bin
Bootloader binary size 0x5720 bytes. 0x28e0 bytes (32%) free.

Project build complete. To flash, run:
idf.py flash
or
idf.py -p PORT flash
or
python -m esptool --chip esp32s3 -b 460800 --before default_reset --after hard_reset write_flash --flash_mode dio --flash_size 16MB --flash_freq 80m 0x0 build\\bootloader\\bootloader.bin 0x8000 build\\partition_table\\partition-table.bin 0x10000 build\\ota_data_initial.bin 0x20000 build\\explorer_ota.bin 0x420000 build\\storage.bin
or from the "E:\\GitHub\\tc-iot-explorer-for-esp32-box3\\examples\\tc-iot-explorer-ota\\build" directory
python -m esptool --chip esp32s3 -b 460800 --before default_reset --after hard_reset write_flash "@flash_args"
注意:
1. 对于 ESP32 系列来说,我们升级只需要工程名.bin即可,对于当前工程来说,我们只需要 build\\explorer_ota.bin
2. 这里修改版本号只是为了直观的验证升级成功与否,正常的开发流程下需要将待升级的固件上传云平台。
6. 将待升级的固件上传到控制台



7. 确保设备正常上线,然后点击升级固件升级。



说明:
1. 待升级版本号:等待升级的设备版本号,这里的设备版本号就是设备上电上报的固件版本号,如果这里下拉菜单找不到待升级版本号,则需要确认设备连接云平台后,是否正确上报了固件版本号。
2. 升级范围:选择全部设备,则将当前产品下,上报此版本号的全部设备,都会推送升级请求。选择指定设备,则只给被指定的设备推送升级请求。
3. 升级确认:选择静默升级,则创建完升级任务后,后天会直接推送升级请求给设备,完成设备升级,整个过程用户无感知。选择用户确认升级,则需要在连连小程序(用户自主小程序)上点击确认升级按钮后,后台才开始下发设备升级请求,完成设备升级。



8. 设备收到固件升级请求后,会从 cos 拉取固件,并上报升级进度。



设备升级界面如下:



同时,在控制台也可以看到当前的升级进度:



9. 设备升级完成后,会校验固件完整性,校验通过后会重启设备,运行新固件。并上报固件版本号:
{"type": "report_version", "report":{"version":"mcu_v1.0.1", "fw_type":"mcu"}}
同时在控制台也可以查询升级任务状态已变为成功:



在设备详情页也可以查看设备当前版本号:





2. 用户确认升级

1. 前面的1~6步骤同上,在创建升级任务时,选择用户确认升级。



2. 以连连小程序为例,找到我们创建的设备,展示设备二维码,使用连连小程序扫一扫添加设备。



说明:
如果设备是通过连连小程序配网连接的网络,则可以省略该步骤,因为在配网完成后,会自动将此设备加入连连家庭,可以在连连小程序上直接找到该设备。
3. 在连连小程序上找到该设备,点击进入设备详情页,会弹出设备升级提示,点击立即升级。



说明:
如果这里没有弹出固件升级的提示框,则点击右上角的三个点进入设备详情页,然后点击固件升级来手动检查升级。
4. 连连小程序会显示下载进度,直到下载完成,在设备详情页点击固件升级可以查看到最新的固件版本号。









断点续传

在弱网环境或者是设备意外断电的情况下,设备下次上电后,上报完当前的固件版本号都会触发设备升级的任务,然后设备就可以收到平台推送的升级请求,在拉取固件前,设备可以读取当前已下载的固件大小,然后决定是否从当前已下载的固件断点处继续传输。断点续传的流程如下所示:



不论是手动确认升级还是静默升级,设备都是支持断点续传的。在上面的升级过程中,可以认为复位开发板,观察到如下日志,说明设备是从断点处开始拉取固件:
DBG|1970-01-01 00:00:07|mqtt_client_publish.c|qcloud_iot_mqtt_publish(339): publish topic seq=2289|topicName=$ota/report/3BY0T9892B/esp32c3_test|payload={"type": "report_version", "report":{"version":"module_v1.0.0", "fw_type":"module"}}
INF|1970-01-01 00:00:07|iot_explorer.c|_event_handler(107): publish success, packet-id=2288
DBG|1970-01-01 00:00:07|ota_mqtt.c|_otamqtt_upgrage_cb(111): topic=$ota/update/3BY0T9892B/esp32c3_test
INF|1970-01-01 00:00:07|ota_mqtt.c|_otamqtt_upgrage_cb(112): len=91, topic_msg={"type":"report_version_rsp","version":"mcu_v1.0.0","result_code":0,"result_msg":"success"}
INF|1970-01-01 00:00:07|ota_client.c|_ota_callback(104): Report version success!
INF|1970-01-01 00:00:07|iot_explorer.c|process_ota(420): wait for ota upgrade command...
INF|1970-01-01 00:00:08|iot_explorer.c|_event_handler(107): publish success, packet-id=2289
DBG|1970-01-01 00:00:08|ota_mqtt.c|_otamqtt_upgrage_cb(111): topic=$ota/update/3BY0T9892B/esp32c3_test
INF|1970-01-01 00:00:08|ota_mqtt.c|_otamqtt_upgrage_cb(112): len=475, topic_msg={"file_size":1256480,"fw_type":"mcu","md5sum":"a50b4************************e4279","type":"update_firmware","url":"http://ota-1255858890.cos.ap-guangzhou.myqcloud.com/96666666666_3BY0T9892B_mcu_v1.0.1?sign=q-sign-algorithm%3Dsha1%26q-ak%3D**********************************%26q-sign-time%3D1706600608%3B1706687008%26q-key-time%3D1706600608%3B1706687008%26q-header-list%3D%26q-url-param-list%3D%26q-signature%3D0b3e19baf98c14325b
DBG|1970-01-01 00:00:08|ota_mqtt.c|_otamqtt_upgrage_cb(111): topic=$ota/update/3BY0T9892B/esp32c3_test
INF|1970-01-01 00:00:08|ota_mqtt.c|_otamqtt_upgrage_cb(112): len=94, topic_msg={"type":"report_version_rsp","version":"module_v1.0.0","result_code":0,"result_msg":"success"}
INF|1970-01-01 00:00:08|ota_client.c|_ota_callback(84): In downloading or downloaded state
INF|1970-01-01 00:00:08|iot_explorer.c|process_ota(420): wait for ota upgrade command...
DBG|1970-01-01 00:00:08|iot_explorer.c|_get_local_fw_info(294): read /data/FW_mcu_v1.0.1.json: {"version":"mcu_v1.0.1", "downloaded_size":208896}
DBG|1970-01-01 00:00:08|iot_explorer.c|_get_local_fw_info(315): get local size : 208896
INF|1970-01-01 00:00:08|iot_explorer.c|_update_fw_downloaded_size(338): calc MD5 for resuming download from offset: 208896
DBG|1970-01-01 00:00:08|iot_explorer.c|_cal_exist_fw_md5(226): total read: 208896
DBG|1970-01-01 00:00:08|iot_explorer.c|_update_fw_downloaded_size(347): local MD5 update done!
DBG|1970-01-01 00:00:08|ota_client.c|IOT_OTA_StartDownload(372): to download FW from offset: 208896, size: 1256480
INF|1970-01-01 00:00:08|HAL_TCP_lwip.c|HAL_TCP_Connect(93): connected with TCP server: ota-1255858890.cos.ap-guangzhou.myqcloud.com:80
DBG|1970-01-01 00:00:08|utils_httpc.c|qcloud_http_client_connect(739): http client connect success
DBG|1970-01-01 00:00:08|iot_explorer.c|process_ota(451): remote fw type : 1 mcu
DBG|1970-01-01 00:00:09|mqtt_client_publish.c|qcloud_iot_mqtt_publish(346): publish packetID=0|topicName=$ota/report/3BY0T9892B/esp32c3_test|payload={"type": "report_progress", "report": {"progress": {"state":"downloading", "percent":"16", "result_code":"0", "result_msg":""}, "fw_type":"mcu","version": "mcu_v1.0.1"}}
DBG|1970-01-01 00:00:09|iot_explorer.c|_update_local_fw_info(261): update : {"version":"mcu_v1.0.1", "downloaded_size":212992} to /data/FW_mcu_v1.0.1.json
DBG|1970-01-01 00:00:10|iot_explorer.c|_update_local_fw_info(261): update : {"version":"mcu_v1.0.1", "downloaded_size":217088} to /data/FW_mcu_v1.0.1.json
DBG|1970-01-01 00:00:11|mqtt_client_publish.c|qcloud_iot_mqtt_publish(346): publish packetID=0|topicName=$ota/report/3BY0T9892B/esp32c3_test|payload={"type": "report_progress", "report": {"progress": {"state":"downloading", "percent":"17", "result_code":"0", "result_msg":""}, "fw_type":"mcu","version": "mcu_v1.0.1"}}
DBG|1970-01-01 00:00:11|iot_explorer.c|_update_local_fw_info(261): update : {"version":"mcu_v1.0.1", "downloaded_size":221184} to /data/FW_mcu_v1.0.1.json
DBG|1970-01-01 00:00:12|iot_explorer.c|_update_local_fw_info(261): update : {"version":"mcu_v1.0.1", "downloaded_size":225280} to /data/FW_mcu_v1.0.1.json
DBG|1970-01-01 00:00:12|mqtt_client_publish.c|qcloud_iot_mqtt_publish(346): publish packetID=0|topicName=$ota/report/3BY0T9892B/esp32c3_test|payload={"type": "report_progress", "report": {"progress": {"state":"downloading", "percent":"18", "result_code":"0", "result_msg":""}, "fw_type":"mcu","version": "mcu_v1.0.1"}}