我一直觉得官方样例,通用性很强,其 代码结构清晰 是我最看好的一点。本文将介绍样例结构。着重解释无线传输如何使用。
- 样例结构
- 各部分功能演示性编程
应用层涉及到的文件全部包括在了APP文件夹内
下面按顺序介绍代码结构与功能(头文件和声明不再多说)
/*********************************************************************
* GLOBAL VARIABLES
*/
// This list should be filled with Application specific Cluster IDs.
const cId_t GenericApp_ClusterList[GENERICAPP_MAX_CLUSTERS] =
{
GENERICAPP_CLUSTERID
};
const SimpleDescriptionFormat_t GenericApp_SimpleDesc =
{
GENERICAPP_ENDPOINT, // int Endpoint;
GENERICAPP_PROFID, // uint16 AppProfId[2];
GENERICAPP_DEVICEID, // uint16 AppDeviceId[2];
GENERICAPP_DEVICE_VERSION, // int AppDevVer:4;
GENERICAPP_FLAGS, // int AppFlags:4;
GENERICAPP_MAX_CLUSTERS, // byte AppNumInClusters;
(cId_t *)GenericApp_ClusterList, // byte *pAppInClusterList;
GENERICAPP_MAX_CLUSTERS, // byte AppNumInClusters;
(cId_t *)GenericApp_ClusterList // byte *pAppInClusterList;
};
// This is the Endpoint/Interface description. It is defined here, but
// filled-in in GenericApp_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 GenericApp_epDesc;
开头的 GLOBAL VARIABLES 下使用const方法定义了 GenericApp_ClusterList[GENERICAPP_MAX_CLUSTERS] 、GenericApp_SimpleDesc 、GenericApp_epDesc 两个明显和无线传输相关的结构体和一个两字节的数组,下面是这几个变量的具体原形(关于const、define和typedef三者在编译时候的区别,请另行搜索)
下面这段取自AF.h 来展示上述const常量的原形。
typedef uint16 cId_t;
// Simple Description Format Structure
typedef struct
{
byte EndPoint;
uint16 AppProfId;
uint16 AppDeviceId;
byte AppDevVer:4;
byte Reserved:4; // AF_V1_SUPPORT uses for AppFlags:4.
byte AppNumInClusters;
cId_t *pAppInClusterList;
byte AppNumOutClusters;
cId_t *pAppOutClusterList;
} SimpleDescriptionFormat_t;
// Endpoint Table - this table is the device description
// or application registration.
// There will be one entry in this table for every
// endpoint defined.
typedef struct
{
byte endPoint;
byte *task_id; // Pointer to location of the Application task ID.
SimpleDescriptionFormat_t *simpleDesc;
afNetworkLatencyReq_t latencyReq;
} endPointDesc_t;
typedef enum
{
noLatencyReqs,
fastBeacons,
slowBeacons
} afNetworkLatencyReq_t;
这三个变量是应用层关于无线传输的重要参数
- 数组里存放了命令簇ID(实质是一堆你自己定义的纯数字,用数字抽象命令),里面的英文,实际是包装数字的宏定义方便理解。命令簇ID是用来在传输的过程中确认输入命令和输出命令的对应情况,在框架下可以通过命令簇来区分命令。
- 简单描述结构体(SimpleDescriptionFormat_t)用来存储关于任务端点号、应用层设备ID、一些协议版本号、输入命令簇、输出命令簇相关信息,这里简单略过,只要知道这个结构体描述了一堆无线传输需要的参数,作用类似 身份信息。
- 端点描述结构体,包含了端点号、任务号和上面身份结构体的全部内容、一个枚举类型。作用是在简单描述后进一步确认具体的设备身份信息
- 这些参数和结构体设计、很多时候会看起来多此一举,然而具备一些计算机网络相关专业知识后就能知道,协议在设计时会考虑很多因素,来保证传输的质量、效率和安全。接下去会专门开一文讲述这些参数的具体含义和作用以及在整个协议中扮演的角色。
/*********************************************************************
* LOCAL VARIABLES
*/
byte GenericApp_TaskID; // Task ID for internal task/event processing
// This variable will be received when
// GenericApp_Init() is called.
devStates_t GenericApp_NwkState;
byte GenericApp_TransID; // This is the unique message ID (counter)
afAddrType_t GenericApp_DstAddr;
接下去标识为本地值的定义,下属变量和结构体分别为,本自定义任务的ID号、自组网所需设备信息、传输ID、无线传输方式。这四个变量最需要关心的,就是无线传输方式结构体。这个结构体决定了组网后信息传输是组播、点播、广播还是绑定。
结构体分别来自ZDApp和AF
typedef enum
{
DEV_HOLD, // Initialized - not started automatically
DEV_INIT, // Initialized - not connected to anything
DEV_NWK_DISC, // Discovering PAN's to join
DEV_NWK_JOINING, // Joining a PAN
DEV_NWK_REJOIN, // ReJoining a PAN, only for end devices
DEV_END_DEVICE_UNAUTH, // Joined but not yet authenticated by trust center
DEV_END_DEVICE, // Started as device after authentication
DEV_ROUTER, // Device joined, authenticated and is a router
DEV_COORD_STARTING, // Started as Zigbee Coordinator
DEV_ZB_COORD, // Started as Zigbee Coordinator
DEV_NWK_ORPHAN // Device has lost information about its parent..
} devStates_t;
typedef struct
{
union
{
uint16 shortAddr;
ZLongAddr_t extAddr;
} addr;
afAddrMode_t addrMode;
byte endPoint;
uint16 panId; // used for the INTER_PAN feature
} afAddrType_t;
typedef enum
{
afAddrNotPresent = AddrNotPresent,
afAddr16Bit = Addr16Bit,
afAddr64Bit = Addr64Bit,
afAddrGroup = AddrGroup,
afAddrBroadcast = AddrBroadcast
} afAddrMode_t;
/*********************************************************************
* PUBLIC FUNCTIONS
*/
/*********************************************************************
* @fn GenericApp_Init
*
* @brief Initialization function for the Generic 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 GenericApp_Init( byte task_id )
{
GenericApp_TaskID = task_id;
GenericApp_NwkState = DEV_INIT;
GenericApp_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().
GenericApp_DstAddr.addrMode = (afAddrMode_t)AddrNotPresent;
GenericApp_DstAddr.endPoint = 0;
GenericApp_DstAddr.addr.shortAddr = 0;
// Fill out the endpoint description.
GenericApp_epDesc.endPoint = GENERICAPP_ENDPOINT;
GenericApp_epDesc.task_id = &GenericApp_TaskID;
GenericApp_epDesc.simpleDesc
= (SimpleDescriptionFormat_t *)&GenericApp_SimpleDesc;
GenericApp_epDesc.latencyReq = noLatencyReqs;
// Register the endpoint description with the AF
afRegister( &GenericApp_epDesc );
// Register for all key events - This app will handle all key events
RegisterForKeys( GenericApp_TaskID );
// Update the display
#if defined ( LCD_SUPPORTED )
HalLcdWriteString( "GenericApp", HAL_LCD_LINE_1 );
#endif
ZDO_RegisterForZDOMsg( GenericApp_TaskID, End_Device_Bind_rsp );
ZDO_RegisterForZDOMsg( GenericApp_TaskID, Match_Desc_rsp );
}
/*********************************************************************
* @fn GenericApp_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 GenericApp_ProcessEvent( byte 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
/*上述定义不详解,将此函数放置在定义的指针域内,OSAL在轮询中触发相应taskID的相应事件,就会反馈一个对应的已经注册的标志位,下面的判断语句,就是对一些反馈做出相应动作。*/
if ( events & SYS_EVENT_MSG )//如果事件是系统消息
{
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( GenericApp_TaskID );//取出对应ID的消息
while ( MSGpkt )//当消息属于这个任务的时候
{
switch ( MSGpkt->hdr.event )//取出消息中的事件类型
{
case ZDO_CB_MSG://事件为绑定,执行对应绑定回调函数
GenericApp_ProcessZDOMsgs( (zdoIncomingMsg_t *)MSGpkt );
break;
case KEY_CHANGE://事件为按键改变,执行按键回调
GenericApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );
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
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://接收到无线传输消息,执行接收处理回调函数
GenericApp_MessageMSGCB( MSGpkt );
break;
case ZDO_STATE_CHANGE://组网情况改变
GenericApp_NwkState = (devStates_t)(MSGpkt->hdr.status);
if ( (GenericApp_NwkState == DEV_ZB_COORD)
|| (GenericApp_NwkState == DEV_ROUTER)
|| (GenericApp_NwkState == DEV_END_DEVICE) )
{
// Start sending "the" message in a regular interval.
osal_start_timerEx( GenericApp_TaskID,
GENERICAPP_SEND_MSG_EVT,
GENERICAPP_SEND_MSG_TIMEOUT );//在网络情况改变时,会注册一个能定时触发的标志位。这个函数也是常用的API,用于注册定时标志。是不同于定时器的,OSAL系统层面的延时。
}
break;
default:
break;
}
// Release the memory
osal_msg_deallocate( (uint8 *)MSGpkt );
// Next
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( GenericApp_TaskID );
}
// return unprocessed events
return (events ^ SYS_EVENT_MSG);
}
// Send a message out - This event is generated by a timer
// (setup in GenericApp_Init()).
if ( events & GENERICAPP_SEND_MSG_EVT )//轮询到这里时,若定时任务触发,执行发送回调函数,并重新注册,相当于定时器周期触发。上方英文建议在任务初始化就进行第一次注册操作。样例是在组网情况变更才执行这个步骤。
{
// Send "the" message
GenericApp_SendTheMessage();
// Setup to send message again
osal_start_timerEx( GenericApp_TaskID,
GENERICAPP_SEND_MSG_EVT,
GENERICAPP_SEND_MSG_TIMEOUT );
// return unprocessed events
return (events ^ GENERICAPP_SEND_MSG_EVT);
}
// Discard unknown events
return 0;
}
/*********************************************************************
* @fn GenericApp_ProcessZDOMsgs()
*
* @brief Process response messages
*
* @param none
*
* @return none
*/
void GenericApp_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 )
{
GenericApp_DstAddr.addrMode = (afAddrMode_t)Addr16Bit;
GenericApp_DstAddr.addr.shortAddr = pRsp->nwkAddr;
// Take the first endpoint, Can be changed to search through endpoints
GenericApp_DstAddr.endPoint = pRsp->epList[0];
// Light LED
HalLedSet( HAL_LED_4, HAL_LED_MODE_ON );
}
osal_mem_free( pRsp );
}
}
break;
}
}
/*********************************************************************
* @fn GenericApp_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
*/
void GenericApp_HandleKeys( byte shift, byte keys )
{
zAddrType_t dstAddr;
// 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 )
{
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(),
GenericApp_epDesc.endPoint,
GENERICAPP_PROFID,
GENERICAPP_MAX_CLUSTERS, (cId_t *)GenericApp_ClusterList,
GENERICAPP_MAX_CLUSTERS, (cId_t *)GenericApp_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,
GENERICAPP_PROFID,
GENERICAPP_MAX_CLUSTERS, (cId_t *)GenericApp_ClusterList,
GENERICAPP_MAX_CLUSTERS, (cId_t *)GenericApp_ClusterList,
FALSE );
}
}
}
/*********************************************************************
* @fn GenericApp_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
*/
void GenericApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
switch ( pkt->clusterId )
{
case GENERICAPP_CLUSTERID:
// "the" message
#if defined( LCD_SUPPORTED )
HalLcdWriteScreen( (char*)pkt->cmd.Data, "rcvd" );
#elif defined( WIN32 )
WPRINTSTR( pkt->cmd.Data );
#endif
break;
}
}
/*********************************************************************
* @fn GenericApp_SendTheMessage
*
* @brief Send "the" message.
*
* @param none
*
* @return none
*/
void GenericApp_SendTheMessage( void )
{
char theMessageData[] = "Hello World";
if ( AF_DataRequest( &GenericApp_DstAddr, &GenericApp_epDesc,
GENERICAPP_CLUSTERID,
(byte)osal_strlen( theMessageData ) + 1,
(byte *)&theMessageData,
&GenericApp_TransID,
AF_DISCV_ROUTE, AF_DEFAULT_RADIUS ) == afStatus_SUCCESS )
{
// Successfully requested to be sent.
}
else
{
// Error occurred in request to send.
}
}
自定义参考样本可以去看下我的github仓库