1

I have already implemented Bluetooth low energy device scanning logic, but I'm struggling a little bit with BluetoothGatt service usage. What I want to do is to connect to each of my ESP32 devices sequentially from my Android phone, so that I can receive the data and then continue to the next device (and disconnect from previous). So there will be only one active connection between ESP32 and Android phone. The ESP32 is already programmed so if android phone connects to it using BluetoothGatt then it sends data (max 20 bytes).

The real struggle is to understand how to manage these connections to properly close/release the resources.

What would be the correct/simplest way to manage these BluetoothGatt connections?

My implementation basically connects to a new device Gatt service when the sent data is "end". The problem is that this works if one ESP32 device is active. If more devices are active then something happens and the data is not received from Gatt service.

Here is part of my implementation (sorry, I couldn't reduce the code size more):

1) I use BLE scanner to discover new devices

private Handler _leScanHandler = new Handler();
private final BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
    @Override
    public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
        final int newRSSI = rssi;
        _leScanHandler.post(new Runnable() {
            @Override
            public void run() {
                addDeviceToListOnLEScan(device, newRSSI);
            }
        });
    }
};

2) And the called method basically manages the BluetoothGatt conenctions..

public void addDeviceToListOnLEScan(BluetoothDevice device, int rssi) {

    // Gets only user defined devices
    boolean isUserDefinedDevice = _definedDevicesHashMap.containsKey(device.getAddress());
    if (!isUserDefinedDevice) {
        return;
    }

    // Adds device and updates 'LastModifiedTime = Date(System.currentTimeMillis())'
    addOrUpdateDevice(device.getAddress(), _scannedDevices);

    // Called only once on each connect button press to enable gatt operations
    if (!_isInitialConnectionHasBeenMade) {
        _isDataSendingCompleteFromCurrentGatt = true;
        _isInitialConnectionHasBeenMade = true;
    }

    // Sequential BLE device connect/disconnect operations
    if (_isDataSendingCompleteFromCurrentGatt) {

        BluetoothGatt previousGatt = _definedDevicesHashMap(previousAddress);
        if (previousGatt != null) {
            previousGatt.disconnect(); // ?
        }

        BluetoothGatt nextGatt = _definedDevicesHashMap(nextAddress);
        if (/* Checks if 'nextAddress' is in _scannedDevices */
            /* And whether device 'IsActive()' */) {

            if (nextGatt == null) {
                nextGatt = connectToDeviceGattService(nextGatt)     
            }
            else {
                // Do something here ?
            }
            _isDataSendingCompleteFromCurrentGatt = false;  
        }
    }
}

3) The following variables/classes that I'm using

private boolean _isDataSendingCompleteFromCurrentGatt = false;
private boolean _isInitialConnectionHasBeenMade = false;
private HashMap<String, BluetoothGatt> _definedDevicesHashMap;
_definedDevicesHashMap.put("ff:ff:9f:c8:c2:93", null);
_definedDevicesHashMap.put("ff:ff:9f:c8:c4:91", null);
...

private HashMap<String, MyBLEDevice> _scannedDevices;

public class MyBLEDevice
{
    private final int deviceInactivityTimeout = 10;
    private String MacAddress;
    private Date _lastModifiedDate;
    public boolean isDeviceActive() {
        // Just gets the time difference (DateNow - lastModified) / 1000 < 10s
    }
}

4) Method which I use to connect to device

public BluetoothGatt connectToDeviceGattService(BluetoothGatt currentGatt, BluetoothDevice device, BluetoothGattCallback callback) {
    _bluetoothAdapter.cancelDiscovery();
    if (currentGatt == null) {
        currentGatt = device.connectGatt(_activity, true, callback);
    }
    else {
        // Is anything here needed ?
    }

    return currentGatt;
}

5) The BluetoothGatt callback

private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) {

            // Discover services ?

        } else if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_DISCONNECTED) {

            // Do nothing ?

        } else if (status != BluetoothGatt.GATT_SUCCESS) {

            // Disconnect from current BluetoothGatt instance? 
            // Also close the connection ?

            _isDataSendingCompleteFromCurrentGatt = true;
        }
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        enableGattConfigurationCharacteristic();
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
        _gattServiceHandler.post(new Runnable() {
            @Override
            public void run() {
                handleMessage(Message.obtain(null, MSG_CHARACTERISTIC_CHANGE, characteristic));
            }
        });
    }
};

