涂鸦 IoT 平台提供 固件 OTA 升级 功能,以满足客户在产品发布后,仍可对已出货设备进行固件版本升级的需求,本节课将介绍不同开发方式下固件 OTA 升级的方法、涂鸦方案的 OTA 流程与交互协议、以及如何在 涂鸦蓝牙 SDK 的基础上移植 OTA 功能。
一. OTA 方案
1.1 模组 OTA
1.1.1 涂鸦标准模组
如果您使用的是 涂鸦标准蓝牙模组,那么其配套的 涂鸦蓝牙模组 SDK 已经封装了 OTA 的实现,即模组已经具备了固件 OTA 升级的能力,您可以直接在 涂鸦 IoT 平台 上传固件来完成 OTA 升级任务,具体步骤请参考 2.2 操作步骤。
1.1.2 自研模组
如果您使用的是 自研模组,则需要先完成 OTA 功能与芯片平台的适配,您可以使用 涂鸦蓝牙 SDK 提供的接口来实现固件 OTA 升级功能,然后再通过 涂鸦 IoT 平台 来创建 OTA 升级任务。
后文将为您介绍涂鸦方案的 OTA 流程、交互协议,以及涂鸦蓝牙 SDK 中有关 OTA 升级的接口,您可以根据这些信息来实现 OTA 升级程序。
1.2 MCU OTA
涂鸦蓝牙 SDK 中已实现了对蓝牙协议模组之外的 MCU 进行 OTA 升级的处理程序,其主要通过串口通信方式与 MCU 进行 OTA 升级文件等相关信息的交互,该方案适用于除了基于涂鸦蓝牙 SDK 开发的涂鸦模组或自研模组外, 还包含额外 MCU 的产品。
需要注意的是,在涂鸦 IoT 平台上新增 MCU 固件时,固件类型要选择 MCU 固件,涂鸦蓝牙 SDK 接收到 OTA 升级请求时才会将该固件传输给 MCU 进行升级。
另外,您需要根据 蓝牙通用串口协议 中的 MCU OTA 相关接口介绍实现 MCU 侧的串口通信程序和 OTA 数据处理程序,从而实现 MCU OTA 完整链路。您也可以在涂鸦 IoT 平台上进入该产品的 硬件开发 页面,将 云端接入方式 临时切换到 MCU SDK,以便能在开发资料中下载 MCU SDK。MCU SDK 已经基于涂鸦蓝牙通用串口协议和 OTA 升级协议实现了串口数据通信与 OTA 升级的程序框架,您只需在此基础上完成 UART 和 Flash 读写等功能与 MCU 平台的适配 (可参考 MCU SDK 移植),即可快速实现 MCU 侧的 OTA 能力接入。
如果您不想使用串口方式进行 OTA 数据传输,可以参考 4.2 MCU OTA 移植 完成通信程序的移植。
二. OTA 流程
2.1 升级流程
2.2 操作步骤
2.2.1 新增固件版本
要升级固件,首先必须将新版本固件上传至 涂鸦 IoT 平台。在上传固件之前,先确认固件是否已经升级了版本号,即在 tuya_ble_sdk_demo.h 文件中修改 TY_DEVICE_FVER_NUM 和 TY_DEVICE_FVER_STR 的值。本课程中直接以升级为 0.2 版本为例:
#define TY_DEVICE_FIR_NAME "ble_module_sdk_development_demo"
#define TY_DEVICE_FVER_NUM 0x00000002
#define TY_DEVICE_FVER_STR "0.2"
蓝牙单点固件的版本号一般采用 bv.sv (0.0~99.99) 格式。实际应用场景中,正式发布的版本一般为 1.0 及以上版本,可参考下表修改,然后编译代码生成新的固件,我们称之为 升级固件。
版本升级 | TY_DEVICE_FVER_NUM | TY_DEVICE_FVER_STR |
---|
1.0 >> 1.1 | 0x00000100 >> 0x00000101 | "1.0" >> "1.1" | 2.10 >> 3.0 | 0x0000020A >> 0x00000300 | "2.10" >> "3.0" |
接下来,进入 涂鸦 IoT 平台 的 固件管理 页面,找到对应的固件,点击【新建版本】。
在 新增固件版本 页面填入和固件中一致的 固件版本号 并上传该固件,填写好其他内容后点击【保存并上架】。
在设置好固件上架范围后点击【确认上架】,即可看到下图页面。该页面也可通过 固件管理 页面操作栏中的【详情】进入,可以看到该页面也有 新增固件版本 页面的入口。
2.2.2 新建升级任务
从左侧导航栏【产品 - 设备 - 固件 OTA】可进入 固件升级 页面,切换到要升级的产品后,选择好对应的固件,然后点击【新增固件升级】来新建升级任务。
参考下图,在 新增固件升级 页面填写好升级信息后点击 【确定】。(以 App 提醒升级 为例)
在正式发布升级任务之前,可以通过测试设备来验证升级任务,点击下图中的【验证】进入设备验证页面。
2.2.3 添加设备验证
点击【通过设备号直接添加】,填入测试设备的 设备虚拟 ID。
设备虚拟 ID 可在测试设备的设备面板中找到,操作方法如下图所示。
添加完成后,打开 智能生活 APP 进入该产品的设备面板时,即可看到升级提醒弹出,按照提示完成升级。
回到 涂鸦 IoT 平台,点击【验证是否完成升级】,测试结果显示【验证成功】。
2.2.5 发布升级任务
最后,点击【发布】进入 版本发布 页面,并根据实际需要选择灰度发布或全量发布后提交。
三. OTA 协议
本节将介绍固件 OTA 升级过程中,智能生活 App 与涂鸦蓝牙模组之间的通信协议,如果您正在使用 涂鸦蓝牙模组 SDK,可通过本节表格中的标识符找到代码中对应的宏和变量。
3.1 数据格式
3.1.1 帧格式
字段 | 字节数 | 说明 |
---|
type | 1 | OTA 指令类型,由 tuya_ble_ota_data_type_t 定义 | data_len | 2 | OTA 数据长度,即 p_data 所占字节数 | p_data | data_len | OTA 数据,具体请参考 3.2 交互协议 |
3.1.2 OTA 指令
tuya_ble_ota_data_type_t 定义的 OTA 指令如下:
OTA 指令 | 标识符 | 指令值 |
---|
OTA 升级请求 | TUYA_BLE_OTA_REQ | 0x00 | OTA 升级文件信息 | TUYA_BLE_OTA_FILE_INFO | 0x01 | OTA 升级文件偏移量 | TUYA_BLE_OTA_FILE_OFFSET_REQ | 0x02 | OTA 升级数据 | TUYA_BLE_OTA_DATA | 0x03 | OTA 升级结束 | TUYA_BLE_OTA_END | 0x04 |
3.2 交互协议
3.2.1 OTA 升级请求
App -> 设备
p_data | 字节数 | 标识符 | 说明 |
---|
[0] | 1 | type | 固定为 0。 |
设备 -> App
p_data | 字节数 | 标识符 | 说明 |
---|
[0] | 1 | flag | 升级请求响应标志。
00 :允许升级;
01 :拒绝升级。 | [1] | 1 | ota_version | OTA 协议大版本。 例如 03 代表 3.x 版本的 OTA 协议。 | [2] | 1 | type | 固定为 0。 | [3:6] | 4 | version | 当前固件版本号。 大端格式,例如 00 00 02 0A 代表 V2.10。 | [7:8] | 2 | package_maxlen | 设备允许的单包数据最大长度。 当前版本协议中设定的最大长度是 512 字节。 |
3.2.2 OTA 升级文件信息
App -> 设备
p_data | 字节数 | 标识符 | 说明 |
---|
[0] | 1 | type | 固定为 0。 | [1:8] | 8 | pid | 产品 PID。(保留) 产品支持 OEM 时会存在 PID 不一致的情况: 建议设备在 OTA 时无需校验 PID 并忽略该参数。 | [9:12] | 4 | version | 升级文件版本号。 大端格式,例如 00 00 03 00 代表 V3.0。 | [13:28] | 16 | md5 | 升级文件 MD5。 | [29:32] | 4 | file_len | 升级文件长度。 | [33:36] | 4 | crc32 | CRC32。 |
设备 -> App
p_data | 字节数 | 标识符 | 说明 |
---|
[0] | 1 | type | 固定为 0。 | [1] | 1 | state | 升级状态。
00 :正常升级;
01 :产品 PID 不一致;
02 :升级文件版本低于或等于当前固件版本;
03 :升级文件大小超过范围。 | [2:5] | 4 | old_file_len | 已储存文件长度。(用于断点续传) App 会根据已储存文件长度计算新文件的 CRC32。 | [6:9] | 4 | old_crc32 | 已储存文件 CRC32。(用于断点续传) App 会将已储存文件的 CRC32 与计算出的新文件的 CRC32 进行比较。 | [10:25] | 16 | old_md5 | 已储存文件 MD5。(目前不使用) |
3.2.3 OTA 升级文件偏移量
App -> 设备
p_data | 字节数 | 标识符 | 说明 |
---|
[0] | 1 | type | 固定为 0。 | [1:4] | 4 | offset | 升级文件偏移量。 |
设备 -> App
p_data | 字节数 | 标识符 | 说明 |
---|
[0] | 1 | type | 固定为 0。 | [1:4] | 4 | offset | 设备要求的起始传输文件偏移量。 实际文件传输的偏移地址应该以设备端要求的为准; 设备端要求的地址会小于等于 App 端给出的偏移。 |
3.2.4 OTA 升级文件数据
App -> 设备
p_data | 字节数 | 标识符 | 说明 |
---|
[0] | 1 | type | 固定为 0。 | [1:2] | 2 | pkg_id | 升级文件数据包号。 从 0 开始,高字节在前。 | [3:4] | 2 | len | 当前包数据长度 n。
| [5:6] | 2 | crc16 | 当前包数据 CRC16。
| [7:(7+n-1)] | n | data | 当前包数据。
|
设备 -> App
p_data | 字节数 | 标识符 | 说明 |
---|
[0] | 1 | type | 固定为 0。 | [1] | 1 | state | 升级状态。
00 :正常升级;
01 :包号异常;
02 :长度不一致;
03 :CRC 校验失败;
04 :其他异常。 |
3.2.5 OTA 升级结束
App -> 设备
p_data | 字节数 | 标识符 | 说明 |
---|
[0] | 1 | type | 固定为 0。 |
设备 -> App
p_data | 字节数 | 标识符 | 说明 |
---|
[0] | 1 | type | 固定为 0。 | [1] | 1 | state | 升级状态。
00 :升级成功;
01 :数据总长度错误;
02 :数据总 CRC 校验失败;
03 :其他异常。 |
四. OTA 实现
4.1 模组 OTA 移植
如果您使用自研模组进行开发,则需要在 涂鸦蓝牙 SDK 基础上,根据前文介绍的 OTA 协议完成模组 OTA 功能的开发,您可以在 涂鸦 IoT 平台 上下载任意的 涂鸦蓝牙模组 SDK 作为参考,或参考下文内容完成 OTA 程序移植。
4.1.1 入口函数
初始化
在 SDK 初始化之后添加 OTA 初始化函数。
void tuya_ble_sdk_demo_init(void)
{
tuya_ble_sdk_init(&tuya_ble_device_param);
tuya_ble_callback_queue_register(tuya_ble_sdk_callback);
tuya_ble_ota_init();
}
以 TLSR825x 平台为例,OTA 初始化函数如下:
uint32_t tuya_ble_ota_init(void)
{
s_old_file = &s_old_file_info;
tuya_ble_ota_info_load();
s_ota_state = TUYA_BLE_OTA_STATE_UNKNOWN;
return 0;
}
Callback
应用需通过注册的回调函数(无 RTOS 环境下)或者注册的接收队列(RTOS 环境下)接收 OTA 数据,事件 ID 为 TUYA_BLE_CB_EVT_OTA_DATA ,在该事件下添加 OTA 数据处理函数。
static void tuya_ble_sdk_callback(tuya_ble_cb_evt_param_t* event)
{
switch(event->evt) {
case TUYA_BLE_CB_EVT_OTA_DATA: {
tuya_ble_ota_handler(&event->ota_data);
} break;
}
以 TLSR825x 平台为例,OTA 数据处理函数如下:
void tuya_ble_ota_handler(tuya_ble_ota_data_t *ota)
{
tuya_ble_ota_response_t rsp;
rsp.type = ota->type;
switch(ota->type) {
case TUYA_BLE_OTA_REQ: {
tuya_ble_ota_req_handler(ota->p_data, ota->data_len, &rsp);
} break;
case TUYA_BLE_OTA_FILE_INFO: {
tuya_ble_ota_file_info_handler(ota->p_data, ota->data_len, &rsp);
} break;
case TUYA_BLE_OTA_FILE_OFFSET_REQ: {
tuya_ble_ota_file_offset_handler(ota->p_data, ota->data_len, &rsp);
} break;
case TUYA_BLE_OTA_DATA: {
tuya_ble_ota_data_handler(ota->p_data, ota->data_len, &rsp);
} break;
case TUYA_BLE_OTA_END: {
tuya_ble_ota_end_handler(ota->p_data, ota->data_len, &rsp);
} break;
case TUYA_BLE_OTA_UNKONWN: {
} break;
default: {
} break;
}
}
4.1.2 API 说明
tuya_ble_ota_response
位于:tuya_ble_api.h
函数名 | tuya_ble_ota_response |
---|
函数原型 | tuya_ble_status_t tuya_ble_ota_response(tuya_ble_ota_response_t *p_data); | 功能概述 | OTA 响应指令 | 参数 | p_data [in]:响应数据 ( 数据格式参考 3.2 交互协议 ) | 返回值 | TUYA_BLE_SUCCESS :发送成功
TUYA_BLE_ERR_INVALID_STATE :状态错误
TUYA_BLE_ERR_INVALID_LENGTH :数据长度错误
TUYA_BLE_ERR_NO_MEM :内存申请失败
TUYA_BLE_ERR_INTERNAL :其他错误 | 备注 | 位于 tuya_ble_api.h 文件中,在 OTA 接收数据处理后调用,以向 App 发送响应信息 |
4.2 MCU OTA 移植
如果模组 SDK 中的串口通信方案无法满足您的需求,您可以参考下文来移植自定义方案的 MCU OTA 处理程序。
4.2.1 OTA 请求
APP 向模组发送 OTA 请求数据时:
- 会触发 SDK 中定义的蓝牙命令事件,处理函数为
tuya_ble_evt_process ; - 如果接收到的是 OTA 相关命令,则执行 OTA 请求数据处理函数
tuya_ble_handle_ota_req ; - 如果是 MCU OTA 任务,则执行 MCU OTA 请求数据处理函数
tuya_ble_uart_common_mcu_ota_data_from_ble_handler ,该函数就是 向 MCU 发送 OTA 请求数据 的处理函数,您可以参考函数中的内容,将通信部分替换为自己的通信协议和接口即可。
static void tuya_ble_handle_ota_req(uint16_t cmd, uint8_t*recv_data, uint32_t recv_len)
{
if (recv_data[13] == 1) {
tuya_ble_uart_common_mcu_ota_data_from_ble_handler(cmd, &recv_data[14], data_len-1);
}
}
void tuya_ble_evt_process(uint16_t cmd, uint8_t*recv_data, uint32_t recv_len)
{
switch (cmd) {
case FRM_OTA_START_REQ:
case FRM_OTA_FILE_INFOR_REQ:
case FRM_OTA_FILE_OFFSET_REQ:
case FRM_OTA_DATA_REQ:
case FRM_OTA_END_REQ:
tuya_ble_handle_ota_req(cmd, recv_data, recv_len);
break;
}
}
4.2.2 OTA 响应
MCU 向模组返回 OTA 响应数据时:
- 会触发 SDK 中定义的串口命令事件,串口通用命令处理函数为
tuya_ble_uart_common_process ; - 如果接收到的是 OTA 相关命令,则执行 MCU OTA 响应数据处理函数
tuya_ble_uart_common_mcu_ota_data_from_uart_handler ,该函数就是 向 APP 发送 OTA 响应数据 的入口函数,您可以参考函数中的内容和自定义的通信协议来替换数据解析部分的代码,并在您的 MCU 数据接收函数中调用这部分内容; - 数据发送的接口函数为
tuya_ble_commData_send ,按要求传入OTA 响应数据即可。
void tuya_ble_uart_common_process(uint8_t *p_in_data, uint16_t in_len)
{
switch (cmd) {
case TUYA_BLE_UART_COMMON_MCU_OTA_REQUEST:
case TUYA_BLE_UART_COMMON_MCU_OTA_FILE_INFO:
case TUYA_BLE_UART_COMMON_MCU_OTA_FILE_OFFSET:
case TUYA_BLE_UART_COMMON_MCU_OTA_DATA:
case TUYA_BLE_UART_COMMON_MCU_OTA_END:
tuya_ble_uart_common_mcu_ota_data_from_uart_handler(cmd,data_buffer, data_len);
break;
};
}
上一篇: 第 5 课:应用开发 下一篇: Comming soon…
|