Zigbee通讯之开发篇(基于TI 的Z-Stack)

 

1.Zigbee协议和Z-Stack

  Zigbee协议和Z-Stack是什么关系?这可能是初学Zigbee同学想知道的问题。给大家举个例子吧,我们生活中使用的插排是要符合一定的标准的,现在国家标准是GB2099.3-2008,里面规定了好多插排的电气、机械等要求。不同厂家生产的插排,要在中国国内销售的话,必须符合这个标准。但是生产这个插排的厂家多了去了,像 公牛、philips等等(排除做广告的嫌疑)。其实Zigbee协议栈规范和Z-Stack的关系也差不多,Z-Stack就是符合Zigbee协议栈规范的一个硬件和软件平台,是Zigbee协议栈的一个具体实现。当然,还有其他的具体实现,freakz协议栈和contiki操作系统、TinyOS等等。大家要注意,Z-Stack是TI公司提供的协议栈,它是个半开源的协议栈,有些核心代码是以库的形式提供的,所以要想深入了解协议栈或者想进一步提升编程能力的同学还是找一个全开源的协议栈玩玩吧。比如,freakz协议栈。

2.IAR和Z-Stack

 Z-Stack的整个开发环境IDE使用的是IAR(IAR的版本需要参考Z-Stack Home Sample Application User's Guide。此文档在TI提供的资料包里\Documents内)。从来没有使用过IAR的同学请参考文章最后附件:IAR入门。这只是个简单的入门指导,想详细了解IAR或者使用过程中遇到什么问题请使用IAR的help选项。

 我们知道Zigbee设备的分为Coordinator、Router、Enddevice三种角色,这三种角色在IAR里怎么修改呐?我们打开一个TI HA(TI提供的关于智能家居的解决方案),下载地址见下载平台:http://down.51cto.com/data/2067778

wKioL1Wk53LxIEsoAAFyv-dkGSI060.jpg

从图中标示的位置可以修改这三种设备角色,其实这里的选项是修改IAR project配置的地方,这里TI提供的project里已经配置好了这三钟Zigbee设备角色的配置文件。我们这里只选择就可以了。

Z-Stack软件结构有一个很大的特点:使用宏定义来区分是否编译某一模块/功能/函数。

wKioL1Wk7nWQmoxpAAGhZuoMECk541.jpg

wKiom1Wk7QPiEbzKAAEYILvI9E8551.jpg

全是根据宏定义来决定是否编译这个功能,有的是根据是否定义这个宏,有的是根据定义的这个宏的值来决定其什么作用。这也可以理解,因为TI提供的是一个通用的基础开发平台,需要考虑兼容性,易用性,并且硬件资源有限,只能使用宏定义的方式在程序预编译阶段根据宏定义就可以知道需要哪些功能了。

那么关键的问题来了,这些宏定义在IAR里是怎么定义的类?在IAR中有两种定义方式:

第一种:

wKiom1Wk7tHBhrJaAAG4NBIk_mE550.jpg

在打开的TI project工程目录的Tools下面,有***.cfg文件,这些是IAR Compiler command-line options。可以在这里定义宏。

wKiom1Wk71aBvXOYAAIr1o9BUYo509.jpg

定义的方法是“-D 加上你要定义的宏”,例如上面的 -DMAC_CFG_TX_MAX,其实定义的宏就是MAC_CFG_TX_MAX,你搜索整个工程就可以找到在哪里使用了这个宏。取消定义可以在定义前加上“//”。例如//-DMAC_CFG_TX_DATA_MAX=5。

你可能想知道IAR读取这些cfg文件需要不需要配置?自己新增加配置文件怎么办?

打开菜单栏 project---option

wKioL1Wk8w7TzqMGAAH4PJsKoOM737.jpg

找到C/C++Complier 选项,然后选择Extra Options,在这里写你自己需要引用的cfg文件就可以了。

第二种:

打开菜单栏 project---option 然后找到preprocessor选项。

wKioL1Wk9GSQ33xeAAJl6zDtaeg698.jpg

在这里也可以定义宏,直接书写宏的名字即可,例如:“ZTOOL_P1”。取消宏定义可以在前面添加“x”,例如“xZTOOL_P1”。当然也可以定义有值的宏,“LCD_SUPPORTED=DEBUG”。

另外,还要说一下上面截图中的上面的部分:是添加头文件包含路径的,这样在源代码包含头文件时你就不用书写好长的路径名称了,直接写头文件名称就行。

 

关于这两种定义宏有什么区别?要是两个地方都定义了相同的宏但值不同(有可能是你马虎定义重复了),这种情况以那个为准?

关于区别,第一种定义方式其实就是将要定义的某一类功能的宏都放到一个文件中,方便修改、查找。这样所有的工程都可以通过指定文件的方式来使用这些宏定义或者宏定义的值。比较方便一些,例如:TI工程里的f8wCoord.cfg这个宏定义配置文件,是针对所有Coordinator的定义,好多不同project想编译成Coordinator角色的都可以引用这个command-line options文件。

第二种方式定义只是针对具体某个project的,通过第二种方式设置的内容都会存储在***.ewp文件中,这个就是具体某个project的具体配置。

还有一点需要说明:

wKioL1Wlx1fQmKbFAACoarni4NA973.jpg

上面第一处是IAR关于某一个project的一个配置文件,选中不同的配置文件,IAR就会根据不同的配置进行编译、链接等等一系列动作。这里的配置主要包括:菜单栏里 roject---option里面所有的设置,还有选择是否编译某一个具体的文件。

wKioL1WlyIXg5kgVAAHZkcjuFi4900.jpg

显示为暗灰色的X号的文件不参与此project的编译,设置方法为在project具体某个文件上右键--option--Exclude from buid。如上图所示。

IAR会单独建立文件夹用于保存不同peoject配置的编译、链接生成的文件。就相当于利用不同的配置实现不同的功能,最明显的你想编译一个release版本,编译一个debug版本,release版本不包括调试信息。你就可以设置两个配置。新增配置的方法是菜单栏--project---edit configration里面add即可。

wKioL1WlyeWyQEVkAACjfSXP9fQ752.jpg

这里是选择同一个工作空间里不同的项目的。即IAR管理思路是这样的:一个workspace里可以包含好多的project,而一个project又可以存在好多种的配置。具体参考菜单栏---help---IDE Project Management and Building Guide。

 

wKiom1WlyHOzxGHAAAB3cEnhh_s461.jpg

这是IAR的链接器使用的链接脚本,使用这个文件制定不同变量或者存储区域的最终链接地址,还有其他一些功能,具体参考菜单栏---help---Linker and Library Reference Guide。

3.Z-Stack软件结构简介

 关于Z-Stack结构比较详细的资料需要看这两个TI的官方资料:Z-Stack Home Developer's Guide.pdf

Z-Stack Home Sample Application User's Guide.pdf。是比较全面的资料。关于TI Z-Stack的project各个文件夹的作用网络上已经有大量的资料,这里就不一一赘述。大家可以到网络搜索资料学习。我这里只是简单说一下基于Z-Stack协议栈开发application的思路和方法。Z-Stack project不仅仅提供了Zigbee协议栈的各层API,还提供了一个基于轮询调度的OS(osal),还提供了一些硬件资源驱动API。各个API使用说明见 TI安装包 Document---API里面有各个API的使用说明。

下面我们重点说说这个OSAL,因为它是一个简易的轮询式操作系统,Z-Stack协议使用它作为简单的任务管理、调度、任务间通通讯,使用它使其软件结构更清晰。另外,我们基于Z-Stack协议的Application设计也要基于此软件结构。关于这个东东的讲解可以参考:http://bbs.feibit.com/thread-16-1-1.html。我这里举个例子让大家好理解这个轮询操作系统。如果大家有嵌入式实时操作系统的知识,那这个OSAL就比较好理解。其实OSAL并不是实际意义上的操作系统,它只是一个轮询系统。大家可以想象一个部门有好几个雇员,只有一个办公电脑,这个办公电脑同一时间只能有一位雇员使用,雇员使用办公电脑需要部门领导审批。部门领导根据雇员年龄的大小进行排序,年龄小的先使用,年龄大的后使用。部门领导负责通知各个雇员在使用办公电脑时都需要做哪些具体的工作,若某一雇员在得到办公电脑的使用权时没有任何事情需要做,那他就将办公电脑让给下一个等待的雇员使用。部门领导会根据发生的事件(包括外部事件、雇员之间需要沟通)记录在一个工作安排薄里,每一个雇员得到办公电脑都需要查询工作安排薄来看自己有哪些工作,工作做完了,再接着查询,直到没有自己的工作了,就让下一个等待的雇员使用办公电脑。

这下大家可能明白了,若是那个年龄最小的雇员老是有任务做,那其他雇员就没有机会使用办公电脑了。所以这个OSAL只是一个简单的轮询外部事件的简单调度系统。想了解嵌入式实时操作系统的相关知识,可以学习一下UCOS的相关资料。

另外,我们还需要了解这样一种软件设计思路:Z-Stack作为一个基础软件开发包,为了易于维护软件结构设计时是分层的,那各层之间如何通讯?上层需要调用下层的服务时,直接调用下层提供的API接口即可,那下层有一些紧急事件或者有些变化是上层关心的,如何通知到上层呐?上层接收到这样的消息时有可能需要做不同的操作,这一般怎么实现呐?这种下层事件发生需要通知上层的情况,需要使用回调函数,下层事件发生,会调用用户高层注册的函数来处理下层事件,这就实现了下层到上层的通讯。

所以一般我们需要注册回调函数,然后底层事件发生会调用我们注册的函数,注册函数可以根据传递过来的参数做相应处理。

4.两个基于TI CC2530和Z-Stack平台的设备Zigbee通讯

 一个Coordinator一个Enddevice,它们之间通讯,我们在Z-Stack提供的project---SimpleLight的基础上进行修改源代码做我们的实验。实现现象如下:Coordinator建立网络后,Enddevice设备加入网络,然后Coordinator通过广播的方式发送字符串“Coordinator send!”,EndDevice收到此字符串后控制LED灯闪烁,并且向Coordinator发送“EndDevice received!\r\n”。Coordinator收到后,通过串口打印出来。使用的硬件平台为battery board。

我们选择CoordinatorEB配置选项。

wKiom1WmDf2y2l_iAACvLl5s4Ks966.jpg

我们移除project中App文件夹中的zcl_samplelight.c、zcl_samplelight.h、zcl_samplelight_data.c,添加Coordinator.c、Coordinator.h这两个文件,OSAL_GenericApp.c这个文件是OSAL层和Application层之间的接口文件。这个文件主要负责OSAL的task的初始化,添加task event处理函数。

先看Coordinator的代码:

我们新添加一个task,在Z-Stack中新添加task一般需要以下两步:1,添加task初始化函数:GenericApp_Init。

wKioL1WmBUXwI8H9AANH8n0uEuA443.jpg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*********************************************************************
  * @fn      osalInitTasks
  *
  * @brief   This function invokes the initialization function for each task.
  *
  * @param   void
  *
  * @return  none
  */
void  osalInitTasks(  void  )
{
   uint8 taskID = 0;
   tasksEvents = (uint16 *)osal_mem_alloc(  sizeof ( uint16 ) * tasksCnt);
   osal_memset( tasksEvents, 0, ( sizeof ( uint16 ) * tasksCnt));
   macTaskInit( taskID++ );
   nwk_init( taskID++ );
   Hal_Init( taskID++ );
#if defined( MT_TASK )
   MT_TaskInit( taskID++ );
#endif
   APS_Init( taskID++ );
#if defined ( ZIGBEE_FRAGMENTATION )
   APSF_Init( taskID++ );
#endif
   ZDApp_Init( taskID++ );
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
   ZDNwkMgr_Init( taskID++ );
#endif
   GenericApp_Init( taskID );
}

这个初始化函数要在OSAL_GenericApp.c文件里的void osalInitTasks( void )函数里添加调用。如上图所示。

2,在OSAL_GenericApp.c文件里的pTaskEventHandlerFn tasksArr[]数组里添加用于task event处理的函数GenericApp_event_loop。

wKioL1WmBevRH21rAANIhS5fKSI783.jpg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// The order in this table must be identical to the task initialization calls below in osalInitTask.
const  pTaskEventHandlerFn tasksArr[] = {
   macEventLoop,
   nwk_event_loop,
   Hal_ProcessEvent,
#if defined( MT_TASK )
   MT_ProcessEvent,
#endif
   APS_event_loop,
#if defined ( ZIGBEE_FRAGMENTATION )
   APSF_ProcessEvent,
#endif
   ZDApp_event_loop,
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
   ZDNwkMgr_event_loop,
#endif
   GenericApp_event_loop
};

注意初始化task和处理task event的函数顺序要一致。

我们先来看void GenericApp_Init(byte task_id)函数都做了些什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void  GenericApp_Init(byte task_id)
{
     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 ); 
     
     
     
     //UART configuration
       halUARTCfg_t uartConfig;
       uartConfig.configured           = TRUE;
       uartConfig.baudRate             = HAL_UART_BR_115200;
       uartConfig.flowControl          = FALSE;
       uartConfig.callBackFunc         = NULL;
       HalUARTOpen (0, &uartConfig);
       
       
       HalLedSet(HAL_LED_ALL,HAL_LED_MODE_OFF);
      
}

