SlideShare a Scribd company logo
Chien-Jung Li
Apr. 2014
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
SmartRF05EB牛刀小試
4
準備好你的硬體
5
安裝相關開發軟體
 …hiver.teamProject Repository0_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.teamProject Repository0_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)
6
SmartRF05EB測試程式 (I) – PER Test
 用IAR開啟
C:Texas Instrumentsswrc135b - cc2530 SW exampleidecc2530_sw_examples.eww
 我們接下來要使用官方的「封包錯誤率測試應用程式」
來測試一下SmartRF05EB
7
SmartRF05EB測試程式 (II) – PER Test
 先Rebuild試試看,你會發現錯誤
 進option換掉linker檔
再rebuild一次
C:Program Files (x86)IAR Systems
Embedded Workbench 6.08051configdevices
Texas Instrumentslnk51ew_cc2530F256.xcl
8
SmartRF05EB測試程式 (III) – PER Test
 將兩片板子分別接到host,
然後下載
 使用板子上的Button1與
Joystick 將 一 片 板 子 設 為
Transmitter,另一片設為
Receiver 。 發 射 端 按 下
Joystick中鍵後即進入PER
測試模式
 RSSI是接收功率強度
 接收板可拿到遠處觀察
範例應用程式:SampleApp
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
相連則為
協調器
11
SampleApp Project – 使用DemoEB
 C:Texas InstrumentsZStack-CC2530-
2.5.1aProjectszstackSamplesSampleAppCC2530DBSampleApp.eww
分別燒錄兩塊板子
1片設為Coordinator
1片設為Router
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 ************************/
BasicApp
瞭解OSAL的基本運作
14
BasicApp
 目標:
 BasicApp是一個簡化過的應用程式範例,我們先將
無線傳輸功能抽掉,將它當成是一般的嵌入式系統
來使用。
 Firmware內載作業系統抽象層OSAL,我們將學習如
何使用OSAL來協助我們完成應用程式。
 此外,我們還會試著使用HAL API來測試一下硬體。
 將BasicApp_wo_RF.zip解壓縮到
C:Texas InstrumentsZStack-CC2530-2.5.1aProjectszstackSamples
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
16
開發應用程式的三個必要檔案
 以BasicApp為例:
 OSAL_BasicApp.c
 BasicApp.h
 BasicApp.c
1. 程式進入點 main()@ZMain.c
2. 作業系統初始化 osal_init_system()@OSAL.c
3. Tasks/應用程式初始化 OSAL_<AppName>.c
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 );
}
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 */
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;
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
}
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;
}
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;
}
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 )
{
}
}
}
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;
}
}
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 )
{
}
}
}
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 )
{
}
}
}
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);
}
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 );
}
}
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);
}
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);
31
練習6:NV記憶體
 CC2530的記憶體結構
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);
}
33
練習7:設定事件與事件處理器
 打開OSAL API手冊。
 OSAL所提供的API,相當於是PC上的系統呼叫,功能非
常多樣與強大,但有些功能是留給框架使用的,我們
在開發App時,大概沒機會用到。
 OSAL是屬於「事件驅動式」的系統,我們首先試一下
跟App開發有關的「事件設定API」。
 只要可以設定並觸發事件,這一切將變得更有趣。
osal_set_event()
osal_start_timerEx()
osal_start_reload_timer()
osal_stop_timerEx()
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);
}
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);
}
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 {
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()而達到重複設定事件的效果?
請寫一支程式展示效果。
38
練習11:紅綠燈系統
 試試看,參考你之前做好的紅綠燈子系統,你能用
「事件驅動」的方法讓它在BasicApp中運作起來嗎?
BasicApp
OSAL的任務間通訊(IPC)機制
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);
41
有了IPC機制,再多Apps也不怕!
 BasicApp是1個App,對OSAL而言,它是個「以事件驅動
為中心的狀態機 (或許稱為Delegator更貼切)」,其實只
是一個事件處理函式,這個函式就是排給OSAL的task。
 系統若存在2個以上的Apps
BasicApp_ProcessEvent()
BasicApp_HandleKeys()
BasicApp_MessageMSGCB()
42
練習:兩個Apps
 BasicApp:
這個應用程式是一個控制器,它會以IPC的方式傳送訊息(指令或資料)給
系統中的另外一個應用程式CountApp。
 CountApp:
收到START指令後,它會開始計數並在LCD第一行顯示計數值。
收到STOP指令時,CountApp將停止計數,LCD第一行顯示將靜止。
收到WRITE指令後,它會將BasicApp傳過來的字串資料寫到LCD的第二行。
收到CLEAR指令後,它會將LCD的第二行清除。
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
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 );
}
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);
}
... 略 ...
}
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」
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);
}
}
}
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 */
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;
}
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;
}
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;
}
}
組織PAN網路實驗
範例應用程式:FlyApp
53
目標
 接下來的實驗,我們會試著組建一個ZigBee的PAN,然
後一步步地接觸descriptors、application framework、
binding等重要的概念。
 Starting a network
 Routing
 Packet Sniffer
 Descriptors
 Application Framework
 Binding
 ZDO APIs
 Callbacks
 Multiple Endpoints
 Mobility
54
通訊協定層
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可容易找到通道。
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
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
58
 將Automatic Polling功能關掉
為簡化實驗,請先關掉Automatic Polling功能。若這個功能啟用的話,End
Device會週期性地對Coordinator發出data request,這樣會干擾我們在
Sniffer想觀察的東西。所以,請在f8wConfig.cfg中令DPOLL_RATE=0 (ED用)。
 在板子實體上標記它們的角色
用便利貼寫下Coordinator、Router以及End Device,並貼在各塊板子上做
識別。
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
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 );
}
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 );
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;
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
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;
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;
}
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;
}
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;
}
}
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 );
}
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 );
}
}
}
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;
}
}
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.
}
}
72
燒錄Coord, Router, 與 ED
 燒錄Coordinator、Router與End Device
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欄位)。
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去)。
程式碼探索:擴充FlyApp
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) 的結構定義。
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的驅動程式。
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
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秒鐘觸
發一次訊息發送事件。
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;




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;
}


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;
}
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手冊
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
}
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 );
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;
}
}
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
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
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 );
...
}
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 )
91
以Sniffer觀察
Cluster與Binding
範例應用程式:FlyApp
93
目標
 改變Router傳送”Hello World”的週期 (目前為5秒發一次)
 新增一個控制發送週期的cluster (命令)
 使用中央綁定來完成 (centralized binding)
 ZigBee裝置的「網路探索」與「綁定功能」是由兩個跟
使用者App無關的東西來管理:AF與ZDO
為了瞭解裝置是如何探索網路與其他裝置進行溝通,我們必須要
了解AF跟ZDO是如何一起工作來完成綁定。
Coord Router
“Hello World”
ED
ED發訊號以控制Router發送速度
自動綁定
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的功能是需要做一些修改的,
但除非必要最好不要亂改。
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
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
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的訊息發送週期。
98
改變Reporting的週期
 FlyApp原本的程式是ED會週期性地發資料給Router跟
Coord,這個週期是在編譯的時候設定的。
 現在我們要加入程式來允許ED (remote)可以動態地設定
Router (sensor)的reporting時間。為了要達成這個目的,
我們要先了解一種新的訊息類性,稱為Cluster。
 除了第一種綁定的data clusters (RouterCoord與ED
Coord)機制,我們還會加入第二種綁定機制—使用
Centralized binding。我們會自創configuration cluster,
讓 ED 來 控 制 Router 的 reporting 速 度 (End Device 
Router)。
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 );
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
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
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
};
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
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
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來看一下。
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。
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給第一
還有第二的裝置。
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來指示這個程序是成功或是失敗的。
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
}
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並下載
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
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。
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
}
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
}
}
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,你會看到傳
送速率變化。
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)
 續下頁
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()
解析SampleApp
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
120
SampleApp運行的基本流程
SampleApp_Init()
SampleApp.c
osalInitTasks()
OSAL_SampleApp.c
osal_init_system()
OSAL.c
main()
ZMain.c
SampleApp_ProcessEvent()
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 );
}
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 );
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;
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 );


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;




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
}


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;
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;
}

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 );
}
}
}


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.
}
}
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.
}
}
解析SimpleApp
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
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
存在以加入之。
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()以嘗試綁定。
136
使用SAPI建立Private應用
 列出app使用到的物理裝置(溫度感測器, 讀取器等),為
每個裝置指定一個16-bits的device_id。
 為每個裝置規劃16-bits的command_id (cluster id)。
 規劃不同裝置上command_id的方向性。
 將以上資訊填入simple descriptor,並為app指定一個
