在一個終端上,可以有多個端點endpoint,這個概念是很重要的。
一個節點可以有多個端點,0號endpoint是Zigbee device object(ZDO)用的一個端點,255號是用作廣播。我們自己可以定義的是1-240這些端點。每個端點對應一個任務taskid。因此,我們每增加一個端點,就要給它配置一個新任務taskid
舉一個列子: 例子一:一個無線節點(radio unit)A上有一個溫濕度傳感器,有一個空調控制系統;另外一個無線節點B則負責接收A發回的溫度數據,並通過一定的算法來控制空調系統。我們不管B如何實現,只研究A如何實現。這種情況的一個很規范的實現方式是:溫濕度傳感器設置一個endpoint,比如為10號;空調控制系統設置一個endpoint,比如為20號。還要說明的是:還應該為每一個endpoint建立一個任務,這樣在注冊端點描述符的時候(調用afRegister函數),就會向協議棧底層說明處理這個端點數據的任務是誰。這樣:當B想要獲取溫濕度的時候,他將會發出一個包含A的短地址和10號端點的信息,這個信息到了A,協議棧會將這個消息轉給10號端點所對應的task去處理,管理空調的20號端點根本就看不到這個消息;類似地,如果B想要控制空調,他發出的數據包將包含A的短地址和20號端點信息,A收到消息后會發給20號端點的task去處理。(需要注意的是:在網絡層面經常會有發給ZDO的消息,這時候信息包的端點號就將是0號)。這種將不同功能分配到不同endpoint上的方法非常有利於任務的划分,是一種很正規的方法。
例子二、一個無線節點(radio uint)A上有4個LED需要被控制,另外一個無線節點B則有4個開關用來控制這4個LED。這種情形的規范實現方式還是要為每一個LED設置一個endpoint(允許的范圍內你任意指定,只要不重復),並為每個endpoint建立一個task。這樣處理之后,B可以用同樣的命令來控制4個LED,而不是每一個led 用不同的命令,這種情況在public profile實際上是必須這么做的。 上面兩個例子可能很多同學認為太麻煩,完全可以變通。變通的想法就是我所有的被控對象都落在一個endpoint上,但是我發的數據包內容不同,接收端這個endpoint通過解析數據包的內容來判斷具體該做什么,這種方式實際上完全可以實現,不過需要你自己規定一下數據包的格式,即第幾個字節表示什么。。。。。雖然這可以實現要求,但是我很不贊成這樣,一方面實際上是增加了你程序設計的復雜度,另一方面完全沒有了互聯的可能,尤其是當你用ZCL的時候,這種方式就行不通了
注冊一個應用的端點描述符afRegister( endPointDesc_t *epDesc )
afRegister( endPointDesc_t *epDesc )這個函數用來注冊一個新的端點到task,這樣當空中有這個端點的數據到來會直接發送到對應的task。
傳入參數:
typedef struct
{
byte endPoint;//端點號
byte *task_id; //端點對應的任務號
SimpleDescriptionFormat_t *simpleDesc;//簡單描述符
afNetworkLatencyReq_t latencyReq;//必須用noLatencyReqs來填充
} endPointDesc_t;
afStatus_t afRegister( endPointDesc_t *epDesc )
{
epList_t *ep;
// Look for duplicate endpoint
if ( afFindEndPointDescList( epDesc->endPoint ) )
return ( afStatus_INVALID_PARAMETER );
//在端點的鏈表中搜索新的端點描述符,看看能不能搜到。如果能搜到,則返回錯誤。
//如果不能搜到,則說明這個端點在此之前沒有被注冊,則調用afRegisterExtended( epDesc, NULL )函數注冊這個端點。
ep = afRegisterExtended( epDesc, NULL );
return ((ep == NULL) ? afStatus_MEM_FAIL : afStatus_SUCCESS);
}
//注冊端點描述符
epList_t *afRegisterExtended( endPointDesc_t *epDesc, pDescCB descFn )
{
epList_t *ep;
epList_t *epSearch;
//注冊意味着在端點鏈表中增加一個新的元素,先通過osal_mem_alloc( sizeof ( epList_t ) )申請需要的內存單元
ep = osal_mem_alloc( sizeof ( epList_t ) );
if ( ep ) //如果申請成功
{
// Fill in the new list entry
ep->epDesc = epDesc;
// Default to allow Match Descriptor.
ep->flags = eEP_AllowMatch;
ep->pfnDescCB = descFn;
ep->nextDesc = NULL;
// Does a list exist?
if ( epList == NULL ) //epList為全局epList_t 指針變量,當鏈表為空時它指向NULL,如果這是創建的第一個鏈表元素,則 epList = ep
epList = ep; // Make this the first entry
else //如果這不是第一個鏈表元素,則首先找到鏈表的首,在首端加入這個新的元素。
{
// Look for the end of the list
epSearch = epList;
while( epSearch->nextDesc != NULL )
epSearch = epSearch->nextDesc;
// Add new entry to end of list
epSearch->nextDesc = ep;
}
}
return ep;
}
typedef struct
{
endPointDesc_t *epDesc;
eEP_Flags flags;
pDescCB pfnDescCB; // Don't use if this function pointer is NULL.
void *nextDesc;
} epList_t;
在實際工作中,afIncomingData( aps_FrameFormat_t *aff, zAddrType_t *SrcAddress, uint16 SrcPanId,NLDE_Signal_t *sig, byte SecurityUse, uint32 timestamp )這個函數從APS層收到數據,在這個函數中判斷收到數據對應的端點號在端點鏈表中能不能找到,如果不能找到則丟掉這包數據,如果能找到則把數據發送的端點對應的task.
通過 afBuildMSGIncoming( aff, epDesc, SrcAddress, SrcPanId, sig, SecurityUse, timestamp )這個函數建立OSAL的數據包到對應的任務,並且置位對應任務的SYS_EVENT_MSG。