做者:Vamei 出處:http://www.cnblogs.com/vamei 嚴禁轉載。html
藍牙是一個使用普遍的無線通訊協議,這兩年又隨着物聯網概念進一步推廣。我將介紹藍牙協議,特別是低功耗藍牙,並用樹莓派來實踐。樹莓派3中內置了藍牙模塊。樹莓派經過UART接口和該模塊通訊。樹莓派1和樹莓派2中沒有內置的藍牙模塊,不過你能夠經過USB安裝額外的藍牙適配器。node
藍牙最初由愛立信創制,旨在實現可不一樣設備之間的無線鏈接。藍牙無線通訊的頻率在2.4GHz附近,和WiFi同樣,都屬於特高頻。相對於低頻信號來講,高頻傳輸的速度比較快,穿透能力強,但傳輸距離比較受限。在沒有遮蔽和干擾的狀況下,藍牙設備的最大通訊距離能達到30米。但在大多數狀況下,藍牙的實際通訊距離在2到5米。相比之下,低頻433MHz設備的通訊距離很容易超過百米。所以,藍牙經常使用於近距離的無線設備,好比無線鼠標和鍵盤。git
藍牙的標誌 github
藍牙的基本工做流程以下:web
根據細節上的差異,藍牙通訊又細分爲兩種:經典藍牙和低功耗藍牙。早期的藍牙通訊方式稱爲經典藍牙(classic bluetooth)。經典藍牙中的數據傳輸協議是串行仿真協議RFCOMM。RFCOMM仿真了常見的串口鏈接。數據從一端輸入,從另外一端取出。經典藍牙的開發很是簡單。基於串口開發的有線鍵鼠程序,就能夠直接用於RFCOMM鏈接的無線鍵鼠。此外,經典藍牙能夠快速傳輸數據。所以,諾基亞N95這樣的早期智能手機,也用RFCOMM來互傳圖片和文件。shell
RFCOMM通訊npm
經典藍牙的缺點是比較耗電。後來,諾基亞發明了一種能夠下降功耗的藍牙通訊方式。2010年出臺的藍牙4.0把這種通訊方式規範爲「低功耗藍牙」(BLE,Bluetooth Low Energy)。BLE把通訊雙方分爲非對稱的雙方,儘可能讓其中的一方承擔主要的開銷,減小另外一方的負擔。舉例來講,手環電量少,並且須要長時間待機。BLE通訊的主要負擔能夠放在電量較充裕且充電方便的手機一側,從而減小手環的能耗。bash
手環做爲外設服務器
BLE通訊通常也包含廣播/掃描的步驟。主動發起廣播的設備稱爲外設(Peripheral),掃描設備稱爲中心設備(Central)。BLE鏈接成功以後,就能夠開始數據傳輸。BLE的數據傳輸協議是ATT和GATT協議。ATT是GATT的基礎。ATT協議把通訊雙方分爲服務器(server)和客戶(client)。客戶主動向服務器發起讀寫操做。須要注意的是,ATT中的服務器和客戶,與廣播階段的外設和中心設備相互獨立。固然,在手環這樣的應用場景下,外設一般也是服務器。ATT協議以屬性(attribute)爲單位進行該數據傳輸。一個屬性的格式以下:併發
ATT屬性
咱們分別來理解屬性的不一樣部分:
服務器儲存了多個屬性。當客戶向服務器請求時,服務器會把本身的屬性列表發給客戶。隨後,客戶能夠向服務器讀取或寫入某一個屬性值。用讀寫的方式,通訊雙方實現了雙向通訊。
以智能手錶爲例。智能手錶和手機配對後,手機能夠用讀的方式得到智能手錶中某個屬性下保存的步數,也能夠用寫的方式寫入另外一個屬性負責的時間。在讀寫操做中,都是由客戶採起主動,服務器只能被動應答。ATT還提供了通知(notification)的工做方式。當服務器改變了某個屬性值時,能夠主動通知訂閱了該屬性值的客戶。智能手錶中的手勢識別,就能夠經過通知的方式告知手機。這樣的話,手機就能夠實時地獲知手勢改變信息。
GATT協議構建在ATT協議之上,爲屬性提供了組織形式。GATT的最小組織單元是Characteristic,能夠由數條屬性組成。下圖中就是一個Characteristic,用於傳輸紅外測溫得到的數據。這個例子來自TI的SensorTag:
從左到右:handle(16進制),handle(10進制),type(16進制),type(文字說明),value(16進制),permission,備註
Characteristc的第一條屬性用於聲明屬性,其類型老是0x2803。這條聲明的value部分又能夠細分爲三部分。第一個部分是0x12,稱爲Characteristic Properties,是GATT協議層面上的權限控制。其具體含義可參考資料。第二部分0x0025,是Characteristic值的handle。找到handle爲0x0025的屬性,就在聲明屬性的下面一行。0x0025的value部分就是紅外溫度的真正數值。剩下的部分是該Characteristic的UUID,總共128位:
F000-AA01-0451-4000-B000-000000000000
檢查Characteristic值的那一行屬性,也就是0x0025屬性。它的類型也是該Characteristic UUID。除了128位的UUID,藍牙官方還提供了16位的UUID可供使用,可參考資料。
能夠看到,一個Characterstic至少須要兩個屬性,一個用於聲明,一個用於儲存它的數據。除此以外,Characteristic還有稱爲Descriptor的額外描述信息。每一個Decriptor佔據一行。好比0x0027這個Descriptor,其屬性值是54:65:6D:70:7E:20:44:61:74:61,翻譯成ASCII就是:
Temp~ Data
此外,溫度單位、測量頻率等描述信息也常常會以Descriptor的形式放入到Characteristic中。在下一個Characteristic聲明出現前的屬性,都是該Characteristic的Descriptor。
咱們再來看更高級的組織單位Service。一個Service也有行屬性做爲聲明,其類型UUID是0x2800。聲明屬性的值就是該Service的128位UUID。藍牙官方也提供了16位的UUID,預留給特定的Service,可參考資料。在下一個Service聲明出現前的屬性,都屬於該Service,好比下圖中從0x0023到0x002D的屬性:
圖中包含了一個與紅外溫度計相關的Service。Service裏又有三個Characteristic,分別0x0024-0x002七、0x0028-0x002A、0x002B-0x002D。我已經介紹過第一個Characteristic。第二個Characteristic用於傳輸溫度計參數,第三個用於設置測溫頻率。
Service和Characteristic都是屬性的組織形式。客戶能夠向服務器請求Service和Characteristic列表,而後對其進行操做。GATT還提供了Profile,能夠包括多個Service。不過,Profile並不像前面二者那樣存在於服務器。Profile是一種標準,用於說明一個特型設備應該有哪些Service。好比說,HID(Human Interface Device)這種Profile,就說明了藍牙輸入設備應該提供的Service。藍牙官方定義的Profile可參考資料。
咱們用樹莓派來深刻實踐上面學到的藍牙知識。首先要在樹莓派上安裝必要的工具。BlueZ是Linux官方的藍牙協議棧。你能夠經過BlueZ提供的接口,進行豐富的藍牙操做。Raspbian中已經安裝了BlueZ。我使用的版本是5.43。你能夠檢查本身的BlueZ版本:
bluetoothd -v
低版本的BlueZ對低功耗藍牙的支持有限。若是你的使用版本低於5.43,那麼我建議你升級BlueZ。
你能夠用下面的命令檢查BlueZ的運行狀態:
systemctl status bluetooth
個人返回結果是:
● bluetooth.service - Bluetooth service
Loaded: loaded (/lib/systemd/system/bluetooth.service; enabled)
Active: active (running) since Sun 2017-04-23 19:03:08 CST; 1 day 6h ago
Docs: man:bluetoothd(8)
Main PID: 709 (bluetoothd)
Status: "Running"
CGroup: /system.slice/bluetooth.service
└─709 /usr/lib/bluetooth/bluetoothd -C
能夠看到,藍牙服務已經打開,並在正常運行。
你能夠用下面命令手動啓動或關閉藍牙服務:
sudo systemctl start bluetooth sudo systemctl stop bluetooth
此外,你還可讓藍牙服務隨系統啓動:
sudo systemctl enable bluetooth
在Raspbian中,基本的藍牙操做能夠經過bluez中的bluetoothctl命令進行。該命令運行後,將進入到一個新的Shell。在這個shell中輸入:
list
將顯示樹莓派上可用的藍牙模塊,例如:
Controller B8:27:EB:72:47:5E raspberrypi [default]
運行scan命令,開啓掃描:
scan on
掃描啓動後,用devices命令,能夠打印掃描到藍牙設備的MAC地址和名稱,例如:
Device 00:9E:C8:62:AF:55 MiBOX3
Device 4D:CE:7A:1D:B8:6A vamei
此外,你還能夠用help命令得到幫助。使用結束後,你能夠用exit命令推出bluetoothctl。
除了bluetoothctl,在Raspbian是shell中能夠經過hciconfig來控制藍牙模塊。好比開關藍牙模塊:
sudo hciconfig hci0 up #啓動hci設備
sudo hciconfig hci0 down #關閉hci設備
命令中的hci0指的是0號HCI設備,即樹莓派的藍牙適配器。
與此同時,你能夠用下面命令來查看藍牙設備的工做日誌:
hcidump
bluez自己還提供了鏈接和讀寫工具。但不一樣版本的bluez相關功能的差別比較大,並且使用起來不太方便,因此我下面使用Node.js的工具來實現相關功能。
下一步,咱們嘗試用樹莓派進行BLE通訊。咱們先把一個樹莓派改形成BLE外設,同時它也將充當鏈接創建後的服務器。這個過程較爲複雜。你能夠借用Node.js下的bleno庫。首先,安裝Node.js:
curl -sL https://deb.nodesource.com/setup_5.x | sudo bash -
sudo apt-get install nodejs
第一行的命令是爲了確保安裝高版本的Node.js。
安裝bleno:
mkdir ble-test-peripheral
cd ble-test-peripheral
npm install bleno
運行pizza的例子:
sudo node node_modules/bleno/examples/pizza/peripheral
你能夠在node_modules/bleno/examples/pizza/看到源代碼,或者到github查看。這個例子提供了一個Service,它的UUID是1333-3333-3333-3333-3333-333333333337。Service中包含了三個Characteristics,分別是用於披薩餅參數、配料參數和烤披薩:
功能 | 權限 | UUID |
披薩餅選項 | 讀/寫 | 13333333333333333333333333330001 |
配料 | 讀/寫 | 13333333333333333333333333330002 |
烤披薩 | 寫/通知 | 13333333333333333333333333330003 |
經過這些Characteristic,咱們能夠對樹莓派進行BLE讀寫。讀寫操做會做用於一個表明比薩的對象。披薩餅選項有:
數值 | 描述 |
0x00 | 正常 |
0x01 | 厚 |
0x02 | 薄 |
配料是一個8位的參數,每一位表明了一種配料。當這一位是1時,那麼說明添加該配料:
第n位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
描述 | SAUSAGE | BELL_PEPPERS | PINEAPPLE | CANADIAN_BACON | BLACK_OLIVES | EXTRA_CHEESE | MUSHROOMS | PEPPERONI |
所以,0x1A表明了添加MUSHROOMS、BLACK_OLIVES、CANADIAN_BACON,感受味道還不錯。
對於烤披薩來講,寫操做設定了烘烤的溫度和時間。時間到了以後,中心設備會發出通知,告訴客戶端烘烤完成。咱們下一步將用另外一個樹莓派做爲BLE中心設備。不過,即便你沒有額外的樹莓派,你能夠用iPhone上LightBlue這樣的App來測試這一部分完成的BLE外設。
咱們拿另外一個做爲BLE的中心設備進行掃描,併發起鏈接請求。鏈接創建後,該服務器將充當客戶。和bleno對應,Node.js下有一個叫noble的項目,能夠便捷地完成這一任務。首先,安裝noble:
mkdir ble-test-central
cd ble-test-central
npm install noble
noble中有一個一樣名爲pizza的例子,不過這個例子實現的是客戶端。運行該例子:
sudo node node_modules/noble/examples/pizza/peripheral
這個例子將自動執行掃描、鏈接、服務發現、數據傳輸的全過程。若是你把bleno和noble部署到兩個樹莓派上,就能夠在這兩個樹莓派之間進行藍牙通訊了。若是你想自定義開發,那麼能夠在node_modules/noble/examples/pizza/參考源代碼,或者到github查看。
蘋果在BLE的基礎上推出了iBeacon協議。iBeacon使用了BLE的廣播部分,但不創建鏈接。一個遵照iBeacon協議的外設稱爲Beacon。Beacon會廣播本身的身份信息和發射信號的強度。中心設備接收到廣播以後,除了能夠獲知Beacon的身份以外,還能經過信號的衰減算出本身與Beacon的距離。在一個典型的超市應用場景中,每件商品能夠帶上一個Beacon。消費者能夠用手機看到本身周圍有哪些商品,工做人員也能夠用手機來清點貨物。商家還能夠在服務器上提供商品相關的質保、促銷等信息。用戶能夠根據Beacon的編號,得到這些附加信息。
咱們把配備了藍牙模塊的樹莓派改形成一個Beacon。既然Beacon只使用了藍牙中的廣播,那麼應該關閉樹莓派的掃描,打開廣播,而且不接受藍牙鏈接:
sudo hciconfig hci0 noscan # 再也不掃描 sudo hciconfig hci0 leadv 3 # 開始廣播,而且不接受鏈接
下一步,把廣播信息改成符合iBeacon協議的內容:
sudo hcitool -i hci0 cmd 0x08 0x0008 1E 02 01 1A 1A FF 4C 00 02 15 63 6F 3F 8F 64 91 4B EE 95 F7 D8 CC 64 A8 63 B5 00 01 00 02 C5
上面的命令附加了一串16進制信息。其中0x08說明了整條信息是藍牙命令,0x0008說明後面的內容將做爲廣播信息。
1E是廣播信息開始的標誌。按照藍牙通訊的規定,廣播信息最多有31個字節。1E後面的廣播信息分爲兩組:
02 01 1A
1A FF 4C 00 02 15 63 6F 3F 8F 64 91 4B EE 95 F7 D8 CC 64 A8 63 B5 00 01 00 02 C5
每一組一開始的一個字節說明了該組信息的長度。02說明了2個字節,1A說明是26個字節。隨後一個字節說明了改組信息的類型。第一組的01說明了該組信息是藍牙控制標誌,第二組的FF說明了該組是藍牙製造商相關信息。
咱們來看第二組信息的細節:
在iPhone上安裝應用Locate Beacon來測試。當我進入到樹莓派的廣播範圍時,該應用就會顯示出手機距離樹莓派的距離。
使用結束後,能夠用下面命令來恢復掃描和中止廣播:
sudo hciconfig hci0 piscan # 恢復掃描 sudo hciconfig hci0 noleadv # 中止廣播
這裏簡單介紹了藍牙協議,特別是低功耗藍牙。我以樹莓派的藍牙模塊爲基礎,實現了BLE通訊。
歡迎閱讀「騎着企鵝採樹莓」系列文章
個人博客即將搬運同步至騰訊雲+社區,邀請你們一同入駐:https://cloud.tencent.com/developer/support-plan