profile id。
 在app程式中實現每個物理裝置的操作邏輯、以及綁定。
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是該
感測量的數值。
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。
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()
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()
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()
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()
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
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
};
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


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 );
}
}
... 略 ...
}
}


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 )
{
}
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++ = ' ';
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
}
}
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
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 );
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 );
}
}
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();
}
}
... 略 ...
}
}
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 ??
}
}
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;
}
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 );
}
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
}
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
}
ZigBee Cluster Library
160
ZigBee Cluster Library (ZCL)
 ZCL是給public profiles共用的一套指令clusters或cross-
cluster,ZigBee聯盟希望用它來加速廠商開發與維持裝
置間的通用性。
161
使用Cluster以擴充裝置功能
 如果你要做的是 public profile device,那你就需要ZCL。
 如果是private application profiles,ZCL就並非必需。有
很多private application profiles並沒用ZCL或只用到library
中的一些東西而已。
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)。
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
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
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。
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指令。
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:需要資訊的裝置會去
詢問其他裝置的現狀。
 例如某人拿起電視遙控器時,此
時詢問屋外的溫度感測器的溫度
再傳回遙控器上或電視螢幕。
168
屬性的讀寫
 一般而言,表示實際物件狀態的屬性(例如一顆燈的
on/off)、或者感測真實世界的屬性(溫度感測器的溫度
值)是不可以直接寫入的。
 為了讓代表狀態屬性發生作用,必須要使用指令才行,例如用OnOff
指令來toggle一個電燈。
 可以直接寫入ZCL屬性的東西是像「文字說明」的東西,例如寫
入”Kitchen”來表明節點的位置是在廚房。
 ZCL規範清楚指出了那些cluster的屬性是可read、可
write或report。例如OnOff Cluster的OnOff是可read與
report,但沒辦法write進去。
169
ZCL General Clusters
 ZCL裡面有一組general clusters,因為這些cluster會普遍
地存在於每個ZigBee public application profile。
存在於 every device。
用來做網路commission,存在於
most devices。
存在於 some device。
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。
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等。
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 來完成。
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
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
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。
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
實驗: Home Automation Profile
複雜性很高,不過也只是框架運用的觀念
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的實現
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的命令回調函式
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。
181
ZCL的角色
zcl_registerAttrList()
zcl_registerValidateAttrData()
zcl_registerForMsg()
zcl_registerPlugin()
zcl_<appname>_data.c
zcl_<appname>.c
appname_TaskID
zcl_TaskID
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;
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;
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
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
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
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 );
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 );
}
}
SampleLight
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;
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 );
}
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 );
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;
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;
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
}
},
};
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;
};
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
};
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
};
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 );
}
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;
}
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手冊
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;
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 );
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;
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;
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;
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;
SampleSwitch
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;
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 );
}
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 );
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
};
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
};
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 );
}
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;
}
??
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;
}
}
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 );
}

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 );
}
}
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;
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;
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
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;
}
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
附錄:SAPI
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
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;
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 );
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 );
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 );
... 略 ...
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;
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;
}

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 );
}





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;
}



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;
}

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 );
}
}


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;
}
}
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
}
}

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
}
}
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
}
}
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;
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;
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);
}
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;
}
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;
} }
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
}
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
Ad

More Related Content

What's hot (20)

Linux device drivers
Linux device drivers Linux device drivers
Linux device drivers
Emertxe Information Technologies Pvt Ltd
 
Linux I2C
Linux I2CLinux I2C
Linux I2C
KaidenYu
 
100%Kotlin ORM Ktormを試してみた
100%Kotlin ORM Ktormを試してみた100%Kotlin ORM Ktormを試してみた
100%Kotlin ORM Ktormを試してみた
Keita Tsukamoto
 
用Raspberry Pi 學Linux I2C Driver
用Raspberry Pi 學Linux I2C Driver用Raspberry Pi 學Linux I2C Driver
用Raspberry Pi 學Linux I2C Driver
艾鍗科技
 
JVMに裏から手を出す!JVMTIに触れてみよう(オープンソースカンファレンス2020 Online/Hiroshima 講演資料)
JVMに裏から手を出す!JVMTIに触れてみよう(オープンソースカンファレンス2020 Online/Hiroshima 講演資料)JVMに裏から手を出す!JVMTIに触れてみよう(オープンソースカンファレンス2020 Online/Hiroshima 講演資料)
JVMに裏から手を出す!JVMTIに触れてみよう(オープンソースカンファレンス2020 Online/Hiroshima 講演資料)
NTT DATA Technology & Innovation
 
20221226_TITECH_lecture_ishizaki_public.pdf
20221226_TITECH_lecture_ishizaki_public.pdf20221226_TITECH_lecture_ishizaki_public.pdf
20221226_TITECH_lecture_ishizaki_public.pdf
Kazuaki Ishizaki
 
Service Function Chaining with SRv6
Service Function Chaining with SRv6Service Function Chaining with SRv6
Service Function Chaining with SRv6
Ahmed AbdelSalam
 
Linux packet-forwarding
Linux packet-forwardingLinux packet-forwarding
Linux packet-forwarding
Masakazu Asama
 
Hands-on ethernet driver
Hands-on ethernet driverHands-on ethernet driver
Hands-on ethernet driver
SUSE Labs Taipei
 
LLVM Backend の紹介
LLVM Backend の紹介LLVM Backend の紹介
LLVM Backend の紹介
Akira Maruoka
 
Geep networking stack-linuxkernel
Geep networking stack-linuxkernelGeep networking stack-linuxkernel
Geep networking stack-linuxkernel
Kiran Divekar
 
Part-1 : Mastering microcontroller with embedded driver development
Part-1 : Mastering microcontroller with embedded driver development Part-1 : Mastering microcontroller with embedded driver development
Part-1 : Mastering microcontroller with embedded driver development
FastBit Embedded Brain Academy
 
katagaitaictf11_misc_ysk
katagaitaictf11_misc_yskkatagaitaictf11_misc_ysk
katagaitaictf11_misc_ysk
ysk256
 
Container Networking Deep Dive
Container Networking Deep DiveContainer Networking Deep Dive
Container Networking Deep Dive
Hirofumi Ichihara
 
ISP Load Balancing with Mikrotik ECMP
ISP Load Balancing with Mikrotik ECMPISP Load Balancing with Mikrotik ECMP
ISP Load Balancing with Mikrotik ECMP
GLC Networks
 
無線LANデバイスについて(kernelレベル)
無線LANデバイスについて(kernelレベル) 無線LANデバイスについて(kernelレベル)
無線LANデバイスについて(kernelレベル)
Yuki Uchikoba
 
PART-2 : Mastering RTOS FreeRTOS and STM32Fx with Debugging
PART-2 : Mastering RTOS FreeRTOS and STM32Fx with DebuggingPART-2 : Mastering RTOS FreeRTOS and STM32Fx with Debugging
PART-2 : Mastering RTOS FreeRTOS and STM32Fx with Debugging
FastBit Embedded Brain Academy
 
NIDD (Non-IP Data Delivery) のご紹介
NIDD (Non-IP Data Delivery) のご紹介NIDD (Non-IP Data Delivery) のご紹介
NIDD (Non-IP Data Delivery) のご紹介
Device WebAPI Consortium
 
Linux network stack
Linux network stackLinux network stack
Linux network stack
Takuya ASADA
 
Ifupdown2: Network Interface Manager
Ifupdown2: Network Interface ManagerIfupdown2: Network Interface Manager
Ifupdown2: Network Interface Manager
Cumulus Networks
 
100%Kotlin ORM Ktormを試してみた
100%Kotlin ORM Ktormを試してみた100%Kotlin ORM Ktormを試してみた
100%Kotlin ORM Ktormを試してみた
Keita Tsukamoto
 
用Raspberry Pi 學Linux I2C Driver
用Raspberry Pi 學Linux I2C Driver用Raspberry Pi 學Linux I2C Driver
用Raspberry Pi 學Linux I2C Driver
艾鍗科技
 
JVMに裏から手を出す!JVMTIに触れてみよう(オープンソースカンファレンス2020 Online/Hiroshima 講演資料)
JVMに裏から手を出す!JVMTIに触れてみよう(オープンソースカンファレンス2020 Online/Hiroshima 講演資料)JVMに裏から手を出す!JVMTIに触れてみよう(オープンソースカンファレンス2020 Online/Hiroshima 講演資料)
JVMに裏から手を出す!JVMTIに触れてみよう(オープンソースカンファレンス2020 Online/Hiroshima 講演資料)
NTT DATA Technology & Innovation
 
20221226_TITECH_lecture_ishizaki_public.pdf
20221226_TITECH_lecture_ishizaki_public.pdf20221226_TITECH_lecture_ishizaki_public.pdf
20221226_TITECH_lecture_ishizaki_public.pdf
Kazuaki Ishizaki
 
Service Function Chaining with SRv6
Service Function Chaining with SRv6Service Function Chaining with SRv6
Service Function Chaining with SRv6
Ahmed AbdelSalam
 
Linux packet-forwarding
Linux packet-forwardingLinux packet-forwarding
Linux packet-forwarding
Masakazu Asama
 
LLVM Backend の紹介
LLVM Backend の紹介LLVM Backend の紹介
LLVM Backend の紹介
Akira Maruoka
 
Geep networking stack-linuxkernel
Geep networking stack-linuxkernelGeep networking stack-linuxkernel
Geep networking stack-linuxkernel
Kiran Divekar
 
Part-1 : Mastering microcontroller with embedded driver development
Part-1 : Mastering microcontroller with embedded driver development Part-1 : Mastering microcontroller with embedded driver development
Part-1 : Mastering microcontroller with embedded driver development
FastBit Embedded Brain Academy
 
katagaitaictf11_misc_ysk
katagaitaictf11_misc_yskkatagaitaictf11_misc_ysk
katagaitaictf11_misc_ysk
ysk256
 
Container Networking Deep Dive
Container Networking Deep DiveContainer Networking Deep Dive
Container Networking Deep Dive
Hirofumi Ichihara
 
ISP Load Balancing with Mikrotik ECMP
ISP Load Balancing with Mikrotik ECMPISP Load Balancing with Mikrotik ECMP
ISP Load Balancing with Mikrotik ECMP
GLC Networks
 
無線LANデバイスについて(kernelレベル)
無線LANデバイスについて(kernelレベル) 無線LANデバイスについて(kernelレベル)
無線LANデバイスについて(kernelレベル)
Yuki Uchikoba
 
PART-2 : Mastering RTOS FreeRTOS and STM32Fx with Debugging
PART-2 : Mastering RTOS FreeRTOS and STM32Fx with DebuggingPART-2 : Mastering RTOS FreeRTOS and STM32Fx with Debugging
PART-2 : Mastering RTOS FreeRTOS and STM32Fx with Debugging
FastBit Embedded Brain Academy
 
Linux network stack
Linux network stackLinux network stack
Linux network stack
Takuya ASADA
 
Ifupdown2: Network Interface Manager
Ifupdown2: Network Interface ManagerIfupdown2: Network Interface Manager
Ifupdown2: Network Interface Manager
Cumulus Networks
 

Viewers also liked (20)

Multiband Transceivers - [Chapter 1]
Multiband Transceivers - [Chapter 1] Multiband Transceivers - [Chapter 1]
Multiband Transceivers - [Chapter 1]
Simen Li
 
[嵌入式系統] MCS-51 實驗 - 使用 IAR (2)
[嵌入式系統] MCS-51 實驗 - 使用 IAR (2)[嵌入式系統] MCS-51 實驗 - 使用 IAR (2)
[嵌入式系統] MCS-51 實驗 - 使用 IAR (2)
Simen Li
 
[嵌入式系統] MCS-51 實驗 - 使用 IAR (3)
[嵌入式系統] MCS-51 實驗 - 使用 IAR (3)[嵌入式系統] MCS-51 實驗 - 使用 IAR (3)
[嵌入式系統] MCS-51 實驗 - 使用 IAR (3)
Simen Li
 
RF Module Design - [Chapter 4] Transceiver Architecture
RF Module Design - [Chapter 4] Transceiver ArchitectureRF Module Design - [Chapter 4] Transceiver Architecture
RF Module Design - [Chapter 4] Transceiver Architecture
Simen Li
 
Multiband Transceivers - [Chapter 2] Noises and Linearities
Multiband Transceivers - [Chapter 2]  Noises and LinearitiesMultiband Transceivers - [Chapter 2]  Noises and Linearities
Multiband Transceivers - [Chapter 2] Noises and Linearities
Simen Li
 
[嵌入式系統] MCS-51 實驗 - 使用 IAR (1)
[嵌入式系統] MCS-51 實驗 - 使用 IAR (1)[嵌入式系統] MCS-51 實驗 - 使用 IAR (1)
[嵌入式系統] MCS-51 實驗 - 使用 IAR (1)
Simen Li
 
RF Module Design - [Chapter 5] Low Noise Amplifier
RF Module Design - [Chapter 5]  Low Noise AmplifierRF Module Design - [Chapter 5]  Low Noise Amplifier
RF Module Design - [Chapter 5] Low Noise Amplifier
Simen Li
 
Multiband Transceivers - [Chapter 3] Basic Concept of Comm. Systems
Multiband Transceivers - [Chapter 3]  Basic Concept of Comm. SystemsMultiband Transceivers - [Chapter 3]  Basic Concept of Comm. Systems
Multiband Transceivers - [Chapter 3] Basic Concept of Comm. Systems
Simen Li
 
RF Module Design - [Chapter 7] Voltage-Controlled Oscillator
RF Module Design - [Chapter 7] Voltage-Controlled OscillatorRF Module Design - [Chapter 7] Voltage-Controlled Oscillator
RF Module Design - [Chapter 7] Voltage-Controlled Oscillator
Simen Li
 
RF Module Design - [Chapter 3] Linearity
RF Module Design - [Chapter 3]  LinearityRF Module Design - [Chapter 3]  Linearity
RF Module Design - [Chapter 3] Linearity
Simen Li
 
ADF4113 Frequency Synthesizer 驅動程式實作
ADF4113 Frequency Synthesizer 驅動程式實作ADF4113 Frequency Synthesizer 驅動程式實作
ADF4113 Frequency Synthesizer 驅動程式實作
Simen Li
 
RF Module Design - [Chapter 8] Phase-Locked Loops
RF Module Design - [Chapter 8] Phase-Locked LoopsRF Module Design - [Chapter 8] Phase-Locked Loops
RF Module Design - [Chapter 8] Phase-Locked Loops
Simen Li
 
RF Module Design - [Chapter 2] Noises
RF Module Design - [Chapter 2] NoisesRF Module Design - [Chapter 2] Noises
RF Module Design - [Chapter 2] Noises
Simen Li
 
RF Module Design - [Chapter 1] From Basics to RF Transceivers
RF Module Design - [Chapter 1] From Basics to RF TransceiversRF Module Design - [Chapter 1] From Basics to RF Transceivers
RF Module Design - [Chapter 1] From Basics to RF Transceivers
Simen Li
 
Multiband Transceivers - [Chapter 5] Software-Defined Radios
Multiband Transceivers - [Chapter 5]  Software-Defined RadiosMultiband Transceivers - [Chapter 5]  Software-Defined Radios
Multiband Transceivers - [Chapter 5] Software-Defined Radios
Simen Li
 
RF Module Design - [Chapter 6] Power Amplifier
RF Module Design - [Chapter 6]  Power AmplifierRF Module Design - [Chapter 6]  Power Amplifier
RF Module Design - [Chapter 6] Power Amplifier
Simen Li
 
Multiband Transceivers - [Chapter 7] Spec. Table
Multiband Transceivers - [Chapter 7]  Spec. TableMultiband Transceivers - [Chapter 7]  Spec. Table
Multiband Transceivers - [Chapter 7] Spec. Table
Simen Li
 
Multiband Transceivers - [Chapter 6] Multi-mode and Multi-band Transceivers
Multiband Transceivers - [Chapter 6] Multi-mode and Multi-band TransceiversMultiband Transceivers - [Chapter 6] Multi-mode and Multi-band Transceivers
Multiband Transceivers - [Chapter 6] Multi-mode and Multi-band Transceivers
Simen Li
 
Multiband Transceivers - [Chapter 7] Multi-mode/Multi-band GSM/GPRS/TDMA/AMP...
Multiband Transceivers - [Chapter 7]  Multi-mode/Multi-band GSM/GPRS/TDMA/AMP...Multiband Transceivers - [Chapter 7]  Multi-mode/Multi-band GSM/GPRS/TDMA/AMP...
Multiband Transceivers - [Chapter 7] Multi-mode/Multi-band GSM/GPRS/TDMA/AMP...
Simen Li
 
Multiband Transceivers - [Chapter 4] Design Parameters of Wireless Radios
Multiband Transceivers - [Chapter 4] Design Parameters of Wireless RadiosMultiband Transceivers - [Chapter 4] Design Parameters of Wireless Radios
Multiband Transceivers - [Chapter 4] Design Parameters of Wireless Radios
Simen Li
 
Multiband Transceivers - [Chapter 1]
Multiband Transceivers - [Chapter 1] Multiband Transceivers - [Chapter 1]
Multiband Transceivers - [Chapter 1]
Simen Li
 
[嵌入式系統] MCS-51 實驗 - 使用 IAR (2)
[嵌入式系統] MCS-51 實驗 - 使用 IAR (2)[嵌入式系統] MCS-51 實驗 - 使用 IAR (2)
[嵌入式系統] MCS-51 實驗 - 使用 IAR (2)
Simen Li
 
[嵌入式系統] MCS-51 實驗 - 使用 IAR (3)
[嵌入式系統] MCS-51 實驗 - 使用 IAR (3)[嵌入式系統] MCS-51 實驗 - 使用 IAR (3)
[嵌入式系統] MCS-51 實驗 - 使用 IAR (3)
Simen Li
 
RF Module Design - [Chapter 4] Transceiver Architecture
RF Module Design - [Chapter 4] Transceiver ArchitectureRF Module Design - [Chapter 4] Transceiver Architecture
RF Module Design - [Chapter 4] Transceiver Architecture
Simen Li
 
Multiband Transceivers - [Chapter 2] Noises and Linearities
Multiband Transceivers - [Chapter 2]  Noises and LinearitiesMultiband Transceivers - [Chapter 2]  Noises and Linearities
Multiband Transceivers - [Chapter 2] Noises and Linearities
Simen Li
 
[嵌入式系統] MCS-51 實驗 - 使用 IAR (1)
[嵌入式系統] MCS-51 實驗 - 使用 IAR (1)[嵌入式系統] MCS-51 實驗 - 使用 IAR (1)
[嵌入式系統] MCS-51 實驗 - 使用 IAR (1)
Simen Li
 
RF Module Design - [Chapter 5] Low Noise Amplifier
RF Module Design - [Chapter 5]  Low Noise AmplifierRF Module Design - [Chapter 5]  Low Noise Amplifier
RF Module Design - [Chapter 5] Low Noise Amplifier
Simen Li
 
Multiband Transceivers - [Chapter 3] Basic Concept of Comm. Systems
Multiband Transceivers - [Chapter 3]  Basic Concept of Comm. SystemsMultiband Transceivers - [Chapter 3]  Basic Concept of Comm. Systems
Multiband Transceivers - [Chapter 3] Basic Concept of Comm. Systems
Simen Li
 
RF Module Design - [Chapter 7] Voltage-Controlled Oscillator
RF Module Design - [Chapter 7] Voltage-Controlled OscillatorRF Module Design - [Chapter 7] Voltage-Controlled Oscillator
RF Module Design - [Chapter 7] Voltage-Controlled Oscillator
Simen Li
 
RF Module Design - [Chapter 3] Linearity
RF Module Design - [Chapter 3]  LinearityRF Module Design - [Chapter 3]  Linearity
RF Module Design - [Chapter 3] Linearity
Simen Li
 
ADF4113 Frequency Synthesizer 驅動程式實作
ADF4113 Frequency Synthesizer 驅動程式實作ADF4113 Frequency Synthesizer 驅動程式實作
ADF4113 Frequency Synthesizer 驅動程式實作
Simen Li
 
RF Module Design - [Chapter 8] Phase-Locked Loops
RF Module Design - [Chapter 8] Phase-Locked LoopsRF Module Design - [Chapter 8] Phase-Locked Loops
RF Module Design - [Chapter 8] Phase-Locked Loops
Simen Li
 
RF Module Design - [Chapter 2] Noises
RF Module Design - [Chapter 2] NoisesRF Module Design - [Chapter 2] Noises
RF Module Design - [Chapter 2] Noises
Simen Li
 
RF Module Design - [Chapter 1] From Basics to RF Transceivers
RF Module Design - [Chapter 1] From Basics to RF TransceiversRF Module Design - [Chapter 1] From Basics to RF Transceivers
RF Module Design - [Chapter 1] From Basics to RF Transceivers
Simen Li
 
Multiband Transceivers - [Chapter 5] Software-Defined Radios
Multiband Transceivers - [Chapter 5]  Software-Defined RadiosMultiband Transceivers - [Chapter 5]  Software-Defined Radios
Multiband Transceivers - [Chapter 5] Software-Defined Radios
Simen Li
 
RF Module Design - [Chapter 6] Power Amplifier
RF Module Design - [Chapter 6]  Power AmplifierRF Module Design - [Chapter 6]  Power Amplifier
RF Module Design - [Chapter 6] Power Amplifier
Simen Li
 
Multiband Transceivers - [Chapter 7] Spec. Table
Multiband Transceivers - [Chapter 7]  Spec. TableMultiband Transceivers - [Chapter 7]  Spec. Table
Multiband Transceivers - [Chapter 7] Spec. Table
Simen Li
 
Multiband Transceivers - [Chapter 6] Multi-mode and Multi-band Transceivers
Multiband Transceivers - [Chapter 6] Multi-mode and Multi-band TransceiversMultiband Transceivers - [Chapter 6] Multi-mode and Multi-band Transceivers
Multiband Transceivers - [Chapter 6] Multi-mode and Multi-band Transceivers
Simen Li
 
Multiband Transceivers - [Chapter 7] Multi-mode/Multi-band GSM/GPRS/TDMA/AMP...
Multiband Transceivers - [Chapter 7]  Multi-mode/Multi-band GSM/GPRS/TDMA/AMP...Multiband Transceivers - [Chapter 7]  Multi-mode/Multi-band GSM/GPRS/TDMA/AMP...
Multiband Transceivers - [Chapter 7] Multi-mode/Multi-band GSM/GPRS/TDMA/AMP...
Simen Li
 
Multiband Transceivers - [Chapter 4] Design Parameters of Wireless Radios
Multiband Transceivers - [Chapter 4] Design Parameters of Wireless RadiosMultiband Transceivers - [Chapter 4] Design Parameters of Wireless Radios
Multiband Transceivers - [Chapter 4] Design Parameters of Wireless Radios
Simen Li
 
Ad

Similar to [ZigBee 嵌入式系統] ZigBee 應用實作 - 使用 TI Z-Stack Firmware (20)

Metasepi team meeting #6: "Snatch-driven development"
Metasepi team meeting #6: "Snatch-driven development"Metasepi team meeting #6: "Snatch-driven development"
Metasepi team meeting #6: "Snatch-driven development"
Kiwamu Okabe
 
Introduction to Android G Sensor I²C Driver on Android
Introduction to Android G Sensor I²C Driver on AndroidIntroduction to Android G Sensor I²C Driver on Android
Introduction to Android G Sensor I²C Driver on Android
Bo-Yi Wu
 
Labs_BT_20221017.pptx
Labs_BT_20221017.pptxLabs_BT_20221017.pptx
Labs_BT_20221017.pptx
ssuserb4d806
 
Systemtap
SystemtapSystemtap
Systemtap
Feng Yu
 
Node.js basics
Node.js basicsNode.js basics
Node.js basics
Ben Lin
 
Mod06 new development tools
Mod06 new development toolsMod06 new development tools
Mod06 new development tools
Peter Haase
 
Crash_Report_Mechanism_In_Tizen
Crash_Report_Mechanism_In_TizenCrash_Report_Mechanism_In_Tizen
Crash_Report_Mechanism_In_Tizen
Lex Yu
 
Androidaop 170105090257
Androidaop 170105090257Androidaop 170105090257
Androidaop 170105090257
newegg
 
Blogging tizen devlab@seoul ppt - optimization
Blogging   tizen devlab@seoul ppt - optimizationBlogging   tizen devlab@seoul ppt - optimization
Blogging tizen devlab@seoul ppt - optimization
Jin Yoon
 
Qe Reference
Qe ReferenceQe Reference
Qe Reference
Susan Gold
 
Report for weather pi
Report for weather piReport for weather pi
Report for weather pi
Asutosh Hota
 
Virus Bulletin 2015: Exposing Gatekeeper
Virus Bulletin 2015: Exposing GatekeeperVirus Bulletin 2015: Exposing Gatekeeper
Virus Bulletin 2015: Exposing Gatekeeper
Synack
 
Android 5.0 Lollipop platform change investigation report
Android 5.0 Lollipop platform change investigation reportAndroid 5.0 Lollipop platform change investigation report
Android 5.0 Lollipop platform change investigation report
hidenorly
 
Virtual platform
Virtual platformVirtual platform
Virtual platform
sean chen
 
Cell processor lab
Cell processor labCell processor lab
Cell processor lab
coolmirza143
 
망고100 보드로 놀아보자 15
망고100 보드로 놀아보자 15망고100 보드로 놀아보자 15
망고100 보드로 놀아보자 15
종인 전
 
Post Exploitation Bliss: Loading Meterpreter on a Factory iPhone, Black Hat U...
Post Exploitation Bliss: Loading Meterpreter on a Factory iPhone, Black Hat U...Post Exploitation Bliss: Loading Meterpreter on a Factory iPhone, Black Hat U...
Post Exploitation Bliss: Loading Meterpreter on a Factory iPhone, Black Hat U...
Vincenzo Iozzo
 
Db2 For I Parallel Data Load
Db2 For I Parallel Data LoadDb2 For I Parallel Data Load
Db2 For I Parallel Data Load
Thomas Wolfe
 
Android Things in action
Android Things in actionAndroid Things in action
Android Things in action
Stefano Sanna
 
Debugging Python with gdb
Debugging Python with gdbDebugging Python with gdb
Debugging Python with gdb
Roman Podoliaka
 
