背景
-
藍牙歷史
說到藍牙,就不得不說下藍牙技術聯盟(Bluetooth SIG),它負責藍牙規范制定和推廣的國際組織。如果需要開發藍牙硬件產品,應該會和他們打交道。有些小伙伴可能只聽過藍牙1.0,藍牙2.0,藍牙3.0,藍牙4.0之類的以數字結尾的藍牙版本號。這些是以前的標準,而最新的標準中,已經不使用數字版本號來作為藍牙版本的區分了,取而代之的就是經典藍牙以及低功耗藍牙。稍微提下藍牙版本的演進過程:99年藍牙1.0發布,后面就是藍牙2.1。藍牙2.1的版本使用最廣,很多產品都是這個版本,也就是我們所謂的經典藍牙,以前非智能手機也是支持這個版本的。到了藍牙3.0,又名為高速藍牙,在2.1的基礎上大大提升了傳輸速度(24Mbps)。后面4.0/4.1引入了低功耗藍牙。藍牙5.0也已經發布。主要是在信號范圍,連接速度,以及廣播速度進行了優化,藍牙5.0還對物聯網方向做了單獨的改進。
Ps:可能很多人會誤解藍牙4.0就是低功耗藍牙,但是完整的藍牙4.0規范實際上是包含經典藍牙和低功耗藍牙兩部分的。只是說在藍牙4.0規范,藍牙技術聯盟才引入了BLE。
-
低功耗藍牙--BLE
全稱是Bluetooth Low Energy,簡稱BLE。也就是說最大的一個特點就是低功耗了。有一些BLE設備一個紐扣電池可以使用一兩年。當然所有的優點和缺點都是相對的,而BLE的對立面當然就是歷史悠久的經典藍牙了。經典傳統藍牙有3個功率級別,Class1,Class2,Class3分別支持100m,10m,1m的傳輸距離,一般要求至少使用兩節3A電池。而且工作幾天或者幾周就沒電了。前面說了低功耗藍牙一個紐扣電池就可以工作一兩年,3A電池的電量一般是紐扣電池的10至12倍。這是BLE的一個很大的優點,尤其是對于現在的穿戴設備以及各種物聯網傳感器,BLE電池的續航是一個很大的突破點。正所謂有得必有失,低功耗帶來的低傳輸速率,當然BLE被設計的本來就是傳輸少量數據的,對于很多傳感器設備,例如心跳帶,血壓計等設備,是非常適合的。
-
BLE ON Android
從Android 4.3 Jelly Bean(API 18)才開始支持低功耗藍牙,但是僅僅是支持中心(Central)模式。所謂的中心模式,即可以連接其他藍牙外設。直到2014.6.26 Android Lollipop的面世,才帶來了周邊API的支持(BluetoothLeAdvertiser)。即Android 5.0以后的手機可以作為一個外設來進行發布。Android SDK 中 BLE 相關的 API 都在 android.bluetooth.* 下面,同時在 Android 5.0 也引入了一些也需要用到 android.bluetooth.le* 下面的 API。另外,要在 APP 中使用藍牙功能,需要在 Manifest 中申請藍牙相關的權限。在 Android 6.0 及以上平臺中,還需要申請定位權限。
協議棧
藍牙4.1BLE協議棧的結構圖如下:
<div align=center>
</div>
下面是詳細介紹各個層級的含義:
PHY(Physical Layer):物理層,藍牙是工作在2.4GHz附近,這是工業、科學、醫療ISM的頻段,免許可證。WIFI也是工作在同一個頻段。藍牙把頻段切分為40個通道,3個廣播通道,37個數據通道,按照一個規律跳頻通信。
LL(Linker Layer):鏈路層,用于控制設備的射頻狀態,設備將處于五種狀態之一:等待、廣告、掃描、初始化、連接。廣播設備不需要建立連接就可以發送數據,而掃描設備接收廣播設備發送的數據;發起連接的設備通過發送連接請求來回應廣播設備,如果廣播設備接受連接請求,那么廣播設備與發起連接的設備將會進入連接狀態。發起連接的設備稱為主機,接受連接請求的設備稱為從機。
HCI(Host Controller Interface):主機和控制器就是通過這個接口來進行同學的,通信的介質就是HCI命令。這層在協議棧中是可選的,一些小型終端可能沒有,但是Android設備上肯定有,這層是藍牙上層和芯片的交互必經之路,對于藍牙硬件開發者,這里的log能夠很好的幫助解決問題。
HOST部分要復雜一些,有鏈路控制和適配層(L2CAP),安全管理(SM)等。其中L2CAP和SM我們知道概念就可以了,這里就不多關注了。我們重點來看屬性協議層,也就是ATT。它是整個BLE通信的基礎。ATT負責數據封裝,向外暴露為"屬性",提供"屬性"的為服務端,獲取"屬性"的為客戶端。ATT是專門為BLE低功耗藍牙而設計的傳輸協議,結構簡單,傳輸數據短(后面會有提及)。
-
GATT(Generic Attribute Profile):全稱叫做通用屬性配置文件,是基于ATT做進一步的邏輯封裝,定義數據的交互方式和含義,APP的開發其實就已經接觸到這一層了。GATT按照層級定義了三個非常重要的概念:服務(Service)、特征(Characteristic)、描述(Descripter)。他們之間的關系如下:
<div align=center>
圖片名稱
</div>
一個Service可以包含若干個Characteristic,一個Characteristic可以包含屬性(properties)和值(value),還可以包含多個descripter。Characteristic實際上具有讀、寫、通知等權限,我們在對一個BLE設備發起連接成功以后,對他進行讀寫,其實就是對Characteristic的讀寫或者訂閱通知。圖中所謂的Profile,實際上是一組服務的集合,這些服務被人組合起來就形成了一個特定的使用場景。比如說,小米手環,里面就有一個計算用戶當前步數的服務。這就是這個Ble可以做的事情,也就是它的profile。
第5個我們又提到一個BLE設備實際上就是一組Service的集合,那BLE藍牙以什么來標識多個Service呢。答案就是UUID。BLE中的Service,Characteristic,Descripter都是使用UUID來作為唯一標識。所以我們在讀寫BLE藍牙數據時,都要帶上相應的UUID。
GAP(Generic Access Profile):通用訪問控制配置文件。定義了BLE整個通信過程中的流程,負責處理設備訪問模式和程序,包括設備發現,建立連接,終止連接,初始化安全特性,設備配置。GAP層總是作為下面四種角色之一:(1)廣播者:不可連接的廣播設備。(2)觀察者:掃描設備,但不發起建立連接。(3)外部設備:可連接的廣播設備,可以在單個鏈路層連接中作為從機。(4)集中器:掃描廣播設備并發起連接,可以在單個鏈路層連接中作為主機。
應用開發
BLE應用可以分為兩大類:基于非連接的和基于連接的。
基于非連接的:意思就是外設和周邊設備不發生連接,外設主要依賴周邊設備發出的BLE廣播,也叫做Beacon。這里有兩個角色,發送廣播的一方叫做Broadcaster。監聽廣播的一方叫做Oberver。這個在藍牙協議棧的GAP層都有相應的角色定義。
基于連接的:就是外設和周邊設備要建議顯式的GATT連接,需要雙方有通信。這個也有兩個角色,外設設備(周邊)叫做Peripheral。中心設備(一般是手機)叫做Centeral。
下面就對于這兩類應用做更詳細的解釋,其實搞懂了這些,基本上BLE藍牙開發的整個流程及原理就搞得很清楚了。
Beacon
基于非連接的BLE應用,下面是它的網絡拓撲結構:
<div align=center>
</div>
由于廣播是單向的,Broadcaster向外發送廣播,Observer接受廣播。總體來說是多對多的關系。BLE廣播中也能夠帶上數據,包括某些私有協議等。所以基于這種協議也是能夠開發出獨特的應用。完全基于廣播的應用,有大名鼎鼎的iBeacon,這是蘋果公司基于BLE廣播實現的功能,可以實現廣告推送以及室內定位。包括某些商場內部柜臺尋找的功能,也是基于這種協議開發出來的。
在上圖我畫的設備都是單一的角色,而實際上有些設備是可以同時實現兩種角色的,它即可以發送廣播,也可以接受廣播。想象一下,在現在比較火的智能家居系統中,有很多的傳感器,如果一個設備接受到廣播,做了處理以后在發送出去,就形成了一個雙向的網格,是不是就有點像因特網了,這也就是有名的藍牙Mesh。有興趣的同學可以去看看。
關于Beacon應用里有一個很重要的點就是廣播包的數據結構。數據包的格式如下圖(官方圖):
<div align=center>
</div>
從圖中就可以看出,每個廣播數據包包含31個字節,分為有效數據以及無效數據兩部分。
無效數據部分:因為廣播包的長度必須是 31 個 byte,如果有效數據部分不到 31 自己,剩下的就用 0 補全。這部分的數據是無效的,解釋的時候,忽略即可。
有效數據部分:包含若干個廣播數據單元,稱為 AD Structure。如圖中所示,AD Structure 的組成是:第一個字節是長度值 Len,表示接下來的 Len 個字節是數據部分。數據部分的第一個字節表示數據的類型 AD Type,剩下的 Len - 1 個字節是真正的數據 AD data。其中 AD type 非常關鍵,決定了 AD Data 的數據代表的是什么和怎么解析。
我們詳細來解釋一下這個有效數據部分。上面的解釋可能比較抽象,這里我用一個例子來解釋一下:
E/TAG:scandata:02011A05FFAC0134560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
我們只截取有效數據部分: 02011A05FFAC013456。我們按照圖中的協議進行一下分隔,02 01 1A 05 FF AC013456。我們看第一個字節為02,表示后面兩個字節為一段數據。第二個字節為01,表示就是數據的type。1A就是這個type的內容數據了。05 表示后面5個字節是一個數據段,FF表示一種數據type。AC013456就是數據。掃描的數據塊都是通過這種協議來組織的,所以這里最重要的就是這個數據type。官方解釋看這里。我稍稍做了部分整理,如下:
<div align=center>
</div>
掃描的工作流程:
<div align=center>
</div>
對于掃描的建議:1、首先,盡可能使用新的 API,功能更強大;2、盡可能少地掃描,因為畢竟掃描是一個比較重的操作,耗電,也會減慢 BLE 連接速度;3、掃描的時候,盡量設置 ScanFilter,只掃描那些你感興趣的設備,而不是全盤掃描;4、正確使用 API,特別是合理停止掃描,防止資源泄漏,開始掃描的callback一定要和停止的callback是一個對象,防止底層句柄造成泄露。
基于連接的藍牙應用(Connection App)
網路拓撲結構:
<div align=center>
</div>
拓撲結構比較簡單,一個中心設備可以連接多個外設,但是一個外設只能連接一個中心(主要是因為連接成功以后,外設就會停止對外廣播,別人則發現不了它了)。其實一個中心連接的外設設備數量也是有限的,據說是7個。原因不明,應該底層芯片做了限制。
這篇文章中我就不貼出具體的代碼了(下篇介紹藍牙連接sdk的時候會有)。先通過一個demo來了解一下GATT的結構,以求更深的理解基于連接的藍牙應用的原理。先看下面的demo圖,圖里面是一個客戶端連接服務端后返回的數據。
<div align=center>
</div>
圖片左邊的外設我一個私有服務都沒寫,但是藍牙聯盟預定了一些服務,分別是1801和1800。這兩個都是官方指定的service。1800和1801分別表示Generic Access和Generic Attribute,描述了設備連接相關屬性。有的設備(小米手環)還有1802這個官方服務,表示Immediate Alert,如果有這個服務,則說明外設設備支持即時提醒功能,如果向其中包含的Characteristic寫入一個值,那么設備應當可以發出對應的響聲。而右邊的圖,很明顯多了兩個私有服務,這個私有服務也只有服務提供者知道。關于這塊的代碼會在下一篇文章的藍牙連接sdk中一并發出。這里只是知道原理即可。
連接的工作流程:
<div align=center>
</div>
可以看出來,BLE從連接到讀寫還是比較復雜的,各種回調。客戶端先發起connect請求,不像讀寫更多的是軟件來完成,connect實際上是雙方芯片來完成然后回調給各自上層。所以不管服務端和客戶端,都會有一個onConnectStateChanged回調。參數也是一致的,對于客戶端來說,返回的BluetoothDevice就是外設設備,對于外設設備返回的就是中心設備。這里有一點需要注意,由于BLE藍牙并不想經典藍牙有永久mac地址一說,對于經典藍牙,獲取到mac地址后,這個藍牙的mac地址就是不變的,所以可以做離線連接。對于BLE,mac地址在每次重新廣播都會發生改變,從而導致當客戶端觸發onConnectionStateChanged后獲取的BluetoothDevice的mac地址都是不一樣的。這個是藍牙官方規定,無法修改。那對于BLE藍牙,如果做離線連接就要復雜的多。
圖中倒數第二和第三是BLE特有的機制,這里簡單說明一下,如果某個服務設置了Notify或者Indicate機制(UUID是固定的)。則改服務上的該屬性可以接受服務端對客戶端的notify,前提是連接完成以后客戶端需要設置改屬性為true。這樣服務端可以推送一些固定的信息給客戶端,這就比較適合那種電量推送的場景。
結語
上面我們分別介紹了BLE藍牙的一些基本概念,并且分析了藍牙的協議棧。而且也講解了Android設備作為BLE應用的四個角色:監聽者、廣播者、中心設備以及外設。也提到了一些我在從事醫療項目開發遇到的坑。
實際情況在我們平時的開發中,也許是硬件團隊丟給我們一臺設備,然后協議文檔,或許我們只需要了解兩個角色就可以了,監聽者和中心設備,然后對著API寫相關的業務代碼,但是我覺得了解協議棧或者藍牙服務端是如何工作的,才能更好的解決問題。
下篇文章中我會介紹封裝的Android藍牙連接sdk。即支持經典藍牙,也支持BLE藍牙。