Android 藍(lán)牙開發(fā) Ble 開發(fā)

轉(zhuǎn)載請注明出處:http://blog.csdn.net/vnanyesheshou/article/details/51943870


一、ble簡單介紹

BLE:? Bluetooth Low Energy,即藍(lán)牙低功耗,它是一種技術(shù),從藍(lán)牙4.0開始支持。藍(lán)牙低功耗芯片有兩種模式:單模和雙模。

單模:只能執(zhí)行低功耗協(xié)議棧,也就是只支持ble。

雙模:支持傳統(tǒng)藍(lán)牙以及ble的使用。

較傳統(tǒng)藍(lán)牙:傳輸速度更快,覆蓋范圍更廣,安全性更高,延遲更短,耗電低等優(yōu)點(diǎn)。

關(guān)鍵術(shù)語和概念:

Gatt:(Generic Attribute Profile)—通用屬性配置文件,用于在ble鏈路上發(fā)送和接收被稱為“屬性”的數(shù)據(jù)塊。目前所有的ble應(yīng)用都是基于GATT的。一個(gè)設(shè)備可以實(shí)現(xiàn)多個(gè)配置文件。

ble交互的橋梁是Service、Characteristic、Desciptor。

Characteristic:可以理解為一個(gè)數(shù)據(jù)類型,它包括一個(gè)value和0至多個(gè)對此characteristic的描述(Descriptor)。

Descriptor:對Characterisctic的描述,如范圍、單位等。

Service:Characteristic的集合。它可以包含多個(gè)Characteristic。

一個(gè)ble終端可以包含多個(gè)Service,一個(gè)Service可以包含多個(gè)Characteristic,一個(gè)Characteristic包含一個(gè)value和多個(gè)Descriptor,一個(gè)Descriptor包含一個(gè)value。其中Characteristic比較重要,用的比較多。

這三部分都由UUID作為唯一標(biāo)示符,以此區(qū)分。

UUID(Universally Unique Identifier),含義是通用唯一識別碼,它是在一定范圍內(nèi)唯一的機(jī)器生成的標(biāo)識符。標(biāo)準(zhǔn)的UUID格式為:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12)。

ble中有四個(gè)角色:

廣播者(Braodcaster):廣播發(fā)送者,是不可連接的設(shè)備。

觀察者(Observer):掃描廣播,不能夠啟動連接。

廣播者和觀察者不能建立連接。應(yīng)用:溫度傳感器和溫度顯示器。

外圍(periphery):廣播發(fā)送者,可連接的設(shè)備,在單一鏈路層作為從機(jī)。

中央(central):掃描廣播,啟動連接,在單一或多鏈路層作為主機(jī)。

中央和外圍可以進(jìn)行配對、連接、數(shù)據(jù)通信。應(yīng)用:手機(jī)和手表。

一個(gè)中央可以同時(shí)連接多個(gè)周邊,但是一個(gè)周邊只能連接一個(gè)中央(但是我測試,周邊可以連接多個(gè)中央設(shè)備,并且能正常通信)。

二、Android?

注意:Android 4.3(API 18)引入ble相關(guān)接口。

相關(guān)類

目錄:frameworks/base/core/java/android/bluetooth/

BluetoothGatt:中央使用和處理數(shù)據(jù);

BluetoothGattCallback:中央的回調(diào)。

BluetoothGattServer:周邊提供數(shù)據(jù);

BluetoothGattServerCallback:周邊的回調(diào)

BluetoothGattService:Gatt服務(wù)

BluetoothGattCharacteristic:Gatt特性

BluetoothGattDescriptor:Gatt描述

2.1 中央設(shè)備

搜索ble設(shè)備?

//搜索附近所有的外圍設(shè)備

mBluetoothAdapter.startLeScan(mLeScanCallback);

//搜索某些uuid的外圍設(shè)備。

mBluetoothAdapter.startLeScan(uuid[] ,mLeScanCallback);

停止掃描

mBluetoothAdapter.stopLeScan(mLeScanCallback);

監(jiān)聽掃描結(jié)果。

mLeScanCallback = new BluetoothAdapter.LeScanCallback() {

public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {

}

};

device 搜索到的ble設(shè)備。

rssi 信號強(qiáng)度

scanRecord 遠(yuǎn)程設(shè)備廣告記錄的內(nèi)容(藍(lán)牙名稱)

