百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

物联网之——基于涂鸦sdk二次开发源码分析(三)

liebian365 2024-10-20 09:59 43 浏览 0 评论

前言

之前我们介绍了如何在涂鸦智能平台创建智能设备, 以及开发环境搭建, 今天我们就来分析源码,一步步了解对接涂鸦云过程以及API接口,文章所涉及的工程源码,我会上传到我的github:https://github.com/astonb/tuya_platform,需要的小伙伴自行下载。喜欢diy的小伙伴可以参考此工程,结合以下的源码分析,可以改造成自己的智能设备,实现远程控制。好了,进入我们的正题~

分析

  1. 首先需要知道应用入口函数是: 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;
}

以上我们完成了设备控制和状态上报功能。

  1. 回到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 !!!");
}

其函数主要是判断下设备是否成功连接云端,如果不是,则直接退出,如果是,则上报设备状态。

  1. 继续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一样, 只是定时器是主动查询。

  1. 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是设备属性的结构体,用于管理设备(我这里是开关为例子),

  1. 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编码的中文)。就像一位翻译官,它能将一种语言(宽字符...

取消回复欢迎 发表评论: