[zigbee 嵌入式系統] zigbee 應用實作 - 使用 ti z-stack firmware

246
Chien-Jung Li Apr. 2014 ZigBee應用實作 使用TI Z-Stack Firmware

Upload: simenli

Post on 08-Aug-2015

289 views

Category:

Engineering


13 download

TRANSCRIPT

Page 1: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

Chien-Jung Li

Apr. 2014

ZigBee應用實作 — 使用TI Z-Stack Firmware

Page 2: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

2

大綱 SmartRF05EB牛刀小試:使用PER Test範例展示

SampleApp範例展示

BasicApp:瞭解OSAL的基本運作

BasicApp:OSAL的任務間通訊(IPC)機制

FlyApp:組織PAN網路實驗

擴充FlyApp

FlyApp:Cluster與Binding

解析SampleApp

解析SimpleApp:使用 Simple API

ZigBee Cluster Library

Page 3: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

SmartRF05EB牛刀小試

Page 4: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

4

準備好你的硬體

Page 5: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

5

安裝相關開發軟體 …\hiver.team\Project Repository\0_ZigBee模組\TI Software

swrc176y - SmartRF Studio 7 v1.15.0

swrc044s - SmartRF Flash Programmer v1.12.7

swrc045y - SmartRF Packet Sniffer v2.17.1

swrc096e - zSensorMonitor v1.3.2

hiver.team\Project Repository\0_ZigBee模組\TI SDK and Examples

swrc126g - Zstack CC2530 2.5.1a (預設裝在C:\Texas Instruments資料夾下)

swrc135b - cc2530 SW example.zip 解開至C:\Texas Instruments

IAR for 8051 (這大家應該都有了/記得compiler版本要升到8.10.4)

Page 6: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

6

SmartRF05EB測試程式 (I) – PER Test

用IAR開啟 C:\Texas Instruments\swrc135b - cc2530 SW example\ide\cc2530_sw_examples.eww

我們接下來要使用官方的「封包錯誤率測試應用程式」來測試一下SmartRF05EB

Page 7: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

7

SmartRF05EB測試程式 (II) – PER Test

先Rebuild試試看,你會發現錯誤

進option換掉linker檔 再rebuild一次

C:\Program Files (x86)\IAR Systems \Embedded Workbench 6.0\8051\config\devices \Texas Instruments\lnk51ew_cc2530F256.xcl

Page 8: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

8

SmartRF05EB測試程式 (III) – PER Test

將兩片板子分別接到host,然後下載

使 用 板 子 上 的 Button1 與Joystick 將 一 片 板 子 設 為Transmitter,另一片設為Receiver 。 發 射 端 按 下Joystick中鍵後即進入PER測試模式

RSSI是接收功率強度

接收板可拿到遠處觀察

Page 9: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

範例應用程式:SampleApp

Page 10: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

10

測試SampleApp應用程式 準備SmartRF05EB 2片

用Jumper將1片設為Coordinator,另1片設為Router

連線完成後,按Coordinator的SW1會廣播訊息給Group1裝置,收到訊息者會toggle LED1。

按Router的SW2會使改變自身是否屬於「Group1」。若Router脫離Group1,那麼Coordinator按SW1時,Router將不會有任何反應

P18_9 P18_11 相連則為 協調器

Page 11: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

11

SampleApp Project – 使用DemoEB C:\Texas Instruments\ZStack-CC2530-

2.5.1a\Projects\zstack\Samples\SampleApp\CC2530DB\SampleApp.eww

分別燒錄兩塊板子 1片設為Coordinator 1片設為Router

Page 12: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

12

SampleApp.c

/***************************************************************************** Filename: SampleApp.c Description: Sample Application (no Profile). *****************************************************************************/ /**************************************************************************** This application isn't intended to do anything useful, it is intended to be a simple example of an application's structure. This application sends it's messages either as broadcast or broadcast filtered group messages. The other (more normal) message addressing is unicast. Most of the other sample appli- cations are written to support the unicast message model. Key control: SW1: Sends a flash command to all devices in Group 1. SW2: Adds/Removes (toggles) this device in and out of Group 1. This will enable and disable the reception of the flash command. *****************************************************************************/ /************************* INCLUDES **************************/ … 略 … /************************* MACROS ***************************/ /************************* CONSTANTS ************************/

Page 13: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

BasicApp

瞭解OSAL的基本運作

Page 14: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

14

BasicApp 目標:

BasicApp是一個簡化過的應用程式範例,我們先將無線傳輸功能抽掉,將它當成是一般的嵌入式系統來使用。

Firmware內載作業系統抽象層OSAL,我們將學習如何使用OSAL來協助我們完成應用程式。

此外,我們還會試著使用HAL API來測試一下硬體。

將BasicApp_wo_RF.zip解壓縮到

C:\Texas Instruments\ZStack-CC2530-2.5.1a\Projects\zstack\Samples

Page 15: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

15

系統運作原理

Endpoint

App 240

User App

Endpoint

App 239

User App

Endpoint

App 1

User App

Endpoint

App 0

ZDO

Application Framework (AF)

ZigBee Stack (Z-stack)

MAC (TI-MAC)

PHY Hardware

OSAL

HAL

I/O

Page 16: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

16

開發應用程式的三個必要檔案

以BasicApp為例:

OSAL_BasicApp.c

BasicApp.h

BasicApp.c

1. 程式進入點 main()@ZMain.c

2. 作業系統初始化 osal_init_system()@OSAL.c

3. Tasks/應用程式初始化 OSAL_<AppName>.c

Page 17: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

17

OSAL_BasicApp.c // Filename: OSAL_BasicApp.c #include "ZComDef.h" #include "hal_drivers.h" #include "OSAL.h" #include "OSAL_Tasks.h" #include "BasicApp.h" /********* GLOBAL VARIABLES ********/ // The order in this table must be identical to the task // initialization calls below in osalInitTask. const pTaskEventHandlerFn tasksArr[] = { Hal_ProcessEvent, BasicApp_ProcessEvent }; const uint8 tasksCnt = sizeof( tasksArr ) / sizeof( tasksArr[0] ); uint16 *tasksEvents; /********* FUNCTIONS **********/ /******************************************************* * @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)); Hal_Init( taskID++ ); BasicApp_Init( taskID ); }

Page 18: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

18

BasicApp.h // BasicApp.h #ifndef BasicApp_H #define BasicApp_H #include "ZComDef.h" /******* CONSTANTS ********/ // These constants are only for example and should be // changed to the device's needs #define BasicApp_ENDPOINT 10 #define BasicApp_PROFID 0x0F04 #define BasicApp_DEVICEID 0x0001 #define BasicApp_DEVICE_VERSION 0 #define BasicApp_FLAGS 0 #define BasicApp_MAX_CLUSTERS 1 #define BasicApp_CLUSTERID 1 // Application Events (OSAL) - These are bit weighted definitions. #define BasicApp_SEND_MSG_EVT 0x0001 /********* FUNCTIONS *********/ /* Task Initialization for the Generic Application */ extern void BasicApp_Init( byte task_id ); /* Task Event Processor for the Generic Application */ extern UINT16 BasicApp_ProcessEvent( byte task_id, UINT16 events ); #endif /* BasicApp_H */

Page 19: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

19

BasicApp.c

// BasicApp.c /******* INCLUDES ********/ #include "OSAL.h" #include "AF.h" #include "ZDApp.h" //#include "ZDObject.h" //#include "ZDProfile.h" #include "BasicApp.h" #include "DebugTrace.h" #if !defined( WIN32 ) #include "OnBoard.h" #endif /* HAL */ #include "hal_lcd.h" #include "hal_led.h" #include "hal_key.h" #include "hal_uart.h"

/******* GLOBAL VARIABLES *******/ // This list should be filled with Application specific Cluster IDs. const cId_t BasicApp_ClusterList[BasicApp_MAX_CLUSTERS] = { BasicApp_CLUSTERID }; const SimpleDescriptionFormat_t BasicApp_SimpleDesc = { BasicApp_ENDPOINT, // int Endpoint; BasicApp_PROFID, // uint16 AppProfId[2]; BasicApp_DEVICEID, // uint16 AppDeviceId[2]; BasicApp_DEVICE_VERSION, // int AppDevVer:4; BasicApp_FLAGS, // int AppFlags:4; BasicApp_MAX_CLUSTERS, // byte AppNumInClusters; (cId_t *)BasicApp_ClusterList, // byte *pAppInClusterList; BasicApp_MAX_CLUSTERS, // byte AppNumInClusters; (cId_t *)BasicApp_ClusterList // byte *pAppInClusterList; }; endPointDesc_t BasicApp_epDesc; /******* LOCAL VARIABLES *******/ byte BasicApp_TaskID; // Task ID for internal task/event processing // This variable will be received when // BasicApp_Init() is called. afAddrType_t BasicApp_DstAddr;

Page 20: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

20

