物联网之——基于涂鸦sdk二次开发源码分析(三)
liebian365 2024-10-20 09:59 44 浏览 0 评论
前言
之前我们介绍了如何在涂鸦智能平台创建智能设备, 以及开发环境搭建, 今天我们就来分析源码,一步步了解对接涂鸦云过程以及API接口,文章所涉及的工程源码,我会上传到我的github:https://github.com/astonb/tuya_platform,需要的小伙伴自行下载。喜欢diy的小伙伴可以参考此工程,结合以下的源码分析,可以改造成自己的智能设备,实现远程控制。好了,进入我们的正题~
分析
- 首先需要知道应用入口函数是: OPERATE_RET device_init(VOID) ,在device.c文件中。
OPERATE_RET device_init(VOID)
{
OPERATE_RET op_ret;
PR_NOTICE("fireware info name:%s version:%s", APP_BIN_NAME, USER_SW_VER);
//Product_Init();
//1. 涂鸦设备初始化, 参数: pid, app控制回调
op_ret = tuya_device_init(PRODECT_KEY, device_cb, USER_SW_VER);
if (op_ret != OPRT_OK)
{
return op_ret;
}
tuya_active_reg(syn_active_data);
op_ret = sys_add_timer(syn_timer_cb, NULL, &Syntimer);
if(OPRT_OK != op_ret)
{
PR_ERR("add syn_timer err");
return op_ret;
}
else
{
sys_start_timer(Syntimer, SYN_TIME*1000, TIMER_CYCLE);
}
Product_Init();
op_ret = device_differ_init();
if (op_ret != OPRT_OK)
{
return op_ret;
}
return op_ret;
}
分析:
1. tuya_device_init(PRODECT_KEY, device_cb, USER_SW_VER);
功能:该函数主要完成涂鸦设备初始化。
参数1:设备pid,pid在涂鸦平台创建智能设备时获取,大家根据自己创建的设备为准
参数2:在app操作,下发指令给设备的回调函数:
VOID device_cb(SMART_CMD_E cmd,cJSON *root)
{
CHAR *buf = cJSON_PrintUnformatted(root);
if (NULL == buf)
{
PR_ERR("malloc error");
return;
}
PR_DEBUG("root cmd:%s",buf);
//实际控制设备
device_ControlByApp(root);
Free(buf);
}
如:上述例子中的, 将收到的指令传递给device_ControlByApp(root)函数,完成实际对设备的控制:
/***********************************************************
* Function: Motor_ControlByApp
* Description: 接受app控制处理函数
* Input: root命令
* Output:
* Return:
***********************************************************/
void device_ControlByApp(cJSON *root)
{
cJSON *cmd = cJSON_GetObjectItem(root, DP_control); //DP_control是在涂鸦平台定义的数据点
if(cmd != NULL){
switch_dev.statu = (bool)cmd->valueint;
tuya_write_gpio_level(WF_SWITCH_GPIO_PIN, switch_dev.statu);
}
cmd = cJSON_GetObjectItem(root, DP_delayoff);////DP_delayoff是在涂鸦平台定义的数据点
if(cmd != NULL){
switch_dev.delayoff = cmd->valueint;
}
PostSemaphore(Upload_SemHandle);
}
说明:DP_control 和 DP_delayoff 是在涂鸦平台定义的设备数据点:
PostSemaphore(Upload_SemHandle); 是 二值信号量(作用:任务同步,因为我们在另外开了一个任务,专门用于上报设备状态,如下代码),用于上报设备状态的, 这里我们控制完设备后, 就应该主动上报下设备状态,以便app能实时。
//================上报设备状态的任务====================
void MSG_Upload_Poll_Task(PVOID pArg)
{
OPERATE_RET ret;
//创建信号量
Upload_SemHandle = CreateSemaphore();
if(Upload_SemHandle == NULL)
{
PR_ERR("Create Upload_Sem failed");
ThrdJoin(Upload_TaskHandle, NULL);
return;
}
//初始化为二值信号量,且初始化值为0
InitSemaphore(Upload_SemHandle, 0, 1);
//创建互斥量,用于上报设备状态时加锁(这里可以省去,不影响)
ret = CreateMutexAndInit(&Upload_mutexHandle);
if(ret != OPRT_OK)
{
PR_ERR("Create Upload_Mutex failed");
ThrdJoin(Upload_TaskHandle, NULL);
return;
}
while(1)
{
//一直等待信号量,等到了就上报设备状态
if(WaitSemaphore(Upload_SemHandle) == OPRT_OK)
{
ret = msg_upload_proc();
if(ret != OPRT_OK)
PR_ERR("***msg_upload failed!***");
}
}
}
其中msg_upload_proc();就是实现设备状态上报:
/***********************************************************
* Function: msg_upload_proc
* Description: dp上报函数
* Input:
* Output:
* Return:
***********************************************************/
INT msg_upload_proc(void)
{
OPERATE_RET err;
//判断连接涂鸦云的状态,如果是断开的,也就没必要上报了
if(tuya_get_cloud_stat() == FALSE)
return OPRT_DP_REPORT_CLOUD_ERR;
MutexLock(Upload_mutexHandle);
//判断wifi连接状态,如果不是正常连接,那也没必要上报了
GW_WIFI_STAT_E wf_stat = tuya_get_wf_status();
if(STAT_UNPROVISION == wf_stat || \
STAT_STA_UNCONN == wf_stat || \
(tuya_get_gw_status() != STAT_WORK)) {
err = OPRT_WF_CONN_FAIL;
goto exit;
}
//================以下是设备数据点格式转化==================
cJSON *root = cJSON_CreateObject();
if(NULL == root) {
err = OPRT_BUF_NOT_ENOUGH;
goto exit;
}
//根据实际情况上报设备数据点
cJSON_AddBoolToObject(root, DP_control, switch_dev.statu);
cJSON_AddNumberToObject(root, DP_delayoff, switch_dev.delayoff);
CHAR *out = cJSON_PrintUnformatted(root);
cJSON_Delete(root);
if(NULL == out) {
PR_ERR("cJSON_PrintUnformatted err:");
err = OPRT_CJSON_PARSE_ERR;
goto exit;
}
PR_DEBUG("out[%s]", out);
//=======================end===========================
//最终调用该函数实现数据点上报
OPERATE_RET op_ret = tuya_obj_dp_trans_report(out);
Free(out);
if( OPRT_OK == op_ret ) {
err = OPRT_OK;
goto exit;
}else {
PR_DEBUG("msg upload error");
err = OPRT_DP_REPORT_CLOUD_ERR;
goto exit;
}
exit:
MutexUnLock(Upload_mutexHandle);
return err;
}
以上我们完成了设备控制和状态上报功能。
- 回到device_init用户主函数中,接着继续分析下面的代码:
tuya_active_reg(syn_active_data);
该函数作用是:当设备连接涂鸦云成功激活的回调函数,主要是上报下设备状态,相当于设备重新上电启动连接云端时,同步下app显示的设备状态。
参数: syn_active_data
STATIC VOID syn_active_data(VOID)
{
PR_DEBUG("sys active data");
if(tuya_get_cloud_stat())
PostSemaphore(Upload_SemHandle);
else
PR_DEBUG("cloud unconnect !!!");
}
其函数主要是判断下设备是否成功连接云端,如果不是,则直接退出,如果是,则上报设备状态。
- 继续device_init代码块:
op_ret = sys_add_timer(syn_timer_cb, NULL, &Syntimer);
if(OPRT_OK != op_ret)
{
PR_ERR("add syn_timer err");
return op_ret;
}
else
{
sys_start_timer(Syntimer, SYN_TIME*1000, TIMER_CYCLE);
}
以上是开启一个定时器, 每2秒(SYN_TIME*1000)去查询连接云端状态,我们看下定时器的回调函数:
STATIC VOID syn_timer_cb(UINT timerID,PVOID pTimerArg)
{
PR_DEBUG("syn timer cb ...");
if( FALSE == tuya_get_cloud_stat() ) {
return;
}
PostSemaphore(Upload_SemHandle);
sys_stop_timer(Syntimer);
}
可以看到其作用和上面的syn_active_data一样, 只是定时器是主动查询。
- Product_Init();
void Product_Init(void)
{
//设备初始化
memset(&switch_dev, 0x0, sizeof(switch_dev));
//设置GPIO
PIN_FUNC_SELECT(WF_SWITCH_GPIO_MUX, WF_SWITCH_GPIO_FUNC);
GPIO_DIS_OUTPUT(WF_SWITCH_GPIO_PIN);//设置成输入模式
//内部实现了按键轮询事件(实现了一键配网功能, 具体见代码)
Key_Init();
OPERATE_RET op_ret;
//用于延迟关闭检测定时器
op_ret = sys_add_timer(Timer_delayoff, NULL, &delayoffTimer);
if(OPRT_OK != op_ret) {
PR_ERR("add Timer_TaskKEY err");
//return op_ret;
}else{
sys_start_timer(delayoffTimer, 1000, TIMER_CYCLE);
}
}
该函数是我们自己定义实现的,其函数内部主要是初始化了相关的gpio, 以及按键轮询事件定时器,主要是为了实现一键配网功能(以便更换一个网络,再次对设备进行配网)
其中switch_dev是设备属性的结构体,用于管理设备(我这里是开关为例子),
- device_init主函数中的最后一个函数device_differ_init:
op_ret = device_differ_init();
if (op_ret != OPRT_OK)
{
return op_ret;
}
其作用是设备的一些个性化初始化, 其实和上面的Product_Init()函数一样,只是涂鸦sdk规范了结构而已。 具体看代码:
STATIC OPERATE_RET device_differ_init(VOID)
{
OPERATE_RET op_ret;
TIMER_ID timer;
CreateAndStart(&Upload_TaskHandle, MSG_Upload_Poll_Task, NULL, 2048, TRD_PRIO_2, "msg_upload_task");
op_ret = sys_add_timer(wfl_timer_cb, NULL, &timer);
if (OPRT_OK != op_ret)
{
return op_ret;
}
else
{
sys_start_timer(timer, 300, TIMER_CYCLE);
}
return OPRT_OK;
}
可以看到在这里我们创建了上面提及的设备状态上报任务MSG_Upload_Poll_Task, 以及创建了一个wifi状态定时查询的定时器:
STATIC VOID wfl_timer_cb(UINT timerID, PVOID pTimerArg)
{
STATIC UINT last_wf_stat = 0xffffffff;
GW_WIFI_STAT_E wf_stat = tuya_get_wf_status();
//The system is about to restart without monitoring!
if (last_wf_stat != wf_stat)
{
PR_DEBUG("wf_stat:%d",wf_stat);
switch (wf_stat)
{
case STAT_UNPROVISION:
PR_DEBUG("STAT_UNPROVISION");
break;
case STAT_AP_STA_UNCONN:
case STAT_AP_STA_CONN:
PR_DEBUG("STAT_AP_STA_UNCONN");
break;
case STAT_STA_CONN:
PR_DEBUG("STAT_STA_CONN");
break;
default:
break;
}
last_wf_stat = wf_stat;
}
}
在wifi定时查询状态回调中,可以实现不同状态,不同led状态显示, 方便用户根据led状态,知道设备目前是处于啥情况,是断网了呢, 还是成功连接着路由器,还是断开了云端等等(根据自己的情况处理即可)。
编译下载,控制设备
至此,主线的代码已经简单分析完了,剩余一些,如一键配网等函数,很简单,大家可以自行查阅代码,有任何不明白的都可私信或直接在github中提问都可。接下来我们就编译我们的工程,
根据上一篇的开发环境搭建,我们打开虚拟机,进入到sdk下的app,输入以下指令:
sh build_app.sh wifi_switch 1.0.0
编译过程:
可以到sdk路径/bin/upgrade目录中,查看我们编译得到的固件
说明:wifi_switch(1)_1.0.0.bin 是 烧录固件
wifi_switch(2)_1.0.0.bin 是OTA在线升级固件
接着我们烧录我们的固件,打开乐鑫的下载工具:ESPFlashDownloadTool_v3.6.4.exe
根据以下设置(TYWE1S模组,若使用其他模组,根据实际情况下载):
好了,看到“FINSH 完成”就表明下载成功, 接着下载涂鸦智能app(可到涂鸦官网或手机应用搜索”涂鸦智能“自行下载,这里不方便贴出二维码~~)
打开app后,我们可以使用“手动搜索” 或 ”自动搜索“ 添加设备(当然此时设备要上电啦~)
成功添加后,可以进入我们的设备控制主界面:
之后我们就可以进行控制啦~ (同时我们可以将设备连接电脑串口,观察app控制时,串口接收到数据)
小结
好了,以上就是涂鸦sdk源码分析和下载验证,相信经过上面的介绍,小伙伴参看代码可以一步步实现自己的智能设备啦,开始行动吧~, 当然有什么不明白的地方,可以私信或下方评论。 另外如果大家对sdk这种方式理解有点困难(涉及一些操作系统知识,如信号量,互斥量,任务等概念), 之后我会另开一篇文章介绍如何使用mcu串口对接, 方便大家更好的入手。 OK, 今天先到此,感兴趣的小伙伴记得关注,收藏,转发~
相关推荐
- 看黑客是如何获取你电脑最高权限的,一定要看
-
在渗透过程中,通过各种方式获取到一枚cmdshell,但是这个shell的权限比较低,无法让我们做我们想要做的一些操作,比如说获取系统密码,获取数据库信息,又或者比如说拿到服务器中的另一个站点的权限,...
- 是50个常用的Visual Basic代码示例:
-
以下是50个常用的VisualBasic代码示例:1.声明变量```vb...
- 电脑系统型号怎么看版本(如何看电脑系统型号)
-
有时候我们会需要进行查看电脑上安装的windows系统版本及系统版本号,但对于不懂电脑知识的小白来说要怎么查看电脑系统版本信息呢?别着急,有小编在接下来,就将查看电脑系统版本的教程来分享给你们,希望对...
- dos命令systeminfo,查看系统启动时间。电脑卡慢,win10怎么了?
-
最近一段时间,有几个反应电脑卡慢的,都是windows10的系统。询问得知每天电脑有关机,打开任务管理器,内存使用量达到百分之九十多,而程序只打开微信、wps、360浏览器。cmd窗口运行命令syst...
- systeminfo命令:全面解析系统信息!
-
你是否曾想过,仅凭一条简单的命令,就能深入了解计算机的"内心世界"?是不是有点不可思议?那么,让我们一起探寻这个神奇的命令,揭开它背后的奥秘吧!它能提供的信息超乎你的想象,从操作系统到硬件配置,再到驱...
- 电脑序列号怎么查询?只需两行命令一键查询
-
当我们的电脑出问题需要保修的时候,需要查询到电脑的型号和序列号才更便于进行下一步的操作,有包装盒的朋友还可以在包装盒上查询,笔记本用户可以在电脑底部标签上查询,没有包装盒和标签破损的用户就无从下手了。...
- 快速显示系统信息:Systeminfo命令详解
-
Systeminfo命令是windows系统中显示系统信息的命令,此命令可以显示出计算机的操作系统的详细配置信息,包括操作系统配置、安全信息、产品ID和硬件属性(如RAM、磁盘空间和网卡)。使用...
- dos命令systeminfo图文教程,显示操作系统配置信息msinfo32
-
大家好,我是老盖,首先感谢观看本文,本篇文章做的有视频,视频讲述的比较详细,也可以看我发布的视频。今天我们学习systeminfo命令,该工具显示本地或远程机器(包括服务包级别)的操作系统配置的信息,...
- 基于uniapp+vue3跨端仿制chatgpt实例uniapp-chatgpt
-
#夏日生活打卡季#...
- 原创新作uniapp+vue3+pinia2高仿微信App聊天
-
前段时间有给大家分享一个flutter3.x桌面端os系统。今天再分享一款最新原创之作uniapp-vue3-wechat聊天实例。uni-vue3-wechat采用...
- UniApp开发的设备适配(uniapp服务器配置)
-
UniApp是一个跨平台开发框架,支持多端应用(如H5、小程序、iOS、Android等)。由于不同设备的屏幕尺寸、分辨率、操作系统等存在差异,设备适配是开发过程中需要重点关注的问题。以下是Uni...
- 如何用服务器搭建自己的个人网站(自己服务器怎么做网站)
-
这篇教程主要是告诉大家如何利用TCP和HTTP协议来完成网站的搭建。首先你需要有C/C++语言基础,且有服务器、客户端概念,如果你了解TCP或者HTTP协议的话,那么将会帮助你更快的学会如何搭建个人网...
- 大话C语言:字符数组(c语言字符数组教学视频)
-
1字符数组概述C语言中没有字符串这种数据类型,可以通过char的数组来替代。数字0(和字符'\0'等价)结尾的char数组就是一个字符串,字符串是一种特殊的char的数组。...
- 源码分享:在pdf上加盖电子签章(pdf怎么加电子签章)
-
在pdf上加盖电子签章,并不是只是加个印章图片,。而是要使用一对密钥中的私钥对文件进行签字。为啥要用私钥呢?很简单,因为公钥是公开的,其他人才可以用公钥为你证明,这个文件是你签的。这就是我们常说的:私...
- C语言wcstombs函数详解:宽字符字符串到多字节的「翻译官」
-
核心定位wcstombs是C语言中用于将宽字符字符串转换为多字节字符串的「翻译官」,它能将宽字符(wchar_t)转换为多字节字符(如UTF-8编码的中文)。就像一位翻译官,它能将一种语言(宽字符...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- wireshark怎么抓包 (75)
- qt sleep (64)
- cs1.6指令代码大全 (55)
- factory-method (60)
- sqlite3_bind_blob (52)
- hibernate update (63)
- c++ base64 (70)
- nc 命令 (52)
- wm_close (51)
- epollin (51)
- sqlca.sqlcode (57)
- lua ipairs (60)
- tv_usec (64)
- 命令行进入文件夹 (53)
- postgresql array (57)
- statfs函数 (57)
- .project文件 (54)
- lua require (56)
- for_each (67)
- c#工厂模式 (57)
- wxsqlite3 (66)
- dmesg -c (58)
- fopen参数 (53)
- tar -zxvf -c (55)
- 速递查询 (52)