Android藍牙筆記

一、相關介紹

1、概述

藍牙無線技術是一種全球通用的短距離無線技術,通過藍牙技術能夠實現多種電子設備間的相互連接,特別是在小型無線電、耗電量低、成本低、安全性、穩定性、易用性以及特別的聯網能力等固有的優勢上,藍牙無線技術發展迅速。

2、分類

藍牙分爲三種:Bluetooth Smart Ready、Bluetooth Smart(Smart是低功耗藍牙的標識)、以及標準 Bluetooth。根據 Bluetooth SIG的說法,這樣是爲了要分辨裝置間的相容性以及標識各版本的傳輸頻率。基本上來說,Bluetooth Smart Ready適用於任何雙模藍牙4.0的電子產品,而Bluetooth Smart是應用在心率監視器或計步器等使用扭扣式電池並傳輸單一的裝置。Bluetooth Smart Ready的相容性最高,可與Bluetooth Smart及標準藍牙相通。標準藍牙則無法與Bluetooth Smart相通。
\

3、BLE介紹

BLE是Bluetooth Low Energy的縮寫,又叫藍牙4.0,區別於藍牙3.0和之前的技術。BLE前身是NOKIA開發的Wibree技術,主要用於實現移動智能終端與周邊配件之間的持續連接,是功耗極低的短距離無線通信技術,並且有效傳輸距離被提升到了100米以上,同時只需要一顆鈕釦電池就可以工作數年之久。BLE是在藍牙技術的基礎上發展起來的,既同於藍牙,又區別於傳統藍牙。BLE設備分單模和雙模兩種,雙模簡稱BR,商標爲Bluetooth Smart Ready,單模簡稱BLE或者LE,商標爲Bluetooth Smart。Android是在4.3後才支持BLE,這說明不是所有藍牙手機都支持BLE,而且支持BLE的藍牙手機一般是雙模的。雙模兼容傳統藍牙,可以和傳統藍牙通信,也可以和BLE通信,常用在手機上,android4.3和IOS4.0之後版本都支持BR,也就是雙模設備。單模只能和BR和單模的設備通信,不能和傳統藍牙通信,由於功耗低,待機長,所以常用在手環的智能設備上。

二、基本概念

1、Generic Access Profile(GAP)

用來控制設備連接和廣播,GAP使你的設備被其他設備可見,並決定了你的設備是否可以或者怎樣與合同設備進行交互。

2、Generic Attribute Profile(GATT)

通過BLE連接,讀寫屬性類數據的Profile通用規範,現在所有的BLE應用Profile都是基於GATT的。

3、Attribute Protocol (ATT)

GATT是基於ATTProtocol的,ATT針對BLE設備做了專門的優化,具體就是在傳輸過程中使用盡量少的數據,每個屬性都有一個唯一的UUID,屬性將以characteristics and services的形式傳輸。

4、Characteristic

Characteristic可以理解爲一個數據類型,它包括一個value和0至多個對次value的描述(Descriptor)。

5、Descriptor

對Characteristic的描述,例如範圍、計量單位等。

6、Service

Characteristic的集合。例如一個service叫做“Heart Rate Monitor”,它可能包含多個Characteristics,其中可能包含一個叫做“heart ratemeasurement”的Characteristic。

7、UUID

唯一標示符,每個Service,Characteristic,Descriptor,都是由一個UUID定義。

三、Android BLE API

1、BluetoothGatt

繼承BluetoothProfile,通過BluetoothGatt可以連接設備(connect),發現服務(discoverServices),並把相應地屬性返回到BluetoothGattCallback,可以看成藍牙設備從連接到斷開的生命週期。

2、BluetoothGattCharacteristic

相當於一個數據類型,可以看成一個特徵或能力,它包括一個value和0~n個value的描述(BluetoothGattDescriptor)。

3、BluetoothGattDescriptor

描述符,對Characteristic的描述,包括範圍、計量單位等。

4、BluetoothGattService

服務,Characteristic的集合。

5、BluetoothProfile

一個通用的規範,按照這個規範來收發數據。

6、BluetoothManager

通過BluetoothManager來獲取BluetoothAdapter。
BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);

7、BluetoothAdapter

代表了移動設備的本地的藍牙適配器, 通過該藍牙適配器可以對藍牙進行基本操作,一個Android系統只有一個BluetoothAdapter,通過BluetoothManager獲取。
BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();

