Android Bluetooth Low Energy Getting response to specific request

12.1k views Asked by At

There's something I'm not quite getting with using Gatt to communicate with a BLE device. According to this: https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback)

BluetoothGatt gatt = device.connectGatt(context,true,new BluetoothGattCallback(){....})

I can connect to a BLE device and give it a callbacks object to be notified on stuff like onCharacteristicRead and onCharacteristicWrite

What I'm not getting is how do which write corresponds to which read callback?

This method signatures are:

public void onCharacteristicRead (BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
public void onCharacteristicWrite (BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)

so if I do:

BluetoothGattCharacteristic char = gatt.getService(UART_SERVICE_UUID).getCharacteristic(UART_TX_CHARACTERISTIC_UUID);
char1.setValue("command1");
gatt.writeCharacteristic(char);
char1.setValue("command2");
gatt.writeCharacteristic(char);

in the onCharacteristicRead callback, how do I know if the characteristic.getStringValue() is for command1 or command2?

Thanks!

4

There are 4 answers

0
istirbu On

In this implementation you should wait for the onCharacteristicWrite after the first write before issuing the next write operation. But this holds if you did not change the write type with setWriteType (int writeType) from the default WRITE_TYPE_DEFAULT, which calls back on write operations. You might need to keep state for these ops.

Another option is to have a command code (e.g. 1 byte) in the response so you can map it to the command.

0
nickle_nine On
private String heartRate;

private String temperature;

...

char.setValue("command1");

gatt.writeCharacteristic(char);

...

Then in onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) check if the heartRate variable is null. If it is, get the value of the characteristic and assign it to the heartRate variable. Then do characteristic.setValue("command2");

gatt.writeCharacteristic(characteristic); and it will call onCharacteristicWrite(...) again.

If the heartRate variable is not null, get the value and assign it to the temperature variable. You can also use the UUID of the passed characteristic to differentiate between what the value is for.

Bonus: A single characteristic can contain multiple descriptors. You can have two descriptors with their own UUID for that characteristic. You can then call characteristic.getDescriptor(UUIDDescriptor).getValue().

0
Rob Gorman On

There are a couple of important things to know about when using BluetoothGatt.writeCharacteristic() and related BluetoothGatt methods.

  1. Please notice that writeCharacteristic() (and many other methods of BluetoothGatt) return a boolean. When the result is false, it means that the operation was not successfully initiated. When does that occur? See the next item.

  2. Using aBluetoothGatt object, it is not possible to initiate two operations at the same time. The call to initiate the second will fail and return false. After calling writeCharacteristic(), the code must wait for the callback response (onCharacteristicWrite) before issuing another write. In the sample code for this question the second call to writeCharacteristic() will almost certainly return false for this reason.

In this way, if each BluetoothGatt operation waits for a callback on the previously issued command, you can successfully pair command initiations and callbacks -- really you are forced to.

0
Romain DEQUIDT On

As I've already explained here, I'll give the answer I'd like to have when I've started "playing" with GATT BLE.

In order to create kind of an API (request with parameter(s) => response, as it's sais in the title of this question), follow these steps:

  • create a GATT service with:
    • a Write characteristic (unlike read characteristic, write one receives data) to manage request (with parameter(s) through the data payload)
    • a Read characteristic to manage reponse as notification
    • a Client Characteristic Configuration Descriptor for the Read characteristic to enable notification (standard 16-bit CCCD UUID: 0x2902).
  • Client write Client Characteristic Configuration Descriptor to enable notification/indication (once after ble connection)
  • Client write request Characteristic (request)
  • GATT server send response "status" OK for write Characteristic (request)
  • GATT server send indication (doesn't wait for acknowledge from client) with "data" from read Characteristic

Concretly, how to do that with FreeRTOS?

Create your GATT service as follow:

static const BTAttribute_t pxAttributeTable[] =
{
    {
        .xServiceUUID =
        {
            .uu.uu128 = GATT_SERVICE_UUID,
            .ucType = eBTuuidType128

        }
    },
    {
        .xAttributeType = eBTDbCharacteristic,
         .xCharacteristic =
        {
            .xUuid =
            {
                .uu.uu128 = GATT_CHARACTERISTIC_REQUEST_UUID,
                .ucType = eBTuuidType128
            },
            .xPermissions = (IOT_BLE_CHAR_WRITE_PERM),
            .xProperties = (eBTPropWrite)
        }
    },
    {
        .xAttributeType = eBTDbCharacteristic,
        .xCharacteristic =
        {
            .xUuid = 
            {
                .uu.uu128 = GATT_CHARACTERISTIC_RESPONSE_UUID,
                .ucType = eBTuuidType128
            },
            .xPermissions = (IOT_BLE_CHAR_READ_PERM),
            .xProperties = (eBTPropRead | eBTPropNotify)
        }
    },
    {
        .xAttributeType = eBTDbDescriptor,
        .xCharacteristicDescr =
        {
            .xUuid = 
            {
                .uu.uu16 = 0x2902,
                .ucType = eBTuuidType16
            },
            .xPermissions = (IOT_BLE_CHAR_READ_PERM | IOT_BLE_CHAR_WRITE_PERM)
        }
     }
};