Android BLE:onCharacteristicChanged永远不会触发

时间:2015-02-18 14:30:56

标签: android bluetooth-lowenergy android-bluetooth

我正在尝试编写一个Android应用,模仿我编写的iOS应用中已有的功能。我正在与2个不同的BLE设备连接:

  1. 血压袖带
  2. 体重秤
  3. 在iOS上,我让两台设备都运行良好并报告数据。在Android上,我无法让它发挥作用。经过数小时的研究和测试,我认为我试图解决的基本问题是:

    在iOS上,我调用以下代码以使BLE设备在有数据要报告时通知我的iOS设备:

    #pragma mark - CBPeripheralDelegate Protocol methods
    - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
        for (CBCharacteristic *characteristic in [service characteristics]) {
            [peripheral setNotifyValue:YES forCharacteristic:characteristic];
        }
    }
    

    那就是它。 iOS中此方法的注释如下:

      

    如果指定的特征配置为允许通知和指示,则调用此方法仅启用通知。

    基于此(以及在iOS中运行这一事实),我想知道我想要通知的特性的配置描述符应该像这样配置:

    descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
    gatt.writeDescriptor(descriptor);
    

    考虑到这一点,我的BLEDevice课程如下:

    public abstract class BLEDevice {
        protected BluetoothAdapter.LeScanCallback   mLeScanCallback;
        protected BluetoothGattCallback             mBluetoothGattCallback;
        protected byte[]                            mBytes;
        protected Context                           mContext;
        protected GotReadingCallback                mGotReadingCallback;
        protected String                            mDeviceName;
        public final static UUID                    UUID_WEIGHT_SCALE_SERVICE                       
                = UUID.fromString(GattAttributes.WEIGHT_SCALE_SERVICE);
        public final static UUID                    UUID_WEIGHT_SCALE_READING_CHARACTERISTIC        
                = UUID.fromString(GattAttributes.WEIGHT_SCALE_READING_CHARACTERISTIC);
        public final static UUID                    UUID_WEIGHT_SCALE_CONFIGURATION_CHARACTERISTIC  
                = UUID.fromString(GattAttributes.WEIGHT_SCALE_CONFIGURATION_CHARACTERISTIC);
        public final static UUID                    UUID_WEIGHT_SCALE_CONFIGURATION_DESCRIPTOR      
                = UUID.fromString(GattAttributes.WEIGHT_SCALE_CONFIGURATION_DESCRIPTOR);
    
        abstract void processReading();
    
        interface GotReadingCallback {
            void gotReading(Object reading);
        }
    
        public BLEDevice(Context context, String deviceName, GotReadingCallback gotReadingCallback) {
            mContext                    = context;
            BluetoothManager btManager  = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
            final BluetoothAdapter btAdapter  = btManager.getAdapter();
            if (btAdapter != null && !btAdapter.isEnabled()) {
                Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                mContext.startActivity(enableIntent);
            }
            mDeviceName = deviceName;
            mBluetoothGattCallback = new BluetoothGattCallback() {
                @Override
                public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
                    byte[] data = characteristic.getValue();
                    mBytes = data;
                    Log.d("BluetoothGattCallback.onCharacteristicChanged", "data: " + data.toString());
                }
                @Override
                public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) {
                    // this will get called when a device connects or disconnects
                    if (newState == BluetoothProfile.STATE_CONNECTED) {
                        gatt.discoverServices();
                    } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                        if (mBytes != null) {
                            processReading();
                        }
                    }
                }
                @Override
                public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
                    super.onDescriptorWrite(gatt, descriptor, status);
                    Log.d("onDescriptorWrite", "descriptor: " + descriptor.getUuid() + ". characteristic: " + descriptor.getCharacteristic().getUuid() + ". status: " + status);
                }
                @Override
                public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
                    // this will get called after the client initiates a BluetoothGatt.discoverServices() call
                    BluetoothGattService service = gatt.getService(UUID_WEIGHT_SCALE_SERVICE);
                    if (service != null) {
                        BluetoothGattCharacteristic characteristic;
                        characteristic                              = service.getCharacteristic(UUID_WEIGHT_SCALE_READING_CHARACTERISTIC);
                        if (characteristic != null) {
                            gatt.setCharacteristicNotification(characteristic, true);
                        }
                        characteristic                              = service.getCharacteristic(UUID_WEIGHT_SCALE_CONFIGURATION_CHARACTERISTIC);
                        if (characteristic != null) {
                            BluetoothGattDescriptor descriptor      = characteristic.getDescriptor(UUID_WEIGHT_SCALE_CONFIGURATION_DESCRIPTOR);
                            if (descriptor != null) {
                                descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
                                gatt.writeDescriptor(descriptor);
                            }
                        }
                    }
                }
            };
            mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
                @Override
                public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
                    Log.d("LeScanCallback", device.toString());
                    if (device.getName().contains("{Device Name}")) {
                        BluetoothGatt bluetoothGatt = device.connectGatt(mContext, false, mBluetoothGattCallback);
                        btAdapter.stopLeScan(mLeScanCallback);
                    }
                }
            };
            btAdapter.startLeScan(mLeScanCallback);
        }
    }
    
      

    注意: 了解这两个设备的功能可能非常重要:

         
        
    1. 打开BLE设备,在设备上启动测量。
    2.   
    3. 一旦进行了测量,BLE设备将尝试启动BLE连接。
    4.   
    5. 一旦建立了BLE连接,设备几乎立即发送数据,有时会发送几个数据包。 (如果先前的数据测量结果尚未通过BLE成功发送,它会将它们保存在内存中并发送所有数据,因此我只关心最终的数据包。)
    6.   
    7. 发送完最终数据包后,BLE设备将快速断开连接。
    8.   
    9. 如果BLE设备无法发送数据(正如目前Android应用中发生的那样),则BLE设备会很快断开连接。
    10.   

    在我的LogCat中,我看到很多输出与我期望的完全一样。

    1. 我看到了一个像我期望的服务列表,包括我想要的数据服务。
    2. 我看到了一个像我期望的特征列表,包括我想要的数据特征。
    3. 我看到了一个像我期望的描述符列表,包括"配置" (0x2902)描述符。
    4. 我遇到的最近一次失败是" 128"正在onCharacteristicWrite报道。对问题#3(下面)的评论似乎表明这是一个资源问题。

      我查看了以下问题:

      1. Android BLE onCharacteristicChanged not called
      2. Android BLE, read and write characteristics
      3. Android 4.3 onDescriptorWrite returns status 128
      4. 这就是为什么他们不能给我我需要的东西:

        1. 这个问题的答案不是读取描述符的值。我不是那样做的,所以这不会成为阻碍他们行事的原因。
        2. 这基本上是可用的各种方法的概述,我想我现在明白了。这个问题/答案的关键不是多次写入不同的描述符,但我也没有这样做。我只关心这一个特点。
        3. 这个问题/答案似乎与BLE资源限制有关,但我认为这不适用。我只连接这一台设备,而且我正在尝试进行非常非常简单的数据传输。我不认为我达到了资源上限。
        4. 我尝试过一系列示例和教程,包括Google的Android示例代码。他们似乎都没有让BLE设备通知我的Android设备数据更新。它显然不是设备,因为iOS版本有效。那么,iOS代码在后台做什么来使通知工作以及Android端的哪些代码将模仿该功能?

          <小时/>

          EDIT / UPDATE

          根据@ yonran的评论,我通过将onServicesDiscovered实施更改为此更新了我的代码:

          @Override
          public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
              // this will get called after the client initiates a BluetoothGatt.discoverServices() call
              BluetoothGattService service = gatt.getService(UUID_WEIGHT_SCALE_SERVICE);
              if (service != null) {
                  BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID_WEIGHT_SCALE_READING_CHARACTERISTIC);
                  if (characteristic != null) {
                      if (gatt.setCharacteristicNotification(characteristic, true) == true) {
                          Log.d("gatt.setCharacteristicNotification", "SUCCESS!");
                      } else {
                          Log.d("gatt.setCharacteristicNotification", "FAILURE!");
                      }
                      BluetoothGattDescriptor descriptor = characteristic.getDescriptors().get(0);
                      if (0 != (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE)) {
                          // It's an indicate characteristic
                          Log.d("onServicesDiscovered", "Characteristic (" + characteristic.getUuid() + ") is INDICATE");
                          if (descriptor != null) {
                              descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
                              gatt.writeDescriptor(descriptor);
                          }
                      } else {
                          // It's a notify characteristic
                          Log.d("onServicesDiscovered", "Characteristic (" + characteristic.getUuid() + ") is NOTIFY");
                          if (descriptor != null) {
                              descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                              gatt.writeDescriptor(descriptor);
                          }
                      }
                  }
              }
          }
          

          确实 似乎已经改变了一些东西。这是代码更改之后的当前Logcat:

          D/BluetoothGatt﹕ setCharacteristicNotification() - uuid: <UUID> enable: true
          D/gatt.setCharacteristicNotification﹕ SUCCESS!
          D/onServicesDiscovered﹕ Characteristic (<UUID>) is INDICATE
          D/BluetoothGatt﹕ writeDescriptor() - uuid: 00002902-0000-1000-8000-00805f9b34fb
          D/BluetoothGatt﹕ onDescriptorWrite() - Device=D0:5F:B8:01:6C:9E UUID=<UUID>
          D/onDescriptorWrite﹕ descriptor: 00002902-0000-1000-8000-00805f9b34fb. characteristic: <UUID>. status: 0
          D/BluetoothGatt﹕ onClientConnectionState() - status=0 clientIf=6 device=D0:5F:B8:01:6C:9E
          

          因此,我现在可以正确设置所有内容(因为setCharacteristicNotification返回trueonDescriptorWrite状态为0)。但是,onCharacteristicChanged仍然永远不会发生。


