(精简)基于CC2530的ZIgbee3.0 Zstack协议ZCL命令控制收发LED实验
ZStack 2.0 和 ZStack 3.0最大的区别就是 2.0是基于AF的,3.0则是基于ZCL的。可以理解为ZCL是对AF的进一层包装。ZCL层实现了不同厂商品牌的Zigbee产品互通。
一、前言:
全程干货无废话,只讲应用层代码API调用,不深入底层!
ZStack 2.0 和 ZStack 3.0最大的区别就是 2.0是基于AF架构的,3.0则是基于ZCL架构的。
可以理解为ZCL是对AF的进一层包装。
ZCL层实现了不同厂商品牌的Zigbee产品互通。
二、实验流程:
本实验现象:终端向协调器报告自身地址,协调器收到地址信息后向终端发送控制命令,终端的LED灯周期性亮灭。

打开例程zstack\HomeAutomation\SampleSwitch\CC2530DB下的SampleSwitch.eww工程
三、应用层代码编写:
组网
首先,协调器和终端之间要通讯首先要做的就是组网,然后才能来回发送接收信息。
找到zclSampleSw_Init()函数在里面添加如下代码:
#ifdef ZDO_COORDINATOR
bdb_StartCommissioning ( BDB_COMMISSIONING_MODE_NWK_FORMATION |
BDB_COMMISSIONING_MODE_FINDING_BINDING );
//协调器组件网络函数
NLME_PermitJoiningRequest(255);
#else
bdb_StartCommissioning ( BDB_COMMISSIONING_MODE_NWK_STEERING |
BDB_COMMISSIONING_MODE_FINDING_BINDING );
//协调器加入网络函数
#endif
很简单,协调器组建网络,终端加入网络。
为了让终端入网成功,我们还需要进行容错,因为有的时候不一定一次入网成功,我们需要使用网络状态检查函数,在入网失败时再次入网,以确保稳定。
找到zclSampleSw_ProcessCommissioningStatus()网络状态检查函数,
case BDB_COMMISSIONING_NWK_STEERING:
if(bdbCommissioningModeMsg->bdbCommissioningStatus == BDB_COMMISSIONING_SUCCESS)
{
//入网成功不做处理
}
else
{
//入网失败重新入网
#ifdef ZDO_COORDINATOR
#else
osal_start_timerEx(zclSampleSw_TaskID,
SAMPLEAPP_REJOIN_EVT,
SAMPLEAPP_REJOIN_PERIOD
); //让终端重新加入网络
#endif
}
我们找到BDB_COMMISSIONING_NWK_STEERING:这个是检查终端网络的地方,可以看到当状态位bdbCommissioningModeMsg->bdbCommissioningStatus是BDB_COMMISSIONING_SUCCESS成功的时候,入网成功不做处理,当入网失败我们再次入网。这个英文命名的非常规范,我们也很好理解。
我们入网是通过定时器实现的,所以我们要定义一个再次入网事件,并把事件号和周期在头文件中定义。
打开zcl_samplesw.h头文件,添加如下代码:
#ifdef ZDO_COORDINATOR
#else
#define SAMPLEAPP_REJOIN_EVT 0x0080 //事件号
#define SAMPLEAPP_REJOIN_PERIOD 3000 //事件周期
#endif
定义入网事件具体实现。
在zcl_samplesw.c文件中找到zclSampleSw_event_loop()函数,在里面添加如下代码:
#ifdef ZDO_COORDINATOR
#else
if( events & SAMPLEAPP_REJOIN_EVT )
{
bdb_StartCommissioning( BDB_COMMISSIONING_MODE_NWK_STEERING |
BDB_COMMISSIONING_MODE_FINDING_BINDING );
return ( events ^ SAMPLEAPP_REJOIN_EVT );
}
#endif
至此,组网完毕。
终端部分:
终端部分我们要做的有两件事:1.报告协调器自身的地址 2.收到协调器命令控制自身LED
1.报告自身地址
在zcl_samplesw.c里添加代码。
#ifdef ZDO_COORDINATOR
#else
ZDP_DeviceAnnce(NLME_GetShortAddr(),NLME_GetExtAddr(),ZDO_Config_Node_Descriptor.CapabilityFlags,0);
//把自己的网络地址,物理地址告诉协调器
#endif
ZDP_DeviceAnnce 函数用来设备宣告:当一个设备加入zigbee网络时,广播其网络信息NLME_GetShortAddr(),和物理地址NLME_GetExtAddr()。
2.控制自身LED
在zcl_samplesw.c找一个空白地方,定义一个接收命令控制LED函数。
#ifdef ZDO_COORDINATOR
#else
static void zclSample_LightCMD(uint8 cmd)
{
if( cmd == COMMAND_OFF )
{
HalLedSet(HAL_LED_ALL,HAL_LED_MODE_ON);
}
else if( cmd == COMMAND_ON )
{
HalLedSet(HAL_LED_ALL,HAL_LED_MODE_OFF);
}
}
#endif
COMMAND_ON和COMMAND_OFF是协议栈定义好的宏定义。我们之所以在COMMAND_ON时熄灭所有LED,在COMMAND_OFF开启所有LED,是因为我们的开发板是低电平点亮,和TI的官方板正好相反。我们看协议栈里的宏定义就知道了。