1,初始化了task的ID GenericApp_TaskID的值。

2,定义了一个endPointDesc_t端点描述符。

3,初始化了串口,用于串口输出。

4,初始化所有LED为OFF状态。

我们再来看uint16 GenericApp_event_loop( uint8 task_id, uint16 events )函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
uint16 GenericApp_event_loop( uint8 task_id, uint16 events )
{
   afIncomingMSGPacket_t *MSGpkt;
   ( void )task_id;   // Intentionally unreferenced parameter
   if  ( events & SYS_EVENT_MSG )
   {
     while  ( (MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( GenericApp_TaskID )) )
     {
       switch  ( MSGpkt->hdr.event )
       {
         case  AF_INCOMING_MSG_CMD:
         GenericApp_MessageMSGCB( MSGpkt );    
         break
         case  ZDO_STATE_CHANGE:
              GenericApp_NwkState = (devStates_t) (MSGpkt->hdr.status);
              if (GenericApp_NwkState == DEV_ZB_COORD)
              {
                 osal_start_timerEx(GenericApp_TaskID,SEND_BROADCAST_MESSAGE,2000);
              }
              break
         default :
           break ;
       }
      
       // Release the memory
       osal_msg_deallocate( (uint8 *)MSGpkt );
     }
     // return unprocessed events
            
     return  (events ^ SYS_EVENT_MSG);
   }
   
   //if need send brodcast message
   if (events & SEND_BROADCAST_MESSAGE )
   {
       GenericApp_SendTheMessage( );
       osal_start_timerEx(GenericApp_TaskID,SEND_BROADCAST_MESSAGE,5000);
       return  (events ^ SEND_BROADCAST_MESSAGE);
   }
   // Discard unknown events
   return  0;
}

