一、前言:

        全程干货无废话,只讲应用层代码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->bdbCommissioningStatusBDB_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发展尽一份绵薄之力,如果有帮助到您,请点一个免费的赞呗!

Logo

电影级数字人,免显卡端渲染SDK,十行代码即可调用,工业级demo免费开源下载!

更多推荐