TuyaOS
OTA

涂鸦 TuyaOS 支持对 Powered by Tuya 设备的固件进行升级。在涂鸦IoT平台上创建的产品,可以上传固件、绑定升级通道 OTA channel ,每一个通道对应着设备上的一个可以进行版本升级的固件或者是文件、配置,开发者通过对该固件、文件或配置进行配置升级,然后推送到设备上。设备在接收到升级通知的时候,会去涂鸦IoT平台上获取对应通道所绑定的升级版本固件、文件或者配置,在本地进行校验、写入更新,然后上报新的版本号,完成升级过程。

涂鸦 TuyaOS 的升级操作主要分为以下几种类型:

升级类型 描述
静默升级 设备在启动之时以及每运行6小时,都会自动向IoT平台请求检测是否有静默升级任务。
如果存在静默升级任务,则进入升级流程进行升级;
如果不存在静默升级任务,则再等待6个小时重新检测。
静默升级中打开设备面板,会有进度框显示,并且无法操作设备。
提醒升级 用户手机APP首次打开设备面板时,会收到升级提醒弹框,可以选择升级或者不升级。
如果用户选择升级,则会推送升级任务到设备,设备并获取升级任务,进入升级流程,并上报升级进度。设备面板显示升级进度,并且无法操作设备。
检测升级 用户手机APP打开设备面板,点击点击右上角进入设备信息界面,检测设备固件版本,主动更新。
如果检测到有新的固件版本,则会推送升级任务到设备,设备并获取升级任务,进入升级流程,并上报升级进度。设备面板显示升级进度,并且无法操作设备。
强制升级 用户手机APP首次打开设备面板时,会收到升级提醒弹框,只有确定可以选择,无法取消。
用户选择升级,则会推送升级任务到设备,设备并获取升级任务,进入升级流程,并上报升级进度。设备面板显示升级进度,并且无法操作设备。

OTA Channel

OTA Channel 是涂鸦 TuyaOS 对设备内部可以进行升级的固件、文件或者配置进行升级通道绑定的一种机制,主要分为两种,分别为系统 OTA channel 和扩展 OTA Channel

  • 系统 OTA channel:涂鸦 TuyaOS 使用的,固化的一些通道,用于指定类型的固件升级使用, DEV_NM_ATH_SNGL是主联网固件,即当前模组的固件;DEV_NM_NOT_ATH_SNGLMCU固件。
  • 扩展 OTA Channel:涂鸦 TuyaOS 定义的,开发者可以根据自己的实际需求进行规划使用,在使用的时候,需要在初始化上报GW_ATTACH_ATTR_T,升级的时候在平台上配置对应OTA Channel的升级固件。
名称 通道号 描述
DEV_NM_ATH_SNGL 0 系统 OTA Channel,主联网固件升级通道
DEV_BLE_SNGL 1 系统 OTA Channel,蓝牙固件升级通道
DEV_ZB_SNGL 3 系统 OTA Channel,ZigBee固件升级通道
DEV_NM_NOT_ATH_SNGL 9 系统 OTA Channel,MCU固件升级通道
DEV_ATTACH_MOD_x >=10 扩展 OTA Channel,开发者自定义的升级通道

在升级过程中,涂鸦 TuyaOS 在获取到升级信息之后,会对升级通道进行一个检查。如果升级通道是主联网固件升级通道,即 DEV_NM_ATH_SNGL,涂鸦 TuyaOS 会自行处理;如果升级通道是其他的系统 OTA Channel 或者是扩展的 OTA Channel,则会调用开发者在初始化时候提供的 TY_IOT_CBS_S 里的回调函数 gw_ug_cb。在这个回调函数里,开发者可以根据里面的 tp 来判断是什么样的 OTA Channel,然后进行区分处理。

一些客户对主联网固件的升级也有特殊的定制需求,可以通过一个接口 tuya_svc_upgrade_register_pre_cb 进行定制,只要注册一个 NULL 指针即可。

除了回调函数之外,OTA的其他功能不需要用于进行编码开发,涂鸦 TuyaOS 直接提供相关的能力。

接口描述

升级前置通知

typedef INT_T(*DEV_UPGRADE_INFORM_CB)(CONST FW_UG_S *fw);
tuya sdk ota firmware info
Definition: tuya_cloud_com_defs.h:663
INT_T(* DEV_UPGRADE_INFORM_CB)(CONST FW_UG_S *fw)
Handler of GW upgrade inform
Definition: tuya_svc_upgrade.h:76

在接受到升级推送消息之后,需要确认当前设备是否满足升级条件,如果不满足条件,如处于特别的繁忙、低电量等状态,可以不进行升级操作。此接口是在设备初始化提供的一组回调中,如果不提供则认为不需要做通知,默认可以进行升级。

升级通知

typedef INT_T(*SUBDEV_UPGRADE_INFORM_CB)(CONST CHAR_T *dev_id, CONST FW_UG_S *fw);
INT_T(* SUBDEV_UPGRADE_INFORM_CB)(CONST CHAR_T *dev_id, CONST FW_UG_S *fw)
Handler of sub-device upgrade inform
Definition: tuya_svc_upgrade.h:88

