1. 程式人生 > >基於ZigBee的智慧家居系統

基於ZigBee的智慧家居系統

本專案基於C#編寫智慧家居系統PC客戶端,同時編寫安卓客戶端,程式碼下載連結https://download.csdn.net/download/hzqgangtiexia/10435931

1、硬體選型及資料採集

節點板子以CC2530晶片為核心,PL2303晶片做usb轉串列埠,

感測器資料採集及控制:

溫度和溼度:DHT11   溫度量程0-50C,溼度量程20-90%RH     單匯流排協議讀

光敏感測器(用於自動開燈關燈):用AD讀,本質是電阻

紅外感測器(用於安防):HC-SR501,直接判斷高低電平,輸出 高3.3v / 低0v

煙霧感測器(甲烷、液化氣、可燃氣):用AD讀,本質是電阻

繼電器:    高低電平

步進電機:  驅動晶片為ULN2003,5—12v寬電壓驅動,五線四相,通過四個IO口設定的高電平外加延時2ms控制。

2、整體架構

                                 

2.1、各節點(路由器)採集資料

      節點採集資料分別涉及溫度、溼度煙霧、光敏溫度、紅外感測器資料,並控制步進電機和繼電器來控制用電器。

所有資料由統一的陣列來發送,陣列長度9,data_str[9],其data_str[0]=0X1A用於標記資料傳輸的方向是節點-à協調器—>PC,data_str[1]=0X60代表感測器資料,以後代表資料等等,PC進行解析的時候直接if(buf[1]==0x60)。  

2.2、協調器

      協調器不解析任何資料,只是做轉發功能(因為網路一旦建立,它的作用就相當於路由器)

2.3、PC (C#上位機)

      上位機通過串列埠接收來自協調器的資料,然後對串列埠資料進行解析。

      上位機發送控制命令只需要按下按鈕  Buf[6], Buf[0]=0X2A,代表資料向中端節點傳輸,Buf[1]=0X1燈,Buf[1]=0X2電機,Buf[1]=0X3等等,Buf[2]代表傳送到哪個節點,其他是控制命令。

       總結通訊資料的傳輸:自定義通訊協議

採用結構體對資料幀進行封裝,和用陣列進行進行封裝各有各用,最後都轉化為字元(unsitned char)流取地址傳送

 裝置自身的安全,第二是資料資訊的安全。

C#整體程式設計

串列埠:

       串列埠初始化

       掃描顯示串列埠

       設定串列埠接收事件,並在其接收方法用this.invoke來訪問UI資源

TCPserver:

       獲取Ip地址並顯示

       啟動伺服器,建立socket連線

       將接收到的APP資料通過串列埠直接傳送(不需要解析)

       close();

3、效能調優:

關於利用休眠的節能

       假設5號電池容量1500mA.h,兩節就是3000mA.h,終端節點資料傳送期間瞬時電流29mA,資料接收瞬時電流24mA,再加上各種感測器所需要的電流,假設共計60mA,那麼兩節5號電池可以工作50h,對應實際應用時,定時採集,假設每小時工作50s,其餘時間在休眠(休眠時工作電壓工作在微安級,可忽略不計)那麼可以工作3600小時,那麼可以工作半年的時間。

如何優化:

       設定低功耗標誌

       通過可以被每一個任務呼叫的一個函式設定每個任務是否接受在低功耗執行

       設定睡眠時間等等

1. voidosal_start_system( void )

      所有應用程式,無論是自己寫的最簡單的測試程式還是複雜的OSAL作業系統,都必須從main( )來入口。所謂的OS作業系統,我們不妨這樣想:自己寫一個最簡單的main( ),裡面就一句列印“Hello, World”.如果需要加入Key, LED這樣的輸入輸出功能,那麼就需要擴充main( ),加入Key, LED的驅動,如果要實現多執行緒排程,就要加入Timer驅動等等。其實作業系統就是這麼來的,簡單吧:)。
      一般在OS中都會有個死迴圈,在這個迴圈中去處理各種各樣的事件。在Zstack OSAL中,這個死迴圈就存在於osal_start_system()這個函式中。下面來詳細分析在這個死迴圈中到底在做哪些事情。

2. OSAL的“心跳”

      在OSAL的死迴圈中,各個事件只是在某些特定的情況下發生,如果OSAL一刻不停去輪詢去處理這些應用程式,遲早會累死(熱量,功耗,壽命…),這樣做是完全沒有必要的。所以這裡就引入了心跳的概念,也就是OS的時鐘節奏。在Zstack OSAL中這個節奏定義為1ms, 由8 bits HW_TIMER4來控制,當然這些都可以由程式設計師來修改,後面就以系統的預設值來講述。在void InitBoard( byte level )這個函式中有下面這段程式碼就是在定義系統的心跳Timer。
