Android 藍芽Mesh組網程式碼詳解
前言
上面的幾篇文章都是在說Android網路程式設計方面的內容,我本來就有打算做成一個系列。但最近因為工作的原因,一直在研究藍芽mesh組網對藍芽裝置進行控制,研究了近兩個星期,總算有了點自己的理解。先對藍芽Mesh 組網做一個總結,下面的文章會繼續寫Android 網路程式設計方面的內容。網上關於Mesh 組網的理論解釋倒是很多,但是很少有關於Android 程式碼具體實現的,這篇文章將基於Android Mesh 組網的程式碼實現進行講解,希望能帶給大家一些幫助。
Mesh組網基本理解
藍芽技術聯盟寫了解密藍芽mesh系列,一共10篇文章 講述了藍芽mesh理論內容以及整個流程 ,想要詳細瞭解的可以點選檢視。下面我們簡單介紹下Mesh組網到底是什麼:
MESH是一種新型的無線網路架構,藍芽Mesh組網內每臺裝置均通過低功耗藍芽無線連線進行通訊,而這些裝置被稱之為節點。每個節點都能傳送和接收訊息,訊息能夠在節點之間被中繼,從而讓訊息傳輸至比無線電波正常傳輸距離更遠的位置。歸結成一句話:藍芽Mesh組網 就是一種在同一個網路內任意藍芽裝置都進行資料互動的技術。這樣的話,APP 只要能發現組網內的任何一臺裝置,就能由裝置發現組網內的其他Mesh裝置,並和任何一臺裝置建立連線並控制。
其實,我對藍芽Mesh 的組網也僅僅限於上面的理解。作為一個Android 開發人員,我更關注的是Android 程式碼到底如何去實現藍芽裝置的組網。在網上也找到了一些專案和程式碼,仔細研究了下也是很迷茫。後來找到了泰凌微提供的藍芽Mesh燈專案以及開放的Mesh 組網流程的原始碼,才真正的算是實現了Android 組網。我將這個資料上傳到了
掃描裝置
因為下面的分析是基於上面專案的程式碼去分析的,所以需要你們下載下來專案跑起來。
無論是連線已經完成組網裝置還是將一個待組網裝置進行組網,APP 做的第一步永遠是掃描,掃描到所有的藍芽裝置,然後拿到指定的Mesh組網的裝置進行組網操作。下面是程式碼分析:
在掃描介面執行: startScan(params) 啟動掃描,然後會執行一個迴圈任務EventLoopTask,在迴圈任務中每200毫秒查詢一次狀態,然後 startLeScan() 方法中判斷裝置是否正在掃描,如果沒有,則在LeBluetooth類中開啟掃描startScan()。
在註冊TelinkLightService的時候,會建立一個LightAdapter並啟動,會設定裝置的掃描結果回撥setLeScanCallback(LeScanCallback callback),然後當上面的掃描開啟後,會將掃描得到的裝置返回的這個回撥中的onLeScan()方法中.在這個方法中,會先對裝置本身返回的資訊進行處理和判斷,符合標準的裝置返回到在掃描介面設定監聽的performed方法中的,然後執行onLeScan(),掃描這一步就完成了。
掃描完拿到三個資訊 device rssi 以及scanRecord 。藍芽裝置的地址可以根據device.getAddress()直接獲取 比如說我拿到的燈裝置 scanRecord 的資料如下:
02:01:05:05:09:4D:65:73:68:09:FF:11:02:11:02:35:43:68:38:1E:FF:11:02:11:02:35:43:68:38:21:43:01:35:00:10:19:44:45:4C:20:68:73:65:4D:20:47:53:54:42:4A:00:00:00:00:00:00:00:00:00:00:00:00
在DefaultAdvertiseDataFilter類中可以看到 有meshName,meshAddress,meshUUID,productUUID等資料。很明顯我們可以根據這些資料分析當前裝置在哪個mesh 下,地址又是什麼。
總結:執行SDK提供的開始掃描介面startScan()的時候,可以從裝置發現的回撥中拿到具體的藍芽裝置和廣播。mac 地址和裝置名稱等資訊可以從裝置中拿到,而裝置所屬的Mesh 組網名稱、裝置的MeshAddress(裝置在組網內的唯一標識,通訊地址)等資訊可以在廣播中獲取到。其中productUUID是指的產品型別,可以在裝置中自定義這個型別。而meshUUID則是廠商預設設定的值,至於status 這個值是廠商的預留值,也是可以在裝置中自定義的資訊,總的來說,在這個APP 的程式碼中,並沒有實際用到這三個引數。
組網
meshAddress
在onLeScan() 方法裡,會呼叫一個mesh.getDeviceAddress() 的方法,這個方法很重要,這裡面拿到的meshAddress就是後面要修改新加入組網裝置的meshAddress,他在後面會被設為newMeshAddress然後儲存下來。因為在藍芽mesh組網下,meshAddress是用來確定裝置的,他在這個組網內是唯一固定的。那這個方法實現的邏輯是怎樣的呢?比如說要加入一個新裝置要加入當前的組網,不管他原先的meshAddress是多少 ,只看我當前組網下的meshAddress。比如說有兩個裝置,地址分別是 1、3,在這個方法裡,會遍歷1-254的值,然後就會返回一個2 ,這個2就是要設定的新meshAddress。meshAddress的範圍就是1-254,當然了你也可以修改這個範圍。
新增與重連
組網的時候,SDK提供了兩個介面,一個是新增新的裝置進行組網,呼叫的是TelinkLightService.Instance().updateMesh(params);更新介面,這個介面能夠更改裝置本身出廠時的引數meshName、password、ltk三個資訊,這三個資訊修改成功, 就標誌著裝置已經組⽹網成功,將裝置成功加入到新的組網下。另一個是如果當前裝置的meshName,password是你要組⽹網的名稱,可以執⾏**TelinkLightService.Instance().autoConnect(connectParams);**自動重連線口,直接把APP 跟裝置連線起來。
UUID
在組網之前,我們需要知道UUID。UUID是根據一定演算法,計算得到的一長串數字,這個數字的產生使用了多種元素,所以使得這串數字不會重複,每次生成都會產生不一樣的序列,所以可以用來作為唯一標識。
在藍芽協議中,UUID被用來標識藍芽裝置所提供的服務,並非是標識藍芽裝置本身哦,一個藍芽裝置可以提供多種服務,比如A2DP(藍芽音訊傳輸)、HEADFREE(擴音)、PBAP(電話本)、SPP(串列埠通訊)等等,每種服務都對應一個UUID,其中在藍芽協議棧裡,這些預設提供的profile是都有對應的UUID的,也就是預設的UUID,比如SPP,00001101-0000-1000-8000-00805F9B34FB就是一個非常 well-known的UUID,基本上所有的藍牙板不修改的話都是這個值。但是,不同的裝置也不同的UUID,如果是與一個藍芽開發板進行通訊,需要APP 和 藍芽裝置的UUID 保持一致。而APP程式碼中的UuidInformation類裡面就可以設定你要控制的低功耗藍芽裝置的UUID。一般都要修改服務、狀態通知、控制、OTA、加密這五個UUID。下面進行的登入,修改引數、控制裝置等等操作都需要UUID 的驗證。
下面我們主要講一下更新介面的實現,其實自動重連線口就是比更新介面少了修改裝置資訊這一步。
連線
發現裝置後,拿到要組網的裝置的本身的資訊,然後就開始進行連線了。其實就是走的低功耗藍芽裝置的connectGatt(this, false, mGattCallback); 連線方法。
更新引數執行updateMesh()⽅法來更新裝置的meshName、password、ltk,也還是會執⾏行一個迴圈任務 EventLoopTask,在迴圈任務中每200毫秒查詢一次狀態,然後update()方法中進行connect(),然後在LightController類中執行connect(),最終會執行Peripheral類的connect()⽅法,然後通過此BluetoothDevice的connectGatt(this, false, mGattCallback)方法獲取裝置連線。⽆論當連線上裝置或者失去連線時會回撥onConnectionStateChange(),當連線成功後呼叫discoverServices函式嘗試發現服務,當裝置是否找到服務時,會回撥onServicesDiscovered()函式,然後會在LightPeripheral類中的onServicesDiscovered()回撥給LightController,最後發給 LightAdapter類CONNECT_SUCCESS. 連線成功後會執行登入的⽅法。
登入
登入的過程是一個比較複雜的過程,涉及到多次的加密驗證。這個驗證之間的過程就會用到我們上面說的服務特徵值UUID以及加密特徵值UUID。
在LightAdapter類執行登入login的方法,最終的實現是在LightController類中的login方法.參考了BLE_LIGHT加密流程簡介V1.9.pdf文件,登入校驗的具體實現如下:
- 根據 meshName、password、randm 這 3 個引數生成一個 sk,然 後把 randm 和生成的 sk 的低 8 個 byte(校驗用)一起傳送給裝置
- 裝置獲取到發過來的 randm 並和 BLE Light 本身儲存的 meshname 和 password 進行加密獲取一個 sk,將生成的 sk 的低 8Byte 和 Master 發過來的 sk 進行比較,如果正確,則表示認證成功。
- 裝置也會隨機生成8Byte的 rands、以及本地儲存的 mesh name、password 這 3 個引數加密生成一個 sk,會把sk 和 rands 傳給APP,然後在本地會根據 randm、rands、meshname、password 共同生成一個新的sk,後續的加密和解密都將使用剛剛生成的 sk。
- APP 拿到裝置發過來的sk後會進行校驗,校驗成功後獲取8Byte的 rands,並根據該 rands 和randm、mesh name、password共同生成一個 sk,此時,APP 和 裝置 兩邊的 sk 都是一樣的,也就可以進行正常的加密和解密,同時的,登入的過程也就完成了。
修改資訊
我上面說過,加新裝置進行組網的過程就是修改meshName、password、ltk,這三個引數的過程。其實這麼說也並不是特別準確,其實在修改這三個引數之前還需要對比meshAddress(就是掃描裝置拿到的meshAddress)與上面儲存的newMeshAddress 要不要修改,因為我們上面說過,因為在藍芽mesh組網下,meshAddress是用來確定裝置的,他在這個組網內是唯一固定的。修改成功後才能進行下面三個引數的修改,這三個引數修改完成就標誌這新裝置已經成功組網。
上面我說到LTK,可能有的朋友不知道這個是什麼東西,meshName、password都很好理解,是mesh 組網的名稱和密碼,而LTK 是節點之間的通訊祕鑰,只要LTK一致,同一個組網間的裝置就能正常通訊。但是在程式碼中updateMesh(params)方法中,我們並沒有給LTK賦值,所以當我們設定的時候將使用廠商預設LTK值。如果要修改自定義的LTK,可以呼叫params.setLtk() 方法進行賦值。
下面是程式碼分析:
登入的sk校驗會出現在LightController類的LoginCommandCallback裡面,然後在LightAdapter裡面的ConnectionListener() 返回一個登入成功或者失敗的回撥。登入成功後要修改meshName, password, ltk這三個資訊,這三個資訊就是組網的標誌。修改的過程發生在LightController類的reset方法,會先判斷裝置的meshAddress要不要修改,如果要修改的話就先修改meshAddress,然後在onDeviceAddressNotify()方法裡繼續執行reset方法。通過加密的方法將這三個引數傳送給裝置,具體的加密過程也可以參照上面的BLE_LIGHT加密流程簡介V1.9.pdf 文件去解析下,最後發發送了一個重新檢查的命令,等待裝置解密並修改完成發出的確認資訊就完成了資訊的修改。修改命令的回撥都在ResetCommandCallback裡,收到最後的TAG_RESET_MESH_CHECK的確認後,繼續往下執行,會發出一個RESET_MESH_SUCCESS事件 ,然後在ResetMeshListener的回撥裡設定STATUS_UPDATE_MESH_COMPLETED,表明已完成資訊更新,掃描到的這個裝置組網完成,最後在掃描介面onDeviceStatusChanged的方法裡面繼續執行加燈操作,直到掃不到裝置。
控制與接收裝置資料
控制與接收裝置資料這裡的程式碼就比較簡單了,都有特定的方法去實現,唯一需要注意的一點就是傳送資料的協議,一定要是當前裝置的協議,否則無法進行控制。具體的實現可以參照下面的流程:
總結
以上就是藍芽Mesh 組網的整個流程了,下面引用一張我們ios 同事畫的流程圖理解一下:
喜歡這篇文章或者對你有幫助的話希望能點個贊!