Metasepi team meeting #6: "Snatch-driven development"
Metasepi team meeting #6: "Snatch-driven development"Metasepi team meeting #6: "Snatch-driven development"
Metasepi team meeting #6: "Snatch-driven development"
Kiwamu Okabe
 
Introduction to Android G Sensor I²C Driver on Android
Introduction to Android G Sensor I²C Driver on AndroidIntroduction to Android G Sensor I²C Driver on Android
Introduction to Android G Sensor I²C Driver on Android
Bo-Yi Wu
 
Labs_BT_20221017.pptx
Labs_BT_20221017.pptxLabs_BT_20221017.pptx
Labs_BT_20221017.pptx
ssuserb4d806
 
Systemtap
SystemtapSystemtap
Systemtap
Feng Yu
 
Node.js basics
Node.js basicsNode.js basics
Node.js basics
Ben Lin
 
Mod06 new development tools
Mod06 new development toolsMod06 new development tools
Mod06 new development tools
Peter Haase
 
Crash_Report_Mechanism_In_Tizen
Crash_Report_Mechanism_In_TizenCrash_Report_Mechanism_In_Tizen
Crash_Report_Mechanism_In_Tizen
Lex Yu
 
Androidaop 170105090257
Androidaop 170105090257Androidaop 170105090257
Androidaop 170105090257
newegg
 
Blogging tizen devlab@seoul ppt - optimization
Blogging   tizen devlab@seoul ppt - optimizationBlogging   tizen devlab@seoul ppt - optimization
Blogging tizen devlab@seoul ppt - optimization
Jin Yoon
 
Report for weather pi
Report for weather piReport for weather pi
Report for weather pi
Asutosh Hota
 
Virus Bulletin 2015: Exposing Gatekeeper
Virus Bulletin 2015: Exposing GatekeeperVirus Bulletin 2015: Exposing Gatekeeper
Virus Bulletin 2015: Exposing Gatekeeper
Synack
 
Android 5.0 Lollipop platform change investigation report
Android 5.0 Lollipop platform change investigation reportAndroid 5.0 Lollipop platform change investigation report
Android 5.0 Lollipop platform change investigation report
hidenorly
 
Virtual platform
Virtual platformVirtual platform
Virtual platform
sean chen
 
Cell processor lab
Cell processor labCell processor lab
Cell processor lab
coolmirza143
 
망고100 보드로 놀아보자 15
망고100 보드로 놀아보자 15망고100 보드로 놀아보자 15
망고100 보드로 놀아보자 15
종인 전
 
Post Exploitation Bliss: Loading Meterpreter on a Factory iPhone, Black Hat U...
Post Exploitation Bliss: Loading Meterpreter on a Factory iPhone, Black Hat U...Post Exploitation Bliss: Loading Meterpreter on a Factory iPhone, Black Hat U...
Post Exploitation Bliss: Loading Meterpreter on a Factory iPhone, Black Hat U...
Vincenzo Iozzo
 
Db2 For I Parallel Data Load
Db2 For I Parallel Data LoadDb2 For I Parallel Data Load
Db2 For I Parallel Data Load
Thomas Wolfe
 
Android Things in action
Android Things in actionAndroid Things in action
Android Things in action
Stefano Sanna
 
Debugging Python with gdb
Debugging Python with gdbDebugging Python with gdb
Debugging Python with gdb
Roman Podoliaka
 
Ad

More from Simen Li (13)

2018 VLSI/CAD Symposium Tutorial (Aug. 7, 20:00-21:00 Room 3F-VII)
2018 VLSI/CAD Symposium Tutorial (Aug. 7, 20:00-21:00 Room 3F-VII)2018 VLSI/CAD Symposium Tutorial (Aug. 7, 20:00-21:00 Room 3F-VII)
2018 VLSI/CAD Symposium Tutorial (Aug. 7, 20:00-21:00 Room 3F-VII)
Simen Li
 
全端物聯網探索之旅 - 重點整理版
全端物聯網探索之旅 - 重點整理版全端物聯網探索之旅 - 重點整理版
全端物聯網探索之旅 - 重點整理版
Simen Li
 
Node.js Event Loop & EventEmitter
Node.js Event Loop & EventEmitterNode.js Event Loop & EventEmitter
Node.js Event Loop & EventEmitter
Simen Li
 
Voltage Controlled Oscillator Design - Short Course at NKFUST, 2013
Voltage Controlled Oscillator Design - Short Course at NKFUST, 2013Voltage Controlled Oscillator Design - Short Course at NKFUST, 2013
Voltage Controlled Oscillator Design - Short Course at NKFUST, 2013
Simen Li
 
Phase-locked Loops - Theory and Design
Phase-locked Loops - Theory and DesignPhase-locked Loops - Theory and Design
Phase-locked Loops - Theory and Design
Simen Li
 
專題製作發想與報告撰寫技巧
專題製作發想與報告撰寫技巧專題製作發想與報告撰寫技巧
專題製作發想與報告撰寫技巧
Simen Li
 
Agilent ADS 模擬手冊 [實習3] 壓控振盪器模擬
Agilent ADS 模擬手冊 [實習3] 壓控振盪器模擬Agilent ADS 模擬手冊 [實習3] 壓控振盪器模擬
Agilent ADS 模擬手冊 [實習3] 壓控振盪器模擬
Simen Li
 
Agilent ADS 模擬手冊 [實習2] 放大器設計
Agilent ADS 模擬手冊 [實習2]  放大器設計Agilent ADS 模擬手冊 [實習2]  放大器設計
Agilent ADS 模擬手冊 [實習2] 放大器設計
Simen Li
 
Agilent ADS 模擬手冊 [實習1] 基本操作與射頻放大器設計
Agilent ADS 模擬手冊 [實習1] 基本操作與射頻放大器設計Agilent ADS 模擬手冊 [實習1] 基本操作與射頻放大器設計
Agilent ADS 模擬手冊 [實習1] 基本操作與射頻放大器設計
Simen Li
 
射頻電子實驗手冊 - [實驗8] 低雜訊放大器模擬
射頻電子實驗手冊 - [實驗8] 低雜訊放大器模擬射頻電子實驗手冊 - [實驗8] 低雜訊放大器模擬
射頻電子實驗手冊 - [實驗8] 低雜訊放大器模擬
Simen Li
 
射頻電子實驗手冊 - [實驗7] 射頻放大器模擬
射頻電子實驗手冊 - [實驗7] 射頻放大器模擬射頻電子實驗手冊 - [實驗7] 射頻放大器模擬
射頻電子實驗手冊 - [實驗7] 射頻放大器模擬
Simen Li
 
射頻電子實驗手冊 [實驗6] 阻抗匹配模擬
射頻電子實驗手冊 [實驗6] 阻抗匹配模擬射頻電子實驗手冊 [實驗6] 阻抗匹配模擬
射頻電子實驗手冊 [實驗6] 阻抗匹配模擬
Simen Li
 
射頻電子實驗手冊 [實驗1 ~ 5] ADS入門, 傳輸線模擬, 直流模擬, 暫態模擬, 交流模擬
射頻電子實驗手冊 [實驗1 ~ 5] ADS入門, 傳輸線模擬, 直流模擬, 暫態模擬, 交流模擬射頻電子實驗手冊 [實驗1 ~ 5] ADS入門, 傳輸線模擬, 直流模擬, 暫態模擬, 交流模擬
射頻電子實驗手冊 [實驗1 ~ 5] ADS入門, 傳輸線模擬, 直流模擬, 暫態模擬, 交流模擬
Simen Li
 
2018 VLSI/CAD Symposium Tutorial (Aug. 7, 20:00-21:00 Room 3F-VII)
2018 VLSI/CAD Symposium Tutorial (Aug. 7, 20:00-21:00 Room 3F-VII)2018 VLSI/CAD Symposium Tutorial (Aug. 7, 20:00-21:00 Room 3F-VII)
2018 VLSI/CAD Symposium Tutorial (Aug. 7, 20:00-21:00 Room 3F-VII)
Simen Li
 
全端物聯網探索之旅 - 重點整理版
全端物聯網探索之旅 - 重點整理版全端物聯網探索之旅 - 重點整理版
全端物聯網探索之旅 - 重點整理版
Simen Li
 
Node.js Event Loop & EventEmitter
Node.js Event Loop & EventEmitterNode.js Event Loop & EventEmitter
Node.js Event Loop & EventEmitter
Simen Li
 
Voltage Controlled Oscillator Design - Short Course at NKFUST, 2013
Voltage Controlled Oscillator Design - Short Course at NKFUST, 2013Voltage Controlled Oscillator Design - Short Course at NKFUST, 2013
Voltage Controlled Oscillator Design - Short Course at NKFUST, 2013
Simen Li
 
Phase-locked Loops - Theory and Design
Phase-locked Loops - Theory and DesignPhase-locked Loops - Theory and Design
Phase-locked Loops - Theory and Design
Simen Li
 
