Ble藍牙開發詳解

藍牙主要分爲兩種:經典藍牙和低功耗藍牙

Ble簡介

低功耗藍牙4.0只有android4.3或4.3以上才支持
1、BLE藍牙4.0核心profile,主要特點是快速搜索,快速連接,超低功耗保持連接和數據傳輸,缺點:數據傳輸速率低,BLE藍牙一包數據最多爲20字節,因此安卓系統下最好不要使用BLE藍牙傳輸大量數據,由於其具有低功耗特點,所以經常用在可穿戴設備之中。
2、Profile: 藍牙規範,一個標準的通信協議,例如:A2DP、HFP、GAP、SPP,GATT等都是一種藍牙規範,其存在於手機中,藍牙組織規定了一些標準的profile:HID OVER GATT ,防丟器等,每個profile中包含了多個service。
3、service: 藍牙提供的服務,是Characteristic的集合,在BLE從機中有多個服務,電量信息,系統服務信息等,每一個service中包含了多個characteristic特徵值,每一個具體的characteristic特徵值纔是BLE通信的主題,Service也可以包含Service,在開發中,一個外設設備一般只有一個服務。設備中每一個不同的 Service 都有一個 128 bit 的 UUID 作爲這個 Service 的獨立標誌。藍牙核心規範制定了兩種不同的UUID,一種是基本的UUID,一種是代替基本UUID的16位UUID。
4、characteristic特徵值:BLE主機從機通信均是通過characteristic進行,可以將其理解爲一個標籤,通過該標籤可以讀取或寫入相關信息,在實際中可以認爲一個數據傳輸通道,是GATT協議的最少邏輯單元。Characteristic 的特性可以通過Descriptor配置。
5、 UUID(統一標識碼):service和characteristic均需要這個唯一的UUID進行標識。
6、Descriptor:對Characteristic 的描述,如Characteristic 是否可寫,可讀,是否使能通知等信息
7、GATT:在 BLE中,其協議規範爲GATT(Generic Attribute Profile:發送和接收很短的數據段的通用規範),GATT協議由若干個Service組成。
8、角色和職責,就是中心設備和外圍設備(GATT server vs. GATT client.),運用了ble的app和ble設備之間的通信,當app搜索到了ble設備,app會收到ble反饋過來的信息比如電量,也就是characteristic上面說的特徵值,相反我們app也可以通過寫入一些信息到這個對象(characteristic)發送給設備,設備收到之後就會執行我們的要它做的動作了。

BLE API 簡介

BluetoothAdapter
BluetoothAdapter 擁有基本的藍牙操作,例如開啓藍牙掃描,使用已知的 MAC 地址 (BluetoothAdapter#getRemoteDevice)實例化一個 BluetoothDevice 用於連接藍牙設備的操作等等。

BluetoothDevice
代表一個遠程藍牙設備。這個類可以讓你連接所代表的藍牙設備或者獲取一些有關它的信息,例如它的名字,地址和綁定狀態等等。

BluetoothGatt
這個類提供了 Bluetooth GATT 的基本功能。例如重新連接藍牙設備,發現藍牙設備的 Service 等等。

BluetoothGattService
這個類通過 BluetoothGatt#getService 獲得,如果當前服務不可見那麼將返回一個 null。這個類對應上面說過的 Service。我們可以通過這個類的 getCharacteristic(UUID uuid) 進一步獲取 Characteristic 實現藍牙數據的雙向傳輸。

BluetoothGattCharacteristic
這個類對應上面提到的 Characteristic。通過這個類定義需要往外圍設備寫入的數據和讀取外圍設備發送過來的數據。

ble藍牙開發流程:
(一)申請權限
安卓手機涉及藍牙權限問題,藍牙開發需要在AndroidManifest.xml文件中添加權限聲明:

<!-- 藍牙權限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

在Android5.0之前,是默認申請GPS硬件功能的。而在Android 5.0 之後,需要在manifest 中申明GPS硬件模塊功能的使用。

<uses-feature android:name="android.hardware.location.gps" />

在 Android 6.0 及以上,還需要打開位置權限。如果應用沒有位置權限,藍牙掃描功能不能使用(其它藍牙操作例如連接藍牙設備和寫入數據不受影響)

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

手機權限管理中允許此權限,否則會出現無法搜索到設備的情況。

(二)打開藍牙
在搜索設備之前需要詢問打開手機藍牙,其關鍵代碼如下:

 //獲取系統藍牙適配器管理類
    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();
            }
        }
    }

