基於低耗藍芽的GATT Profile以及BLE中的回撥
一;先介紹GATT Profile的相關理論知識,接下來再討論具體程式碼實現。
1;先了解一些關鍵詞的含義;
——porfile;一種規範,一種標準的通訊協議。每個profile中會包含多個service服務,每個service代表該從機的一種能力。
——service;一種服務,每種服務就代表一種能力,例如,藍芽從機中有很多服務,電量資訊服務、系統資訊服務等,每個service中又包含多個characteristic特徵值。每個具體的characteristic特徵值才是ble通訊的主題。比如當前的電量是80%,所以會通過電量的characteristic特徵值存在從機的profile裡,這樣主機就可以通過這個characteristic來讀取80%這個資料。
——characteristic;特徵值,ble主從機的通訊均是通過characteristic來實現,可以理解為一個標籤,通過這個標籤可以獲取或者寫入想要的內容。
——UUID,統一識別碼,我們剛才提到的service和characteristic,都需要一個唯一的uuid來標識
2;GATT是什麼;
現在低功耗藍芽(BLE)連線都是建立在 GATT (Generic Attribute Profile) 協議之上。GATT 是一個在藍芽連線之上的傳送和接收很短的資料段的通用規範,這些很短的資料段被稱為屬性(Attribute)。
3;GAP
——詳細介紹 GATT 之前,需要了解 GAP(Generic Access Profile),它在用來控制裝置連線和廣播。GAP 使你的裝置被其他裝置可見,並決定了你的裝置是否可以或者怎樣與合同裝置進行互動。例如 Beacon 裝置就只是向外廣播,不支援連線,小米手環就等裝置就可以與中心裝置連線。
——GAP 給裝置定義了若干角色,其中主要的兩個是:外圍裝置(Peripheral)和中心裝置(Central)。
外圍裝置:這一般就是非常小或者簡單的低功耗裝置,用來提供資料,並連線到一個更加相對強大的中心裝置。例如小米手環。
中心裝置:中心裝置相對比較強大,用來連線其他外圍裝置。例如手機等。
——在 GAP 中外圍裝置通過兩種方式向外廣播資料: Advertising Data Payload(廣播資料)和 Scan Response Data Payload(掃描回覆),每種資料最長可以包含 31 byte。這裡廣播資料是必需的,因為外設必需不停的向外廣播,讓中心裝置知道它的存在。掃描回覆是可選的,中心裝置可以向外設請求掃描回覆,這裡包含一些裝置額外的資訊,例如裝置的名字。
注意;實際上,我們在 Android 開發中,可以直接使用裝置的 MAC 地址,發起連線,可以不經過掃描的步驟。這並不意味不需要經過 GAP,實際上在晶片級別已經給你做好了,藍芽晶片發起連線,總是先掃描裝置,掃描到了才會發起連線。意思就是在android開發中不需要管GAP,
4;GATT連線;
GATT 的全名是 Generic Attribute Profile(姑且翻譯成:普通屬性協議),它定義兩個 BLE 裝置通過叫做 Service 和 Characteristic 的東西進行通訊。
注意;GATT 連線需要特別注意的是:GATT 連線是獨佔的。也就是一個 BLE 外設同時只能被一箇中心裝置連線。一旦外設被連線,它就會馬上停止廣播,這樣它就對其他裝置不可見了。當裝置斷開,它又開始廣播。 中心裝置和外設需要雙向通訊的話,唯一的方式就是建立 GATT 連線。
5;總結,這些理論告訴我們的就是
1;利用GAP進行連線,但是在android中我們不直接使用,直接通過獲取MAC地址進行連線
2;主從機通過uuid找到服務和特徵值進行讀取修改等操作。
二,程式碼實現
1;先分析這幾個類;都是api介紹;
注意;
在BLE協議中,有兩個角色,周邊(Periphery)和中央(Central);周邊是資料提供者,中央是資料使用/處理者;
一箇中央可以同時連線多個周邊,但是一個周邊某一時刻只能連線一箇中央。
a) BluetoothGattServer作為周邊來提供資料;BluetoothGattServerCallback返回周邊的狀態。
b) BluetoothGatt作為中央來使用和處理資料;BluetoothGattCallback返回中央的狀態和周邊提供的資料。
因為我們討論的是Android的BLE SDK,下面所有的BluetoothGattServer代表周邊,BluetoothGatt代表中央。
2;重新理解關於藍芽ble通訊的幾個關鍵類;
BluetoothGatt:BluetoothGatt 是我們用的最多,也是我們最重要的一個類,為了儘可能通俗的理解,這裡我們可以把它看成Android手機與BLE終端裝置建立通訊的一個***管道***,只有有了這個管道,我們才有了通訊的前提。
BluetoothGattService:藍芽裝置的服務,在這裡我們把BluetoothGattService比喻成班級。而Bluetoothdevice我們把它比喻成學校,一個學校裡面可以有很多班級,也就是說我們每臺BLE終端裝置擁有多個服務,班級(各個服務)之間通過UUID(唯一識別符號)區別。
BluetoothGattCharacteristic: 藍芽裝置所擁有的特徵,它是手機與BLE終端裝置交換資料的關鍵,我們做的所有事情,目的就是為了得到它。在這裡我們把它比喻成學生,一個班級裡面有很多個學生,也就是說我們每個服務下擁有多個特徵,學生(各個特徵)之間通過UUID(唯一識別符號)區別。
總結:當我們想要用手機與BLE裝置進行通訊時,實際上也就相當於我們要去找一個學生交流,首先我們需要搭建一個管道,也就是我們需要先獲取得到一個BluetoothGatt,其次我們需要知道這個學生在哪一個班級,學號是什麼,這也就是我們所說的serviceUUID,和charUUID。這裡我們還需要注意一下,找到這個學生後並不是直接和他交流,他就好像一箇中介一樣,在手機和BLE終端裝置之間幫助這兩者傳遞著資訊,我們手機所發資料要先經過他,在由他傳遞到BLE裝置上,而BLE裝置上的返回資訊,也是先傳遞到他那邊,然後手機再從他那邊進行讀取。
**重點;Android手機與BLE終端裝置通訊結果都是以回撥的形式返回:**
3;瞭解ble中的回撥事件。
——3.1;連線藍芽BLE終端裝置的方法以及回撥方法;
——3.2;啟動服務方法以及找到服務後回撥的方法
——3.3;從特定的服務中獲取特定的特徵值;
之前我們說過,我們的最終目的就是獲取Characteristic來進行通訊,正常情況下,我們可以從硬體工程師那邊得到serviceUUID和characteristicUUID.假設我們在知道serviceUUID和characteristicUUID的前提下,我們就可以通過下面程式碼獲取相應特徵值:
BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb"));
BluetoothGattCharacteristic characteristic= service.getCharacteristic(UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb"));
——3.4;開始通訊,
在獲取characteristic之後對其進行讀寫操作的方法以及其回撥方法
——3.5;BLE終端裝置返回資料回撥事件
4;細講ble中的回撥事件。
部分原始碼
MainActivity 類
public class MainActivity extends Activity {
//BluetoothLeClass 類的物件
private BluetoothLeClass mBLE;
// 發現BLE終端的Service時回撥
mBLE.setOnServiceDiscoverListener(mOnServiceDiscover);
//例項化一個OnServiceDiscoverListener 介面物件
private BluetoothLeClass.OnServiceDiscoverListener mOnServiceDiscover = new OnServiceDiscoverListener() {
@Override
public void onServiceDiscover(BluetoothGatt gatt) {
}
}
BluetoothLeClass類;
public class BluetoothLeClass {
//建立一個監聽的引用
private OnServiceDiscoverListener mOnServiceDiscoverListener;
//一個回撥介面
public interface OnServiceDiscoverListener {
public void onServiceDiscover(BluetoothGatt gatt);
}
//進行註冊,對mOnServiceDiscoverListener進行賦值,也就是記住傳過來的監聽物件
public void setOnServiceDiscoverListener(OnServiceDiscoverListener l) {
mOnServiceDiscoverListener = l;
}
//當服務被發現時呼叫介面中的方法。
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
mOnServiceDiscoverListener.onServiceDiscover(gatt);
}
}
}
通過這個圖來理解上面的程式碼。
MainActivity 類為客服端C;
BluetoothLeClass類;為服務端S;