專題製作發想與報告撰寫技巧
專題製作發想與報告撰寫技巧專題製作發想與報告撰寫技巧
專題製作發想與報告撰寫技巧
Simen Li
 
Agilent ADS 模擬手冊 [實習3] 壓控振盪器模擬
Agilent ADS 模擬手冊 [實習3] 壓控振盪器模擬Agilent ADS 模擬手冊 [實習3] 壓控振盪器模擬
Agilent ADS 模擬手冊 [實習3] 壓控振盪器模擬
Simen Li
 
Agilent ADS 模擬手冊 [實習2] 放大器設計
Agilent ADS 模擬手冊 [實習2]  放大器設計Agilent ADS 模擬手冊 [實習2]  放大器設計
Agilent ADS 模擬手冊 [實習2] 放大器設計
Simen Li
 
Agilent ADS 模擬手冊 [實習1] 基本操作與射頻放大器設計
Agilent ADS 模擬手冊 [實習1] 基本操作與射頻放大器設計Agilent ADS 模擬手冊 [實習1] 基本操作與射頻放大器設計
Agilent ADS 模擬手冊 [實習1] 基本操作與射頻放大器設計
Simen Li
 
射頻電子實驗手冊 - [實驗8] 低雜訊放大器模擬
射頻電子實驗手冊 - [實驗8] 低雜訊放大器模擬射頻電子實驗手冊 - [實驗8] 低雜訊放大器模擬
射頻電子實驗手冊 - [實驗8] 低雜訊放大器模擬
Simen Li
 
射頻電子實驗手冊 - [實驗7] 射頻放大器模擬
射頻電子實驗手冊 - [實驗7] 射頻放大器模擬射頻電子實驗手冊 - [實驗7] 射頻放大器模擬
射頻電子實驗手冊 - [實驗7] 射頻放大器模擬
Simen Li
 
射頻電子實驗手冊 [實驗6] 阻抗匹配模擬
射頻電子實驗手冊 [實驗6] 阻抗匹配模擬射頻電子實驗手冊 [實驗6] 阻抗匹配模擬
射頻電子實驗手冊 [實驗6] 阻抗匹配模擬
Simen Li
 
射頻電子實驗手冊 [實驗1 ~ 5] ADS入門, 傳輸線模擬, 直流模擬, 暫態模擬, 交流模擬
射頻電子實驗手冊 [實驗1 ~ 5] ADS入門, 傳輸線模擬, 直流模擬, 暫態模擬, 交流模擬射頻電子實驗手冊 [實驗1 ~ 5] ADS入門, 傳輸線模擬, 直流模擬, 暫態模擬, 交流模擬
射頻電子實驗手冊 [實驗1 ~ 5] ADS入門, 傳輸線模擬, 直流模擬, 暫態模擬, 交流模擬
Simen Li
 

Recently uploaded (20)

DT REPORT by Tech titan GROUP to introduce the subject design Thinking
DT REPORT by Tech titan GROUP to introduce the subject design ThinkingDT REPORT by Tech titan GROUP to introduce the subject design Thinking
DT REPORT by Tech titan GROUP to introduce the subject design Thinking
DhruvChotaliya2
 
Development of MLR, ANN and ANFIS Models for Estimation of PCUs at Different ...
Development of MLR, ANN and ANFIS Models for Estimation of PCUs at Different ...Development of MLR, ANN and ANFIS Models for Estimation of PCUs at Different ...
Development of MLR, ANN and ANFIS Models for Estimation of PCUs at Different ...
Journal of Soft Computing in Civil Engineering
 
Fort night presentation new0903 pdf.pdf.
Fort night presentation new0903 pdf.pdf.Fort night presentation new0903 pdf.pdf.
Fort night presentation new0903 pdf.pdf.
anuragmk56
 
Data Structures_Introduction to algorithms.pptx
Data Structures_Introduction to algorithms.pptxData Structures_Introduction to algorithms.pptx
Data Structures_Introduction to algorithms.pptx
RushaliDeshmukh2
 
introduction to machine learining for beginers
introduction to machine learining for beginersintroduction to machine learining for beginers
introduction to machine learining for beginers
JoydebSheet
 
DATA-DRIVEN SHOULDER INVERSE KINEMATICS YoungBeom Kim1 , Byung-Ha Park1 , Kwa...
DATA-DRIVEN SHOULDER INVERSE KINEMATICS YoungBeom Kim1 , Byung-Ha Park1 , Kwa...DATA-DRIVEN SHOULDER INVERSE KINEMATICS YoungBeom Kim1 , Byung-Ha Park1 , Kwa...
DATA-DRIVEN SHOULDER INVERSE KINEMATICS YoungBeom Kim1 , Byung-Ha Park1 , Kwa...
charlesdick1345
 
Lidar for Autonomous Driving, LiDAR Mapping for Driverless Cars.pptx
Lidar for Autonomous Driving, LiDAR Mapping for Driverless Cars.pptxLidar for Autonomous Driving, LiDAR Mapping for Driverless Cars.pptx
Lidar for Autonomous Driving, LiDAR Mapping for Driverless Cars.pptx
RishavKumar530754
 
Oil-gas_Unconventional oil and gass_reseviours.pdf
Oil-gas_Unconventional oil and gass_reseviours.pdfOil-gas_Unconventional oil and gass_reseviours.pdf
Oil-gas_Unconventional oil and gass_reseviours.pdf
M7md3li2
 
Compiler Design Unit1 PPT Phases of Compiler.pptx
Compiler Design Unit1 PPT Phases of Compiler.pptxCompiler Design Unit1 PPT Phases of Compiler.pptx
Compiler Design Unit1 PPT Phases of Compiler.pptx
RushaliDeshmukh2
 
Smart_Storage_Systems_Production_Engineering.pptx
Smart_Storage_Systems_Production_Engineering.pptxSmart_Storage_Systems_Production_Engineering.pptx
Smart_Storage_Systems_Production_Engineering.pptx
rushikeshnavghare94
 
Smart Storage Solutions.pptx for production engineering
Smart Storage Solutions.pptx for production engineeringSmart Storage Solutions.pptx for production engineering
Smart Storage Solutions.pptx for production engineering
rushikeshnavghare94
 
theory-slides-for react for beginners.pptx
theory-slides-for react for beginners.pptxtheory-slides-for react for beginners.pptx
theory-slides-for react for beginners.pptx
sanchezvanessa7896
 
railway wheels, descaling after reheating and before forging
railway wheels, descaling after reheating and before forgingrailway wheels, descaling after reheating and before forging
railway wheels, descaling after reheating and before forging
Javad Kadkhodapour
 
Explainable-Artificial-Intelligence-XAI-A-Deep-Dive (1).pptx
Explainable-Artificial-Intelligence-XAI-A-Deep-Dive (1).pptxExplainable-Artificial-Intelligence-XAI-A-Deep-Dive (1).pptx
Explainable-Artificial-Intelligence-XAI-A-Deep-Dive (1).pptx
MahaveerVPandit
 
Mathematical foundation machine learning.pdf
Mathematical foundation machine learning.pdfMathematical foundation machine learning.pdf
Mathematical foundation machine learning.pdf
TalhaShahid49
 
Process Parameter Optimization for Minimizing Springback in Cold Drawing Proc...
Process Parameter Optimization for Minimizing Springback in Cold Drawing Proc...Process Parameter Optimization for Minimizing Springback in Cold Drawing Proc...
Process Parameter Optimization for Minimizing Springback in Cold Drawing Proc...
Journal of Soft Computing in Civil Engineering
 
IntroSlides-April-BuildWithAI-VertexAI.pdf
IntroSlides-April-BuildWithAI-VertexAI.pdfIntroSlides-April-BuildWithAI-VertexAI.pdf
IntroSlides-April-BuildWithAI-VertexAI.pdf
Luiz Carneiro
 
ELectronics Boards & Product Testing_Shiju.pdf
ELectronics Boards & Product Testing_Shiju.pdfELectronics Boards & Product Testing_Shiju.pdf
ELectronics Boards & Product Testing_Shiju.pdf
Shiju Jacob
 
RICS Membership-(The Royal Institution of Chartered Surveyors).pdf
RICS Membership-(The Royal Institution of Chartered Surveyors).pdfRICS Membership-(The Royal Institution of Chartered Surveyors).pdf
RICS Membership-(The Royal Institution of Chartered Surveyors).pdf
MohamedAbdelkader115
 
QA/QC Manager (Quality management Expert)
QA/QC Manager (Quality management Expert)QA/QC Manager (Quality management Expert)
QA/QC Manager (Quality management Expert)
rccbatchplant
 
DT REPORT by Tech titan GROUP to introduce the subject design Thinking
DT REPORT by Tech titan GROUP to introduce the subject design ThinkingDT REPORT by Tech titan GROUP to introduce the subject design Thinking
DT REPORT by Tech titan GROUP to introduce the subject design Thinking
DhruvChotaliya2
 
