一篇了解 BLE 藍牙開發詳解
前言
有老鐵們私信,要講解下藍牙開發,那么今天來了;
Android 4.3(API Level 18)開始引入Bluetooth Low Energy(BLE,低功耗藍牙)的核心功能并提供了相應的 API, 應用程序通過這些 API 掃描藍牙設備、查詢 services、讀寫設備的 characteristics(屬性特征)等操作。
BLE低功耗藍牙,主要特點是快速搜索,快速連接,超低功耗保持連接和數據傳輸;
一、BLE開發流程
1、申請權限
安卓手機涉及藍牙權限問題,藍牙開發需要在AndroidManifest.xml文件中添加權限聲明:
- <!-- 藍牙權限 -->
- <uses-permission android:name="android.permission.BLUETOOTH" />
- <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
- 為適配安卓6.0以及以上版本需要添加一個模糊定位的權限
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
- 手機權限管理中允許此權限,否則會出現無法搜索到設備的情況;
為適配安卓6.0以及以上版本需要添加一個模糊定位的權限
手機權限管理中允許此權限,否則會出現無法搜索到設備的情況;
2、打開藍牙
在搜索設備之前需要詢問打開手機藍牙:
- //獲取系統藍牙適配器管理類
- private BluetoothAdapter mBluetoothAdapter = BluetoothAdapter
- .getDefaultAdapter();
- // 詢問打開藍牙
- if (mBluetoothAdapter != null && !mBluetoothAdapter.isEnabled()) {
- Intent enableBtIntent = new Intent(
- BluetoothAdapter.ACTION_REQUEST_ENABLE);
- startActivityForResult(enableBtIntent, 1);
- }
- // 申請打開藍牙請求的回調
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- // TODO Auto-generated method stub
- super.onActivityResult(requestCode, resultCode, data);
- if (requestCode == 1) {
- if (resultCode == RESULT_OK) {
- Toast.makeText(this, "藍牙已經開啟", Toast.LENGTH_SHORT).show();
- } else if (resultCode == RESULT_CANCELED) {
- Toast.makeText(this, "沒有藍牙權限", Toast.LENGTH_SHORT).show();
- finish();
- }
- }
- }
3、搜索設備
- mBluetoothAdapter.startLeScan(callback);
- private LeScanCallback callback = new LeScanCallback() {
- @Override
- public void onLeScan(BluetoothDevice device, int arg1, byte[] arg2) {
- //device為掃描到的BLE設備
- if(device.getName() == "目標設備名稱"){
- //獲取目標設備
- targetDevice = device;
- }
- }
- };
4、連接設備
通過掃描BLE設備,根據設備名稱區分出目標設備targetDevice,下一步實現與目標設備的連接,在連接設備之前要停止搜索藍牙;
mBluetoothAdapter.stopLeScan(callback);
停止搜索一般需要一定的時間來完成,最好調用停止搜索函數之后加以100ms的延時,保證系統能夠完全停止搜索藍牙設備。停止搜索之后啟動連接過程;
BLE藍牙的連接方法相對簡單只需調用connectGatt方法;
- mBluetoothAdapter.startLeScan(callback);
- private LeScanCallback callback = new LeScanCallback() {
- @Override
- public void onLeScan(BluetoothDevice device, int arg1, byte[] arg2) {
- //device為掃描到的BLE設備
- if(device.getName() == "目標設備名稱"){
- //獲取目標設備
- targetDevice = device;
- }
- }
- };
參數說明
返回值 BluetoothGatt: BLE藍牙連接管理類,主要負責與設備進行通信;
boolean autoConnect:建議置為false,能夠提升連接速度;
BluetoothGattCallback callback 連接回調,重要參數,BLE通信的核心部分;
5、設備通信
與設備建立連接之后與設備通信,整個通信過程都是在BluetoothGattCallback的異步回調函數中完成;
BluetoothGattCallback中主要回調函數如下:
- private BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
- @Override
- public void onConnectionStateChange(BluetoothGatt gatt, int status,
- int newState) {
- }
- @Override
- public void onCharacteristicWrite(BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic, int status) {
- super.onCharacteristicWrite(gatt, characteristic, status);
- }
- @Override
- public void onDescriptorWrite(BluetoothGatt gatt,
- BluetoothGattDescriptor descriptor, int status) {
- };
- @Override
- public void onServicesDiscovered(BluetoothGatt gatt, int status) {
- }
- @Override
- public void onCharacteristicChanged(BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic) {
- }
- };
上述幾個回調函數是BLE開發中不可缺少的;
6、等待設備連接成功
當調用targetdDevice.connectGatt(context, false, gattCallback)后系統會主動發起與BLE藍牙設備的連接,若成功連接到設備將回調onConnectionStateChange方法,其處理過程如下:
- @Override
- public void onConnectionStateChange(BluetoothGatt gatt, int status,
- int newState) {
- if (newState == BluetoothGatt.STATE_CONNECTED) {
- Log.e(TAG, "設備連接上 開始掃描服務");
- // 開始掃描服務,安卓藍牙開發重要步驟之一
- mBluetoothGatt.discoverServices();
- }
- if (newState == BluetoothGatt.STATE_DISCONNECTED) {
- // 連接斷開
- /*連接斷開后的相應處理*/
- }
- };
判斷newState == BluetoothGatt.STATE_CONNECTED表明此時已經成功連接到設備;
7、開啟掃描服務
mBluetoothGatt.discoverServices();
掃描BLE設備服務是安卓系統中關于BLE藍牙開發的重要一步,一般在設備連接成功后調用,掃描到設備服務后回調onServicesDiscovered()函數,函數原型如下:
- @Override
- public void onServicesDiscovered(BluetoothGatt gatt, int status) {
- private List<BluetoothGattService> servicesList;
- //獲取服務列表
- servicesList = mBluetoothGatt.getServices();
- }
- BLE藍牙協議下數據的通信方式采用BluetoothGattService、BluetoothGattCharacteristic和BluetoothGattDescriptor三個主要的類實現通信;
- BluetoothGattService 簡稱服務,是構成BLE設備協議棧的組成單位,一個藍牙設備協議棧一般由一個或者多個BluetoothGattService組成;
- BluetoothGattCharacteristic 簡稱特征,一個服務包含一個或者多個特征,特征作為數據的基本單元;
- 一個BluetoothGattCharacteristic特征包含一個數據值和附加的關于特征的描述
- BluetoothGattDescriptor:用于描述特征的類,其同樣包含一個value值;
8、獲取負責通信的BluetoothGattCharacteristic
BLE藍牙開發主要有負責通信的BluetoothGattService完成的。當且稱為通信服務。通信服務通過硬件工程師提供的UUID獲取。獲取方式如下:
- BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString("藍牙模塊提供的負責通信UUID字符串"));
- 通信服務中包含負責讀寫的BluetoothGattCharacteristic,且分別稱為notifyCharacteristic和writeCharacteristic。其中notifyCharacteristic負責開啟監聽,也就是啟動收數據的通道,writeCharacteristic負責寫入數據;
具體操作方式如下:
- BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString("藍牙模塊提供的負責通信服務UUID字符串"));
- // 例如形式如:49535343-fe7d-4ae5-8fa9-9fafd205e455
- notifyCharacteristic = service.getCharacteristic(UUID.fromString("notify uuid"));
- writeCharacteristic = service.getCharacteristic(UUID.fromString("write uuid"));
9、開啟監聽
開啟監聽,即建立與設備的通信的首發數據通道,BLE開發中只有當上位機成功開啟監聽后才能與下位機收發數據。開啟監聽的方式如下:
- mBluetoothGatt.setCharacteristicNotification(notifyCharacteristic, true)
- BluetoothGattDescriptor descriptor = characteristic
- .getDescriptor(UUID
- .fromString
- ("00002902-0000-1000-8000-00805f9b34fb"));
- descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
若開啟監聽成功則會回調BluetoothGattCallback中的onDescriptorWrite()方法,處理方式如下:
- @Override
- public void onDescriptorWrite(BluetoothGatt gatt,
- BluetoothGattDescriptor descriptor, int status) {
- if (status == BluetoothGatt.GATT_SUCCESS) {
- //開啟監聽成功,可以像設備寫入命令了
- Log.e(TAG, "開啟監聽成功");
- }
- };
10、寫入數據
監聽成功后通過向 writeCharacteristic寫入數據實現與下位機的通信。寫入方式如下:
- //value為上位機向下位機發送的指令
- writeCharacteristic.setValue(value);
- mBluetoothGatt.writeCharacteristic(writeCharacteristic)
其中:value一般為Hex格式指令,其內容由設備通信的藍牙通信協議規定;
11、接收數據
若寫入指令成功則回調BluetoothGattCallback中的onCharacteristicWrite()方法,說明將數據已經發送給下位機;
- @Override
- public void onCharacteristicWrite(BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic, int status) {
- if (status == BluetoothGatt.GATT_SUCCESS) {
- Log.e(TAG, "發送成功");
- }
- super.onCharacteristicWrite(gatt, characteristic, status);
- }
若發送的數據符合通信協議,則下位機會向上位機回復相應的數據。發送的數據通過回調onCharacteristicChanged()方法獲取,其處理方式如下:
- @Override
- public void onCharacteristicChanged(BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic) {
- // value為設備發送的數據,根據數據協議進行解析
- byte[] value = characteristic.getValue();
- }
通過向下位機發送指令獲取下位機的回復數據,即可完成與設備的通信過程;
12、斷開連接
當與設備完成通信之后之后一定要斷開與設備的連接。調用以下方法斷開與設備的連接:
- mBluetoothGatt.disconnect();
- mBluetoothGatt.close();
二、藍牙操作的注意事項
- 藍牙的寫入操作, 讀取操作必須序列化進行. 寫入數據和讀取數據是不能同時進行的, 如果調用了寫入數據的方法, 馬上調用又調用寫入數據或者讀取數據的方法,第二次調用的方法會立即返回 false, 代表當前無法進行操作;
- Android 連接外圍設備的數量有限,當不需要連接藍牙設備的時候,必須調用 BluetoothGatt#close 方法釋放資源;
- 藍牙 API 連接藍牙設備的超時時間大概在 20s 左右,具體時間看系統實現。有時候某些設備進行藍牙連接的時間會很長,大概十多秒。如果自己手動設置了連接超時時間在某些設備上可能會導致接下來幾次的連接嘗試都會在 BluetoothGattCallback#onConnectionStateChange 返回 state == 133;
- 所有的藍牙操作使用 Handler 固定在一條線程操作,這樣能省去很多因為線程不同步導致的麻煩;
總結
藍牙開發中有很多問題,要靜下心分析問題,肯定可以解決的,一起加油;