今天来学习一下tuya的例程。其基本的软件框架如下:
文件 | 概要 |
---|---|
tuya_device.c | 用来配置wifi连接 |
dp_process.c | 用来处理云命令 |
light_system.c | 作为云命令的底层 |
为了更加清晰地让大家观察到系统执行的内部架构,我们首先循着主线来观察。
在dp_process.h
文件中我们可以找到如下这个消息响应函数:
VOID_T deal_dp_proc(IN CONST TY_OBJ_DP_S *root)
{
OPERATE_RET op_ret = OPRT_OK;
UCHAR_T dpid;
dpid = root->dpid;
PR_DEBUG("dpid:%d", dpid);
switch(dpid) {
case DPID_LIGHT_SWITCH:
if (root->value.dp_bool == TRUE) {
op_ret = set_light_status(LIGHT_ON);
if (op_ret != OPRT_OK) {
PR_ERR("dp process set light status error, %d", op_ret);
return;
}
} else {
op_ret = set_light_status(LIGHT_OFF);
if (op_ret != OPRT_OK) {
PR_ERR("dp process set light status error, %d", op_ret);
return;
}
}
/* update device current status to cloud */
update_all_dp();
break;
default :
break;
}
}
在本项开关灯例程中,我们注册了一项消息,名为:DPID_LIGHT_SWITCH
,在头文件中被定义为20。
当发生这个消息的时候将会首先检测当前灯的状态,然后根据等的状态来调用函数set_light_status()
来设定灯的开关。
最终同步当前设备的状态到云端,使用函数update_all_dp
。
其中,set_light_status()
在文件light_system.c
中给出了定义,具体实现如下:
OPERATE_RET set_light_status(LED_STATUS_E status)
{
OPERATE_RET op_ret = OPRT_OK;
if (status == LIGHT_ON) {
op_ret = light_on();
if (op_ret != OPRT_OK) {
return op_ret;
}
} else {
light_off();
if (op_ret != OPRT_OK) {
return op_ret;
}
}
return op_ret;
}
这个函数作为对灯的操作的底层。其中调用的light_off();
和light_on();
是这个底层函数的具体实现,两个函数都被定义成了static
的函数,作为局部函数来用,以防用户错误调用。
其中通过函数tuya_gpio_write
来实现对于特定IO口的操作。在GPIO的定义中,我们可以看到链接灯的引脚是TY_GPIOA_16
。这个函数在库函数中给出,但是具体源码不表,其中应该也是通过调整寄存器实现对于GPIO的操作。
在明确底层之后,我们需要回过头看一下,与云端的上报函数update_all_dp
。其基本得思路概括如下:
// 检测是不是及已经建立连接
// 创建、初始化DP对象(上报对象)
// 然后创建一个DP对象
// 上传DP对象
dev_report_dp_json_async();
// 释放DP对象
DP就是一个数据上报的基本结构体其定义如下:
/**
* @brief Definition of structured dp
*/
typedef struct {
/** dp id */
BYTE_T dpid;
/** dp type, see DP_PROP_TP_E */
DP_PROP_TP_E type;
/** dp value, see TY_OBJ_DP_VALUE_U */
TY_OBJ_DP_VALUE_U value;
/** dp happen time. if 0, mean now */
UINT_T time_stamp;
}TY_OBJ_DP_S;
其中,保存数据的单元是TY_OBJ_DP_VALUE_U
用一个union来定义。其中通过type
字段来指明具体使用的类型。
/**
* @brief tuya sdk dp value union
*/
typedef union {
INT_T dp_value; // valid when dp type is value
UINT_T dp_enum; // valid when dp type is enum
CHAR_T *dp_str; // valid when dp type is str
BOOL_T dp_bool; // valid when dp type is bool
UINT_T dp_bitmap; // valid when dp type is bitmap
}TY_OBJ_DP_VALUE_U;
看完了主要逻辑,接下来要观察一下通信的部分,这一部分在tuya_device.c
中实现。其中响应了在手机端控制的按键消息。
在函数wifi_key_process
中实现了这一按键相应的功能。实现逻辑与dp_process
中相同。
在wifi_key_init
函数中注册了这一消息响应函数,可以看到:
key_def.call_back = wifi_key_process;
而这个函数会在系统初始化过程中被调用,从而实现消息注册。
综上所述,我们可以概括一下如果需要实现一项基于tuya的项目需要经历的步骤:
注册特定事件触发的消息及其响应函数
完成事件的底层逻辑
在响应函数中调用其底层逻辑
而底层逻辑的细节可以在tuya给出的sdk库中找到。
以上,共同学习,共同进步!
涂鸦主要是给非电子开发者用的,非常容易上手。