Fort night presentation new0903 pdf.pdf.
Fort night presentation new0903 pdf.pdf.Fort night presentation new0903 pdf.pdf.
Fort night presentation new0903 pdf.pdf.
anuragmk56
 
Data Structures_Introduction to algorithms.pptx
Data Structures_Introduction to algorithms.pptxData Structures_Introduction to algorithms.pptx
Data Structures_Introduction to algorithms.pptx
RushaliDeshmukh2
 
introduction to machine learining for beginers
introduction to machine learining for beginersintroduction to machine learining for beginers
introduction to machine learining for beginers
JoydebSheet
 
DATA-DRIVEN SHOULDER INVERSE KINEMATICS YoungBeom Kim1 , Byung-Ha Park1 , Kwa...
DATA-DRIVEN SHOULDER INVERSE KINEMATICS YoungBeom Kim1 , Byung-Ha Park1 , Kwa...DATA-DRIVEN SHOULDER INVERSE KINEMATICS YoungBeom Kim1 , Byung-Ha Park1 , Kwa...
DATA-DRIVEN SHOULDER INVERSE KINEMATICS YoungBeom Kim1 , Byung-Ha Park1 , Kwa...
charlesdick1345
 
Lidar for Autonomous Driving, LiDAR Mapping for Driverless Cars.pptx
Lidar for Autonomous Driving, LiDAR Mapping for Driverless Cars.pptxLidar for Autonomous Driving, LiDAR Mapping for Driverless Cars.pptx
Lidar for Autonomous Driving, LiDAR Mapping for Driverless Cars.pptx
RishavKumar530754
 
Oil-gas_Unconventional oil and gass_reseviours.pdf
Oil-gas_Unconventional oil and gass_reseviours.pdfOil-gas_Unconventional oil and gass_reseviours.pdf
Oil-gas_Unconventional oil and gass_reseviours.pdf
M7md3li2
 
Compiler Design Unit1 PPT Phases of Compiler.pptx
Compiler Design Unit1 PPT Phases of Compiler.pptxCompiler Design Unit1 PPT Phases of Compiler.pptx
Compiler Design Unit1 PPT Phases of Compiler.pptx
RushaliDeshmukh2
 
Smart_Storage_Systems_Production_Engineering.pptx
Smart_Storage_Systems_Production_Engineering.pptxSmart_Storage_Systems_Production_Engineering.pptx
Smart_Storage_Systems_Production_Engineering.pptx
rushikeshnavghare94
 
Smart Storage Solutions.pptx for production engineering
Smart Storage Solutions.pptx for production engineeringSmart Storage Solutions.pptx for production engineering
Smart Storage Solutions.pptx for production engineering
rushikeshnavghare94
 
theory-slides-for react for beginners.pptx
theory-slides-for react for beginners.pptxtheory-slides-for react for beginners.pptx
theory-slides-for react for beginners.pptx
sanchezvanessa7896
 
railway wheels, descaling after reheating and before forging
railway wheels, descaling after reheating and before forgingrailway wheels, descaling after reheating and before forging
railway wheels, descaling after reheating and before forging
Javad Kadkhodapour
 
Explainable-Artificial-Intelligence-XAI-A-Deep-Dive (1).pptx
Explainable-Artificial-Intelligence-XAI-A-Deep-Dive (1).pptxExplainable-Artificial-Intelligence-XAI-A-Deep-Dive (1).pptx
Explainable-Artificial-Intelligence-XAI-A-Deep-Dive (1).pptx
MahaveerVPandit
 
Mathematical foundation machine learning.pdf
Mathematical foundation machine learning.pdfMathematical foundation machine learning.pdf
Mathematical foundation machine learning.pdf
TalhaShahid49
 
IntroSlides-April-BuildWithAI-VertexAI.pdf
IntroSlides-April-BuildWithAI-VertexAI.pdfIntroSlides-April-BuildWithAI-VertexAI.pdf
IntroSlides-April-BuildWithAI-VertexAI.pdf
Luiz Carneiro
 
ELectronics Boards & Product Testing_Shiju.pdf
ELectronics Boards & Product Testing_Shiju.pdfELectronics Boards & Product Testing_Shiju.pdf
ELectronics Boards & Product Testing_Shiju.pdf
Shiju Jacob
 
RICS Membership-(The Royal Institution of Chartered Surveyors).pdf
RICS Membership-(The Royal Institution of Chartered Surveyors).pdfRICS Membership-(The Royal Institution of Chartered Surveyors).pdf
RICS Membership-(The Royal Institution of Chartered Surveyors).pdf
MohamedAbdelkader115
 
