早在1994 年愛立信公司就創立了藍牙技術,並制定了基本的技術規範,原意是創造一種設備間通訊的標准化協議,以解決設備間通訊不兼容的情況,規範公布後得到大量移動設備制造商的支持,並於1999年成立藍牙技術聯盟(Bluetooth Special Interest Group),該聯盟制定並維護藍牙無線規範,並對設備制造廠商提供Bluetooth認證與授權。

當前影響最廣的版本應該是藍牙4.0,本標准中增加了 Bluetooth Smart 和Bluetooth SmartReady 標准。特別是Bluetooth Smart版本,作為低功耗藍牙(Bluetooth Low Energy,簡稱BLE),相對歷史版本有質的飛躍,主要表現在成本低,功耗低,峰值電流極小並可以非常快速的建立連接,使用一粒紐扣電池就可以連續工作數年之久。相較於Zigbee和傳統藍牙,協議標准化和低功耗的優勢讓BLE在智能家居和穿戴設備上的優勢一目了然,如圖1所示。

image

                                               圖1 常用無線協議比較

特別是2011年開始蘋果iOS原生支持BLE之後,BLE得到大量iOS周邊廠商和智能設備廠商的響應,基於BLE技術開發的智能硬件遍地開花,如:智能體重秤,智能手環,智能燈控,智能水杯,智能馬桶,智能鬧鐘……

項目背景

近兩年各種智能家居的產品層出不窮,而且眾多“科技公司”“互聯網企業”“資本投資公司”紛紛加入戰團,看到做手機的 A 公司出了一個智能淨化器,做空調的 B 公司出了一個智能手機,做手表的 C 公司出了一個智能手環,做馬桶的 D 公司坐不住了,在馬桶裡面塞進去了一個溫控+通訊模塊,就誕生了智能馬桶…智能設備市場好熱鬧啊。那麼智能產業迅速發展的現在,我們在智能家居上還能有什麼創意可以挑釁?靈感真的都源於生活,小熊有一天開門收快遞,然後無情的風把萬惡的門拍死了,只剩小熊穿著睡衣舉著手機拿著包裹在風中凌亂,開鎖小哥在小熊女友擔保外加3張毛爺爺之後才高抬貴手開了門,懊惱啊…拍大腿的瞬間,當時小熊就萌發了一個念頭,要是門鎖換成智能控制的話,是不是鑰匙就不再困擾我們了!!!只需要打開手機 APP,就可以操縱智能門鎖開門,或者像有強迫症的熊二,每次鎖完門都要回頭再看一次門有沒有鎖好,是不是有了門鎖的手機APP,就可以隨時看自家門鎖的開關狀態了!

想法出來了,回頭就開始一步步落實。經過對場景設想,最終選用藍牙+以太網網關的方案,正是因為藍牙短距離+傳輸速度快的優勢,其中以太網網關部分使用W5500 硬件協議棧芯片實現。為了更加體現門鎖的智能化,小熊設想了幾個場景:其一是小熊出門收快遞,門被無意中關上了,可以直接用手機開門;其二小熊不在家,小熊女朋友需要進門洗衣服做飯,小熊可以遠程給女朋友開門;其三是熊二被女朋友趕出來了,需要臨時借宿,小熊可以給熊二手機授予限時的開門權限(如2周)。看起來是不是很強大!圖2就是小熊今天要為大家介紹的藍牙智能門鎖的實現示意圖。

image

                                           圖2 藍牙智能門鎖示意圖

本方案的組成主要分為三個部分:BLE門鎖機構,BLE網關設計,門鎖管理服務器。因為門鎖管理服務器主要為數據庫管理以及APP調用等內容,本文不做過多闡述,本文將主要講述BLE門鎖和BLE的以太網網關的實現等部分內容。

關於BLE的實現,我們選用的是目前市場上最常見的TI CC2541,CC2541是BLE單芯片解決方案,包含一個工業級的8051內核以及RF收發器,集成TI的BLE低功耗協議棧並擁有相對完善的低功耗外設;而以太網部分用的硬件協議棧芯片W5500,W5500 芯片使用硬件邏輯門電路實現TCP/IP協議棧的傳輸層及網絡層(如:TCP, UDP, ICMP, IPv4, ARP, IGMP, PPPoE等協議),並集成了數據鏈路層,物理層,以及 32K 字節片上 RAM 作為數據收發緩存。不言而喻,W5500 非常適合CC2541這種8051內核,而且片上資源不是很豐富的MCU,一切就這麼愉快的決定了。

