Zigbee通信之開發篇(基於TI 的Z-Stack)

1.Zigbee協議和Z-Stackhtml

  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-Stackapi

 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軟件結構有一個很大的特色:使用宏定義來區分是否編譯某一模塊/功能/函數。app

wKioL1Wk7nWQmoxpAAGhZuoMECk541.jpg

wKiom1Wk7QPiEbzKAAEYILvI9E8551.jpg

全是根據宏定義來決定是否編譯這個功能,有的是根據是否認義這個宏,有的是根據定義的這個宏的值來決定其什麼做用。這也能夠理解,由於TI提供的是一個通用的基礎開發平臺,須要考慮兼容性,易用性,而且硬件資源有限,只能使用宏定義的方式在程序預編譯階段根據宏定義就能夠知道須要哪些功能了。異步

那麼關鍵的問題來了,這些宏定義在IAR裏是怎麼定義的類?在IAR中有兩種定義方式:ide

第一種:函數

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.ZCLHA是什麼?

 

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下載(文章最後有附件下載)

相關文章
相關標籤/搜索