【涂鸦BK7231N】样例代码精读分析
<p cid="n0" mdtype="paragraph">今天来学习一下tuya的例程。其基本的软件框架如下:</p><figure cid="n2" mdtype="table">
<table>
<thead>
<tr cid="n3" mdtype="table_row">
<th>文件</th>
<th>概要</th>
</tr>
</thead>
<tbody>
<tr cid="n6" mdtype="table_row">
<td>tuya_device.c</td>
<td>用来配置wifi连接</td>
</tr>
<tr cid="n9" mdtype="table_row">
<td>dp_process.c</td>
<td>用来处理云命令</td>
</tr>
<tr cid="n12" mdtype="table_row">
<td>light_system.c</td>
<td>作为云命令的底层</td>
</tr>
</tbody>
</table>
</figure>
<p cid="n15" mdtype="paragraph">为了更加清晰地让大家观察到系统执行的内部架构,我们首先循着主线来观察。</p>
<p cid="n16" mdtype="paragraph">在<code>dp_process.h</code>文件中我们可以找到如下这个消息响应函数:</p>
<pre cid="n17" lang="C++" mdtype="fences" spellcheck="false">
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;
}
}</pre>
<p cid="n18" mdtype="paragraph">在本项开关灯例程中,我们注册了一项消息,名为:<code>DPID_LIGHT_SWITCH</code>,在头文件中被定义为20。</p>
<p cid="n19" mdtype="paragraph">当发生这个消息的时候将会首先检测当前灯的状态,然后根据等的状态来调用函数<code>set_light_status()</code>来设定灯的开关。</p>
<p cid="n20" mdtype="paragraph">最终同步当前设备的状态到云端,使用函数<code>update_all_dp</code>。</p>
<p cid="n21" mdtype="paragraph">其中,<code>set_light_status()</code>在文件<code>light_system.c</code>中给出了定义,具体实现如下:</p>
<pre cid="n22" lang="C++" mdtype="fences" spellcheck="false">
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;
}</pre>
<p cid="n23" mdtype="paragraph">这个函数作为对灯的操作的底层。其中调用的<code>light_off();</code>和<code>light_on();</code>是这个底层函数的具体实现,两个函数都被定义成了<code>static</code>的函数,作为局部函数来用,以防用户错误调用。</p>
<p cid="n24" mdtype="paragraph">其中通过函数<code>tuya_gpio_write</code>来实现对于特定IO口的操作。在GPIO的定义中,我们可以看到链接灯的引脚是<code>TY_GPIOA_16</code>。这个函数在库函数中给出,但是具体源码不表,其中应该也是通过调整寄存器实现对于GPIO的操作。</p>
<p cid="n25" mdtype="paragraph">在明确底层之后,我们需要回过头看一下,与云端的上报函数<code>update_all_dp</code>。其基本得思路概括如下:</p>
<pre cid="n26" lang="text" mdtype="fences" spellcheck="false">
// 检测是不是及已经建立连接
// 创建、初始化DP对象(上报对象)
// 然后创建一个DP对象
// 上传DP对象
dev_report_dp_json_async();
// 释放DP对象</pre>
<p cid="n27" mdtype="paragraph">DP就是一个数据上报的基本结构体其定义如下:</p>
<pre cid="n28" lang="C++" mdtype="fences" spellcheck="false">
/**
* @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;</pre>
<p cid="n29" mdtype="paragraph">其中,保存数据的单元是<code>TY_OBJ_DP_VALUE_U</code>用一个union来定义。其中通过<code>type</code>字段来指明具体使用的类型。</p>
<pre cid="n30" lang="C++" mdtype="fences" spellcheck="false">
/**
* @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;</pre>
<p cid="n31" mdtype="paragraph">看完了主要逻辑,接下来要观察一下通信的部分,这一部分在<code>tuya_device.c</code>中实现。其中响应了在手机端控制的按键消息。</p>
<p cid="n32" mdtype="paragraph">在函数<code>wifi_key_process</code>中实现了这一按键相应的功能。实现逻辑与<code>dp_process</code>中相同。</p>
<p cid="n33" mdtype="paragraph">在<code>wifi_key_init</code>函数中注册了这一消息响应函数,可以看到:</p>
<pre cid="n34" lang="C++" mdtype="fences" spellcheck="false">
key_def.call_back = wifi_key_process;</pre>
<p cid="n35" mdtype="paragraph">而这个函数会在系统初始化过程中被调用,从而实现消息注册。</p>
<p cid="n36" mdtype="paragraph">综上所述,我们可以概括一下如果需要实现一项基于tuya的项目需要经历的步骤:</p>
<blockquote cid="n37" mdtype="blockquote">
<ul cid="n38" data-mark="+" mdtype="list">
<li cid="n39" mdtype="list_item">
<p cid="n40" mdtype="paragraph">注册特定事件触发的消息及其响应函数</p>
</li>
<li cid="n41" mdtype="list_item">
<p cid="n42" mdtype="paragraph">完成事件的底层逻辑</p>
</li>
<li cid="n43" mdtype="list_item">
<p cid="n44" mdtype="paragraph">在响应函数中调用其底层逻辑</p>
</li>
</ul>
</blockquote>
<p cid="n45" mdtype="paragraph">而底层逻辑的细节可以在tuya给出的sdk库中找到。</p>
<p cid="n46" mdtype="paragraph">以上,共同学习,共同进步!</p>
分析的很到位,其实涂鸦的最终目的是不用开发者写代码。 <p>确实确实,对不起有点职业病了,哈哈哈哈哈哈哈哈哈。</p>
<p>但是对于嵌入式工程师其实可以学习一下它的内部逻辑~这种面向对象的思想在嵌入式系统中的应用,哈哈哈哈哈哈哈。</p>
<p>涂鸦主要是给非电子开发者用的,非常容易上手。</p>
页:
[1]