8、BluetoothDevice

掃描後發現可連接的設備,獲取已經連接的設備。
BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(address);

9、BluetoothGattCallback

已經連接上設備,對設備的某些操作後返回的結果。

?
1
2
3
4
5
<code class="hljs cs">BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback(){
    //實現回調方法,根據業務做相應處理
};
BluetoothGatt bluetoothGatt = bluetoothDevice.connectGatt(this, false, bluetoothGattCallback);
</code>

三、操作流程

1、藍牙開啓

在使用藍牙BLE之前,需要確認Android設備是否支持BLE feature(required爲false時),另外要需要確認藍牙是否打開。如果發現不支持BLE,則不能使用BLE相關的功能;如果支持BLE,但是藍牙沒打開,則需要打開藍牙。代碼示例如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<code class="hljs java">//是否支持藍牙模塊
@TargetApi(18)
public static boolean isSupportBle(Context context) {
    if(context != null && context.getPackageManager().hasSystemFeature("android.hardware.bluetooth_le")) {
        BluetoothManager manager = (BluetoothManager)context.getSystemService("bluetooth");
        return manager.getAdapter() != null;
    } else {
        return false;
    }
}
//是否開啓藍牙
@TargetApi(18)
public static boolean isBleEnable(Context context) {
    if(!isSupportBle(context)) {
        return false;
    } else {
        BluetoothManager manager = (BluetoothManager)context.getSystemService("bluetooth");
        return manager.getAdapter().isEnabled();
    }
}
//開啓藍牙
public static void enableBle(Activity act, int requestCode) {
    Intent mIntent = new Intent("android.bluetooth.adapter.action.REQUEST_ENABLE");
    act.startActivityForResult(mIntent, requestCode);
}
//藍牙開啓過程
if(isSupportBle(mContext)){
    //支持藍牙模塊
    if(!isBleEnable(mContext)){
        //沒開啓藍牙則開啓
        enableBle(mSelfActivity, 1);
    }
} else{
    //不支持藍牙模塊處理
}
//藍牙開啓回調
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    //判斷requestCode是否爲開啓藍牙時傳進去的值,再做相應處理
    if(requestCode == 1){
        //藍牙開啓成功後的處理
    }
    super.onActivityResult(requestCode, resultCode, data);
}</code>
2、設備搜索

BluetoothAdapter.startDiscovery在大多數手機上是可以同時發現經典藍牙和Ble的,但是startDiscovery的回調無法返回Ble的廣播,所以無法通過廣播識別設備,且startDiscovery掃描Ble的效率比StartLeScan低很多。所以在實際應用中,還是StartDiscovery和StartLeScan分開掃,前者掃傳統藍牙,後者掃低功耗藍牙。

由於搜索需要儘量減少功耗,因此在實際使用時需要注意:當找到對應的設備後,立即停止掃描;不要循環搜索設備,爲每次搜索設置適合的時間限制,避免設備不在可用範圍的時候持續不停掃描,消耗電量。

通過調用BluetoothAdapter的 startLeScan() 搜索BLE設備。調用此方法時需要傳入 BluetoothAdapter.LeScanCallback 參數。具體代碼示例如下:

?
1
2
3
4
5
6
7
8
<code class="hljs java">BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
bluetoothAdapter.startLeScan(new BluetoothAdapter.LeScanCallback() {
    @Override
    public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
        //對掃描到的設備進行處理,可以依據BluetoothDevice中的信息、信號強度rssi以及廣播包和響應包組成的scanRecord字節數組進行分析
    }
});</code>
3、設備通信

