4

Background:

I have a BLE peripheral with two modes: "Application" and "Bootloader". In both modes, the device advertises with the same MAC address.

To switch from one mode to the other, the BLE peripheral must reboot itself. In doing so, it has to disconnect any active BLE connection.

The BLE peripheral only stays in Bootloader mode for about 5 seconds. If nobody connects to it within that window, it switches to Application mode.

The Problem:

Android takes a very long time to reconnect to the BLE device, long enough that I'm missing the 5 second window. The raw code has a few layers down to the BluetoothGATT and BluetoothAdapter layers, but the sequence of calls boils down to:

BluetoothGattCharacteristic c = mCharacteristics.get(POWER_STATE_UUID);
c.setValue(SHUTDOWN_VALUE);
mBluetoothGatt.writeCharacteristic(c);
// Signalled by BluetoothGattCallback.onCharacteristicWrite
bleWriteCondition.await();

mBluetoothGatt.disconnect();
// Wait for the underlying layer to confirm we're disconnected
while( mConnectionState != BluetoothProfile.STATE_DISCONNECTED ) {
    // Signalled by BluetoothGattCallback.onConnectionStateChange
    bleStateCondition.await(); 
}
mBluetoothGatt.connect();
while (mConnectionState != BluetoothProfile.STATE_CONNECTED) {
    // Signalled by BluetoothGattCallback.onConnectionStateChange
    bleStateCondition.await();
    if (bleStateCondition.stat != 0) {
        break;
    }
}

Am I going about this entirely the wrong way? I've tried calling close() on the BluetoothGatt instance, then generating a new one with BluetoothDevice.connectGatt, but I get the same extremely slow behavior.

I'm testing on a Samsung Galaxy S4, API level 21.

James Whong
  • 231
  • 5
  • 5
  • How often does the device advertise? – 323go Nov 20 '15 at 20:49
  • In bootloader mode, once every 100ms. In application mode, once per second. – James Whong Nov 20 '15 at 21:00
  • did you solve this problem? I'm seeing that after losing connection to a ble device, it takes exactly 20 seconds to reconnect to the same peripheral, even if you manually call disconnect(), close(), connect(), regardless of the using direct or background – Mark Ch Jul 25 '16 at 11:31
  • 1
    I managed a work-around that works for some versions of Android. I installed a delay in the reboot command of the peripheral, so that the Android device could initiate the disconnect. So the peripheral receives the reboot command, but doesn't actually reboot until the Android side closes the connection. Android is then OK with re-connecting to the peripheral. Obviously this only works if you have access to the firmware of your peripheral, but I hope it helps. – James Whong Jul 26 '16 at 18:34
  • @James thanks that is an interesting way to do it. I've added in a 0.5 sec delay before peripheral reboot, and now the peripheral actually has time to disconnect properly before the power is cycled. At some lower layer the disconnect routine must include notifying the other end of the link, because now the Android device disconnects immediately and is able to reconnect quickly. Thanks – Mark Ch Sep 03 '16 at 19:15

1 Answers1

3

The problem here is that the gatt connect call issues a background connection request. It can take quite a long time for this call to result in a connection. A description of the two types of connection request is here : Direct vs Background connections

The absolute fastest way to get a connection is to do a scan and upon finding your device issue a direct connection request to it. As the scan has just found it, you know it is there and the connection will complete quickly. This is more complicated than your example code, but will be most effective given your small window. A scan is the most aggressive way to find a device. However, if you already have the device object, you could just call a direct connection request on the device.

Scans are issued using code like this :

scanner = bluetoothAdapter.getBluetoothLeScanner();
settings = new ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
            .build();
filters = new ArrayList<ScanFilter>();
ScanFilter uuidFilter = new ScanFilter.Builder()
            .setServiceUuid(YOUR_SERVICE_UUID).build();
filters.add(uuidFilter);
scanner.startScan(filters, settings, myScanCallback);

Upon finding your device (using the scan callback), issue a direct connection request via this method call :

myGatt = myDevice.connectGatt(this, false, myGattCallback);

The key part being the parameter of false. The connection request will time out in around 30s if the device is not found.

Community
  • 1
  • 1
pyrrhoofcam
  • 233
  • 2
  • 10
  • did you test this piece of code with any of your BLE device ? Currently, I am facing connectivity issue while connecting from Android 5 – UVM Aug 19 '16 at 08:05
  • It is code taken from an app that is deployed in the field. This question and answer is specific to the time taken to get a connection, it isn't a full solution to connecting to BLE devices which is far more complicated than the above. – pyrrhoofcam Aug 19 '16 at 09:24