為了實現BLE通訊,我們需要使用兩個 CC2541 模塊,一個作為 Central,另一個作為 Peripheral;他們之間實現BLE通信,其中 Peripheral 作為門鎖機構的控制,而 Central 則驅動 W5500 作為 TCP Client 實現網絡通信,從而在網絡端查詢門鎖狀態以及實現網絡控制開鎖的功能。圖3列出了需要准備的硬件設備。

image

圖3 所需硬件設備

准備工作:

1. 安裝編譯環境 IAR Embedded Workbench for 8051 8.10 Evaluation
 2. 安裝協議棧 BLE-CC254x-1.3.2
 3. 安裝 CC-Debugger 模塊調試下載器驅動

BLE 以太網網關部分:

BLE 以太網網關部分結構如圖4:

image

圖4 BLE 以太網網關接線圖

需要在 CC2541 的 Central 模式中集成 W5500 的驅動以及 Socket 處理部分,由於 W5500 的函數驅動庫是分層次書寫的,我們只需將 SPI 通信的硬件抽像層的函數重新編寫即可。以下為 CC2541 的 SPI1 的初始化配置函數和數據收發函數的程序,以及復位管腳的控制程序:

01 void WIZ_SPI_Init(void)

02 {

03     PERCFG |=  0×02;                // PERCFG.U1CFG = 1

04     P1SEL  |=  0xE0;                // P1_7, P1_6, and P1_5 are

05                peripherals

06

07     P1SEL  &= ~0×10;                // P1_4 is GPIO (SSN)

08     P1DIR  |=  0×10;                // SSN is set as output

09

10     // Set baud rate to max (system clock frequency / 8)

11     U1CSR = 0;                      // SPI Master Mode

12     // Configure phase, polarity, and bit order

13     U1GCR = 15;

14     U1BAUD = 255;

15     U1UCR = 0×80;

16     U1GCR |= BV(5);

17

18     P1SEL  &= ~0×08;            // P1_3 is GPIO (SSN)

19     P1DIR  |=  0×08;            // P1_3 is set as output

20 }

21

22 void WIZ_RST(uint8 val)

23 {

24     if (val == LOW)

25     {

26         P1_3=0;

27     }

28     else if (val == HIGH)

29     {

30         P1_3=1;

31     }

32 }

33 // Connected to Data Flash

34 void WIZ_CS(uint8 val)

35 {

36     if (val == LOW)

37     {

38         P1_4=0;

39     }

40     else if (val == HIGH)

41     {

42         P1_4=1;

43     }

44 }

需要注意的是,CC2541 的 LCD 驅動的部分引腳與SPI1的幾個引腳是復用的,需要將和 LCD 有關的編譯項去掉,避免發生衝突,導致 SPI 不可用。具體方法為在工程選項的編譯子項裡,將 ”HAL_LCD=TRUE”更改為“HAL_LCD=FALSE”。

程序重寫完畢後,打開TI官方 BLE 的例程 SerialApp3,此例程包含 Central 和Peripheral 兩部分,原始例程可以實現串口數據透傳,基於此例進行集成相對起來會比較簡單。將 W5500 的驅動程序包添加到工程中,如圖 5 所示。

image

                                     圖5 將 W5500 部分集成並初始化

其中Ethernrt_Init函數主要負責W5500的接口和參數的初始化:

01 /*該函數將會在任務函數的初始化函數中調用*/

02 void Ethernrt_Init( uint8 taskID )

03 {

04     WIZ_SPI_Init();     //初始化CC2541的SPI1作為 W5500 的通訊接口

05     Reset_W5500();      //復位W5500

06     set_default();  //設置初始化參數如MAC,IP地址等參數

07     set_network();      //使初始化的IP參數生效

08

09     //記錄任務函數的taskID,備用

10     sendMsgTo_TaskID = taskID;

11 }

TI 的官方 BLE 例程中有比較完備的協議棧程序,提供了完備的應用函數供用戶調用,用戶可以在應用層添加自己的任務和事件;更改應用層數據之前我們必須了解一下 BLE 的應答機制以及 CC2541 的 OSAL 也就是系統抽像層的任務調用機制,首先TI已經做了大量工作,我們無需重寫 BLE 協議,而只需要根據自身的需求更改相應任務參數即可,在 SimpleBLECentral_Init 函數中將 Central 的相關參數初始化,比如:

GAPCentralRole_SetParameter:設置 Central 可以掃描到的最大從端設備數目;

GATT_InitClient:初始化 GATT_Client,一般情況下 Central 做為 Client,Peripheral 作為 Server,Central 通過 GATT_WriteCharValue 和GATT_ReadCharValue 來和 Peripheral 進行通訊;

GATT_RegisterForInd:注冊當前任務為 GATT 的 notify 和 indicatee 的接收端,當 Peripheral 通過 notify 發來數據時,當前任務函數會收到數據;

osal_set_event:該函數會啟動任務函數 START_DEVICE_EVT以及SBP_PERIODIC_EVT,由此轉入系統任務的運行。

而我們的目的是在當前的任務處理機制中增加 W5500 的通訊連接維持以及通訊數據處理的工作,TI 的意圖是減少每項任務的處理時間,從而獲得系統的快速響應,否則單項任務處理時間過長的話就會因為任務延遲而造成 BLE 連接中斷。基於此原理,應盡量減少 W5500 每個任務循環所占用的時間,從而獲得盡快的任務響應,所以應該減少原始的程序中任務等待和delay時間,我在將 W5500 的通訊任務函數 do_tcpclient() 放進主循環之後也對本函數進行了一些瘦身處理。

01 void osal_start_system( void )

02 {

03 #if !defined ( ZBIT ) && !defined ( UBIT )

04     for (;;) // Forever Loop

05 #endif

06     {

07 #ifdef BLECentral

08         do_tcpclient();

09 #endif

10

11         osal_run_system();

12     }

13 } 

01 void do_tcpclient(void)

02 {

03     uint8 s=0;

04     uint16 anyport=3000;      /*定義一個任意端口並初始化*/

05

06     switch (getSn_SR(s))

07     {

08     case SOCK_CLOSED:      //查詢Socket狀態是closed狀態時,初始化當前Socket

09         socket(s,Sn_MR_TCP,anyport++,Sn_MR_ND);

10         break;

11

12     case SOCK_INIT:        //查詢Socket狀態是初始化狀態時,打開此Socket

13         connect(s,pc_ip,pc_port);

14         break;

15

16     case SOCK_ESTABLISHED:  //查詢Socket狀態是連接已建立狀態時,進行數據通信

17

18         break;

19

20     case SOCK_CLOSE_WAIT:  //查詢Socket狀態是等待關閉狀態時,關閉此Socket

21         close(s);

22         break;

23     }

24 }

大家可能注意到了,在 SOCK_ESTABLISHED 狀態下,沒有任何實際處理,那是因為我們為了減少任務處理時間,將 Socket 數據處理任務放在了前文提到的SBP_PERIODIC_EVT 中,這個任務由 SimpleBLECentral_ProcessEvent 調用,並在任務的末尾進行重新委托,以 SBP_PERIODIC_EVT_PERIOD 為間隔循環調用,從而實現定時查詢 Socket 狀態,發送和接收 W5500 的緩存中數據。

01 if ( events & SBP_PERIODIC_EVT )

02 {

03     if ( SBP_PERIODIC_EVT_PERIOD )

04     {

05         if ((getSn_SR(0))== SOCK_ESTABLISHED)

06         {

07             if (getSn_IR(0) & Sn_IR_CON)

08             {

09                 setSn_IR(0, Sn_IR_CON);

10             }

11             len=getSn_RX_RSR(0);

12             if (len>0)

13             {

14                 recv(0,buffer,len);       //接收Socket數據

15                 buffer[len]=0×00;

16                 SerialPrintString(buffer); //串口打印接收到Socket的數據

17                 //printf(“%srn”,pub_buf);

18                 w_send(0,buffer,len);     //向Socket發送接收到的數據,方便調試

19                 sbpGattWriteString(buffer,len);//將接收的數據向Peripheral發送

20             }

21         }

22         osal_start_timerEx( simpleBLETaskId, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD );

23     }

24     return (events ^ SBP_PERIODIC_EVT);

25 }

           至此,W5500 的集成已經完成,下一步是處理任務數據,在原始例程中,已經有指令處理部分,比如 AT+SCAN 是搜索 Peripheral,AT+CON1 是與搜索到的第一個從端建立鏈接,這部分我們無需處理,只需要把門鎖的控制指令按標准數據發送就可以。