兩個設備通過BLE通信,首先需要建立GATT連接,這裏我們講的是Android設備作爲client端,連接GATT Server。連接GATT Server,需要調用BluetoothDevice的connectGatt()方法,此函數帶三個參數:Context、autoConnect(boolean)和 BluetoothGattCallback 對象。調用後返回BluetoothGatt對象,它是GATT profile的封裝,通過這個對象,我們就能進行GATT Client端的相關操作。如斷開連接bluetoothGatt.disconnect(),發現服務bluetoothGatt.discoverServices()等等。示例代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<code class="hljs java">BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(address);
BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback(){
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        super.onConnectionStateChange(gatt, status, newState);
    }
 
    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        super.onServicesDiscovered(gatt, status);
    }
 
    @Override
    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        super.onCharacteristicRead(gatt, characteristic, status);
    }
 
    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        super.onCharacteristicWrite(gatt, characteristic, status);
    }
 
    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        super.onCharacteristicChanged(gatt, characteristic);
    }
 
    @Override
    public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
        super.onDescriptorRead(gatt, descriptor, status);
    }
 
    @Override
    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
        super.onDescriptorWrite(gatt, descriptor, status);
    }
 
    @Override
    public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
        super.onReliableWriteCompleted(gatt, status);
    }
 
    @Override
    public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
        super.onReadRemoteRssi(gatt, rssi, status);
    }
 
    @Override
    public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
        super.onMtuChanged(gatt, mtu, status);
    }
};
BluetoothGatt bluetoothGatt = bluetoothDevice.connectGatt(this, false, bluetoothGattCallback);
 
//以下爲獲得Gatt後的相關操作對應的響應方法
//notification to onCharacteristicChanged;
bluetoothGatt.setCharacteristicNotification(characteristic, true);
 
//readCharacteristic to onCharacteristicRead;
bluetoothGatt.readCharacteristic(characteristic);
 
//writeCharacteristic to onCharacteristicWrite;
bluetoothGatt.wirteCharacteristic(mCurrentcharacteristic);
 
//connect and disconnect to onConnectionStateChange;
bluetoothGatt.connect();
bluetoothGatt.disconnect();
 
//readDescriptor to onDescriptorRead;
bluetoothGatt.readDescriptor(descriptor);
 
//writeDescriptor to onDescriptorWrite;
bluetoothGatt.writeDescriptor(descriptor);
 
//readRemoteRssi to onReadRemoteRssi;
bluetoothGatt.readRemoteRssi();
 
//executeReliableWrite to onReliableWriteCompleted;
bluetoothGatt.executeReliableWrite();
 
//discoverServices to onServicesDiscovered;
bluetoothGatt.discoverServices();</code>

四、數據解析

BLE中有兩種角色Central和Peripheral,也就是中心設備和外圍設備,中心設備可以主動連接外圍設備,外圍設備發送廣播或者被中心設備連接,外圍通過廣播被中心設備發現,廣播中帶有外圍設備自身的相關信息。

數據包有兩種:廣播包(Advertising Data)和響應包(Scan Response),其中廣播包是每個設備必須廣播的,而響應包是可選的。數據包的格式如下圖所示(圖片來自官方 Spec):
\
每個包都是 31 字節,數據包中分爲有效數據(significant)和無效數據(non-significant)兩部分。

