Android BLE从不接收来自特征的通知



我已经挣扎了很长一段时间,试图让我的BLE设备与我的android应用程序通信。

首先,这是我处理BLE的完整代码:

BleCentral.java

import java.util.HashMap;
import java.util.function.Supplier;
import android.util.Log;
import android.content.Context;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothManager;
public class BleCentral
{
private HashMap<String, BluetoothGatt> m_connectedDevices;
public BleCentral()
{
m_connectedDevices = new HashMap<>();
}
private BluetoothAdapter m_GetAdapter(Context ctx)
{
final BluetoothManager bleMgr = (BluetoothManager)(ctx.getSystemService(Context.BLUETOOTH_SERVICE));
BluetoothAdapter adapter = bleMgr.getAdapter();
if (adapter == null || !adapter.isEnabled())
{
Log.e("BLE Central", "BLE either not available or not enabled. Please do something about it.");
return null;
}
return adapter;
}
public BluetoothDevice GetDevice(Context ctx, String address)
{
return m_GetAdapter(ctx).getRemoteDevice(address);
}
public <T extends BlePeripheral>
T Connect(Context ctx, String address, Supplier<T> supplier)
{
BluetoothDevice device = GetDevice(ctx, address);
T result = supplier.get();
m_connectedDevices.put(address, device.connectGatt(ctx, false, result, BluetoothDevice.TRANSPORT_LE));
return result;
}
}

BlePeripheral.java

import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.UUID;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattService;