HalTimerConfig (OSAL_TIMER,
HAL_TIMER_MODE_CTC,                 HAL_TIMER_CHANNEL_SINGLE,
HAL_TIMER_CH_MODE_OUTPUT_COMPARE,
                 OnboardTimerIntEnable,
                 Onboard_TimerCallBack);

      在OSAL的Timer定義好了以後,就要啟動Timer, 至於如何啟動Timer, 請自行查閱2430 Spec, 我這裡想說的是,在一步步跟蹤原始碼到死迴圈開始,都沒有發現啟動OSALTimer的程式碼,最後通過觀察Timer相關的控制暫存器,發現,在網路層初始化函式nwk_init( taskID++ )執行完畢後Timer啟動了,也就是說在網路層初始化函式中有啟動Timer的語句,因為網路層初始化是不開源的,無從去看原始碼驗證,總之,Timer啟動了就好。

      每當1ms心跳來臨時,Timer4的中斷標誌置位,這樣在OSAL的死迴圈中檢測到這個標誌置位後,就去輪詢處理各事件。沒有檢測到這個標誌位則繼續死迴圈。在死迴圈的開始有呼叫Hal_ProcessPoll()這條語句,實際上就是在查詢中斷標誌並作相應的處理。

3. OSAL的“心跳”來臨後的處理

      上節提到Hal_ProcessPoll()這條語句,實際上就是在查詢中斷標誌並作相應的處理。那麼當1ms心跳來臨時,我們跟蹤進這個函式看看它到底幹了些什麼。
當判斷到中斷標誌,表明1ms心跳來臨了,就去呼叫Timer4相應的回撥函式。這個回撥函式由HalTimerConfig()的最後一個引數來定義,請回看上節,OSAL Timer的回撥函式就是Onboard_TimerCallBack(),一步步跟進,最終呼叫osalTimerUpdate()這個函式。在這個函式中會去輪詢Timer事件連結串列。
Timer事件連結串列是下面這樣一個結構,next指向下一個Timer事件,timeout值表明本Timer事件還需要timeout個心跳才需要被處理,因為此處心跳是1ms,所以也就是說還需要timeout個ms才處理。所謂的處理也就是檢測timeout是否小於1ms,如果小於1ms,則發出event_flag這個訊息到訊息佇列,這個訊息隸屬於task_id這個任務。如果大於1ms,說明該Timer事件還不到處理的時候,則Timeout = Timeout-1,然後繼續耐心等待下一次心跳。注:Timer事件連結串列的維護是通過osal_start_timerEx()這個函式來實現的。

typedef struct
{
void *next;
UINT16 timeout;
UINT16 event_flag;
byte task_id;
} osalTimerRec_t;


4. 訊息發出後的處理

      上節講到在心跳中發出任務的事件訊息到訊息佇列。那麼這個訊息由誰來處理?回頭再看osal_start_system( )中的死迴圈,有檢測訊息佇列的語句,當發現有訊息時,判斷該訊息隸屬於哪個任務就去呼叫對應於該任務的訊息處理函式。各任務的訊息處理函式是在tasksArr[]這個常量陣列中定義。這個陣列中定義的訊息處理函式和任務初始化函式中的任務必須一一對應。

5. 節電模式

      細心的同學會發現在死迴圈體的後面有呼叫osal_pwrmgr_powerconserve()這樣一條語句。從名字及註釋來看,屬於節電模式的呼叫。此處不詳細列舉程式碼,只講其工作原理。
上面章節講到1ms心跳來臨時去輪詢各事件Timer是否需要處理。這裡心跳很快(1ms),各事件的Timeout很慢(往往成百上千)。譬如Key檢測的Timer事件的Timeout是100,意思是說100ms才去檢測一次是否有Key按下。假如說Key檢測的Timout在各Timer事件中Timeout最小,那麼也就是說有99次心跳都不會有事件需要處理,但是死迴圈依然在跑,在做無用功,為了解決這個問題,就加入了節電模式。
在osal_pwrmgr_powerconserve()這個函式中會檢測Timer時間連結串列中Timeout最小的值,假設為next, 然後設定CPU進入休眠模式next個毫秒。休眠時間到了甦醒過來立即就會有Timer事件需要處理,這樣就可以達到省電的目的。

6. 小結

      到此為止,OSAL神祕面紗已完全揭開,為了鞏固知識,下面以Key為例講述從Key按下到Key訊息被處理的整個過程。
首先在Key的初始化過程中會呼叫下面這條語句:osal_start_timerEx(Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_POLLING_VALUE),這條語句的功能就是將檢測Key的這個事件放入Timer事件連結串列。這個事件隸屬於Hal_Task, Timeout是HAL_KEY_POLLING_VALUE。
      當1ms心跳來臨時,判斷timeout是否小於1,如果不小於,則timeout=timeout-1並等待下一次心跳。如果小於1,則發出HAL_KEY_EVENT這個訊息到訊息佇列,然後呼叫Hal_Task的事件處理函式Hal_ProcessEvent()處理HAL_KEY_EVENT訊息,在處理這個訊息的過程中呼叫函式HalKeyPoll()。這個函式檢測當前有無按鍵,如果有按鍵並且和上次按鍵值不同則認為有新的按鍵按下併發出相應的按鍵訊息。

      在上面這個過程完成後,必須通過osal_start_timerEx()這個函式將Key檢測事件繼續放入Timer事件連結串列,以便後面心跳時能檢測到該事件,也就是說每100ms都會掃描看有無按鍵按下。