有效數據部分:包含若干個廣播數據單元,稱爲AD Structure。如圖中所示,AD Structure的組成是:第一個字節是長度值Len,表示接下來的Len個字節是數據部分。數據部分的第一個字節表示數據的類型AD Type,剩下的Len - 1個字節是真正的數據AD data。其中AD type非常關鍵,決定了AD Data的數據代表的是什麼和怎麼解析,這個在後面會詳細講;<喎�"/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPs7e0KfK/b7dsr+31qO60vLOqrnjsqWw/LXEs6S2yLHY0OvKxzMx19a92qOsyOe5+9PQ0KfK/b7dsr+31rK7tb0zMdfWvdqjrMqjz8K1xL7N08MwsrnG66Os1eKyv7fWtcTK/b7dysfO3tCntcSjrL3izva1xMqxuvKjrNaxvdO69sLUvLS/yaGjPC9wPg0KPHA+sum/tE5vcmRpY7XEU0RL1tC1xLao0uWjrEFEIHR5cGW1xLao0uXU2rPM0PK1xCZsZHF1bztibGVfZ2FwLmgmcmRxdW87zbfOxLz+1tCho7ao0uXI58/Co7o8L3A+DQo8cHJlIGNsYXNzPQ=="brush:java;">#define BLE_GAP_AD_TYPE_FLAGS 0x01 //< Flags for discoverability. #define BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE 0x02 //< Partial list of 16 bit service UUIDs. #define BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE 0x03 //< Complete list of 16 bit service UUIDs. #define BLE_GAP_AD_TYPE_32BIT_SERVICE_UUID_MORE_AVAILABLE 0x04 //< Partial list of 32 bit service UUIDs. #define BLE_GAP_AD_TYPE_32BIT_SERVICE_UUID_COMPLETE 0x05 //< Complete list of 32 bit service UUIDs. #define BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE 0x06 //< Partial list of 128 bit service UUIDs. #define BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE 0x07 //< Complete list of 128 bit service UUIDs. #define BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME 0x08 //< Short local device name. #define BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME 0x09 //< Complete local device name. #define BLE_GAP_AD_TYPE_TX_POWER_LEVEL 0x0A //< Transmit power level. #define BLE_GAP_AD_TYPE_CLASS_OF_DEVICE 0x0D //< Class of device. #define BLE_GAP_AD_TYPE_SIMPLE_PAIRING_HASH_C 0x0E //< Simple Pairing Hash C. #define BLE_GAP_AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R 0x0F //< Simple Pairing Randomizer R. #define BLE_GAP_AD_TYPE_SECURITY_MANAGER_TK_VALUE 0x10 //< Security Manager TK Value. #define BLE_GAP_AD_TYPE_SECURITY_MANAGER_OOB_FLAGS 0x11 //< Security Manager Out Of Band Flags. #define BLE_GAP_AD_TYPE_SLAVE_CONNECTION_INTERVAL_RANGE 0x12 //< Slave Connection Interval Range. #define BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_16BIT 0x14 //< List of 16-bit Service Solicitation UUIDs. #define BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_128BIT 0x15 //< List of 128-bit Service Solicitation UUIDs. #define BLE_GAP_AD_TYPE_SERVICE_DATA 0x16 //< Service Data - 16-bit UUID. #define BLE_GAP_AD_TYPE_PUBLIC_TARGET_ADDRESS 0x17 //< Public Target Address. #define BLE_GAP_AD_TYPE_RANDOM_TARGET_ADDRESS 0x18 //< Random Target Address. #define BLE_GAP_AD_TYPE_APPEARANCE 0x19 //< Appearance. #define BLE_GAP_AD_TYPE_ADVERTISING_INTERVAL 0x1A //< Advertising Interval. #define BLE_GAP_AD_TYPE_LE_BLUETOOTH_DEVICE_ADDRESS 0x1B //< LE Bluetooth Device Address. #define BLE_GAP_AD_TYPE_LE_ROLE 0x1C //< LE Role. #define BLE_GAP_AD_TYPE_SIMPLE_PAIRING_HASH_C256 0x1D //< Simple Pairing Hash C-256. #define BLE_GAP_AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R256 0x1E //< Simple Pairing Randomizer R-256. #define BLE_GAP_AD_TYPE_SERVICE_DATA_32BIT_UUID 0x20 //< Service Data - 32-bit UUID. #define BLE_GAP_AD_TYPE_SERVICE_DATA_128BIT_UUID 0x21 //< Service Data - 128-bit UUID. #define BLE_GAP_AD_TYPE_3D_INFORMATION_DATA 0x3D //< 3D Information Data. #define BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA 0xFF //< Manufacturer Specific Data. 所有的 AD Type 的定義在文檔Core Specification Supplement中。根據上面頭文件中的定義,AD Type包括如下類型:

1、TYPE = 0x01:標識設備LE物理連接的功能,佔一個字節,各bit爲1時定義如下:

?
1
2
3
4
5
6
<code class="hljs vhdl">bit 0: LE有限發現模式
bit 1: LE普通發現模式
bit 2: 不支持BR/EDR
bit 3: 對Same Device Capable(Controller)同時支持BLE和BR/EDR
bit 4: 對Same Device Capable(Host)同時支持BLE和BR/EDR
bit 5..7: 預留</code>

2、TYPE = 0x02:非完整的16 bit UUID列表
3、TYPE = 0x03:完整的16 bit UUID列表
4、TYPE = 0x04:非完整的32 bit UUID列表
5、TYPE = 0x05:完整的32 bit UUID列表
6、TYPE = 0x06:非完整的128 bit UUID列表
7、TYPE = 0x07:完整的128 bit UUID列表
8、TYPE = 0x08:設備簡稱
9、TYPE = 0x09:設備全名
10、TYPE = 0x0A:表示設備發送廣播包的信號強度
11、TYPE = 0x0D:設備類別
12、TYPE = 0x0E:設備配對的Hash值
13、TYPE = 0x0F:設備配對的隨機值
14、TYPE = 0x10:TK安全管理(Security Manager TK Value)
15、TYPE = 0x11:帶外安全管理(Security Manager Out of Band),各bit定義如下:

