Zigbee协议栈开发笔记(三)网络广播实验
<p>Zigbee网络中进行数据通信主要有三种类型,广播,组播,单播</p><p>在消息发送函数中</p><pre class="prism-highlight prism-language-c">my_DstAddr.addrMode = (afAddrMode_t)Addr16Bit;//何种方式发送(单播)</pre><p>可以设置消息发送的形式</p><p>当addrMODE=AddrBroadcast时,使用广播方式发送数据</p><p>addrMODE=AddrGroup 组播</p><p>addrMODE=Addr16Bit 单播</p><p> </p><p>在代码中这样具体实现:</p><ol class=" list-paddingleft-2"><li><div style="text-align: justify">定义一个afAddrType_t类型的变量</div><p>afAddrType_t Send DataAddr</p></li><li><div style="text-align: justify">然后,设置其addrMode参数,例如</div><p>SendDataAddr.addrMode = (afAddrMode_t)Addr16Bit;</p></li><li><div style="text-align: justify">最后,调用AF_DataRequest函数发送数据</div><p>AF_DataRequest(&SendDataAddr,……)</p></li></ol><h3>网络通信实验</h3><p>协调器周期性以广播的形式向终端节点发送数据(5s),终端收到数据后将板上的led灯翻转,同时向协调器发送EndDevice received! 协调器收到数据后通过串口输出到pc</p><h4>协调器编程</h4><p>要修改的代码有</p><p>定义部分</p><pre class="prism-highlight prism-language-c">#define SEND_TO_ALL_EVENT 0x01//定义发送事件</pre><p>在事件处理函数中,加入两种情况,</p><p>收到新数据后,将事件交给消息处理函数</p><p>建立网络后,每5s发送一次数据广播</p><pre class="prism-highlight prism-language-c">switch(MSGpkt->hdr.event)
{
case AF_INCOMING_MSG_CMD://如果是无线数据,用下面函数处理
GenericApp_MessageMSGCB( MSGpkt);//11111
break;
case ZDO_STATE_CHANGE:
GenericApp_NwkState = (devStates_t)(MSGpkt->hdr.status);
if(GenericApp_NwkState == DEV_ZB_COORD)
{
osal_start_timerEx(GenericApp_TaskID,SEND_TO_ALL_EVENT,5000);
}
default:
break;
}</pre><p>消息处理函数中,将消息推到串口,当建立网络后,设置5s定时,定时到达后,设置SEND_TO_ALL_EVENT事件,在该事件中调用数据发送函数,</p><p>在收到终端发回的数据后,读取并发送到串口,此时需改写消息接收函数,跟之前串口实验的写法区别不大</p><p>还需要修改数据发送函数,将发送方式改为广播,发送地址改为0xFFFF,以及定义发送数据包的数据内容指针和消息长度:</p><pre class="prism-highlight prism-language-c">my_DstAddr.addrMode = (afAddrMode_t)AddrBroadcast;//何种方式发送(广播)
my_DstAddr.endPoint = GENERICAPP_ENDPOINT;//初始化端口号
my_DstAddr.addr.shortAddr = 0xFFFF;//广播地址为0xFFFF
AF_DataRequest( &my_DstAddr, //目的地节点的网络地址和发送数据的格式
&GenericApp_epDesc,//端口号
GENERICAPP_CLUSTERID,//命令号
osal_strlen(theMessageData)+1,//数据长度
theMessageData,//指向数据缓冲区的指针
&GenericApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS);</pre><p> </p><p>由于是目前计划中最后一个打算学习的例程代码,下面贴上协调器的全部代码</p><pre class="prism-highlight prism-language-c">#include "OSAL.h"//为了实现多事件和多任务切换,需要把事件和任务对应的应用,并起一个名字OSAL操作系统抽象层。
#include "AF.h"
#include "ZDApp.h"
#include "ZDObject.h"
#include "ZDprofile.h"
#include <string.h>
#include "Coordinator.h"
#include "DebugTrace.h"
#if !defined( WIN32)
#include "OnBoard.h"
#endif
#include "hal_lcd.h"
#include "hal_led.h"
#include "hal_key.h"
#include "hal_uart.h"
#define SEND_TO_ALL_EVENT 0x01//定义发送事件
const cId_t GenericApp_ClusterList[GENERICAPP_MAX_CLUSTERS] =
{
GENERICAPP_CLUSTERID
};//const 常量
const SimpleDescriptionFormat_t GenericApp_SimpleDesc =
{
GENERICAPP_ENDPOINT, // int Endpoint;
GENERICAPP_PROFID, // uint16 AppProfId[2];
GENERICAPP_DEVICEID, // uint16 AppDeviceId[2];
GENERICAPP_DEVICE_VERSION, // int AppDevVer:4;
GENERICAPP_FLAGS, // int AppFlags:4;
GENERICAPP_MAX_CLUSTERS, // byte AppNumInClusters;
(cId_t *)GenericApp_ClusterList, // byte *pAppInClusterList;
0, // byte AppNumInClusters;
(cId_t *)NULL // byte *pAppInClusterList;
};//上述数据姐构体可以用来描述一个eigbee设备节点,成为简单设备描述符
endPointDesc_t GenericApp_epDesc;//endPointDesc_t 是结构体,定义在AF.h
devStates_t GenericApp_NwkState;//定义存储网络状态的变量
byte GenericApp_TaskID;
byte GenericApp_TransID;
//unsigned char uartbuf[128];//串口缓冲区
//节点描述符 GenericApp_epDesc 任务优先级TaskID 数据发送序列号TransID
/*typedef union h
{
uint8 TEMP[4];
struct RFRXBUF
{
unsigned char Head ;
unsigned char value[2];
unsigned char Tail;
}BUF;
}TEMPERATUER;//定义共用体,规定数据包的格式*/
void GenericApp_MessageMSGCB( afIncomingMSGPacket_t *pckt );//消息处理函数
void GenericApp_SendTheMessage( void );//数据发送函数
//static void rxCB(uint8 port,uint8 event);//串口相关
void GenericApp_Init( byte task_id )
{
halUARTCfg_t uartConfig;//定义串口配置结构体
GenericApp_TaskID = task_id;//初始化任务优先级
GenericApp_TransID = 0;//将发送数据包序号初始化,
//每发送一个包自动加一,在接收端查看以计算丢包率
GenericApp_epDesc.endPoint = GENERICAPP_ENDPOINT;//
GenericApp_epDesc.task_id = &GenericApp_TaskID;//
GenericApp_epDesc.simpleDesc
= (SimpleDescriptionFormat_t *)&GenericApp_SimpleDesc;//
GenericApp_epDesc.latencyReq = noLatencyReqs;//
afRegister( &GenericApp_epDesc );//调用函数为节点描述符注册,以使用OSAL提供的服务
/*以下是串口收发实验新增部分,对串口进行配置,如是否打开串口,波特率,是否使用流控等*/
uartConfig.configured = TRUE;
uartConfig.baudRate = HAL_UART_BR_115200;
uartConfig.flowControl = FALSE;
uartConfig.callBackFunc = NULL;//在串口配置部分,回调函数不需要了
HalUARTOpen(0,&uartConfig);//最后使用HalUARTOpen对串口进行初始化
}
// 上述代码是该任务初始化函数
//注释为空的部分较为固定,不用修改
UINT16 GenericApp_ProcessEvent( byte task_id, UINT16 events )
{
//以下代码在基础串口收发实验时不用 ,将事件处理函数改为空函数
afIncomingMSGPacket_t *MSGpkt;//定义了一个指向消息接受结构体的指针MSGpkt
if(events & SYS_EVENT_MSG)
{
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive(GenericApp_TaskID);//使用osal_msg_receive函数从消息队列上接受消息
//该消息包含了指向接收到数据包的指针
while(MSGpkt)
{
switch(MSGpkt->hdr.event)
{
case AF_INCOMING_MSG_CMD://如果是无线数据,用下面函数处理
GenericApp_MessageMSGCB( MSGpkt);//11111
break;
case ZDO_STATE_CHANGE:
GenericApp_NwkState = (devStates_t)(MSGpkt->hdr.status);
if(GenericApp_NwkState == DEV_ZB_COORD)
{
osal_start_timerEx(GenericApp_TaskID,SEND_TO_ALL_EVENT,5000);
}
default:
break;
}
osal_msg_deallocate( (uint8 *)MSGpkt);//处理完消息需要释放内存空间,调用deallocate函数以释放
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive
(GenericApp_TaskID);//处理队列中下一个消息直到处理完
}
return(events ^ SYS_EVENT_MSG);
}
if(events & SEND_TO_ALL_EVENT)
{
GenericApp_SendTheMessage();
osal_start_timerEx(GenericApp_TaskID,SEND_TO_ALL_EVENT,5000);
}
return 0;
}
//上述代码是消息处理函数,大部分固定,唯一可以修改的是注释标记处
//需要熟悉该函数的格式
/*串口实验中新增*/
/*static void rxCB(uint8 port,uint8 event)
{
HalUARTRead(0,uartbuf,7);//调用函数,从串口读取数据并存放在uartbuf中
if(osal_memcmp(uartbuf,"ldztzrh",7))//判断接受到的数据,如果是,返回TURE
{
HalUARTWrite(0,uartbuf,8);//调用该函数,发送收到的数据
}
}*/
//osal_memcmp()经常使用,是一个回调函数,在 uartConfig.callBackFunc = rxCB; 处
//将rxCB传递给了成员callBackFunc,是一个函数指针,也就是将函数的地址传给callBackFunc
//这样就可以通过callBackFunc成员函数来调用rxCB()函数了。
//回调函数是在特定条件或事件发生时,由另一方调用的,用于响应
//以下代码在基础串口收发实验时不用
void GenericApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
char buf[20];
char a = '\n';
//TEMPERATUER temperature;//定义TEMPERATURE类型变量temperature
switch(pkt->clusterId)
{
case GENERICAPP_CLUSTERID:
osal_memcpy(buf,pkt->cmd.Data,20);//使用osal_memcpy函数将收到的数据拷贝到变量中
HalUARTWrite(0,(int8*)&buf,20);//因为该函数的第二个参数是uint 8*类型的指针,所以需要类型强转
HalUARTWrite(0,(int8*)&a,1);//输出回车换行
break;
}
}
void GenericApp_SendTheMessage(void)
{
unsigned char *theMessageData = "Coordinator send!";//用于存放要发送的数据
//uint8 tvalue;
//TEMPERATUER temperature;
//temperature.BUF.Head = '&';
//tvalue = readTemp();
//temperature.BUF.value[0] = tvalue/10+'0';
//temperature.BUF.value[1] = tvalue%10+'0';
//temperature.BUF.Tail = 'C';
afAddrType_t my_DstAddr;//为满足AF_DataRequest第一个参数定义的变量
my_DstAddr.addrMode = (afAddrMode_t)AddrBroadcast;//何种方式发送(广播)
my_DstAddr.endPoint = GENERICAPP_ENDPOINT;//初始化端口号
my_DstAddr.addr.shortAddr = 0xFFFF;//广播地址为0xFFFF
AF_DataRequest( &my_DstAddr, //目的地节点的网络地址和发送数据的格式
&GenericApp_epDesc,//端口号
GENERICAPP_CLUSTERID,//命令号
osal_strlen(theMessageData)+1,//数据长度
theMessageData,//指向数据缓冲区的指针
&GenericApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS);
}</pre><h4>终端编程</h4><p>此时终端节点也具有了接受消息的功能,所以需要将之前写在协调器里的事件处理和消息处理函数移植到终端中。</p><p>在改写消息处理函数时,如果收到协调器发送来的Cordinatro send!时,使用消息发送函数发送返回信息,并且将LED状态反转</p><pre class="prism-highlight prism-language-c">#include "OSAL.h"
#include "AF.h"
#include "ZDApp.h"
#include "ZDObject.h"
#include "ZDProfile.h"
#include <string.h>
#include "Coordinator.h"
#include "DebugTrace.h"
#include "Sensor.h"
#if !defined( WIN32 )
#include "OnBoard.h"
#endif
#include "hal_lcd.h"
#include "hal_led.h"
#include "hal_key.h"
#include "hal_uart.h"
//#define SEND_DATA_EVENT 0x01//添加新事件
const cId_t GenericApp_ClusterList[GENERICAPP_MAX_CLUSTERS] =
{
GENERICAPP_CLUSTERID
};
const SimpleDescriptionFormat_t GenericApp_SimpleDesc =
{
GENERICAPP_ENDPOINT, // int Endpoint;
GENERICAPP_PROFID, // uint16 AppProfId[2];
GENERICAPP_DEVICEID, // uint16 AppDeviceId[2];
GENERICAPP_DEVICE_VERSION, // int AppDevVer:4;
GENERICAPP_FLAGS, // int AppFlags:4;
GENERICAPP_MAX_CLUSTERS, // byte AppNumInClusters;
(cId_t *)GenericApp_ClusterList, // byte *pAppInClusterList;
GENERICAPP_MAX_CLUSTERS, // byte AppNumInClusters;
(cId_t *)GenericApp_ClusterList // byte *pAppInClusterList;
};//描述一个设备节点,参考coordinator.c文件
endPointDesc_t GenericApp_epDesc;
byte GenericApp_TaskID;
byte GenericApp_TransID;
devStates_t GenericApp_NwkState;
/*定义四个变量
//节点描述符 GenericApp_epDesc 任务优先级TaskID 数据发送序列号TransID
GenericApp_NwkState 保存节点状态*/
/*typedef union h
{
uint8 TEMP[4];
struct RFRXBUF
{
unsigned char Head ;
unsigned char value[2];
unsigned char Tail;
}BUF;
}TEMPERATUER;//定义共用体,规定数据包的格式
*/
void GenericApp_MessageMSGCB( afIncomingMSGPacket_t *pckt );
void GenericApp_SendTheMessage( void );//发送函数
void GenericApp_Init( byte task_id )
{
GenericApp_TaskID = task_id;//初始化任务优先级
GenericApp_NwkState = DEV_INIT;//状态初始化为DEV_INIT表示该节点没有链接到网络
GenericApp_TransID = 0;//将发送数据包序号初始化,
//每发送一个包自动加一,在接收端查看以计算丢包率
GenericApp_epDesc.endPoint = GENERICAPP_ENDPOINT;//
GenericApp_epDesc.task_id = &GenericApp_TaskID;//
GenericApp_epDesc.simpleDesc
= (SimpleDescriptionFormat_t *)&GenericApp_SimpleDesc;//
GenericApp_epDesc.latencyReq = noLatencyReqs;//
afRegister( &GenericApp_epDesc );//调用函数为节点描述符注册,以使用OSAL提供的服务
}
// 上述代码是该任务初始化函数
//注释为空的部分较为固定,不用修改
UINT16 GenericApp_ProcessEvent( byte task_id, UINT16 events )
{
afIncomingMSGPacket_t *MSGpkt;//定义了一个指向消息接受结构体的指针MSGpkt
if(events & SYS_EVENT_MSG)
{
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive(GenericApp_TaskID);//使用osal_msg_receive函数从消息队列上接受消息
//该消息包含了指向接收到数据包的指针
while(MSGpkt)
{
switch(MSGpkt->hdr.event)
{
case AF_INCOMING_MSG_CMD:
GenericApp_MessageMSGCB(MSGpkt);
break;
default:
break;
}
osal_msg_deallocate((uint8*)MSGpkt);
MSGpkt =
(afIncomingMSGPacket_t *)osal_msg_receive( GenericApp_TaskID);
}
return (events ^ SYS_EVENT_MSG);
}
return 0;
}
void GenericApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
char *recvbuf;
//char a = '\n';
//TEMPERATUER temperature;//定义TEMPERATURE类型变量temperature
switch(pkt->clusterId)
{
case GENERICAPP_CLUSTERID:
osal_memcpy(recvbuf,pkt->cmd.Data,osal_strlen("Coordinator send!")+1);//使用osal_memcpy函数将收到的数据拷贝到变量中
if(osal_memcpy(recvbuf,"Coordinator send!",osal_strlen("Coordinator send!")+1))
{
GenericApp_SendTheMessage();
}
else
{
}
break;
}
}
/*在串口收发实验中,作用是调用无线发送函数,并且定时1s*/
/*
上述代码是消息处理函数,大部分固定
需要熟悉该函数的格式
*/
void GenericApp_SendTheMessage(void)
{
//unsigned char theMessageData[10] = "EndDevice";//用于存放要发送的数据
//uint8 tvalue;
//TEMPERATUER temperature;
//temperature.BUF.Head = '&';
//tvalue = readTemp();
//temperature.BUF.value[0] = tvalue/10+'0';
//temperature.BUF.value[1] = tvalue%10+'0';
//temperature.BUF.Tail = 'C';
unsigned char *theMessageData = "EndDvice received!";
afAddrType_t my_DstAddr;//为满足AF_DataRequest第一个参数定义的变量
my_DstAddr.addrMode = (afAddrMode_t)Addr16Bit;//何种方式发送(单播)
my_DstAddr.endPoint = GENERICAPP_ENDPOINT;//初始化端口号
my_DstAddr.addr.shortAddr = 0x0000;//协调器地址固定为0x0000
AF_DataRequest( &my_DstAddr, //目的地节点的网络地址和发送数据的格式
&GenericApp_epDesc,//端口号
GENERICAPP_CLUSTERID,//命令号
osal_strlen(theMessageData)+1,//数据长度
theMessageData,//指向数据缓冲区的指针
&GenericApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS);
HalLedSet(HAL_LED_2,HAL_LED_MODE_TOGGLE);
}
/*数据类型afAddrType和函数AF_DataRequest原型都在书上*/</pre><p><br/></p>
Q.E.D.