QA/QC Manager (Quality management Expert)
QA/QC Manager (Quality management Expert)QA/QC Manager (Quality management Expert)
QA/QC Manager (Quality management Expert)
rccbatchplant
 

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

  • 1. Chien-Jung Li Apr. 2014 ZigBee應用實作 — 使用TI Z-Stack Firmware
  • 2. 2 大綱  SmartRF05EB牛刀小試:使用PER Test範例展示  SampleApp範例展示  BasicApp:瞭解OSAL的基本運作  BasicApp:OSAL的任務間通訊(IPC)機制  FlyApp:組織PAN網路實驗  擴充FlyApp  FlyApp:Cluster與Binding  解析SampleApp  解析SimpleApp:使用 Simple API  ZigBee Cluster Library
  • 5. 5 安裝相關開發軟體  …hiver.teamProject Repository0_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.teamProject Repository0_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)
  • 6. 6 SmartRF05EB測試程式 (I) – PER Test  用IAR開啟 C:Texas Instrumentsswrc135b - cc2530 SW exampleidecc2530_sw_examples.eww  我們接下來要使用官方的「封包錯誤率測試應用程式」 來測試一下SmartRF05EB
  • 7. 7 SmartRF05EB測試程式 (II) – PER Test  先Rebuild試試看,你會發現錯誤  進option換掉linker檔 再rebuild一次 C:Program Files (x86)IAR Systems Embedded Workbench 6.08051configdevices Texas Instrumentslnk51ew_cc2530F256.xcl
  • 8. 8 SmartRF05EB測試程式 (III) – PER Test  將兩片板子分別接到host, 然後下載  使用板子上的Button1與 Joystick 將 一 片 板 子 設 為 Transmitter,另一片設為 Receiver 。 發 射 端 按 下 Joystick中鍵後即進入PER 測試模式  RSSI是接收功率強度  接收板可拿到遠處觀察
  • 10. 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 相連則為 協調器
  • 11. 11 SampleApp Project – 使用DemoEB  C:Texas InstrumentsZStack-CC2530- 2.5.1aProjectszstackSamplesSampleAppCC2530DBSampleApp.eww 分別燒錄兩塊板子 1片設為Coordinator 1片設為Router
  • 12. 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 ************************/
  • 14. 14 BasicApp  目標:  BasicApp是一個簡化過的應用程式範例,我們先將 無線傳輸功能抽掉,將它當成是一般的嵌入式系統 來使用。  Firmware內載作業系統抽象層OSAL,我們將學習如 何使用OSAL來協助我們完成應用程式。  此外,我們還會試著使用HAL API來測試一下硬體。  將BasicApp_wo_RF.zip解壓縮到 C:Texas InstrumentsZStack-CC2530-2.5.1aProjectszstackSamples
  • 15. 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
  • 16. 16 開發應用程式的三個必要檔案  以BasicApp為例:  OSAL_BasicApp.c  BasicApp.h  BasicApp.c 1. 程式進入點 main()@ZMain.c 2. 作業系統初始化 osal_init_system()@OSAL.c 3. Tasks/應用程式初始化 OSAL_<AppName>.c
  • 17. 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 ); }
  • 18. 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 */
  • 19. 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;
  • 20. 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 }
  • 21. 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; }
  • 22. 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; }
  • 23. 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 ) { } } }
  • 24. 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; } }
  • 25. 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 ) { } } }
  • 26. 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 ) { } } }
  • 27. 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); }
  • 28. 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 ); } }
  • 29. 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); }
  • 30. 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);
  • 32. 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); }
  • 33. 33 練習7:設定事件與事件處理器  打開OSAL API手冊。  OSAL所提供的API,相當於是PC上的系統呼叫,功能非 常多樣與強大,但有些功能是留給框架使用的,我們 在開發App時,大概沒機會用到。  OSAL是屬於「事件驅動式」的系統,我們首先試一下 跟App開發有關的「事件設定API」。  只要可以設定並觸發事件,這一切將變得更有趣。 osal_set_event() osal_start_timerEx() osal_start_reload_timer() osal_stop_timerEx()
  • 34. 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); }
  • 35. 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); }
  • 36. 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 {
  • 37. 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()而達到重複設定事件的效果? 請寫一支程式展示效果。
  • 40. 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);
  • 43. 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
  • 44. 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 ); }
  • 45. 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); } ... 略 ... }
  • 46. 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」
  • 47. 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); } } }
  • 48. 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 */
  • 49. 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; }
  • 50. 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; }
  • 51. 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; } }
  • 53. 53 目標  接下來的實驗,我們會試著組建一個ZigBee的PAN,然 後一步步地接觸descriptors、application framework、 binding等重要的概念。  Starting a network  Routing  Packet Sniffer  Descriptors  Application Framework  Binding  ZDO APIs  Callbacks  Multiple Endpoints  Mobility
  • 55. 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可容易找到通道。
  • 56. 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
  • 57. 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
  • 58. 58  將Automatic Polling功能關掉 為簡化實驗,請先關掉Automatic Polling功能。若這個功能啟用的話,End Device會週期性地對Coordinator發出data request,這樣會干擾我們在 Sniffer想觀察的東西。所以,請在f8wConfig.cfg中令DPOLL_RATE=0 (ED用)。  在板子實體上標記它們的角色 用便利貼寫下Coordinator、Router以及End Device,並貼在各塊板子上做 識別。
  • 59. 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
  • 60. 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 ); }
  • 61. 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 );
  • 62. 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;
  • 63. 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
  • 64. 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;
  • 65. 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; }
  • 66. 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; }
  • 67. 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; } }
  • 68. 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 ); }
  • 69. 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 ); } } }
  • 70. 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; } }
  • 71. 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. } }
  • 72. 72 燒錄Coord, Router, 與 ED  燒錄Coordinator、Router與End Device
  • 73. 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欄位)。
  • 74. 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去)。
  • 76. 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) 的結構定義。
  • 77. 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的驅動程式。
  • 78. 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
  • 79. 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秒鐘觸 發一次訊息發送事件。
  • 80. 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;    
  • 81. 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; }  
  • 82. 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; }
  • 83. 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手冊
  • 84. 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 }
  • 85. 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 );
  • 86. 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; } }
  • 87. 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
  • 88. 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
  • 89. 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 ); ... }
  • 90. 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 )
  • 93. 93 目標  改變Router傳送”Hello World”的週期 (目前為5秒發一次)  新增一個控制發送週期的cluster (命令)  使用中央綁定來完成 (centralized binding)  ZigBee裝置的「網路探索」與「綁定功能」是由兩個跟 使用者App無關的東西來管理:AF與ZDO 為了瞭解裝置是如何探索網路與其他裝置進行溝通,我們必須要 了解AF跟ZDO是如何一起工作來完成綁定。 Coord Router “Hello World” ED ED發訊號以控制Router發送速度 自動綁定
  • 94. 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的功能是需要做一些修改的, 但除非必要最好不要亂改。
  • 95. 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
  • 96. 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
  • 97. 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的訊息發送週期。
  • 98. 98 改變Reporting的週期  FlyApp原本的程式是ED會週期性地發資料給Router跟 Coord,這個週期是在編譯的時候設定的。  現在我們要加入程式來允許ED (remote)可以動態地設定 Router (sensor)的reporting時間。為了要達成這個目的, 我們要先了解一種新的訊息類性,稱為Cluster。  除了第一種綁定的data clusters (RouterCoord與ED Coord)機制,我們還會加入第二種綁定機制—使用 Centralized binding。我們會自創configuration cluster, 讓 ED 來 控 制 Router 的 reporting 速 度 (End Device  Router)。
  • 99. 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 );
  • 100. 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
  • 101. 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
  • 102. 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 };
  • 103. 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
  • 104. 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
  • 105. 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來看一下。
  • 106. 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。
  • 107. 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給第一 還有第二的裝置。
  • 108. 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來指示這個程序是成功或是失敗的。
  • 109. 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 }
  • 110. 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並下載
  • 111. 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
  • 112. 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。
  • 113. 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 }
  • 114. 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 } }
  • 115. 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,你會看到傳 送速率變化。
  • 116. 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)  續下頁
  • 117. 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()
  • 119. 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
  • 121. 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 ); }
  • 122. 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 );
  • 123. 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;
  • 124. 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 );  
  • 125. 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;    
  • 126. 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 }  
  • 127. 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;
  • 128. 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; } 
  • 129. 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 ); } } }  
  • 130. 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. } }
  • 131. 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. } }
  • 133. 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
  • 134. 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 存在以加入之。
  • 135. 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()以嘗試綁定。
  • 136. 136 使用SAPI建立Private應用  列出app使用到的物理裝置(溫度感測器, 讀取器等),為 每個裝置指定一個16-bits的device_id。  為每個裝置規劃16-bits的command_id (cluster id)。  規劃不同裝置上command_id的方向性。  將以上資訊填入simple descriptor,並為app指定一個 profile id。  在app程式中實現每個物理裝置的操作邏輯、以及綁定。
  • 137. 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是該 感測量的數值。
  • 138. 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。
  • 139. 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()
  • 140. 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()
  • 141. 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()
  • 142. 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()
  • 143. 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
  • 144. 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 };
  • 145. 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  
  • 146. 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 ); } } ... 略 ... } }  
  • 147. 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 ) { }
  • 148. 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++ = ' ';
  • 149. 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 } }
  • 150. 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
  • 151. 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 );
  • 152. 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 ); } }
  • 153. 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(); } } ... 略 ... } }
  • 154. 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 ?? } }
  • 155. 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; }
  • 156. 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 ); }
  • 157. 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 }
  • 158. 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 }
  • 160. 160 ZigBee Cluster Library (ZCL)  ZCL是給public profiles共用的一套指令clusters或cross- cluster,ZigBee聯盟希望用它來加速廠商開發與維持裝 置間的通用性。
  • 161. 161 使用Cluster以擴充裝置功能  如果你要做的是 public profile device,那你就需要ZCL。  如果是private application profiles,ZCL就並非必需。有 很多private application profiles並沒用ZCL或只用到library 中的一些東西而已。
  • 162. 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)。
  • 163. 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
  • 164. 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
  • 165. 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。
  • 166. 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指令。
  • 167. 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:需要資訊的裝置會去 詢問其他裝置的現狀。  例如某人拿起電視遙控器時,此 時詢問屋外的溫度感測器的溫度 再傳回遙控器上或電視螢幕。
  • 168. 168 屬性的讀寫  一般而言,表示實際物件狀態的屬性(例如一顆燈的 on/off)、或者感測真實世界的屬性(溫度感測器的溫度 值)是不可以直接寫入的。  為了讓代表狀態屬性發生作用,必須要使用指令才行,例如用OnOff 指令來toggle一個電燈。  可以直接寫入ZCL屬性的東西是像「文字說明」的東西,例如寫 入”Kitchen”來表明節點的位置是在廚房。  ZCL規範清楚指出了那些cluster的屬性是可read、可 write或report。例如OnOff Cluster的OnOff是可read與 report,但沒辦法write進去。
  • 169. 169 ZCL General Clusters  ZCL裡面有一組general clusters,因為這些cluster會普遍 地存在於每個ZigBee public application profile。 存在於 every device。 用來做網路commission,存在於 most devices。 存在於 some device。
  • 170. 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。
  • 171. 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等。
  • 172. 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 來完成。
  • 173. 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
  • 174. 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
  • 175. 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。
  • 176. 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
  • 177. 實驗: Home Automation Profile 複雜性很高,不過也只是框架運用的觀念
  • 178. 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的實現
  • 179. 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的命令回調函式
  • 180. 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。
  • 182. 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;
  • 183. 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;
  • 184. 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
  • 185. 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
  • 186. 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
  • 187. 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 );
  • 188. 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 ); } }
  • 190. 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;
  • 191. 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 ); }
  • 192. 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 );
  • 193. 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;
  • 194. 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;
  • 195. 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 } }, };
  • 196. 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; };
  • 197. 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 };
  • 198. 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 };
  • 199. 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 ); }
  • 200. 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; }
  • 201. 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手冊
  • 202. 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;
  • 203. 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 );
  • 204. 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;
  • 205. 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;
  • 206. 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;
  • 207. 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;
  • 209. 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;
  • 210. 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 ); }
  • 211. 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 );
  • 212. 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 };
  • 213. 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 };
  • 214. 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 ); }
  • 215. 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; } ??
  • 216. 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; } }
  • 217. 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 ); } 
  • 218. 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 ); } }
  • 219. 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;
  • 220. 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;
  • 221. 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
  • 222. 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; }
  • 223. 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
  • 225. 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
  • 226. 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;
  • 227. 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 );
  • 228. 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 );
  • 229. 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 ); ... 略 ...
  • 230. 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;
  • 231. 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; } 
  • 232. 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 ); }     
  • 233. 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; }   
  • 234. 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; } 
  • 235. 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 ); } }  
  • 236. 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; } }
  • 237. 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 } } 
  • 238. 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 } }
  • 239. 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 } }
  • 240. 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;
  • 241. 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;
  • 242. 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); }
  • 243. 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; }
  • 244. 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; } }
  • 245. 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 }
  • 246. 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