这个函数里主要处理了两个系统event,一个是ZDO_STATE_CHANGE事件,当Zigbee网络发生变化时(有新设备加入)产生此事件,在此事件里我们启动了一个定时事件SEND_BROADCAST_MESSAGE,用于广播Zigbee信息。另外还有一个AF_INCOMING_MSG_CMD事件,当接收到Zigbee信息包时,会产生此事件,在此事件处理函数中将Coordinator接收到的信息通过串口打印出来。

所需要的函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void  GenericApp_MessageMSGCB(afIncomingMSGPacket_t *pkt)
{
     unsigned  char  buffer[20] ;
     
     unsigned  char  frame_end[2] = { '\r' , '\n' };
     
     switch (pkt->clusterId)
     {
         case  GENERICAPP_CLUSTERID:
              osal_memcpy(buffer,pkt->cmd.Data,20);
              HalUARTWrite(0, buffer, 20);
              HalUARTWrite(0, frame_end, 2);
             
              break ;
       
     }
}
void  GenericApp_SendTheMessage( void )
{
     unsigned  char  *theMessageData =  "Coordinator send!" ;
     afAddrType_t my_DstAddr;
     my_DstAddr.addrMode = (afAddrMode_t)AddrBroadcast;
     my_DstAddr.endPoint = GENERICAPP_ENDPOINT;
     my_DstAddr.addr.shortAddr =0xFFFF ;
     
     AF_DataRequest(&my_DstAddr,&GenericApp_epDesc,GENERICAPP_CLUSTERID,osal_strlen(( char  *)theMessageData)+1,theMessageData,&GenericApp_TransID,AF_DISCV_ROUTE,AF_DEFAULT_RADIUS);
}

