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之间的信息往返,整个数据链就打通了。

来自为知笔记(Wiz)

附件列表

时间: 2024-11-07 17:12:03

ZLL主机接口的信息处理流程的相关文章

php 基本概念,常规语法

什么是php 一种服务器端的 HTML 脚本/编程语言,是一种简单的.面向对象的.解释型的.健 壮的.安全的.性能非常之高的.独立于架构的.可移植的.动态的脚本语言.是一种 广泛用于 Open Source(开放源代码)的尤其适合 Web 开发并可以嵌入 HTML 的多用 途脚本语言.它的语法接近 C,Java 和 Perl,而且容易学习.该语言让 Web 开发人 员快速的书写动态生成的网页. php的中文名字及全称 超文本预处理器  全称:Hypertext Preprocessor. php

深入理解Java:注解(Annotation)自己定义注解入门

深入理解Java:注解(Annotation)自己定义注解入门 要深入学习注解.我们就必须能定义自己的注解,并使用注解,在定义自己的注解之前.我们就必须要了解Java为我们提供的元注解和相关定义注解的语法. 元注解: 元注解的作用就是负责注解其它注解. Java5.0定义了4个标准的meta-annotation类型.它们被用来提供对其它 annotation类型作说明.Java5.0定义的元注解: [email protected], [email protected], [email pro

深入理解Java:注解(Annotation)自定义注解入门

引用: http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html 要深入学习注解,我们就必须能定义自己的注解,并使用注解,在定义自己的注解之前,我们就必须要了解Java为我们提供的元注解和相关定义注解的语法. 元注解: 元注解的作用就是负责注解其他注解.Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明.Java5.0定义的元注解: [email protected

(转)深入理解Java:注解(Annotation)自定义注解入门

向作者致敬! 转自:http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html 要深入学习注解,我们就必须能定义自己的注解,并使用注解,在定义自己的注解之前,我们就必须要了解Java为我们提供的元注解和相关定义注解的语法. 元注解: 元注解的作用就是负责注解其他注解.Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明.Java5.0定义的元注解: [email pro

使用注解实现自定义junit(简)

最近在学Spring,发现Spring里面使用了注解(annotation),非常好奇这里面是什么原理!于是就稍微的研究 下其底层的实现原理,然后突然发现junit使用的也是注解的方式,所以决定自定义一个简单版本的junit框架.下面就对junit的实现原理进行分析: 一.首先我们需要先对junit的注解进行声明 我这里就选择了常用的三个注解 @Test.@Before.@After import java.lang.annotation.ElementType; import java.lan

[2]注解(Annotation)-- 深入理解Java:注解(Annotation)自定义注解入门

转载 http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html 深入理解Java:注解(Annotation)自定义注解入门 要深入学习注解,我们就必须能定义自己的注解,并使用注解,在定义自己的注解之前,我们就必须要了解Java为我们提供的元注解和相关定义注解的语法. 元注解: 元注解的作用就是负责注解其他注解.Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明.J

spring使用基础(1)

这是一个改善架构的框架,一个面向架构的框架.他所追求的东西是无入侵式的设计.在开发中我们至少做到最少入侵. spring的使用特点是你几乎不需要了解或使用到api 环境搭建 spring2.5是需要一个spring.jar和一个logging.jar即可.spring3及以上版本自己百度.这里不依依叙述 依赖注入 对象的创建方式 普通方式 <bean id="beanName" class="className"></bean> 测试代码:

【转】深入理解Java:注解(Annotation)自定义注解入门

要深入学习注解,我们就必须能定义自己的注解,并使用注解,在定义自己的注解之前,我们就必须要了解Java为我们提供的元注解和相关定义注解的语法. 元注解: 元注解的作用就是负责注解其他注解.Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明.Java5.0定义的元注解: [email protected], [email protected], [email protected], [email protected] 这些类型和它

【信息资源管理】第一章:信息资源管理基础

导读: 信息琢磨不定,你感觉到它却不能很好理解它,只有很好的理解它才能更好的去感受 它. 花了一点时间对第一章进行了详读,顿时感觉文科生的优势出来了,啊,强大的抽象 理解力,开个玩笑.言归正传,第一章就是介绍了什么是"化".什么是信息.什么是信息 资源.什么信息资源管理.都是些概念性的东西,需要大量的深入脑髓的理解工作. 自己学习完对整章进行了细致的总结,基本上所有的重点都囊括了.画了思维导图进 行宏观理解.基本上好好看这篇博客可以节省半个小时+回答第一章课后练习.值得一 看. 知识点