(三)搜索設備
本文主要針對BLE藍牙開發,因此採用mBluetoothAdapter.startLeScan(LeScanCallback callback)方式掃描BLE藍牙設備。調用方法如下:

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;
        }
    }
};

(四)連接設備
通過掃描BLE設備,根據設備名稱區分出目標設備targetDevice,下一步實現與目標設備的連接。在連接設備之前要停止搜索藍牙。

mBluetoothAdapter.stopLeScan(callback);

注 :停止搜索一般需要一定的時間來完成,最好調用停止搜索函數之後加以100ms的延時,保證系統能夠完全停止搜索藍牙設備。停止搜索之後啓動連接過程。
BLE藍牙的連接方法相對簡單隻需調用connectGatt方法,函數原型如下:

public BluetoothGatt connectGatt (Context context, boolean autoConnect, BluetoothGattCallback callback);

參數說明
返回值 BluetoothGatt: BLE藍牙連接管理類,主要負責與設備進行通信。後續會進一步介紹該類。
boolean autoConnect:建議置爲false,能夠提升連接速度。
BluetoothGattCallback callback 連接回調,重要參數,BLE通信的核心部分。

(五)設備通信
與設備建立連接之後與設備通信,整個通信過程都是在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開發中不可缺少的,每個函數的意義以及被調用的時機會在後續步驟中一一說明。

(1)等待設備連接成功
當調用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表明此時已經成功連接到設備。

(2)開啓掃描服務

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。
BluetoothGattDescriptor:用於描述特徵的類,其同樣包含一個value值。

(3)獲取負責通信的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"));

(4)開啓監聽
開啓監聽,即建立與設備的通信的首發數據通道,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, "開啓監聽成功");
        }
            
};

(5)寫入數據
監聽成功後通過向 writeCharacteristic寫入數據實現與下位機的通信。寫入方式如下:
//value爲上位機向下位機發送的指令

writeCharacteristic.setValue(value);
mBluetoothGatt.writeCharacteristic(writeCharacteristic)

其中:value一般爲Hex格式指令,其內容由設備通信的藍牙通信協議規定。
(6)接收數據
若寫入指令成功則回調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();

}

通過向下位機發送指令獲取下位機的回覆數據,即可完成與設備的通信過程。

(六)斷開連接
當與設備完成通信之後之後一定要斷開與設備的連接。調用以下方法斷開與設備的連接:

mBluetoothGatt.disconnect();
mBluetoothGatt.close();

(七)數據分包和拼包的處理方法

分包:藍牙ble發送的字節一般爲20byte,在使用的時候可能會有超過20byte時候就需要分包處理,分包處理很簡單,簡需要發送的數據分成每包20byte發送即可,尾包按實際字節數發送。
拼包:藍牙ble接收的字節數一般也爲20字節,所以需要做拼包處理,處理方法是開闢一個數組緩衝區,將接收到的數據放進緩衝區,每次存放數據都要檢測數據是否接收完畢,對於異常數據做拋棄處理,緩衝區長度應可自動伸縮,避免當需要一次性傳輸大數據包是要重新修改藍牙庫。

(八)和經典藍牙混合使用的注意項

通過mBluetoothAdapter.startDisCovery()方式掃描藍牙,在部分高版本手機中會出現將ble也掃描出來,並通過廣播發送出來,因此需要增加判斷
A2dp/Hfp藍牙連接注意情況
a. 通過fetchUuidsWithSdp發現經典藍牙的服務。此方法不應該調用多次,調用多次會造成部分手機經典藍牙A2DP/HFP服務連接失敗連接
b.在通過fetchUuidsWithSdp發現經典藍牙發現服務時,應先讀取設備的UUIDS,判斷是否有緩存,如果有就跳過服務發現,直接連接服務,不然會出現連接失敗。

文章參考
https://www.jianshu.com/p/a27f3ca027e3
https://blog.csdn.net/tongziwei1991/article/details/81588314

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