目录
Zigbee组播通信原理
实验原理
实验过程
实验设计
发送模块
接收模块
实验现象
组播通信总结
Zigbee组播通信原理
实验原理
1、组播通信:在Zigbee无线网络里,模块可以进行分组来标记。发送的模块如果发送的组号和网络里标记模块的组号相对应,那么这些模块就可以拿到发送模块发送的这些无线数据包。
2、组播特点:
- 分组中组的编号是两个字节,和我们的簇是一样的。
- 组都是和模块里已经定义了的端点相关联,如果我们说一个模块标记为组1,那么这个模块里至少有1个定义了的可用的端点和组0x0001相关联。
- 一个组可以与多个端点关联,同样一个端点也可以同时关联多个组。
- 发送的模块按照组的方式发送了一个无线数据包,需要指定的内容:目标模块的组编号、端点号、簇,原则上只有当接收模块的这三个参数都匹配上了,才能拿到和处理这样一个无线数据包。
举例如下所示:发送模块A发送了一个无线数据包,无线数据包中指定了接收者要满足:组号为0x0001、端点号为10、簇为0x0001,因此通过判断只有模块B的10号端点最终成功接收到了数据包。
3、组播通信与广播通信相似,单个发送设备可以同时向多个接收设备发送数据。
实验过程
实验设计
本节实验代码基于上一节广播实验进行修改。
关于实验我设计的内容如下图所示:
两个模块上电成功组网后,接收模块默认不关联任何组,按钮1按下,端点将只与组0x0001关联,按钮2按下,端点将只与组0x0002关联。发送模块按下按钮1或者按钮2发送数据包,接收模块簇0x0001如果接收到数据则将有效数据发送给串口上位机,否则什么也不做。
注:按钮按下后,模块对应的LED灯闪烁3S。
发送模块
发送数据的程序我们只需将发送模式改为组播,目标地址改为组编号即可,其它不变(端点号默认为10,簇默认为0x0001)
注意StarryApp_DstAddr结构体无论是在单播、广播还是组播中都是用来指定描述接收模块的一些信息
任务事件处理函数中按键事件代码为:
if( events & StarryApp_MY_KEY_EVT)
{
keyChange_t *msgPtr;
msgPtr = (keyChange_t *)osal_msg_allocate( sizeof(keyChange_t) );//定义一个按钮状态改变的消息
if( msgPtr )
{
msgPtr->hdr.event = KEY_CHANGE;//给这个消息填写类型
if(P0_0==0)//KEY1
{
msgPtr->keys=1;//赋值
char theMessageData[] = "你好";
StarryApp_DstAddr.addrMode = (afAddrMode_t)AddrGroup;//发送模式是组播的方式
StarryApp_DstAddr.addr.shortAddr = 0x0001;//表示目标接收模块的组编号
// Take the first endpoint, Can be changed to search through endpoints
StarryApp_DstAddr.endPoint = StarryApp_ENDPOINT;//10
AF_DataRequest( &StarryApp_DstAddr, &StarryApp_epDesc,
StarryApp_CLUSTERID,//0x0001
(byte)osal_strlen( theMessageData ) + 1,
(byte *)&theMessageData,
&StarryApp_TransID,
AF_DISCV_ROUTE, AF_DEFAULT_RADIUS );
}
if(P0_1==0)//KEY2
{
msgPtr->keys=2;//赋值
char theMessageData[] = "hello";
StarryApp_DstAddr.addrMode = (afAddrMode_t)AddrGroup;//发送模式是组播的方式
StarryApp_DstAddr.addr.shortAddr = 0x0002;//表示目标接收模块的组编号
// Take the first endpoint, Can be changed to search through endpoints
StarryApp_DstAddr.endPoint = StarryApp_ENDPOINT;//10
AF_DataRequest( &StarryApp_DstAddr, &StarryApp_epDesc,
StarryApp_CLUSTERID,//0x0001
(byte)osal_strlen( theMessageData ) + 1,
(byte *)&theMessageData,
&StarryApp_TransID,
AF_DISCV_ROUTE, AF_DEFAULT_RADIUS );
}
osal_msg_send( StarryApp_TaskID, (uint8 *)msgPtr );//把发送给应用层StarryApp_TaskID的消息发送到消息队列
}
}
LED灯对应定时器如下
接收模块
在应用层初始化函数中默认挂钩了端点号10,所以我们不需要改,具体可以跳转到宏定义去查看
同样簇的编号也被默认定义了0x0001,我们也不需要动
因此我们只需要关联组即可。
我们标记组的话,首先要定义一个组的结构体变量aps_Group_t StarryApp_Group;
组的结构体和相关函数可以在aps_groups.h中找到
定义完组结构体,给组结构体的第一个成员变量ID赋值为组编号0x0001,第二个参数name可以不用,name的作用就相当于一个注释,给组起个别名,最多不能超过16个字节
然后我们调用函数ZStatus_t aps_AddGroup( uint8 endpoint, aps_Group_t *group );(也在组头文件中有声明),它的作用是将我们的端点与组关联起来。(第一个参数为关联的端点号,第二个参数是组结构体的地址)
在关联之前我们还需要调用函数extern uint8 aps_RemoveGroup( uint8 endpoint, uint16 groupID );,它的作用时去除当前端点所关联的组编号。来确保只有一个组与我们的10号端点关联。(第一个参数是我们要取消关联的端点,第二个参数是取消关联的组号)
按键操作程序如下:
if( events & StarryApp_MY_KEY_EVT)
{
keyChange_t *msgPtr;
aps_Group_t StarryApp_Group;
msgPtr = (keyChange_t *)osal_msg_allocate( sizeof(keyChange_t) );//定义一个按钮状态改变的消息
if( msgPtr )
{
msgPtr->hdr.event = KEY_CHANGE;//给这个消息填写类型
if(P0_0==0)//KEY1
{
msgPtr->keys=1;//赋值
aps_RemoveGroup(10,0x0002);
//如果10号端点关联了0x0002,那么就取消组2的关联;如果没有关联,就不做处理。
StarryApp_Group.ID=0x0001;
aps_AddGroup(10,&StarryApp_Group);
}
if(P0_1==0)//KEY2
{
msgPtr->keys=2;//赋值
aps_RemoveGroup(10,0x0001);
//如果10号端点关联了0x0001,那么就取消组2的关联;如果没有关联,就不做处理。
StarryApp_Group.ID=0x0002;
aps_AddGroup(10,&StarryApp_Group);
}
osal_msg_send( StarryApp_TaskID, (uint8 *)msgPtr );//把发送给应用层StarryApp_TaskID的消息发送到消息队列
}
}
消息处理函数要修改成适合组播的接收处理程序,其中pkt结构体变量的groupId成员,如果为0,表示收到的数据包不是通过组播发送的,而是通过单播或者广播等
static void StarryApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
unsigned char rcvbuff[40]={0};
unsigned char str[20];
if(pkt->groupId==0x0001)
{
if(pkt->endPoint==10)
{
switch(pkt->clusterId)
{
case 0x0001:
strncpy(str,pkt->cmd.Data,pkt->cmd.DataLength);
sprintf((unsigned char*)rcvbuff,"receive: %s\n",str);
String_Print("组0x0001 ");
String_Print(rcvbuff);
break;
}
}
}
if(pkt->groupId==0x0002)
{
if(pkt->endPoint==10)
{
switch(pkt->clusterId)
{
case 0x0001:
strncpy(str,pkt->cmd.Data,pkt->cmd.DataLength);
sprintf((unsigned char*)rcvbuff,"receive: %s\n",str);
String_Print("组0x0002 ");
String_Print(rcvbuff);
break;
}
}
}
}
实验现象
我们将发送程序下载到终端,接收程序分别下载到协调器和路由器。由于LED灯的闪烁效果不方便拍照,我在这里只截取了通信过程中的上位机串口显示,也仅说明串口显示的过程。
首先三个模块分别上电:协调器->路由器->终端(谁先上电无所谓,最好协调器先上电创建好网络,其它模块再上电)
然后终端按下S1和S2,此时发现只有终端的LED灯发生闪烁;协调器和终端没有收到任何数据,说明当前两个接收模块都还未关联组。
接下来按下协调器的S1(关联组0x0001),然后按下终端的S1,协调器成功收到路由器数据
再来按下协调器的S2(关联组0x0002),然后按下终端的S2,协调器成功收到路由器数据
最后我们再按下路由器的S2(关联组0x0002),然后再按下终端的S2,发现协调器模块和终端模块同时收到数据。(到此成功验证了我们组播模块通信实验正确无误)
组播通信总结
- 对于发送模块来说组播和广播没有什么太大区别,仅仅是发送的地址模式和地址内容这两个参数要修改为组播。
- 对于接收模块来说,组播用到了组的相关结构体,因此要在应用层文件中添加组头文件aps_groups.h,并且在数据处理函数中要添加组号的相关判断。
- 此外通过实验可以发现,组播通信与广播通信相似,单个发送设备可以同时向多个接收设备发送数据。
- 还有消息处理函数要修改成适合组播的接收处理程序,其中pkt结构体变量的groupId成员,如果为0,表示收到的数据包不是通过组播发送的,而是通过单播或者广播等。