public abstract class BlePeripheral
extends BluetoothGattCallback
{
private final String kCCC_DESCRIPTOR_UUID = "00002902-0000-1000-8000-00805f9b34fb";
private Handler       m_handler;
private boolean       m_deviceReady;
private BluetoothGatt m_bluetoothGatt;
private Queue<CmdQueueItem> m_cmdQueue;
private boolean             m_cmdQueueProcessing;
// ------------------------------------------------------------------------
// -- Own methods
protected BlePeripheral()
{
m_handler       = new Handler();
m_deviceReady   = false;
m_bluetoothGatt = null;
m_cmdQueue = new LinkedList<>();
m_cmdQueueProcessing = false;
}
public boolean IsDeviceReady()
{ return m_deviceReady; }
public void EnableNotifications(UUID service, UUID characteristic)
{  EnqueueSetNotificationForCharacteristic(m_bluetoothGatt.getService(service).getCharacteristic(characteristic), true); }
public void DisableNotifications(UUID service, UUID characteristic)
{  EnqueueSetNotificationForCharacteristic(m_bluetoothGatt.getService(service).getCharacteristic(characteristic), false); }
protected void WriteCharacteristic(UUID service, UUID characteristic, byte[] value, boolean requestResponse)
{ EnqueueWriteCharacteristic(m_bluetoothGatt.getService(service).getCharacteristic(characteristic), value, requestResponse); }
protected void ReadCharacteristic(UUID service, UUID characteristic)
{ EnqueueReadCharacteristic(m_bluetoothGatt.getService(service).getCharacteristic(characteristic)); }
// ------------------------------------------------------------------------
// -- BluetoothGattCallback overrides
@Override
public void onConnectionStateChange(final BluetoothGatt gatt, int status, int newState)
{
super.onConnectionStateChange(gatt, status, newState);
final BluetoothDevice device = gatt.getDevice();
switch (status)
{
case 133: /* GATT_ERROR */
Log.e("BLE", "GATT_ERROR");
gatt.close();
try   { Thread.sleep(150); }
catch (InterruptedException e) { e.printStackTrace(); }
break;
case 0: /* GATT_SUCCESS */
switch (newState)
{
case BluetoothGatt.STATE_CONNECTED:
Log.i("BLE", "Connected to " + device.getAddress() + " (" + device.getName() + ")");
m_bluetoothGatt     = gatt;
int delayWhenBonded = (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) ? 2000 : 0;
switch (device.getBondState())
{
case BluetoothDevice.BOND_NONE:
delayWhenBonded = 0;
case BluetoothDevice.BOND_BONDED:
m_handler.postDelayed(new Runnable() {
@Override
public void run() {
boolean result = gatt.discoverServices();
if (!result)
Log.e("BLE", "discoverServices() failed to start");
}
}, delayWhenBonded);
break;
case BluetoothDevice.BOND_BONDING:
Log.i("BLE", "Waiting for bonding to complete");
break;
}
break;
case BluetoothGatt.STATE_DISCONNECTED:
gatt.close();
break;
}
break;
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status)
{
super.onServicesDiscovered(gatt, status);
if (status == 129 /* GATT_INTERNAL_ERROR */)
{
Log.e("BLE", "Service discovery failed");
gatt.disconnect();
return;
}
final List<BluetoothGattService> services = gatt.getServices();
Log.i("BLE", "Discovered " + services.size() + " services for " + gatt.getDevice().getAddress());
m_deviceReady   = SetupDevice(gatt);
if (!m_deviceReady)
Log.e("BLE", "Peripheral does not comply to this device's requirements");
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic)
{
super.onCharacteristicChanged(gatt, characteristic);
Log.i("BLE", "onCharacteristicChanged: " + characteristic.getUuid());
final byte[] value = new byte[characteristic.getValue().length];
System.arraycopy(characteristic.getValue(), 0, value, 0, characteristic.getValue().length);
OnUpdate(characteristic.getService().getUuid(), characteristic.getUuid(), value);
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, int status)
{
super.onCharacteristicRead(gatt, characteristic, status);
ProcessCmdQueue();
Log.i("BLE", "onCharacteristicRead: " + characteristic.getUuid());
final byte[] value = new byte[characteristic.getValue().length];
System.arraycopy(characteristic.getValue(), 0, value, 0, characteristic.getValue().length);
OnUpdate(characteristic.getService().getUuid(), characteristic.getUuid(), value);
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, int status)
{
super.onCharacteristicWrite(gatt, characteristic, status);
ProcessCmdQueue();
Log.i("BLE", "onCharacteristicWrite: " + characteristic.getUuid());
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, final BluetoothGattDescriptor descriptor, final int status)
{
super.onDescriptorWrite(gatt, descriptor, status);
ProcessCmdQueue();
final BluetoothGattCharacteristic parentCharacteristic = descriptor.getCharacteristic();
if(status != 0 /* GATT_SUCCESS */)
{
Log.e("BLE", "WriteDescriptor failed for characteristic " + parentCharacteristic.getUuid());
return;
}
if(descriptor.getUuid().equals(UUID.fromString(kCCC_DESCRIPTOR_UUID)))
{
if(status == 0 /* GATT_SUCCESS */)
{
byte[] value = descriptor.getValue();
if (value != null)
{
if (value[0] != 0)
Log.i("BLE", "Characteristic " + parentCharacteristic.getUuid() + " is now notifying");
else
Log.i("BLE", "Characteristic " + parentCharacteristic.getUuid() + " is now NOT notifying");
}
}
}
}
// ------------------------------------------------------------------------
// -- Command Queue implementation
/* An enqueueable write operation - notification subscription or characteristic write */
private class CmdQueueItem
{
BluetoothGattCharacteristic characteristic;
byte[] dataToWrite; // Only used for characteristic write
boolean writeWoRsp; // Only used for characteristic write
boolean enabled;    // Only used for characteristic notification subscription
public m_queueItemType type;
}
private enum m_queueItemType
{
SubscribeCharacteristic,
ReadCharacteristic,
WriteCharacteristic
}
/* queues enables/disables notification for characteristic */
public void EnqueueSetNotificationForCharacteristic(BluetoothGattCharacteristic ch, boolean enabled)
{
// Add to queue because shitty Android GATT stuff is only synchronous
CmdQueueItem m_queueItem    = new CmdQueueItem();
m_queueItem.characteristic = ch;
m_queueItem.enabled        = enabled;
m_queueItem.type           = m_queueItemType.SubscribeCharacteristic;
EnqueueBleCommand(m_queueItem);
}
/* queues enables/disables notification for characteristic */
public void EnqueueWriteCharacteristic(final BluetoothGattCharacteristic ch, final byte[] dataToWrite, boolean requestResponse)
{
// Add to queue because shitty Android GATT stuff is only synchronous
CmdQueueItem m_queueItem    = new CmdQueueItem();
m_queueItem.characteristic = ch;
m_queueItem.dataToWrite    = dataToWrite;
m_queueItem.writeWoRsp     = !requestResponse;
m_queueItem.type           = m_queueItemType.WriteCharacteristic;
EnqueueBleCommand(m_queueItem);
}
/* request to fetch newest value stored on the remote device for particular characteristic */
public void EnqueueReadCharacteristic(BluetoothGattCharacteristic ch)
{
// Add to queue because shitty Android GATT stuff is only synchronous
CmdQueueItem m_queueItem = new CmdQueueItem();
m_queueItem.characteristic = ch;
m_queueItem.type = m_queueItemType.ReadCharacteristic;
EnqueueBleCommand(m_queueItem);
}
/**
* Add a transaction item to transaction queue
* @param m_queueItem
*/
private void EnqueueBleCommand(CmdQueueItem m_queueItem)
{
m_cmdQueue.add(m_queueItem);
// If there is no other transmission processing, go do this one!
if (!m_cmdQueueProcessing)
ProcessCmdQueue();
}
/**
* Call when a transaction has been completed.
* Will process next transaction if queued
*/
private void ProcessCmdQueue()
{
if (m_cmdQueue.size() <= 0)
{
m_cmdQueueProcessing = false;
return;
}
m_cmdQueueProcessing = true;
CmdQueueItem m_queueItem = m_cmdQueue.remove();
switch (m_queueItem.type)
{
case WriteCharacteristic:
writeDataToCharacteristic(m_queueItem.characteristic, m_queueItem.dataToWrite, m_queueItem.writeWoRsp);
break;
case SubscribeCharacteristic:
setNotificationForCharacteristic(m_queueItem.characteristic, m_queueItem.enabled);
break;
case ReadCharacteristic:
requestCharacteristicValue(m_queueItem.characteristic);
break;
}
}
public void requestCharacteristicValue(BluetoothGattCharacteristic ch)
{
if (m_bluetoothGatt == null)
return;
m_bluetoothGatt.readCharacteristic(ch);
}
private void writeDataToCharacteristic(final BluetoothGattCharacteristic ch, final byte[] dataToWrite, boolean writeWoRsp)
{
if (m_bluetoothGatt == null || ch == null)
return;
ch.setValue(dataToWrite);
ch.setWriteType(writeWoRsp ? BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE : BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
m_bluetoothGatt.writeCharacteristic(ch);
}
/* enables/disables notification for characteristic */
private void setNotificationForCharacteristic(BluetoothGattCharacteristic ch, boolean enabled)
{
if (m_bluetoothGatt == null || ch == null)
return;
ch.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
boolean success = m_bluetoothGatt.setCharacteristicNotification(ch, enabled);
if(success)
{
// This is also sometimes required (e.g. for heart rate monitors) to enable notifications/indications
// see: https://developer.bluetooth.org/gatt/descriptors/Pages/DescriptorViewer.aspx?u=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
BluetoothGattDescriptor descriptor = ch.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if(descriptor != null)
{
if (enabled)
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
else
descriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
if (m_bluetoothGatt.writeDescriptor(descriptor))
{
Log.i("BLE Peripheral", "SetNotification (Set + CCC) succeeded!");
}
}
else
Log.i("BLE Peripheral", "SetNotification (Set only) succeeded!");
}
else
Log.e("BLE Peripheral", "SetNotification failed!");
}
// ------------------------------------------------------------------------
// -- Abstract methods
protected abstract boolean SetupDevice(BluetoothGatt gatt);
protected abstract void    OnUpdate(UUID service, UUID characteristic, final byte[] value);
}

创建并连接到设备的代码

BleCentral central = new BleCentral();
m_customDevice     = central.Connect(this, deviceMacAddress, () -> new CustomDevice());

CustomDevice只是继承了BlePeripheral类,实现了SetupDevice(检查所有服务和特性是否存在(和OnUpdate(接收并处理新数据(。

现在,有两件事困扰着我:

  • 连接到设备时,有时它会立即工作,有时则不会。如果没有,我必须通过另一个应用程序(如Bluefruit connect(连接到设备,然后再次启动我的应用程序,然后它就会连接;

  • 当它连接时,它会进行服务发现和所有操作,在setNotificationForCharacteristic函数中,所有操作都会被正确调用(我调用了onDescriptorWrite和所有操作(,但我从未收到任何通知。

由于我是BLE外围设备上运行代码的幕后推手,我可以保证我试图从中获取数据的特性是NOTIFY类型(而不是例如INDEITE(。

如果它能以任何方式提供帮助,那么唯一的NOTIFY特性会尽可能频繁地发送一个56字节的数组,其中填充了14个浮点值。Web蓝牙或NativeScript(带有NativeScript蓝牙插件(的早期原型向我展示了这一点,在这些情况下,我大约每90毫秒就会得到一次结果。

我想我已经重写了这个代码大约3次了,我有点绝望了,所以任何朝着正确方向前进的帮助都是值得感激的。:D

非常感谢!

编辑:为了科学起见,我试图在设备上将该特性切换为READ特性,然后每秒生成一个读取该特性的线程,而不是等待通知。调用了onCharacteristicRead,但传递给它的字节数组的长度总是零。。。

我发现了这个问题-它根本与Java代码无关,而是由BLE设备中的硬件故障引起的,该故障导致所有值更新都被丢弃。

最新更新