前言:
当前咱们对“c语言operate函数”都比较讲究,兄弟们都需要了解一些“c语言operate函数”的相关文章。那么小编在网络上网罗了一些有关“c语言operate函数””的相关知识,希望咱们能喜欢,兄弟们一起来学习一下吧!前言
之前我们介绍了如何在涂鸦智能平台创建智能设备, 以及开发环境搭建, 今天我们就来分析源码,一步步了解对接涂鸦云过程以及API接口,文章所涉及的工程源码,我会上传到我的github:,需要的小伙伴自行下载。喜欢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, 今天先到此,感兴趣的小伙伴记得关注,收藏,转发~
标签: #c语言operate函数