繁体   English   中英

Android BLE:onCharacteristicChanged永远不会触发

[英]Android BLE: onCharacteristicChanged never fires

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

  1. 血压袖带
  2. 计重秤

在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. 一旦进行了测量,BLE设备就会尝试启动BLE连接。
  3. 一旦建立了BLE连接,设备就会立即发送数据,有时会发送一些数据包。 (如果以前的数据测量没有通过BLE成功发送,它会将它们保存在内存中并发送所有数据,所以我只关心最终的数据包。)
  4. 一旦发送了最终数据包,BLE设备就会快速断开连接。
  5. 如果BLE设备无法发送数据(如当前在Android应用程序上发生的那样),则BLE设备会非常快速地断开连接。

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

  1. 我看到了一个像我期望的服务列表,包括我想要的数据服务。
  2. 我看到了一个像我期望的特征列表,包括我想要的数据特征。
  3. 我看到了一个像我期望的描述符列表,包括“配置”(0x2902)描述符。

我遇到的最新故障是onCharacteristicWrite中报告的状态为“128”。 问题#3(下面)的评论似乎表明这是一个资源问题。

我看了下面的问题:

  1. Android BLE onCharacteristicChanged未调用
  2. Android BLE,读写特性
  3. Android 4.3 onDescriptorWrite返回状态128

这就是为什么他们不给我我需要的东西:

  1. 这个问题的答案不是读取描述符的值。 我不是那样做的,所以这不可能是阻碍的。
  2. 这基本上是可用的各种方法的概述,我想我现在明白了。 这个问题/答案的关键不是多次写入不同的描述符,但我也没有这样做。 我只关心一个特征。
  3. 这个问题/答案似乎与BLE资源限制有关,但我不认为这适用。 我只是连接这个设备,我正在尝试进行非常非常简单的数据传输。 我认为我没有达到资源上限。

我尝试过一些示例和教程,包括谷歌的Android示例代码。 他们似乎都没有让BLE设备通知我的Android设备数据更新。 它显然不是设备,因为iOS版本有效。 那么,iOS代码在后台做什么来使通知工作以及Android端的哪些代码将模仿该功能?


编辑/ 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仍然永远不会发射。


我已经能够通过以下方式成功捕获具有多种服务和特征的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; } } 

我遇到了同样的问题。 这是因为当设备发送指示值时,您的应用程序将在另一个进程中收费,这就是为什么您永远不会获得使onCharacteristicChanged永远不会触发的指示值的原因。 解决您的问题尝试将所有traitement放入服务中。 并且只需从您的活动中调用函数。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM