[讨论] 【涂鸦BK7231N】样例代码精读分析

javnson   2022-5-10 09:56 楼主

今天来学习一下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库中找到。

以上,共同学习,共同进步!

回复评论 (3)

分析的很到位,其实涂鸦的最终目的是不用开发者写代码。
点赞  2022-5-10 16:27

确实确实,对不起有点职业病了,哈哈哈哈哈哈哈哈哈。

但是对于嵌入式工程师其实可以学习一下它的内部逻辑~这种面向对象的思想在嵌入式系统中的应用,哈哈哈哈哈哈哈。

点赞  2022-5-10 23:01

涂鸦主要是给非电子开发者用的,非常容易上手。

点赞  2022-5-11 14:19
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复