mbm*_*414 11 android bluetooth-lowenergy android-bluetooth
我正在尝试编写一个Android应用程序,模仿我编写的iOS应用程序中已存在的功能.我正在与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];
}
}
Run Code Online (Sandbox Code Playgroud)
而已.iOS中此方法的注释说明如下:
如果指定的特性配置为允许通知和指示,则调用此方法仅启用通知.
基于此(以及它在iOS中工作的事实),我正在计算我想要通知的特性的配置描述符应该像这样配置:
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
gatt.writeDescriptor(descriptor);
Run Code Online (Sandbox Code Playgroud)
考虑到这一点,我的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);
}
}
Run Code Online (Sandbox Code Playgroud)
注意:知道这两个设备以下列方式运行可能很重要:
- 打开BLE设备,在设备上启动测量.
- 一旦进行了测量,BLE设备就会尝试启动BLE连接.
- 一旦建立了BLE连接,设备就会立即发送数据,有时会发送一些数据包.(如果以前的数据测量没有通过BLE成功发送,它会将它们保存在内存中并发送所有数据,所以我只关心最终的数据包.)
- 一旦发送了最终数据包,BLE设备就会快速断开连接.
- 如果BLE设备无法发送数据(如当前在Android应用程序上发生的那样),则BLE设备会非常快速地断开连接.
在我的LogCat中,我看到很多输出与我期望的完全一样.
我遇到的最近一次失败是报告"128"的状态onCharacteristicWrite.问题#3(下面)的评论似乎表明这是一个资源问题.
我看了下面的问题:
这就是为什么他们不给我我需要的东西:
我尝试过一些示例和教程,包括谷歌的Android示例代码.他们似乎都没有让BLE设备通知我的Android设备数据更新.它显然不是设备,因为iOS版本有效.那么,iOS代码在后台做什么以使通知正常工作以及Android端的哪些代码将模仿该功能?
基于@ 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);
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这似乎确实改变了一些事情.以下是代码更改后的当前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
Run Code Online (Sandbox Code Playgroud)
所以,看起来我现在正确地设置一切(因为setCharacteristicNotification返回true和onDescriptorWrite状态是0).但是,onCharacteristicChanged仍然永远不会开火.
小智 8
我已经能够通过以下方式成功捕获具有多种服务和特征的onCharacteristicChanged():
在服务发现完成后,在主循环中的broadcastReceiver()中写入描述符值.
private final BroadcastReceiver UARTStatusChangeReceiver = new BroadcastReceiver() {
//more code...
if (action.equals(uartservice.ACTION_GATT_SERVICES_DISCOVERED)) {
mService.enableTXNotification();
}
Run Code Online (Sandbox Code Playgroud)和
通过在描述符值设置之间添加延迟
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;
}
}
Run Code Online (Sandbox Code Playgroud)| 归档时间: |
|
| 查看次数: |
8148 次 |
| 最近记录: |