1 个答案:

答案 0 :(得分:8)

我已经能够通过以下方式成功捕获具有多种服务和特征的onCharacteristicChanged():

  1. 服务发现完成后,在主循环中的broadcastReceiver()中编写描述符值。

    private final BroadcastReceiver UARTStatusChangeReceiver = new BroadcastReceiver() {
    //more code...
    if (action.equals(uartservice.ACTION_GATT_SERVICES_DISCOVERED)) {
             mService.enableTXNotification();
        }
    
    1. 在描述符值设置之间添加延迟

      public void enableTXNotification(){ 
      /*
      if (mBluetoothGatt == null) {
          showMessage("mBluetoothGatt null" + mBluetoothGatt);
          broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
          return;
      }
          */
      /**
       * Enable Notifications for the IO service and characteristic
       *
       */
      BluetoothGattService IOService = mBluetoothGatt.getService(IO_SERVICE_UUID);
      if (IOService == null) {
          showMessage("IO service not found!");
          broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_IO);
          return;
      }
      BluetoothGattCharacteristic IOChar = IOService.getCharacteristic(IO_CHAR_UUID);
      if (IOChar == null) {
          showMessage("IO charateristic not found!");
          broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_IO);
          return;
      }
      mBluetoothGatt.setCharacteristicNotification(IOChar,true);
      BluetoothGattDescriptor descriptorIO = IOChar.getDescriptor(CCCD);
      descriptorIO.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
      mBluetoothGatt.writeDescriptor(descriptorIO);
      
      /**
       * For some reason android (or the device) can't handle
       * writing one descriptor after another properly.  Without
       * the delay only the first characteristic can be caught in
       * onCharacteristicChanged() method.
       */
      try {
          Thread.sleep(200);
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
      /**
       * Enable Indications for the RXTX service and characteristic
       */
      BluetoothGattService RxService = mBluetoothGatt.getService(RXTX_SERVICE_UUID);
      if (RxService == null) {
          showMessage("Rx service not found!");
          broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
          return;
      }
      BluetoothGattCharacteristic RxChar = RxService.getCharacteristic(RXTX_CHAR_UUID);
      if (RxChar == null) {
          showMessage("Tx charateristic not found!");
          broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
          return;
      }
      mBluetoothGatt.setCharacteristicNotification(RxChar,true);
      BluetoothGattDescriptor descriptor = RxChar.getDescriptor(CCCD);
      descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE );
      mBluetoothGatt.writeDescriptor(descriptor);
      
      try {
          Thread.sleep(200);
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
      /**
       * Enable Notifications for the Battery service and Characteristic?
       */
      BluetoothGattService batteryService = mBluetoothGatt.getService(BATTERY_SERVICE_UUID);
      if (batteryService == null) {
          showMessage("Battery service not found!");
          broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_BATTERY);
          return;
      }
      BluetoothGattCharacteristic batteryChar = batteryService.getCharacteristic(BATTERY_CHAR_UUID);
      if (batteryChar == null) {
          showMessage("Battery charateristic not found!");
          broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_BATTERY);
          return;
      }
      }