Ble4.0 APP開發總結
最近自學了一個多禮拜的藍牙ble開發,對這方面有些粗略的理解,及時做個總結,不對的地方希望大家幫忙揪出來。
首先介紹幾個類 :BluetoothGatt,BluetoothGattService,BluetoothGattCharacteristic,BluetoothGattCallback
BluetoothGatt
這個類是開發裏最重要最常用到的東西了,我把它理解爲 手機與 設備通信的管道。有了這個管道,收發數據就容易了。
BluetoothGatt 是通過藍牙設備連接獲得:
bluetoothDevice.connectGatt(this.context, false, gattCallback);
先來談一下這個方法:
android.bluetooth.BluetoothDevice.connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback)
第一個參數context不說
第二個autoConnect
爲false 立刻發起一次連接爲true 自動連接,只要藍牙設備變得可用實測發現,用false連接比較好,比較快, true會等個十幾秒甚至幾分鐘纔會連接上。 開發過程中一般都是用false,掃描到bluetoothdevice之後,直接用false連接即可。
第三個BluetoothGattCallback
是非常重要的回調函數,手機與藍牙設備的一切通信結果都在這裏體現。待會仔細分析。
BluetoothGattService
藍牙設備所擁有的服務。在這裏比喻成 班級 吧。bluetoothdevice就比喻成學校吧。 一個學校可以有很多個班級。班級 根據UUID來區別。
BluetoothGattCharacteristic
藍牙設備所擁有的特徵。比喻成 學生。一個班級裏也可以有很多個學生。學生也是根據UUID 來區別
當你需要用手機來和藍牙設備通信的時候,就相當於 你想 和一個學生交流,你得先知道 這個學生的學號,所在班級號,就是開發中的所說的CharacUUID, ServiceUUID。這個uuid就是相當於網絡中的端口號,藍牙的mac地址就是相當於網絡中的ip地址,只不過uuid是具體的分了一層,首先需要CharacUUID得到一個大方向的集合,然後再加 ServiceUUID得到具體的功能。
BluetoothGattCallback
所有操作的回調函數。
private BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
//收到設備notify值 (設備上報值)
}
@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);
if (status == BluetoothGatt.GATT_SUCCESS) {
//write成功(發送值成功)
}
}
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
super.onConnectionStateChange(gatt, status, newState);
if (status == BluetoothGatt.GATT_SUCCESS) {
if (newState == BluetoothGatt.STATE_CONNECTED) {
// 連接成功
} else if (newState == BluetoothGatt.STATE_DISCONNECTED) {
// 斷開連接
}
}
}
@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 onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
super.onReadRemoteRssi(gatt, rssi, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
//獲取到RSSI, RSSI 正常情況下 是 一個 負值,如 -33 ; 這個值的絕對值越小,代表設備離手機越近
//通過mBluetoothGatt.readRemoteRssi();來獲取
}
}
@Override
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
super.onReliableWriteCompleted(gatt, status);
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
//尋找到服務
}
}
};
當調用了連接函數 mBluetoothGatt = bluetoothDevice.connectGatt(this.context, false, gattCallback);之後, 如果連接成功就會 走到 連接狀態回調:
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
if (status == BluetoothGatt.GATT_SUCCESS) {
//首先判斷這個status如果等於BluetoothGatt.GATT_SUCCESS(value=0)代表這個回調是正常的,
//如果不等於 0,那邊就代表沒有成功,也不需要進行其他操作了,
// 連接成功和斷開連接都會走到這裏
if (newState == BluetoothGatt.STATE_CONNECTED) {
// 連接成功
//連接成功之後,我們應該立刻去尋找服務(上面提到的BluetoothGattService),只有尋找到服務之後,纔可以和設備進行通信
gatt.discoverServices();// 尋找服務
} else if (newState == BluetoothGatt.STATE_DISCONNECTED) {
// 斷開連接
}
}
}
當判斷到連接成功之後,會去尋找服務, 這個過程是異步的,會耗點時間,當尋找到服務之後,會走到回調:
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
//尋找到服務
//尋找服務之後,我們就可以和設備進行通信,比如下發配置值,獲取設備電量什麼的
//具體操作
readBatrery(); //讀取電量操作
sendSetting(); //下發配置值
}
}
/***
* 讀操作
***/
public void readBatrery() {
//如上面所說,想要和一個學生通信,先知道他的班級(ServiceUUID)和學號(CharacUUID)
BluetoothGattService batteryService = mBluetoothGatt.getService(UUID.fromString("0000180f-0000-1000-8000-00805f9b34fb")); //此處的0000180f...是舉例,實際開發需要詢問硬件那邊
if (batteryService != null) {
BluetoothGattCharacteristic batteryCharacteristic = batteryService.getCharacteristic(UUID.fromString("00002a19-0000-1000-8000-00805f9b34fb"));
//此處的00002a19...是舉例,實際開發需要詢問硬件那邊
if (batteryCharacteristic != null) {
//讀取電量, 這是讀取batteryCharacteristic值的方法,讀取其他的值也是如此,只是它們的ServiceUUID 和CharacUUID不一樣
}
}
}
如果讀取電量(或者讀取其他值)成功之後 ,會來到 回調:
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
//讀取到值,根據UUID來判斷讀到的是什麼值
if (characteristic.getUuid().toString().equals("00002a19-0000-1000-8000-00805f9b34fb")) {
// 獲取到電量
int battery = characteristic.getValue()[0];
}
}
/***
* 寫操作
***/
void sendSetting() {
BluetoothGattService sendService = mBluetoothGatt.getService(UUID.fromString("00001805-0000-1000-8000-00805f9b34fb"));//此處的00001805...是舉例,實際開發需要詢問硬件那邊
if (sendService != null) {
BluetoothGattCharacteristic sendCharacteristic = sendService.getCharacteristic(UUID.fromString("00002a08-0000-1000-8000-00805f9b34fb"));//此處的00002a08...是舉例,實際開發需要詢問硬件那邊
if (sendCharacteristic != null) {
sendCharacteristic.setValue(new byte[]{0x01, 0x20, 0x03});//隨便舉個數據
mBluetoothGatt.writeCharacteristic(sendCharacteristic);//寫命令到設備,
}
}
}
如果下發配置成功之後,會來到回調:
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
//write成功(發送值成功),可以根據 characteristic.getValue()來判斷是哪個值發送成功了,比如 連接上設備之後你有一大串命令需要下發,你調用多次寫命令, 這樣你需要判斷是不是所有命令都成功了,因爲android不太穩定,有必要來check命令是否成功,否則你會發現你明明調用 寫命令,但是設備那邊不響應
}
}
講解完讀寫操作,還有一個重要操作 就是 Notify(通知)
notify 就是讓設備 可以發送通知給你,也可以說上報值給你(發送命令給你)
首先你得打開設備的通知功能:
//參數 enable 就是打開還是關閉, characteristic就是你想讓具有通知功能的BluetoothGattCharacteristic
private boolean enableNotification(boolean enable, BluetoothGattCharacteristic characteristic) {
if (mBluetoothGatt == null || characteristic == null)
return false;
if (!mBluetoothGatt.setCharacteristicNotification(characteristic,
enable))
return false;
BluetoothGattDescriptor clientConfig = characteristic.getDescriptor(UUIDUtils.CCC);
if (clientConfig == null)
return false;
if (enable) {
clientConfig.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
} else {
clientConfig.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
}
return mBluetoothGatt.writeDescriptor(clientConfig);
}
// 一旦設備那邊notify 數據給你,你會在回調裏收到:
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
//收到設備notify值 (設備上報值),根據 characteristic.getUUID()來判斷是誰發送值給你,根據 characteristic.getValue()來獲取這個值
}
關於發送命令
比如剛連接上設備,你會把很多配置都發送給設備,此時可能會調用多次 write操作,這個時候你應該需要在 write操作之間 加點間隔 例如開啓線程,用 Thread.sleep(150), 因爲只有收到onCharacteristicWrite回調之後才代表你的值成功了,如果不加間隔,你會發現可能只會成功一個值。目前測試下來,我才用的是間隔150毫秒比較好,不會太慢,成功率也會高, 可自己實踐慢慢調試,這個數值還與設備端update速率有關在Write 操作的時候,還有個辦法可以增快下發速度,且不需要加sleep就是設置:
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
就是不需要回復,這樣速度會快,還是有點可靠的。各位可斟酌使用