目录
Zigbee单播通信理论相关概念原理
端点(Endpoint)
簇(ClusterID)
通信数据帧抓包分析
接收过程中的端点和簇(接收模块)
接收过程中的端点
接收过程中的簇
发送过程中的端点和簇(发送模块)
属性
Zigbee单播通信理论相关概念原理
端点(Endpoint)
1、它是一个字节编号的,数据接收和发送的基本单元,在模块通信的时候,发送模块必须指定收发双方模块的网络地址和端点。(端点编号是0-255)
2、端点在使用前定义的时候,必须要和模块里的某个任务挂钩。
首先每一个端点可以看成是一个1给字节数字编号的开有一扇门的房间,数据的最终目标是进入到无线数据包指定的目标端点房间,而取无线数据这个相关代码在任务事件处理函数中,而TI协议栈每一个层都有各自的一个任务事件处理函数,所以必须要指定在哪个任务事件处理函数来取这个无线数据包中有用的数据。
3、一个端点只能挂钩在一个任务上,而一个任务可以挂钩多个端点,并且端点对所有的任务是公用的,定义一个少一个。
一个端点假如可以挂钩在多个任务上,那么接收模块接收到无线数据的时候,同一个端点有多个任务事件处理函数去处理,不合理;一个任务上挂钩多个端点(如6和7挂在应用层任务),发送给协调器模块的6、7端点的数据都会进入到应用层任务事件处理函数里来,仅仅做个判断到底是投递到6号房间还是7号房间就可以了。
举例:假如我们接收模块定义两个两个端点6和7,并且都挂钩在应用层,那么端点6和7就不能再被其它层定义和挂钩。如果发送方发送一个无线数据到接收模块的6号端点或者7号端点,那么数据都会进入到应用层任务的任务事件处理函数里来,最后再判断是进入到6号端点房间还是7号端点房间。
4、如果同时有两个端点与应用层任务挂钩,那么一旦接收到数据,最后都会到StarryApp_MessageMSGCB中,因此我们可以针对不同端点进行区分,将外部来数据处理函数StarryApp_MessageMSGCB修改如下:
static void StarryApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
if(应该接收的目标端点A)
{
switch ( pkt->clusterId )
{
case StarryApp_CLUSTERID:
... ...
break;
}
}
if(应该接收的目标端点B)
{
switch ( pkt->clusterId )
{
case StarryApp_CLUSTERID:
... ...
break;
}
}
... ...
}
簇(ClusterID)
簇就是相对于端点房间里的人,是数据的最终接收者,可以有很多个。它为2个字节编号。在射频发送的时候,必须要指定接收模块的簇,发送模块不需要指定。(具体后面有补充)
通信数据帧抓包分析
继续用上一次的终端模块和协调器模块,两个模块组网后,终端模块按下按键S1,发送数据包。协调器接收到数据包后,将数据包的有效信息通过串口发送给电脑上位机。
SmartRF Packet Sniffer抓包如下:
发送数据帧
应答信号帧
- 其中P.nbr.和Time(us)都是抓包工具自动生成的(和实际数据帧没有关系),P.nbr.表示从捕获开始空气中的第几个数据帧,Time(us)表示两帧之间的间隔时间以及从抓包开启到当前的时间长度
- 从Length开始才是数据包携带的信息。其中Length表示当前这个帧一共有多少个字节
- Dest Address为目标地址,Src Address为源地址(网络地址)
- Dest Endpoint为目标端点:10;Src Endpoint为源端点:10
- 帧末尾的LQI表示当前帧的信号强度是多少,最大为255;FCS表示硬件校验,校验成功为OK,失败为ERR
- APS Payload域里面的内容就是我们发送模块发送的有效信息,都为16进制表示,将十六进制转换为十进制为:50 48 50 51 45 48 54 45 51 48 0,转换成ASCLL码就是2023-06-30\0,也就是我们程序中发送的字符串
- Cluster Id是簇,我们在后面介绍
- APS Counter表示当前发送模块发送的第几个数据包(数据帧)
- APS Profile Id在端点描述符里有提到,也待会介绍
接收过程中的端点和簇(接收模块)
接收过程中的端点
打开协议栈工程,在SampleApp.c应用层文件中的应用初始化函数StarryApp_Init中,我们可以找到端点结构体的初始化和端点的登记注册
void StarryApp_Init( uint8 task_id )
{
... ...
// Fill out the endpoint description.
StarryApp_epDesc.endPoint = StarryApp_ENDPOINT;
StarryApp_epDesc.task_id = &StarryApp_TaskID;
StarryApp_epDesc.simpleDesc
= (SimpleDescriptionFormat_t *)&StarryApp_SimpleDesc;
StarryApp_epDesc.latencyReq = noLatencyReqs;
// Register the endpoint description with the AF
afRegister( &StarryApp_epDesc );
... ...
}
注:端点在脑海中被抽象成了带编号的房子,在代码中实际上是一个结构体endPointDesc_t
下面我们分析下结构体的成员如何赋值
- 其中StarryApp_epDesc.endPoint = StarryApp_ENDPOINT;的StarryApp_ENDPOINT是一个宏,协议栈中默认是10
- 第二个StarryApp_epDesc.task_id = &StarryApp_TaskID;中的StarryApp_TaskID是任务ID号,这里表示应用层任务ID,通过这步赋值,端点就会与应用层任务挂钩起来了
- 第三个StarryApp_epDesc.simpleDesc = (SimpleDescriptionFormat_t *)&StarryApp_SimpleDesc;中的StarryApp_SimpleDesc存储其它信息,为了更加详细描述这个端点的一些情况,就像我们定义一个编号房间,描述房间里大概有多少人之类的信息
- 第四个StarryApp_epDesc.latencyReq = noLatencyReqs;和第三个结构体成员一样,也是用来详细描述其它的一些信息
最后我们一定要调用afRegister( &StarryApp_epDesc );函数,才能完成这个挂钩操作
接收过程中的簇
在接收过程中,簇只在接收数据处理函数用到,用于判断簇(最终接收人)是谁来签收数据。
接收数据处理函数中,默认只有一个簇ID:StarryApp_CLUSTERID是0x01,我们可以自己在switch语句中添加更多的簇
补充:簇的个数由接收数据处理函数中的case来决定,几个case就有几个簇(接收人) ,定义在结构体
所以基本实验(模块0xF680 端点10 发送数据给 模块0x0000 端点10)的过程概况如下:
当数据帧从终端模块发出去以后,首先和目标协调器模块的网络地址0x0000匹配上了,协调器可以在底层任务拿到这个无线数据包,判断10号端点房间已经定义并且和我们的应用层任务挂钩,那么就会发送一个消息给我们的应用层任务,然后到任务处理函数中的"case AF_INCOMING_MSG_CMD:"中的来数据处理函数中,通过簇来判断,最终将数据包有效信息通过串口发送给上位机。
发送过程中的端点和簇(发送模块)
在发送模块里,我们用的数据发送源端点编号也是10,所以我们定义这个端点10也和应用层任务挂钩。原则上,外部一旦给我们的此实验的发送模块(终端模块)的10号端点来数据,也会进入应用层任务事件处理函数中。虽然我们这个端点仅仅作为发送模块,但是我们要使用10号端点就必须要挂钩定义。(这里定义的端点10既可以当本模块作为发送方时的源端点,也可以当本模块作为接收方时的目标端点。在本实验中作为源端点)
因此我们来看发送模块的代码,发现和接收模块定义的端点一模一样
发送信号帧的内容在代码里的体现:
在StarryApp_ProcessEvent的射频数据发送函数配置中
char theMessageData[]="2023-06-30";
StarryApp_DstAddr.addrMode = (afAddrMode_t)Addr16Bit;
StarryApp_DstAddr.addr.shortAddr = 0x0000;//接收模块的网络地址
// Take the first endpoint, Can be changed to search through endpoints
StarryApp_DstAddr.endPoint = StarryApp_ENDPOINT;//接收模块的端点房间号
//StarryApp_epDesc 结构体:端点描述符,其中包含源端点的信息,端点号也是10
AF_DataRequest( &StarryApp_DstAddr, &StarryApp_epDesc
StarryApp_CLUSTERID,//目标端点簇,房间里的接收人数据宏默认是1;簇是2个字节,所以在射频是0x0001
(byte)osal_strlen( theMessageData ) + 1,
(byte *)&theMessageData,//有效数据内容
&StarryApp_TransID,//记录我们应用层任务从开始到当前发送的数据包个数(也能表示当前数据包是发送的第几个)
AF_DISCV_ROUTE, AF_DEFAULT_RADIUS );
端点描述符StarryApp_epDesc结构体中的SimpleDescriptionFormat_t有APS Profile Id,它在应用层初始化函数中的StarryApp_SimpleDesc中可以找到定义,默认值0x0F04
属性
属性就是在应用层有用的数据载荷,做专门规定的最小单元
比如我们规定PAS Payload中如果接收到的数据前2个字节是0x01、0x02,那么第三个字节就代表空调温度,这样的连续三个字节就叫做属性。
TI就是根据属性的内容不同发行了不同版本的协议栈,如HA智能家居、SE智能能源...