前言
隨着可穿戴設備的越來越火爆,可移動終端設備上的應用也隨之熱門起來,現在主流的ios和android系統中,對於BLE藍牙的開發有些差別,ios由於其藍牙控制的穩定性,開發起來相對穩定簡單,而android上面對藍牙開發可就沒那麼簡單穩定了,在api19後換上新的Bluedroid協議棧後開始支持BLE4.0低功耗藍牙設備通信,系統對藍牙支持也差強人意,加上android系統開放性,國內各手機廠商對系統所謂的優化定製後,手機系統對藍牙的支持就不太穩定了,大部分情況都要對不同手機進行適配處理,導致android開發難度大大增加…
1.BLE藍牙的特點
- 近距離通信,典型距離是 10 米以內(BLE4.0)
- 速度最高可達 24 Mbps
- 低功耗 (峯值電流15mA,休眠電流 1uA)
- 低成本
- 低延遲連接 (3ms)
- 高可彈性 (多種模式)
- 高安全性 (128bit AES加密)
2.BLE 名稱解釋
2.1 GATT/ATT
GATT,全稱叫做通用屬性配置文件,它是建立在前面說的 ATT 的基礎上,對 ATT 進行進一步的邏輯封裝,定義數據的交互方式和含義。這是我們做 BLE 開發的時候直接接觸的概念。GATT 按照層級定義了三個概念:服務(Service)、特徵(Characteristic)和描述(Descriptor)。他們的包含關係如右邊這個圖所表示的:一個 Service 包含若干個 Characteristic,一個 Characteristic 可以包含若干 Descriptor。而 Characteristic 定義了數值和操作。Characteristic 的操作這幾種權限:讀、寫、通知等權限。我們說的 BLE 通信,其實就是對 Characteristic 的讀寫或者訂閱通知。還有最外面一層,Profile配置文件,把若干個相關的 Service 組合在一起,就成爲了一個 Profile,Profile 就是定義了一個實際的應用場景。
2.2 UUID
Service、Characteristic 還有 Descriptor 都是使用 UUID 唯一標示的。具體的表現形式,我們在後面會講到。我們先來說說 UUID 是什麼? UUID 是全局唯一標識,它是 128bit 的值,爲了便於識別和閱讀,一般標示程如下的形式,8-4-4-12 的16進制標示。
關於 UUID 有一些規範,爲了避免衝突,一般都不會自己手動去定義。例如 Android 中提供了 UUID.randomUUID() 來生成一個隨機的 UUID。我們也看到,UUID 有點太長了,在低功耗藍牙中這種數據長度非常受限的情況下,使用起來肯定不方便,所以藍牙又使用了所謂的 16 bit 或者 32 bit 的 UUID。其實本質上並沒有什麼 16bit 或者 32 bit UUID,藍牙 SIG 定義了一個基礎的UUID(Bluetooth Base UUID),形式如下。除了 XXXX 那幾位意外,其他都是固定,所以說,其實 16 bit UUID 是對應了一個 128 bit 的 UUID。這樣一來,UUID 就大幅減少了,例如 16 bit uuid 只有有限的 65536 個,所以 16 bit UUID 並不能隨便使用。SIG 已經預先定義了一些 UUID,如果你想添加一些自己的 16 bit 的 UUID,可以花錢買。
2.3 GAP
GAP是通用訪問控制配置文件 ,由名字可以看出,它定義了 BLE 整個通信過程中的流程,例如廣播、掃描、連接等流程。還定義了參與通信的設備角色,以及他們各自的職能,例如廣播數據的 Broadcaster,接收廣播的 Observer,還有被連接的“外設” Peripheral 和發起連接的“中心設備” Central。可以看到,參與交互的設備角色都不是對等。詳細的交互流程,我們放到後面講。
3.BLE 藍牙棧結構
3.1 Arch
作爲 Android 開發者,我們不必理解 BLE 的協議棧每個細節,這裏大概介紹一下協議架構。我們都知道,協議一般都是分層設計的。BLE 協議棧也不例外。我們來看一下這個圖。整個協議棧大致分爲三部分,從下到上分別爲,控制器(Controller)→主機(Host)→應用(Applications)。
控制器:它是協議棧的底層的實現,直接與硬件相關,一般直接集成在 SoC 中,由芯片廠商實現,包括物理層和鏈路層。 主機:這是協議棧的上層實現,是硬件的抽象,與具體的硬件和廠家無關。 應用層:就是使用 Host 層提供的 API,開發的應用。
3.2 Controller
首先是物理層。藍牙是工作在 2.4GHz 附近,這是工業、科學、醫療 ISM 頻段。可以看到它和 WiFi 工作在同一個頻段。藍牙把頻段切分爲 40 個通道,3 個廣播通道,37 個數據通道,按照一定規律跳頻通信(高斯頻移鍵控 GFSK)。
在 Host 層和 Controller 之間有一個接口層,簡稱爲 HCI。主機和控制器之間就是通過 HCI 命令和事件交互的。HCI 這一層是協議棧中是可選的,例如在一些簡單小型的設備上可能就沒有,但是所有的 Android 設備上肯定是有。這是藍牙上層應用和芯片的交互的必經之路。後面我們會講到,這一層的 log,能夠很好的幫助我們分析和調試問題。
3.3 Host
在 Host 部分,協議結構要複雜一些,有邏輯鏈路控制和適配層,安全管理模塊等等。我們重點來看屬性協議,簡稱爲 ATT,它是 BLE 通信的基礎。ATT 把數據封裝,向外暴露爲“屬性”,提供“屬性”的爲服務端,獲取“屬性”的爲客戶端。ATT 是專門爲低功耗藍牙設計的,結構非常簡單,數據長度很短。
3.Android BLE
3.1 Android Ble 開發
從 Android 4.3 Jelly Bean,也就是 API 18 纔開始支持低功耗藍牙。這時支持 BLE 的 Central 模式,也就是我們在上面 GAP 中說的,Android 設備只能作爲中心設備去連接其他設備。從 Android 5.0 開始才支持外設模式。
Android SDK 中 BLE 相關的 API 都在 android.bluetooth.* 下面,同時在 Android 5.0 也引入了一些也需要用到 android.bluetooth.le* 下面的 API。
例如,在前面介紹的 GATT 定義的一些概念,都有對應的類,例如 BluetoothGatt/BluetoothGattService/BluetoothGattCharacteristic 等。有了前面的介紹,我們就能很容易的理解這些類的作用。
另外,要在 APP 中使用藍牙功能,需要在 Manifest 中申請藍牙相關的權限。在 Android 6.0 及以上平臺中,還需要申請定位權限。爲什麼會這樣?因爲 BLE 確實有定位的能力,我們後面會講到。
3.2 Android Ble 底層架構
Android 上藍牙實現的架構。我們來看右邊這個圖,這是 Android 上一個非常經典的層級結構。最下面硬件部分,可以使用各廠家的具體實現,通過硬件抽象層(HAL)接口統一連接到 Android AOSP 中,通過 JNI 提供 Java 訪問接口。藍牙服務運行在 com.android.bluetooth 進程中,最後通過 Binder 機制向客戶端,也就是 APP 提供相關的 API。
關於藍牙協議棧,這裏多說一點。Android 4.2 以前用的是老牌協議棧實現 BlueZ,4.2 開始換成了由 Google 和 Broadcom(博通)聯合開發的 BlueDroid,專門在 Android 平臺使用。BlueDroid 作爲全新的實現,功能不是完善,我們可以看到在 4.3 以後才支持 BLE,在 5.0 以後才支持外設模式,到目前爲止,功能其實還是不是很完善。也有一些 Bug。
介紹一下 Android 中 BLE 操作的過程,APP 發起一個 BLE 操作,然後理解返回,操作結果通過回調上報。操作被封裝爲一個消息,然後放到協議棧的消息隊列中,有一個獨立的線程獲取消息進行處理,這裏非常類似於我們熟知的 Looper 和 Handler 機制。
因爲是使用消息機制,回調的時候必須知道通知哪個客戶端?客戶端發起請求之前,首先要向協議棧註冊客戶端,註冊成功以後,返回一個 clientIf,這是一個整型,是客戶端在協議棧的一個句柄,客戶端的後續操作,都只需要帶上這個 clientIf 句柄即可。
在操作完成的時候,一般都有一個顯式的停止操作,用來釋放前面的申請的 clientIf 和資源。如果不能正確的釋放,不僅會造成內存泄漏,而且可能會導致後續所有的 BLE 操作都是不能做了。因爲這個 clientIf 是有限,在現在藍牙協議棧中只有 32 個,而且是Android 上所有 APP 共用的。當這些資源用完以後,只有通過殺掉對應的 APP 或者重啓藍牙才能恢復。
3.3 Ble 藍牙廣播
我們所說的廣播數據其實包含兩部分:Advertising Data(廣播數據) 和 Scan Response Data(掃描響應數據)。通常情況下,廣播的一方,按照一定的間隔,往空中廣播 Advertising Data,當某個監聽設備監聽到這個廣播數據時候,會通過發送 Scan Response Request,請求廣播方發送掃描響應數據數據。這兩部分數據的長度都是固定的 31 字節。在 Android 中,系統會把這兩個數據拼接在一起,返回一個 62 字節的數組。
廣播數據包的結構如這個圖所示(動畫)。廣播包中是包含一個一個的小 AD structure,每個 AD structure 是一個完整的數據,它的結構是:第一個字節表示長度 n,後面緊接 n 個字節的數據。數據部分第一個字節表示數據類型,也就是後面的數據含義,後面 n - 1 個字節表示真實數據。例如 0x08 是設備的名字,後面的數據就是設備名字的 UTF-8 編碼。
這些廣播數據可以自己手動去解析,在 Android 5.0 也提供 ScanRecord 幫你解析,直接可以通過這個類獲得有意義的數據。
廣播中可以有哪些數據類型呢?設備連接屬性,標識設備支持的 BLE 模式,這個是必須的。設備名字,設備包含的關鍵 GATT service,或者 Service data,廠商自定義數據等等。
這裏我們也可以看到,廣播數據只有最多62字節,所以廣播數據空間每個直接都非常珍貴。一般都把哪些數據放到廣播中呢?原則上是廣播中要放一些能夠表達設備身份的數據,還有一些需要暴露的必要數據。因爲空間的限制,所以基於廣播做不了太複雜的應用,到了 Bluetooth 5.0,據說要大幅擴展廣播數據的容量,擴展到 512 字節,這時想象空間就比較大了。
另外,再順帶提一下,無線通信中基本都有信號強度的概念 — 也就是 RSSI,RSSI 單位是 dB,通過 RSSI 能夠大致推測出距離的遠近。但是這個在 Android 設備上非常不靠譜,RSSI 的值波動很大,跟環境和手機的角度關係很大。
我們來直觀看一下掃描到的結果是什麼樣。例如我們使用一個 APP 掃描,掃到一個設備,這些內容都是從廣播數據中解析出來的,例如設備名字,設備類型,GATT 服務數據,產商自定義數據等。原始數據是這樣的(動畫),這裏是一個 62 字節的16進制標示,下面這個表格中,每一行就是一個 AD Structure。
3.4 android 藍牙廣播掃描
我們來看一下 Android 作爲接收者怎麼接收廣播數據,掃描設備。代碼其實很簡單,首先創建一個 LeScanCallback,用來接收收到廣播以後,回調上報數據。然後會用 BluetoothAdapter 的 startLeScan 來開始掃描,需要停止掃描的時候,使用 stopLeScan 來停止。
我麼來詳細看一下這個回調函數,onLeScan,有 BluetoothDevice 這個參數,代表掃描到的設備,關鍵是設備的的 MAC 地址信息。然後就是 RSSI,表示掃描到的設備的信號強度,接下來 scanRecord 就是我們前面介紹的廣播數據,這個數據的長度是62字節。值得提的一點是,BLE 所有回調函數都不是在主線程中的。
這裏有幾點需要注意,這裏在不需要掃描以後,一定要 stopLeScan,而且 start 和 stop 中傳入的 LeScanCallback 一定要是同一個,因爲 LeScanCallback 就是我們客戶端的標識。否者就會出現我們前面說的 clientIf 不釋放的問題。在 Android 開發中,我們經常會使用匿名內部類來做參數,在這裏就千萬不要這麼做。
在 Android 5.0 中,提供了全新的掃描 API — BluetoothLeScanner,它提供了對掃描更加精細的控制。
除了這種方法,還可以使用經典藍牙掃描的方式,BluetoothAdapter 的 startDiscovery(),然後通過 BroadcastReceiver 來接收收到的廣播。如果只是做 BLE 的開發,不建議使用這個方法,這是一個非常重的操作,靈活性非常差。
接下來我們來看一下掃描的工作流程。首先 APP 發起掃描請求,通過藍牙的 Service 發送請求給藍牙芯片。藍牙芯片開始掃描,掃描到了設備,就通過回調上報。我們知道,掃描真正執行實在 BT 芯片中,只要 APP 發送了請求下去以後,Android 系統就可以休眠了,也就是我們常說的 AP (Application Processor),等掃描到了設備以後,底層 BP (Baseband Processor)就會喚醒上層 AP,執行回調通知到 APP,(動畫)就像我們圖中紅色框標出的這樣。這裏有一個問題,隨着我們周圍的 BLE 設備逐漸增多,頻繁掃描到設備,系統就會被頻繁的喚醒,甚至睡眠不下去,從而導致耗電嚴重。
爲了避免這種問題,耗電的問題。我們需要儘可能少的使用掃描。即使需要掃描,我們也希望儘可能少的上報掃描到的設備。這裏就可以使用 Android 5.0 上提供的新接口,設置 ScanFilter,通過一定的規則過濾,只有掃描到了符合我們的規則的設備才上報,或者通過設置延遲上報,從而減少喚醒系統的次數。
這裏總結一下掃描中一些建議。1、首先,儘可能使用新的 API,功能更強大;2、儘可能少地掃描,因爲畢竟掃描是一個比較重的操作,耗電,也會減慢 BLE 連接速度;3、掃描的時候,儘量設置 ScanFilter,只掃描那些你感興趣的設備,而不是全盤掃描;4、正確使用 API,特別是合理停止掃描,防止資源泄漏。
3.3 Ble 藍牙連接
BLE 連接的建立是通過 GAP 來協商和創建連接。Central 設備發起連接,外設接收連接請求,並協商連接參數。
前面我們介紹了 GATT,GATT 核心內容就是 Service、Characteristic 以及 Descriptor。每個 BLE 外設,根據自己的功能,向外暴露 Service 等。其實最重要的獲取 Service 中的 Characteristic,Characteristic 可以被讀、寫、還有變化的時候有通知,這樣就實現了雙向的通信。
未完待續。。。