BLE門鎖部分:

           在完成 BLE 以太網網關之後,我們下一步進行門鎖部分的工作,作為Peripheral,程序上和 Central 大同小異,Central 搜索 Peripheral,並通過GATT_WriteCharValue 和 GATT_ReadCharValue 查詢和調用Peripheral上具體的服務;我們要做的是,在系統抽像層的任務調用機制中增加步進電機控制機制,以及在接收到 Central 發送的門鎖的控制指令後執行相應的開鎖和閉鎖動作,另外還可以增加一個干簧管用作磁控開關用來判斷門的閉合狀態,如圖 6 所示。

image

                                      圖 6 BLE 門鎖步進電機傳動部分

           本文選用的 5V 供電的 5 線 4 相步進電機,步進電機的原始例程是通過Delay 函數來實現延遲的,而在 TI 的這種盡量減少每個任務處理時間的機制下,我們需要更改為定時器 Timer3 觸發來執行步進電機的控制,更改部分如圖7所示。

image

圖7 定時器改進部分代碼

01 void Moter_GPIO_init(void)

02 {

03     P1DIR |= BV(0) | BV(1) | BV(2)| BV(3);         //P1.0定義為輸出口

04     P1SEL &= ~(BV(0) | BV(1) | BV(2)| BV(3)); //P1.0定義為一般GPIO

05

06     T3CTL |= BV(3); //開啟溢出中斷

07     T3CTL |= BV(5) | BV(6) | BV(7) ;//128分頻

08     //T3CTL &= ~(BV(0) | BV(1)); //0×00~0xff,重復

09     T3CC0 = 0x4f;

10     T3CTL |= BV(0)| BV(1);

11

12     T3CTL |= BV(4); //啟動timer3

13     T3IE =1; //開啟T3中斷控制

14     EA=1;

15 }

         在 SimpleBLEPeripheral_Init 函數中進行了步進電機控制 IO 的初始化Moter_GPIO_init(); 並在其中將 Timer3 進行了初始化;選取 P1.0,P1.1,P1.2,P1.3,分別作為步進電機四相 ABCD ;如果按照 A-B-C-D 的順序就是正轉,也就是開門,相應 D-C-B-A 就是反轉,也就是閉門操作;

Timer3 的中斷觸發執行如下步驟

01 #pragma vector = T3_VECTOR

02 __interrupt void Timer3_ISR(void)

03 {

04     IRCON = 0×00;

05     /*這裡實現每隔0.5秒翻轉一次,128分頻,由於timer3為8為,從0×00~0xff,

06     128/16M *Number=0.5s,所以Number=62500次

07     運行255次,count*255=62500,所以,count=245次*/

08     if (xxcount++>10)

09     {

10         xxcount=0;

11         if (Motor_AB_flag > 0 && Motor_BA_flag == 0)

12         {

13             MotorCW();

14             Motor_AB_flag++;

15             if (Motor_AB_flag >= 1024)

16             {

17                 MotorStop();

18                 Motor_AB_flag = 0;

19                 sbpSerialAppSendNoti(“unlockrn”,8);

20             }

21

22         }

23         if (Motor_BA_flag > 0 && Motor_AB_flag == 0)

24         {

25             MotorCCW();

26             Motor_BA_flag++;

27             if (Motor_BA_flag >= 1024)

28             {

29                 MotorStop();

30                 Motor_BA_flag = 0;

31                 sbpSerialAppSendNoti(“lockrn”,6);

32             }

33         }

34     }

35 }

           可以看到在中斷觸發中根據 Motor_AB_flag 和 Motor_BA_flag 的狀態來執行開鎖和閉鎖動作,當執行 1024 個周期後停止動作並將相應的標志位置 0;而Motor_AB_flag 和 Motor_BA_flag 的狀態這時根據 Central 發送過來的數據進行相關解析之後進行的,在Peripheral的初始化函數 SimpleBLEPeripheral_Init 中注冊了一個接收數據的回調函數 simpleProfileChangeCB。

           當Central對Peripheral 發送數據更改 PROFILE 服務的 CHAR1 字段時,下面的步驟被觸發

01 case SIMPLEPROFILE_CHAR1:

02 //SimpleProfile_GetParameter( SIMPLEPROFILE_CHAR1, &newValue );

03 SimpleProfile_GetParameter( SIMPLEPROFILE_CHAR1, newValueBuf );

04 if (newValueBuf[0]== ‘a’ && newValueBuf[1]== ‘d’ && newValueBuf[2]== ‘m’ && newValueBuf[3]== ‘i’ && newValueBuf[4]== ‘n’)

05 {

06     if (newValueBuf[5] == ‘?’)

07     {

08         BLE_REPLY();

09     }

10     else

11     {

12         if (newValueBuf[5]==’=’&&newValueBuf[6]== ‘A’ && newValueBuf[7]== ‘B’)

13         {

14             Motor_AB_flag = 1;

15         }

16         else if(newValueBuf[5]==’=’&&newValueBuf[6]==’B’&& newValueBuf[7]== ‘A’)

17         {

18             Motor_BA_flag = 1;

19         }

20         else

21         {

22             SetRGB(newValueBuf[5], newValueBuf[6], newValueBuf[7]);

23             sbpSerialAppWrite (newValueBuf, 20);

24 #if (defined HAL_LCD) && (HAL_LCD == TRUE)

25             HalLcdWriteString((char*)newValueBuf, HAL_LCD_LINE_4 );

26 #endif // (defined HAL_LCD) && (HAL_LCD == TRUE)

27         }

28     }

29 }

30

31 break;

為了安全的需要,我們定義了一個密碼段,這個密碼段可以進一步改進為 SSL 加密或者 RSA 算法加密,本文以 admin 為例,查詢狀態指令為”admin?”,Peripheral 根據當前”lock”或”unlock” 狀態以及磁控開關回復當前狀態;開鎖指令目前為”admin=AB”;閉鎖為”admin=BA”。

 系統測試:

系統測試部分我們可以按照手機 APP(BLE)控制門鎖和網絡控制門鎖兩部分進行:

1.使用手機的藍牙和 BLE 門鎖通訊

為了方便測試 BLE,需要在手機上安裝一個 APP“LightBlue – Bluetooth Low Energy”,

image

圖8 APP LightBlue – Bluetooth Low Energy

可以測試 BLE 的數據收發等工作,我們在 apple store 上安裝此 app 之後,就可以搜索到 BLE 門鎖“Smart Lock”,如圖9所示。

image

在選擇 “Smart Lock” 並對UUID :FFF0的服務Characteristic1字段寫入HEX字符的admin=AB“0x61646D696E3D4142”,如圖10所示;之後就可以聽到“Smart Lock”傳來的齒輪摩擦聲,成功。

1.使用 BLE 以太網網關和 BLE 門鎖通訊

網關通訊測試我分為兩部分進行:

1)通過 BLE 以太網網關的串口指令和 ”Smart Lock” 配對並控制門鎖開關,如圖11

image

                                 圖 11 通過串口指令與”Smart Lock”配對

我們一共發送了三個指令:

AT+SCAN    //搜索從端設備

AT+CON1    //與搜索到的第一個設備建立連接

admin=AB   //前面兩個是發送給網關的指令,而 admin=AB 會作為數據發送給從端從端在接收到本數據之後控制電機轉動,到達指令周期之後停轉並復 “unlock”給網關,控制周期完成。

1.通過 socket 發送數據給 ”Smart Lock” 控制門鎖開關

BLE 門鎖 ”Smart Lock” 在上電之後作為 TCP Cllient 去和 Server 建立通訊連接並等待接收數據,當通訊連接建立之後,我們通過 Socket 測試工具 “Pdu Receiver”發送控制指令給 “W5500+BLE” 網關,網關會把數據轉發給 ”Smart Lock”,從而控制步進電機按周期轉動,如圖12所示。 

image

                          圖12 通過測試工具Pdu Receiver進行門鎖控制

最後至此,已經實現了藍牙 BLE 網關對門鎖的智能控制,希望能給大家提供一個DIY 的思路。那麼,在智能家居的大潮中,藍牙 BLE 技術的應用已被越來越多廠商關注並采用,在這裡借助 W5500 的硬件協議棧,給大家提示一個更便捷的聯網方式,進一步減輕智能網關開發的難度,真正做到制作簡便、智能易用!

本文已刊登至《无线电》六月刊