/******* LOCAL FUNCTIONS *******/ static void BasicApp_HandleKeys( byte shift, byte keys ); static void BasicApp_MessageMSGCB( afIncomingMSGPacket_t *pckt ); /******* PUBLIC FUNCTIONS *****/ /********************************************************************* * @fn BasicApp_Init * @brief Initialization function for the Basic App Task. * This is called during initialization and should contain * any application specific initialization (ie. hardware * initialization/setup, table initialization, power up * notificaiton ... ). * @param task_id - the ID assigned by OSAL. This ID should be * used to send messages and set timers. * @return none */ void BasicApp_Init( uint8 task_id ) { BasicApp_TaskID = task_id; // Register for all key events - This app will handle all key events RegisterForKeys( BasicApp_TaskID ); // Update the display #if defined ( LCD_SUPPORTED ) HalLcdWriteString( "BasicApp", HAL_LCD_LINE_1 ); #endif }

Page 21: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

21

/*********************************************************************************************** * @fn BasicApp_ProcessEvent * @brief Generic Application Task event processor. This function is called to process * all events for the task. Events include timers, messages and any other user * defined events. * @param task_id - The OSAL assigned task ID. * @param events - events to process. This is a bit map and can contain more than one event. * @return none */ uint16 BasicApp_ProcessEvent( uint8 task_id, uint16 events ) { afIncomingMSGPacket_t *MSGpkt; (void)task_id; // Intentionally unreferenced parameter if ( events & SYS_EVENT_MSG ) { MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( BasicApp_TaskID ); while ( MSGpkt ) { switch ( MSGpkt->hdr.event ) { case KEY_CHANGE: BasicApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys ); break; case AF_INCOMING_MSG_CMD: BasicApp_MessageMSGCB( MSGpkt ); break; default: break; }

Page 22: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

22

// Release the memory osal_msg_deallocate( (uint8 *)MSGpkt ); // Next MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( BasicApp_TaskID ); } // return unprocessed events return (events ^ SYS_EVENT_MSG); } // Send a message out - This event is generated by a timer // (setup in BasicApp_Init()). if ( events & BasicApp_SEND_MSG_EVT ) { return (events ^ BasicApp_SEND_MSG_EVT); } // Discard unknown events return 0; }

Page 23: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

23

/********************************************************************* * @fn BasicApp_HandleKeys * @brief Handles all key events for this device. * @param shift - true if in shift/alt. * @param keys - bit field for key events. Valid entries: HAL_KEY_SW_4, HAL_KEY_SW_3, HAL_KEY_SW_2, HAL_KEY_SW_1 * @return none */ static void BasicApp_HandleKeys( uint8 shift, uint8 keys ) { // Shift is used to make each button/switch dual purpose. if ( shift ) { if ( keys & HAL_KEY_SW_1 ) { } if ( keys & HAL_KEY_SW_2 ) { } if ( keys & HAL_KEY_SW_3 ) { } if ( keys & HAL_KEY_SW_4 ) { } } else {

if ( keys & HAL_KEY_SW_1 ) { } if ( keys & HAL_KEY_SW_2 ) { } if ( keys & HAL_KEY_SW_3 ) { } if ( keys & HAL_KEY_SW_4 ) { } } }

Page 24: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

24

/******** LOCAL FUNCTIONS *******/ /********************************************************************* * @fn BasicApp_MessageMSGCB * @brief Data message processor callback. This function processes * any incoming data - probably from other devices. So, based * on cluster ID, perform the intended action. * @param none * * @return none */ static void BasicApp_MessageMSGCB( afIncomingMSGPacket_t *pkt ) { switch ( pkt->clusterId ) { case BasicApp_CLUSTERID: // "the" message #if defined( LCD_SUPPORTED ) HalLcdWriteScreen( (char*)pkt->cmd.Data, "rcvd" ); #elif defined( WIN32 ) WPRINTSTR( pkt->cmd.Data ); #endif break; } }

Page 25: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

25

練習1:使用LED驅動程式 打開HAL API手冊,找到LED Service。

練習使用

HalLedSet() HalLedBlink() HalLedEnterSleep() HalLedExitSleep()

// @fn BasicApp_HandleKeys static void BasicApp_HandleKeys( uint8 shift, uint8 keys ) { // Shift is used to make each button/switch dual purpose. if ( shift ) { ...略... } else { if ( keys & HAL_KEY_SW_1 ) { } if ( keys & HAL_KEY_SW_2 ) { } if ( keys & HAL_KEY_SW_3 ) { } if ( keys & HAL_KEY_SW_4 ) { } } }

Page 26: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

26

練習2:使用LCD驅動程式 打開HAL API手冊,找到LCD Service。

練習使用

HalLcdWriteString() HalLcdWriteValue() HalLcdWriteScreen() HalLcdWriteStringValue() HalLcdWriteStringValueValue() HalLcdDisplayPercentBar()

// @fn BasicApp_HandleKeys static void BasicApp_HandleKeys( uint8 shift, uint8 keys ) { // Shift is used to make each button/switch dual purpose. if ( shift ) { ...略... } else { if ( keys & HAL_KEY_SW_1 ) { } if ( keys & HAL_KEY_SW_2 ) { } if ( keys & HAL_KEY_SW_3 ) { } if ( keys & HAL_KEY_SW_4 ) { } } }

Page 27: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

27

練習3:在LCD顯示LED狀態 練習使用

HalLedSet() HalLedGetState()

if ( keys & HAL_KEY_SW_1 ) { HalLedSet(HAL_LED_1, HAL_LED_MODE_TOGGLE); if(HalLedGetState()&0x01) HalLcdWriteString("LED1:ON", HAL_LCD_LINE_1); else HalLcdWriteString("LED1:OFF", HAL_LCD_LINE_1); }

Page 28: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

28

練習4:使用RTC // number of seconds since 0 hrs, 0 minutes, 0 seconds, on the // 1st of January 2000 UTC typedef uint32 UTCTime; // To be used with typedef struct { uint8 seconds; // 0-59 uint8 minutes; // 0-59 uint8 hour; // 0-23 uint8 day; // 0-30 uint8 month; // 0-11 uint16 year; // 2000+ } UTCTimeStruct;

#include "OSAL_Clock.h" /***** GLOBAL VARIABLES *****/ UTCTimeStruct utc; UTCTime utcSecs; /***** LOCAL FUNCTIONS *****/ static void showTimeonLCD(UTCTimeStruct *utc); void BasicApp_Init( uint8 task_id ) { ... 略 ... utc.seconds = 20; utc.minutes = 25; utc.hour = 13; utc.day = 23; utc.month = 2; utc.year = 2014;

if ((utc.hour < 24) && (utc.minutes < 60) && (utc.seconds < 60) && (utc.month < 12) && (utc.day < 31) && (utc.year > 1999) && (utc.year < 2136)) { /* check for leap year */ if ((utc.month != 1) || (utc.day < (IsLeapYear( utc.year ) ? 29 : 28))) { /* Numbers look reasonable, convert to UTC */ utcSecs = osal_ConvertUTCSecs( &utc ); } } if ( utcSecs == 0 ) { /* Bad parameter(s) */ } else { /* Parameters accepted, set the time */ osal_setClock( utcSecs ); } }

Page 29: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

29

static void BasicApp_HandleKeys( uint8 shift, uint8 keys ) { // Shift is used to make each button/switch dual purpose. if ( shift ) { ... 略 ... } else { if ( keys & HAL_KEY_SW_1 ) { utcSecs = osal_getClock(); osal_ConvertUTCTime( &utc, utcSecs ); showTimeonLCD(&utc); } ... 略 ... } }

/***** LOCAL FUNCTIONS *****/ void showTimeonLCD(UTCTimeStruct *utc) { char timeStr[17]; timeStr[0] = (utc->year/1000) + 0x30; timeStr[1] = ((utc->year%1000)/100) + 0x30; timeStr[2] = ((utc->year%100)/10) + 0x30; timeStr[3] = (utc->year%10) + 0x30; timeStr[4] = '/'; timeStr[5] = ((utc->month+1)/10) + 0x30; timeStr[6] = ((utc->month+1)%10) + 0x30; timeStr[7] = '/'; timeStr[8] = ((utc->day+1)/10) + 0x30; timeStr[9] = ((utc->day+1)%10) + 0x30; timeStr[10] = ' '; timeStr[11] = ((utc->hour)/10) + 0x30; timeStr[12] = ((utc->hour)%10) + 0x30; timeStr[13] = ':'; timeStr[14] = ((utc->minutes)/10) + 0x30; timeStr[15] = ((utc->minutes)%10) + 0x30; timeStr[16] = '\0'; HalLcdWriteString(timeStr, HAL_LCD_LINE_3); }

Page 30: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

30

練習5:使用ADC讀值並顯示 支援7~12位元解析度(頻寬4~30kHz),取樣速度4 MHz。

共8個ADC通道可使用(P0埠),支援單端與差動類比輸入。

#include "hal_adc.h"

if ( keys & HAL_KEY_SW_2 ) { uint16 sample_catched = 0; sample_catched = HalAdcRead(HAL_ADC_CHANNEL_7, HAL_ADC_RESOLUTION_8); HalLcdWriteValue(100, 10, HAL_LCD_LINE_1); HalLcdWriteStringValue("Read", sample_catched, 10, HAL_LCD_LINE_1); } HalAdcRead(HAL_ADC_CHANNEL_TEMP, HAL_ADC_RESOLUTION_14);

HalAdcRead(HAL_ADC_CHANNEL_VDD, HAL_ADC_RESOLUTION_14);

Page 31: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

31

練習6:NV記憶體 CC2530的記憶體結構

Page 32: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

32

NV記憶體API osal_nv_item_init()

init an item in non-volatile memory

osal_nv_read()

read item

osal_nv_write()

write item

osal_offsetof()

calculate memory offset

// OSAL NV item IDs #define ZCD_NV_EXTADDR 0x0001 #define ZCD_NV_BOOTCOUNTER 0x0002 #define ZCD_NV_STARTUP_OPTION 0x0003 #define ZCD_NV_START_DELAY 0x0004 ... 略 ... // NV Items Reserved for applications (user app) // 0x0401 – 0x0FFF #define ZCD_NV_APP_SAVE1 0x0401 #define ZCD_NV_APP_SAVE2 0x0404

/***** GLOBAL VARIABLES *****/ uint8 nv_init_data = 0xA2; static void BasicApp_HandleKeys( uint8 shift, uint8 keys ) { uint8 nv_data; ... 略 ... if ( keys & HAL_KEY_SW_3 ) { osal_nv_item_init(ZCD_NV_APP_SAVE1,1,NULL); osal_nv_read(ZCD_NV_APP_SAVE1,0,1,&nv_data); HalLcdWriteStringValue("Read", nv_data, 16, HAL_LCD_LINE_1); } if ( keys & HAL_KEY_SW_4 ) { nv_data = 0xB3; osal_nv_write(ZCD_NV_APP_SAVE1,0,1,&nv_data); }

Page 33: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

33

練習7:設定事件與事件處理器 打開OSAL API手冊。

OSAL所提供的API,相當於是PC上的系統呼叫,功能非常多樣與強大,但有些功能是留給框架使用的,我們在開發App時,大概沒機會用到。

OSAL是屬於「事件驅動式」的系統,我們首先試一下跟App開發有關的「事件設定API」。

只要可以設定並觸發事件,這一切將變得更有趣。

osal_set_event() osal_start_timerEx() osal_start_reload_timer() osal_stop_timerEx()

Page 34: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

34

定義App專屬的事件 打開BasicApp.h,設定「事件的名字跟識別碼」

BasicApp.c:在按鍵處理函式調用OSAL的API來觸發事件。

/********* CONSTANTS *********/ ... 略 ... // Application Events (OSAL) - These are bit weighted definitions. #define BasicApp_SEND_MSG_EVT 0x0001 #define BasicApp_TOGGLE_LED1_EVT 0x0001 #define BasicApp_TOGGLE_LED2_EVT 0x0002 #define BasicApp_TOGGLE_LED3_EVT 0x0004 #define BasicApp_COUNT_LCD_EVT 0x0008

if ( keys & HAL_KEY_SW_1 ) { osal_set_event(BasicApp_TaskID, BasicApp_TOGGLE_LED1_EVT); } if ( keys & HAL_KEY_SW_2 ) { osal_start_timerEx(BasicApp_TaskID, BasicApp_TOGGLE_LED2_EVT, 5000); } if ( keys & HAL_KEY_SW_3 ) { osal_start_reload_timer(BasicApp_TaskID, BasicApp_TOGGLE_LED3_EVT, 250); } if ( keys & HAL_KEY_SW_4 ) { osal_start_reload_timer(BasicApp_TaskID, BasicApp_COUNT_LCD_EVT, 1000); }

Page 35: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

35

修改事件處理器 修改BasicApp_ProcessEvent( )

uint8 mycounts = 0; 先在global var加入一全域變數

if ( events & BasicApp_TOGGLE_LED1_EVT ) { HalLedSet(HAL_LED_1, HAL_LED_MODE_TOGGLE); return (events ^ BasicApp_TOGGLE_LED1_EVT); } if ( events & BasicApp_TOGGLE_LED2_EVT ) { HalLedSet(HAL_LED_2, HAL_LED_MODE_TOGGLE); return (events ^ BasicApp_TOGGLE_LED2_EVT); } if ( events & BasicApp_TOGGLE_LED3_EVT ) { HalLedSet(HAL_LED_3, HAL_LED_MODE_TOGGLE); return (events ^ BasicApp_TOGGLE_LED3_EVT); } if ( events & BasicApp_COUNT_LCD_EVT ) { mycounts++; HalLcdWriteStringValue ("Counts:", mycounts, 10, 1); return (events ^ BasicApp_COUNT_LCD_EVT); }

Page 36: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

36

使用osal_stop_timerEx()取消事件觸發

// Shift is used to make each button/switch dual purpose. if ( shift ) { if ( keys & HAL_KEY_SW_1 ) { } if ( keys & HAL_KEY_SW_2 ) { osal_stop_timerEx(BasicApp_TaskID, BasicApp_TOGGLE_LED2_EVT); } if ( keys & HAL_KEY_SW_3 ) { osal_stop_timerEx(BasicApp_TaskID, BasicApp_TOGGLE_LED3_EVT); } if ( keys & HAL_KEY_SW_4 ) { osal_stop_timerEx(BasicApp_TaskID, BasicApp_COUNT_LCD_EVT); } } else {

Page 37: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

37

練習8、9、10 練習8:定時取樣

設計一應用程式,按下起始鈕後,每0.5秒讀一次ADC取樣值,並將結果顯示在LCD。按下停止鈕後,停止取樣與顯示。

練習9:換一顆「事件驅動」的腦袋

按下起始鈕後,每0.5秒遞增變數counts並將其值顯示在LCD上。

當計數值等於30,停止計數,LCD會自動在1秒鐘後將計數值顯示為0。

在計數期間,當counts為5的倍數時,觸發一個事件來toggle LED1。

為 了 練 習 「 事 件 驅 動 」 的 理 念 , 請 用 「 事 件 」 的 觀 念 來 處 理 , 不 要 在BasicApp_COUNT_LCD_EVT的事件處理程序裡面直接調用HalLedSet()。你應該要調用「事件設定」函式來做!

練習10:如何重複?

如何不使用osal_start_reload_timer()而達到重複設定事件的效果?

請寫一支程式展示效果。

Page 38: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

38

練習11:紅綠燈系統

試試看,參考你之前做好的紅綠燈子系統,你能用「事件驅動」的方法讓它在BasicApp中運作起來嗎?

Page 39: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

BasicApp

OSAL的任務間通訊(IPC)機制

Page 40: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

40

一個App自己的世界 BasicApp.h定義的事件

在BasicApp.c中,使用OSAL系統呼叫來設定事件

若你有第二個App (FooApp),你可以在BasicApp.c中調用

來觸發FooApp應用的事件嗎?

如果BasicApp還需要夾帶一段資料給FooApp.c呢?

// Application Events (OSAL) - These are bit weighted definitions. #define BasicApp_TOGGLE_LED1_EVT 0x0001 #define BasicApp_TOGGLE_LED2_EVT 0x0002 #define BasicApp_TOGGLE_LED3_EVT 0x0004 #define BasicApp_COUNT_LCD_EVT 0x0008

osal_set_event(BasicApp_TaskID, BasicApp_TOGGLE_LED1_EVT);

osal_set_event(FooApp_TaskID, FooApp_SOME_EVT);

Page 41: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

41

有了IPC機制,再多Apps也不怕! BasicApp是1個App,對OSAL而言,它是個「以事件驅動

為中心的狀態機 (或許稱為Delegator更貼切)」,其實只是一個事件處理函式,這個函式就是排給OSAL的task。

系統若存在2個以上的Apps

BasicApp_ProcessEvent()

BasicApp_HandleKeys()

BasicApp_MessageMSGCB()

Page 42: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

42

練習:兩個Apps BasicApp:

這個應用程式是一個控制器,它會以IPC的方式傳送訊息(指令或資料)給系統中的另外一個應用程式CountApp。

CountApp:

收到START指令後,它會開始計數並在LCD第一行顯示計數值。

收到STOP指令時,CountApp將停止計數,LCD第一行顯示將靜止。

收到WRITE指令後,它會將BasicApp傳過來的字串資料寫到LCD的第二行。

收到CLEAR指令後,它會將LCD的第二行清除。

Page 43: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

43

先準備BasicAppComm.h BasicAppComm.h 所 準 備 的 東 西 , 是 要 讓 BasicApp 跟

CountApp彼此都認識的事情。

第一個是「訊息的格式」:basicAppMsg_t

第二個是「指令的定義」:指令識別碼

typedef struct { osal_event_hdr_t hdr; uint8 appCmd; uint8 appDataLen; uint8 *appData; } basicAppMsg_t; /********************************************************************* * CONSTANTS */ // Application CMD #define CountApp_START_COUNT_CMD 0x01 #define CountApp_STOP_COUNT_CMD 0x02 #define CountApp_WRITE_LCD_CMD 0x03 #define CountApp_CLEAR_LCD_CMD 0x04

Page 44: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

44

將BasicApp複製為CountApp

因為我們是基於框架來開發應用程式,因此應用程式的「殼」看起來會很相似。將BasicApp.h / BasicApp.c 複製為CountApp.h / CountApp.c,接著打開兩個新檔案將程式中「BasicApp」字樣全部用「CountApp」取代。

首先修改OSAL_BasicApp.c,它負責告訴OSAL要載入哪些應用程式:

/* OSAL_BasicApp.c */ /* INCLUDES */ ...略... #include "BasicApp.h" #include "CountApp.h" /* GLOBAL VARIABLES */ const pTaskEventHandlerFn tasksArr[] = { Hal_ProcessEvent, BasicApp_ProcessEvent, CountApp_ProcessEvent }; const uint8 tasksCnt = sizeof( tasksArr ) / sizeof( tasksArr[0] ); uint16 *tasksEvents;

/* FUNCTIONS */ /* @fn osalInitTasks */ void osalInitTasks( void ) { uint8 taskID = 0; ... 略 ... Hal_Init( taskID++ ); BasicApp_Init( taskID++ ); CountApp_Init( taskID ); }

Page 45: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

45

搞定BasicApp BasicApp.h (加事件定義)

BasicApp.c

#define BasicApp_ENDPOINT 10 // Application Events (OSAL) #define BasicApp_SEND_MSG_EVT 0x0001

#include "BasicAppComm.h" ...略... /* LOCAL FUNCTIONS */ static void BasicApp_send_IPCMsg(uint8 task_id, uint8 appCMD, uint8 *pBuf, uint8 dataLen); ...略...

uint16 BasicApp_ProcessEvent( uint8 task_id, uint16 events ) { ... 略 ... if ( events & BasicApp_SEND_MSG_EVT ) { BasicApp_send_IPCMsg(2, CountApp_START_COUNT_CMD, "", 0); return (events ^ BasicApp_SEND_MSG_EVT); } ... 略 ... }

Page 46: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

46

/* LOCAL FUNCTIONS */ void BasicApp_send_IPCMsg(uint8 task_id, uint8 appCMD, uint8 *pBuf, uint8 dataLen) { basicAppMsg_t *msg; /* Build and send the message to the APP */ msg = (basicAppMsg_t *)osal_msg_allocate(sizeof(basicAppMsg_t) + (dataLen)); if ( msg ) { /* Build and send message up the app */ msg->hdr.event = BasicApp_MSG; msg->hdr.status = 0; msg->appCmd = appCMD; msg->appDataLen = dataLen; if (dataLen == 0) { msg->appData = (uint8*)(msg); } else { msg->appData = (uint8*)(msg+1); osal_memcpy( msg->appData, pBuf, dataLen); } osal_msg_send( task_id, (uint8 *)msg ); } }

用FileSeek搜尋*.h檔關鍵字「OSAL System Message IDs/Events」

Page 47: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

47

static void BasicApp_HandleKeys( uint8 shift, uint8 keys ) { ... 略 ... if ( keys & HAL_KEY_SW_1 ) { osal_set_event(BasicApp_TaskID, BasicApp_SEND_MSG_EVT); } if ( keys & HAL_KEY_SW_2 ) { BasicApp_send_IPCMsg(2, CountApp_STOP_COUNT_CMD, "", 0); } if ( keys & HAL_KEY_SW_3 ) { uint8 msg_str[] = "Show me!"; uint8 msg_len = (sizeof(msg_str)/sizeof(uint8)); BasicApp_send_IPCMsg(2, CountApp_WRITE_LCD_CMD, msg_str, msg_len); } if ( keys & HAL_KEY_SW_4 ) { BasicApp_send_IPCMsg(2, CountApp_CLEAR_LCD_CMD, "", 0); } } }

Page 48: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

48

搞定CountApp CountApp.h

#ifndef CountApp_H #define CountApp_H /* INCLUDES */ #include "ZComDef.h" /* CONSTANTS */ // Application Events (OSAL) - These are bit weighted definitions. #define CountApp_COUNT_LCD_EVT 0x0001 /* FUNCTIONS */ /* * Task Initialization for the Generic Application */ extern void CountApp_Init( byte task_id ); /* * Task Event Processor for the Generic Application */ extern UINT16 CountApp_ProcessEvent( byte task_id, UINT16 events ); #endif /* CountApp_H */

Page 49: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

49

CountApp.c

#include "BasicAppComm.h" #include "CountApp.h" ... 略 ... /* GLOBAL VARIABLES */ uint8 mycounts = 0; ... 略 ... /* LOCAL FUNCTIONS */ static void CountApp_HandleBasicAppMSG(basicAppMsg_t *pkt );

void CountApp_Init( uint8 task_id ) { CountApp_TaskID = task_id; }

Page 50: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

50

/* @fn CountApp_ProcessEvent */ uint16 CountApp_ProcessEvent( uint8 task_id, uint16 events ) { ... 略 ... if ( events & SYS_EVENT_MSG ) { MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( CountApp_TaskID ); while ( MSGpkt ) { switch ( MSGpkt->hdr.event ) { case BasicApp_MSG: CountApp_HandleBasicAppMSG( (basicAppMsg_t *)MSGpkt ); break; ... 略 ... } osal_msg_deallocate( (uint8 *)MSGpkt ); MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( CountApp_TaskID ); } return (events ^ SYS_EVENT_MSG); } if ( events & CountApp_COUNT_LCD_EVT ) { HalLcdWriteStringValue ("Counts:", mycounts, 10, 1); mycounts++; osal_start_timerEx(CountApp_TaskID, CountApp_COUNT_LCD_EVT, 1000); return (events ^ CountApp_COUNT_LCD_EVT); } return 0; }

Page 51: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

51

/********************************************************************* * @fn CountApp_HandleBasicAppMSG * @brief Data message processor callback. This function processes * incoming data from BasicApp. * @param Incoming message * @return none */ static void CountApp_HandleBasicAppMSG(basicAppMsg_t *pkt ) { switch ( pkt->appCmd ) { case CountApp_START_COUNT_CMD: osal_set_event(CountApp_TaskID, CountApp_COUNT_LCD_EVT); break; case CountApp_STOP_COUNT_CMD: osal_stop_timerEx(CountApp_TaskID, CountApp_COUNT_LCD_EVT); break; case CountApp_WRITE_LCD_CMD: HalLcdWriteString((char*)(pkt->appData), HAL_LCD_LINE_2); break; case CountApp_CLEAR_LCD_CMD: HalLcdWriteString("", HAL_LCD_LINE_2); break; } }

Page 52: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

組織PAN網路實驗 範例應用程式:FlyApp

Page 53: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

53

目標 接下來的實驗,我們會試著組建一個ZigBee的PAN,然

後一步步地接觸descriptors、application framework、binding等重要的概念。

Starting a network

Routing

Packet Sniffer

Descriptors

Application Framework

Binding

ZDO APIs

Callbacks

Multiple Endpoints

Mobility

Page 54: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

54

通訊協定層

Page 55: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

55

組建PAN網路 我們將使用FlyApp作為範例,並隨實驗過程慢慢地修改

這個應用程式以符合需求。以下是所需硬體:

需要Coordinator、Router以及End-Device三塊板子

需要CC2531 USB Dongle作為Packet Sniffer

用IAR打開FlyApp Project並設定channel與PAN ID

打開f8wConfig.cfg設定channel跟PAN ID (見下頁表格)。實際應用中,你可以打開很多個channel讓stack自己去搜索。但為了簡化實驗,我們在這裡只打開一個channel就好,以便讓Sniffer可容易找到通道。

Page 56: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

56

在f8wConfig.cfg組態檔設定PAN ID PAN_ID = 0xFFFF & device = Coordinator:

Device uses IEEE address to choose a PAN_ID (last 2 bytes)

PAN_ID = 0xFFFF & device = Router 或 End Device Device will join any available PAN

PAN_ID ≠ 0xFFFF & device = Coordinator

Device will use the set value for the PAN_ID

PAN_ID ≠ 0xFFFF & device = Router 或 End Device Device will ONLY join a PAN that has this PAN_ID

Page 57: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

57

設定f8wConfig.cfg 根據下表任意選取一組workgroup的通道與PAN ID設定

進行實驗:

Workgroup Channel PAN ID Frequency (MHz)

1 12 (0x0C) 0x0AAA 2410

2 13 (0x0D) 0x0BEE 2415

3 14 (0x0E) 0x0CEE 2420

4 15 (0x0F) 0x0DEE 2425

5 16 (0x10) 0x0EEE 2430

6 17 (0x11) 0x0EFF 2435

7 18 (0x12) 0x0BAB 2440

Page 58: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

58

將Automatic Polling功能關掉

為簡化實驗,請先關掉Automatic Polling功能。若這個功能啟用的話,End Device會週期性地對Coordinator發出data request,這樣會干擾我們在Sniffer想觀察的東西。所以,請在f8wConfig.cfg中令DPOLL_RATE=0 (ED用)。

在板子實體上標記它們的角色

用便利貼寫下Coordinator、Router以及End Device,並貼在各塊板子上做識別。

Page 59: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

59

修改OSAL_FlyApp.c /**** INCLUDES ****/ ... 略 #include "nwk.h" #include "APS.h" #include "ZDApp.h" #if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT ) #include "ZDNwkMgr.h" #endif #if defined ( ZIGBEE_FRAGMENTATION ) #include "aps_frag.h" #endif

const pTaskEventHandlerFn tasksArr[] = { macEventLoop, nwk_event_loop, Hal_ProcessEvent, 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 FlyApp_ProcessEvent };

mac_api.h

nwk.h

APS.h

aps_frag.h

ZDApp.h

ZDMwkMgr.h

Page 60: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

60

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++ ); 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 FlyApp_Init( taskID ); }

Page 61: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

61

回顧一下FlyApp.h (不用改) /**** Filename: FlyApp.h ****/ ...略 #include "ZComDef.h" /**** CONSTANTS ****/ #define FlyApp_ENDPOINT 10 #define FLYAPP_PROFID 0x0F04 #define FLYAPP_DEVICEID 0x0001 #define FLYAPP_DEVICE_VERSION 0 #define FLYAPP_FLAGS 0 #define FLYAPP_MAX_CLUSTERS 1 #define FLYAPP_CLUSTERID 1 // Send Message Timeout #define FLYAPP_SEND_MSG_TIMEOUT 5000 // Every 5 seconds // Application Events (OSAL) - These are bit weighted definitions. #define FLYAPP_SEND_MSG_EVT 0x0001 /**** FUNCTIONS ****/ /* Task Initialization for the Generic Application */ extern void FlyApp_Init( byte task_id ); /* Task Event Processor for the Generic Application */ extern UINT16 FlyApp_ProcessEvent( byte task_id, UINT16 events );

Page 62: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

62

修改FlyApp.c /**** INCLUDES ****/ #include "OSAL.h" #include "AF.h" #include "ZDApp.h" #include "ZDObject.h" #include "ZDProfile.h" #include "FlyApp.h" #include "DebugTrace.h" ... 略 ...

/***** LOCAL VARIABLES *****/ byte FlyApp_TaskID; // Task ID for internal task/event processing // This variable will be received when // FlyApp_Init() is called. devStates_t FlyApp_NwkState; byte FlyApp_TransID; // This is the unique message ID (counter) afAddrType_t FlyApp_DstAddr;

/**** LOCAL FUNCTIONS ****/ static void FlyApp_ProcessZDOMsgs( zdoIncomingMsg_t *inMsg ); static void FlyApp_HandleKeys( byte shift, byte keys ); static void FlyApp_MessageMSGCB( afIncomingMSGPacket_t *pckt ); static void FlyApp_SendTheMessage( void );

const cId_t \ FlyApp_ClusterList[FLYAPP_MAX_CLUSTERS] = { FLYAPP_CLUSTERID };

const SimpleDescriptionFormat_t FlyApp_SimpleDesc = { FLYAPP_ENDPOINT, // int Endpoint; FLYAPP_PROFID, // uint16 AppProfId[2]; FLYAPP_DEVICEID, // uint16 AppDeviceId[2]; FLYAPP_DEVICE_VERSION, // int AppDevVer:4; FLYAPP_FLAGS, // int AppFlags:4; FLYAPP_MAX_CLUSTERS, // byte AppNumInClusters; (cId_t *)FlyApp_ClusterList, // byte *pAppInClusterList; FLYAPP_MAX_CLUSTERS, // byte AppNumInClusters; (cId_t *)FlyApp_ClusterList // byte *pAppInClusterList; }; endPointDesc_t FlyApp_epDesc;

Page 63: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

63

void FlyApp_Init( uint8 task_id ) { FlyApp_TaskID = task_id; FlyApp_NwkState = DEV_INIT; FlyApp_TransID = 0; // Device hardware initialization can be added here or in main() (Zmain.c). // If the hardware is application specific - add it here. // If the hardware is other parts of the device add it in main(). FlyApp_DstAddr.addrMode = (afAddrMode_t)AddrNotPresent; FlyApp_DstAddr.endPoint = 0; FlyApp_DstAddr.addr.shortAddr = 0; // Fill out the endpoint description. FlyApp_epDesc.endPoint = FLYAPP_ENDPOINT; FlyApp_epDesc.task_id = &FlyApp_TaskID; FlyApp_epDesc.simpleDesc = (SimpleDescriptionFormat_t *)&FlyApp_SimpleDesc; FlyApp_epDesc.latencyReq = noLatencyReqs; // Register the endpoint description with the AF afRegister( &FlyApp_epDesc ); // Register for all key events - This app will handle all key events RegisterForKeys( FlyApp_TaskID ); // Update the display #if defined ( LCD_SUPPORTED ) HalLcdWriteString( "FlyApp", HAL_LCD_LINE_1 ); #endif ZDO_RegisterForZDOMsg( FlyApp_TaskID, End_Device_Bind_rsp ); ZDO_RegisterForZDOMsg( FlyApp_TaskID, Match_Desc_rsp ); }

見 ZDProfile.h

手冊 Z-Stack API.pdf

Page 64: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

64

uint16 FlyApp_ProcessEvent( uint8 task_id, uint16 events ) { afIncomingMSGPacket_t *MSGpkt; afDataConfirm_t *afDataConfirm; // Data Confirmation message fields byte sentEP; ZStatus_t sentStatus; byte sentTransID; // This should match the value sent (void)task_id; // Intentionally unreferenced parameter if ( events & SYS_EVENT_MSG ) { MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( FlyApp_TaskID ); while ( MSGpkt ) { switch ( MSGpkt->hdr.event ) { case ZDO_CB_MSG: FlyApp_ProcessZDOMsgs( (zdoIncomingMsg_t *)MSGpkt ); break; case KEY_CHANGE: FlyApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys ); break;

Page 65: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

65

case AF_DATA_CONFIRM_CMD: // This message is received as a confirmation of a data packet sent. // The status is of ZStatus_t type [defined in ZComDef.h] // The message fields are defined in AF.h afDataConfirm = (afDataConfirm_t *)MSGpkt; sentEP = afDataConfirm->endpoint; sentStatus = afDataConfirm->hdr.status; sentTransID = afDataConfirm->transID; (void)sentEP; (void)sentTransID; // Action taken when confirmation is received. if ( sentStatus != ZSuccess ) { // The data wasn't delivered -- Do something } break; case AF_INCOMING_MSG_CMD: FlyApp_MessageMSGCB( MSGpkt ); break; case ZDO_STATE_CHANGE: FlyApp_NwkState = (devStates_t)(MSGpkt->hdr.status); if ( (FlyApp_NwkState == DEV_ZB_COORD) || (FlyApp_NwkState == DEV_ROUTER) || (FlyApp_NwkState == DEV_END_DEVICE) ) { // Start sending "the" message in a regular interval. osal_start_timerEx( FlyApp_TaskID, FLYAPP_SEND_MSG_EVT, FLYAPP_SEND_MSG_TIMEOUT ); } break; default: break; }

Page 66: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

66

// Release the memory osal_msg_deallocate( (uint8 *)MSGpkt ); // Next MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( FlyApp_TaskID ); } // while ( MSGpkt ) // return unprocessed events return (events ^ SYS_EVENT_MSG); } // Send a message out - This event is generated by a timer // (setup in FlyApp_Init()). if ( events & FLYAPP_SEND_MSG_EVT ) { // Send "the" message FlyApp_SendTheMessage(); // Setup to send message again osal_start_timerEx( FlyApp_TaskID, FLYAPP_SEND_MSG_EVT, FLYAPP_SEND_MSG_TIMEOUT ); // return unprocessed events return (events ^ FLYAPP_SEND_MSG_EVT); } // Discard unknown events return 0; }

Page 67: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

67

static void FlyApp_ProcessZDOMsgs( zdoIncomingMsg_t *inMsg ) { switch ( inMsg->clusterID ) { case End_Device_Bind_rsp: if ( ZDO_ParseBindRsp( inMsg ) == ZSuccess ) { // Light LED HalLedSet( HAL_LED_4, HAL_LED_MODE_ON ); } #if defined( BLINK_LEDS ) else { // Flash LED to show failure HalLedSet ( HAL_LED_4, HAL_LED_MODE_FLASH ); } #endif break; case Match_Desc_rsp: ZDO_ActiveEndpointRsp_t *pRsp = ZDO_ParseEPListRsp( inMsg ); if ( pRsp ) { if ( pRsp->status == ZSuccess && pRsp->cnt ) { FlyApp_DstAddr.addrMode = (afAddrMode_t)Addr16Bit; FlyApp_DstAddr.addr.shortAddr = pRsp->nwkAddr; // Take the first endpoint, Can be changed to search through endpoints FlyApp_DstAddr.endPoint = pRsp->epList[0]; // Light LED HalLedSet( HAL_LED_4, HAL_LED_MODE_ON ); } osal_mem_free( pRsp ); } break; } }

Page 68: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

68

static void FlyApp_HandleKeys( uint8 shift, uint8 keys ) { zAddrType_t dstAddr; // Shift is used to make each button/switch dual purpose. if ( shift ) { ... 略 ... } else { if ( keys & HAL_KEY_SW_1 ) { // Since SW1 isn't used for anything else in this application... #if defined( SWITCH1_BIND ) // we can use SW1 to simulate SW2 for devices that only have one switch, keys |= HAL_KEY_SW_2; #elif defined( SWITCH1_MATCH ) // or use SW1 to simulate SW4 for devices that only have one switch keys |= HAL_KEY_SW_4; #endif } if ( keys & HAL_KEY_SW_2 ) { HalLedSet ( HAL_LED_4, HAL_LED_MODE_OFF ); // Initiate an End Device Bind Request for the mandatory endpoint dstAddr.addrMode = Addr16Bit; dstAddr.addr.shortAddr = 0x0000; // Coordinator ZDP_EndDeviceBindReq( &dstAddr, NLME_GetShortAddr(), FlyApp_epDesc.endPoint, FLYAPP_PROFID, FLYAPP_MAX_CLUSTERS, (cId_t *)FlyApp_ClusterList, FLYAPP_MAX_CLUSTERS, (cId_t *)FlyApp_ClusterList, FALSE ); }

Page 69: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

69

if ( keys & HAL_KEY_SW_3 ) { } if ( keys & HAL_KEY_SW_4 ) { HalLedSet ( HAL_LED_4, HAL_LED_MODE_OFF ); // Initiate a Match Description Request (Service Discovery) dstAddr.addrMode = AddrBroadcast; dstAddr.addr.shortAddr = NWK_BROADCAST_SHORTADDR; ZDP_MatchDescReq( &dstAddr, NWK_BROADCAST_SHORTADDR, FLYAPP_PROFID, FLYAPP_MAX_CLUSTERS, (cId_t *)FlyApp_ClusterList, FLYAPP_MAX_CLUSTERS, (cId_t *)FlyApp_ClusterList, FALSE ); } } }

Page 70: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

70

/********************************************************************* * @fn FlyApp_MessageMSGCB * @brief Data message processor callback. This function processes * any incoming data - probably from other devices. So, based * on cluster ID, perform the intended action. * @param none * @return none */ static void FlyApp_MessageMSGCB( afIncomingMSGPacket_t *pkt ) { switch ( pkt->clusterId ) { case FLYAPP_CLUSTERID: // "the" message #if defined( LCD_SUPPORTED ) HalLcdWriteScreen( (char*)pkt->cmd.Data, "rcvd" ); #elif defined( WIN32 ) WPRINTSTR( pkt->cmd.Data ); #endif break; } }

Page 71: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

71

/********************************************************************* * @fn FlyApp_SendTheMessage * @brief Send "the" message. * @param none * @return none */ static void FlyApp_SendTheMessage( void ) { char theMessageData[] = "Hello World"; if ( AF_DataRequest( &FlyApp_DstAddr, &FlyApp_epDesc, FLYAPP_CLUSTERID, (byte)osal_strlen( theMessageData ) + 1, (byte *)&theMessageData, &FlyApp_TransID, AF_DISCV_ROUTE, AF_DEFAULT_RADIUS ) == afStatus_SUCCESS ) { // Successfully requested to be sent. } else { // Error occurred in request to send. } }

Page 72: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

72

燒錄Coord, Router, 與 ED

燒錄Coordinator、Router與End Device

Page 73: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

73

打開Coordinator,以Sniffer觀察封包

Coordinator會先掃描通道。因此時其他裝置都還沒開啟,LCD上會顯示Energy Level Scan Failed 。 預 設 掃 描 的 energy level 可 以 透 過 參 數ZDNWKMGR_ACCEPTABLE_ENERGY_LEVEL來設定。在Sniffer記下主動掃描期間的beacon request,Coordinator應該把自己的地址assign為0x0000。

打開Router

開啟Router後,Coordinator會指定一個short address給Router,請在Sniffer中找出這個短地址為何(可觀察解析封包中的association欄位)。

Page 74: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

74

打開End Device

這是第一個加入網路的End Device,Coordinator會指定一個short address給End Device,請在Sniffer中找出這個短地址為何。

綁定(Binding)

兩種綁定方式:按下SW2為手動綁定,按下SW4是自動綁定。

在綁定之後,裝置就會開始傳一些東西。

8-1: 按下End Device的SW4(自動綁定),若綁定成功LED1就會亮起。

8-2: End Device同時被綁至Router跟Coordinator,而且每5秒會傳送「Hello World」給對方。 Coord跟Router的LCD會出現Hello World。

8-3: 使用自動綁定時因為End Device只能夠存一個目的地址,因為 Router可能是最後回應的一 個,因此End Device傳出去的訊息終點是跑到Router (也可能會發生Coordinator最後回應, 所以End Device的訊息的終點會跑到Coordinator去)。

Page 75: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

程式碼探索:擴充FlyApp

Page 76: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

76

程式碼探索 維持f8wConfig.cfg中channel跟PAN ID設定,並確認End

Device的Polling是關閉的

先trace一下FlyApp.c應用程式的邏輯

開啟檔案FlyApp.h,找到以下參數之定義 FLYAPP_ENDPOINT =

FLYAPP_PROFID =

FLYAPP_DEVICEID =

FLYAPP_CLUSTERID =

如果各應用之間要能溝通,那麼這些 ID的設定要相同才行 (實際上,profile ID是由ZigBee聯盟提供)。

打開FlyApp.c

在 GLOBAL VARIABLES 區 塊 中 有 FlyApp_ClusterList 以 及 FlyApp Simple Descriptor (FlyApp_SimpleDesc) 的結構定義。

Page 77: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

77

FlyApp.c應用程式中的主要函式

兩個API (開放由OSAL調用):

FlyApp_Init() – 由OSAL_FlyApp.c 的osalInitTasks()調用

FlyApp_ProcessEvent() – 由OSAL調用

四個local函數 (宣告為static):

FlyApp_ProcessZDOMsgs()

FlyApp_HandleKeys()

FlyApp_MessgaeMSGCB()

FlyApp_SendTheMessage()

FlyApp_Init() :

當FlyApp在OSAL_FlyApp.c被初始化時,OSAL會先執行FlyApp_Init()。

定義Endpoint Descriptor結構、向AF層註冊此endpoint的description

向OSAL註冊按鍵callback、負責寫東西到LCD、向ZDO註冊2個綁定類型的callbacks,以及負責做end device綁定回應與match descriptor回應。

在程式碼中,LCD Write上面的”if defined”敘述則是編譯器preprocessor的定義,用來引入或引出LCD的驅動程式。

Page 78: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

78

void FlyApp_Init( uint8 task_id ) { FlyApp_TaskID = task_id; FlyApp_NwkState = DEV_INIT; FlyApp_TransID = 0; // Device hardware initialization can be added here or in main() (Zmain.c). // If the hardware is application specific - add it here. // If the hardware is other parts of the device add it in main(). FlyApp_DstAddr.addrMode = (afAddrMode_t)AddrNotPresent; FlyApp_DstAddr.endPoint = 0; FlyApp_DstAddr.addr.shortAddr = 0; // Fill out the endpoint description. FlyApp_epDesc.endPoint = FLYAPP_ENDPOINT; FlyApp_epDesc.task_id = &FlyApp_TaskID; FlyApp_epDesc.simpleDesc = (SimpleDescriptionFormat_t *)&FlyApp_SimpleDesc; FlyApp_epDesc.latencyReq = noLatencyReqs; // Register the endpoint description with the AF afRegister( &FlyApp_epDesc ); // Register for all key events - This app will handle all key events RegisterForKeys( FlyApp_TaskID ); // Update the display #if defined ( LCD_SUPPORTED ) HalLcdWriteString( "FlyApp", HAL_LCD_LINE_1 ); #endif ZDO_RegisterForZDOMsg( FlyApp_TaskID, End_Device_Bind_rsp ); ZDO_RegisterForZDOMsg( FlyApp_TaskID, Match_Desc_rsp ); }

見 ZDProfile.h

手冊 Z-Stack API.pdf

Page 79: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

79

FlyApp_ProcessEvent():

這是FlyApp的run-time部分。當系統事件(SYS_EVENT_MSG)或應用事件(FLYAPP_SEND_MSG_EVT) 發 生 時 , FlyApp_ProcessEvent() 會 被 系 統callback,然後透過一段判斷程式來判斷到底是哪一種事件被接收了(它是事件委派delegating events的實作)。

Message 執行

ZDO callback FlyApp_ProcessZDOMsgs()

Key change FlyApp_HandleKeys()

AF_DATA_CONFIRM_CMD 傳送訊息的確認

AF_INCOMING_MSG_CMD FlyApp_MessgaeMSGCB()

ZDO_STATE_CHANGE 綁定成功後,即開始發送訊息要用的OSAL timer來觸發事件

FLYAPP_SEND_MSG_EVT (由OSAL的timer過期所觸發),訊息就會被送出去,然後OSAL timer會被重新設置而持續地每5秒鐘觸發一次訊息發送事件。

Page 80: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

80

uint16 FlyApp_ProcessEvent( uint8 task_id, uint16 events ) { afIncomingMSGPacket_t *MSGpkt; afDataConfirm_t *afDataConfirm; // Data Confirmation message fields byte sentEP; ZStatus_t sentStatus; byte sentTransID; // This should match the value sent (void)task_id; // Intentionally unreferenced parameter if ( events & SYS_EVENT_MSG ) { MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( FlyApp_TaskID ); while ( MSGpkt ) { switch ( MSGpkt->hdr.event ) { case ZDO_CB_MSG: FlyApp_ProcessZDOMsgs( (zdoIncomingMsg_t *)MSGpkt ); break; case KEY_CHANGE: FlyApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys ); break;

Page 81: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

81

case AF_DATA_CONFIRM_CMD: // This message is received as a confirmation of a data packet sent. // The status is of ZStatus_t type [defined in ZComDef.h] // The message fields are defined in AF.h afDataConfirm = (afDataConfirm_t *)MSGpkt; sentEP = afDataConfirm->endpoint; sentStatus = afDataConfirm->hdr.status; sentTransID = afDataConfirm->transID; (void)sentEP; (void)sentTransID; HalLedSet ( HAL_LED_1, HAL_LED_MODE_TOGGLE ); // Action taken when confirmation is received. if ( sentStatus != ZSuccess ) { // The data wasn't delivered -- Do something } break; case AF_INCOMING_MSG_CMD: FlyApp_MessageMSGCB( MSGpkt ); break; case ZDO_STATE_CHANGE: FlyApp_NwkState = (devStates_t)(MSGpkt->hdr.status); if ( (FlyApp_NwkState == DEV_ZB_COORD) || (FlyApp_NwkState == DEV_ROUTER) || (FlyApp_NwkState == DEV_END_DEVICE) ) { // Start sending "the" message in a regular interval. osal_start_timerEx( FlyApp_TaskID, FLYAPP_SEND_MSG_EVT, FLYAPP_SEND_MSG_TIMEOUT ); } break; default: break; }

Page 82: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

82

// Release the memory osal_msg_deallocate( (uint8 *)MSGpkt ); // Next MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( FlyApp_TaskID ); } // return unprocessed events return (events ^ SYS_EVENT_MSG); } // Send a message out - This event is generated by a timer // (setup in FlyApp_Init()). if ( events & FLYAPP_SEND_MSG_EVT ) { // Send "the" message FlyApp_SendTheMessage(); // Setup to send message again osal_start_timerEx( FlyApp_TaskID, FLYAPP_SEND_MSG_EVT, FLYAPP_SEND_MSG_TIMEOUT ); // return unprocessed events return (events ^ FLYAPP_SEND_MSG_EVT); } // Discard unknown events return 0; }

Page 83: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

83

FlyApp_ProcessZDOMsgs()

當ED綁定請求成功時會點亮LED4,若請求失敗則會閃爍LED4。綁定資訊會自動地被儲存。若是match descriptor request綁定成功,一樣會點亮LED4並存下綁定資訊。

static void FlyApp_ProcessZDOMsgs( zdoIncomingMsg_t *inMsg ) { switch ( inMsg->clusterID ) { case End_Device_Bind_rsp: if ( ZDO_ParseBindRsp( inMsg ) == ZSuccess ) { // Light LED HalLedSet( HAL_LED_4, HAL_LED_MODE_ON ); } #if defined( BLINK_LEDS ) else { HalLedSet ( HAL_LED_4, HAL_LED_MODE_FLASH ); // Flash LED to show failure } #endif break; case Match_Desc_rsp: { ZDO_ActiveEndpointRsp_t *pRsp = ZDO_ParseEPListRsp( inMsg ); if ( pRsp ) { if ( pRsp->status == ZSuccess && pRsp->cnt ) { FlyApp_DstAddr.addrMode = (afAddrMode_t)Addr16Bit; FlyApp_DstAddr.addr.shortAddr = pRsp->nwkAddr; // Take the first endpoint, Can be changed to search through endpoints FlyApp_DstAddr.endPoint = pRsp->epList[0]; HalLedSet( HAL_LED_4, HAL_LED_MODE_ON ); // Light LED

} osal_mem_free( pRsp ); } } break; } }

API手冊

見上一章p.54 / ZDProfile.h

API手冊

Page 84: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

84

FlyApp_HandleKeys()

若SW2被按下: 發出end device bind request (手動綁定)。

若SW4被按下: 發出match descriptor request (自動綁定)。

static void FlyApp_HandleKeys( uint8 shift, uint8 keys ) { zAddrType_t dstAddr; if ( shift ) { // Shift is used to make each button/switch dual purpose. if ( keys & HAL_KEY_SW_1 ) { } if ( keys & HAL_KEY_SW_2 ) { } if ( keys & HAL_KEY_SW_3 ) { } if ( keys & HAL_KEY_SW_4 ) { } } else { if ( keys & HAL_KEY_SW_1 ) { // Since SW1 isn't used for anything else in this application... #if defined( SWITCH1_BIND ) // we can use SW1 to simulate SW2 for devices that only have one switch, keys |= HAL_KEY_SW_2; #elif defined( SWITCH1_MATCH ) // or use SW1 to simulate SW4 for devices that only have one switch keys |= HAL_KEY_SW_4; #endif }

Page 85: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

85

if ( keys & HAL_KEY_SW_2 ) { HalLedSet ( HAL_LED_4, HAL_LED_MODE_OFF ); // Initiate an End Device Bind Request for the mandatory endpoint dstAddr.addrMode = Addr16Bit; dstAddr.addr.shortAddr = 0x0000; // Coordinator ZDP_EndDeviceBindReq( &dstAddr, NLME_GetShortAddr(), FlyApp_epDesc.endPoint, FLYAPP_PROFID, FLYAPP_MAX_CLUSTERS, (cId_t *)FlyApp_ClusterList, FLYAPP_MAX_CLUSTERS, (cId_t *)FlyApp_ClusterList, FALSE ); } if ( keys & HAL_KEY_SW_3 ) { } if ( keys & HAL_KEY_SW_4 ) { HalLedSet ( HAL_LED_4, HAL_LED_MODE_OFF ); // Initiate a Match Description Request (Service Discovery) dstAddr.addrMode = AddrBroadcast; dstAddr.addr.shortAddr = NWK_BROADCAST_SHORTADDR; ZDP_MatchDescReq( &dstAddr, NWK_BROADCAST_SHORTADDR, FLYAPP_PROFID, FLYAPP_MAX_CLUSTERS, (cId_t *)FlyApp_ClusterList, FLYAPP_MAX_CLUSTERS, (cId_t *)FlyApp_ClusterList, FALSE ); } } }

API手冊

API手冊

API手冊

afStatus_t ZDP_EndDeviceBindReq( ... { ... // LocalCoordinator + SrcExtAddr + ep + ProfileID + NumInClusters + NumOutClusters. len = 2 + Z_EXTADDR_LEN + 1 + 2 + 1 + 1; len += (NumInClusters + NumOutClusters) * sizeof ( uint16 );

Page 86: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

86

FlyApp_MessgaeMSGCB()

分析收進來的訊息,並顯示於LCD上。

static void FlyApp_MessageMSGCB( afIncomingMSGPacket_t *pkt ) { switch ( pkt->clusterId ) { case FLYAPP_CLUSTERID: // "the" message #if defined( LCD_SUPPORTED ) HalLcdWriteScreen( (char*)pkt->cmd.Data, "rcvd" ); #elif defined( WIN32 ) WPRINTSTR( pkt->cmd.Data ); #endif break; } }

Page 87: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

87

FlyApp_SendTheMessage()

建構訊息並以無線發送出(透過AF_DataRequest)字串”Hello World”。

static void FlyApp_SendTheMessage( void ) { char theMessageData[] = "Hello World"; if ( AF_DataRequest( &FlyApp_DstAddr, &FlyApp_epDesc, FLYAPP_CLUSTERID, (byte)osal_strlen( theMessageData ) + 1, (byte *)&theMessageData, &FlyApp_TransID, AF_DISCV_ROUTE, AF_DEFAULT_RADIUS ) == afStatus_SUCCESS ) { // Successfully requested to be sent. HalLedSet ( HAL_LED_2, HAL_LED_MODE_TOGGLE ); } else { // Error occurred in request to send. } }

API手冊

#include char *s1 = "ABCDE" ; char s2[] = "ABCDE" ; [結果] strlen(s1) = 5 strlen(s2) = 5 [說明] strlen():回傳字串長度(“不”含哨兵字元) [比較] sizeof(s2) = 6 (“有”包括哨兵字元) sizeof(s1) = 1 (sizeof 回傳的是指標的大小為1 byte) 另外, strlen()會讀到第一個哨兵字元為止 也就是ascii-code十六進位值00 所以萬一你想要讓動態產生的字元陣列可以讀取長度的話 請務必在最後面加一個\0

Page 88: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

88

Build FlyApp並下載到各塊板子 (上一個實驗已build好)

打開Sniffer然後依照Coord、Router與ED的順序開啟電源。在Sniffer中觀察相關的association process。

在End Device按下SW4(自動綁定),綁定成功後LED4會點亮。然後End Device每5秒鐘會發送”Hello World”字串給其他板子。因為我們是用自動綁定,所以只會看見到router的流量(或只有到Coord)。

Match_Desc_req

Page 89: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

89

練習:修改LCD顯示訊息 除了顯示收到的Hello World字串外,如果能讓LCD顯示

收到訊息的次數就更好了

檢視FlyApp.c中的FlyApp_MessageMSGCB()

在FlyApp.c的區域變數區塊加入變數count

在HalLcdWriteScreen()之下加入以下程式碼

/***** LOCAL VARIABLES *****/ uint16 count = 0;

static void FlyApp_MessageMSGCB( afIncomingMSGPacket_t *pkt ) { ... HalLcdWriteScreen( (char*)pkt->cmd.Data, "rcvd" ); count++; HalLcdWriteValue( count, 10, HAL_LCD_LINE_3 ); ... }

Page 90: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

90

練習:增加App-level ACK

我們實驗中,裝置都擺的很靠近,但是在真實應用時,訊息從一個裝置傳送到另外一個裝置可能要經過好幾個hop才會送達。MAC層的確認訊息(ACK)只會指示第1個hop是否成功,沒辦法指示訊息是否正確送到好幾個hops外的另一個裝置。因此,你可能會需要使用應用層的確認(ACK),來處理這件事情。

檢視FlyApp.c的FlyApp_SendTheMessage()

修改AF_DataRequest()的option中倒數第二個引數

AF.h定義了options參數的bitmaps,務必要確認當你加入AF_ACK_REQUEST option時,不要消除掉已經存在的AF_DISCV_ROUTE option (所以用OR的)。

if ( AF_DataRequest( &FlyApp_DstAddr, &FlyApp_epDesc, FLYCAPP_CLUSTERID, (byte)osal_strlen( theMessageData ) + 1, (byte *)&theMessageData, &FlyApp_TransID, AF_ACK_REQUEST | AF_DISCV_ROUTE, AF_DEFAULT_RADIUS ) == afStatus_SUCCESS )

Page 91: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

91

以Sniffer觀察

Page 92: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

Cluster與Binding 範例應用程式:FlyApp

Page 93: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

93

目標 改變Router傳送”Hello World”的週期 (目前為5秒發一次)

新增一個控制發送週期的cluster (命令)

使用中央綁定來完成 (centralized binding)

ZigBee裝置的「網路探索」與「綁定功能」是由兩個跟使用者App無關的東西來管理:AF與ZDO

為了瞭解裝置是如何探索網路與其他裝置進行溝通,我們必須要了解AF跟ZDO是如何一起工作來完成綁定。

Coord Router

“Hello World”

ED

ED發訊號以控制Router發送速度

自動綁定

Page 94: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

94

AF與ZDO Application Framework (AF)

AF 主 要 是 在 處理 收 進 來 的訊息 , 並 將 訊息進 行 解 多 工送到 相 對 應的endpoint(或app)。

使用者的App在初始化之時,一定要向AF註冊,這樣子AF才知道要將收進來的訊息分派給誰。

AF所需知道的裝置資訊,通通都塞在Endpoint Descriptor這個資料結構裡面。

ZigBee Device Object (ZDO)

ZDO是一個必要的應用元件,它負責建立、探索、加入網路以及如何綁定與安全性管理等。

ZDO一定屬於Endpoint 0。ZDO降低了客戶需要自行開發網路管理的程式,但是在產品開發的過程中,也許有一些ZDO的功能是需要做一些修改的,但除非必要最好不要亂改。

Page 95: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

95

Clusters (I) Clusters由一個16-bits ID定義,它們是application objects。

cluster定義了application的意義(用途)。

Clusters將指令(commands)與資料(data)、屬性(attribute)封裝在一起。

指令引起action,屬性追蹤cluster的狀態。

Clusters只有在某個特定profile中才有意義。

Cluster除了做identifier以外,他還有方向性。SimpleDescriptor

描述cluster時又拆分成input跟output兩種list。

Department of Electronic Engineering, NTUT

Page 96: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

96

Clusters (II) Commands: Public profiles使用ZCL,可透過通用指令組,

使「獲得(get)」或「設定(set)」屬性變得簡單。

Attributes: 屬性是一個16-bits的數字,它可以是0x0000到 0xFFFF 之 間 的 任 何 值 。 在 ZCL 中 , 它 們 傾 向 於 從0x0000開始編起。

我們的範例使用private profile,沒有用到ZCL。在真正碰到ZCL之前,我們先把cluster視為是單純的”命令集”即可。

EP_X EP_Y

Cluster List (命令集)

Cluster List (命令集)

OUT IN

Page 97: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

97

實驗過程 FlyApp_HandleKeys()函式使用了ZDO綁定函式:

自 動 綁 定 是 透 過 呼 叫 ZDP_MatchDescReq() 函 數 來 初 始 化 , 你 要 將Endpoint Descriptor作為輸入餵給這個函數。初始化完畢後,自動尋找機制會使用 ZigBee 定義好的網路探索機制對不同的節點找出匹配clusters的應用元件。

自動綁定的情況下,對綁定請求的回應會使用ZDO_STATE_CHANGE事件傳回給應用,它提供給應用通訊時所需要的資訊。

先 開 啟 Coord 跟 Router , 然 後 在 Router 按 下 SW4 先 跟Coord綁在一起。若成功,Router就會每5秒傳一次Hello World給Coord。

在 Router 新 增 一 個 control cluster 並 使 用 centralized binding來跟一個ED綁在一起。ED也有相同的control cluster來控制Router的訊息發送週期。

Page 98: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

98

改變Reporting的週期 FlyApp原本的程式是ED會週期性地發資料給Router跟

Coord,這個週期是在編譯的時候設定的。

現在我們要加入程式來允許ED (remote)可以動態地設定Router (sensor)的reporting時間。為了要達成這個目的,我們要先了解一種新的訊息類性,稱為Cluster。

除了第一種綁定的data clusters (RouterCoord與ED Coord) 機制,我們還會加入第二種綁定 機制 — 使用Centralized binding。我們會自創configuration cluster,讓 ED 來 控 制 Router 的 reporting 速 度 (End Device Router)。

Page 99: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

99

修改FlyApp.h

將 更改成

修改FlyApp.c,加入local變數SendMsgTimeout

在FlyApp_Init()起始處,將此變數初始化為預設值

更新FlyApp_ProcessEvent()的expired timer(有兩處)

// Send Message Timeout #define FLYAPP_SEND_MSG_TIMEOUT 5000 // Every 5 seconds

#define FLYAPP_SEND_MSG_TIMEOUT_DEFAULT 5000

/***** LOCAL VARIABLES *****/ uint16 SendMsgTimeout;

void FlyApp_Init( uint8 task_id ) { SendMsgTimeout = FLYAPP_SEND_MSG_TIMEOUT_DEFAULT; FlyApp_TaskID = task_id;

osal_start_timerEx( FlyApp_TaskID, FLYAPP_SEND_MSG_EVT, FLYAPP_SEND_MSG_TIMEOUT );

osal_start_timerEx( FlyApp_TaskID, FLYAPP_SEND_MSG_EVT, SendMsgTimeout );

Page 100: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

100

Build並下載至各板子後,使用自動綁定

開啟Sniffer,然後依序打開Coord、Router,等到Router綠色LED亮起之後再打開ED。按下ED SW4進行自動綁定,綁定成功後你可以暫停Packet Sniffer然後觀察APS payload。

裝置執行綁定時會呼叫自動尋找函數並初始化Match Descriptor Request 的傳輸。這個Match Descriptor Request由網內所有Cluster匹配裝置在Match Descriptor Response 單 播 回 Requester 。 要 分 析 Match Descriptor Request的APS payload會有點困難,因為它在Sniffer裡面並沒有依照field被拆開,所以要自己根據fields定義來看

Match Descriptor Request – APS Payload

2 bytes – Transaction ID (incremented automatically) 2 bytes – Destination Address (0xFFFF – broadcast) 2 bytes – Profile ID (0x0F04 – found in FlyApp.h) 1 byte – Number of input Clusters 2 byte array – Input Cluster List [] 1 byte – Number of output Clusters 2 byte array – Output Cluster List[]

Match Descriptor Response – APS Payload

2 bytes – Transaction ID (incremented automatically) 1 byte – Status 2 bytes – Sender Network Address 1 byte – Match counter (Number of matches) 1 byte array[] – Endpoint ID of match

Page 101: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

101

加入Control Cluster:修改FlyApp.h

在App裡加入第2個Endpoint(控制訊息)。第2個Endpoint的目標是管理一個新的Cluster,用來控制Router中send timer的timeout period。

我們會在同一個FlyApp的.c與.h檔來實現這個新的Endpoint (實際上最好是一個ep對一個App)。雖然也可以用不同的檔案去隔離不同的Endpoints,或是在原本舊的Endpoint裡實現data Cluster與control Cluster,不過我們還是以建立一個新的Endpoint來管理control Cluster。除了可以簡化整個邏輯之外,也可以利用現有ZDO支援函數而不需要做大幅度的程式修改。

在FlyApp.h增加要用來更新sender report period的新Endpoint ID跟新Cluster,我們稱其為FLYAPP_TIMEOUT_CLUSTER。在FlyApp.h下增加下列定義

/********************************************************************* * CONSTANTS */ ... #define FLYAPP_CTRL_ENDPOINT 11 #define FLYAPP_MAX_CTRL_CLUSTERS 1 #define FLYAPP_TIMEOUT_CLUSTER 8

Page 102: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

102

在FlyApp.c的全域變數區段增加cluster list

增加的cluster list等一下在本地建立綁定表時會用到。

/********************************************************************* * GLOBAL VARIABLES */ // This list is for our new timeout control cluster const cId_t FlyApp_ClusterCtrlList[FLYAPP_MAX_CTRL_CLUSTERS] = { FLYAPP_TIMEOUT_CLUSTER };

Page 103: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

103

新增Descriptor

新增第2個Simple Descriptor跟Endpoint Descriptor以便跟舊的data cluster做區分。新的cluster我們也會向AF註冊,然後在綁定時用到。

// Simple Descriptor for Control Clusters const SimpleDescriptionFormat_t FlyApp_SimpleCtrlDesc = { FLYAPP_CTRL_ENDPOINT, // int Endpoint; FLYAPP_PROFID, // uint16 AppProfId[2]; FLYAPP_DEVICEID, // uint16 AppDeviceId[2]; FLYAPP_DEVICE_VERSION, // int AppDevVer:4; FLYAPP_FLAGS, // int AppFlags:4; #if !defined(COORDINATOR) // Router and End Device 0, // no input clusters (cId_t *) NULL, FLYAPP_MAX_CTRL_CLUSTERS, // byte AppNumOutClusters; (cId_t *)FlyApp_ClusterCtrlList, // byte *pAppOutClusterList; #endif #if defined(COORDINATOR) // Coordinator FLYAPP_MAX_CTRL_CLUSTERS, // byte AppNumInClusters; (cId_t *)FlyApp_ClusterCtrlList, // byte *pAppInClusterList; 0, // no output clusters (cId_t *) NULL, #endif }; // Endpoint Descriptor for Control Clusters #if !defined(COORDINATOR) endPointDesc_t FlyApp_ctrlEpDesc; #endif

Page 104: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

104

加入Preprocessor定義

分 別 打 開 Coordinator 、 Router 跟 End Device 的 Project Options 為 每 個Workspace進行組態,在C/C++ compiler選單下面點選Preprocessor頁籤。在Defined symbols list新增「COORDINATOR」到Coordinator’s options、新增「ROUTER」到Router options以及新增「ENDDEVICE」到End Device options。

初始化並向AF註冊新的Descriptor

在FlyApp.c的FlyApp_Init()最後加入

#if !defined(COORDINATOR) FlyApp_ctrlEpDesc.endPoint = FLYAPP_CTRL_ENDPOINT; FlyApp_ctrlEpDesc.task_id = & FlyApp_TaskID; FlyApp_ctrlEpDesc.simpleDesc = (SimpleDescriptionFormat_t *)&FlyApp_SimpleCtrlDesc; FlyApp_ctrlEpDesc.latencyReq = noLatencyReqs; afRegister( & FlyApp_ctrlEpDesc ); #endif

Page 105: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

105

確定編譯不會出錯

目前我們還沒有增加任何新的 binding。

為了觀察binding process,先暫時關掉資料傳輸來簡化Sniffer view,將ZDO_STATE_CHANGE事件處理中的osal_start_timerEx()呼叫先註釋掉,這樣就不會見到週期性的traffic。

Build並燒錄Coord、Router跟End Device。在適當的channel跑Sniffer,然後依序打開Coord、Router以及End Device。在End Device上按下SW4來執行自動綁定。使用Packet Sniffer來看一下。

Page 106: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

106

使用Centralized Binding (I) 現在我們要使用Centralized binding來將Router跟End Device綁在一

起,讓End Device可以動態控制Router的report period。

此機制是在選擇的裝置按下按鍵後,在規定的timeout period內進行綁定。

Coord在timeout period內會收集End Device Bind Request messages並且依據profile ID與cluster ID的匹配來建立綁定表元素。預設的End device binding timeout (APS_DEFAULT_MAXBINDING_TIME)是16秒(定義在ZGlobals.h),但是可以到f8wConfig.cfg加入設定來修改。

FlyApp裡面End Device Bind的例子程式碼是透過按下SW2來執行的。FlyApp.c的key handler呼叫ZDProfile.c的ZDP_EndDeviceBindReq(),他會收集所有App的endpoint資訊並且發送給Coordinator。

Page 107: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

107

使用Centralized Binding (II) 當 Coord 在 timeout 時 間 內 收 到 2 個 matching ED Bind

Requests,他會在請求裝置建立source binding entries。

Coord會跑的程序(假設在ZDO ED Bind Requests時有找到matches):

1) 發送一個ZDO Unbind Request給第一個device。因為End Device Bind是一種toggle process,所以unbind會先被送出去以移除任何可能存在的bind entry。

2) 等待ZDO Unbind Response,如果response status是ZDP_NO_ENTRY,那麼便發送一個ZDO Bind Request來使binding entry存入source device。如果response status是ZDP_SUCCESS,則繼續處理第一個裝置的cluster ID。

3) 等待ZDO Bind Response。當收到時,繼續第一個device的其他cluster ID。

4) 當第一個device搞定後,對第二的device做同樣的程序。

5) 當第二個device也搞定後,發送ZDO End Device Bind Response message給第一還有第二的裝置。

Page 108: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

108

啟用REFLECTOR option。

為了讓上述程序發生,要先開啟REFLECTOR編譯選項啟用 local binding storage。在Router以及End Device的workspace options加入這個compile option。到C/C++ Compile選擇Preprocessor頁籤,在Defined Symbols box裡面加入REFLECTOR。

發送Bind Request

在 FlyApp_HandleKeys() 的 SW1 處 理 程 式 加 入 一 些 敘 述 來 使 用ZDP_EndDeviceBindReq() (請確認你傳遞了正確的Endpoint ID)執行binding process。

下頁程式碼實現了當SW1在Router跟End Device被按下,完成了新的control endpoints 之 間 的 centralized binding 。 如 果 binding 成 功 ,FlyApp_ProcessZDOMsgs()會點亮LED4來指示這個程序是成功或是失敗的。

Page 109: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

109

static void FlyApp_HandleKeys( uint8 shift, uint8 keys ) { zAddrType_t dstAddr; // Shift is used to make each button/switch dual purpose. if ( shift ) { // 略 } else { if ( keys & HAL_KEY_SW_1 ) { #if !defined(COORDINATOR) HalLedSet ( HAL_LED_4, HAL_LED_MODE_OFF ); dstAddr.addrMode = Addr16Bit; dstAddr.addr.shortAddr = 0x0000; // Coordinator ZDP_EndDeviceBindReq( &dstAddr, NLME_GetShortAddr(), FlyApp_ctrlEpDesc.endPoint, FLYAPP_PROFID, FLYAPP_MAX_CTRL_CLUSTERS, (cId_t *)FlyApp_ClusterCtrlList, FLYAPP_MAX_CTRL_CLUSTERS, (cId_t *)FlyApp_ClusterCtrlList, FALSE ); #endif // Since SW1 isn't used for anything else in this application... #if defined( SWITCH1_BIND ) // we can use SW1 to simulate SW2 for devices that only have one switch, keys |= HAL_KEY_SW_2; #elif defined( SWITCH1_MATCH ) // or use SW1 to simulate SW4 for devices that only have one switch keys |= HAL_KEY_SW_4; #endif }

Page 110: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

110

Build然後download。

依照正常的build/load與start-up process開啟。現在觀察,當你在Router按下SW1時的binding process,然後在16秒內按下ED的SW1。在Sniffer觀察這個綁定的結果。

註:這個實驗你要先按Router再按ED才能binding。如果你想要改變這個順序,你就要把ED的periodic polling重新開啟才可以(因為你若先按ED,但是ED之後都不會再去問Parent有沒有訊息要給它,當然不會bind成功)。

如果你的binding成功,在Router跟ED上的LED4都會點亮。因為我們剛剛先關掉OSAL timer逾時觸發,所以並不會有任何Hello World訊息會被傳送。

重新啟用週期性的資料傳輸。

將註釋掉的osal_start_timerEx()恢復

重新Build並下載

Page 111: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

111

增加Timeout Cluster的收發程式 接著我們要增加一段Timeout cluster的發送程式,新增

ED 的 control key handler 以 及 增 加 Router 的 Timeout receive程式碼,最後測試驗證之。

現在我們已經在End Device跟Router之間建立了綁定,我們現在要增加一些code來使用這個綁定關係以發送訊號給Router,讓他可以調整他的reporting period。注意,這個程式碼只對End Device 適用。

發送Control Information

要發送資料給一個device需要呼叫AF_DataRequest()函數,因此我們需要建立第二個local function來發送這個新的訊息。將下面的local function declaration加入FlyApp.c。

/********* LOCAL FUNCTIONS *********/ ...略 static void FlyApp_SendTheMessage( void ); #if defined(ENDDEVICE) static void FlyApp_SendTheControl( uint16 reportPeriod ); #endif

Page 112: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

112

新增FlyApp_SendTheControl()函式

將下列程式FlyApp_SendTheControl()加到FlyApp.c的後面

#if defined(ENDDEVICE) /********************************************************************* * @fn FlyApp_SendTheControl * @brief Send a control message. * @param reportPeriod: timeout of the timer * @return none */ static void FlyApp_SendTheControl( uint16 reportPeriod ) { afAddrType_t addrPlaceholder; addrPlaceholder.addrMode = afAddrNotPresent; if ( AF_DataRequest( &addrPlaceholder, & Flypp_ctrlEpDesc, FLYAPP_TIMEOUT_CLUSTER, (byte)sizeof(reportPeriod), (byte *)&reportPeriod, &FlyApp_TransID, AF_DISCV_ROUTE | AF_ACK_REQUEST, AF_DEFAULT_RADIUS ) == afStatus_SUCCESS ) { // Successfully requested to be sent. } else { // Error occurred in request to send. } } #endif

addrPlaceHolder是destination address的place holder。在address mode使用afAddrNotPresent讓應用自己在 binding table 裡 面 根 據 commandId 去 look-up address , 指 向 要 發 送 的 目 地 去 。 然 後 stack software就可以使用那個address來傳送訊息到正確的destination。

Page 113: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

113

在FlyApp_HandleKeys()增加Key Handler程式

增加SW3的key handler code來發送control information。這會用來切換Router 1秒或5秒的report period。

static void FlyApp_HandleKeys( uint8 shift, uint8 keys ) { zAddrType_t dstAddr; static volatile uint8 defaultTime=FALSE; ... if ( keys & HAL_KEY_SW_3 ) { #if defined (ENDDEVICE) if(defaultTime) { FlyApp_SendTheControl( 5000 ); } else { FlyApp_SendTheControl( 1000 ); } defaultTime ^= 1; // toggle value #endif }

Page 114: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

114

在FlyApp_MessageMSGCB()更新Router的Timer

ED以所設計的timing control message送給Router。我們要在Router上增加一 些 code 來 處 理 這 個 訊 息 。 App 會 透 過 一 個 system 事 件(AF_INCOMING_MSG_CMD)來得知incoming message通知。當收到此通知時,我們的App會呼叫FlyApp_MessageMSGCB()來分析與處理這個訊息。在這個函數裡,我們檢查incoming packet的ClusterId來判斷我們收到的message是甚麼類型。

static void FlyApp_MessageMSGCB( afIncomingMSGPacket_t *pkt ) { switch ( pkt->clusterId ) { case FLYAPP_CLUSTERID: // "the" message ...略 break; #if !defined (COORDINATOR) case FLYAPP_TIMEOUT_CLUSTER: // update timeout variable for outgoing "CLUSTERID" message SendMsgTimeout = *((uint16 *)(pkt->cmd.Data)); break; #endif } }

Page 115: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

115

Build然後下載你的project到這三個devices

驗證一下你的改變能如預期般工作,End Device現在應該具動態調整Router report time的能力。

1) 開啟Coordinator。

2) 開啟Router。

3) 按下Router的SW4來初始化與Coordinator的自動綁定。Router會開始送messages給Coordinator,以預設的5秒鐘間隔。

4) 打開End Device。

5) 按下Router的SW1,然後在16秒內按下 End Device的SW1來初始化這個控制應用的centralized binding。

6) 按下End Device的SW3,這會無線發送control message去改變Router的reporting period (1或5秒)。現在看Coordinator的LCD,你會看到傳送速率變化。

Page 116: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

116

App開發總結 (I) 在f8wConfig.cfg設定channel跟PAN ID

DPOLL_RATE=0 (ED)只是實驗用,實際情況是要設定的

綁定方式: SW1與SW2為中央綁定、SW4為自動綁定(實作時就直接參考範例程式碼的作法),實際上還有另外兩種綁定方法:輔助綁定(assisted binding,第三方設備輔助)、自設計應用綁定(application binding)。

兩個OSAL會回調的Callback

FlyApp_Init()

由OSAL_FlyApp.c 的osalInitTasks()調用

它的內容主要是在系統初始化時註冊一堆東西(EP, Callbacks等)

FlyApp_ProcessEvent()

由OSAL調用,它是一個事件委派器(delegator)

續下頁

Page 117: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

117

App開發總結 (II) FlyApp_ProcessEvent()

事件來源:

• SYS_EVENT_MSG系統事件 (可在 ZComDef.h 自訂系統事件) • ZDO_CB_MSG (要用ZDO_RegisterForZDOMsg()註冊App,ClusterId在ZDProfile.h)

• KEY_CHANGE (要用RegisterForKeys()註冊App,實作在OnBoard.c)

• AF_DATA_CONFIRM_CMD (要用afRegister()註冊ep,全域系統訊息定義在ZComDef.h)

• AF_INCOMING_MSG_CMD (要用afRegister()註冊ep收OTA,App訊息事件可用Cluster實作)

• ZDO_STATE_CHANGE (全域系統訊息定義在ZComDef.h)

• 應用自定義的事件 (定義在FlyApp.h)

• 以上兩類事件,處理完後return時要把events ^ 掉。

事件會分派給「本地Callback函式」去處理,回調函式內容要自己做

App的OTA訊息事件用「Cluster」實作,FlyApp_MessageMSGCB()也是delegator,把收到的Cluster再分給自己設計的Callback函式處理

Cluster要掛在Endpoint Descriptor上 (EP要向AF註冊,這樣AF才知道OTA訊息收到後要流給誰)

發送OTA訊息:調用AF_DataRequest()

Page 118: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

解析SampleApp

Page 119: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

119

SampleApp Source Code 功能:

1. 硬體jumper決定裝置啟動為Coord或Router (P18_9-11=Coord)

2. SW1廣播給Group1 EP,該EP會使LED1閃爍

3. SW2解除Group1

程式碼

OSAL_SampleApp.c

SampleAppHw.h

SampleApp.h

SampleApp.c

Page 120: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

120

SampleApp運行的基本流程

SampleApp_Init() SampleApp.c

osalInitTasks() OSAL_SampleApp.c

osal_init_system() OSAL.c

main() ZMain.c

SampleApp_ProcessEvent()

Page 121: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

121

OSAL_SampleApp.c

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 SampleApp_ProcessEvent };

const uint8 tasksCnt = sizeof( tasksArr ) / sizeof( tasksArr[0] ); uint16 *tasksEvents; 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 SampleApp_Init( taskID ); }

Page 122: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

122

SampleApp.h /********** CONSTANTS ************/ #define SAMPLEAPP_ENDPOINT 20 #define SAMPLEAPP_PROFID 0x0F08 #define SAMPLEAPP_DEVICEID 0x0001 #define SAMPLEAPP_DEVICE_VERSION 0 #define SAMPLEAPP_FLAGS 0 #define SAMPLEAPP_MAX_CLUSTERS 2 #define SAMPLEAPP_PERIODIC_CLUSTERID 1 #define SAMPLEAPP_FLASH_CLUSTERID 2 // Send Message Timeout #define SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT 5000 // Every 5 seconds // Application Events (OSAL) - These are bit weighted definitions. #define SAMPLEAPP_SEND_PERIODIC_MSG_EVT 0x0001 // Group ID for Flash Command #define SAMPLEAPP_FLASH_GROUP 0x0001 // Flash Command Duration - in milliseconds #define SAMPLEAPP_FLASH_DURATION 1000 /********** FUNCTIONS ***********/ /* Task Initialization for the Generic Application */ extern void SampleApp_Init( uint8 task_id ); /* Task Event Processor for the Generic Application */ extern UINT16 SampleApp_ProcessEvent( uint8 task_id, uint16 events );

Page 123: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

123

SampleApp.c /********** GLOBAL VARIABLES ***********/ // This list should be filled with Application specific Cluster IDs. const cId_t SampleApp_ClusterList[SAMPLEAPP_MAX_CLUSTERS] = { SAMPLEAPP_PERIODIC_CLUSTERID, SAMPLEAPP_FLASH_CLUSTERID }; const SimpleDescriptionFormat_t SampleApp_SimpleDesc = { SAMPLEAPP_ENDPOINT, // int Endpoint; SAMPLEAPP_PROFID, // uint16 AppProfId[2]; SAMPLEAPP_DEVICEID, // uint16 AppDeviceId[2]; SAMPLEAPP_DEVICE_VERSION, // int AppDevVer:4; SAMPLEAPP_FLAGS, // int AppFlags:4; SAMPLEAPP_MAX_CLUSTERS, // uint8 AppNumInClusters; (cId_t *)SampleApp_ClusterList, // uint8 *pAppInClusterList; SAMPLEAPP_MAX_CLUSTERS, // uint8 AppNumOutClusters; (cId_t *)SampleApp_ClusterList // uint8 *pAppOutClusterList; }; // This is the Endpoint/Interface description. It is defined here, but // filled-in in SampleApp_Init(). Another way to go would be to fill // in the structure here and make it a "const" (in code space). The // way it's defined in this sample app it is define in RAM. endPointDesc_t SampleApp_epDesc;

Page 124: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

124

/********************************************************************* * LOCAL VARIABLES */ uint8 SampleApp_TaskID; // Task ID for internal task/event processing // This variable will be received when // SampleApp_Init() is called. devStates_t SampleApp_NwkState; uint8 SampleApp_TransID; // This is the unique message ID (counter) afAddrType_t SampleApp_Periodic_DstAddr; afAddrType_t SampleApp_Flash_DstAddr; aps_Group_t SampleApp_Group; uint8 SampleAppPeriodicCounter = 0; uint8 SampleAppFlashCounter = 0; /********************************************************************* * LOCAL FUNCTIONS */ void SampleApp_HandleKeys( uint8 shift, uint8 keys ); void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pckt ); void SampleApp_SendPeriodicMessage( void ); void SampleApp_SendFlashMessage( uint16 flashTime );

Page 125: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

125

void SampleApp_Init( uint8 task_id ) { SampleApp_TaskID = task_id; SampleApp_NwkState = DEV_INIT; SampleApp_TransID = 0; #if defined ( BUILD_ALL_DEVICES ) // The "Demo" target: We are looking at a jumper (defined in SampleAppHw.c) // to be jumpered together - if they are - we will start up a coordinator. // Otherwise, the device will start as a router. if ( readCoordinatorJumper() ) zgDeviceLogicalType = ZG_DEVICETYPE_COORDINATOR; else zgDeviceLogicalType = ZG_DEVICETYPE_ROUTER; #endif // BUILD_ALL_DEVICES #if defined ( HOLD_AUTO_START ) // HOLD_AUTO_START is a compile option that will suppress ZDApp from starting // the device and wait for the application to start the device. ZDOInitDevice(0); #endif // Setup for the periodic message's destination address Broadcast to everyone SampleApp_Periodic_DstAddr.addrMode = (afAddrMode_t)AddrBroadcast; SampleApp_Periodic_DstAddr.endPoint = SAMPLEAPP_ENDPOINT; SampleApp_Periodic_DstAddr.addr.shortAddr = 0xFFFF; // Setup for the flash command's destination address - Group 1 SampleApp_Flash_DstAddr.addrMode = (afAddrMode_t)afAddrGroup; SampleApp_Flash_DstAddr.endPoint = SAMPLEAPP_ENDPOINT; SampleApp_Flash_DstAddr.addr.shortAddr = SAMPLEAPP_FLASH_GROUP;

Page 126: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

126

// Fill out the endpoint description. SampleApp_epDesc.endPoint = SAMPLEAPP_ENDPOINT; SampleApp_epDesc.task_id = &SampleApp_TaskID; SampleApp_epDesc.simpleDesc = (SimpleDescriptionFormat_t *)&SampleApp_SimpleDesc; SampleApp_epDesc.latencyReq = noLatencyReqs; // Register the endpoint description with the AF afRegister( &SampleApp_epDesc ); // Register for all key events - This app will handle all key events RegisterForKeys( SampleApp_TaskID ); // By default, all devices start out in Group 1 SampleApp_Group.ID = 0x0001; osal_memcpy( SampleApp_Group.name, "Group 1", 7 ); aps_AddGroup( SAMPLEAPP_ENDPOINT, &SampleApp_Group ); #if defined ( LCD_SUPPORTED ) HalLcdWriteString( "SampleApp", HAL_LCD_LINE_1 ); #endif }

Page 127: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

127

uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events ) { afIncomingMSGPacket_t *MSGpkt; (void)task_id; // Intentionally unreferenced parameter if ( events & SYS_EVENT_MSG ) { MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID ); while ( MSGpkt ) { switch ( MSGpkt->hdr.event ) { // Received when a key is pressed case KEY_CHANGE: SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys ); break; // Received when a messages is received (OTA) for this endpoint case AF_INCOMING_MSG_CMD: SampleApp_MessageMSGCB( MSGpkt ); break; // Received whenever the device changes state in the network case ZDO_STATE_CHANGE: SampleApp_NwkState = (devStates_t)(MSGpkt->hdr.status); if ( (SampleApp_NwkState == DEV_ZB_COORD) || (SampleApp_NwkState == DEV_ROUTER) || (SampleApp_NwkState == DEV_END_DEVICE) ) { // Start sending the periodic message in a regular interval. osal_start_timerEx( SampleApp_TaskID, SAMPLEAPP_SEND_PERIODIC_MSG_EVT, SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT ); } else { // Device is no longer in the network } break;

Page 128: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

128

default: break; } // Release the memory osal_msg_deallocate( (uint8 *)MSGpkt ); // Next - if one is available MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID ); } // return unprocessed events return (events ^ SYS_EVENT_MSG); } // Send a message out - This event is generated by a timer // (setup in SampleApp_Init()). if ( events & SAMPLEAPP_SEND_PERIODIC_MSG_EVT ) { // Send the periodic message SampleApp_SendPeriodicMessage(); // Setup to send message again in normal period (+ a little jitter) osal_start_timerEx( SampleApp_TaskID, SAMPLEAPP_SEND_PERIODIC_MSG_EVT, (SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT + (osal_rand() & 0x00FF)) ); // return unprocessed events return (events ^ SAMPLEAPP_SEND_PERIODIC_MSG_EVT); } // Discard unknown events return 0; }

Page 129: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

129

void SampleApp_HandleKeys( uint8 shift, uint8 keys ) { (void)shift; // Intentionally unreferenced parameter if ( keys & HAL_KEY_SW_1 ) { /* This key sends the Flash Command is sent to Group 1. * This device will not receive the Flash Command from this * device (even if it belongs to group 1). */ SampleApp_SendFlashMessage( SAMPLEAPP_FLASH_DURATION ); } if ( keys & HAL_KEY_SW_2 ) { /* The Flash Command is sent to Group 1. * This key toggles this device in and out of group 1. * If this device doesn't belong to group 1, this application * will not receive the Flash command sent to group 1. */ aps_Group_t *grp; grp = aps_FindGroup( SAMPLEAPP_ENDPOINT, SAMPLEAPP_FLASH_GROUP ); if ( grp ) { // Remove from the group aps_RemoveGroup( SAMPLEAPP_ENDPOINT, SAMPLEAPP_FLASH_GROUP ); } else { // Add to the flash group aps_AddGroup( SAMPLEAPP_ENDPOINT, &SampleApp_Group ); } } }

Page 130: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

130

void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt ) { uint16 flashTime; switch ( pkt->clusterId ) { case SAMPLEAPP_PERIODIC_CLUSTERID: break; case SAMPLEAPP_FLASH_CLUSTERID: flashTime = BUILD_UINT16(pkt->cmd.Data[1], pkt->cmd.Data[2] ); HalLedBlink( HAL_LED_4, 4, 50, (flashTime / 4) ); break; } }

void SampleApp_SendPeriodicMessage( void ) { if ( AF_DataRequest( &SampleApp_Periodic_DstAddr, &SampleApp_epDesc, SAMPLEAPP_PERIODIC_CLUSTERID, 1, (uint8*)&SampleAppPeriodicCounter, &SampleApp_TransID, AF_DISCV_ROUTE, AF_DEFAULT_RADIUS ) == afStatus_SUCCESS ) { } else { // Error occurred in request to send. } }

Page 131: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

131

void SampleApp_SendFlashMessage( uint16 flashTime ) { uint8 buffer[3]; buffer[0] = (uint8)(SampleAppFlashCounter++); buffer[1] = LO_UINT16( flashTime ); buffer[2] = HI_UINT16( flashTime ); if ( AF_DataRequest( &SampleApp_Flash_DstAddr, &SampleApp_epDesc, SAMPLEAPP_FLASH_CLUSTERID, 3, buffer, &SampleApp_TransID, AF_DISCV_ROUTE, AF_DEFAULT_RADIUS ) == afStatus_SUCCESS ) { } else { // Error occurred in request to send. } }

Page 132: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

解析SimpleApp

Page 133: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

133

TI Z-Stack Simple API (SAPI) 簡化版的API (適用private profile) – 更高層的應用框架

精簡過的 API 函式集與 callback events

簡化 stack startup procedure

可使用 Z-Tool 於run-time期間組態stack

SAPI提供以下服務

初始化

組態

網路與服務探索 (device, network及service discovery )

資料傳輸

zb_SystemReset zb_StartRequest

zb_ReadConfiguration zb_WriteConfiguration zb_GetDeviceInfo

zb_FindDeviceRequest zb_BindDevice zb_AllowBind zb_PermitJoiningRequest

zb_SendDataRequest zb_ReceiveDataIndication

Page 134: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

134

裝置的啟動 每個device都有一組組態參數可設定,參數有預設值(寫在程

式中),可透過PC工具或外部MCU來修改參數。

所有device中的“network-specific”組態參數要設為相同 。

每個device中的“device-specific”組態參數可為不同。

使用ZCD_NV_LOGICAL_TYPE指定一個device為Coord,而電池供電的device則要設為ED。

Coord依照ZCD_NV_CHANLIST參數以掃描通道。

Coord依照ZCD_NV_PANID參數以決定PANID。

Routers跟EDs依照 ZCD_NV_CHANLIST參數以掃描通道。

Routers跟EDs試圖尋找是否有ZCD_NV_PANID參數指定的PAN存在以加入之。

Page 135: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

135

應用的綁定 一旦binding在source device建立後,app發送data時就不須指

定目標地址。呼叫zb_SendDataRequest()時使用0xFFFE當目標地址,這會使stack在內部綁定表找出真正的目標地址。

一個binding entry可以指向多個目標地址,stack會自動複製封包並傳送到每個被綁定的目標地址去。

如果NV_RESTORE編譯選項有啟用,stack會將binding entries存到NV-ram。如此,裝置重開機後,設定仍然會存在。

組態裝置綁定的兩種機制:

若extended addr已知,可用zb_BindDevice()建binding entry

若extended addr未知,就要用類似按鈕的方式來建立。目標裝置按下按鈕後調用zb_AllowBindResponse()進入允許綁定狀態,來源裝置按下按鈕後調用zb_ BindDevice()以嘗試綁定。

Page 136: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

136

使用SAPI建立Private應用 列出app使用到的物理裝置(溫度感測器, 讀取器等),為

每個裝置指定一個16-bits的device_id。

為每個裝置規劃16-bits的command_id (cluster id)。

規劃不同裝置上command_id的方向性。

將以上資訊填入simple descriptor,並為app指定一個profile id。

在app程式中實現每個物理裝置的操作邏輯、以及綁定。

Page 137: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

137

SimpleApp 功能簡介 (I) SimpleCollector (Coord/Router) 與 SimpleSensor (ED)

SimpleSensor會將感測到的溫度與自身的電池電量資訊傳送給SimpleCollector收集。

網路建立流程: 自動組網

Sensor裝置加入網路後,會自動綁定到Collector

Sensor定期發送資訊給Collector,並要求點對點ACK

若Sensor收不到ACK,會移除到該Collector的綁定,並重新搜索網路以綁到新的Collector (可能是同一個)

Command: SENSOR_REPORT_CMD_ID (sensor端是輸出/collector端是輸入),此命令訊息夾帶2-bytes的資料,第1個byte指示感測類型(溫度或電量)、第2個byte是該感測量的數值。

Page 138: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

138

SimpleApp 功能簡介 (II) 服務發現與綁定:

SimpleSensor 加 入 網 路 後 會 嘗 試 探 索 並 將 自 己 綁 到 SimpleCollector。 若它找到一個以上的collector裝置,會自動選擇第一個回覆的collector當綁定對象。若找不到則持續找下去。

加 入 網 路 後 , SimpleCollector 要 進 Allow Bind mode 來 接 受SimpleSensor的 binding requests。此範例以按下SW1開啟Allow Bind模式(LED1亮起);按下S2則關閉 Allow Bind mode與LED1。

封包傳送與接收

成功綁定後,sensor在讀取溫度與電量後會發送REPORT 命令封包給collector (並加上點對點ACK)。若沒收到ACK,SAPI會以zb_SendDataConfirm回調通知App;sensor將移除現有綁定並在網路中重試綁定。

Collector收到sensor封包後,以serial port將資料傳給PC。

Page 139: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

139

sapi.c 重點摘錄 const pTaskEventHandlerFn tasksArr[] = { macEventLoop, nwk_event_loop, Hal_ProcessEvent, #if defined( MT_TASK ) MT_ProcessEvent, #endif APS_event_loop, ZDApp_event_loop, SAPI_ProcessEvent }; endPointDesc_t sapi_epDesc; uint8 sapi_TaskID;

UINT16 SAPI_ProcessEvent( byte task_id, UINT16 events ) { ... 略 ... if ( events & SYS_EVENT_MSG ) { while ( pMsg ) { switch ( pMsg->event ) { case ZDO_CB_MSG: SAPI_ProcessZDOMsgs( (zdoIncomingMsg_t *)pMsg ); break; case AF_DATA_CONFIRM_CMD: pDataConfirm = (afDataConfirm_t *) pMsg; SAPI_SendDataConfirm( pDataConfirm->transID, pDataConfirm->hdr.status ); break; case AF_INCOMING_MSG_CMD: pMSGpkt = (afIncomingMSGPacket_t *) pMsg; SAPI_ReceiveDataIndication( pMSGpkt->srcAddr.addr.shortAddr, pMSGpkt->clusterId, pMSGpkt->cmd.DataLength, pMSGpkt->cmd.Data); break;

回調 zb_FindDeviceConfirm()或 zb_BindConfirm()

回調 zb_SendDataConfirm()

回調 zb_ReceiveDataIndication()

Page 140: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

140

case ZDO_STATE_CHANGE: // If the device has started up, notify the application if (pMsg->status == DEV_END_DEVICE || pMsg->status == DEV_ROUTER || pMsg->status == DEV_ZB_COORD ) { SAPI_StartConfirm( ZB_SUCCESS ); } else if (pMsg->status == DEV_HOLD || pMsg->status == DEV_INIT) { SAPI_StartConfirm( ZB_INIT ); } break; case ZDO_MATCH_DESC_RSP_SENT: SAPI_AllowBindConfirm( ((ZDO_MatchDescRspSent_t *)pMsg)->nwkAddr ); break; case KEY_CHANGE: #if ( SAPI_CB_FUNC ) zb_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys ); #endif break; case SAPICB_DATA_CNF: SAPI_SendDataConfirm( (uint8)((sapi_CbackEvent_t *)pMsg)->data, ((sapi_CbackEvent_t *)pMsg)->hdr.status ); break; case SAPICB_BIND_CNF: SAPI_BindConfirm( ((sapi_CbackEvent_t *)pMsg)->data, ((sapi_CbackEvent_t *)pMsg)->hdr.status ); break;

回調 zb_StartConfirm()

回調 zb_AllowBindConfirm()

回調 zb_HandleKeys()

回調 zb_SendDataConfirm()

回調 zb_BindConfirm()

Page 141: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

141

case SAPICB_START_CNF: SAPI_StartConfirm( ((sapi_CbackEvent_t *)pMsg)->hdr.status ); break; default: // User messages should be handled by user or passed to the application if ( pMsg->event >= ZB_USER_MSG ) { } break; } // Release the memory osal_msg_deallocate( (uint8 *) pMsg ); // Next pMsg = (osal_event_hdr_t *) osal_msg_receive( task_id ); } // Return unprocessed events return (events ^ SYS_EVENT_MSG); } if ( events & ZB_ALLOW_BIND_TIMER ) { afSetMatch(sapi_epDesc.simpleDesc->EndPoint, FALSE); return (events ^ ZB_ALLOW_BIND_TIMER); } if ( events & ZB_BIND_TIMER ) { // Send bind confirm callback to application SAPI_BindConfirm( sapi_bindInProgress, ZB_TIMEOUT ); sapi_bindInProgress = 0xffff; return (events ^ ZB_BIND_TIMER); }

回調 zb_StartConfirm()

回調 zb_BindConfirm()

Page 142: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

142

if ( events & ZB_ENTRY_EVENT ) { uint8 startOptions; // Give indication to application of device startup #if ( SAPI_CB_FUNC ) zb_HandleOsalEvent( ZB_ENTRY_EVENT ); #endif // LED off cancels HOLD_AUTO_START blink set in the stack HalLedSet (HAL_LED_4, HAL_LED_MODE_OFF); zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions ); if ( startOptions & ZCD_STARTOPT_AUTO_START ) { zb_StartRequest(); } else { // blink leds and wait for external input to config and restart HalLedBlink(HAL_LED_2, 0, 50, 500); } return (events ^ ZB_ENTRY_EVENT ); } // This must be the last event to be processed if ( events & ( ZB_USER_EVENTS ) ) { // User events are passed to the application #if ( SAPI_CB_FUNC ) zb_HandleOsalEvent( events ); #endif // Do not return here, return 0 later } // Discard unknown events return 0; }

回調 zb_HandleOsalEvent()

回調 zb_HandleOsalEvent()

Page 143: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

143

SimpleApp.h /************************************************************************************************** Filename: SimpleApp.h Revised: $Date: 2009-03-18 15:56:27 -0700 (Wed, 18 Mar 2009) $ Revision: $Revision: 19453 $ Description: Sample application utilizing the Simple API. **************************************************************************************************/ #ifndef SIMPLE_APP_H #define SIMPLE_APP_H #define MY_PROFILE_ID 0x0F10 #define MY_ENDPOINT_ID 0x02 // Define devices #define DEV_ID_SWITCH 1 #define DEV_ID_CONTROLLER 2 #define DEV_ID_SENSOR 3 #define DEV_ID_COLLECTOR 4 #define DEVICE_VERSION_SWITCH 1 #define DEVICE_VERSION_CONTROLLER 1 #define DEVICE_VERSION_SENSOR 1 #define DEVICE_VERSION_COLLECTOR 1 // Define the Command ID's used in this application #define TOGGLE_LIGHT_CMD_ID 1 #define SENSOR_REPORT_CMD_ID 2 #endif // SIMPLE_APP_H

Page 144: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

144

SimpleCollector.c /************************************************************************************************** Filename: SimpleCollector.c Description: Sample application utilizing the Simple API. /****** INCLUDES *****/ ... 略 ... #include "sapi.h" #include "SimpleApp.h" /****** CONSTANTS ******/ // Application States #define APP_INIT 0 #define APP_START 1 // Application osal event identifiers #define MY_START_EVT 0x0001 // Same definitions as in SimpleSensor.c #define TEMP_REPORT 0x01 #define BATTERY_REPORT 0x02 /***** LOCAL VARIABLES ******/ static uint8 myAppState = APP_INIT; static uint8 myStartRetryDelay = 10; /***** GLOBAL VARIABLES ******/ // Inputs and Outputs for Collector device #define NUM_OUT_CMD_COLLECTOR 0 #define NUM_IN_CMD_COLLECTOR 1

// List of output and input commands for Collector device const cId_t zb_InCmdList[NUM_IN_CMD_COLLECTOR] = { SENSOR_REPORT_CMD_ID }; // Define SimpleDescriptor for Collector device const SimpleDescriptionFormat_t zb_SimpleDesc = { MY_ENDPOINT_ID, // Endpoint MY_PROFILE_ID, // Profile ID DEV_ID_COLLECTOR, // Device ID DEVICE_VERSION_COLLECTOR, // Device Version 0, // Reserved NUM_IN_CMD_COLLECTOR, // Number of Input Commands (cId_t *) zb_InCmdList, // Input Command List NUM_OUT_CMD_COLLECTOR, // Number of Output Commands (cId_t *) NULL // Output Command List };

Page 145: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

145

/****************************************************************************** * @fn zb_HandleOsalEvent * @brief The zb_HandleOsalEvent function is called by the operating * system when a task event is set */ void zb_HandleOsalEvent( uint16 event ) { } /********************************************************************* * @fn zb_HandleKeys * @brief Handles all key events for this device. void zb_HandleKeys( uint8 shift, uint8 keys ) { uint8 startOptions; uint8 logicalType; ... 略 ... if ( keys & HAL_KEY_SW_1 ) { if ( myAppState == APP_INIT ) { // In the init state, keys are used to indicate the logical mode. // Key 1 starts device as a coordinator zb_ReadConfiguration( ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType ); if ( logicalType != ZG_DEVICETYPE_ENDDEVICE ) { logicalType = ZG_DEVICETYPE_COORDINATOR; zb_WriteConfiguration(ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType); } // Do more configuration if necessary and then restart device with auto-start bit set // write endpoint to simple desc...dont pass it in start req..then reset

見sapi.c

Page 146: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

146

zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions ); startOptions = ZCD_STARTOPT_AUTO_START; zb_WriteConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions ); zb_SystemReset(); } else { // Turn ON Allow Bind mode indefinitely zb_AllowBind( 0xFF ); HalLedSet( HAL_LED_1, HAL_LED_MODE_ON ); } } if ( keys & HAL_KEY_SW_2 ) { if ( myAppState == APP_INIT ) { // In the init state, keys are used to indicate the logical mode. // Key 2 starts device as a router zb_ReadConfiguration( ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType ); if ( logicalType != ZG_DEVICETYPE_ENDDEVICE ) { logicalType = ZG_DEVICETYPE_ROUTER; zb_WriteConfiguration(ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType); } zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions ); startOptions = ZCD_STARTOPT_AUTO_START; zb_WriteConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions ); zb_SystemReset(); } else { // Turn OFF Allow Bind mode indefinitely zb_AllowBind( 0x00 ); HalLedSet( HAL_LED_1, HAL_LED_MODE_OFF ); } } ... 略 ... } }

Page 147: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

147

/****************************************************************************** * @fn zb_StartConfirm * @brief The zb_StartConfirm callback is called by the ZigBee stack * after a start request operation completes void zb_StartConfirm( uint8 status ) { // If the device sucessfully started, change state to running if ( status == ZB_SUCCESS ) { myAppState = APP_START; } else { // Try again later with a delay osal_start_timerEx( sapi_TaskID, MY_START_EVT, myStartRetryDelay ); } }

/********************************************************** * @fn zb_SendDataConfirm * @brief The zb_SendDataConfirm callback function * is called by the ZigBee after a send data * operation completes void zb_SendDataConfirm( uint8 handle, uint8 status ) { }

/********************************************************** * @fn zb_BindConfirm * @brief The zb_BindConfirm callback is called * by the ZigBee stack after a bind operation * completes. void zb_BindConfirm( uint16 commandId, uint8 status ) { }

/**************************************** * @fn zb_AllowBindConfirm * @brief Indicates when another * device attempted to bind * to this device void zb_AllowBindConfirm( uint16 source ) { }

/****************************************** * @fn zb_FindDeviceConfirm * @brief The zb_FindDeviceConfirm * callback function is called * by the stack when a find device * operation completes. void zb_FindDeviceConfirm( uint8 searchType, uint8 *searchKey, uint8 *result ) { }

Page 148: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

148

/****************************************************************************** * @fn zb_ReceiveDataIndication * @brief The zb_ReceiveDataIndication callback function is called * asynchronously by the ZigBee stack to notify the application * when data is received from a peer device. * * @param source - The short address of the peer device that sent the data * command - The commandId associated with the data * len - The number of bytes in the pData parameter * pData - The data sent by the peer device CONST uint8 strDevice[] = "Device:0x"; CONST uint8 strTemp[] = "Temp: "; CONST uint8 strBattery[] = "Battery: "; void zb_ReceiveDataIndication( uint16 source, uint16 command, uint16 len, uint8 *pData ) { uint8 buf[32]; uint8 *pBuf; uint8 tmpLen; uint8 sensorReading; if (command == SENSOR_REPORT_CMD_ID) { // Received report from a sensor sensorReading = pData[1]; // If tool available, write to serial port tmpLen = (uint8)osal_strlen( (char*)strDevice ); pBuf = osal_memcpy( buf, strDevice, tmpLen ); _ltoa( source, pBuf, 16 ); pBuf += 4; *pBuf++ = ' ';

Page 149: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

149

if ( pData[0] == BATTERY_REPORT ) { tmpLen = (uint8)osal_strlen( (char*)strBattery ); pBuf = osal_memcpy( pBuf, strBattery, tmpLen ); *pBuf++ = (sensorReading / 10 ) + '0'; // convent msb to ascii *pBuf++ = '.'; // decimal point ( batt reading is in units of 0.1 V) *pBuf++ = (sensorReading % 10 ) + '0'; // convert lsb to ascii *pBuf++ = ' '; *pBuf++ = 'V'; } else { tmpLen = (uint8)osal_strlen( (char*)strTemp ); pBuf = osal_memcpy( pBuf, strTemp, tmpLen ); *pBuf++ = (sensorReading / 10 ) + '0'; // convent msb to ascii *pBuf++ = (sensorReading % 10 ) + '0'; // convert lsb to ascii *pBuf++ = ' '; *pBuf++ = 'C'; } *pBuf++ = '\r'; *pBuf++ = '\n'; *pBuf = '\0'; #if defined( MT_TASK ) debug_str( (uint8 *)buf ); #endif // can also write directly to uart } }

Page 150: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

150

SimpleSensor.c /*********************************************************************************** Filename: SimpleSensor.c Description: Sample application for a simple sensor utilizing the Simple API. /**** INCLUDES ****/ ...略... #include "sapi.h" #include "hal_adc.h" #include "hal_mcu.h" #include "SimpleApp.h" /**** CONSTANTS ****/ // Application States #define APP_INIT 0 // Initial state #define APP_START 1 // Sensor has joined network #define APP_BOUND 2 // Sensor is bound to collector // Application osal event identifiers, Bit mask of events ( from 0x0000 to 0x00FF ) #define MY_START_EVT 0x0001 #define MY_REPORT_TEMP_EVT 0x0002 #define MY_REPORT_BATT_EVT 0x0004 #define MY_FIND_COLLECTOR_EVT 0x0008 // ADC definitions for CC2430/CC2530 from the hal_adc.c file #if defined (HAL_MCU_CC2430) || defined (HAL_MCU_CC2530) #define HAL_ADC_REF_125V 0x00 /* Internal 1.25V Reference */ #define HAL_ADC_DEC_064 0x00 /* Decimate by 64 : 8-bit resolution */ #define HAL_ADC_DEC_128 0x10 /* Decimate by 128 : 10-bit resolution */ #define HAL_ADC_DEC_512 0x30 /* Decimate by 512 : 14-bit resolution */ #define HAL_ADC_CHN_VDD3 0x0f /* Input channel: VDD/3 */ #define HAL_ADC_CHN_TEMP 0x0e /* Temperature sensor */ #endif //HAL_MCU_CC2430 || HAL_MCU_CC2530

Page 151: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

151

/**** LOCAL VARIABLES ****/ static uint8 myAppState = APP_INIT; static uint16 myStartRetryDelay = 10000; // milliseconds static uint16 myTempReportPeriod = 5000; // milliseconds static uint16 myBatteryCheckPeriod = 21000; // milliseconds static uint16 myBindRetryDelay = 10000; // milliseconds /***** GLOBAL VARIABLES *****/ // Inputs and Outputs for Sensor device #define NUM_OUT_CMD_SENSOR 1 #define NUM_IN_CMD_SENSOR 0 // List of output and input commands for Sensor device const cId_t zb_OutCmdList[NUM_OUT_CMD_SENSOR] = { SENSOR_REPORT_CMD_ID }; #define TEMP_REPORT 0x01 #define BATTERY_REPORT 0x02 // Define SimpleDescriptor for Sensor device const SimpleDescriptionFormat_t zb_SimpleDesc = { MY_ENDPOINT_ID, // Endpoint MY_PROFILE_ID, // Profile ID DEV_ID_SENSOR, // Device ID DEVICE_VERSION_SENSOR, // Device Version 0, // Reserved NUM_IN_CMD_SENSOR, // Number of Input Commands (cId_t *) NULL, // Input Command List NUM_OUT_CMD_SENSOR, // Number of Output Commands (cId_t *) zb_OutCmdList // Output Command List };

/**** LOCAL FUNCTIONS ****/ static void myApp_StartReporting( void ); static void myApp_StopReporting( void ); static uint8 myApp_ReadTemperature( void ); static uint8 myApp_ReadBattery( void );

Page 152: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

152

/***************************************************************************** * @fn zb_HandleOsalEvent * @brief The zb_HandleOsalEvent function is called by the operating * system when a task event is set void zb_HandleOsalEvent( uint16 event ) { uint8 pData[2]; if ( event & MY_START_EVT ) { zb_StartRequest(); } if ( event & MY_REPORT_TEMP_EVT ) { // Read and report temperature value pData[0] = TEMP_REPORT; pData[1] = myApp_ReadTemperature(); zb_SendDataRequest( 0xFFFE, SENSOR_REPORT_CMD_ID, 2, pData, 0, AF_ACK_REQUEST, 0 ); osal_start_timerEx( sapi_TaskID, MY_REPORT_TEMP_EVT, myTempReportPeriod ); } if ( event & MY_REPORT_BATT_EVT ) { // Read battery value // If battery level low, report battery value pData[0] = BATTERY_REPORT; pData[1] = myApp_ReadBattery(); zb_SendDataRequest( 0xFFFE, SENSOR_REPORT_CMD_ID, 2, pData, 0, AF_ACK_REQUEST, 0 ); osal_start_timerEx( sapi_TaskID, MY_REPORT_BATT_EVT, myBatteryCheckPeriod ); } if ( event & MY_FIND_COLLECTOR_EVT ) { // Find and bind to a collector device zb_BindDevice( TRUE, SENSOR_REPORT_CMD_ID, (uint8 *)NULL ); } }

Page 153: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

153

/********************************************************************* * @fn zb_HandleKeys * @brief Handles all key events for this device. void zb_HandleKeys( uint8 shift, uint8 keys ) { uint8 startOptions; uint8 logicalType; // Shift is used to make each button/switch dual purpose. if ( shift ) { ... 略 ... } else { if ( keys & HAL_KEY_SW_1 ) { if ( myAppState == APP_INIT ) { logicalType = ZG_DEVICETYPE_ENDDEVICE; zb_WriteConfiguration(ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType); zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions ); startOptions = ZCD_STARTOPT_AUTO_START; zb_WriteConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions ); zb_SystemReset(); } } if ( keys & HAL_KEY_SW_2 ) { if ( myAppState == APP_INIT ) { logicalType = ZG_DEVICETYPE_ENDDEVICE; zb_WriteConfiguration(ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType); zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions ); startOptions = ZCD_STARTOPT_AUTO_START; zb_WriteConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions ); zb_SystemReset(); } } ... 略 ... } }

Page 154: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

154

/****************************************************************************** * @fn zb_StartConfirm * @brief The zb_StartConfirm callback is called by the ZigBee stack * after a start request operation completes void zb_StartConfirm( uint8 status ) { if ( status == ZB_SUCCESS ) { myAppState = APP_START; // Set event to bind to a collector osal_start_timerEx( sapi_TaskID, MY_FIND_COLLECTOR_EVT, myBindRetryDelay ); } else { // Try joining again later with a delay osal_start_timerEx( sapi_TaskID, MY_START_EVT, myStartRetryDelay ); } } /******************************************************************************

* @fn zb_SendDataConfirm * @brief The zb_SendDataConfirm callback function is called by the * ZigBee after a send data operation completes void zb_SendDataConfirm( uint8 handle, uint8 status ) { (void)handle; // Intentionally unreferenced parameter if ( status != ZSuccess ) { // Remove bindings to the existing collector zb_BindDevice( FALSE, SENSOR_REPORT_CMD_ID, (uint8 *)NULL ); myAppState = APP_START; myApp_StopReporting(); // Start process of finding new collector with minimal delay osal_start_timerEx( sapi_TaskID, MY_FIND_COLLECTOR_EVT, 1 ); } else { // send data ?? } }

Page 155: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

155

/****************************************************************************** * @fn zb_BindConfirm * @brief The zb_BindConfirm callback is called by the ZigBee stack * after a bind operation completes. void zb_BindConfirm( uint16 commandId, uint8 status ) { (void)commandId; if ( ( status == ZB_SUCCESS ) && ( myAppState == APP_START ) ) { myAppState = APP_BOUND; //Start reporting sensor values myApp_StartReporting(); } else { // Continue to discover a collector osal_start_timerEx( sapi_TaskID, MY_FIND_COLLECTOR_EVT, myBindRetryDelay ); } } /******************************************************************************

* @fn zb_AllowBindConfirm * @brief Indicates when another device attempted to bind to this device void zb_AllowBindConfirm( uint16 source ) { (void)source; }

/****************************************************************************** * @fn zb_FindDeviceConfirm * @brief The zb_FindDeviceConfirm callback function is called by the * ZigBee stack when a find device operation completes. void zb_FindDeviceConfirm( uint8 searchType, uint8 *searchKey, uint8 *result ) { // Add your code here and remove the "(void)" lines. (void)searchType; (void)searchKey; (void)result; }

Page 156: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

156

/****************************************************************************** * @fn zb_ReceiveDataIndication * @brief The zb_ReceiveDataIndication callback function is called * asynchronously by the ZigBee stack to notify the application * when data is received from a peer device. void zb_ReceiveDataIndication( uint16 source, uint16 command, uint16 len, uint8 *pData ) { // Add your code here and remove the "(void)" lines. (void)source; (void)command; (void)len; (void)pData; }

/****************************************************************************** * @fn my_StartReporting * @brief Starts the process to periodically report sensor readings void myApp_StartReporting( void ) { osal_start_timerEx( sapi_TaskID, MY_REPORT_TEMP_EVT, myTempReportPeriod ); osal_start_timerEx( sapi_TaskID, MY_REPORT_BATT_EVT, myBatteryCheckPeriod ); HalLedSet( HAL_LED_1, HAL_LED_MODE_ON ); }

/****************************************************************************** * @fn my_StopReporting * @brief Stops the process to periodically report sensor readings void myApp_StopReporting( void ) { osal_stop_timerEx( sapi_TaskID, MY_REPORT_TEMP_EVT ); osal_stop_timerEx( sapi_TaskID, MY_REPORT_BATT_EVT ); HalLedSet( HAL_LED_1, HAL_LED_MODE_OFF ); }

Page 157: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

157

/****************************************************************************** * @fn myApp_ReadBattery * @brief Reports battery sensor reading uint8 myApp_ReadBattery( void ) { #if defined (HAL_MCU_CC2430) || defined (HAL_MCU_CC2530) uint16 value; /* Clear ADC interrupt flag */ ADCIF = 0; ADCCON3 = (HAL_ADC_REF_125V | HAL_ADC_DEC_128 | HAL_ADC_CHN_VDD3); /* Wait for the conversion to finish */ while ( !ADCIF ); /* Get the result */ value = ADCL; value |= ((uint16) ADCH) << 8; /* value now contains measurement of Vdd/3 * 0 indicates 0V and 32767 indicates 1.25V * voltage = (value*3*1.25)/32767 volts * we will multiply by this by 10 to allow units of 0.1 volts */ value = value >> 6; // divide first by 2^6 value = (uint16)(value * 37.5); value = value >> 9; // ...and later by 2^9...to prevent overflow during multiplication return value; #endif // CC2430 or CC2530 }

Page 158: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

158

/****************************************************************************** * @fn myApp_ReadTemperature * @brief Reports temperature sensor reading uint8 myApp_ReadTemperature( void ) { #if defined (HAL_MCU_CC2430) || defined (HAL_MCU_CC2530) uint16 value; /* Clear ADC interrupt flag */ ADCIF = 0; ADCCON3 = (HAL_ADC_REF_125V | HAL_ADC_DEC_512 | HAL_ADC_CHN_TEMP); /* Wait for the conversion to finish */ while ( !ADCIF ); /* Get the result */ value = ADCL; value |= ((uint16) ADCH) << 8; /* value ranges from 0 to 0x8000 indicating 0V and 1.25V * VOLTAGE_AT_TEMP_ZERO = 0.743 V = 19477 * TEMP_COEFFICIENT = 0.0024 V/C = 62.9 /C * These parameters are typical values and need to be calibrated * See the datasheet for the appropriate chip for more details * also, the math below may not be very accurate */ #if defined (HAL_MCU_CC2430) #define VOLTAGE_AT_TEMP_ZERO 19477 // 0.743 V #define TEMP_COEFFICIENT 62.9 // 0.0024 V/C #elif defined (HAL_MCU_CC2530) /* Assume ADC = 5158 at 0C and ADC = 15/C */ #define VOLTAGE_AT_TEMP_ZERO 5158 #define TEMP_COEFFICIENT 14 #endif

// limit min temp to 0 C if ( value < VOLTAGE_AT_TEMP_ZERO ) value = VOLTAGE_AT_TEMP_ZERO; value = value - VOLTAGE_AT_TEMP_ZERO; // limit max temp to 99 C if ( value > TEMP_COEFFICIENT * 99 ) value = TEMP_COEFFICIENT * 99; return ( (uint8)(value/TEMP_COEFFICIENT) ); #endif // CC2430 || CC2530 }

Page 159: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

ZigBee Cluster Library

Page 160: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

160

ZigBee Cluster Library (ZCL) ZCL是給public profiles共用的一套指令clusters或cross-

cluster,ZigBee聯盟希望用它來加速廠商開發與維持裝置間的通用性。

Page 161: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

161

使用Cluster以擴充裝置功能 如果你要做的是 public profile device,那你就需要ZCL。

如果是private application profiles,ZCL就並非必需。有很多private application profiles並沒用ZCL或只用到library中的一些東西而已。

Page 162: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

162

Cluster的分類範疇 Public profiles將使用ZCL的使用分為三個範疇:

Clusters which are mandatory for all devices within the profile

Clusters which are mandatory for a particular device within the profile

Clusters which are optional for a particular device within the profile

ZCL也允許製造商延伸devices的功能,製造商可以為他們的產品增加features。

以電燈為例,最簡單的情形下,它只要支援turn on或off即可。不過,或許我們可以為它增加感測環境光線與自動調光的功能。兩種情況都同樣需要 On/Off Cluster (0x0006),但是可以調光的版本又需要Illuminance Level Sensing Cluster (0x0401)。

Page 163: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

163

Cluster到底是何方神聖?

// General Clusters #define ZCL_CLUSTER_ID_GEN_BASIC 0x0000 #define ZCL_CLUSTER_ID_GEN_POWER_CFG 0x0001 #define ZCL_CLUSTER_ID_GEN_DEVICE_TEMP_CONFIG 0x0002 #define ZCL_CLUSTER_ID_GEN_IDENTIFY 0x0003 #define ZCL_CLUSTER_ID_GEN_GROUPS 0x0004 #define ZCL_CLUSTER_ID_GEN_SCENES 0x0005 #define ZCL_CLUSTER_ID_GEN_ON_OFF 0x0006 #define ZCL_CLUSTER_ID_GEN_ON_OFF_SWITCH_CONFIG 0x0007 #define ZCL_CLUSTER_ID_GEN_LEVEL_CONTROL 0x0008 #define ZCL_CLUSTER_ID_GEN_ALARMS 0x0009 #define ZCL_CLUSTER_ID_GEN_TIME 0x000A #define ZCL_CLUSTER_ID_GEN_LOCATION 0x000B #define ZCL_CLUSTER_ID_GEN_ANALOG_INPUT_BASIC 0x000C #define ZCL_CLUSTER_ID_GEN_ANALOG_OUTPUT_BASIC 0x000D #define ZCL_CLUSTER_ID_GEN_ANALOG_VALUE_BASIC 0x000E #define ZCL_CLUSTER_ID_GEN_BINARY_INPUT_BASIC 0x000F #define ZCL_CLUSTER_ID_GEN_BINARY_OUTPUT_BASIC 0x0010 #define ZCL_CLUSTER_ID_GEN_BINARY_VALUE_BASIC 0x0011 #define ZCL_CLUSTER_ID_GEN_MULTISTATE_INPUT_BASIC 0x0012 #define ZCL_CLUSTER_ID_GEN_MULTISTATE_OUTPUT_BASIC 0x0013 #define ZCL_CLUSTER_ID_GEN_MULTISTATE_VALUE_BASIC 0x0014 #define ZCL_CLUSTER_ID_GEN_COMMISSIONING 0x0015 #define ZCL_CLUSTER_ID_OTA 0x0019 #define ZCL_CLUSTER_ID_GREEN_POWER_PROXY 0x001A

Page 164: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

164

那Cluster是如何發揮作的? 站 在 endpoint 的 simple descriptor 觀 點 , client 端 列 出

cluster ID且為output cluster;server端也列出一樣的cluster ID,不過是作為input cluster。

ZCL詳細描述了每個cluster,所以不同的vendors都可據此創建出相容的產品。

ZCL_CLUSTER_ID_GEN_ON_OFF

ZCL_CLUSTER_ID_GEN_LEVEL_CONTROL

ZCL_CLUSTER_ID_GEN_ON_OFF_SWITCH_CONFIG

Page 165: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

165

如何使用Cluster? ZCL Foundation: ZCL為ZigBee規範引出了attributes(屬性)

與commands(命令/指令)的觀念。

Attributes是cluster內定義的data items或states。

Commands是cluster要執行的actions。

舉例來說,HA On/Off Light使用On/Off Cluster(cluster ID 0x0006),On/Off Cluster的屬性會指示light是on (0x01)還是 off (0x00)。指令會將light轉on (0x01)、off (0x00)或toggle (0x02),指令執行動作會影響OnOff屬性的狀態。

我們可以用crosscluster ZCL指令以read、write以及OTA report屬性。這些cross-cluster的指令稱為ZCL foundation。

Page 166: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

166

ZCL Crosscluster Commands (Foundation)

Cross-cluster指令對任何ZCL的cluster都有用。

例如「read attributes」指令可以讀取On/Off Cluster (cluster ID 0x0006)與Level Control Cluster (cluster ID 0x0008)屬性。

此機制非常強大,使得配備ZigBee Donlgle的PC只要安裝第三方工具軟體,即可檢視整個網路的狀態。

注意,只有支援ZCL的endpoints才支援ZCL foundation指令。

Page 167: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

167

Push 與 Pull Method ZCL 使 用 “push”與 “pull”方法 (report 與 read attribute

methods)來找出任何cluster屬性的狀態。

Push:當屬性變化時,裝置自動發送reports。Report可設為time-based、change-in-value-based或both。

例如一個on/off light可以設定為每次狀態改變(on/off)時就report。一個溫度感測器可以設定為每分鐘report一次,或者溫度改變超過5度時report(看何者先發生)。

Pull:需要資訊的裝置會去詢問其他裝置的現狀。

例如某人拿起電視遙控器時,此時詢問屋外的溫度感測器的溫度再傳回遙控器上或電視螢幕。

Page 168: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

168

屬性的讀寫 一般而言,表示實際物件狀態的屬性(例如一顆燈的

on/off)、或者感測真實世界的屬性(溫度感測器的溫度值)是不可以直接寫入的。

為了讓代表狀態屬性發生作用,必須要使用指令才行,例如用OnOff指令來toggle一個電燈。

可以直接寫入ZCL屬性的東西是像「文字說明」的東西,例如寫入”Kitchen”來表明節點的位置是在廚房。

ZCL規範清楚指出了那些cluster的屬性是可 read、可write或report。例如OnOff Cluster的OnOff是可read與report,但沒辦法write進去。

Page 169: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

169

ZCL General Clusters ZCL裡面有一組general clusters,因為這些cluster會普遍

地存在於每個ZigBee public application profile。

存在於 every device。

用來做網路commission,存在於 most devices。

存在於 some device。

Page 170: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

170

Attribute Sets (I) 在ZCL規範裡,你會看到白皮書裡會列出每個cluster的

attribute sets。這可能會引起混淆,問題在於甚麼是「attribute sets」,它們跟「attribute IDs」有何不同?

Attribute ID是一個16-bit的數字(從0x0000到0xffff),它定義一個cluster裡面每個attribute,而且attributes Ids在ZCL cluster總是從0x0000開始編起。Attribute IDs只有在那個cluster裡面才具有意義。

Attribute sets是attribute IDs在ZCL document中的組織方式。OTA傳輸時只有attribute ID的觀念(而非sets),ZigBee stacks與attributes互動就是使用16-bit attribute ID。我們可以把attribute sets看作是16-bit attribute ID的top 12-bits。

Page 171: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

171

Attribute Sets (II)

Attribute sets 是 組 織 過 的 ,attribute IDs 0x0000到0x000f是 屬 於 device’s information (read only),而attribute ID的 0x0010 到 0x001f是可設定的參數(可read/write) 。

Basic Cluster (cluster ID 0x0001)在每個支援ZCL的裝置上一定都有,這個cluster內含一組屬性用於定義裝置的普通資訊與 設 定 , 如 ZCL version 、hardware與software version、manufacturer ID及location等。

Page 172: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

172

Basic Cluster Basic cluster 內 的 屬 性 並 非 都 是 必 要 的 , 有 些 是

optional(像manufacturer name或model identifier)。不過,如果你的application使用了ZCL,建議產品要包含所有欄位,因為這可以讓別人透過OTA的方式來查詢裝置資訊。

Basic cluster雖然有一堆屬性,不過它卻只有一個指令:「Reset to Factory Defaults」。這個指令是optional的,不過你的產品若是支援ZCL,那你就應該要支援這個指令。如此一來,當網路或裝置發生問題的時候,使用者可以用透過無線的方式reset裝置以解決問題。

Basic cluster的reset指令不會reset ZigBee settings,像節點是連到哪個網路、屬於哪些groups、或本地bindings都不會消失。它只會reset cluster的attributes到出廠預設值 。 如 果 要 真 的 reset 到 純 出 廠 狀 態 , 就 要 透 過commissioning cluster 來完成。

Page 173: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

173

ZCL API ZCL clusters 的 attributes 表

示資料、commands表示執行 動 作 。 不 要 嘗 試 寫 入on/off attribute來打開或關閉電燈,而是要使用on、off或toggle command來做,如此attribute會隨著動作做相應變化。

在TI的平台上,要讀寫ZCL cluster 的 attributes 與 發 送指令,見TI Z-Stack ZCL API第3章。

Department of Electronic Engineering, NTUT

Page 174: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

174

The Identify Cluster的屬性 ZCL General Clusters有兩個很特別的:Identify跟Groups。

這兩個clusters在commissioning網路的時候非常有用。

Identify cluster只有一個屬性:Identify Time,它定義device處在identify mode的時間(以秒計時)。當裝置進入Identify Mode的時候,通常會用閃燈或聲音來讓使用者知道。

想像一下家裡有很多電燈,它們都安裝在天花板上。使用者如果想要將一個switch跟數個電燈做連結,其中一個方法就是讓依序詢問每個電燈,讓他們進入Identify Mode,如果那顆燈是你想要的,我們就可以設計在使用者端按下某個按鈕來表示”Yes”。如此一來整個系統的安裝就會方便許多。

Department of Electronic Engineering, NTUT

Page 175: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

175

The Identify Cluster的指令

Identify cluster有兩個指令:Identify與Identify Query

Identify指令與寫入屬性一樣,不過這是少數寫入屬性時會有立即效果的。但仍然建議使用指令,而不是直接寫入屬性。該欄位設為0x0000會將identify關掉,或是設為0x0001–0xffff(數字也表示秒數)將identity打開。

建議:不要讓裝置進入Identify mode超過20~30秒,因為Identify 的狀態會干擾大部分的其他裝置運作。

Identify Query指令只有當裝置處於Identify Mode時才有效。這指令正常是broadcast到整個網路的。

Department of Electronic Engineering, NTUT

若 使 用 者 同 時 將 10 個 lights 轉 進 Identify Mode, 此時只有一個OTA指令(identify query),所有電燈都會做出respond,然後switch就可以一次綁到多個電燈。最後,再將所有電燈退出Identify Mode。

Page 176: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

176

The Groups Cluster的屬性 這個cluster實在太有用了,所以應該要把它放入ZigBee

Device Profile 。 在 ZigBee 規 範 中 , Group support 為optional,但是它在很多public profiles的裝置是必要的。

Groups可以讓我們只用一個OTA指令就為很多節點與endpoints定址。如果endpoint是那個group的一員,它就會處理收到的指令。

Groups cluster只有一個屬性:NameSupport (read-only)。

如果這個節點支援UTF-8 text的group names,它可以允許其他節點查詢。實際上, 如果沒有必要,group naming 的功能最好關掉。只要使用16-bit Group IDs就好,名稱判斷做在PC或PDA軟體中就好。

Department of Electronic Engineering, NTUT

Page 177: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

實驗: Home Automation Profile 複雜性很高,不過也只是框架運用的觀念

Page 178: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

178

使用ZCL之應用程式檔案規劃

Profile zcl_ha.h

zcl_ha.c

zcl.c (如果你要使用zcl,你將會間接用到這支應用程式)

AppName OSAL_<appname>.c: 初始化tasks

zcl_<appname>_data.c : 資料結構定義, 包含App支援的cluster屬性

zcl_<appname>.h: App endpoint在此定義

zcl_<appname>.c: 應用程式主要邏輯與callbacks的實現

Page 179: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

179

使用TI ZCL 要 使 用 ZCL , 開 發 者 所 建 的 profile 必 須 與 相 關 的 ZCL

cluster 功 能 相 互 配 合 。 你 會 需 要 Foundation layer 跟General functional domain cluster的功能。

Foundation層提供APIs給上層調用:

用於產生Request與Response的命令

註冊App的屬性清單(attribute list)

註冊App用於「屬性資料驗證的回調函式 」

註冊Cluster Library Handler回調函式

註冊App task,用以接收未處理的Foundation command/response訊息

General與Protocol Interfaces:

用於產生Request與Response的命令

註冊App的命令回調函式

Page 180: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

180

Client/Server Model ZCL使用Client/Server model。Cluster是命令(commands)

與屬性(attributes)的集合,它們定義出使用某種功能的介面。通常,Server端是指儲存cluster屬性的一端,而試圖操作那些屬性的一端稱為Client。然而,若有需要,屬性也會存在於Client。

例如,Client發出讀寫屬性的命令以讓Server裝置操作對應的屬性。相應於命令的任何response會由Server發出,而由Client接收。

Report屬性命令讓屬性動態報告更簡單,這由Server device發送給綁定的Client device。

Page 181: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

181

ZCL的角色

zcl_registerAttrList() zcl_registerValidateAttrData() zcl_registerForMsg() zcl_registerPlugin()

zcl_<appname>_data.c

zcl_<appname>.c

appname_TaskID

zcl_TaskID

Page 182: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

182

註冊函式 ZStatus_t zcl_registerAttrList( uint8 endpoint, uint8 numAttr, zclAttrRec_t *newAttrList );

typedef struct { uint16 attrId; // Attribute ID uint8 dataType; // Data Type - defined in AF.h uint8 accessControl; // Read/write - bit field void *dataPtr; // Pointer to data field } zclAttribute_t;

typedef struct { uint16 clusterID; // Real cluster ID zclAttribute_t attr; } zclAttrRec_t;

ZStatus_t zcl_registerValidateAttrData( zclValidateAttrData_t pfnValidateAttrData );

typedef uint8 (*zclValidateAttrData_t)( zclAttrRec_t *pAttr, zclWriteRec_t *pAttrInfo );

// Write Attribute record typedef struct { uint16 attrID; // attribute ID uint8 dataType; // attribute data type uint8 *attrData; // this structure is allocated, so the data is HERE // - the size depends on the attribute data type } zclWriteRec_t;

Page 183: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

183

ZStatus_t zclGeneral_RegisterCmdCallbacks( uint8 endpoint, zclGeneral_AppCallbacks_t *callbacks );

// Register Callbacks table entry - enter function pointers for callbacks that // the application would like to receive typedef struct { zclGCB_BasicReset_t pfnBasicReset; // Basic Cluster Reset command zclGCB_Identify_t pfnIdentify; // Identify command zclGCB_IdentifyQueryRsp_t pfnIdentifyQueryRsp; // Identify Query Response command zclGCB_OnOff_t pfnOnOff; // On/Off cluster commands zclGCB_LevelControlMoveToLevel_t pfnLevelControlMoveToLevel; // Level Control Move to Level command zclGCB_LevelControlMove_t pfnLevelControlMove; // Level Control Move command zclGCB_LevelControlStep_t pfnLevelControlStep; // Level Control Step command zclGCB_LevelControlStop_t pfnLevelControlStop; // Level Control Stop command zclGCB_GroupRsp_t pfnGroupRsp; // Group Response commands zclGCB_SceneStoreReq_t pfnSceneStoreReq; // Scene Store Request command zclGCB_SceneRecallReq_t pfnSceneRecallReq; // Scene Recall Request command zclGCB_SceneRsp_t pfnSceneRsp; // Scene Response command zclGCB_Alarm_t pfnAlarm; // Alarm (Response) commands #ifdef SE_UK_EXT zclGCB_GetEventLog_t pfnGetEventLog; // Get Event Log command zclGCB_PublishEventLog_t pfnPublishEventLog; // Publish Event Log command #endif zclGCB_Location_t pfnLocation; // RSSI Location command zclGCB_LocationRsp_t pfnLocationRsp; // RSSI Location Response command } zclGeneral_AppCallbacks_t;

Page 184: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

184

Foundation層 Foundation層提供可以操作屬性與普通task(不屬於特定

cluster)的通用命令,這些命令有

Read attributes

Read attributes response

Write attributes

Write attributes undivided

Write attributes response

Write attributes no response

Configure reporting

Configure reporting response

Read reporting configuration

Read reporting configuration response

Report attributes

Default response

Discover attributes

Discover attributes response

Page 185: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

185

SampleLight and SampleSwitch w/ HA

Profile zcl_ha.h

zcl_ha.c

zcl.c (如果你要使用zcl,你將會間接用到這支應用程式)

SampleLight OSAL_SampleLight.c

zcl_samplelight_data.c

zcl_samplelight.h

zcl_samplelight.c

SampleSwitch OSAL_SampleSw.c

zcl_samplesw_data.c

zcl_samplesw.h

zcl_samplesw.c

Page 186: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

186

zcl_ha.h /********************************************************************* * CONSTANTS */ // Zigbee Home Automation Profile Identification #define ZCL_HA_PROFILE_ID 0x0104 // Generic Device IDs #define ZCL_HA_DEVICEID_ON_OFF_SWITCH 0x0000 #define ZCL_HA_DEVICEID_LEVEL_CONTROL_SWITCH 0x0001 #define ZCL_HA_DEVICEID_ON_OFF_OUTPUT 0x0002 #define ZCL_HA_DEVICEID_LEVEL_CONTROLLABLE_OUTPUT 0x0003 #define ZCL_HA_DEVICEID_SCENE_SELECTOR 0x0004 #define ZCL_HA_DEVICEID_CONFIGURATIOPN_TOOL 0x0005 #define ZCL_HA_DEVICEID_REMOTE_CONTROL 0x0006 #define ZCL_HA_DEVICEID_COMBINED_INETRFACE 0x0007 #define ZCL_HA_DEVICEID_RANGE_EXTENDER 0x0008 #define ZCL_HA_DEVICEID_MAINS_POWER_OUTLET 0x0009 // temp: nnl #define ZCL_HA_DEVICEID_TEST_DEVICE 0x00FF // Lighting Device IDs #define ZCL_HA_DEVICEID_ON_OFF_LIGHT 0x0100 #define ZCL_HA_DEVICEID_DIMMABLE_LIGHT 0x0101 #define ZCL_HA_DEVICEID_COLORED_DIMMABLE_LIGHT 0x0102 #define ZCL_HA_DEVICEID_ON_OFF_LIGHT_SWITCH 0x0103 #define ZCL_HA_DEVICEID_DIMMER_SWITCH 0x0104 #define ZCL_HA_DEVICEID_COLOR_DIMMER_SWITCH 0x0105 #define ZCL_HA_DEVICEID_LIGHT_SENSOR 0x0106 #define ZCL_HA_DEVICEID_OCCUPANCY_SENSOR 0x0107

Page 187: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

187

// Closures Device IDs #define ZCL_HA_DEVICEID_SHADE 0x0200 #define ZCL_HA_DEVICEID_SHADE_CONTROLLER 0x0201 // HVAC Device IDs #define ZCL_HA_DEVICEID_HEATING_COOLING_UNIT 0x0300 #define ZCL_HA_DEVICEID_THERMOSTAT 0x0301 #define ZCL_HA_DEVICEID_TEMPERATURE_SENSOR 0x0302 #define ZCL_HA_DEVICEID_PUMP 0x0303 #define ZCL_HA_DEVICEID_PUMP_CONTROLLER 0x0304 #define ZCL_HA_DEVICEID_PRESSURE_SENSOR 0x0305 #define ZCL_HA_DEVICEID_FLOW_SENSOR 0x0306 // Intruder Alarm Systems (IAS) Device IDs #define ZCL_HA_DEVICEID_IAS_CONTROL_INDICATING_EQUIPMENT 0x0400 #define ZCL_HA_DEVICEID_IAS_ANCILLARY_CONTROL_EQUIPMENT 0x0401 #define ZCL_HA_DEVICEID_IAS_ZONE 0x0402 #define ZCL_HA_DEVICEID_IAS_WARNING_DEVICE 0x0403 /* ZCL Home Automation Profile initialization function */ extern void zclHA_Init( SimpleDescriptionFormat_t *simpleDesc );

Page 188: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

188

zcl_ha.c

void zclHA_Init( SimpleDescriptionFormat_t *simpleDesc ) { endPointDesc_t *epDesc; // Register the application's endpoint descriptor // - This memory is allocated and never freed. epDesc = osal_mem_alloc( sizeof ( endPointDesc_t ) ); if ( epDesc ) { // Fill out the endpoint description. epDesc->endPoint = simpleDesc->EndPoint; epDesc->task_id = &zcl_TaskID; // all messages get sent to ZCL first epDesc->simpleDesc = simpleDesc; epDesc->latencyReq = noLatencyReqs; // Register the endpoint description with the AF afRegister( epDesc ); } }

Page 189: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

SampleLight

Page 190: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

190

OSAL_SampleLight.c /*********************************************************************** Filename: OSAL_SampleLight.c Description: This file contains all the settings and other functions that the user should set and change. /**** INCLUDES ****/ ... 略 ... #include "zcl_samplelight.h“ /**** GLOBAL VARIABLES ****/ 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 zcl_event_loop, zclSampleLight_event_loop }; const uint8 tasksCnt = sizeof( tasksArr ) / sizeof( tasksArr[0] ); uint16 *tasksEvents;

Page 191: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

191

/********************************************************************* * @fn osalInitTasks * @brief This function invokes the initialization function for each task. 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 zcl_Init( taskID++ ); zclSampleLight_Init( taskID ); }

Page 192: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

192

zcl_samplelight.h /************************************************************************** Filename: zcl_samplelight.h Description: This file contains the Zigbee Cluster Library Home Automation Sample Application. /**** INCLUDES *****/ #include "zcl.h" /**** CONSTANTS ****/ #define SAMPLELIGHT_ENDPOINT 13 #define SAMPLELIGHT_MAX_ATTRIBUTES 12 #define LIGHT_OFF 0x00 #define LIGHT_ON 0x01 // Application Events #define SAMPLELIGHT_IDENTIFY_TIMEOUT_EVT 0x0001 /**** VARIABLES ****/ extern SimpleDescriptionFormat_t zclSampleLight_SimpleDesc; extern CONST zclAttrRec_t zclSampleLight_Attrs[]; extern uint8 zclSampleLight_OnOff; extern uint16 zclSampleLight_IdentifyTime; /**** FUNCTIONS ****/ /* Initialization for the task */ extern void zclSampleLight_Init( byte task_id ); /* Event Process for the task */ extern UINT16 zclSampleLight_event_loop( byte task_id, UINT16 events );

Page 193: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

193

zcl_samplelight_data.c /************************************************************************* Filename: zcl_samplelight_data.c Description: Zigbee Cluster Library - sample device application. /***** INCLUDES *****/ ... 略 ... #include "zcl.h" #include "zcl_general.h" #include "zcl_ha.h" #include "zcl_samplelight.h" /**** CONSTANTS *****/ #define SAMPLELIGHT_DEVICE_VERSION 0 #define SAMPLELIGHT_FLAGS 0 #define SAMPLELIGHT_HWVERSION 1 #define SAMPLELIGHT_ZCLVERSION 1 /**** GLOBAL VARIABLES ****/ // Basic Cluster const uint8 zclSampleLight_HWRevision = SAMPLELIGHT_HWVERSION; const uint8 zclSampleLight_ZCLVersion = SAMPLELIGHT_ZCLVERSION; const uint8 zclSampleLight_ManufacturerName[] = { 16, 'T','e','x','a','s','I','n','s','t','r','u','m','e','n','t','s' }; const uint8 zclSampleLight_ModelId[] = { 16, 'T','I','0','0','0','1',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ' }; const uint8 zclSampleLight_DateCode[] = { 16, '2','0','0','6','0','8','3','1',' ',' ',' ',' ',' ',' ',' ',' ' }; const uint8 zclSampleLight_PowerSource = POWER_SOURCE_MAINS_1_PHASE; uint8 zclSampleLight_LocationDescription[17] = { 16, ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ' }; uint8 zclSampleLight_PhysicalEnvironment = 0; uint8 zclSampleLight_DeviceEnable = DEVICE_ENABLED; // Identify Cluster uint16 zclSampleLight_IdentifyTime = 0; // On/Off Cluster uint8 zclSampleLight_OnOff = LIGHT_OFF;

Page 194: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

194

/********************************************************************* * ATTRIBUTE DEFINITIONS - Uses REAL cluster IDs */ CONST zclAttrRec_t zclSampleLight_Attrs[SAMPLELIGHT_MAX_ATTRIBUTES] = { // *** General Basic Cluster Attributes *** { ZCL_CLUSTER_ID_GEN_BASIC, // Cluster IDs - defined in the foundation (ie. zcl.h) { // Attribute record ATTRID_BASIC_HW_VERSION, // Attribute ID - Found in Cluster Library header (ie. zcl_general.h) ZCL_DATATYPE_UINT8, // Data Type - found in zcl.h ACCESS_CONTROL_READ, // Variable access control - found in zcl.h (void *)&zclSampleLight_HWRevision // Pointer to attribute variable } }, { ZCL_CLUSTER_ID_GEN_BASIC, { // Attribute record ATTRID_BASIC_ZCL_VERSION, ZCL_DATATYPE_UINT8, ACCESS_CONTROL_READ, (void *)&zclSampleLight_ZCLVersion } }, { ZCL_CLUSTER_ID_GEN_BASIC, { // Attribute record ATTRID_BASIC_MANUFACTURER_NAME, ZCL_DATATYPE_CHAR_STR, ACCESS_CONTROL_READ, (void *)zclSampleLight_ManufacturerName } },

{ ZCL_CLUSTER_ID_GEN_BASIC, { // Attribute record ATTRID_BASIC_MODEL_ID, ZCL_DATATYPE_CHAR_STR, ACCESS_CONTROL_READ, (void *)zclSampleLight_ModelId } }, { ZCL_CLUSTER_ID_GEN_BASIC, { // Attribute record ATTRID_BASIC_DATE_CODE, ZCL_DATATYPE_CHAR_STR, ACCESS_CONTROL_READ, (void *)zclSampleLight_DateCode } },

typedef struct { uint16 clusterID; zclAttribute_t attr; } zclAttrRec_t; typedef struct

{ uint16 attrId; uint8 dataType; uint8 accessControl; void *dataPtr; } zclAttribute_t;

Page 195: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

195

{ ZCL_CLUSTER_ID_GEN_BASIC, { // Attribute record ATTRID_BASIC_LOCATION_DESC, ZCL_DATATYPE_CHAR_STR, (ACCESS_CONTROL_READ | ACCESS_CONTROL_WRITE), (void *)zclSampleLight_LocationDescription } }, { ZCL_CLUSTER_ID_GEN_BASIC, { // Attribute record ATTRID_BASIC_PHYSICAL_ENV, ZCL_DATATYPE_UINT8, (ACCESS_CONTROL_READ | ACCESS_CONTROL_WRITE), (void *)&zclSampleLight_PhysicalEnvironment } }, { ZCL_CLUSTER_ID_GEN_BASIC, { // Attribute record ATTRID_BASIC_DEVICE_ENABLED, ZCL_DATATYPE_BOOLEAN, (ACCESS_CONTROL_READ | ACCESS_CONTROL_WRITE), (void *)&zclSampleLight_DeviceEnable } },

// *** Identify Cluster Attribute *** { ZCL_CLUSTER_ID_GEN_IDENTIFY, { // Attribute record ATTRID_IDENTIFY_TIME, ZCL_DATATYPE_UINT16, (ACCESS_CONTROL_READ | ACCESS_CONTROL_WRITE), (void *)&zclSampleLight_IdentifyTime } }, // *** On/Off Cluster Attributes *** { ZCL_CLUSTER_ID_GEN_ON_OFF, { // Attribute record ATTRID_ON_OFF, ZCL_DATATYPE_UINT8, ACCESS_CONTROL_READ, (void *)&zclSampleLight_OnOff } }, };

Page 196: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

196

/********************************************************************* * SIMPLE DESCRIPTOR */ // This is the Cluster ID List and should be filled with Application // specific cluster IDs. #define ZCLSAMPLELIGHT_MAX_INCLUSTERS 5 const cId_t zclSampleLight_InClusterList[ZCLSAMPLELIGHT_MAX_INCLUSTERS] = { ZCL_CLUSTER_ID_GEN_BASIC, ZCL_CLUSTER_ID_GEN_SCENES, ZCL_CLUSTER_ID_GEN_GROUPS, ZCL_CLUSTER_ID_GEN_ON_OFF, ZCL_CLUSTER_ID_GEN_LEVEL_CONTROL }; #define ZCLSAMPLELIGHT_MAX_OUTCLUSTERS 1 const cId_t zclSampleLight_OutClusterList[ZCLSAMPLELIGHT_MAX_OUTCLUSTERS] = { ZCL_CLUSTER_ID_GEN_BASIC }; SimpleDescriptionFormat_t zclSampleLight_SimpleDesc = { SAMPLELIGHT_ENDPOINT, // int Endpoint; ZCL_HA_PROFILE_ID, // uint16 AppProfId[2]; ZCL_HA_DEVICEID_DIMMABLE_LIGHT, // uint16 AppDeviceId[2]; SAMPLELIGHT_DEVICE_VERSION, // int AppDevVer:4; SAMPLELIGHT_FLAGS, // int AppFlags:4; ZCLSAMPLELIGHT_MAX_INCLUSTERS, // byte AppNumInClusters; (cId_t *)zclSampleLight_InClusterList, // byte *pAppInClusterList; ZCLSAMPLELIGHT_MAX_OUTCLUSTERS, // byte AppNumInClusters; (cId_t *)zclSampleLight_OutClusterList // byte *pAppInClusterList; };

Page 197: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

197

zcl_samplelight.c /************************************************************************************************ Filename: zcl_sampleLight.c Description: Zigbee Cluster Library - sample device application. /************************************************************************************************ This device will be like a Light device. This application is not intended to be a Light device, but will use the device description to implement this sample code. *********************************************************************/ /***** INCLUDES *****/ ... 略 ... #include "zcl.h" #include "zcl_general.h" #include "zcl_ha.h" #include "zcl_samplelight.h“ ... 略 ... /***** GLOBAL VARIABLES *****/ byte zclSampleLight_TaskID; /***** LOCAL VARIABLES ******/ //static afAddrType_t zclSampleLight_DstAddr; #define ZCLSAMPLELIGHT_BINDINGLIST 2 static cId_t bindingInClusters[ZCLSAMPLELIGHT_BINDINGLIST] = { ZCL_CLUSTER_ID_GEN_ON_OFF, ZCL_CLUSTER_ID_GEN_LEVEL_CONTROL };

// Test Endpoint to allow SYS_APP_MSGs static endPointDesc_t sampleLight_TestEp = { 20, // Test endpoint &zclSampleLight_TaskID, // No Simple description for this test endpoint (SimpleDescriptionFormat_t *)NULL, // No Network Latency req (afNetworkLatencyReq_t)0 };

Page 198: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

198

/**** LOCAL FUNCTIONS *****/ static void zclSampleLight_HandleKeys( byte shift, byte keys ); static void zclSampleLight_BasicResetCB( void ); static void zclSampleLight_IdentifyCB( zclIdentify_t *pCmd ); static void zclSampleLight_IdentifyQueryRspCB( zclIdentifyQueryRsp_t *pRsp ); static void zclSampleLight_OnOffCB( uint8 cmd ); static void zclSampleLight_ProcessIdentifyTimeChange( void ); // Functions to process ZCL Foundation incoming Command/Response messages static void zclSampleLight_ProcessIncomingMsg( zclIncomingMsg_t *msg ); #ifdef ZCL_READ static uint8 zclSampleLight_ProcessInReadRspCmd( zclIncomingMsg_t *pInMsg ); #endif #ifdef ZCL_WRITE static uint8 zclSampleLight_ProcessInWriteRspCmd( zclIncomingMsg_t *pInMsg ); #endif static uint8 zclSampleLight_ProcessInDefaultRspCmd( zclIncomingMsg_t *pInMsg ); #ifdef ZCL_DISCOVER static uint8 zclSampleLight_ProcessInDiscRspCmd( zclIncomingMsg_t *pInMsg ); #endif

/***** ZCL General Profile Callback table *****/ static zclGeneral_AppCallbacks_t zclSampleLight_CmdCallbacks = { zclSampleLight_BasicResetCB, // Basic Cluster Reset command zclSampleLight_IdentifyCB, // Identify command zclSampleLight_IdentifyQueryRspCB, // Identify Query Response command zclSampleLight_OnOffCB, // On/Off cluster command NULL, // Level Control Move to Level command NULL, // Level Control Move command NULL, // Level Control Step command NULL, // Group Response commands NULL, // Scene Store Request command NULL, // Scene Recall Request command NULL, // Scene Response command NULL, // Alarm (Response) command NULL, // RSSI Location commands NULL, // RSSI Location Response commands };

Page 199: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

199

/********************************************************************* * @fn zclSampleLight_Init * @brief Initialization function for the zclGeneral layer. void zclSampleLight_Init( byte task_id ) { zclSampleLight_TaskID = task_id; // Set destination address to indirect //zclSampleLight_DstAddr.addrMode = (afAddrMode_t)AddrNotPresent; //zclSampleLight_DstAddr.endPoint = 0; //zclSampleLight_DstAddr.addr.shortAddr = 0; // This app is part of the Home Automation Profile zclHA_Init( &zclSampleLight_SimpleDesc ); // Register the ZCL General Cluster Library callback functions zclGeneral_RegisterCmdCallbacks( SAMPLELIGHT_ENDPOINT, &zclSampleLight_CmdCallbacks ); // Register the application's attribute list zcl_registerAttrList( SAMPLELIGHT_ENDPOINT, SAMPLELIGHT_MAX_ATTRIBUTES, zclSampleLight_Attrs ); // Register the Application to receive the unprocessed Foundation command/response messages zcl_registerForMsg( zclSampleLight_TaskID ); // Register for all key events - This app will handle all key events RegisterForKeys( zclSampleLight_TaskID ); // Register for a test endpoint afRegister( &sampleLight_TestEp ); }

Page 200: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

200

/********************************************************************* * @fn zclSample_event_loop * @brief Event Loop Processor for zclGeneral. uint16 zclSampleLight_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( zclSampleLight_TaskID )) ) { switch ( MSGpkt->hdr.event ) { case ZCL_INCOMING_MSG: // Incoming ZCL Foundation command/response messages zclSampleLight_ProcessIncomingMsg( (zclIncomingMsg_t *)MSGpkt ); break; case KEY_CHANGE: zclSampleLight_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys ); break; default: break; } // Release the memory osal_msg_deallocate( (uint8 *)MSGpkt ); } // return unprocessed events return (events ^ SYS_EVENT_MSG); }

if ( events & SAMPLELIGHT_IDENTIFY_TIMEOUT_EVT ) { if ( zclSampleLight_IdentifyTime > 0 ) zclSampleLight_IdentifyTime--; zclSampleLight_ProcessIdentifyTimeChange(); return ( events ^ SAMPLELIGHT_IDENTIFY_TIMEOUT_EVT ); } // Discard unknown events return 0; }

Page 201: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

201

/********************************************************************* * @fn zclSampleLight_HandleKeys * @brief Handles all key events for this device. static void zclSampleLight_HandleKeys( byte shift, byte keys ) { zAddrType_t dstAddr; (void)shift; // Intentionally unreferenced parameter if ( keys & HAL_KEY_SW_2 ) { // Initiate an End Device Bind Request, this bind request will // only use a cluster list that is important to binding. dstAddr.addrMode = afAddr16Bit; dstAddr.addr.shortAddr = 0; // Coordinator makes the match ZDP_EndDeviceBindReq( &dstAddr, NLME_GetShortAddr(), SAMPLELIGHT_ENDPOINT, ZCL_HA_PROFILE_ID, ZCLSAMPLELIGHT_BINDINGLIST, bindingInClusters, 0, NULL, // No Outgoing clusters to bind TRUE ); } ... 略 ... }

/********************************************************************* * @fn zclSampleLight_ProcessIdentifyTimeChange * @brief Called to process any change to the IdentifyTime attribute. static void zclSampleLight_ProcessIdentifyTimeChange( void ) { if ( zclSampleLight_IdentifyTime > 0 ) { osal_start_timerEx( zclSampleLight_TaskID, SAMPLELIGHT_IDENTIFY_TIMEOUT_EVT, 1000 ); HalLedBlink ( HAL_LED_4, 0xFF, HAL_LED_DEFAULT_DUTY_CYCLE, HAL_LED_DEFAULT_FLASH_TIME ); } else { if ( zclSampleLight_OnOff ) HalLedSet ( HAL_LED_4, HAL_LED_MODE_ON ); else HalLedSet ( HAL_LED_4, HAL_LED_MODE_OFF ); osal_stop_timerEx( zclSampleLight_TaskID, SAMPLELIGHT_IDENTIFY_TIMEOUT_EVT ); } }

API手冊

Page 202: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

202

/********************************************************************* * @fn zclSampleLight_BasicResetCB * @brief Callback from the ZCL General Cluster Library * to set all the Basic Cluster attributes to default values. static void zclSampleLight_BasicResetCB( void ) { // Reset all attributes to default values } /********************************************************************* * @fn zclSampleLight_IdentifyCB * @brief Callback from the ZCL General Cluster Library when * it received an Identity Command for this application. static void zclSampleLight_IdentifyCB( zclIdentify_t *pCmd ) { zclSampleLight_IdentifyTime = pCmd->identifyTime; zclSampleLight_ProcessIdentifyTimeChange(); } /********************************************************************* * @fn zclSampleLight_IdentifyQueryRspCB * @brief Callback from the ZCL General Cluster Library when * it received an Identity Query Response Command for this application. static void zclSampleLight_IdentifyQueryRspCB( zclIdentifyQueryRsp_t *pRsp ) { // Query Response (with timeout value) (void)pRsp; } typedef struct

{ afAddrType_t *srcAddr; uint16 timeout; } zclIdentifyQueryRsp_t;

typedef struct { afAddrType_t *srcAddr; uint16 identifyTime; } zclIdentify_t;

Page 203: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

203

/********************************************************************* * @fn zclSampleLight_OnOffCB * @brief Callback from the ZCL General Cluster Library when * it received an On/Off Command for this application. static void zclSampleLight_OnOffCB( uint8 cmd ) { // Turn on the light if ( cmd == COMMAND_ON ) zclSampleLight_OnOff = LIGHT_ON; // Turn off the light else if ( cmd == COMMAND_OFF ) zclSampleLight_OnOff = LIGHT_OFF; // Toggle the light else { if ( zclSampleLight_OnOff == LIGHT_OFF ) zclSampleLight_OnOff = LIGHT_ON; else zclSampleLight_OnOff = LIGHT_OFF; } // In this sample app, we use LED4 to simulate the Light if ( zclSampleLight_OnOff == LIGHT_ON ) HalLedSet( HAL_LED_4, HAL_LED_MODE_ON ); else HalLedSet( HAL_LED_4, HAL_LED_MODE_OFF ); }

// This callback is called to process an incoming // On, Off or Toggle command. // cmd - received command, which will be either // COMMAND_ON, COMMAND_OFF or COMMAND_TOGGLE. typedef void (*zclGCB_OnOff_t)( uint8 cmd );

Page 204: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

204

/****************************************************************************** * Functions for processing ZCL Foundation incoming Command/Response messages *****************************************************************************/ /********************************************************************* * @fn zclSampleLight_ProcessIncomingMsg * @brief Process ZCL Foundation incoming message static void zclSampleLight_ProcessIncomingMsg( zclIncomingMsg_t *pInMsg) { switch ( pInMsg->zclHdr.commandID ) { #ifdef ZCL_READ case ZCL_CMD_READ_RSP: zclSampleLight_ProcessInReadRspCmd( pInMsg ); break; #endif #ifdef ZCL_WRITE case ZCL_CMD_WRITE_RSP: zclSampleLight_ProcessInWriteRspCmd( pInMsg ); break; #endif #ifdef ZCL_REPORT // See ZCL Test Applicaiton (zcl_testapp.c) for sample code on Attribute Reporting case ZCL_CMD_CONFIG_REPORT: //zclSampleLight_ProcessInConfigReportCmd( pInMsg ); break; case ZCL_CMD_CONFIG_REPORT_RSP: //zclSampleLight_ProcessInConfigReportRspCmd( pInMsg ); break; case ZCL_CMD_READ_REPORT_CFG: //zclSampleLight_ProcessInReadReportCfgCmd( pInMsg ); break;

typedef struct { osal_event_hdr_t hdr; zclFrameHdr_t zclHdr; uint16 clusterId; afAddrType_t srcAddr; uint8 endPoint; void *attrCmd; } zclIncomingMsg_t;

typedef struct { zclFrameControl_t fc; uint16 manuCode; uint8 transSeqNum; uint8 commandID; } zclFrameHdr_t;

Page 205: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

205

case ZCL_CMD_READ_REPORT_CFG_RSP: //zclSampleLight_ProcessInReadReportCfgRspCmd( pInMsg ); break; case ZCL_CMD_REPORT: //zclSampleLight_ProcessInReportCmd( pInMsg ); break; #endif case ZCL_CMD_DEFAULT_RSP: zclSampleLight_ProcessInDefaultRspCmd( pInMsg ); break; #ifdef ZCL_DISCOVER case ZCL_CMD_DISCOVER_RSP: zclSampleLight_ProcessInDiscRspCmd( pInMsg ); break; #endif default: break; } if ( pInMsg->attrCmd ) osal_mem_free( pInMsg->attrCmd ); }

#ifdef ZCL_READ /********************************************************************* * @fn zclSampleLight_ProcessInReadRspCmd * @brief Process the "Profile" Read Response Command static uint8 zclSampleLight_ProcessInReadRspCmd(zclIncomingMsg_t *pInMsg) { zclReadRspCmd_t *readRspCmd; uint8 i; readRspCmd = (zclReadRspCmd_t *)pInMsg->attrCmd; for (i = 0; i < readRspCmd->numAttr; i++) { // Notify the originator of the results of the original read // attributes attempt and, for each successfull request, the // value of the requested attribute } return TRUE; } #endif // ZCL_READ

typedef struct { uint8 numAttr; zclReadRspStatus_t attrList[]; } zclReadRspCmd_t;

typedef struct { uint16 attrID; uint8 status; uint8 dataType; uint8 *data; } zclReadRspStatus_t;

Page 206: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

206

#ifdef ZCL_WRITE /********************************************************************* * @fn zclSampleLight_ProcessInWriteRspCmd * @brief Process the "Profile" Write Response Command static uint8 zclSampleLight_ProcessInWriteRspCmd( zclIncomingMsg_t *pInMsg ) { zclWriteRspCmd_t *writeRspCmd; uint8 i; writeRspCmd = (zclWriteRspCmd_t *)pInMsg->attrCmd; for (i = 0; i < writeRspCmd->numAttr; i++) { // Notify the device of the results of the its original write attributes // command. } return TRUE; } #endif // ZCL_WRITE

/********************************************************************* * @fn zclSampleLight_ProcessInDefaultRspCmd * @brief Process the "Profile" Default Response Command static uint8 zclSampleLight_ProcessInDefaultRspCmd( zclIncomingMsg_t *pInMsg ) { // zclDefaultRspCmd_t *defaultRspCmd = (zclDefaultRspCmd_t *)pInMsg->attrCmd; // Device is notified of the Default Response command. (void)pInMsg; return TRUE; }

typedef struct { uint8 numAttr; zclWriteRspStatus_t attrList[]; } zclWriteRspCmd_t;

typedef struct { uint8 status; uint16 attrID; } zclWriteRspStatus_t;

typedef struct { uint8 commandID; uint8 statusCode; } zclDefaultRspCmd_t;

Page 207: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

207

ifdef ZCL_DISCOVER /********************************************************************* * @fn zclSampleLight_ProcessInDiscRspCmd * @brief Process the "Profile" Discover Response Command static uint8 zclSampleLight_ProcessInDiscRspCmd( zclIncomingMsg_t *pInMsg ) { zclDiscoverRspCmd_t *discoverRspCmd; uint8 i; discoverRspCmd = (zclDiscoverRspCmd_t *)pInMsg->attrCmd; for ( i = 0; i < discoverRspCmd->numAttr; i++ ) { // Device is notified of the result of its attribute discovery command. } return TRUE; } #endif // ZCL_DISCOVER

typedef struct { uint8 discComplete; uint8 numAttr; zclDiscoverInfo_t attrList[]; } zclDiscoverRspCmd_t;

typedef struct { uint16 attrID; uint8 dataType; } zclDiscoverInfo_t;

Page 208: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

SampleSwitch

Page 209: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

209

OSAL_SampleSw.c /*************************************************************************** Filename: OSAL_SampleSw.c Description: This file contains all the settings and other functions that the user should set and change. /***** INCLUDES *****/ ... 略 ... #include "zcl_samplesw.h" /***** GLOBAL VARIABLES *****/ 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 zcl_event_loop, zclSampleSw_event_loop }; const uint8 tasksCnt = sizeof( tasksArr ) / sizeof( tasksArr[0] ); uint16 *tasksEvents;

Page 210: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

210

/********************************************************************* * @fn osalInitTasks * @brief This function invokes the initialization function for each task. 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 zcl_Init( taskID++ ); zclSampleSw_Init( taskID ); }

Page 211: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

211

zcl_samplesw.h /********************************************************************** Filename: zcl_samplesw.h Description: This file contains the Zigbee Cluster Library Home Automation Sample Application. /***** INCLUDES *****/ #include "zcl.h" /***** CONSTANTS ****/ #define SAMPLESW_ENDPOINT 12 #define SAMPLESW_MAX_ATTRIBUTES 11 #define LIGHT_OFF 0x00 #define LIGHT_ON 0x01 // Events for the sample app #define SAMPLESW_IDENTIFY_TIMEOUT_EVT 0x0001 /***** VARIABLES ***/ extern SimpleDescriptionFormat_t zclSampleSw_SimpleDesc; extern CONST zclAttrRec_t zclSampleSw_Attrs[]; extern uint8 zclSampleSw_OnOff; extern uint16 zclSampleSw_IdentifyTime; /***** FUNCTIONS *****/ /* Initialization for the task */ extern void zclSampleSw_Init( byte task_id ); /* Event Process for the task */ extern UINT16 zclSampleSw_event_loop( byte task_id, UINT16 events );

Page 212: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

212

zcl_samplesw.c /************************************************************************ Filename: zcl_samplesw.c Description: Zigbee Cluster Library - sample device application. /********************************************************************* This device will be like an On/Off Switch device. This application is not intended to be a On/Off Switch device, but will use the device description to implement this sample code. *********************************************************************/ /***** INCLUDES *****/ ... 略 ... #include "zcl.h" #include "zcl_general.h" #include "zcl_ha.h" #include "zcl_samplesw.h" ... 略 ... /***** GLOBAL VARIABLES *****/ byte zclSampleSw_TaskID; /***** LOCAL VARIABLES ******/ static afAddrType_t zclSampleSw_DstAddr; #define ZCLSAMPLESW_BINDINGLIST 1 static cId_t bindingOutClusters[ZCLSAMPLESW_BINDINGLIST] = { ZCL_CLUSTER_ID_GEN_ON_OFF };

// Test Endpoint to allow SYS_APP_MSGs static endPointDesc_t sampleSw_TestEp = { 20, // Test endpoint &zclSampleSw_TaskID, (SimpleDescriptionFormat_t *)NULL, // No Simple descrip for this test endpoint (afNetworkLatencyReq_t)0 // No Network Latency req };

Page 213: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

213

/***** LOCAL FUNCTIONS *****/ static void zclSampleSw_ProcessZDOMsgs( zdoIncomingMsg_t *inMsg ); static void zclSampleSw_HandleKeys( byte shift, byte keys ); static void zclSampleSw_BasicResetCB( void ); static void zclSampleSw_IdentifyCB( zclIdentify_t *pCmd ); static void zclSampleSw_IdentifyQueryRspCB( zclIdentifyQueryRsp_t *pRsp ); static void zclSampleSw_ProcessIdentifyTimeChange( void ); // Functions to process ZCL Foundation incoming Command/Response messages static void zclSampleSw_ProcessIncomingMsg( zclIncomingMsg_t *msg ); #ifdef ZCL_READ static uint8 zclSampleSw_ProcessInReadRspCmd( zclIncomingMsg_t *pInMsg ); #endif #ifdef ZCL_WRITE static uint8 zclSampleSw_ProcessInWriteRspCmd( zclIncomingMsg_t *pInMsg ); #endif static uint8 zclSampleSw_ProcessInDefaultRspCmd( zclIncomingMsg_t *pInMsg ); #ifdef ZCL_DISCOVER static uint8 zclSampleSw_ProcessInDiscRspCmd( zclIncomingMsg_t *pInMsg ); #endif

/**** ZCL General Profile Callback table ****/ static zclGeneral_AppCallbacks_t zclSampleSw_CmdCallbacks = { zclSampleSw_BasicResetCB, // Basic Cluster Reset command zclSampleSw_IdentifyCB, // Identify command zclSampleSw_IdentifyQueryRspCB, // Identify Query Response command NULL, // On / Off cluster command - not needed. NULL, // Level Control Move to Level command NULL, // Level Control Move command NULL, // Level Control Step command NULL, // Group Response commands NULL, // Scene Store Request command NULL, // Scene Recall Request command NULL, // Scene Response commands NULL, // Alarm (Response) commands NULL, // RSSI Location commands NULL, // RSSI Location Response commands };

Page 214: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

214

/********************************************************************* * @fn zclSampleSw_Init * @brief Initialization function for the zclGeneral layer. void zclSampleSw_Init( byte task_id ) { zclSampleSw_TaskID = task_id; // Set destination address to indirect zclSampleSw_DstAddr.addrMode = (afAddrMode_t)AddrNotPresent; zclSampleSw_DstAddr.endPoint = 0; zclSampleSw_DstAddr.addr.shortAddr = 0; // This app is part of the Home Automation Profile zclHA_Init( &zclSampleSw_SimpleDesc ); // Register the ZCL General Cluster Library callback functions zclGeneral_RegisterCmdCallbacks( SAMPLESW_ENDPOINT, &zclSampleSw_CmdCallbacks ); // Register the application's attribute list zcl_registerAttrList( SAMPLESW_ENDPOINT, SAMPLESW_MAX_ATTRIBUTES, zclSampleSw_Attrs ); // Register the Application to receive the unprocessed Foundation command/response messages zcl_registerForMsg( zclSampleSw_TaskID ); // Register for all key events - This app will handle all key events RegisterForKeys( zclSampleSw_TaskID ); // Register for a test endpoint afRegister( &sampleSw_TestEp ); ZDO_RegisterForZDOMsg( zclSampleSw_TaskID, End_Device_Bind_rsp ); ZDO_RegisterForZDOMsg( zclSampleSw_TaskID, Match_Desc_rsp ); }

Page 215: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

215

/********************************************************************* * @fn zclSample_event_loop * @brief Event Loop Processor for zclGeneral. uint16 zclSampleSw_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( zclSampleSw_TaskID )) ) { switch ( MSGpkt->hdr.event ) { case ZCL_INCOMING_MSG: // Incoming ZCL Foundation command/response messages zclSampleSw_ProcessIncomingMsg( (zclIncomingMsg_t *)MSGpkt ); break; case ZDO_CB_MSG: zclSampleSw_ProcessZDOMsgs( (zdoIncomingMsg_t *)MSGpkt ); break; case KEY_CHANGE: zclSampleSw_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys ); break; default: break; } // Release the memory osal_msg_deallocate( (uint8 *)MSGpkt ); } // return unprocessed events return (events ^ SYS_EVENT_MSG); }

if ( events & SAMPLESW_IDENTIFY_TIMEOUT_EVT ) { zclSampleSw_IdentifyTime = 10; zclSampleSw_ProcessIdentifyTimeChange(); return ( events ^ SAMPLESW_IDENTIFY_TIMEOUT_EVT ); } // Discard unknown events return 0; }

??

Page 216: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

216

/********************************************************************* * @fn zclSampleSw_ProcessZDOMsgs() * @brief Process response messages void zclSampleSw_ProcessZDOMsgs( zdoIncomingMsg_t *inMsg ) { switch ( inMsg->clusterID ) { case End_Device_Bind_rsp: if ( ZDO_ParseBindRsp( inMsg ) == ZSuccess ) { HalLedSet( HAL_LED_4, HAL_LED_MODE_ON ); // Light LED } #if defined(BLINK_LEDS) else { HalLedSet ( HAL_LED_4, HAL_LED_MODE_FLASH ); // Flash LED to show failure } #endif break; case Match_Desc_rsp: { ZDO_ActiveEndpointRsp_t *pRsp = ZDO_ParseEPListRsp( inMsg ); if ( pRsp ) { if ( pRsp->status == ZSuccess && pRsp->cnt ) { zclSampleSw_DstAddr.addrMode = (afAddrMode_t)Addr16Bit; zclSampleSw_DstAddr.addr.shortAddr = pRsp->nwkAddr; // Take the first endpoint, Can be changed to search through endpoints zclSampleSw_DstAddr.endPoint = pRsp->epList[0]; HalLedSet( HAL_LED_4, HAL_LED_MODE_ON ); // Light LED } osal_mem_free( pRsp ); } } break; } }

Page 217: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

217

/********************************************************************* * @fn zclSampleSw_HandleKeys * @brief Handles all key events for this device. static void zclSampleSw_HandleKeys( byte shift, byte keys ) { zAddrType_t dstAddr; (void)shift; // Intentionally unreferenced parameter if ( keys & HAL_KEY_SW_1 ) { // Using this as the "Light Switch" #ifdef ZCL_ON_OFF zclGeneral_SendOnOff_CmdToggle( SAMPLESW_ENDPOINT, &zclSampleSw_DstAddr, false, 0 ); #endif } if ( keys & HAL_KEY_SW_2 ) { HalLedSet ( HAL_LED_4, HAL_LED_MODE_OFF ); // Initiate an End Device Bind Request, this bind request will // only use a cluster list that is important to binding. dstAddr.addrMode = afAddr16Bit; dstAddr.addr.shortAddr = 0; // Coordinator makes the match ZDP_EndDeviceBindReq( &dstAddr, NLME_GetShortAddr(), SAMPLESW_ENDPOINT, ZCL_HA_PROFILE_ID, 0, NULL, // No incoming clusters to bind ZCLSAMPLESW_BINDINGLIST, bindingOutClusters, TRUE ); }

Page 218: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

218

if ( keys & HAL_KEY_SW_3 ) { } if ( keys & HAL_KEY_SW_4 ) { HalLedSet ( HAL_LED_4, HAL_LED_MODE_OFF ); // Initiate a Match Description Request (Service Discovery) dstAddr.addrMode = AddrBroadcast; dstAddr.addr.shortAddr = NWK_BROADCAST_SHORTADDR; ZDP_MatchDescReq( &dstAddr, NWK_BROADCAST_SHORTADDR, ZCL_HA_PROFILE_ID, ZCLSAMPLESW_BINDINGLIST, bindingOutClusters, 0, NULL, // No incoming clusters to bind FALSE ); } }

/********************************************************************* * @fn zclSampleSw_ProcessIdentifyTimeChange * @brief Called to process any change to the IdentifyTime attribute. static void zclSampleSw_ProcessIdentifyTimeChange( void ) { if ( zclSampleSw_IdentifyTime > 0 ) { osal_start_timerEx( zclSampleSw_TaskID, SAMPLESW_IDENTIFY_TIMEOUT_EVT, 1000 ); HalLedBlink ( HAL_LED_4, 0xFF, HAL_LED_DEFAULT_DUTY_CYCLE, HAL_LED_DEFAULT_FLASH_TIME ); } else { if ( zclSampleSw_OnOff ) HalLedSet ( HAL_LED_4, HAL_LED_MODE_ON ); else HalLedSet ( HAL_LED_4, HAL_LED_MODE_OFF ); osal_stop_timerEx( zclSampleSw_TaskID, SAMPLESW_IDENTIFY_TIMEOUT_EVT ); } }

Page 219: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

219

/********************************************************************* * @fn zclSampleSw_BasicResetCB * @brief Callback from the ZCL General Cluster Library * to set all the Basic Cluster attributes to default values. static void zclSampleSw_BasicResetCB( void ) { } /********************************************************************* * @fn zclSampleSw_IdentifyCB * @brief Callback from the ZCL General Cluster Library when * it received an Identity Command for this application. static void zclSampleSw_IdentifyCB( zclIdentify_t *pCmd ) { zclSampleSw_IdentifyTime = pCmd->identifyTime; zclSampleSw_ProcessIdentifyTimeChange(); } /********************************************************************* * @fn zclSampleSw_IdentifyQueryRspCB * @brief Callback from the ZCL General Cluster Library when * it received an Identity Query Response Command for this application. static void zclSampleSw_IdentifyQueryRspCB( zclIdentifyQueryRsp_t *pRsp ) { // Query Response (with timeout value) (void)pRsp; }

typedef struct { afAddrType_t *srcAddr; uint16 timeout; } zclIdentifyQueryRsp_t;

typedef struct { afAddrType_t *srcAddr; uint16 identifyTime; } zclIdentify_t;

Page 220: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

220

/****************************************************************************** * Functions for processing ZCL Foundation incoming Command/Response messages *****************************************************************************/ /********************************************************************* * @fn zclSampleSw_ProcessIncomingMsg * @brief Process ZCL Foundation incoming message static void zclSampleSw_ProcessIncomingMsg( zclIncomingMsg_t *pInMsg ) { switch ( pInMsg->zclHdr.commandID ) { #ifdef ZCL_READ case ZCL_CMD_READ_RSP: zclSampleSw_ProcessInReadRspCmd( pInMsg ); break; #endif #ifdef ZCL_WRITE case ZCL_CMD_WRITE_RSP: zclSampleSw_ProcessInWriteRspCmd( pInMsg ); break; #endif #ifdef ZCL_REPORT // See ZCL Test Applicaiton (zcl_testapp.c) for sample code on Attribute Reporting case ZCL_CMD_CONFIG_REPORT: //zclSampleSw_ProcessInConfigReportCmd( pInMsg ); break; case ZCL_CMD_CONFIG_REPORT_RSP: //zclSampleSw_ProcessInConfigReportRspCmd( pInMsg ); break; case ZCL_CMD_READ_REPORT_CFG: //zclSampleSw_ProcessInReadReportCfgCmd( pInMsg ); break;

typedef struct { osal_event_hdr_t hdr; zclFrameHdr_t zclHdr; uint16 clusterId; afAddrType_t srcAddr; uint8 endPoint; void *attrCmd; } zclIncomingMsg_t;

typedef struct { zclFrameControl_t fc; uint16 manuCode; uint8 transSeqNum; uint8 commandID; } zclFrameHdr_t;

Page 221: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

221

case ZCL_CMD_READ_REPORT_CFG_RSP: //zclSampleSw_ProcessInReadReportCfgRspCmd( pInMsg ); break; case ZCL_CMD_REPORT: //zclSampleSw_ProcessInReportCmd( pInMsg ); break; #endif case ZCL_CMD_DEFAULT_RSP: zclSampleSw_ProcessInDefaultRspCmd( pInMsg ); break; #ifdef ZCL_DISCOVER case ZCL_CMD_DISCOVER_RSP: zclSampleSw_ProcessInDiscRspCmd( pInMsg ); break; #endif default: break; } if ( pInMsg->attrCmd ) osal_mem_free(pInMsg->attrCmd); }

#ifdef ZCL_READ /***************************************************************** * @fn zclSampleSw_ProcessInReadRspCmd * @brief Process the "Profile" Read Response Command static uint8 zclSampleSw_ProcessInReadRspCmd( zclIncomingMsg_t *pInMsg ) { zclReadRspCmd_t *readRspCmd; uint8 i; readRspCmd = (zclReadRspCmd_t *)pInMsg->attrCmd; for (i = 0; i < readRspCmd->numAttr; i++) { // Notify the originator of the results of the original read // attributes attempt and, for each successfull request, the // value of the requested attribute } return TRUE; } #endif // ZCL_READ

Page 222: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

222

#ifdef ZCL_WRITE /********************************************************************* * @fn zclSampleSw_ProcessInWriteRspCmd * @brief Process the "Profile" Write Response Command static uint8 zclSampleSw_ProcessInWriteRspCmd( zclIncomingMsg_t *pInMsg ) { zclWriteRspCmd_t *writeRspCmd; uint8 i; writeRspCmd = (zclWriteRspCmd_t *)pInMsg->attrCmd; for (i = 0; i < writeRspCmd->numAttr; i++) { // Notify the device of the results of the its original write attributes // command. } return TRUE; } #endif // ZCL_WRITE

/********************************************************************* * @fn zclSampleSw_ProcessInDefaultRspCmd * @brief Process the "Profile" Default Response Command static uint8 zclSampleSw_ProcessInDefaultRspCmd( zclIncomingMsg_t *pInMsg ) { // zclDefaultRspCmd_t *defaultRspCmd = (zclDefaultRspCmd_t *)pInMsg->attrCmd; // Device is notified of the Default Response command. (void)pInMsg; return TRUE; }

Page 223: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

223

#ifdef ZCL_DISCOVER /********************************************************************* * @fn zclSampleSw_ProcessInDiscRspCmd * @brief Process the "Profile" Discover Response Command static uint8 zclSampleSw_ProcessInDiscRspCmd( zclIncomingMsg_t *pInMsg ) { zclDiscoverRspCmd_t *discoverRspCmd; uint8 i; discoverRspCmd = (zclDiscoverRspCmd_t *)pInMsg->attrCmd; for ( i = 0; i < discoverRspCmd->numAttr; i++ ) { // Device is notified of the result of its attribute discovery command. } return TRUE; } #endif // ZCL_DISCOVER

Page 224: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

附錄:SAPI

Page 225: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

225

sapi.h /** Filename: sapi.h Description: Z-Stack Simple Application Interface. */ #ifndef SAPI_H #define SAPI_H /****** INCLUDES ******/ #include "ZComDef.h" #include "af.h" /****** CONSTANTS *****/ // Simple Task Events #define ZB_ALLOW_BIND_TIMER 0x4000 //0x0001 #define ZB_BIND_TIMER 0x2000 //0x0002 #define ZB_ENTRY_EVENT 0x1000 //0x0004 #define ZB_USER_EVENTS 0x00FF // Find Device Search Types #define ZB_IEEE_SEARCH 1

// Device Info Constants #define ZB_INFO_DEV_STATE 0 #define ZB_INFO_IEEE_ADDR 1 #define ZB_INFO_SHORT_ADDR 2 #define ZB_INFO_PARENT_SHORT_ADDR 3 #define ZB_INFO_PARENT_IEEE_ADDR 4 #define ZB_INFO_CHANNEL 5 #define ZB_INFO_PAN_ID 6 #define ZB_INFO_EXT_PAN_ID 7 // SAPI SendDataRequest destinations #define ZB_BINDING_ADDR INVALID_NODE_ADDR #define ZB_BROADCAST_ADDR 0xffff

Page 226: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

226

// SAPI Status Codes #define ZB_SUCCESS ZSuccess #define ZB_FAILURE ZFailure #define ZB_INVALID_PARAMETER ZInvalidParameter #define ZB_ALREADY_IN_PROGRESS ZSapiInProgress #define ZB_TIMEOUT ZSapiTimeout #define ZB_INIT ZSapiInit #define ZB_AF_FAILURE afStatus_FAILED #define ZB_AF_MEM_FAIL afStatus_MEM_FAIL #define ZB_AF_INVALID_PARAMETER afStatus_INVALID_PARAMETER // SAPI Scan Duration Values #define ZB_SCAN_DURATION_0 0 // 19.2 ms ... 略 ... #define ZB_SCAN_DURATION_14 14 // 314.57 sec // Device types definitions ( from ZGlobals.h file ) #define ZG_DEVICETYPE_COORDINATOR 0x00 #define ZG_DEVICETYPE_ROUTER 0x01 #define ZG_DEVICETYPE_ENDDEVICE 0x02 /***** TYPEDEFS *****/ typedef struct { uint16 panID; uint8 channel; } zb_NetworkList_t; typedef struct { osal_event_hdr_t hdr; uint16 data; } sapi_CbackEvent_t;

/********************************************* * PUBLIC VARIABLES */ extern uint8 sapi_TaskID; extern endPointDesc_t sapi_epDesc;

Page 227: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

227

/****** PUBLIC FUNCTIONS ******/ ... 略 ... extern const SimpleDescriptionFormat_t zb_SimpleDesc; /* @brief The zb_SystemReset function reboots the ZigBee Stack. The zb_SystemReset * function can be called after a call to zb_WriteConfiguration to restart * Z-Stack with the updated configuration. extern void zb_SystemReset ( void ); /* @brief The zb_StartRequest function starts the ZigBee stack. When the ZigBee stack * starts, the device reads configuration parameters from Nonvolatile memory and * the device joins its network. The ZigBee stack calls the zb_StartConfirm * callback function when the startup process completes. extern void zb_StartRequest ( void ); /* @brief The zb_PermitJoiningRequest function is used to control the joining permissions * and thus allow or disallow new devices from joining the network. extern uint8 zb_PermitJoiningRequest ( uint16 destination, uint8 timeout ); /* @brief The zb_BindDevice function establishes or removes a ‘binding’ between two * devices. Once bound, an application can send messages to a device by * referencing the commandId for the binding. extern void zb_BindDevice ( uint8 create, uint16 commandId, uint8 *pDestination ); /* @brief The zb_AllowBind function puts the device into the Allow Binding Mode for a * given period of time. A peer device can establish a binding to a device in the * Allow Binding Mode by calling zb_BindDevice with a destination address of NULL extern void zb_AllowBind ( uint8 timeout ); /* @brief The zb_SendDataRequest function initiates transmission of data to a peer device * extern void zb_SendDataRequest ( uint16 destination, uint16 commandId, uint8 len, uint8 *pData, uint8 handle, uint8 ack, uint8 radius );

Page 228: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

228

/* @brief The zb_ReadConfiguration function is used to get a Configuration Protperty * from Nonvolatile memory. extern uint8 zb_ReadConfiguration( uint8 configId, uint8 len, void *pValue ); /* @brief The zb_WriteConfiguration function is used to write a Configuration Property * to nonvolatile memory. extern uint8 zb_WriteConfiguration( uint8 configId, uint8 len, void *pValue ); /* @brief The zb_GetDeviceInfo function retrieves a Device Information Property. extern void zb_GetDeviceInfo ( uint8 param, void *pValue ); /* @brief The zb_FindDeviceRequest function is used to determine the short address for * a device in the network. The device initiating a call to zb_FindDeviceRequest * and the device being discovered must both be a member of the same network. When * the search is complete, the zb_FindDeviceConfirm callback function is called. extern void zb_FindDeviceRequest( uint8 searchType, void *searchKey ); /* @brief The zb_HandleOsalEvent function is called by the operating system when a task * event is set extern void zb_HandleOsalEvent( uint16 event ); /* @brief The zb_StartConfirm callback is called by the ZigBee stack after a start request * operation completes extern void zb_StartConfirm( uint8 status ); /* @brief The zb_SendDataConfirm callback function is called by the ZigBee after a send * data operation completes extern void zb_SendDataConfirm( uint8 handle, uint8 status ); /* @brief The zb_BindConfirm callback is called by the ZigBee stack after a bind * operation completes. extern void zb_BindConfirm( uint16 commandId, uint8 status );

Page 229: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

229

/* @brief The zb_FindDeviceConfirm callback function is called by the ZigBee stack * when a find device operation completes. extern void zb_FindDeviceConfirm( uint8 searchType, uint8 *searchKey, uint8 *result ); /* @brief The zb_ReceiveDataIndication callback function is called asynchronously by the * ZigBee stack to notify the application when data is received from a peer device. extern void zb_ReceiveDataIndication( uint16 source, uint16 command, uint16 len, uint8 *pData ); extern void zb_AllowBindConfirm( uint16 source ); extern void zb_HandleKeys( uint8 shift, uint8 keys ); /* External declarations required by SAPI */ extern UINT16 SAPI_ProcessEvent( byte task_id, UINT16 events ); extern void SAPI_Init( byte task_id ); extern void osalAddTasks( void ); ... 略 ...

Page 230: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

230

sapi.c /******************************************************** Filename: sapi.c Description: Z-Stack Simple Application Interface. /**** INCLUDES *****/ ... 略 ... #include "sapi.h" #include "MT_SAPI.h" extern uint8 zgStartDelay; extern uint8 zgSapiEndpoint; /**** CONSTANTS ****/ #if !defined OSAL_SAPI #define OSAL_SAPI TRUE #endif #if !defined SAPI_CB_FUNC #define SAPI_CB_FUNC TRUE #endif // Message ID's for application user messages // must be in 0xE0-0xEF range #define ZB_USER_MSG 0xE0 #define SAPICB_DATA_CNF 0xE0 #define SAPICB_BIND_CNF 0xE1 #define SAPICB_START_CNF 0xE2

/**** GLOBAL VARIABLES ****/ #if OSAL_SAPI // 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, ZDApp_event_loop, SAPI_ProcessEvent }; const uint8 tasksCnt = \ sizeof( tasksArr ) / sizeof( tasksArr[0] ); uint16 *tasksEvents; #endif endPointDesc_t sapi_epDesc; uint8 sapi_TaskID; static uint16 sapi_bindInProgress;

Page 231: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

231

/**** LOCAL FUNCTIONS ****/ void SAPI_ProcessZDOMsgs( zdoIncomingMsg_t *inMsg ); static void SAPI_SendCback( uint8 event, uint8 status, uint16 data ); static void SAPI_StartConfirm( uint8 status ); static void SAPI_SendDataConfirm( uint8 handle, uint8 status ); static void SAPI_BindConfirm( uint16 commandId, uint8 status ); static void SAPI_FindDeviceConfirm( uint8 searchType, uint8 *searchKey, uint8 *result ); static void SAPI_ReceiveDataIndication( uint16 source, uint16 command, uint16 len, uint8 *pData ); static void SAPI_AllowBindConfirm( uint16 source );

void zb_SystemReset ( void ) { SystemResetSoft(); // Especially useful for CC2531 to not break comm with USB Host. }

void zb_StartRequest() { uint8 logicalType; zb_ReadConfiguration( ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType ); if ((logicalType > ZG_DEVICETYPE_ENDDEVICE) || #if !ZG_BUILD_ENDDEVICE_TYPE // Only RTR or Coord possible. (logicalType == ZG_DEVICETYPE_ENDDEVICE) || #endif #if !ZG_BUILD_RTR_TYPE // Only End Device possible. (logicalType == ZG_DEVICETYPE_ROUTER) || (logicalType == ZG_DEVICETYPE_COORDINATOR) || #elif ZG_BUILD_RTRONLY_TYPE // Only RTR possible. (logicalType == ZG_DEVICETYPE_COORDINATOR) || #elif !ZG_BUILD_JOINING_TYPE // Only Coord possible. (logicalType == ZG_DEVICETYPE_ROUTER) || #endif (0)) {

logicalType = ZB_INVALID_PARAMETER; SAPI_SendCback(SAPICB_START_CNF, logicalType, 0); } else { logicalType = ZB_SUCCESS; ZDOInitDevice(zgStartDelay); } return; }

Page 232: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

232

void zb_BindDevice ( uint8 create, uint16 commandId, uint8 *pDestination ) { zAddrType_t destination; uint8 ret = ZB_ALREADY_IN_PROGRESS; if ( create ) { if (sapi_bindInProgress == 0xffff) { if ( pDestination ) { destination.addrMode = Addr64Bit; osal_cpyExtAddr( destination.addr.extAddr, pDestination ); ret = APSME_BindRequest( sapi_epDesc.endPoint, commandId, &destination, sapi_epDesc.endPoint ); if ( ret == ZSuccess ) { // Find nwk addr ZDP_NwkAddrReq(pDestination, ZDP_ADDR_REQTYPE_SINGLE, 0, 0 ); osal_start_timerEx( ZDAppTaskID, ZDO_NWK_UPDATE_NV, 250 ); } } else { ret = ZB_INVALID_PARAMETER; destination.addrMode = Addr16Bit; destination.addr.shortAddr = NWK_BROADCAST_SHORTADDR; if ( ZDO_AnyClusterMatches( 1, &commandId, sapi_epDesc.simpleDesc->AppNumOutClusters, sapi_epDesc.simpleDesc->pAppOutClusterList ) ) { // Try to match with a device in the allow bind mode ret = ZDP_MatchDescReq( &destination, NWK_BROADCAST_SHORTADDR, sapi_epDesc.simpleDesc->AppProfId, 1, &commandId, 0, (cId_t *)NULL, 0 ); }

Page 233: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

233

else if ( ZDO_AnyClusterMatches( 1, &commandId, sapi_epDesc.simpleDesc->AppNumInClusters, sapi_epDesc.simpleDesc->pAppInClusterList ) ) { ret = ZDP_MatchDescReq( &destination, NWK_BROADCAST_SHORTADDR, sapi_epDesc.simpleDesc->AppProfId, 0, (cId_t *)NULL, 1, &commandId, 0 ); } if ( ret == ZB_SUCCESS ) { // Set a timer to make sure bind completes #if ( ZG_BUILD_RTR_TYPE ) osal_start_timerEx(sapi_TaskID, ZB_BIND_TIMER, AIB_MaxBindingTime); #else // AIB_MaxBindingTime is not defined for an End Device osal_start_timerEx(sapi_TaskID, ZB_BIND_TIMER, zgApsDefaultMaxBindingTime); #endif sapi_bindInProgress = commandId; return; // dont send cback event } } } SAPI_SendCback( SAPICB_BIND_CNF, ret, commandId ); } else { // create == FALSE // Remove local bindings for the commandId BindingEntry_t *pBind; // Loop through bindings an remove any that match the cluster while ( pBind = bindFind( sapi_epDesc.simpleDesc->EndPoint, commandId, 0 ) ) { bindRemoveEntry(pBind); } osal_start_timerEx( ZDAppTaskID, ZDO_NWK_UPDATE_NV, 250 ); } return; }

Page 234: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

234

uint8 zb_PermitJoiningRequest ( uint16 destination, uint8 timeout ) { #if defined( ZDO_MGMT_PERMIT_JOIN_REQUEST ) zAddrType_t dstAddr; dstAddr.addrMode = Addr16Bit; dstAddr.addr.shortAddr = destination; return( (uint8) ZDP_MgmtPermitJoinReq( &dstAddr, timeout, 0, 0 ) ); #else (void)destination; (void)timeout; return ZUnsupportedMode; #endif }

void zb_AllowBind ( uint8 timeout ) { osal_stop_timerEx(sapi_TaskID, ZB_ALLOW_BIND_TIMER); if ( timeout == 0 ) { afSetMatch(sapi_epDesc.simpleDesc->EndPoint, FALSE); } else { afSetMatch(sapi_epDesc.simpleDesc->EndPoint, TRUE); if ( timeout != 0xFF ) { if ( timeout > 64 ) { timeout = 64; } osal_start_timerEx(sapi_TaskID, ZB_ALLOW_BIND_TIMER, timeout*1000); } } return; }

Page 235: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

235

void zb_SendDataRequest ( uint16 destination, uint16 commandId, uint8 len, uint8 *pData, uint8 handle, uint8 txOptions, uint8 radius ) { afStatus_t status; afAddrType_t dstAddr; txOptions |= AF_DISCV_ROUTE; // Set the destination address if (destination == ZB_BINDING_ADDR) { // Binding dstAddr.addrMode = afAddrNotPresent; } else { // Use short address dstAddr.addr.shortAddr = destination; dstAddr.addrMode = afAddr16Bit; if ( ADDR_NOT_BCAST != NLME_IsAddressBroadcast( destination ) ) { txOptions &= ~AF_ACK_REQUEST; } } dstAddr.panId = 0; // Not an inter-pan message. dstAddr.endPoint = sapi_epDesc.simpleDesc->EndPoint; // Set the endpoint. // Send the message status = AF_DataRequest(&dstAddr, &sapi_epDesc, commandId, len, pData, &handle, txOptions, radius); if (status != afStatus_SUCCESS) { SAPI_SendCback( SAPICB_DATA_CNF, status, handle ); } }

Page 236: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

236

uint8 zb_ReadConfiguration( uint8 configId, uint8 len, void *pValue ) { uint8 size; size = (uint8)osal_nv_item_len( configId ); if ( size > len ) { return ZFailure; } else { return( osal_nv_read(configId, 0, size, pValue) ); } } uint8 zb_WriteConfiguration( uint8 configId, uint8 len, void *pValue )

{ return( osal_nv_write(configId, 0, len, pValue) ); }

void zb_GetDeviceInfo ( uint8 param, void *pValue ) { switch(param) { case ZB_INFO_DEV_STATE: osal_memcpy(pValue, &devState, sizeof(uint8)); break; case ZB_INFO_IEEE_ADDR: osal_memcpy(pValue, &aExtendedAddress, Z_EXTADDR_LEN); break; case ZB_INFO_SHORT_ADDR: osal_memcpy(pValue, &_NIB.nwkDevAddress, sizeof(uint16)); break; case ZB_INFO_PARENT_SHORT_ADDR: osal_memcpy(pValue, &_NIB.nwkCoordAddress, sizeof(uint16)); break; case ZB_INFO_PARENT_IEEE_ADDR: osal_memcpy(pValue, &_NIB.nwkCoordExtAddress, Z_EXTADDR_LEN); break; case ZB_INFO_CHANNEL: osal_memcpy(pValue, &_NIB.nwkLogicalChannel, sizeof(uint8)); break;

case ZB_INFO_PAN_ID: osal_memcpy(pValue, &_NIB.nwkPanId, sizeof(uint16)); break; case ZB_INFO_EXT_PAN_ID: osal_memcpy(pValue, &_NIB.extendedPANID, Z_EXTADDR_LEN); break; } }

Page 237: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

237

void zb_FindDeviceRequest( uint8 searchType, void *searchKey ) { if (searchType == ZB_IEEE_SEARCH) { ZDP_NwkAddrReq((uint8*) searchKey, ZDP_ADDR_REQTYPE_SINGLE, 0, 0 ); } }

void SAPI_StartConfirm( uint8 status ) { #if defined ( MT_SAPI_CB_FUNC ) /* First check if MT has subscribed for this callback. If so , pass it as a event to MonitorTest and return control to calling function after that */ if ( SAPICB_CHECK( SPI_CB_SAPI_START_CNF ) ) { zb_MTCallbackStartConfirm( status ); } else #endif //MT_SAPI_CB_FUNC { #if ( SAPI_CB_FUNC ) zb_StartConfirm( status ); #endif } }

void SAPI_SendDataConfirm( uint8 handle, uint8 status ) { #if defined ( MT_SAPI_CB_FUNC ) /* First check if MT has subscribed for this callback. If so , pass it as a event to MonitorTest and return control to calling function after that */ if ( SAPICB_CHECK( SPI_CB_SAPI_SEND_DATA_CNF ) ) { zb_MTCallbackSendDataConfirm( handle, status ); } else #endif //MT_SAPI_CB_FUNC { #if ( SAPI_CB_FUNC ) zb_SendDataConfirm( handle, status ); #endif } }

Page 238: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

238

void SAPI_BindConfirm( uint16 commandId, uint8 status ) { #if defined ( MT_SAPI_CB_FUNC ) /* First check if MT has subscribed for this callback. If so , pass it as a event to MonitorTest and return control to calling function after that */ if ( SAPICB_CHECK( SPI_CB_SAPI_BIND_CNF ) ) { zb_MTCallbackBindConfirm( commandId, status ); } else #endif //MT_SAPI_CB_FUNC { #if ( SAPI_CB_FUNC ) zb_BindConfirm( commandId, status ); #endif } } void SAPI_AllowBindConfirm( uint16 source )

{ #if defined ( MT_SAPI_CB_FUNC ) /* First check if MT has subscribed for this callback. If so , pass it as a event to MonitorTest and return control to calling function after that */ if ( SAPICB_CHECK( SPI_CB_SAPI_ALLOW_BIND_CNF ) ) { zb_MTCallbackAllowBindConfirm( source ); } else #endif //MT_SAPI_CB_FUNC { #if ( SAPI_CB_FUNC ) zb_AllowBindConfirm( source ); #endif } }

Page 239: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

239

void SAPI_FindDeviceConfirm( uint8 searchType, uint8 *searchKey, uint8 *result ) { #if defined ( MT_SAPI_CB_FUNC ) /* First check if MT has subscribed for this callback. If so , pass it as a event to MonitorTest and return control to calling function after that */ if ( SAPICB_CHECK( SPI_CB_SAPI_FIND_DEV_CNF ) ) { zb_MTCallbackFindDeviceConfirm( searchType, searchKey, result ); } else #endif //MT_SAPI_CB_FUNC { #if ( SAPI_CB_FUNC ) zb_FindDeviceConfirm( searchType, searchKey, result ); #endif } }

void SAPI_ReceiveDataIndication( uint16 source, uint16 command, uint16 len, uint8 *pData ) { #if defined ( MT_SAPI_CB_FUNC ) /* First check if MT has subscribed for this callback. If so , pass it as a event to MonitorTest and return control to calling function after that */ if ( SAPICB_CHECK( SPI_CB_SAPI_RCV_DATA_IND ) ) { zb_MTCallbackReceiveDataIndication( source, command, len, pData ); } else #endif //MT_SAPI_CB_FUNC { #if ( SAPI_CB_FUNC ) zb_ReceiveDataIndication( source, command, len, pData ); #endif } }

Page 240: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

240

UINT16 SAPI_ProcessEvent( byte task_id, UINT16 events ) { osal_event_hdr_t *pMsg; afIncomingMSGPacket_t *pMSGpkt; afDataConfirm_t *pDataConfirm; if ( events & SYS_EVENT_MSG ) { pMsg = (osal_event_hdr_t *) osal_msg_receive( task_id ); while ( pMsg ) { switch ( pMsg->event ) { case ZDO_CB_MSG: SAPI_ProcessZDOMsgs( (zdoIncomingMsg_t *)pMsg ); break; case AF_DATA_CONFIRM_CMD: // This message is received as a confirmation of a data packet sent. // The status is of ZStatus_t type [defined in ZComDef.h] // The message fields are defined in AF.h pDataConfirm = (afDataConfirm_t *) pMsg; SAPI_SendDataConfirm( pDataConfirm->transID, pDataConfirm->hdr.status ); break; case AF_INCOMING_MSG_CMD: pMSGpkt = (afIncomingMSGPacket_t *) pMsg; SAPI_ReceiveDataIndication( pMSGpkt->srcAddr.addr.shortAddr, pMSGpkt->clusterId, pMSGpkt->cmd.DataLength, pMSGpkt->cmd.Data); break;

Page 241: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

241

case ZDO_STATE_CHANGE: // If the device has started up, notify the application if (pMsg->status == DEV_END_DEVICE || pMsg->status == DEV_ROUTER || pMsg->status == DEV_ZB_COORD ) { SAPI_StartConfirm( ZB_SUCCESS ); } else if (pMsg->status == DEV_HOLD || pMsg->status == DEV_INIT) { SAPI_StartConfirm( ZB_INIT ); } break; case ZDO_MATCH_DESC_RSP_SENT: SAPI_AllowBindConfirm( ((ZDO_MatchDescRspSent_t *)pMsg)->nwkAddr ); break; case KEY_CHANGE: #if ( SAPI_CB_FUNC ) zb_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys ); #endif break; case SAPICB_DATA_CNF: SAPI_SendDataConfirm( (uint8)((sapi_CbackEvent_t *)pMsg)->data, ((sapi_CbackEvent_t *)pMsg)->hdr.status ); break; case SAPICB_BIND_CNF: SAPI_BindConfirm( ((sapi_CbackEvent_t *)pMsg)->data, ((sapi_CbackEvent_t *)pMsg)->hdr.status ); break;

Page 242: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

242

case SAPICB_START_CNF: SAPI_StartConfirm( ((sapi_CbackEvent_t *)pMsg)->hdr.status ); break; default: // User messages should be handled by user or passed to the application if ( pMsg->event >= ZB_USER_MSG ) { } break; } // Release the memory osal_msg_deallocate( (uint8 *) pMsg ); // Next pMsg = (osal_event_hdr_t *) osal_msg_receive( task_id ); } // Return unprocessed events return (events ^ SYS_EVENT_MSG); } if ( events & ZB_ALLOW_BIND_TIMER ) { afSetMatch(sapi_epDesc.simpleDesc->EndPoint, FALSE); return (events ^ ZB_ALLOW_BIND_TIMER); } if ( events & ZB_BIND_TIMER ) { // Send bind confirm callback to application SAPI_BindConfirm( sapi_bindInProgress, ZB_TIMEOUT ); sapi_bindInProgress = 0xffff; return (events ^ ZB_BIND_TIMER); }

Page 243: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

243

if ( events & ZB_ENTRY_EVENT ) { uint8 startOptions; // Give indication to application of device startup #if ( SAPI_CB_FUNC ) zb_HandleOsalEvent( ZB_ENTRY_EVENT ); #endif // LED off cancels HOLD_AUTO_START blink set in the stack HalLedSet (HAL_LED_4, HAL_LED_MODE_OFF); zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions ); if ( startOptions & ZCD_STARTOPT_AUTO_START ) { zb_StartRequest(); } else { // blink leds and wait for external input to config and restart HalLedBlink(HAL_LED_2, 0, 50, 500); } return (events ^ ZB_ENTRY_EVENT ); } // This must be the last event to be processed if ( events & ( ZB_USER_EVENTS ) ) { // User events are passed to the application #if ( SAPI_CB_FUNC ) zb_HandleOsalEvent( events ); #endif // Do not return here, return 0 later } // Discard unknown events return 0; }

Page 244: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

244

void SAPI_ProcessZDOMsgs( zdoIncomingMsg_t *inMsg ) { switch ( inMsg->clusterID ) { case NWK_addr_rsp: { // Send find device callback to application ZDO_NwkIEEEAddrResp_t *pNwkAddrRsp = ZDO_ParseAddrRsp( inMsg ); SAPI_FindDeviceConfirm( ZB_IEEE_SEARCH, (uint8*)&pNwkAddrRsp->nwkAddr, pNwkAddrRsp->extAddr ); } break; case Match_Desc_rsp: { zAddrType_t dstAddr; ZDO_ActiveEndpointRsp_t *pRsp = ZDO_ParseEPListRsp( inMsg ); if ( sapi_bindInProgress != 0xffff ) { dstAddr.addrMode = Addr16Bit; // Create a binding table entry dstAddr.addr.shortAddr = pRsp->nwkAddr; if ( APSME_BindRequest( sapi_epDesc.simpleDesc->EndPoint, sapi_bindInProgress, &dstAddr, pRsp->epList[0] ) == ZSuccess ) { osal_stop_timerEx(sapi_TaskID, ZB_BIND_TIMER); osal_start_timerEx( ZDAppTaskID, ZDO_NWK_UPDATE_NV, 250 ); ZDP_IEEEAddrReq( pRsp->nwkAddr, ZDP_ADDR_REQTYPE_SINGLE, 0, 0 ); // Find IEEE addr #if defined ( MT_SAPI_CB_FUNC ) zb_MTCallbackBindConfirm( sapi_bindInProgress, ZB_SUCCESS ); #endif #if ( SAPI_CB_FUNC ) // Send bind confirm callback to application zb_BindConfirm( sapi_bindInProgress, ZB_SUCCESS ); #endif sapi_bindInProgress = 0xffff; } } } break; } }

Page 245: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

245

void SAPI_Init( byte task_id ) { sapi_TaskID = task_id; sapi_bindInProgress = 0xffff; sapi_epDesc.task_id = &sapi_TaskID; sapi_epDesc.endPoint = 0; #if ( SAPI_CB_FUNC ) sapi_epDesc.endPoint = zb_SimpleDesc.EndPoint; sapi_epDesc.task_id = &sapi_TaskID; sapi_epDesc.simpleDesc = (SimpleDescriptionFormat_t *)&zb_SimpleDesc; sapi_epDesc.latencyReq = noLatencyReqs; // Register the endpoint/interface description with the AF afRegister( &sapi_epDesc ); #endif // Turn off match descriptor response by default afSetMatch(sapi_epDesc.simpleDesc->EndPoint, FALSE); // Register callback evetns from the ZDApp ZDO_RegisterForZDOMsg( sapi_TaskID, NWK_addr_rsp ); ZDO_RegisterForZDOMsg( sapi_TaskID, Match_Desc_rsp ); #if ( SAPI_CB_FUNC ) #if (defined HAL_KEY) && (HAL_KEY == TRUE) // Register for HAL events RegisterForKeys( sapi_TaskID ); if ( HalKeyRead () == HAL_KEY_SW_5) { // If SW5 is pressed and held while powerup, force auto-start and nv-restore off and reset uint8 startOptions = ZCD_STARTOPT_CLEAR_STATE | ZCD_STARTOPT_CLEAR_CONFIG; zb_WriteConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions ); zb_SystemReset(); } #endif // HAL_KEY

// Set an event to start the application osal_set_event(task_id, ZB_ENTRY_EVENT); #endif }

Page 246: [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware

246

void SAPI_SendCback( uint8 event, uint8 status, uint16 data ) { sapi_CbackEvent_t *pMsg; pMsg = (sapi_CbackEvent_t *)osal_msg_allocate( sizeof(sapi_CbackEvent_t) ); if( pMsg ) { pMsg->hdr.event = event; pMsg->hdr.status = status; pMsg->data = data; osal_msg_send( sapi_TaskID, (uint8 *)pMsg ); } }

#if OSAL_SAPI 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++ ); ZDApp_Init( taskID++ ); SAPI_Init( taskID ); } #endif