Ble4.0 APP開發總結

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

就是不需要回復,這樣速度會快,還是有點可靠的。各位可斟酌使用

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