安卓BLE从未收到来自特性的通知

问题描述 投票:0回答:1

我已经挣扎了好一阵子,试图让我的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类型(而不是例如INDICATE)。

如果能帮上忙的话,唯一的NOTIFY特性是尽可能频繁地发送一个56字节的数组,里面充满了14个浮点数。早期使用Web蓝牙或NativeScript(使用nativescript-bluetooth插件)的原型向我展示了这实际上是可行的,在这些情况下,我大约每90毫秒就能得到结果。

我想我已经把这段代码重写了3次,我有点绝望了,所以任何在正确方向上的帮助都是感激的。

非常感谢!

编辑:我想我已经把这段代码重写了3次了,所以非常感谢你的帮助。: 为了科学起见,我试着把设备上的特性转换为READ特性,然后每隔一秒生成一个线程读取它,而不是等待通知。好吧,onCharacteristicRead被调用了,但是传递给它的字节数组总是长度为0......

android bluetooth-lowenergy android-bluetooth gatt bluetooth-gatt
1个回答
0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.