在确认可以进入升级之后,会从云端获取对应的升级信息,如果升级的内容是主联网固件DEV_NM_ATH_SNGL,会自行处理,如果是其他的,如DEV_NM_NOT_ATH_SNGL,则通过此回调来通知应用层,进行相应的准备之后,在此接口内启动升级。此接口是在设备初始化提供的一组回调中,如果不提供则认为不需支持模组固件之外的对象OTA

启动升级

OPERATE_RET tuya_svc_upgrade_start(CONST CHAR_T *dev_id,
CONST FW_UG_S *fw,
CONST GET_FILE_DATA_CB get_file_cb,
VOID *pri_data,
CONST UPGRADE_NOTIFY_CB upgrd_nofity_cb,
CONST BOOL_T upload_upgrade_percent,
CONST UINT_T download_buf_size);
OPERATE_RET tuya_svc_upgrade_start(CONST CHAR_T *dev_id, CONST FW_UG_S *fw, CONST GET_FILE_DATA_CB get_file_cb, VOID *pri_data, CONST UPGRADE_NOTIFY_CB upgrd_nofity_cb, CONST BOOL_T upload_upgrade_percent, CONST UINT_T download_buf_size)
Start to download the specific firmware

此接口用于在开发者提供的 gw_ug_cb 里启动OTA流程。接口里需要提供 get_file_cb,用于获取文件内容写入flash,upgrd_notify_cb 用于通知升级结束或者失败。

注册升级回调pre_cb

VOID(* DEV_UPGRADE_PRE_INFORM_CB)(BOOL_T *handled, CONST FW_UG_S *fw)
Handler of pre-process inform
Definition: tuya_svc_upgrade.h:81
VOID tuya_svc_upgrade_register_pre_cb(DEV_UPGRADE_PRE_INFORM_CB pre_ug_cb)
Register pre-precess handler to replace the default one

此接口用于注册升级回调之前的特殊操作,涂鸦 TuyaOS 内部默认有一个 pre_cb,在内部的 pre_cb 里,涂鸦 TuyaOS 处理了主联网固件(模组自身固件)的升级逻辑。如果需要关闭此逻辑,把主联网固件(模组自身固件)的升级也放到开发者在初始化时候提供的回调函数 gw_ug_cb 里处理,可以通过 tuya_svc_upgrade_register_pre_cb(NULL) 来替换涂鸦 TuyaOS 内部的默认 pre_cb;如果希望使用一个新的handler来处理主联网固件(模组自身固件)可以通过 tuya_svc_upgrade_register_pre_cb(new_pre_ug_cb) 来替换涂鸦 TuyaOS 内部的默认 pre_cb

非主联网固件升级结果上报

OPERATE_RET tuya_svc_upgrade_result_report(CONST CHAR_T *dev_id, CONST DEV_TYPE_T type, CONST int result);
OPERATE_RET tuya_svc_upgrade_result_report(CONST CHAR_T *dev_id, CONST DEV_TYPE_T type, CONST int result)
Sync dowload result to cloud

此接口用于固件升级结果上报,用于处理非主联网固件升级结果,如果不调用此接口,云端和APP通过超时时间也可以得到升级的结果,但需要较长时间的等待。

非主联网固件拒绝升级

OPERATE_RET tuya_svc_upgrade_refuse(CONST FW_UG_S *fw, CONST CHAR_T *dev_id);
OPERATE_RET tuya_svc_upgrade_refuse(CONST FW_UG_S *fw, CONST CHAR_T *dev_id)
Refuse to download the specific firmware

此接口用于固件升级过程中强制停止,告知云端和APP升级失败,并将设备状态重置为EXT_NORMAL_S状态。在强制停止升级的时候,也需要在应用侧停止升级下载逻辑,以避免影响其他功能。主联网固件升级结果,不需要应用层处理。

使用示例

此示例实现了MCU固件的OTA处理逻辑,忽略了其他TY_IOT_CBS_S回调函数的实现。

