目录
Zigbee MAC地址通信
前言
查看MAC地址(含组网过程抓包分析)
方法1:通过dongle抓包查看MAC地址
方法2:仿真调试查看MAC
实验过程
实现步骤
实验效果
出错分析
最终现象
结果分析
Zigbee MAC地址通信
前言
1、Zigbee模块在无线网络里两个地址:
- 2个字节在网络里唯一的网络短地址
- TI在每个CC2530芯片出厂的时候,每一颗芯片都固化了一个唯一的8个字节的硬件地址、MAC或者IEEE地址
2、本次实验我们通过发送模块(终端),按下S1,通过指定接收方MAC地址的方法发送数据包。当接收模块(协调器)接收到数据后,将接收内容通过串口发给电脑上位机。
3、本实验用到的新知识点:
#include "NLMEDE.h"
extern uint16 NLME_GetShortAddr( void );//获取当前模块的网络地址
extern byte *NLME_GetExtAddr( void );//获取当前模块的MAC
extern uint16 NLME_GetCoordShortAddr( void );//获取父节点的网络地址
extern void NLME_GetCoordExtAddr( byte * );//获取父节点的MAC
查看MAC地址(含组网过程抓包分析)
方法1:通过dongle抓包查看MAC地址
1、协调器模块先上电
第一帧是协调器模块发出的信标请求帧,等待其它模块回复。功能是协调器用于判断周围有没有其它模块(对于终端和路由器,上电也会发此帧,作用是让在它附近的所有具备介绍人资格的节点都回复信标帧,这些返回的信标帧被这个想要加入的无线模块拿到,通过这些信标帧选出最佳介绍人节点,请求加入。)
第二帧是协调器模块发出的网络连接状态帧数据帧,表示协调器创建无线网络成功。
第三帧与第二帧一样,网络连接状态帧如果没有模块回复,会默认每隔15s,重新发送一次,目的是告诉周围模块网络创建成功,等待其它模块的加入
我们可以看见,如果没有模块回应会一直发网络连接状态帧,且每隔15s一次。
2、接着终端上电
创建新页面重新抓包
第一帧还是协调器发出的网络连接状态帧
第二帧是终端模块发出的信标请求帧,用于发现周围的网络,请求加入
第三帧是协调器模块发出的信标帧,终端模块在拿到这一帧之后可以得到协调器模块相对于它自己的信号强度,判断是不是最佳介绍人(其中就包含了协调器的MAC地址)
第四帧——第七帧重复信标请求接收的过程(由于终端还没有正式入网,所以终端发送的所有帧的目的PANID都是0xFFFF;而协调器目的网络PANID是自己创建的网络PANID)
第八帧是由终端模块发送给协调器模块,目的是告诉协调器,你可以作为我的介绍人,并把自己的MAC地址告诉对方
第九帧是协调器模块硬件回复给终端模块的ACK,表明协调器模块已经收到了终端发过来的请求帧,接受成为终端的介绍人
第十帧是终端发给协调器模块的帧,请求协调器,把根据前面发送的MAC地址分频好的网络地址发给终端
第十一帧:协调器模块硬件回复给终端的ACK,表明收到了终端的请求。
第十二帧:协调器把为终端分配好的网络地址发给终端。这个帧需要非常明确地发给终端,但是终端模块还不知道自己的网络地址是多少,所以在指定目标地址的时候用MAC地址。
第十三帧:在终端模块根据自己的MAC地址收到了协调器发过来分频给自己的网络地址,硬件自动回复ACK,表明自己已经收到
第十四帧:终端模块发送的入网宣告帧,目的是协调器我成功入网了,我的网络地址是0xB463。(这里与协调器与路由器组网不同,路由器成功入网后,会主动广播入网宣告;而终端是先发送给协调器,然后再通过协调器来广播转发给网络里所有节点。这里体现了,终端和路由器的差异,终端与父节点之外的模块进行收发消息必须要经过父节点,且终端无法作为父节点)
第十五帧:协调器模块硬件回复给终端的ACK,表明收到了终端的请求。
第十六帧:协调器模块在收到了终端模块发出的入网宣告帧后,将内容进行广播转发(此时已经入网成功)
第十七、十八帧表示协调器模块和终端模块在成功组网后,协调器向子节点终端发送应答命令帧,然后终端回复。这样是为了保持父子节点之间的通信。
之后的所有帧都是重复第十七、十八帧的内容。(终端作为子节点,父子节点每隔固定时间会进行一次询问)
而协调器和终端组网后,协调器依然会每隔15s广播发送网络连接状态帧,来等待其它更多模块的加入(如下图所示)。
协调器和路由器
第十三帧以及之前的入网过程与终端入网相同,之间从第十四帧开始。
第十四和第十五帧是路由器的入网宣告帧,路由器先广播,父节点协调器收到后进行广播转发
第十六帧和第十七帧分别是协调器和路由器发送的网络连接状态帧,目的是表明自己当前状态稳定,可以等待其它模块的加入(成为其子节点入网),与终端不同,路由器入网后,不会直接与父节点进行应答回复,而是通过网络连接状态帧的广播向父节点表示当前连接稳定。
第十八帧开始及之后都是重复第十六、十七帧的内容。
我们记录下这两个模块的MAC
模块A:0x00124B002A59E327
模块B:0x00124B002A59E7FD
方法2:仿真调试查看MAC
我们需要用到两个函数,NLME_GetShortAddr用于获取当前模块的网络地址,NLME_GetExtAddr用于获取当前模块的物理地址。在NLMEDE.h中声明。
extern uint16 NLME_GetShortAddr( void );
extern byte *NLME_GetExtAddr( void );
首先在应用层文件定义两个变量并初始化 uint16 NwkAddr=0xFFFF; uint8 MacAddr[8];
然后再协调器上电初始化消息判断处添加函数调用和赋值。
其中用到的memcpy这里做个说明:strcpy和memcpy两者都是C语言里的库函数。strcpy 只能拷贝字符串,它遇到'\0'就结束拷贝;memcpy 是用来做内存拷贝,可以用来拷贝任何数据类型的对象,可以指定拷贝的数据长度。memcpy提供了一般内存的复制,对于需要复制的内容没有限制,用途更广。
接着下载协调器程序,进行仿真,设置断点,Go全速运行
点击add to watch将NwkAdd加入调试变量窗口
此时可以发现NwkAdd的值是初始值0xffff
然后先取消,接着step over,可以发现NwkAdd的值变为了0,因此本机的网络地址为0XFFFF
同样将MacAddr添加到调试窗口
step over后,得到我们当前设备的MAC地址为0x00124B002A59E7FD
此外我们还可以通过下面这两个函数获取当前模块父节点的网络地址和MAC地址。
extern uint16 NLME_GetCoordShortAddr( void );
extern void NLME_GetCoordExtAddr( byte * );
实验过程
实现步骤
编写发送过程有两个地方需要注意:发送模式我们要配置成Addr64Bit(表示通过MAC发送),目标地址我们不用shortAddr,而用extAddr(数组)。因此我们通过自己定义一共DstMac数组来进行Mac地址赋值。
注意:将MAC地址填入数组时要按主机字节序(小端格式)来填,顺序不能反。
接收模块我们不需要怎么改,数据接收处理函数如下即可
实验效果
我将程序分别下载进模块,组网成功后,终端按下按键,接收模块协调器什么反应也没有,上位机也没接收到消息。
通过抓包发现,按下按键的时候确实没有数据包发出。只有协调器模块和终端模块之间进行父子点询问的帧和协调器模块的网络状态帧。并没有我们按下按键通过指定协调器MAC发送的数据帧。
经过半天的百度查阅,但网上关于Zigbee模块MAC地址通信的资料少之又少,困扰了我一晚上,找到了一个可能可行的方法。
我参考了这篇博客https://www.cnblogs.com/gary-guo/p/5737076.html
出错分析
首先我们要搞清楚MAC地址通信的本质是什么:
在网络里面的每一个zigbee模块,它都有一个地址管理器,它会记录与它经常通信的模块的MAC地址以及网络短地址,然后存放到地址管理器里面;
以MAC地址发送数据:在数据发送的时候,它会去检查地址管理器看是否有对应的MAC地址,如果有,那么它会把对应的网络短地址放到无线数据包里面去发送;如果没有对应的MAC地址,为了确保通信成功,那么我们应该怎么做呢?
其中ZDP_NwkAddrReq(DestMac,ZDP_ADDR_REQTYPE_SINGLE,0,0);的作用是让MAC对应的目标模块将当前的网络短地址回复给我
然后收到对应MAC地址的网络短地址后,就可以在地址管理器当中建立这样的映射关系,然后延时30毫秒,发送事件,GENERICAPP_SEND_MSG_EVT,然后进入到该事件里面去发送实际的数据
将程序下载到模块进行抓包
我们发现,按下按键后,请求网络地址的通信过程是正常的,而终端依然无法将数据通过MAC地址发送给协调器。不过至少验证了模块硬件没有问题。
最后我重新安装了一个TI 协议栈:ZStack-CC2530-2.3.0-1.4.0,我原来使用的版本是ZStack-CC2530-2.5.1a。
将我们的原终端程序重新移植到新的新协议栈,通信成功!
char theMessageData[] = "Zigbee MAC实验";
uint8 DestMac[8]={0xFD,0xE7,0x59,0x2A,0x00,0x4B,0x12,0x00};//0x00124B002A59E7FD
GenericApp_DstAddr.addrMode = (afAddrMode_t)Addr64Bit;//发送模式是MAC
//StarryApp_DstAddr.addr.shortAddr = 0x0000;
memcpy(GenericApp_DstAddr.addr.extAddr,DestMac,8);
// Take the first endpoint, Can be changed to search through endpoints
GenericApp_DstAddr.endPoint = GENERICAPP_ENDPOINT;
AF_DataRequest( &GenericApp_DstAddr, &GenericApp_epDesc,
GENERICAPP_CLUSTERID,
(byte)osal_strlen( theMessageData ) + 1,
(byte *)&theMessageData,
&GenericApp_TransID,
AF_DISCV_ROUTE, AF_DEFAULT_RADIUS );
最终现象
抓包发现,按下S1后,终端成功通过协调器的MAC地址发送了数据包给协调器
串口也成功接收到数据
结果分析
我们可以发现,发送数据包的目标地址不是MAC,而是目标的网络短地址。
这是因为当以MAC地址进行发送的时候,发送模块首先会查看自己的地址管理器有没有节点的MAC与我们所指定通信的MAC地址是一致的。如果发现有,它会将目标节点的网络短地址作为无线数据包的发送地址。如果两个模块之间距离很远,且不怎么互相通信,地址管理器查不到对方的MAC。那我们在发送数据包前就必须使用网络短地址请求函数
ZDP_NwkAddrReq(DestMac,ZDP_ADDR_REQTYPE_SINGLE,0,0);
通过发送一个信号,来让我们的MAC地址对应的模块把网络短地址回复给我们,之后等待30ms再发送(确保发送模块成功获取目标模块网络地址),就一定会成功。这也是我刚刚提到的一个解决数据包发送失败的处理办法。