Enddevice的代码和这个基本上差不多。

我们在CoordinatorEB配置下添加Enddevice.c,并将其设置为不参与buid。方法是在Enddevice.c文件上右键----option---Exclude from buid。然后切换配置到EnddeviceEB配置选项,然后将Coordinator.c设置成不参与buid状态。

task初始化函数:void GenericApp_Init(byte task_id)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
void  GenericApp_Init(byte task_id)
{
     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 ); 
     
     
    HalLedSet(HAL_LED_ALL,HAL_LED_MODE_OFF);
}
uint16 GenericApp_event_loop( uint8 task_id, uint16 events )
{
   afIncomingMSGPacket_t *MSGpkt;
   ( void )task_id;   // Intentionally unreferenced parameter
   if  ( events & SYS_EVENT_MSG )
   {
     while  ( (MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( GenericApp_TaskID )) )
     {
       switch  ( MSGpkt->hdr.event )
       {
         case  AF_INCOMING_MSG_CMD:
         GenericApp_MessageMSGCB( MSGpkt );    
         break
         
         default :
           break ;
       }
      
       // Release the memory
       osal_msg_deallocate( (uint8 *)MSGpkt );
     }
     // return unprocessed events
            
     return  (events ^ SYS_EVENT_MSG);
   }
   
   // Discard unknown events
   return  0;
}
void  GenericApp_SendTheMessage( void )
{
     unsigned  char  *theMessageData =  "EndDevice received!\r\n" ;
     
     afAddrType_t my_DstAddr;
     my_DstAddr.addrMode = (afAddrMode_t)Addr16Bit;
     my_DstAddr.endPoint = GENERICAPP_ENDPOINT;
     my_DstAddr.addr.shortAddr =0x0000 ;
     
     AF_DataRequest(&my_DstAddr,&GenericApp_epDesc,GENERICAPP_CLUSTERID,osal_strlen(( char  *)theMessageData)+1,theMessageData,&GenericApp_TransID,AF_DISCV_ROUTE,AF_DEFAULT_RADIUS);
}
static  unsigned  char  led_state = 1;
void  GenericApp_MessageMSGCB(afIncomingMSGPacket_t *pkt)
{
     unsigned  char  buffer[20] ;
    
     
     switch (pkt->clusterId)
     {
         case  GENERICAPP_CLUSTERID:
              osal_memcpy(buffer,pkt->cmd.Data,osal_strlen( "Coordinator send!" )+1);
              
              if (osal_memcmp(buffer, "Coordinator send!" ,osal_strlen( "Coordinator send!" )+1))
              {
                 
                 if (led_state)
                 {   
                     led_state = !led_state ;
                     HalLedSet(HAL_LED_4,HAL_LED_MODE_ON);
                 }
                 else
                 {
                     led_state = !led_state ;
                     HalLedSet(HAL_LED_4,HAL_LED_MODE_OFF);
                 }
                 
                 
                 GenericApp_SendTheMessage();
              }
             
              break ;
       
     }
}