STATIC TUYA_UART_BASE_CFG_T sg_uart_cfg = {
.baudrate = 115200,
.databits = TUYA_UART_DATA_LEN_8BIT,
.flowctrl = TUYA_UART_FLOWCTRL_NONE,
.parity = TUYA_UART_PARITY_TYPE_NONE,
.stopbits = TUYA_UART_STOP_LEN_1BIT
};
STATIC THREAD_CFG_T sg_task = {
.priority = TASK_OTA_PRIORITY,
.stackDepth = TASK_OTA_SIZE,
.thrdname = "ota"
};
STATIC THREAD_HANDLE sg_ota_handle;
#define tuya_iot_upgrade_gw(fw, get_file_cb, upgrd_nofity_cb, pri_data) \
tuya_iot_upgrade_gw_notify(fw, get_file_cb, upgrd_nofity_cb, pri_data, TRUE, 0)
OPERATE_RET get_file_data_cb(IN CONST FW_UG_S *fw, IN CONST UINT_T total_len, IN CONST UINT_T offset,
IN CONST BYTE_T *data, IN CONST UINT_T len, OUT UINT_T *remain_len, IN PVOID_T pri_data)
{
PR_DEBUG("--------------Rev File Data-----------");
PR_DEBUG("Total len:%u", total_len);
PR_DEBUG("Offset:%u , len:%u", offset, len);
/* send data to mcu*/
tkl_uart_write(UART_NUM_0, data, len);
*remain_len = 0;
return OPRT_OK;
}
VOID upgrade_notify_cb(IN CONST FW_UG_S *fw, IN CONST INT_T download_result, IN PVOID_T pri_data)
{
PR_DEBUG("------------------upgrade notify callback-------------------");
if (download_result != 0) {
PR_ERR("file download fail!");
} else {
PR_DEBUG("file download success!");
/*report result*/
tuya_iot_dev_upgd_result_report(NULL, DEV_NM_NOT_ATH_SNGL, TUS_UPGRADE_SUCCESS);
}
return;
}
STATIC INT_T __soc_dev_rev_upgrade_info_cb(IN CONST FW_UG_S *fw)
{
OPERATE_RET op_ret = OPRT_OK;
UCHAR_T * fd_version = NULL;
UCHAR_T * fd_upgrade = NULL;
PR_DEBUG("MCU Rev Upgrade Info");
PR_DEBUG("fw->tp:%d", fw->tp);
PR_DEBUG("fw->fw_url:%s", fw->fw_url);
PR_DEBUG("fw->fw_hmac:%s", fw->fw_hmac);
PR_DEBUG("fw->sw_ver:%s", fw->sw_ver);
PR_DEBUG("fw->file_size:%u", fw->file_size);
if(DEV_NM_NOT_ATH_SNGL != fw->tp) {
PR_ERR("not mcu OTA!");
return OPRT_COM_ERROR;
}
return tuya_iot_upgrade_gw(fw, get_file_data_cb, upgrade_notify_cb, NULL);
}
OPERATE_RET __soc_device_init(VOID_T)
{
OPERATE_RET op_ret = OPRT_OK;
TY_IOT_CBS_S iot_cbs = {0};
// Initialize TuyaOS product information
iot_cbs.pre_gw_ug_cb = __soc_dev_rev_upgrade_info_cb;
op_ret = tuya_iot_wf_soc_dev_init(GWCM_OLD, WF_START_AP_FIRST, &iot_cbs, PID, USER_SW_VER);
if (OPRT_OK != op_ret) {
PR_ERR("tuya_iot_wf_soc_dev_init err:%d", op_ret);
return -1;
}
op_ret = tkl_uart_init(UART_NUM_0, &sg_uart_cfg);
if(OPRT_OK != op_ret) {
PR_ERR("err<%d>,Uart init fail!", op_ret);
return;
}
return 0;
}
STATIC VOID_T user_main(VOID_T)
{
OPERATE_RET rt = OPRT_OK;
TY_INIT_PARAMS_S init_param = {0};
init_param.init_db = TRUE;
strcpy(init_param.sys_env, TARGET_PLATFORM);
TUYA_CALL_ERR_LOG(tuya_iot_init_params(NULL, &init_param));
// Initialization LWIP
__soc_device_init();
return;
}
VOID __ota_task(VOID* param)
{
while (1) {
PR_DEBUG(USER_SW_VER);
}
}
VOID_T tuya_app_main(VOID_T)
{
OPERATE_RET op_ret = OPRT_OK;
user_main();
op_ret = tal_thread_create_and_start(&sg_ota_handle, NULL, NULL, __ota_task, NULL, &sg_task);
if(OPRT_OK != op_ret) {
PR_ERR("err<%d>,ota task create fail", op_ret);
return;
}
return;
}
void TUYA_LwIP_Init(void)
tuya lwip init
thread parameters
Definition: tal_thread.h:73
Definition of TUYA DevOS init param
Definition: tuya_cloud_com_defs.h:163
CHAR_T sys_env[20]
Definition: tuya_cloud_com_defs.h:167
BOOL_T init_db
Definition: tuya_cloud_com_defs.h:165
Definition of gateway callback funtions
Definition: tuya_cloud_com_defs.h:807
GW_UG_INFORM_CB pre_gw_ug_cb
Definition: tuya_cloud_com_defs.h:827
VOID_T tal_system_sleep(UINT32_T time_ms)
This API is used for system sleep.
OPERATE_RET tuya_iot_init_params(IN CONST CHAR_T *fs_storge_path, IN CONST TY_INIT_PARAMS_S *p_param)
TuyaOS system service init
OPERATE_RET tuya_iot_dev_upgd_result_report(IN CONST CHAR_T *dev_id, IN CONST DEV_TYPE_T type, IN CONST INT_T result)
tuya_iot_dev_upgd_result_report