?
1
2
3
4
<code class="hljs vhdl">bit 0: OOB Flag,0-表示沒有OOB數據,1-表示有
bit 1: 支持LE
bit 2: 對Same Device Capable(Host)同時支持BLE和BR/EDR
bit 3: 地址類型,0-表示公開地址,1-表示隨機地址</code>

16、TYPE = 0x12:外設(Slave)連接間隔範圍,數據中定義了Slave最大和最小連接間隔,數據包含4個字節:前兩字節定義最小連接間隔,取值範圍:0x0006 ~ 0x0C80,而0xFFFF表示未定義;後兩字節,定義最大連接間隔,取值範圍同上,不過需要保證最大連接間隔大於或者等於最小連接間隔。
17、TYPE = 0x14:服務搜尋16 bit UUID列表
18、TYPE = 0x15:服務搜尋128 bit UUID列表
19、TYPE = 0x16:16 bit UUID Service,前兩個字節是UUID,後面是Service的數據
20、TYPE = 0x17:公開目標地址,表示希望這個廣播包被指定的目標設備處理,此設備綁定了公開地址
21、TYPE = 0x18:隨機目標地址,表示希望這個廣播包被指定的目標設備處理,此設備綁定了隨機地址
22、TYPE = 0x19:表示設備的外觀
23、TYPE = 0x1A:廣播區間
24、TYPE = 0x1B:LE設備地址
25、TYPE = 0x1C:LE設備角色
26、TYPE = 0x1D:256位設備配對的Hash值
27、TYPE = 0x1E:256位設備配對的隨機值
28、TYPE = 0x20:32 bit UUID Service,前4個字節是UUID,後面是Service的數據
29、TYPE = 0x21:128 bit UUID Service,前16個字節是UUID,後面是Service的數據
30、TYPE = 0x3D:3D信息數據
31、TYPE = 0xFF:廠商自定義數據,廠商自定義的數據中,前兩個字節表示廠商ID,剩下的是廠商自己按照需求添加,裏面的數據內容自己定義。

根據如下數據包,舉例說明解析的思路
搜索設備獲取的數據包如下:
?
1
<code class="hljs mathematica">02 01 06 14 FF 11 22 00 00 00 01 00 1F 09 01 00 00 00 CE DD 5E 5A 5D 23 06 08 48 45 54 2D 35 09 03 E7 FE 12 FF 0F 18 0A 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00</code>

根據解析規則,可分成如下部分:
1、廣播數據

?
1
<code class="hljs mathematica">02 01 06 14 FF 11 22 00 00 00 01 00 1F 09 01 00 00 00 CE DD 5E 5A 5D 23 06 08 48 45 54 2D 35 </code>

2、響應數據

?
1
<code class="hljs autohotkey">09 03 E7 FE 12 FF 0F 18 0A 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00</code>

3、有效數據

?
1
<code class="hljs mathematica">02 01 06 14 FF 11 22 00 00 00 01 00 1F 09 01 00 00 00 CE DD 5E 5A 5D 23 06 08 48 45 54 2D 35 09 03 E7 FE 12 FF 0F 18 0A 18</code>

4、無效數據

?
1
<code class="hljs ">00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00</code>

其中的有效數據又可分爲如下幾個數據單元:
02 01 06
14 FF 11 22 00 00 00 01 00 1F 09 01 00 00 00 CE DD 5E 5A 5D 23
06 08 48 45 54 2D 35
09 03 E7 FE 12 FF 0F 18 0A 18
根據上面定義的AD Type分別解析如下:
第一組數據告訴我們該設備屬於LE普通發現模式,不支持BR/EDR;
第二組數據告訴我們該數據爲廠商自定義數據,一般是必須解析的,可根據協議規則進行解析獲取對應的所需信息;
第三組數據告訴我們該設備的簡稱爲HET-5,其中對應的字符是查找ASSIC表得出的;
第四組數據告訴我們UUID爲E7FE-12FF-0F18-0A18(此處有疑,類型03表示的是16位的UUID,對應的兩個字節,而此處有8個字節,估計是設備燒錄時把字節位數理解爲了字符位數導致的問題).

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章