想使用串口,还需要定义宏:

wKiom1WmD2mSUnOaAAJdpwfVFnI871.jpg

将上述代码编译后分别下载到两个Zigbee开发板上,Enddevice板上的LED灯会闪烁,Coordinator的串口会输出:“EndDevice received!\r\n”。说明程序正常运行。

通过上面的简单实验,你可能对TI的Z-Stack有了一定的感性认识,但是对代码和原理还不是特别清楚。

没关系,这是第一步,有了感性认识,再结合TI提供的开发文档和源代码,我们对原理也会有一定的认识的。

 

下面我们来看一个重要的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*********************************************************************
  * @fn      AF_DataRequest
  *
  * @brief   Common functionality for invoking APSDE_DataReq() for both
  *          SendMulti and MSG-Send.
  *
  * input parameters
  *
  * @param  *dstAddr - Full ZB destination address: Nwk Addr + End Point.
  * @param  *srcEP - Origination (i.e. respond to or ack to) End Point Descr.
  * @param   cID - A valid cluster ID as specified by the Profile.
  * @param   len - Number of bytes of data pointed to by next param.
  * @param  *buf - A pointer to the data bytes to send.
  * @param  *transID - A pointer to a byte which can be modified and which will
  *                    be used as the transaction sequence number of the msg.
  * @param   options - Valid bit mask of Tx options.
  * @param   radius - Normally set to AF_DEFAULT_RADIUS.
  *
  * output parameters
  *
  * @param  *transID - Incremented by one if the return value is success.
  *
  * @return  afStatus_t - See previous definition of afStatus_... types.
  */
uint8 AF_DataRequestDiscoverRoute = DISC_ROUTE_NETWORK;
afStatus_t AF_DataRequest( afAddrType_t *dstAddr, endPointDesc_t *srcEP,
                            uint16 cID, uint16 len, uint8 *buf, uint8 *transID,
                            uint8 options, uint8 radius )

这个函数是用来发送Zigbee无线数据的。我们看看这个函数都需要哪些参数?

dstAddr:包括网络地址和端点号。

 

srcEP: 端点描述符

cID:  指定一个ClusterID。

len:  发送数据的长度。

*buf:  需要发送的数据内容。

 

我们知道Zigbee要发送信息,需要知道对方的短地址,端点号,还需要指定一个Cluster ID,这些信息和程序是怎么关联起来的呐?光说这些名词感觉有点抽象。

好吧,我们就一起分析一下。

我们在程序里定义了一个endPointDesc_t GenericApp_epDesc;

这个结构体变量中都有哪些内容呐?

GenericApp_epDesc.endPoint = GENERICAPP_ENDPOINT;

GenericApp_epDesc.task_id  = &GenericApp_TaskID;

GenericApp_epDesc.simpleDesc =(SimpleDescriptionFormat_t *)&GenericApp_SimpleDesc ;

有端点号,taskID,还有一个简单描述符(这个后面再说)。

你看,通过这个端点描述符把端点号和taskID联系在了一起。