6) And finally, the handler which is used to receive data from Gatt callback

private static final int MSG_CHARACTERISTIC_CHANGE = 0;
private Handler _gattServiceHandler = new Handler();
private void handleMessage(Message msg) {
    BluetoothGattCharacteristic characteristic;
    switch (msg.what) {
        case MSG_CHARACTERISTIC_CHANGE: {
            characteristic = (BluetoothGattCharacteristic) msg.obj;
            if (BLELogic.PROPERTY_NOTIFY_CHAR_UUID.equals(characteristic.getUuid())) {

                String notification = BluetoothExtension.getCharacteristicValue(characteristic);
                if (notification.equals("end")) {
                    _isDataSendingCompleteFromCurrentGatt = true;           
                } else {
                    UpdateViewOnGattNotificationSent(notification);
                }
            }
        }
        default:
            return;
    }
}

At the beginning I wanted to simplify all the logic, but it looks like when using BLE/Gatt service connections, there is nothing simple about this.

halfer
  • 19,824
  • 17
  • 99
  • 186
Mr. Blond
  • 1,113
  • 2
  • 18
  • 41
  • If I am understanding your question correctly, you want to maintain only one BLE connection even though there are multiple devices advertising. After one device gets disconnected you want to connect to another device and have only one active connection. is that correct? – Avinash4551 May 28 '19 at 19:25
  • Yes, that was my goal, because I checked that the code becomes much more complex when many connections are active at the same time. – Mr. Blond May 28 '19 at 19:35
  • Stop scanning before calling connectToDeviceGattService() and enable scanning only after the device disconnects. if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_DISCONNECTED) { startscanning() } else if (status != BluetoothGatt.GATT_SUCCESS) { startscanning() } – Avinash4551 May 28 '19 at 19:57
  • Your setup should work fine so there must be some bugs somewhere. "If more devices are active then something happens and the data is not received from Gatt service.." Can you elaborate on that "something"? Which steps are working and exactly where does it fail? You could add more logs and see how far it comes. How does your enableGattConfigurationCharacteristic look like? – Emil May 28 '19 at 20:33
  • Well, it kinda didn't work. So, I guess something else is not working. I was adding logs everywhere, but still, it is hard to understand where is the problem. I am a bit disappointed in the `BluetoothAdapter` and `BluetoothGatt` libraries. They are just created so hard to use and understand.. Also, this is not urgent anymore as I found other way to send data from ESP32 to Android using locally hosted web server, where data is sent via WiFi.. – Mr. Blond May 29 '19 at 08:46

1 Answers1

0

Here is a Question and answer that might interest you

Short from the answer:

To achieve multiple BLE connection you have to store multiple BluetoothGatt object and use those object for different deveice. To store multiple connection object of BluetoothGatt you can use Map<>

private Map connectedDeviceMap;.............

Erik
  • 5,039
  • 10
  • 63
  • 119
  • Thanks for the reply. Yes, I already saw that post. Although, as I understood, they tried to connect to multiple devices at the same time. – Mr. Blond May 28 '19 at 18:35
  • Are you using the same `BluetoothGattCallback` for all devices? Every `BluetoothGatt` really must have it´s own little world where it can live and die, that´s my experience, dunno.. – Erik May 28 '19 at 18:41
  • Yes, I'm using only one callback for all devices. Should they conflict or somehow impact how the program works if there is only one active connection? – Mr. Blond May 28 '19 at 18:43
  • I created [this Multiple-Ble-sample_app-LiveData-PagerAdapter](https://github.com/erikswed/Multiple-Ble-sample_app-LiveData-PagerAdapter) github projects showing just that, maybe it helps – Erik May 28 '19 at 18:45
  • To use separate callbacks I haven't tried yet, will check it out, thanks. – Mr. Blond May 28 '19 at 18:50
  • I understand you want to do it sequentially but check my GitHub out if you want, how to separate logic – Erik May 28 '19 at 18:51