ZLL主机接口的信息处理流程
主机接口的信息处理流程
在我们翻译的文档中是用电脑端来模拟主机的,电脑代替网关发送主机接口命令的环节是在zll_controller.c中实现的,(在下载的文件中已经提供了其对应的可执行文件zllCmdLine.exe)下面我们看看这个文件的结构。
先看最前面的两个函数:
void usage( char* exeName )
printf("Usage: ./%s <port>\n", exeName);
printf("Eample: ./%s /dev/ttyACM0\n", exeName);
}
这个函数是用来显示程序名及一些附加信息的

void commandUsage( void )//用来提示本程序的功能信息
上面两个函数对我们的STM32网关无关紧要,在主函数中接下来使用zllSocOpen( (char*)argv[1] );打开了主机到CC2531的串口,这一功能需要移植到STM32网关中,其具体实现过程在此不探究。
接下来主函数调用了zllSocRegisterCallbacks( zllSocCbs );用来注册回调函数,这个功能的加入是主机接口中最难理解的,在后面我会详细分析,现在我们只需要知道一点,根据后面的分析,这里注册的回调函数是主机(也就是网关)接收到CC2531的数据并解析后调用的函数。
最后在主函数中用while(1)循环来处理用户从命令行输入的参数,
while(1)
{
processConsoleCommand();
}
在processConsoleCommand();中接收了用户输入的参数,如:

再调用
getConsoleCommandParams(cmdBuff, &nwkAddr, &addrMode, &endpoint, &value, &transitionTime);对参数进行了一些处理,这个函数我们也不需要清楚是如何实现的,因为其是对电脑模拟网关时参数的一种处理方式。而在STM32网关中我们已经实现了标准命令的解析。
接下来是重点,对参数处理过后,就需要解析用户要干什么,这是通过一系列条件语句实现的,我还是以设置灯的状态为例,如:
else if((strstr(cmdBuff, "setstate")) != 0)
{
zllSocSetState(value, nwkAddr, endpoint, addrMode);
printf("setstate command executed with params: \n");
printf(" Network Addr :0x%04x\n End Point :0x%02x\n Addr Mode :0x%02x\n Value :0x%02x\n\n",
nwkAddr, endpoint, addrMode, value);
} 从上面可以看看到,当程序解析到用户需要“setstate”,就调用了相应的主机接口函数,(红字标注)。这就与我上一篇文档分析的STM32网关解析命令后调用相应的主机接口函数的过程衔接起来了,这一过程在STM32中我们已经实现,也无需移植。
接下来我们看看zllSocSetState(value, nwkAddr, endpoint, addrMode);这个函数我们是需要移植的(在zllSocCmd.c中)
void zllSocSetState(uint8_t state, uint16_t dstAddr, uint8_t endpoint, uint8_t addrMode)
{
uint8_t cmd[] = {
0xFE,
11, /*RPC payload Len */
0x29, /*MT_RPC_CMD_AREQ + MT_RPC_SYS_APP */
0x00, /*MT_APP_MSG */
0x0B, /*Application Endpoint */
(dstAddr & 0x00ff),
(dstAddr & 0xff00) >> 8,
endpoint, /*Dst EP */
(ZCL_CLUSTER_ID_GEN_ON_OFF & 0x00ff),
(ZCL_CLUSTER_ID_GEN_ON_OFF & 0xff00) >> 8,
0x04, //Data Len
addrMode,
0x01, //0x01 ZCL frame control field. (send to the light cluster only)
transSeqNumber++,
(state ? 1:0),
0x00 //FCS - fill in later
};
calcFcs(cmd, sizeof(cmd));
COM_Write( hZllSoc,cmd, sizeof(cmd));
}
这个函数结构是很简单的,先构造命令,再构造帧,最后通过串口发给CC2531(我猜的)
对于上述这类“set ~~~”命令,是有去无回的,即CC2531接收到命令后就去执行,至于结果成功与否,CC2531都不会返回信息给主机。但是对于“get····”命令,如:”get state”,即获取灯的状态,CC2531接收到命令后,立即执行,当CC2531获取了灯的状态后还得将信息返回给主机,主机再转发给用户。那主机(网关)中是如何处理来自CC2531的消息的呢?这里我们就讲到了一个很重要的需要移植的一些函数,这些函数在zllSocCmd.c中实现的
当主机接收到CC2531的信息后,假设其存储在uint8_t* rpcBuff中,就调用
void zllSocProcessRpc (uint8_t* rpcBuff)
来处理这个消息,下面我们看看这个函数
void zllSocProcessRpc (uint8_t* rpcBuff)
{
static uint8_t retryAttempts = 0;
switch (rpcBuff[0] & MT_RPC_SUBSYSTEM_MASK)
{
case MT_RPC_SYS_DBG:
{
processRpcSysDbg(&rpcBuff[0]);
break;
}
case MT_RPC_SYS_APP:
{
processRpcSysApp(&rpcBuff[0]);
break;
}
default:
{
printf("zllSocProcessRpc: CMD0:%x, CMD1:%x, not handled\n", rpcBuff[0], rpcBuff[1] );
break;
}
}
这个函数我们也不用深究,应该可以完整移植,我们简单看看其过程,switch语句中解析了消息的类别,即MT_RPC_SYS_DBG:和MT_RPC_SYS_APP,前一种是调试信息,如显示“命令成功接收”和“命令错误”等,这个不是重点,我们的重点在后一种情况,即接收到的是应用信息,下面我们看看其调用的函数processRpcSysApp(&rpcBuff[0]),也是要移植的
void processRpcSysApp(uint8_t *rpcBuff)
{
if( rpcBuff[1] == MT_APP_ZLL_TL_IND )
{
processRpcSysAppTlInd(&rpcBuff[2]);
}
else if( rpcBuff[1] == MT_APP_ZLL_NEW_DEV_IND )
{
processRpcSysAppNewDevInd(&rpcBuff[2]);
}
else if( rpcBuff[1] == MT_APP_RSP )
{
processRpcSysAppZclRsp(&rpcBuff[2]);
}
else if( rpcBuff[1] == 0 )
{
if( rpcBuff[2] == 0)
{
printf("processRpcSysApp: Command Received Successfully\n\n");
}
else
{
printf("processRpcSysApp: Command Error\n\n");
}
}
else
{
printf("processRpcSysApp: Unsupported MT App Msg\n");
}
return;
}
从这个函数我们可以看到应用消息也有三种:
① TouchLink触摸指示 MT_APP_ZLL_TL_IND
② 新设备指示 MT_APP_ZLL_NEW_DEV_IND
③ 应用响应(如:状态响应,亮度响应,色调响应,饱和度响应) MT_APP_RSP
三种消息分别调用了三种处理函数:这三个函数都是需要移植的
processRpcSysAppTlInd(&rpcBuff[2]);
processRpcSysAppNewDevInd(&rpcBuff[2]);
processRpcSysAppZclRsp(&rpcBuff[2]);
这里我只以第三个消息处理函数为例,(在zllSocCmd.c中),因为是分析流程,具体代码在此不粘贴,可自行查看。我还是以获取灯的状态为例,即已经解析到CC2531发来的消息是响应灯的状态的,也就是说信息中含有说明灯的状态的字段,其解析过程我也分析了,可以完整移植,接下来主机应该将灯的状态返回给用户了,我们看看实现过程:
if( (clusterID == ZCL_CLUSTER_ID_GEN_ON_OFF) && (attrID ==
ATTRID_ON_OFF) )
{
if(zllSocCb.pfnZclGetStateCb)
{
uint8_t state =
zclRspBuff[0];
zllSocCb.pfnZclGetStateCb(state, nwkAddr,
endpoint);
}
}
这里我们就用到了我们前面注册的回调函数,我们回过头来看看这个注册过程是如何实现的,在前面我们知道主函数调用zllSocRegisterCallbacks( zllSocCbs );注册回调函数,我们先看看参数zllSocCbs,在zll_controller.c中定义为
static
zllSocCallbacks_t zllSocCbs =
{
tlIndicationCb, // pfnTlIndicationCb - TouchLink
Indication callback
NULL, //
pfnNewDevIndicationCb - New Device Indication callback
zclGetStateCb, //
pfnZclGetHueCb - ZCL response callback for get Hue
zclGetLevelCb, //pfnZclGetSatCb
- ZCL response callback for get Sat
zclGetHueCb, //pfnZclGetLevelCb_t - ZCL response
callback for get Level
zclGetSatCb //pfnZclGetStateCb - ZCL
response callback for get State
};
其数据类型为zllSocCallbacks_t的结构体,在zllSocCmd.h中已定义
typedef struct
{
zllSocTlIndicationCb_t
pfnTlIndicationCb; // TouchLink Indication callback
zllNewDevIndicationCb_t
pfnNewDevIndicationCb; // New device Indication
callback
zllSocZclGetStateCb_t
pfnZclGetStateCb; // ZCL response callback for get State
zllSocZclGetLevelCb_t
pfnZclGetLevelCb; // ZCL response callback for get
Level
zllSocZclGetHueCb_t
pfnZclGetHueCb; // ZCL response
callback for get Hue
zllSocZclGetSatCb_t
pfnZclGetSatCb; // ZCL response callback
for get Sat
} zllSocCallbacks_t;
也就是说在zll_controller.c我们定义了一个这种结构的数据类型,并对其赋了初值,如现在zllSocCbs.
pfnZclGetStateCb= zclGetStateCb
这个参数我们说清楚了,下面我们看看zllSocRegisterCallbacks( zllSocCbs );(在zllSocCmd.c中)的过程
void zllSocRegisterCallbacks(
zllSocCallbacks_t zllSocCallbacks)
{
//copy the callback function pointers
memcpy(&zllSocCb,
&zllSocCallbacks, sizeof(zllSocCallbacks_t));
return;
}
函数中zllSocCallbacks就是参数zllSocCbs,实际上就是将zllSocCbs复制给了zllSocCb,zllSocCb是在zllSocCmd.c中定义的与zllSocCbs类型相同的一个全局量
zllSocCallbacks_t
zllSocCb;
现在我们可以回到processRpcSysAppZclRsp(&rpcBuff[2]);了,在得到“灯的状态”后,调用了
zllSocCb.pfnZclGetStateCb(state,
nwkAddr, endpoint);
乍一看我们不知道它调用了什么函数,这时我们看在zllSocCmd.h中有一些指针函数定义
typedef uint8_t
(*zllSocTlIndicationCb_t)(epInfo_t *epInfo);
typedef uint8_t
(*zllNewDevIndicationCb_t)(epInfo_t *epInfo);
typedef uint8_t
(*zllSocZclGetStateCb_t)(uint8_t state, uint16_t nwkAddr, uint8_t endpoint);
typedef uint8_t
(*zllSocZclGetLevelCb_t)(uint8_t level, uint16_t nwkAddr, uint8_t endpoint);
typedef uint8_t
(*zllSocZclGetHueCb_t)(uint8_t hue, uint16_t nwkAddr, uint8_t endpoint);
typedef uint8_t
(*zllSocZclGetSatCb_t)(uint8_t sat, uint16_t nwkAddr, uint8_t endpoint);
pfnZclGetStateCb的数据类型就是zllSocZclGetStateCb_t
实际上前面的分析我们知道zllSocCb.pfnZclGetStateCb=zllSocCbs. pfnZclGetStateCb= zclGetStateCb;也就是说这里本质上调用的是
zclGetStateCb (state, nwkAddr, endpoint);这个函数的类型就是前面高亮显示的指针函数类型
下面我们看看zclGetStateCb
(state, nwkAddr, endpoint)是干什么的,(在zll_controller.c中)
uint8_t zclGetStateCb(uint8_t state,
uint16_t nwkAddr, uint8_t endpoint)
{
printf("\nzclGetStateCb:\n Network Addr :
0x%04x\n End Point :
0x%02x\n State :
0x%02x\n\n",
nwkAddr, endpoint,
state);
return 0;
}
这个函数很简单,就是将得到的信息(包括灯的状态)打印给用户看,当然,这里是用电脑模拟主机(网关),只要在电脑上显示给用户看就行了,那如果是真正的网关需要调用的回调函数应该是将这个信息发送给客户端,即在interface_srpcserver.c中的
void RPCS_ZLL_CallBack_getStateRsp(uint8_t
state, uint16_t srcAddr, uint8_t endpoint, uint32_t clientFd);
这就需要修改相应的回调函数了。
自此,客户端与网关之间的信息往返,网关与CC2531之间的信息往返,整个数据链就打通了。
附件列表
ZLL主机接口的信息处理流程的更多相关文章
- 深入理解Java:注解
注解作用:每当你创建描述符性质的类或者接口时,一旦其中包含重复性的工作,就可以考虑使用注解来简化与自动化该过程. Java提供了四种元注解,专门负责新注解的创建工作. 元注解 元注解的作用就是负责注解 ...
- 深入理解Java:注解(Annotation)自定义注解入门
转载:http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html 元注解: 元注解的作用就是负责注解其他注解.Java5.0定义了4个标准 ...
- TI Zigbee Light Link 参考设计
TI Zigbee Light Link 参考设计 原文出处: http://processors.wiki.ti.com/index.php/Category:ZigBee_Light_Link ...
- 注解Annotation 详解(转)
要深入学习注解,我们就必须能定义自己的注解,并使用注解,在定义自己的注解之前,我们就必须要了解Java为我们提供的元注解和相关定义注解的语法. 元注解: 元注解的作用就是负责注解其他注解.Java5. ...
- annotation(@Retention@Target)详解
一.注解:深入理解JAVA注解 要深入学习注解,我们就必须能定义自己的注解,并使用注解,在定义自己的注解之前,我们就必须要了解Java为我们提供的元注解和相关定义注解的语法. 1.元注解(meta-a ...
- Java注解(Annotation)自定义注解入门
要深入学习注解,我们就必须能定义自己的注解,并使用注解,在定义自己的注解之前,我们就必须要了解Java为我们提供的元注解和相关定义注解的语法. 元注解: 元注解的作用就是负责注解其他注解.Java5. ...
- Java之注解
package com.demo.test; import java.lang.annotation.Documented; import java.lang.annotation.ElementTy ...
- Java:注解(Annotation)自定义注解入门
转载地址:http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html 要深入学习注解,我们就必须能定义自己的注解,并使用注解,在定义自己的 ...
- JAVA提高五:注解Annotation
今天我们学习JDK5.0中一个非常重要的特性,叫做注解.是现在非常流行的一种方式,可以说因为配置XML 比较麻烦或者比容易查找出错误,现在越来越多的框架开始支持注解方式,比如注明的Spring 框架, ...
随机推荐
- devstack meaning of: n-cond, n-novnc and n-xvnc
devstack has shortened names for a number of services, e.g. g-api = glance api g-reg = glance regist ...
- 【Other】最近正在看的
待看: https://my.oschina.net/yunfound/blog/141222 https://www.zhihu.com/question/22925358 http://study ...
- 79 两个整数集合A和B,求其交集
[本文链接] http://www.cnblogs.com/hellogiser/p/ab-intersect.html [题目] 两个整数集合A和B,求其交集. [分析] 1. 读取整数集合A中 ...
- unity3d优化总结篇
转自http://www.unitymanual.com/thread-21597-1-1.html 此总结由自己经验及网上收集整理优化内容 包括:1.代码方面:2.函数使用方面:3.ngui注意方面 ...
- iOS keyChain 的使用
详细资料,请参看苹果官方文档Keychain Services Reference . ios中的keychain,用于保存用户的机密信息,对keychain的操作有4种,就是 增,删,改,查: Se ...
- java web 学习 --第八天(Java三级考试)
第七天的学习内容:http://www.cnblogs.com/tobecrazy/p/3464231.html EL表达式 EL : Expression Language 使用EL表达式可以减少& ...
- ABAP 承运路单
*&---------------------------------------------------------------------* *& Report ZSDR010 ...
- 4.openstack之mitaka搭建glance镜像服务
部署镜像服务 一:安装和配置服务 1.建库建用户 mysql -u root -p CREATE DATABASE glance; GRANT ALL PRIVILEGES ON glance.* T ...
- linq lanbda表达式的用法
1. 查询Student表中的所有记录的Sname.Ssex和Class列.select sname,ssex,class from studentLinq: from s in Student ...
- 【python】入门学习(六)
type() #检查变量或值得数据类型 >>> type(5) <class 'int'> 序列:包括字符串.元组和列表.序列都可以用索引.切片.len()(计算元素个数 ...