一、相關介紹
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個字節,估計是設備燒錄時把字節位數理解爲了字符位數導致的問題).