【发布时间】:2015-04-19 14:26:14
【问题描述】:
我正在尝试编写一个模仿我编写的 iOS 应用程序中已有功能的 Android 应用程序。我正在与 2 个不同的 BLE 设备连接:
- 血压袖带
- 体重秤
在 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);
}
}
注意:了解这两种设备以下列方式运行可能很重要:
- BLE 设备已开启并在设备上启动测量。
- 进行测量后,BLE 设备会尝试启动 BLE 连接。
- 一旦建立 BLE 连接,设备几乎会立即发送数据,有时会发送几个数据包。 (如果之前的数据测量没有通过 BLE 成功发送,它会将它们保存在内存中并全部发送,所以我只关心最终的数据包。)
- 一旦发送最终数据包,BLE 设备会迅速断开连接。
- 如果 BLE 设备无法发送数据(目前在 Android 应用上发生的情况),BLE 设备会很快断开连接。
在我的 LogCat 中,我看到了很多与我预期完全一样的输出。
- 我看到了我期望的服务列表,包括我想要的数据服务。
- 我看到了我期望的特征列表,包括我想要的数据特征。
- 我看到了一个符合预期的描述符列表,包括“配置”(0x2902) 描述符。
我最近遇到的故障是onCharacteristicWrite 中报告的“128”状态。问题 #3(下)的 cmets 似乎表明这是一个资源问题。
我查看了以下问题:
- Android BLE onCharacteristicChanged not called
- Android BLE, read and write characteristics
- Android 4.3 onDescriptorWrite returns status 128
这就是为什么他们没有给我我需要的东西:
- 这个问题的答案不是读取描述符的值。我不这样做,所以这不会是阻碍。
- 这基本上是对可用的各种方法的概述,我想我现在已经理解了。这个问题/答案的关键不是多次写入不同的描述符,但我也没有这样做。我只关心一个特征。
- 这个问题/答案似乎与 BLE 资源限制有关,但我认为这并不适用。我只连接这一个设备,我正在尝试进行非常非常简单的数据传输。我认为我没有达到资源上限。
我尝试了很多示例和教程,包括 Google 的 Android 示例代码。它们似乎都无法使 BLE 设备通知我的 Android 设备数据更新。这显然不是设备,因为 iOS 版本可以工作。那么,iOS 代码在后台做什么以使通知工作,以及 Android 端的哪些代码会模仿该功能?
编辑/更新
基于@yonran 的cmets,我通过将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 返回true 并且onDescriptorWrite 状态为0)。但是,onCharacteristicChanged 仍然不会触发。
【问题讨论】:
-
您是否介意检查特征属性以查看支持的属性(通知与指示)?
0 != (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE)? iOS 会自动为您执行此操作。 -
@yonran 我愿意!当我回到我的办公室时会这样做。谢谢你给我一个想法。你认为我可能需要以不同的方式启用它?
-
@yonran 以下代码返回 true(执行进入“if”语句):
if (0 != (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE)) { -
等等,看起来您正在对不同的特征调用 setCharacteristicNotification,而不是您启用指示的客户端特征配置。使用相同的特征。
-
@yonran 是的,我发现并更改了它。我即将发布更新。
标签: android bluetooth-lowenergy android-bluetooth