接着,我们使用afRegister( &GenericApp_epDesc );向AF层注册了这个端点描述符。也就是说AF层收到发给相应端点号的数据时,会通过OSAL注册一个系统event,OSAL轮询调度会调用与之关联的taskID,这里就是GenericApp_event_loop。在这个函数里我们看到有如下代码:

wKioL1WmHLDRhBqJAAEZRP8vv5s108.jpg判断这个系统event如果是AF_INCOMING_MSG_CMD,也就是收到了AF层发来的信息,就调用GenericApp_MessageMSGCB( MSGpkt );在这个函数里:

wKiom1WmG0XC6RYgAAD08lzWupA515.jpg

判断是哪个cluster ID,根据不同的cluster ID做不同的动作。

总结一下就是:短地址用于确定发送给哪个Zigbee设备,端点号用于确定你发给这个Zigbee设备的哪个端口,在发送给这个端口中使用Cluster ID来区分不同的功能。

简单描述符:

1
2
3
4
5
6
7
8
9
10
11
12
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,           //  uint8  AppNumInClusters;
   (cId_t *)GenericApp_ClusterList,   //  uint8 *pAppInClusterList;
   0,                                 //  uint8  AppNumInClusters;
   (cId_t *)NULL                      //  uint8 *pAppOutClusterList;
};

这里面包含了端点号、profileID、deviceID等内容,这些是和profile的规范有关,zclSampleLight_InClusterList 和zclSampleLight_OutClusterList这些和binding有关系。就是建立绑定关系时,需要相应的Cluster一致。

5.ZCL和HA是什么?

 

ZCL的全称是Zigbee Cluster Library。

ZCL在zigbee中相当于Cluster功能仓库的作用。开发者开发新的Profile就必须加入相应的ZCL簇功能函数到该Profile中。

也就是说ZCL相当于一个存储命令集合的仓库。节点与节点之间,利用ZCL的命令来进行通信。

wKioL1WmI57wn6gfAAHarqd0wRs375.jpg

在Zigbee中,一个簇群就是一个容器,在容器中以命令结构体包含了一个或多个属于某个应用剖面的属性/消息,不管应用剖面如何,相同的设备(比如开关)拥有相同的定义和功能。属性是设备的变量或特性,能够设置或获得。比如设置自动调温器的加热点。ZCL提供了一种机制,利用这种机制设备能够将变化异步地报告给属性(attribute),比如当空气变热时自动控温器服务器就将室温改变报告给他的客户端,这个过程不需要客户端发起请求。

ZCL采用客户端/服务器模块的模式,一般储存簇属性的作为服务器,影响或操作属性的作为客户端。然而如果需要,属性也可以呈现在客户端上。例如,设备通过读写属性的命令来操作属性,这些命令从客户端设备发送到服务器设备;对这些命令的应答从服务器设备发送到客户端设备;但是报告属性命令是从服务器发送到客户端。cluster ID是每个簇的标志,由剖面分配,在内部使用的是逻辑簇ID,所以还有一个Cluster ID转换表。

 

Z-Stack中这一部分的代码在project的Profile文件夹中,使用Z-Stack的ZCL API请参阅TI提供的资料Document/api/Z-Stack ZCL API.pdf.

HA的全称是home Automation,家庭自动化。这个是Zigbee联盟专门为智能家居领域指定的一个规范,这个是基于Zigbee协议栈的一个应用层的规范。这个规范主要规定了智能家居产品的属性和动作,以及收到相应数据后应该做的动作。不同厂家生产的基于Zigbee的智能家居产品都可以兼容,只要他们做的产品符合HA的规范。

上个图:

上述部分说的比较简略,详细内容请参阅:文章后面的附件:ZigBee_Cluster_Library_Public.pdf和ZigBee Home AutomationPublicApplicationProfile.pdf。

6.Zigbee抓包工具(SmartRF Packet Sniffer)的使用

 Zigbee Radio层采用2.4G无线传输信息,我们希望利用工具抓到空中数据包用于学习、分析无线数据包的格式。另外,抓包还可以分析实际遇到的问题。

TI给我们提供了空中抓包方案,需要一个硬件CC2531 USB Dongle,还需要安装一个SmartRF Packet Sniffer的抓包软件。具体抓包软件使用方法请参见 SmartRF Packet Sniffer软件菜单栏---help---User’s Manual。这里不再赘述,也可以到网上搜索相关资料。

 

wKiom1WmJiziunKIAA8hK8O-T_g554.jpg上面是一张我使用上述工具抓到的数据包,可以分析Zigbee协议各个层的数据内容,协议内容。结合Zigbee Specification可以加深对Zigbee协议的理解,同样,也可以分析实际应用中遇到的问题。

 

