मैं काफी समय से संघर्ष कर रहा हूं, अपने बीएलई डिवाइस को अपने एंड्रॉइड ऐप के साथ संवाद करने की कोशिश कर रहा हूं।

सबसे पहले, बीएलई हैंडलिंग के लिए मेरा पूरा कोड यहां दिया गया है:

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 (जो नया डेटा प्राप्त करता है और इसे संभालता है)।

अब, दो चीजें मुझे परेशान करती हैं:

  • डिवाइस से कनेक्ट करते समय, कभी-कभी यह तुरंत काम करता है, और कभी-कभी नहीं। यदि नहीं, तो मुझे ब्लूफ्रूट कनेक्ट जैसे किसी अन्य ऐप के माध्यम से डिवाइस से कनेक्ट करना होगा, फिर मेरा ऐप फिर से शुरू करना होगा, और फिर यह कनेक्ट हो जाएगा;

  • जब यह कनेक्ट होता है, तो यह सेवा खोज और सभी के माध्यम से जाता है, और setNotificationForCharacteristic फ़ंक्शन में सब कुछ सही ढंग से कॉल किया जाता है (मुझे onDescriptorWrite कहा जाता है और सभी) लेकिन मुझे कभी कोई सूचना नहीं मिलती है।

चूंकि मैं अपने बीएलई परिधीय पर चल रहे कोड के पीछे हूं, इसलिए मैं गारंटी दे सकता हूं कि जिस विशेषता से मैं डेटा प्राप्त करने का प्रयास कर रहा हूं वह अधिसूचना प्रकार का है (और उदाहरण के लिए संकेत नहीं)।

यदि यह किसी भी तरह से मदद कर सकता है, तो केवल NOTIFY विशेषता 14-फ़्लोट्स से भरी 56-बाइट सरणी जितनी बार हो सके भेजती है। वेब ब्लूटूथ या नेटिवस्क्रिप्ट (नेटिवस्क्रिप्ट-ब्लूटूथ प्लगइन के साथ) के शुरुआती प्रोटोटाइप ने मुझे दिखाया कि यह वास्तव में काम करता है और इन मामलों में मुझे लगभग हर 90 मिलीसेकंड में परिणाम मिलते हैं।

मुझे लगता है कि मैंने इस कोड को लगभग 3 बार पहले ही लिखा है और मैं थोड़ा हताश हो रहा हूं, इसलिए सही दिशा में जाने वाली किसी भी मदद की सराहना की जाती है। :डी

बहुत - बहुत धन्यवाद!

संपादित करें: केवल विज्ञान के लिए, मैंने डिवाइस पर विशेषता को एक रीड में बदलने की कोशिश की, फिर सूचनाओं की प्रतीक्षा करने के बजाय, इसे हर सेकंड पढ़ने के लिए एक थ्रेड उत्पन्न किया। ठीक है, onCharacteristicRead कहा जाता है, लेकिन बाइट सरणी इसे पास करने के लिए हमेशा शून्य की लंबाई होती है ...

0
xtrium 22 अप्रैल 2020, 21:05

1 उत्तर

सबसे बढ़िया उत्तर

मुझे समस्या मिली - यह जावा कोड से बिल्कुल भी संबंधित नहीं था, लेकिन मेरे बीएलई डिवाइस में हार्डवेयर गलती के कारण हुआ जिससे सभी मूल्य अपडेट को छोड़ दिया गया।

0
xtrium 26 अप्रैल 2020, 22:17