[英]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设备连接:
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:
注意:知道这两个设备以下列方式运行可能很重要:
- The BLE device is turned on an a measurement is initiated on the device.
打开BLE设备,在设备上启动测量。
- Once the measurement has been taken, the BLE device attempts to initiate a BLE connection.
一旦进行了测量,BLE设备就会尝试启动BLE连接。
- 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成功发送,它会将它们保存在内存中并发送所有数据,所以我只关心最终的数据包。)
- Once the final data packet is sent, the BLE device disconnects rapidly.
一旦发送了最终数据包,BLE设备就会快速断开连接。
- 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中,我看到很多输出与我期望的完全一样。
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: 我看了下面的问题:
Here's why they don't give me what I need: 这就是为什么他们不给我我需要的东西:
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端的哪些代码将模仿该功能?
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
返回true
和onDescriptorWrite
状态为0
)。 However, onCharacteristicChanged
still never fires. 但是,
onCharacteristicChanged
仍然永远不会发射。
I've been able to successfully catch onCharacteristicChanged() with multiple services and characteristics by: 我已经能够通过以下方式成功捕获具有多种服务和特征的onCharacteristicChanged():
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 和
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.