简体   繁体   English

Android BLE:onCharacteristicChanged永远不会触发

[英]Android BLE: onCharacteristicChanged never fires

I'm trying to write an Android app that mimics functionality already present in an iOS app I wrote. 我正在尝试编写一个Android应用程序,模仿我编写的iOS应用程序中已存在的功能。 I am interfacing with 2 different BLE devices: 我正在与2个不同的BLE设备连接:

  1. Blood Pressure Cuff 血压袖带
  2. Weight Scale 计重秤

On iOS, I have both devices working well and reporting data. 在iOS上,我有两个设备都运行良好并报告数据。 On Android, I can't get it to work. 在Android上,我无法让它工作。 After hours of research and testing, I think the basic issue I'm trying to solve is this: 经过数小时的研究和测试,我认为我要解决的基本问题是:

On iOS, I call the following code to enable the BLE device to notify my iOS device when it has data to report: 在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];
    }
}

That's it. 而已。 The notes for this method in iOS say the following: iOS中此方法的注释说明如下:

If the specified characteristic is configured to allow both notifications and indications, calling this method enables notifications only. 如果指定的特性配置为允许通知和指示,则调用此方法仅启用通知。

Based on that (and the fact that it works in iOS), I'm figuring that the configuration descriptor for the characteristic for which I want notifications should be configured like this: 基于此(以及在iOS中工作的事实),我正在计算我想要通知的特性的配置描述符应该像这样配置:

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

With that in mind, my BLEDevice class looks like this: 考虑到这一点,我的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);
    }
}

NOTE: It might be important to know that these 2 devices function in the following way: 注意:知道这两个设备以下列方式运行可能很重要:

  1. The BLE device is turned on an a measurement is initiated on the device. 打开BLE设备,在设备上启动测量。
  2. Once the measurement has been taken, the BLE device attempts to initiate a BLE connection. 一旦进行了测量,BLE设备就会尝试启动BLE连接。
  3. Once the BLE connection is made, the device pretty much immediately sends the data, sometimes sending a couple of data packets. 一旦建立了BLE连接,设备就会立即发送数据,有时会发送一些数据包。 (If previous data measurements haven't been successfully sent over BLE, it keeps them in memory and sends all of them, so I only really care about the final data packet.) (如果以前的数据测量没有通过BLE成功发送,它会将它们保存在内存中并发送所有数据,所以我只关心最终的数据包。)
  4. Once the final data packet is sent, the BLE device disconnects rapidly. 一旦发送了最终数据包,BLE设备就会快速断开连接。
  5. If the BLE device fails to send data (as is currently happening on the Android app), the BLE device disconnects pretty rapidly. 如果BLE设备无法发送数据(如当前在Android应用程序上发生的那样),则BLE设备会非常快速地断开连接。

In my LogCat, I see a lot of output that's exactly like I'd expect. 在我的LogCat中,我看到很多输出与我期望的完全一样。

  1. I see a list of services like I expect, including the data service I want. 我看到了一个像我期望的服务列表,包括我想要的数据服务。
  2. I see a list of characteristics like I expect, including the data characteristic I want. 我看到了一个像我期望的特征列表,包括我想要的数据特征。
  3. I see a list of descriptors like I expect, including the "configuration" (0x2902) descriptor. 我看到了一个像我期望的描述符列表,包括“配置”(0x2902)描述符。

The most recent failure I'm experiencing is a status of "128" being reported in onCharacteristicWrite . 我遇到的最新故障是onCharacteristicWrite中报告的状态为“128”。 The comments to question #3 (below) seem to indicate this is a resource issue. 问题#3(下面)的评论似乎表明这是一个资源问题。

I've looked at the following questions: 我看了下面的问题:

  1. Android BLE onCharacteristicChanged not called Android BLE onCharacteristicChanged未调用
  2. Android BLE, read and write characteristics Android BLE,读写特性
  3. Android 4.3 onDescriptorWrite returns status 128 Android 4.3 onDescriptorWrite返回状态128

Here's why they don't give me what I need: 这就是为什么他们不给我我需要的东西:

  1. This question's answer was not to read the descriptor's value. 这个问题的答案不是读取描述符的值。 I'm not doing that, so that can't be what's getting in the way. 我不是那样做的,所以这不可能是阻碍的。
  2. This is basically an overview of the various methods that are available, which I think I now understand. 这基本上是可用的各种方法的概述,我想我现在明白了。 The big key in this question/answer is not to write multiple times to different descriptors, but I'm also not doing that. 这个问题/答案的关键不是多次写入不同的描述符,但我也没有这样做。 I only care about the one characteristic. 我只关心一个特征。
  3. This question/answer seems to be related to BLE resource limitations, but I don't think this applies. 这个问题/答案似乎与BLE资源限制有关,但我不认为这适用。 I'm only connecting this one device and I'm trying to do a very, very simple data transfer. 我只是连接这个设备,我正在尝试进行非常非常简单的数据传输。 I don't think I'm hitting resource ceilings. 我认为我没有达到资源上限。

I've tried a bunch of examples and tutorials, including Google's Android sample code. 我尝试过一些示例和教程,包括谷歌的Android示例代码。 None of them seem to enable the BLE device to notify my Android device of data updates. 他们似乎都没有让BLE设备通知我的Android设备数据更新。 It's obviously not the device, since the iOS version works. 它显然不是设备,因为iOS版本有效。 So, what is the iOS code doing in the background to get the notifications to work and what code on the Android side will mimic that functionality? 那么,iOS代码在后台做什么来使通知工作以及Android端的哪些代码将模仿该功能?


EDIT/UPDATE 编辑/ UPDATE

Based on @yonran's comments, I updated my code by changing the onServicesDiscovered implementation to this: 根据@ 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);
                }
            }
        }
    }
}

That does seem to have changed some things a little bit. 这似乎确实改变了一些事情。 Here's the current Logcat, following that code change: 这是代码更改后的当前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

So, it would appear that I'm now setting everything up properly (since setCharacteristicNotification returns true and the onDescriptorWrite status is 0 ). 所以,这样看来,我现在一切都设置正确(因为setCharacteristicNotification返回trueonDescriptorWrite状态为0 )。 However, onCharacteristicChanged still never fires. 但是, onCharacteristicChanged仍然永远不会发射。


I've been able to successfully catch onCharacteristicChanged() with multiple services and characteristics by: 我已经能够通过以下方式成功捕获具有多种服务和特征的onCharacteristicChanged():

  1. Writing descriptor values in the broadcastReceiver() in the main loop after service discovery is finished. 在服务发现完成后,在主循环中的broadcastReceiver()中写入描述符值。

     private final BroadcastReceiver UARTStatusChangeReceiver = new BroadcastReceiver() { //more code... if (action.equals(uartservice.ACTION_GATT_SERVICES_DISCOVERED)) { mService.enableTXNotification(); } 

and

  1. By adding a delay between descriptor value settings 通过在描述符值设置之间添加延迟

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

I was facing the same problem. 我遇到了同样的问题。 that's because when the device is sending the indicate value, your application is charged in another process and that's why you never get the indicate value which make the onCharacteristicChanged never fires. 这是因为当设备发送指示值时,您的应用程序将在另一个进程中收费,这就是为什么您永远不会获得使onCharacteristicChanged永远不会触发的指示值的原因。 to resolve your problem try to put all traitement in a service. 解决您的问题尝试将所有traitement放入服务中。 and just call functions from your activity. 并且只需从您的活动中调用函数。

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

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