更新时间:2024-11-20 08:51:32下载pdf
为了更高效的进行产品功能开发,涂鸦提供了一套标准的应用模版,应用模版包含了设备激活、网络重置、按键功能、授权、校准检测、串口配置、GPS 功能和 DP 数据通信等基础功能模块使用的详细说明,您可以基于应用模版快速完成底层基础能力的使用,专注于开发自身应用代码,更快完成产品的开发。
应用模板包含在 OpenCPU SDK 内,您在获取到 SDK 后,解压开发后即可看到应用模板的代码(apps 文件夹下的 template 文件包)。
apps 文件夹下的应用模版文件结构说明如下:
文件夹名称 | 作用 |
---|---|
template | OpenCPU 应用模板,帮助快速开发上层代码 |
emobile | 应用模板开发实例,通过应用模板开发的样例代码 |
hello | 基础示例代码 |
文件名称 | 作用 |
---|---|
tuya_device.h | 存放除 tuya_dp.c 外其他 .c 的结构体,宏定义,产品信息及公共头文件等 |
tuya_dp.h | 存放 tuya_dp.c 的函数定义, DP 定义,产品 PID 等 |
tuya_application.c | 存放应用开发的代码,具体的功能实现 |
tuya_device.c | 存放应用开发前的一些配置信息及应用开发的入口函数 |
tuya_dp.c | 存放 dp 相关的代码,包括下发 dp 及上报 dp 的接口函数 |
tuya_mf_product.c | 存放产测入口函数 |
将开发者平台下载的功能点调试文件放在 SDK 目录下合适的文件夹中,之后在 apps 同级目录下通过命令行参数将功能调试文件及 DP 的头文件及 .c 文件的路径传参给执行文件,自动生成 DP 功能点的相关函数及定义。
此处示例将测试的 json 文件放在了 template 文件夹下,所以执行的指令如下:
./tools/tuya_dp_tool/tuya_dp_tool ./apps/template/template.json ./apps/template/include/tuya_dp.h ./apps/template/src/tuya_dp.c
然后在 SDK 目录下执行./build.sh
指令,生成对应的应用固件。
在介绍具体的功能前,您需要先了解应用程序启动流程,如下图所示
系统完成应用程序加载后,先调用 appimg_enter
函数,用户可以在该函数中完成一系列函数注册,各注册函数说明如下:
user_pre_app_init
函数,user_pre_app_init
函数实现产测之前 app 需要完成的一些初始化动作,如果没有,可以不调用该注册函数,允许用户不实现user_pre_app_init
。user_pre_device_init
函数,user_pre_device_init
函数实现产测之前 device 需要完成的一些初始化动作,如果没有,可以不调用该注册函数,允许用户不实现user_pre_device_init
。mf_init
传入系统使用,如果不调用该函数,则固件信息默认为系统固件名称和版本号。user_app_init
函数,user_app_init
函数实现产测之后 app 需要完成的一些初始化动作,如果没有,可以不调用该注册函数,允许用户不实现user_app_init
。user_device_init
函数,user_device_init
函数实现产测之后 device 需要完成的一些初始化动作,如果没有,可以不调用该注册函数,允许用户不实现user_device_init
。user_main_entry
函数,user_main_entry
函数为应用程序入口,通过该入口,完成应用程序功能启动。用户必须实现user_main_entry
函数,并调用注册函数完成注册。系统启动后,通过 user_pre_app_init > user_pre_device_init > mf_init > user_app_init > user_device_init > user_main_entry
完成整个应用程序的初始化过程。
在设备激活前需要设置 apn,否则设备激活将会失败。设置 apn 的函数在 tuya_device.c 文件中,代码示例如下:
/**
* @brief 应用初始化前置准备工作
*
* @return VOID_T
*
* @note 在此函数中,应用可以执行一些产测前需要完成的配置设置、初始化或者具体功能操作无关的工作
*
* @note 应用必须对其进行实现,如果不需要,则实现空函数。
*/
VOID_T user_pre_app_init(VOID_T)
{
/* 如果当前没有 apn,系统自动设置 */
tuya_cellular_srv_mds_pdp_active(NULL,NULL,NULL);
PR_NOTICE("user_pre_app_init succ");
}
在 ``` tuya_application.c ``` 中通过 ``` tuya_iot_cat1_opencpu_dev_init``` 函数接口将固件及产品信息注册给底层。
/**
* @brief 设备激活/网络状态监测
*
* @param VOID
*
* @return 0 成功 -1 失败
*/
OPERATE_RET tuya_device_init()
{
...
op_ret = tuya_iot_cat1_opencpu_dev_init(&wf_cbs, g_dev_info.firware_key, g_dev_info.prod_key, g_dev_info.sys_fw_ver, g_dev_info.app_fw_ver, mcu_sw_ver);
if (OPRT_OK != op_ret)
{
PR_ERR("tuya_device_init err:%d", op_ret);
return op_ret;
}
...
return op_ret;
}
设备只有经过授权及校准后才能正常使用,您在运行应用代码前,需要确保这两项已经完成,此处在代码中增加了授权及校准检测功能,以及异常 Log 打印,用于检测设备是否正常完成授权和校验,代码实现如下:
OPERATE_RET tuya_device_init()
{
...
/* 授权,校准校验,未授权,返回 error */
if(0 == get_gw_cntl()->gw_base.auth_key[0] || (0 == get_gw_cntl()->gw_base.uuid[0]))
{
PR_DEBUG("please write uuid and auth_key first");
return OPRT_COM_ERROR;
}
...
bool ret = tuya_hal_cellular_calibrated();
if(!ret)
{
PR_DEBUG("Not calibrated ret/%d", ret);
return OPRT_COM_ERROR;
}
lan_pro_cntl_disable();
return op_ret;
}
应用模板以 UART1 为例增添了 UART 的初始化及配置功能,具体的串口业务需要您自行实现。
/**
* @brief 串口初始化函数
*
* @param VOID
*
* @return 0 成功 -1 失败
*
* @note uart 参数配置,如果需要 uart 串口功能,必须先初始化配置
*/
OPERATE_RET uart_init(VOID)
{
OPERATE_RET op_ret;
demo_uart = tuya_driver_find(TUYA_DRV_UART, TY_UART_INDEX);
if (NULL == demo_uart)
{
PR_ERR("uart1 find failed");
return OPRT_COM_ERROR;
}
//配置串口参数
TUYA_UART_8N1_CFG(demo_uart, 115200, 1024, 0);
//初始化
op_ret = tuya_uart_init(demo_uart);
if (OPRT_OK != op_ret)
{
PR_ERR("uart init failed", op_ret);
return OPRT_COM_ERROR;
}
return op_ret;
}
SDK 内包含了 GPS 使能和 GPS 速度/经纬度/信号强度等的获取功能,由于 LZ211 模组与 LZ201 模组关于 GPS 的功能有些许差异,因此通过宏定义(TY_MODULE_TYPE)来区分使用,您需要根据自己使用的模组进行配置,SDK 内默认 TY_MODULE_TYPE
为 0xFF 时,不论 LZ201 模组还是 LZ211 模组,都不启动 GPS,当需要启动 GPS 功能时,需要根据当前模组类型设置 TY_MODULE_TYPE
,并重启设备,示例如下:
#define TY_MODULE_TYPE 0xFF // 0:"LZ201"; 1:"LZ211"
详细的代码实现,您可以参考 SDK 中应用模板的代码。
您可以通过定时器定时获取电池电量及模组信号强度,并调用上报接口上报到云端用于显示,示例代码如下:
STATIC VOID send_battery_info_msg(UINT_T timerID, PVOID_T pTimerArg)
{
PR_DEBUG("enter get_gps_info");
tuya_msg_send(PROC_BAT_EVENT,1);
}
OPERATE_RET tuya_add_timer_func(VOID)
{
OPERATE_RET op_ret = OPRT_OK;
...
/* 获取电池电量/信号强度 */
op_ret = sys_add_timer(send_battery_info_msg, NULL, &ty_msg.get_battery_info_timer);
if(OPRT_OK != op_ret)
{
return op_ret;
}
return op_ret;
}
STATIC VOID ty_msg_proc(VOID *param)
{
UCHAR_T option = 0;
OPERATE_RET ret = OPRT_OK;
while(1)
{
ret = tuya_hal_queue_fetch(ty_msg.pQueue_event, &option, 1000);
if (ret == OPRT_OK)
{
...
if(option == PROC_BAT_EVENT)
{
UCHAR_T battery_val;
SCHAR_T rssi;
/*电量处理*/
tuya_cellular_battery_get_rsoc(&battery_val);
/*获取当前 gsm 信号强度等级*/
tuya_hal_cellular_get_rssi(&rssi);
}
}
}
}
在 tuya_device_init
函数中,注册了 DP 数据处理的回调函数 tuya_obj_dp_cmd_cb
,当 App 接收到数据时,tuya_obj_dp_cmd_cb
调用 ty_obj_datapoint_proc
对接收到的 DP 数据处理,并执行相关操作,然后调用 上报函数将状态返回给 App 用于展示。
OPERATE_RET tuya_device_init()
{
OPERATE_RET op_ret = OPRT_OK;
/* 设备激活,dp 回调函数注册,网络状态监测 */
TY_IOT_CBS_S wf_cbs = {
.dev_obj_dp_cb = tuya_obj_dp_cmd_cb,
.dev_raw_dp_cb = tuya_raw_dp_cmd_cb,
.dev_dp_query_cb = tuya_dp_query_cb,
};
...
return op_ret;
}
/**
* @brief raw 型 dp 处理函数
*
* @param dp
*
* @return 无
*
* @note 如无需要,可不实现
*/
VOID tuya_obj_dp_cmd_cb(IN CONST TY_RECV_OBJ_DP_S *dp)
{
PR_DEBUG("app_obj_dp_cmd_cb ... ...");
if (!get_gw_dev_cntl()->dev_if.bind)
{
PR_ERR("dev is not bind");
return;
}
if (!dp || dp->dps_cnt == 0)
{
PR_ERR("no valid dp");
return;
}
INT_T i = 0;
//解析云端下发的 DP
for (;;)
{
tuya_obj_datapoint_proc((TY_OBJ_DP_S *)&dp->dps[i]);
if (++i < dp->dps_cnt)
tuya_hal_system_sleep(40);
else
break;
}
}
/**
* @brief obj 型 dp 处理函数
*
* @param obj_dp
*
* @return 无
*
* @note 如无需要,可不实现
*/
STATIC VOID tuya_obj_datapoint_proc(TY_OBJ_DP_S *obj_dp)
{
switch(obj_dp->dpid)
{
//该处代码属于自动生成部分
[TEMPLATE_DP_HANDLE]
default:
break;
}
}
应用模板中的 tuya_dp.c
及 tuya_dp.h
两部分代码,为涂鸦提供的自适应生成代码,您拿到应用模板代码进行编译后,会在 tuya_dp.c/tuya_dp.h
中生成对应的接口函数,包括 DP 的定义、PID 的定义,以及下发/上报 DP 的处理函数,此处以部分 DP 定义的自动生成为例。
生成前:
生成后:
此章节以智能电动车为例,使用 OpenCPU SDK 及应用模板进行开发,演示应用模板使用过程,本实例在应用模板现有功能的基础上添加了单/多 DP 上报处理及固件升级的内容。
以下是关于实例代码的介绍设备与涂鸦云端交互的逻辑主要概括如下:
tuya_dp.h
文件中自动生成 DP 信息。tuya_dp.h
中通过宏定义写入 PID 信息。在 SDK 目录下将 template 文件夹(应用模板的代码)创建一个副本,重命名为自己创建的产品名称。由于该实例产品名为 emobile,所以本例中将副本文件夹命名为 emobile。
如果想要清除生成的产品固件,在 SDK 目录下执行如下命令:
./build.sh clean
选择对应的 target 数字,如下图所示:
如果不想要清除生成的固件,可以跳过该步骤,并不会产生其他的影响,执行固件编译时会将老的固件覆盖掉。
将下载的功能点调试文件放入自己创建的对应产品目录下。由于本例中创建的产品对应目录为 emobile,所以此处将 JSON 文件放在 OpenCPU SDK 目录的 /apps/emobile/ 下,如下图所示:
在 SDK apps 同级目录下执行如下命令,生成 DP 相关信息:
./tools/tuya_dp_tool/tuya_dp_tool ./apps/emobile/emobile.json ./apps/emobile/include/tuya_dp.h ./apps/emobile/src/tuya_dp.c
进入 tuya_dp.h/tuya_dp.c
函数查看 DP 相关信息是否已生成。
执行编译指令./build.sh emobile 0.0.1
,编译应用代码,参数 emobile 为编译目标代码,0.0.1 为指定的生成版本号
主动上报业务不论单个 DP 还是多个 DP 组合上报,依赖于定时器函数,通过定时器函数来按需定时上报。
/**
* @brief 用于添加定时器的接口函数
*
* @param VOID
*
* @return 0 成功 其他 失败
*/
OPERATE_RET tuya_add_timer_func(VOID)
{
OPERATE_RET op_ret = OPRT_OK;
...
/* 获取电池电量/信号强度 */
op_ret = sys_add_timer(send_battery_info_msg, NULL, &ty_msg.get_battery_info_timer);
if(OPRT_OK != op_ret)
{
return op_ret;
}
return op_ret;
}
/**
* @brief 启动对应定时器
*
* @param VOID
*
* @return 0 成功 其他 失败
*/
OPERATE_RET ty_start_timer_func(VOID)
{
OPERATE_RET op_ret = OPRT_OK;
...
op_ret = sys_start_timer(ty_msg.get_battery_info_timer, BAT_INFO_REPORT_INTERVAL, TIMER_CYCLE);
if (op_ret != OPRT_OK)
{
PR_DEBUG("bat info report timer start failed!");
return op_ret;
}
return op_ret;
}
单个 DP 的上报,只需要将状态发生变化的 dpid 对应的状态值,通过接口函数上报即可,以 value 类型为例,示例代码如下:
/**
* @brief 具体的事件处理线程
*
* @param param 线程参数可为空
*
* @return 无
*/
STATIC VOID ty_msg_proc(VOID *param)
{
UCHAR_T option = 0;
OPERATE_RET ret = OPRT_OK;
while(1)
{
ret = tuya_hal_queue_fetch(ty_msg.pQueue_event, &option, 1000);
if (ret == OPRT_OK)
{
PR_DEBUG("ty_msg_proc option = %d", option);
if (option == PROC_GPS_EVENT)
{
DWORD_T speed = ty_gps_get_speedinfo();
...
tuya_dp_value_update(DPID_SPEED, speed);
...
}
...
}
}
}
/**
* @brief 将 dpid 及对应的值传给上报函数
*
* @param dpid:创建产品时添加 dp 功能对应的 dpid
* @param dp_value: DP 处理完成后的值
*/
VOID tuya_dp_value_update(UCHAR_T dpid, INT_T dp_value)
{
TY_OBJ_DP_S dp;
memset(&dp, 0, sizeof(dp));
switch(dpid)
{
case DPID_SPEED:
{
dp.dpid = dpid;
dp.type = PROP_VALUE;
dp.value.dp_value = dp_value;
OPERATE_RET op_ret = dev_report_dp_json_async(get_gw_cntl()->gw_if.id, &dp, 1);
if(OPRT_OK != op_ret)
{
PR_ERR("dev_report_dp_json_async op_ret:%d",op_ret);
}
}
break;
default:
break;
}
}
多个 DP 组合为一包数据上报时,需要将其按如下结构体进行封装:
typedef struct {
UINT_T dps_cnt;
TY_OBJ_DP_S *dps;
} TY_DP_DATA_S;
具体代码实现以电池电量及模组信号强度为例如下:
/**
* @brief 具体的事件处理线程
*
* @param param 线程参数可为空
*
* @return 无
*/
STATIC VOID ty_msg_proc(VOID *param)
{
UCHAR_T option = 0;
OPERATE_RET ret = OPRT_OK;
while(1)
{
ret = tuya_hal_queue_fetch(ty_msg.pQueue_event, &option, 1000);
if (ret == OPRT_OK)
{
PR_DEBUG("ty_msg_proc option = %d", option);
...
if (option == PROC_BAT_EVENT)
{
Multiple_Dp_Report_Test(battery_val, rssi);
...
}
}
}
}
/**
* @brief 多 dp 组合一个包上报示例
*
* @param 无
*
* @return 无
*/
STATIC VOID Multiple_Dp_Report_Test(UCHAR_T battery_val, SCHAR_T rssi)
{
//示例代码如下
TY_DP_DATA_S multiple_dp;
OPERATE_RET ret;
multiple_dp.dps = tuya_hal_system_malloc(128);
multiple_dp.dps_cnt = 2;
multiple_dp.dps[0].dpid = DPID_BATTERY_PERCENTAGE;
multiple_dp.dps[0].type = PROP_VALUE;
multiple_dp.dps[0].value.dp_value = battery_val;
multiple_dp.dps[1].dpid = DPID_SIGNAL_STRENGTH;
multiple_dp.dps[1].type = PROP_VALUE;
multiple_dp.dps[1].value.dp_value = rssi;
if (ty_msg.netstatus == 4)
{
PR_DEBUG("enter Multiple_Dp_Report_Test");
ret = dev_report_dp_json_async(get_gw_cntl()->gw_if.id, multiple_dp.dps, multiple_dp.dps_cnt);
if (ret != OPRT_OK)
{
PR_DEBUG("dp report failed");
}
else
{
PR_DEBUG("dp report successed");
}
}
return;
}
扩展固件指您基于 OpenCPU SDK 开发编译的应用程序固件,扩展固件升级功能在系统固件中已经实现,您只需要在开发者平台上设置好升级策略,并上传升级固件。如设置为 App 提醒升级,云端会自动推送提示升级的消息,用户手机 App 可以根据需要选择是否升级。如下图所示:
点击更新会对当前所有需要更新的固件依次更新。
MCU 固件升级需要遵循涂鸦的串口协议传输,详细的实现如下所示:
串口协议。
协议格式如下:
帧头 + 版本 + 命令字 + data_len + data + 校验
函数实现。
MCU 固件升级的操作原理同扩展固件,设备与 MCU 之间的数据传输通过 UART 完成,下图为实现逻辑:
在使用 MCU 升级前需要先定 MCU_EXIST 宏 #define MCU_EXIST
,代码实现参考以下步骤。
OPERATE_RET uart_init(void)
{
OPERATE_RET op_ret;
...
#ifdef MCU_EXIST
op_ret = tuya_hal_mutex_create_init(&ty_msg.mutex);
if (op_ret != OPRT_OK)
{
PR_ERR("create mutex err");
return op_ret;
}
op_ret = tuya_hal_semaphore_create_init(&ty_msg.mcu_ug_sem, 0, 1);
if (op_ret != OPRT_OK)
{
PR_ERR("create semphore err");
return op_ret;
}
//创建串口数据监控线程
PR_DEBUG("creat uart_init uart recv task");
THRD_PARAM_S thrd_uart_param;
THRD_HANDLE ty_uart_handle = NULL;
thrd_uart_param.priority = TUYA_UART_MONITOR_TASK_PRIORITY;
thrd_uart_param.stackDepth = TUYA_UART_MONITOR_TASK_HEAP_SIZE;
thrd_uart_param.thrdname = "ty_uart_monitor_thread_task";
op_ret = CreateAndStart(&ty_uart_handle, NULL, NULL,
ty_uart_monitor_thread, NULL, &thrd_uart_param);
//启动获取 mcu 版本的定时器
sys_start_timer(ty_msg.get_cur_mcu_ver_timer, GET_MCU_VER_TIME,TIMER_CYCLE);
#endif
return op_ret;
}
OPERATE_RET device_init(void)
{
...
if (0 != tuya_cat1_register_mcu_fota_inform_hook(mcu_start_upgrade_cb))
{
PR_ERR("register mcu fota inform hook failed");
}
if (0 != tuya_cat1_register_mcu_fota_process_hook(mcu_upgrade_process_cb))
{
PR_ERR("register mcu fota process hook failed");
}
if(0 != tuya_cat1_register_mcu_fota_notify_hook(mcu_ug_complete_notify_cb))
{
PR_ERR("register mcu fota notify hook failed");
}
PR_NOTICE("register mcu fota hooks succ");
op_ret = tuya_iot_cat1_opencpu_dev_init(&wf_cbs, firmware_key,
product_key, sys_fw_ver,
app_fw_ver, mcu_sw_ver);
...
return op_ret;
}
注意:回调函数的注册一定要在
tuya_iot_cat1_opencpu_dev_init
函数之前完成。
通过以下代码实现回调函数。
单击 更新升级 后,模组会将文件大小及版本号通过 mcu_start_upgrade_cb
函数下发,该函数的主要功能是通知 MCU 准备升级。
void mcu_start_upgrade_cb(UINT_T fileSize, const CHAR_T* version, const CHAR_T* fwHMAC)
{
PR_NOTICE("mcu_start_upgrade_cb start to upgrade mcu firmware");
PR_NOTICE("fw_hmac: %s", fwHMAC);
PR_NOTICE("fw version: %s", version);
PR_NOTICE("fw fileSize: %d", fileSize);
tuya_hal_semaphore_post(ty_msg.mcu_ug_start_sem);
...
UCHAR_T buf[11] = {0x7E,0x81,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff};
ty_msg.mcu_file_size = fileSize;
buf[3] = 0x0A;
buf[5] = 0x04;
buf[6] = (fileSize >> 24) & 0xFF;
buf[7] = (fileSize >> 16) & 0xFF;
buf[8] = (fileSize >> 8) & 0xFF;
buf[9] = fileSize & 0xFF;
UCHAR_T num = getCheckSum(buf, 10);
buf[10] = num;
//通过 uart 写函数,发送开始数据给 mcu
tuya_uart_write(demo_uart, buf, sizeof(buf));
//考虑超时情况,如果超时,重新发送
sys_start_timer(ty_msg.mcu_ug_fw_timer, MCU_RECV_TIME * 1000, TIMER_ONCE);
...
return;
}
在 mcu_upgrade_process_cb
函数中完成对升级数据的处理与发送。
//demo 假定 uart 按 256 传输,该长度可配 512、1024 等,以实际开发为准
static void mcu_upgrade_process_cb(UINT_T offset, const BYTE_T* data, UINT_T len)
{
...
if (dev_proc.send_len + mcu_ug_buff_len + len >= dev_proc.file_size)
is_last_file_pack = TRUE; //是否为最后一包数据的标志位
copy_len = MIN(send_unit_len - mcu_ug_buff_len, len);
memcpy(&(buff[mcu_ug_buff_len]), data, copy_len);
mcu_ug_buff_len += copy_len;
data_offset = copy_len;
if (mcu_ug_buff_len == send_unit_len)
{
mcu_ug_buff_len = 0;
ret = upgrade_file_cb(buff, send_unit_len); //通过 uart 发送数据
if (OPRT_OK != ret)
{
PR_ERR("MCU upgrade file failed");
return;
}
dev_proc.send_len += send_unit_len;
}
while (data_offset + send_unit_len <= len)
{
ret = upgrade_file_cb(&(data[data_offset]), send_unit_len);
if (OPRT_OK != ret)
{
PR_ERR("MCU upgrade file failed");
return;
}
data_offset += send_unit_len;
dev_proc.send_len += send_unit_len;
}
if (is_last_file_pack)
{
if (mcu_ug_buff_len)
{
ret = upgrade_file_cb(buff, mcu_ug_buff_len);
mcu_ug_buff_len = 0;
if (OPRT_OK != ret)
{
PR_ERR("MCU upgrade file failed");
return;
}
dev_proc.send_len += mcu_ug_buff_len;
}
if (data_offset < len)
{
ret = upgrade_file_cb(&(data[data_offset]), len - data_offset);
if (OPRT_OK != ret)
{
PR_ERR("MCU upgrade file failed");
return;
}
dev_proc.send_len += len - data_offset;
}
PR_NOTICE("[MCU_FOTA]: upgrade mcu fw succ: %d, %d",
dev_proc.send_len, dev_proc.file_size);
}
else if (data_offset < len)
{
memcpy(buff, &(data[data_offset]), len - data_offset);
mcu_ug_buff_len = len - data_offset;
}
return;
}
在 mcu_ug_complete_notify_cb
函数中,该函数指示云端完成 MCU 升级固件的传输,应用程序可以在该函数中实现串口协议,通知 MCU 升级数据传输完毕。
static VOID mcu_ug_complete_notify_cb(INT_T result)
{
if (OPRT_OK == result)
{
...
PR_NOTICE("mcu upgrade succ");
return;
}
...
}
MCU 升级数据传输完毕后,MCU 完成将固件写入 Flash 的操作。然后需要通过串口,按照协议将新的 MCU 版本号传给应用程序,应用程序接收到新的版本号之后,通过调用 SDK 提供的 tuya_iot_cat1_update_mcu_ver
函数,将新的版本号上传至云端,从而通知手机 App,MCU 版本升级成功。
static INT_T ty_uart_data_proc(UCHAR_T* data)
{
OPERATE_RET op_ret = OPRT_OK;
UCHAR_T buf[] = {0};
TY_FRAME_S *ty_frame = (TY_FRAME_S *)data;
PR_DEBUG("frame type: %d", ty_frame->fr_type);
switch (ty_frame->fr_type)
{
...
case GET_MCU_VER:
{
dev_sw_ver = tuya_hal_system_malloc(11);
if (IsThisSysTimerRun(ty_msg.get_cur_mcu_ver_timer))
{
sys_stop_timer(ty_msg.get_cur_mcu_ver_timer);
}
buf[0] = ty_frame->data[0];
buf[1] = ty_frame->data[1];
buf[2] = ty_frame->data[2];
memset(dev_sw_ver, 0, 11);
sprintf(dev_sw_ver, "%d.%d.%d", buf[0], buf[1], buf[2]);
PR_DEBUG("ty_uart_monitor_thread mcu_ug_ver: %s\r\n", dev_sw_ver);
if (!ty_msg.init_flag)
{
ty_msg.init_flag = 1;
//与云端连接实现
op_ret = device_init();
if (OPRT_OK != op_ret)
{
PR_ERR("app_init_entry device_init err: %d", op_ret);
return op_ret;
}
}
//更新升级后的 mcu 版本号至云端 .ty_msg.mcu_ug_ctrl 在升级数据传输结束后置为 1
else if (ty_msg.mcu_ug_ctrl)
{
sys_stop_timer(ty_msg.mcu_ug_fw_timer);
op_ret = tuya_iot_cat1_update_mcu_ver(mcu_sw_ver);
if (op_ret != OPRT_OK)
{
sys_start_timer(ty_msg.mcu_sync_ver_timer, 100, TIMER_ONCE);
PR_DEBUG("op_ret/%d", op_ret);
}
}
tuya_hal_system_free(mcu_sw_ver);
}
break;
default:
break;
}
return op_ret;
}
SDK 内提供编译脚本,您可以通过如下命令调用脚本可实现代码编译。
编译指令:进入到编译脚本所在目录,执行 ./build_app.sh
<项目名称> <版本号>。
命令示例: ./build.sh emobile 0.0.1
编译成功后会在 apps/项目名称/output/版本号/ 目录下生成烧录用的二进制(pac)文件,如下图片所示:
最后将对应的 项目名称_版本号 .pac
文件烧录到设备中。
设备烧录完成后,您可以使用智能生活 App 对设备进行配网和功能调试。
添加设备有两种方式:扫码/蓝牙,您可以根据自己的需要选择。
扫码添加。
该二维码需要使用涂鸦的生产打印工具生成,通常在生产阶段使用。
通过蓝牙添加。
下载智能生活 App 并注册登录。
轻按 添加设备 > 自动发现 > 开启蓝牙 > 开始搜索。
)
搜索到设备后轻按 下一步 开始注册配网,配网完成后默认进入设备面板。
添加设备完成后进入面板,进行数据收发测试,您可以在下方信息框内看到上报和下发的 DP 信息。
)
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