zstack流程梳理与串口事件详解及zigbee调试助手实现细节
聲明:本文撰寫時間為2019年6月,能力有限,有錯誤歡迎批評指正
首先先梳理一遍zstack的流程
協議棧的文件包層次結構:
-
App:應用層目錄,這是用戶創建各種不同工程的區域,在這個目錄中包含了應用層的內容和這個項目的主要內容,在協議中一般是以操作系統的任務實現的。
-
HAL:硬件層目錄,包含有與硬件相關的配置和驅動及操作函數
-
MAC:MAC層目錄,包含了MAC層的參數配置文件及其MAC的LIB庫的函數接口文件
-
MT:實現通過串口可控制各層,并與各層進行直接交互
-
NWK:網絡層目錄,包含網絡層配置參數文件網絡層庫的函數接口文件及APS層庫的函數接口
-
OSAL:協議棧的操作系統
-
Profile:AF(Applicationframework應用框架)層目錄
-
Security:安全層目錄,包含安全層處理函數,比如加密函數等
-
Services:地址處理函數目錄,包括地址模式的定義及地址處理函數
-
Tools: 工程配置目錄,包括空間劃分及Z-Stack相關配置信息
-
ZDO:ZDO目錄
-
ZMac:MAC層目錄,包括MAC層參數配置及MAC層LIB庫函數回調處理函數
-
ZMain:主函數目錄,包括入口函數及硬件配置文件
-
Output:輸出文件目錄,由IAR IDE自動生成
一些名詞
| EndPoint | 端點 | 是協議棧應用層的入口,即入口地址,也可以理解應用對象(Application Object)存在的地方,它是為實現一個設備描述而定義的一組群集 | 端點0 :用于整個ZigBee設備的配置和管理,附屬在端點0的對象被稱為ZigBee設備對象(ZD0) 端點255:用于向所有的端點進行廣播 端點241~254:保留端點 其他端點:映射應用對象,并使得應用程序可以跟ZigBee堆棧其他層進行通信。 |
| Cluster | 簇 | 一個具體的應用(例如智能家居系統)有大量細節上的小規范 | 例如電燈的控制:開燈、關燈等)這個規范即成為簇 |
| COORDINATOR | 協調器 | 協調器是整個網絡的核心,它最主要的作用是啟動網絡,其方法是其方法是選擇一個相對空閑的信道,形成一個PANID | |
| Router | 路由器 | 路由器的主要功能是提供接力作用,能擴展信號的傳輸范圍,因此一般情況下應該一直處于活動狀態,不應休眠。終端設備可以睡眠也可以喚醒,因此可以用電池來供電。 | |
| Channel | 信道 | 2.4GHz的射頻頻段被分為16個獨立的信道。每一個設備都有一個默認的信道集(DEFAULT_CHANLIST)。協調器掃描自己的默認信道并選擇噪聲最小的信道作為自己所建的網絡信道。設備節點和路由器也要掃描默認信道集并選擇信道上已經存在的網絡加入。 | |
| PANID | 網絡編號 | PANID指網絡編號,用于區分不同的網絡設備,PANID值與ZDAPP_CONFIG_PAN_ID的值設定有關。如果協調器的ZDAPP_CONFIG_PAN_ID設置為0xFFFF,則協調器將產生一個隨機的PANID,如果路由器和終端節點的ZDAPP_CONFIG_PAN_ID設置為0xFFFF,路由器和終端節點將會在自己默認信道上隨機的選擇一個網絡加入,網絡協調器的PANID即為自己的PANID。如果協調器的ZDAPP_CONFIG_PAN_ID設置為非0xFFFF值,則協調器根據自己的網絡長地址(IEEE地址)或ZDAPP_CONFIG_PAN_ID隨機產生PANID的值。不同的是如果路由器和終端節點的ZDAPP_CONFIG_PAN_ID 的值設置為非0xFFFF,則會以ZDAPP_CONFIG_PAN_ID值作為PANID。如果協調器的值設為小于等于0x3FFF的有效值,協調器就會以這個特定的PANID值建立網絡,但是如果在默認信道上已經有了該PANID值的網絡存在,則協調器會繼續搜尋其它的PANID,直到找到不沖突的網絡為止,這樣就可能產生一個問題如果協調器在默認信道上發生PANID沖突而更換PANID,終端節點并不知道協調器已經更換了PANID,還會繼續加入到PANID為ZDAPP_CONFIG_PAN_ID值的網絡中 |
在main函數中:
int main( void ) {// Turn off interrupts//關閉中斷osal_int_disable( INTS_ALL );//初始化硬件// Initialization for board related stuff such as LEDsHAL_BOARD_INIT();// Make sure supply voltage ishigh enough to run//電壓檢測,確保芯片能正常工作的電壓zmain_vdd_check();// Initialize board I/O//初始化板載I/OInitBoard( OB_COLD );// Initialze HAL drivers//初始化硬件驅動HalDriverInit();// Initialize NV System//初始化NV系統osal_nv_init( NULL );// Initialize the MAC//初始化MACZMacInit();// Determine the extended address//確定擴展地址(64位IEEE/物理地址)zmain_ext_addr(); #if defined ZCL_KEY_ESTABLISH// Initialize the Certicom certificate information.// 初始化CERT認證系統zmain_cert_init(); #endif// Initialize basic NVitems//初始化基本NV條目zgInit(); #ifndef NONWK//Since the AF isn't a task, call it's initialization routine//如果task里沒有AF任務,需要在此調用他的初始化函數afInit(); #endif// Initialize the operating system//初始化操作系統osal_init_system();...}其中的osal_init_system便是操作系統的初始化,里面包含了os中各個app以及os各組成部分的初始化方法的調用,比如:初始化內存,堆棧等,其中對app的初始化函數為osalInitTasks();
uint8 osal_init_system( void ) {// Initialize the Memory Allocation System//初始化內存分配系統osal_mem_init();// Initialize the message queue//初始化消息隊列 任務之間的通信靠的就是消息隊列osal_qHead = NULL;// Initialize the timers//初始化定時器osalTimerInit();// Initialize the Power Management System//初始化電源管理系統osal_pwrmgr_init();//osal_mem_alloc()該函數是OSAL中的內存管理函數,是一個存儲分配函數,返回指向一個緩存的指針,參數是被分配緩存的大小,其tasksCnt的定義如下const uint8tasksCnt = sizeof( tasksArr ) / sizeof( tasksArr[0] );tasksEvents指向被分配的內存空間,這里注意tasksArr[]函數指針數組的聯系是一一對應的。tasksEvents就是指向第一個被分配的任務的內存空間tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);//把申請的內存空間全部設置為0,tasksCnt任務數 * 單個任務占的內存空間(4byte)osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));// Initialize the system tasks.//初始化系統任務,重點關注osalInitTasks();// Setup efficient search for the first free block of heap.//設置有效的查找堆上的第一個空閑塊osal_mem_kick();return ( SUCCESS ); }任務初始化函數-------osalInitTasks();
void osalInitTasks( void ) {uint8 taskID = 0;//下面就是Z-Stack協議棧中,從MAC層到ZDO層的初始化函數,其中的參數都是任務的ID,不過ID號是依次遞增的macTaskInit(taskID++ ); //mac_ID = 0nwk_init( taskID++ ); //nwk_ID = 1Hal_Init( taskID++ ); //Hal_ID = 2 #if defined( MT_TASK )MT_TaskInit( taskID++ );//mt_ID = 3 #endifAPS_Init( taskID++ ); //APS_ID =4 #if defined ( ZIGBEE_FRAGMENTATION )APSF_Init(taskID++ ); //ZDO_ID =5 #endifZDApp_Init( taskID++ ); ;//ZDO_ID =6 #if defined ( ZIGBEE_FREQ_AGILITY ) ||defined ( ZIGBEE_PANID_CONFLICT )ZDNwkMgr_Init( taskID++ ); //ZDO_ID =7 #endif //協議棧工程下如果選擇Coordinator或EndDevice或Router工程則只會進入這個 #if defined(SAPP_ZSTACK)sapp_taskInitProcess();//ZDO_ID =8 #endif //協議棧工程下如果選擇Deemo工程則只會進入這個 #if defined(SAPP_ZSTACK_DEMO)// 任務建立實驗范例代碼// 啟動定時器osal_start_timerEx(taskID, 0x0001, 1000);//ZDO_ID =8 #endif }追蹤到sapp_taskInitProcess,這里便是app的初始化函數了,在這個協議棧中,app只有一個,但這個app是一個管理很多小功能的APP,所有的小功能被放在functionlist里統一管理
void sapp_taskInitProcess(void) {#if defined ( BUILD_ALL_DEVICES )// The "Demo" target is setup to have BUILD_ALL_DEVICES and HOLD_AUTO_START// 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;elsezgDeviceLogicalType = ZG_DEVICETYPE_ROUTER; #endif // BUILD_ALL_DEVICES#if defined ( HOLD_AUTO_START )// HOLD_AUTO_START is a compile option that will surpress ZDApp// from starting the device and wait for the application to// start the device.ZDOInitDevice(0); #endif// 構造功能列表funcTableBuffer = createFuncTable(funcCount);funcTableBuffer->ft_type = 0x01;funcTableBuffer->ft_count = funcCount;int i;for(i = 0; i < funcCount; i++){funcTableBuffer->ft_list[i].type = funcList[i].function.type;funcTableBuffer->ft_list[i].id = funcList[i].function.id;funcTableBuffer->ft_list[i].cycle = funcList[i].function.cycle;}controlTaskId = tasksCnt - 2;functionTaskId = tasksCnt - 1;HalIOInit(functionTaskId);createEndPoint(&controlEndPointInfo, &controlTaskId, CONTROL_ENDPOINT);for(i = 0; i < funcCount; i++){struct ep_info_t *ep = &funcList[i];createEndPoint(ep, &functionTaskId, i + 1);if(ep->res_available)(*ep->res_available)(ep, ResInit, NULL);} //這里選擇是否注冊串口事件 #if defined(ZDO_COORDINATOR)// || defined(RTR_NWK) // RegisterForKeys( SampleApp_TaskID );MT_UartRegisterTaskID(controlTaskId); #endif }好了所有的初始化任務完成了,現在回到main中,再經過幾個初始化后就進入osal_start_system開始正式啟動OS了
...// Initialize the operating system//初始化操作系統osal_init_system();// Allow interrupts//使能中斷osal_int_enable( INTS_ALL );// Final board initialization//最終板載初始化InitBoard( OB_READY );// Display informationabout this device//顯示設備信息zmain_dev_info();/*Display the device info on the LCD *///添加LCD液晶屏的支持 #ifdef LCD_SUPPORTEDzmain_lcd_init(); #endif #ifdef WDT_IN_PM1/*If WDT is used, this is a good place to enable it. *///看門狗的初始化設置WatchDogEnable( WDTIMX ); #endifosal_start_system(); // No Return from here沒有返回,即進入操作系統return 0; // Shouldn't get here.//不會運行到這進入osal_start_system發現這里有個死循環,也就是為什么main函數不return的原因
void osal_start_system( void ) { #if !defined ( ZBIT ) && !defined (UBIT )for(;;) // 一直循環,是“輪詢”中的輪,即不斷循環執行 #endif{osal_run_system();} }進入osal_run_system中,osal_start_system函數是ZigBee協議棧的靈魂,實現的方法是不斷查詢事件表,如果有事情發生就調用相應的事件處理函數。
void osal_run_system( void ) {uint8 idx = 0;osalTimeUpdate();Hal_ProcessPoll();do {if (tasksEvents[idx]) // 這里就是“輪詢”中的詢,即不斷查詢,而且這個查詢是有優先級順序的{break;//如果有事件發生則跳出循環}} while (++idx < tasksCnt);//判斷是主動跳出循環還是全部判斷完跳出的循環if (idx < tasksCnt){uint16 events;halIntState_t intState;HAL_ENTER_CRITICAL_SECTION(intState);events = tasksEvents[idx];tasksEvents[idx] = 0; // 清除事件標志HAL_EXIT_CRITICAL_SECTION(intState);activeTaskID = idx;events = (tasksArr[idx])( idx, events );//調用相應的事件處理函數,tasksArr[]是一個函數指針數組,每一個元素都是函數指針activeTaskID = TASK_NO_TASK;HAL_ENTER_CRITICAL_SECTION(intState);tasksEvents[idx] |= events; // 如果沒有處理完可以再次設置標志HAL_EXIT_CRITICAL_SECTION(intState);} #if defined( POWER_SAVING )else // Complete pass through all task events with no activity?{osal_pwrmgr_powerconserve(); // Put the processor/system into sleep} #endif/* Yield in case cooperative scheduling is being used. */ #if defined (configUSE_PREEMPTION) && (configUSE_PREEMPTION == 0){osal_task_yield();} #endif }這里可以去看我們的taskArr里有什么了
// 任務列表 const pTaskEventHandlerFn tasksArr[] = {macEventLoop,nwk_event_loop,Hal_ProcessEvent, #if defined( MT_TASK )MT_ProcessEvent, #endifAPS_event_loop, #if defined ( ZIGBEE_FRAGMENTATION )APSF_ProcessEvent, #endifZDApp_event_loop, #if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )ZDNwkMgr_event_loop, #endif #if defined(SAPP_ZSTACK)//重點,這兩個便是我們自己定義的事件處理函數sapp_controlEpProcess,sapp_functionEpProcess, #endif #if defined(SAPP_ZSTACK_DEMO)// Deemo工程對應的事件處理函數Hello_ProcessEvent, #endif }; //學習一下這里sizeof的用法,之后會經常用到 const uint8 tasksCnt = sizeof(tasksArr)/sizeof(tasksArr[0]);這里的taskArr(包含系統級任務如macEventLoop等和用戶app如sapp_functionEpProcess)非常像我們平常使用的操作系統里的任務管理器里的任務(圖中包含了系統自帶應用如windowServer何kernel_task等,也包括了用戶app如QQ),對于我們的OS來說,多個網絡應用是通過端口號(圖中如果有一份數據包發給QQ則會發送給0.0.0.0:1194)來區分的,而對于OSAL來說,接收到一份數據包決定將數據包發送給哪個app則是通過數據包中指定的EndPoint決定的。
比如這一份數據包,就會被發送到EndPoint為0x01的task里。
進入第一個app的處理函數sapp_controlEpProcess,這里主要是為了配合zigbee調試助手使用,原來是讓協調器接收PC中的ZigBee調試助手里發送的命令幀,以及解析命令幀里命令,01對應發送functionlist,02對應發送拓撲信息,03對應發送function的數據?關于zigbee調試助手的原理在最后面會講一下。
最后進入sapp_functionEpProcess看看,這里主要是根據事件執行的functionlist里對應的定義的那些函數
uint16 sapp_functionEpProcess(uint8 task_id, uint16 events) {afIncomingMSGPacket_t *MSGpkt;if(events & SYS_EVENT_MSG){MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( task_id );while ( MSGpkt ){switch ( MSGpkt->hdr.event ){// 接收到數據包case AF_INCOMING_MSG_CMD:{switch ( MSGpkt->clusterId ){case SAPP_PERIODIC_CLUSTERID:if(MSGpkt->endPoint <= funcCount){struct ep_info_t *ep = &funcList[MSGpkt->endPoint - 1];if(ep->incoming_data)(*ep->incoming_data)(ep, MSGpkt->srcAddr.addr.shortAddr, MSGpkt->srcAddr.endPoint, &MSGpkt->cmd);//執行對應的incoming處理函數HalLedBlink( HAL_LED_2, 1, 50, 250 );}break;}}break;case ZDO_STATE_CHANGE:{curNwkState = (devStates_t)(MSGpkt->hdr.status);if ( (curNwkState == DEV_ZB_COORD)|| (curNwkState == DEV_ROUTER)|| (curNwkState == DEV_END_DEVICE) ){int i;int hasTimeOut = 0;for(i = 0; i < funcCount; i++){struct ep_info_t *ep = &funcList[i];if(ep->nwk_stat_change)(*ep->nwk_stat_change)(ep);// 重置端點計數器if(ep->time_out && ep->function.cycle){ep->timerTick = ep->function.cycle;hasTimeOut = 1;}}if(hasTimeOut){// 加入網絡成功,啟動定時器,為各個端點提供定時osal_start_timerEx(task_id,SAPP_SEND_PERIODIC_MSG_EVT,1000);}}elseosal_stop_timerEx(task_id, SAPP_SEND_PERIODIC_MSG_EVT);}break;//IO事件case IOPORT_INT_EVENT:{OSALIOIntData_t* IOIntData;IOIntData =(OSALIOIntData_t*)MSGpkt;if(IOIntData->endPoint <= funcCount){struct ep_info_t *ep = &funcList[IOIntData->endPoint - 1];if(ep->res_available)(*ep->res_available)(ep, ResIOInt, IOIntData->arg);//執行對應的資源可用處理函數}}break; #if defined(HAL_IRDEC) && (HAL_IRDEC == TRUE)case IRDEC_INT_EVENT: //{OSALIRDecIntData_t* TimerIntData = (OSALIRDecIntData_t*)MSGpkt;if(TimerIntData->endPoint <= funcCount){struct ep_info_t *ep = &funcList[TimerIntData->endPoint - 1];if(ep->res_available)(*ep->res_available)(ep, ResTimerInt, TimerIntData->data);}}break; #endifdefault:break;}// Release the memoryosal_msg_deallocate( (uint8 *)MSGpkt );// Next - if one is availableMSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( task_id );}// return unprocessed eventsreturn (events ^ SYS_EVENT_MSG);}// 定時器時間到, 遍歷所有端點看是否有需要調用time_outif(events & SAPP_SEND_PERIODIC_MSG_EVT){int i;for(i = 0; i < funcCount; i++){struct ep_info_t *ep = &funcList[i];if(ep->time_out && ep->function.cycle){// 端點需要周期執行ep->timerTick = ep->timerTick - 1;if(ep->timerTick == 0){// 定時時間到,執行time_out函數(*ep->time_out)(ep);ep->timerTick = ep->function.cycle;}} #if 0if(ep->userTimer && ep->res_available){ep->userTimer = ep->userTimer - 1;if(ep->userTimer <= 1){(*ep->res_available)(ep, ResUserTimer, NULL);ep->userTimer = 0;}} #endif}// 重新啟動定時器osal_start_timerEx(task_id, SAPP_SEND_PERIODIC_MSG_EVT, 1000);// return unprocessed eventsreturn (events ^ SAPP_SEND_PERIODIC_MSG_EVT);}// Discard unknown eventsreturn 0; }Zigbee 串口事件
UART(通用串口)是由zigbee協議棧中的MT層主管的,因此要在預編譯選項中添加上ZTOOL_P1或ZAPP_P1和MT_TASK
相關的文件有:
- MT_UART.c
- MT_UART.h
略過硬件層的一些關于串口的初始化后,我們又回到了OSAL的初始化函數,在這里面有個非常重要的函數MT_TaskInit,這里面是對MT層的一系列設置。
void osalInitTasks( void ) {uint8 taskID = 0;//下面就是Z-Stack協議棧中,從MAC層到ZDO層的初始化函數,其中的參數都是任務的ID,不過ID號是依次遞增的macTaskInit(taskID++ ); //mac_ID = 0nwk_init( taskID++ ); //nwk_ID = 1Hal_Init( taskID++ ); //Hal_ID = 2 #if defined( MT_TASK )MT_TaskInit( taskID++ );//mt_ID = 3...追蹤進去后:
void MT_TaskInit(uint8 task_id) {MT_TaskID = task_id;/* Initialize the Serial port *///初始化UartMT_UartInit();/* Register taskID - Do this after UartInit() because it will reset the taskID *///注冊事件MT_UartRegisterTaskID(task_id);osal_set_event(task_id, MT_SECONDARY_INIT_EVENT); }其中的UartInit中
void MT_UartInit () {halUARTCfg_t uartConfig;/* Initialize APP ID */App_TaskID = 0;/* UART Configuration *///設置為已配置uartConfig.configured = TRUE;//設置為默認波特率115200uartConfig.baudRate = MT_UART_DEFAULT_BAUDRATE;//設置是否采用流控及流控設置uartConfig.flowControl = MT_UART_DEFAULT_OVERFLOW;uartConfig.flowControlThreshold = MT_UART_DEFAULT_THRESHOLD;//設置最大發送緩沖區長度128uartConfig.rx.maxBufSize = MT_UART_DEFAULT_MAX_RX_BUFF;//設置最大接收緩沖區長度128uartConfig.tx.maxBufSize = MT_UART_DEFAULT_MAX_TX_BUFF;//設置超時時間uartConfig.idleTimeout = MT_UART_DEFAULT_IDLE_TIMEOUT;uartConfig.intEnable = TRUE; #if defined (ZTOOL_P1) || defined (ZTOOL_P2)//重要:設置串口回調函數uartConfig.callBackFunc = MT_UartProcessZToolData1; #elif defined (ZAPP_P1) || defined (ZAPP_P2)uartConfig.callBackFunc = MT_UartProcessZAppData; #elseuartConfig.callBackFunc = NULL; #endif/* Start UART */ #if defined (MT_UART_DEFAULT_PORT)//開啟UARTHalUARTOpen (MT_UART_DEFAULT_PORT, &uartConfig); #else/* Silence IAR compiler warning */(void)uartConfig; #endif/* Initialize for ZApp */ #if defined (ZAPP_P1) || defined (ZAPP_P2)/* Default max bytes that ZAPP can take */MT_UartMaxZAppBufLen = 1;MT_UartZAppRxStatus = MT_UART_ZAPP_RX_READY; #endif}追蹤這個回調函數發現這個是我自己寫的回調函數,自己添加新的回調函數時要在MT_UART.h里面聲明
//自定義的串口接收回調函數 void MT_UartProcessZToolData1 ( uint8 port, uint8 event ) {uint8 flag = 0,i,j = 0; //flag判斷有沒有數據,j記錄數據長度uint8 buf[128]; //緩沖128(void)event;while(Hal_UART_RxBufLen(port)) //檢測串口數據是否完成{HalUARTRead(port,&buf[j],1);//數據接收到bufj++;flag = 1;}if(flag == 1) //有數據時{//分配內存,結構體+內容+長度pMsg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof( mtOSALSerialData_t )+j+1);pMsg->hdr.event = CMD_SERIAL_MSG;//事件類型選擇為CMD_SERIAL_MSGpMsg->msg = (uint8*)(pMsg+1); //把數據定位到結構體pMsg->msg [0]= j; //記錄數據長度for(i=0;i<j;i++)pMsg->msg [i+1]= buf[i];osal_msg_send( App_TaskID, (byte *)pMsg ); // 登記任務并發往上層osal_msg_deallocate ( (uint8 *)pMsg ); // 釋放內存} }//原本的回調函數,在后面講zigbee調試助手原理的時候會講到,暫時不用管 void MT_UartProcessZToolData ( uint8 port, uint8 event ) {uint8 ch;uint8 bytesInRxBuffer;(void)event; // Intentionally unreferenced parameterwhile (Hal_UART_RxBufLen(port)){HalUARTRead (port, &ch, 1);switch(state){...}注意串口事件也要在這里注冊才行
void sapp_taskInitProcess(void) {... //這里選擇是否注冊串口事件 #if defined(ZDO_COORDINATOR)// || defined(RTR_NWK) // RegisterForKeys( SampleApp_TaskID );MT_UartRegisterTaskID(controlTaskId); #endif }這樣我們回到sapp_controlEpProcess函數中,就可以對串口事件進行處理了,在這里路由器和協調器的處理函數是不同的
uint16 sapp_controlEpProcess(uint8 task_id, uint16 events) {afIncomingMSGPacket_t *MSGpkt;if ( events & SYS_EVENT_MSG ){//HalLedBlink( HAL_LED_1, 2, 50, 90 );MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive(task_id);while ( MSGpkt ){//HalUARTWrite(0, &MSGpkt->hdr.event,1);switch ( MSGpkt->hdr.event ){ #if defined(ZDO_COORDINATOR)case CMD_SERIAL_MSG:uartMsgProcesser((uint8 *)MSGpkt);HalLedBlink( HAL_LED_1, 2, 50, 90 );break; #endif#if ! defined(ZDO_COORDINATOR) && defined(RTR_NWK)case CMD_SERIAL_MSG:uartMsgProcesser1((uint8 *)MSGpkt);HalLedBlink( HAL_LED_1, 2, 50, 90 );break; #endifZigbee調試助手原理
- 解析的思路
這里也就解釋里為什么我們要重寫他的串口回調函數,因為我們在串口調試助手里發送的數據包在這個回調函數看來是不合法的都被丟棄了,而且也是因為我們不想做這么麻煩的數據包,不過壞處也是有的,就是由于沒有幀校驗位,導致很多數據包的內容都出錯了,因此最好保留一位校驗位,如果可以的話可以自己實現一個漢明碼校驗,這樣就不用重發數據了。
接下來繼續觀察實驗現象,發現zigbee調試助手打開后只有一個協調器節點,等待一會(大概十秒)后才突然出現子節點,同時接收LED燈閃爍,子節點的發送LED燈閃爍,這時整個流程就很清晰了:
zigbee調試助手周期性地發送一個特定格式的串口數據包給協調器
協調器接收到后對數據包進行解析發送給應用層并觸發CMD事件
在CMD的事件處理函數中向廣播地址(包括協調器)發送命令幀
所有設備接收到命令幀后在處理函數里立即向協調器發送拓撲信息包
協調器接收到拓撲信息包后將報文通過串口寫給電腦
電腦中的zigbee調試助手分析拓撲信息包中的節點父子關系繪制圖像
下面我們驗證一下整個流程。步驟123在上面已經介紹了。
步驟5的實現源碼在functionlist中協調器的接收到數據包的處理函數里:
可以看到最后一行是一個終端節點發送的一個數據,數據的內容是字符串"Z-Stack for SAPP",他在zigbee調試助手中如下圖所示(這個圖是后來補的并不完全一致,當時的實驗情況與此圖類似)
注意打開某個節點時可能會出現多個傳感器標簽0,1,2等,對應的就是ENDPORT。
最后總結的zigbee調試助手繪制拓撲圖及遠程控制實現內部原理
zigbee調試助手周期性地發送一個特定格式的串口數據包給協調器
協調器接收到后對數據包進行解析發送給應用層并觸發CMD事件
在CMD的事件處理函數中向廣播地址(包括協調器)發送命令幀
所有設備接收到命令幀后在處理函數里立即向協調器發送拓撲信息包
協調器接收到拓撲信息包后將報文通過串口寫給電腦
電腦中的zigbee調試助手分析拓撲信息包中的節點父子關系繪制圖像
總結
以上是生活随笔為你收集整理的zstack流程梳理与串口事件详解及zigbee调试助手实现细节的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android 短信 易用性总结,详解短
- 下一篇: 开源博客项目eblog完整搭建教程!