發(fā)起連接請求,獲得中央。

mBluetoothGatt = device.connectGatt(mContext, false,mGattCallback);

第二個(gè)參數(shù):

如果為false,則直接立即連接。

如果為true,則等待遠(yuǎn)程設(shè)備可用時(shí)(在范圍內(nèi),。。)連接。并不是斷開后重新連接。

第三個(gè)參數(shù):連接回調(diào)

private BluetoothGattCallback mGattCallback = new?BluetoothGattCallback() {};

BluetoothGattCallback 類中提供了許多回調(diào),包括:連接狀態(tài)改變、characteristic的read、write、change,mtu change等。根據(jù)需要實(shí)現(xiàn)即可。

連接成功后,發(fā)送?gatt服務(wù)發(fā)現(xiàn)請求。mBluetoothGatt.discoverServices().

發(fā)現(xiàn)服務(wù)成功會失敗都會回調(diào)onServicesDiscovered()函數(shù)。通過mBluetoothGatt.getServices()獲取連接的ble設(shè)備所提供的服務(wù)列表,返回值類型為List<BluetoothGattService>。

//連接狀態(tài)改變回調(diào)

onConnectionStateChange(BluetoothGatt gatt, int status, int newState){

if(newState == BluetoothProfile.STATE_CONNECTED){

? ? //連接成功后,發(fā)送發(fā)現(xiàn)服務(wù)請求。

? mBluetoothGatt.discoverServices();

}

}

//發(fā)現(xiàn)服務(wù)回調(diào)。

public void onServicesDiscovered(BluetoothGatt gatt, int status) {

if(status == BluetoothGatt.GATT_SUCCESS){

? //發(fā)現(xiàn)成功后,則可以通過下面方法獲取service 列表。

? mBluetoothGatt.getServices();

}

}

獲得Characteristic和Descriptor。

通過服務(wù)列表中的BluetoothGattService,可以獲取到服務(wù)所包含的characteristic(getCharacteristics()返回值類型為List<BluetoothGattCharacteristic>)。

通過BluetoothGattCharacteristic可以獲取特征所包含的descriptor(getDescriptors()返回值類型是List<BluetoothGattDescriptor>)。

BluetoothGattService、BluetoothGattCharacteristic和BluetoothGattDescriptor三個(gè)類中都提供了一個(gè)方法getUuid(),通過該方法可以獲取其對應(yīng)的uuid,從而可以判斷是否是自己需要的service、characteristic或者descriptor。

通過獲取的特征值,可以進(jìn)行下操作:

寫入特性值

讀取特性值

訂閱特性值。

寫入特征值:

characteristic.setValue(data.getBytes());

mBluetoothGatt.writeCharacteristic(characteristic);

要想成功寫入特征值:

首先此characteristic屬性滿足BluetoothGattCharacteristic.PROPERTY_WRITY或BluetoothGattCharacteristic.PROPERTY_WRITY_NO_RESPONSE,如果其property都不包含這兩個(gè),寫特征值writeCharacteristic()函數(shù)直接返回false,什么都不做處理(具體可以看BluetoothGatt源碼)。

其次此characteristic權(quán)限應(yīng)滿足BluetoothGattCharacteristic.PERMISSION_WRITE,否則onCharacteristicWrite()回調(diào)收到GATT_WRITE_NOT_PERMITTED回應(yīng)。

寫特征值前可以設(shè)置寫的類型setWriteType(),寫類型有三種,如下:

WRITE_TYPE_DEFAULT? 默認(rèn)類型,需要外圍設(shè)備的確認(rèn),也就是需要外圍設(shè)備的回應(yīng),這樣才能繼續(xù)發(fā)送寫。

WRITE_TYPE_NO_RESPONSE?設(shè)置該類型不需要外圍設(shè)備的回應(yīng),可以繼續(xù)寫數(shù)據(jù)。加快傳輸速率。

WRITE_TYPE_SIGNED? 寫特征攜帶認(rèn)證簽名,具體作用不太清楚。

外圍設(shè)備收到中央寫特征值的請求,會回調(diào) onCharacteristicWriteRequest

如果此次請求需要回應(yīng),則外圍設(shè)備回應(yīng) mGattServer.sendResponse

中央設(shè)備收到響應(yīng),回調(diào)onCharacteristicWrite(BluetoothGatt gatt,

BluetoothGattCharacteristic characteristic, int status)?