7.总结

 Zigbee定义了从物理介质传输、网络层、应用层还有不同领域的应用规范。可以说Zigbee协议栈是一个比较大的通讯协议集合。针对Zigbee的认识和学习,我的建议是首先根据你的应用目的利用TI平台提供的各种API实现想要的功能。慢慢加深对Zigbee的认识。针对Zigbee的ZCL和HA详细的内容我也在学习当中,上述内容纯属个人见解,如有错误,欢迎指正。

文章部分附件请到:http://1801179.blog.51cto.com/1791179/1674160下载(文章最后有“附件下载”)

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/34923.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

牛逼,字节跳动又一开源大作!程序员的福音!

最近有位读者私信,说能不能推荐一些大厂使用的安卓类开源项目,想了下,读者中也有不少搞安卓开发的,所以今天给大家推荐一个大厂出品的安卓开源项目。 这个项目是「ByteHook」,目前在 GitHub 收获了 670 Star&#xff…

字节跳动开源又一力作

【公众号回复 “1024”&#xff0c;免费领取程序员赚钱实操经验】 大家好&#xff0c;我是章鱼猫。 今天给大家推荐的这个项目是「BoostMultiDex」&#xff0c;一个用于 Android 低版本设备&#xff08;4.X 及以下&#xff0c;SDK < 21&#xff09;快速加载多 DEX 的解决方案…

炸裂!万字长文拿下HTTP 我在字节跳动等你!

本文将从以下几个方面进行分享。其中包括HTTP发展史&#xff0c;HTTP缓存代理机制&#xff0c;常用的web攻击&#xff0c;HTTP和HTTPS的流量识别&#xff0c;网络协议学习的工具推荐以及高频HTTP与HTTPS的高频面试题题解等&#xff0c;开工。ps(如果需要带目录pdf&#xff0c;私…

字节跳动青训营--前端day2

文章目录 前言一、css1. css的组成2. css三种使用方式3.css的工作方式 二、 css选择器Selector1. 选择元素的方式2. 伪类&#xff08;pseudo-classes&#xff09;2.1 状态伪类2.1 结构伪类 3.通过组合选择元素3.1 直接组合3.2 元素组合的拓展 4. 选择器的特异度&#xff08;Spe…

字节跳动第七期技术训练营——“抓到你了”项目

github地址 一. 项目要求&#xff1a; 作业名称 《抓到你了——具备安全防护能力的账号系统》 产品形态 具有安全风控防护能力的账号注册登录系统 产品功能介绍 设计并开发一个登录注册系统&#xff0c;可以支持注册、登录和登出或注销的基本功能&#xff0c;另外也需要…

字节跳动青训营--前端day8

文章目录 前言一、CSR&#xff0c;SSR&#xff0c;SSG1. CSR2. SSR3. SSG4. SSR&#xff0c;SSG的优势利于SEO更短的首屏时间 二、什么是Next.js三、Next.js客户端开发1. Api2. CSS Modules3. Layout4. 文件式路由 四、Next.js服务端开发 前言 仅以此文章记录学习历程。 一、C…

孙叫兽CSDN社区云----WebIT已创建,欢迎大家前端全栈小伙伴踊跃加入

目录 社区云是什么&#xff1f; 创建CSDN社区云WebIT的目的 推荐分享的技术点&#xff08;如下图所示&#xff09; 社区成员权益 版主权益 管理员权益 WebIT社区云积分规则 WebIT优质版主及管理员可以申请直播分享前端技术 WebIT社区云将为社区运营者提供&#xff1a; …

字节跳动在Rust高性能编程探索和实践

本文整理自字节跳动火山引擎基础架构服务框架工程师吴迪于Qcon大会上的分享内容。 嘉宾及内容介绍 本次分享内容主要分为以下三个部分&#xff1a; 我们为什么选择了 Rust 语言&#xff1b;我们做了什么&#xff1b;展望未来&#xff1a;机遇与挑战。 我们为什么选择了 Rust 语…

字节跳动自研 OpenBMC 方案成功上线,STE 团队工程师都做了哪些事?

OpenBMC 是 Linux Foundation 组织里的一个项目&#xff0c;也是目前开源 BMC 里方案最成熟、社区最活跃的项目。日前&#xff0c;我们得知字节跳动系统部 STE 团队自研的 OpenBMC&#xff0c;已在内部成功上线&#xff0c;并稳定运行了两个多月。这也是字节跳动首次正式上线并…

月薪11K!95后房产经纪人转行当程序员:工资过万不难,精神压力也不大