在 zclSampleSw_CmdCallbacks()命令回调函数里添加终端控制LED函数:
#ifdef ZDO_COORDINATOR
NULL,
#else
zclSample_LightCMD, // On/Off cluster commands
#endif
该回调函数可实现:在收到协调器的CMD命令时立即启动控制LED函数。
协调器部分:
协调器部分我们要做的有两件事:1.接收终端报告的地址 2.发送控制命令
1.接收地址信息
在zclSampleSw_event_loop()函数里找到 if ( events & SYS_EVENT_MSG )
在系统消息通知里面添加如下代码:
#ifdef ZDO_COORDINATOR
case ZDO_CB_MSG:
zclSample_processZDOMsg((zdoIncomingMsg_t *)MSGpkt); //接收终端地址函数
break;
#endif
break;
该标志位 协议栈给出的定义是:ZDO incoming message callback ZDO消息到来处理
接收终端地址函数的具体实现,找一个空白地方,添加如下代码:
#ifdef ZDO_COORDINATOR
//接收终端地址函数
static void zclSample_processZDOMsg(zdoIncomingMsg_t *pMsg)
{
switch (pMsg->clusterID)
{
case Device_annce:
endDeviceAddr = pMsg->srcAddr.addr.shortAddr;
//接收到终端的短地址
HalLcdWriteStringValue("EndDevice: ",pMsg->srcAddr.addr.shortAddr,16,2);
HalLcdWriteString("Send CMD...",HAL_LCD_LINE_4);
osal_start_timerEx(zclSampleSw_TaskID,SAMPLEAPP_CMD_EVT,SAMPLEAPP_CMD_PERIOD);
//启动发送命令事件定时器
break;
default:
break;
}
}
#else
#endif
其中启动了一个发送命令事件的定时器。
在zcl_samplesw.h里添加:
#ifdef ZDO_COORDINATOR
#define SAMPLEAPP_CMD_EVT 0x0040 //发送命令事件标志
#define SAMPLEAPP_CMD_PERIOD 3000 //发送命令事件周期
#else
#endif
在zclSampleSw_event_loop()函数里添加:
#ifdef ZDO_COORDINATOR
//协调器发送命令
if(events & SAMPLEAPP_CMD_EVT)
{
zclSample_SendCMD();
osal_start_timerEx(zclSampleSw_TaskID,SAMPLEAPP_CMD_EVT,SAMPLEAPP_CMD_PERIOD);
return (events ^ SAMPLEAPP_CMD_EVT);
}
#else
#endif
2.发送命令控制
#ifdef ZDO_COORDINATOR
//发送命令函数
static void zclSample_SendCMD(void)
{
afAddrType_t destAddr;
static uint8 count = 0;
static bool onoff = true;
destAddr.endPoint = SAMPLESW_ENDPOINT; //终端设备端点
destAddr.addrMode = Addr16Bit;
destAddr.addr.shortAddr = endDeviceAddr; //终端设备短地址
if(onoff)
{
HalLcdWriteString("SEND CMD ON",HAL_LCD_LINE_3);
zclGeneral_SendOnOff_CmdOn(SAMPLESW_ENDPOINT, &destAddr,TRUE,count++); //发送命令ON
}
else
{
HalLcdWriteString("SEND CMD OFF",HAL_LCD_LINE_3);
zclGeneral_SendOnOff_CmdOff(SAMPLESW_ENDPOINT, &destAddr,TRUE,count++); //发送命令OFF
}
onoff = ! onoff ;
}
#else
#endif
在zclSampleSw_Init()函数里添加如下:
#ifdef ZDO_COORDINATOR
ZDO_RegisterForZDOMsg ( zclSampleSw_TaskID,Device_annce );
//注册一个回调函数以便接收特定类型的ZDO消息
#else
#endif
协调器通知有其他设备加入。
定义的函数在最前面提前声明一下:
#ifdef ZDO_COORDINATOR
//终端发送过来的地址
uint16 endDeviceAddr;
//协调器接收命令函数
static void zclSample_processZDOMsg(zdoIncomingMsg_t *pMsg);
//协调器发送命令函数
static void zclSample_SendCMD(void);
#else
//终端发送命令函数
static void zclSample_LightCMD(uint8 cmd);
#endif
四、实现成果:
协调器周期性开关终端的LED灯,并在OLED周期性显示ON或者OFF
ZCL命令控制LED
如果对实验有疑问,可以在评论区留言,需要工程代码,可以留言或者私信,码字不易,希望本人可以为推广ZIgbee发展尽一份绵薄之力,如果有帮助到您,请点一个免费的赞呗!
更多推荐



所有评论(0)