讀取特征值:

mBluetoothGatt.readCharacteristic(characteristic);

讀特征值與寫類似,也需要響應(yīng)的權(quán)限和屬性。

該characteristic屬性需包含PROPERTY_READ,否則直接返回false(具體可以看BluetoothGatt源碼)。

該characteristic權(quán)限應(yīng)滿足BluetoothGattCharacteristic.PERMISSION_READ,否則onCharacteristicRead()回調(diào)收到GATT_READ_NOT_PERMITTED回應(yīng)。

外圍設(shè)備接收到中央設(shè)備的讀特征值請求,則會回調(diào) onCharacteristicReadRequest()函數(shù),

外圍設(shè)備應(yīng)該回應(yīng)此請求 sendResponse。

中央設(shè)備收到響應(yīng)回調(diào)

onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)


訂閱:

//第二個(gè)參數(shù):true則訂閱該特征,false則取消訂閱。

mBluetoothGatt.setCharacteristicNotification(characteristic, true);

當(dāng)指定Characteristic值發(fā)生變化時(shí),是否接收通知。

當(dāng)設(shè)為true,如果Characteristic發(fā)生變化時(shí),會回調(diào)方法:

onCharacteristicChanged(BluetoothGatt gatt,? ?BluetoothGattCharacteristic characteristic)

通過參數(shù)characteristic,可獲得getValue獲得其中的內(nèi)容。

注意:雖然訂閱了該特征,并且該特征屬性也滿足PROPERTY_NOTIFY,但是并沒有收到特征值改變的回調(diào)。這是為什么呢?查看sdk中的demo,發(fā)現(xiàn)需要寫一下Descriptor。這樣就可以正常監(jiān)聽特征值的改變了。

//CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb"

BluetoothGattDescriptor descriptor = characteristic.getDescriptor(

? ? ? ? UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));

descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);

mBluetoothGatt.writeDescriptor(descriptor);


中央設(shè)備的其他一些方法

readDescriptor(descriptor) ?讀取描述

writeDescriptor(descriptor) ?寫描述

readRemoteRssi() ? ? ? ? ? ?讀取連接設(shè)備的rssi。

disconnect();? ? ? ?斷開bel連接。

close(); ? ? ? ? ? ? ? ? 關(guān)閉中央設(shè)備。(不用時(shí)及時(shí)關(guān)閉,否則有的手機(jī)重連連不上。)


2.2 外圍設(shè)備

獲取/打開周邊(外圍)

mGattServer = mBluetoothManager.openGattServer(mContext, callback);

//其中callback是一個(gè)MyGattServerCallback(繼承了BluetoothGattServerCallback)對象。


初始化描述、特性和服務(wù)。

//描述:

new BluetoothGattDescriptor(UUID.fromString(DESC_UUID), descPermissions);

//特性 :

final int properties = BluetoothGattCharacteristic.PROPERTY_READ

| BluetoothGattCharacteristic.PROPERTY_WRITE?

| BluetoothGattCharacteristic.PROPERTY_NOTIFY;

final int permissions = BluetoothGattCharacteristic.PERMISSION_READ;

| BluetoothGattCharacteristic.PERMISSION_WRITE;

new BluetoothGattCharacteristic(UUID.fromString(CHAR_UUID), properties, permissions);

gattChar.addDescriptor(gattDesc);


property 表示屬性。permission 表示權(quán)限。這兩個(gè)都和權(quán)限相關(guān)。

如果property未設(shè)置PROPERTY_READ,permission設(shè)置PERMISSION_READ,則中央設(shè)備readCharacteristic主動讀取特征值方法返回false,此操作失敗。

而如果property設(shè)置PROPERTY_READ,permission未設(shè)置PERMISSION_READ,則中央設(shè)備readCharacteristic主動讀取特征值方法返回true,此操作成功,外圍設(shè)備發(fā)送響應(yīng),中央設(shè)備收到響應(yīng) GATT_READ_NOT_PERMITTED。

所以說如果想要characteristic可讀,則這兩個(gè)都要設(shè)置。

PROPERTY_WRITE和PERMISSION_WRITE也和上面類似。

PROPERTY_NOTIFY 表示支持notification。

//服務(wù):

BluetoothGattService bs = new BluetoothGattService( UUID.fromString(SERV_UUID),

BluetoothGattService.SERVICE_TYPE_PRIMARY);

bs.addCharacteristic(gattChar);

第二個(gè)參數(shù)為service type,

SERVICE_TYPE_PRIMARY ? 基礎(chǔ)服務(wù)、主要服務(wù)。

SERVICE_TYPE_SECONDARY? 輔助服務(wù)(由初級服務(wù)包含在內(nèi))。

BluetoothGattService 類中方法

addService(bluetoothGattService),將輔助服務(wù)添加到主要服務(wù)中。

getIncludeedServices() 獲取包含的服務(wù)列表。

getType() 獲取服務(wù)的type。

getUuid() 獲取服務(wù)的UUID。

添加服務(wù)

mGattServer.addService(bs);

設(shè)置廣播數(shù)據(jù)

開始廣播

這在android4.3沒有提供,在android5.0才提供了設(shè)置廣播數(shù)據(jù),發(fā)送廣告包等方法。我們開發(fā)是基于android4.3的,按理說我們是不可以作為外圍設(shè)備的,不過我們framework以及底層都進(jìn)行了修改,提供了這些方法,說以我們的android4.3設(shè)備可以作為外圍。

mGattServer.startAdvertising();//開始廣播

mGattServer.stopAdvertising();//停止廣播

收到central掃描請求,回應(yīng)掃描請求。

這個(gè)不需要我們管理,此時(shí)會廣播之前的設(shè)置的廣播數(shù)據(jù)。

收到central連接請求,建立連接。

連接成功后 外圍可以斷開連接。

mGattServer.cancelConnection(device);

響應(yīng)central發(fā)起的gatt服務(wù)發(fā)現(xiàn)請求,回應(yīng)服務(wù)信息。

響應(yīng)central發(fā)起的gatt特性發(fā)現(xiàn)請求,回應(yīng)特性信息。

響應(yīng)central發(fā)起的gatt描述發(fā)現(xiàn)請求,回應(yīng)描述信息。

這三個(gè)不需要我們?nèi)ゲ僮?,系統(tǒng)底層會處理。

對central的讀寫做響應(yīng)。

回應(yīng)特性值

更新特性值。

回應(yīng)特征值:

MyGattServerCallback extends BluetoothGattServerCallback.

其中有幾個(gè)常用的方法:

onConnectionStateChange(BluetoothDevice device, int status, ?int newState)

監(jiān)聽設(shè)備連接狀態(tài)。device遠(yuǎn)程設(shè)備,newStateble連接狀態(tài),只能為BluetoothProfile.STATE_CONNECTED和BluetoothProfile.STATE_DISCONNECTED。

onCharacteristicReadRequest(BluetoothDevice device, ?int requestId, int offset,

BluetoothGattCharacteristic characteristic)

監(jiān)聽中心設(shè)備讀Characteristic的請求,

requestId?請求的標(biāo)識。

offset?特性值偏移量。

Characteristic??要讀的特性。

此方法要求作出響應(yīng)。

mGattServer.sendResponse(device, requestId,

BluetoothGatt.GATT_SUCCESS, offset, null);

最后一個(gè)參數(shù)可以設(shè)置傳的數(shù)據(jù),byte[]類型的。

onCharacteristicWriteRequest(BluetoothDevice device, ?int requestId, BluetoothGattCharacteristic characteristic,

boolean preparedWrite, boolean responseNeeded, int offset,?byte[] value)

監(jiān)聽中心設(shè)備寫Characteristic的請求,

preparedWrite true則寫操作必須排隊(duì)等待稍后執(zhí)行。

responseNeeded?是否需要響應(yīng)。

value?寫的數(shù)據(jù)。

需要響應(yīng)則必須sendResponse.

更新特征值:

外圍設(shè)備向中心設(shè)備不能發(fā)送數(shù)據(jù),必須通過notify 或者indicate的方式,andorid只發(fā)現(xiàn)notify接口。

characteristic.setValue(res.getBytes());

mGattServer.notifyCharacteristicChanged(device,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? characteristic, false);

最后一個(gè)參數(shù)表示是否需要客戶端確認(rèn)。

---------------------

作者:zpengyong

來源:CSDN

原文:https://blog.csdn.net/VNanyesheshou/article/details/51943870

版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請附上博文鏈接!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容