时代在不断的发展&#xff0c;人们对“衣食住行”的要求越来越高&#xff0c;而这其中人们对于“住”是格外的关注。 买房的人希望自己能买到合适的房子&#xff0c;而卖房的人也担心自己的房子卖便宜了&#xff0c;所以越来越多的人在买房卖房这一块儿很难抉择&#xff0c;因…

chatgpt赋能python:Python怎么装Pip

Python怎么装Pip Python是一种非常流行的编程语言&#xff0c;可以用于各种用途&#xff0c;包括网页开发、数据分析、科学计算等。如果你是一个Python编程新手&#xff0c;你可能不知道如何安装和使用Python包。Pip是Python包管理系统&#xff0c;可以帮助你安装和管理Python…

赛事报名启动丨百度Apollo星火自动驾驶大赛开始报名啦!

作为汽车智能化、网联化的关键环节&#xff0c;自动驾驶成为全球科技界、产业界竞争的新赛道。随着人工智能、5G通信、激光雷达、高精地图等多项技术不断完善&#xff0c;自动驾驶的判断力和理解力得到了显著提升。为了推动自动驾驶技术的发展、加快人工智能技术的迭代&#xf…

RocketMQ 在同程旅行的落地实践

本文作者&#xff1a;刘树东 - 同程艺龙技术专家 01/使用概况 同程旅行选择RocketMQ主要基于以下几个方面的考虑&#xff1a; 技术栈&#xff1a;公司主要以 Java 开发为主&#xff0c;因此我们倾向于选择一款用 Java 实现的MQ&#xff0c;且没有任何第三方依赖为最佳&#…

火爆全网的个人行程卡纪念版!3秒教你快速生成!

大家好 咱们直接开门见山&#xff01; 3 秒生成你的专属行程卡纪念版 用下面这款小工具&#xff0c;只需要 3 秒&#xff0c;就能帮大家生成【行程卡纪念版】。 用一张图片记录你三年去过的地方。 回顾过去三年&#xff0c;我们发现许多美好的回忆已经深深地留在了我们的心中。…

CAN网络管理唤醒功能

采用1043的can 芯片:工作模式 – 正常模式 – 具有 INH 输出以及本地和远程唤醒请求功能的待机模式 – 具有 INH 输出以及本地和远程唤醒请求的低功耗睡眠模式 状态切换流程图: CAN唤醒的需求: 唤醒请求(Wake Up Request) 唤醒请求可分为两种: ● 主动唤醒请求:来自模…

(智能车比赛)基于 ADS 逐飞库 英飞凌 TC264 377系列 的多核使用经验

多核并行主要有两个方面 一&#xff0c;程序&#xff0c;变量储存地址设置 二&#xff0c;运行核选择 一、内存分配 概念及过程描述 尽量将不同的功能划分为各个模块&#xff0c;然后交给不同的cpu运行。 或者说不同的cpu负责运行不同的功能&#xff0c;cpu之间以少量的参…

CK-UR05-US桌面式超高频RFID发卡器开发手册之USB控制命令格式

CK-UR05-US桌面式超高频RFID发卡器支持USB控制命令格式&#xff0c;本文重点就此格式展开说明&#xff01; CK-UR05-US桌面式超高频RFID发卡器 1、取版本号(GetReaderVersion) 功能:取读写器的硬件、软件版本 命令码: 02H 命令参数:无 命令包: 『40H 02H 02H BCH』 举例: 如…

RFID课程设计-图书管理系统用户端设计

RFID课程设计-图书管理系统用户端设计课程设计题目课程设计任务内容题目设计基本原理NFC开发概述标签调度系统如何将 NFC 标签映射到 MIME 类型和 URI如何将 NFC 标签分发到应用在 Android 清单中请求 NFC 访问权限过滤 NFC IntentACTION_NDEF_DISCOVEREDACTION_TAG_DISCOVERED…

Linux炫酷终端仪表盘

最近发现了一个适合装逼的终端命令&#xff0c;会在终端显示一个炫酷的仪表盘&#xff08;提示&#xff1a;终端全屏显示效果才好&#xff09; 先上图 首先安装三个软件 sudo apt-get install node nodejs-bin sudo apt install nodejs-legacy sudo apt install git 待会会下…

【袋鼠云】标签系统

文章目录 客户画像标签建设的演进1. 业务单元独立作战2. 离线数仓统一建设3. 实时数据多维补充4. 标签产品化管理5. 数据智能化管理 标签体系设计与加工一、标签体系设计1. 数据梳理2. 基于OLP模型体系化梳理标签3. 定义标签与加工口径 二、标签模型设计三、标签配置1. 根据业务…