透傳的基本概念:
透傳就是透明傳輸的簡稱。那么什么是透明傳輸呢?顧名思義,透明傳輸就是指在傳輸過程中,對外界完全透明,不需要關系傳輸過程以及傳輸協議,終目的是要把傳輸的內容原封不動的傳遞給被接收端,發送和接收的內容完全一致。這就相當于把信息直接扔給你想要傳輸的人,只需要扔(也就是傳輸)這一個步驟,不需要其他的內容安排。
串口無線透傳編程流程:
程序需要實現的功能:
1、 ZigBee 模塊接收到從 PC 機發來的消息,然后無線發送出去
2、 ZigBee 模塊接收到其他 ZigBee 模塊發來的消息,然后發送給 PC 機
發送和接收數據的流程:
發送數據:
填充并注冊端點描述符 配置發送模式及目的地址AF_DataRe quest()發送數據
接收數據:
填充并注冊端點描述符處理系統事件 SYS_EVENT_MSG 中的AF_INCOMING _MSG_CMD 消息獲得消息包中的無線數據
相關的API函數或結構體的介紹:
串口無線透傳我們需要掌握以下重點:
端點描述符
簡單描述符
如何發送數據
如何接收數據
我們重點分析四個函數:
任務的初始化函數
任務的事件處理函數
無線消息接收回調函數
無線消息發送函數
端口描述符的定義:
端點(Endpoint)是協議棧應用層入口,它通常為節點的一個通信部件或設備(如各種類型的傳感器、開關、 LED 燈等)也是用戶定義的應用對象駐留的地方。端點類似于在計算機 TCP/IP 網絡通信中,為方便計算機中不同的進程進行通信,在傳輸層設置的端口(Port)概念。端點和 IEEE64 位擴展地址、16 位網絡短地址一樣,是 ZigBee 無線通信的一個重要地址參數,能夠為多個應用對象提供邏輯子通道以進行通信。每個 ZigBee 設備支持多達 240 個端點,也就是說,每個設備多可以定義 240 個應用對象。端點 0 必須在ZigBee 設備中具有,它分配給 ZigBee 設備對象(ZD0)使用,端點 255 是端點廣播地址,端點 241~254 保留,為以后擴展使用。端點描述符用來描述一個端點,Z-Stack 中的 AF.h 中有關于端點描述符的定義如下:
typedef struct
{
uint8 endPoint; //端口號
uint8 *task_id; // 指向任務ID號 Pointer to location of the Application task ID.
// 簡單設備描述符 可以通過go to…的方式查看結構體變量的類型
SimpleDescriptionFormat_t *simpleDesc;
afNetworkLatencyReq_t latencyReq; // 延時,采用默認值初始化即可
} endPointDesc_t;
端點描述符的初始化:
端點描述符需要在應用層 SampleApp.c 文件中的 SampleApp_Init()函數中進行端點填充,并向 AF 層注冊端點描述符。第一次對話實驗中填充端點描述符的過程如下,該部分代碼比較固定。
void SampleApp_Init( uint8 task_id )
{
// Fill out the endpoint description.
// 以下四行是對節點描述符進行初始化
// 我們需要手動指定端口號,程序中 SAMPLEAPP_ENDPOINT 的宏值為20,
// 可以使用的端口號范圍:1~240,用戶可根據需要進行修改。
SampleApp_epDesc.endPoint = SAMPLEAPP_ENDPOINT;
//指定任務 ID,這里我們指向的是當前任務的 ID。
SampleApp_epDesc.task_id = &SampleApp_TaskID;
// 指向簡單描述符。我們下邊會分析簡單描述符的定義。
SampleApp_epDesc.simpleDesc
= (SimpleDescriptionFormat_t *)&SampleApp_SimpleDesc;
// 采用默認值 noLatencyReqs 即可
SampleApp_epDesc.latencyReq = noLatencyReqs;
// Register the endpoint description with the AF
// 用 afRegister()函數向 AF 層注冊端點描述符。
afRegister( &SampleApp_epDesc );
}
簡單設備描述符的定義:
typedef struct
{
uint8 EndPoint; // 端口號
uint16 AppProfId; // 應用規范ID
uint16 AppDeviceId; // 應用設備ID
uint8 AppDevVer:4; // 應用版本設備號
uint8 Reserved:4; // 保留 AF_V1_SUPPORT uses for AppFlags:4.
uint8 AppNumInClusters; // 輸入簇的個數
cId_t *pAppInClusterList; // 輸入簇的列表
uint8 AppNumOutClusters; // 輸出簇的個數
cId_t *pAppOutClusterList; // 輸出簇的列表
} SimpleDescriptionFormat_t;
簡單設備描述符的初始化:
宏定義在SampleApp.h文件中
#define SAMPLEAPP_ENDPOINT 20 // 端口號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
初始化在SampleApp.c文件中
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]; 應用規范ID
SAMPLEAPP_DEVICEID, // uint16 AppDeviceId[2]; 應用設備ID
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 AppNumInClusters; 輸出簇命令個數
(cId_t *)SampleApp_ClusterList // uint8 *pAppOutClusterList;輸出簇列表
};
發送數據函數詳解:
【1】 afStatus_t AF_DataRequest(
afAddrType_t *dstAddr, //包含目的節點的網絡地址以及發送數據的格式,如廣播、組播或單播。
endPointDesc_t *srcEP,//某一個節點上不同的端口 (endpoint) ,我們可以理解為 TCP/IP 中的端口。
uint16cID,//該參數主要是為了區分不同的命令,對應簡單描述符中的輸出群集
uint16 len,//該參數標識了發送數據的長度。
uint8 *buf,//該參數是指向發送緩沖區的指針。
uint8 *transID,//該參數是指向發送序號的指針。可以用來檢測丟包率,由OSAL維護。
uint8 options,//取默認值 AF_DISCV_ROUTE 即可。
uint8 radius//取默認值 AF_DEFAULT_RADIUS 即可。
)
【2】對于發送數據函數AF_DataRequest函數的具體實現,
只需要理解zigbee協議棧是通過發送函數實現廣播、組播、單播的即可。
【3】這里重點關注AF_DataRequest函數的第一個參數,可以通過go to……的方式跳轉到第一個參數的變量類型:
第一個參數是一個結構體:如下
typedef struct
{
union
{
uint16 shortAddr; //代表節點的網絡地址
ZLongAddr_t extAddr;
} addr;
afAddrMode_t addrMode; //重點關注這個變量,他代表我的發送形式
uint8 endPoint;
uint16 panId; // used for the INTER_PAN feature
} afAddrType_t;
afAddrMode_t變量是一個枚舉類型:
typedef enum
{
afAddrNotPresent = AddrNotPresent,
afAddr16Bit = Addr16Bit, //代表單播
afAddr64Bit = Addr64Bit,
afAddrGroup = AddrGroup, //代表組播
afAddrBroadcast = AddrBroadcast //代表廣播
} afAddrMode_t;
上述使用到的Addr16Bit,AddrGroup,,AddrBroadcast是一個常數,在zigbee協議棧中具有定義:如下
enum
{
AddrNotPresent = 0,
AddrGroup = 1,
Addr16Bit = 2,
Addr64Bit = 3,
AddrBroadcast = 15
};
接收數據函數詳解:
當設備接收到無線數據后,操作系統會將該數據封裝成一個消息然后放入消息隊列中,同時設置系統事件SYS_EVENT_MSG ,應用層的事件處理函數 SampleApp_ProcessEvent 會處理這個事件,他首先會將消息從消息隊列中取出,根據消息的 ID 判斷該消息是否就是接收到的數據,標識接收到新數據的消息 ID 是 AF_INCOMING_MSG_CMD,其中 AF_ INCOMING_MSG_CMD 的值是 0x1a,這是在 ZigBee 協議棧中定義好的,用戶不可更改。
具體的事件處理函數代碼如下所示,這部分代碼比較固定,我們只需要熟悉這種事件處理結構即可。
// 事件處理函數
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 messages is received (OTA) for this endpoint
case AF_INCOMING_MSG_CMD://接收數據事件,調用函數AF_DataRequest()接收數據
SampleApp_MessageMSGCB( MSGpkt );//調用回調函數對收到的數據進行處理
break;
default:
break;
}
// Release the memory 事件處理完了,釋放消息占用的內存
osal_msg_deallocate( (uint8 *)MSGpkt );
// Next - if one is available 指針指向下一個放在緩沖區的待處理的事件,
//返回while ( MSGpkt )重新處理事件,直到緩沖區沒有等待處理事件為止
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );
}
// return unprocessed events 返回未處理的事件
return (events ^ SYS_EVENT_MSG);
}
// Discard unknown events
return 0;
}
SampleApp_MessageMSGCB( MSGpkt );函數使用說明:
// 接收到消息,消息處理回調函數
void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
uint16 flashTime;
switch ( pkt->clusterId )
{
case SAMPLEAPP_PERIODIC_CLUSTERID:
HalUARTWrite (0, (pkt->cmd).Data, ((pkt->cmd).DataLength));
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;
}
}
思考兩個問題:
為什么要根據SAMPLEAPP_ PERIODIC_CLUSTERID 才能得到正確的數據呢?
在 ZigBee 網絡中進行通信時需要注意的,兩者如果想進行通信,需要具有相同的 Cluster ID,而且一個選擇“輸入” ,另一個選擇“輸出”(在簡單描述符中填充) 。
收到的數據在哪里?
在 pkt 指向的那個結構體里,我們去看下 afIncomingMSGPacket_t 的真面目了。
接收數據的存放位置:
typedef struct
{
osal_event_hdr_t hdr; /* OSAL Message header */
uint16 groupId; /* Message's group ID - 0 if not set */
uint16 clusterId; /* Message's cluster ID */
afAddrType_t srcAddr; /* Source Address, if endpoint is STUBAPS_INTER_PAN_EP,
it's an InterPAN message */
uint16 macDestAddr; /* MAC header destination short address */
uint8 endPoint; /* destination endpoint */
uint8 wasBroadcast; /* TRUE if network destination was a broadcast address */
uint8 LinkQuality; /* The link quality of the received data frame */
uint8 correlation; /* The raw correlation value of the received data frame */
int8 rssi; /* The received RF power in units dBm */
uint8 SecurityUse; /* deprecated */
uint32 timestamp; /* receipt timestamp from MAC */
uint8 nwkSeqNum; /* network header frame sequence number */
afMSGCommandFormat_t cmd; /* Application Data */
} afIncomingMSGPacket_t;
afMSGCommandFormat_t結構體的聲明:
typedef struct
{
uint8 TransSeqNumber; //用于存儲發送序列號;
uint16 DataLength; //用于存儲接收到的數據長度;
uint8 *Data; //數據接收后放在一個緩沖區,該參數就是指向緩沖區的指針;
} afMSGCommandFormat_t;