How to Use New Bluetooth LE APIs

Overview

ESF implements a new set of APIs for managing Bluetooth Low Energy and Beacon devices. The new APIs replace the existing Bluetooth APIs, but the old ones are still available and can be used.

The purpose of the new BLE APIs is to simplify the development of applications that interact with Bluetooth LE devices, offering clear and easy-to-use methods, and add new features to correctly manage the connection with remote devices.
Moreover, the APIs organize the methods in a logical way to access all levels of a GATT client, from GATT services to GATT characteristics and descriptors, using UUIDs to identify the correct resource.

🚧

The support for this set of APIs is available in the ReliaGATE 20-25 since Eurotech Linux Version 22.0.0.
The support is also available for the BoltGATE 20-25.

Bluez-Dbus - BLE GATT API

The implementation of the new ESF BLE APIs is based on the Bluez-Dbus library that provides an easy to use Bluetooth LE API based on BlueZ over DBus. The library eases the access to GATT services and the management of BLE connections and discovery, without using any wrapper library as it is based on a newer version of dbus-java which uses jnr-unixsocket.

APIs Description

The new BLE APIs are exported in the org.eclipse.kura.bluetooth.le package. Following, The interfaces provided:

  • BluetoothLeService is the entry point of the OSGi service. It provides to the caller all the Bluetooth interfaces installed on the gateway or a specific one using the name of the adapter.
  • BluetoothLeAdapter represents the physical Bluetooth adapter on the gateway. It allows to start/stop a discovery, search a specific BLE device based on the BD address, power up/down the adapter and get information about the adapter.
  • BluetoothLeDevice represents a Bluetooth LE device. The interface provides methods for connections and disconnections, list the GATT services or search a specific one based on the UUID and get general information about the device.
  • BluetoothLeGattService represents a GATT service and it allows to list all the GATT characteristics provided by the device.
  • BluetoothLeGattCharacteristic represents a GATT characteristic. It provides methods to read from and write to a characteristic, enable or disable notifications and get properties.
  • BluetoothLeGattDescriptor represents a GATT descriptor associated with a characteristic.

More information about the APIs can be found in API Reference.

How to use the New BLE APIs

This section briefly presents how to use the ESF BLE APIs, providing several code snippets that explain how to perform common Bluetooth operations. For a complete example, please refer to the new SensorTag application.

An application that wants to use the ESF BLE APIs should bind the BluetoothLeService OSGi​ service, as shown in the following Java snippet:

public void setBluetoothLeService(BluetoothLeService bluetoothLeService) {
    this.bluetoothLeService = bluetoothLeService;
}

public void unsetBluetoothLeService(BluetoothLeService bluetoothLeService) {
    this.bluetoothLeService = null;
}

and in the component definition:

<reference bind="setBluetoothLeService" 
            cardinality="1..1" 
            interface="org.eclipse.kura.bluetooth.le.BluetoothLeService" 
            name="BluetoothLeService" 
            policy="static" 
            unbind="unsetBluetoothLeService"/>

Get the Bluetooth Adapter

Once performed the binding with the BluetoothLeService, an application can get the Bluetooth adapter and power it, if needed:

this.bluetoothLeAdapter = this.bluetoothLeService.getAdapter(adapterName);
if (this.bluetoothLeAdapter != null) {
    if (!this.bluetoothLeAdapter.isPowered()) {
        this.bluetoothLeAdapter.setPowered(true);
    }
}

where adapterName is the name of the adapter, i.e. hci0.

Search BLE Devices

The BluetoothLeAdapter provides several methods to search for a device, a.k.a. perform a BLE discovery:

  • Future<BluetoothLeDevice> findDeviceByAddress(long timeout, String address) searches for a BLE device with the specified address. The method will perform a BLE discovery for at most timeout seconds or until the device is found. It will return a Future instance and the discovered device can be retrieved using the get() method.
  • Future<BluetoothLeDevice> findDeviceByName(long timeout, String name) searches for a BLE device with the specified system name and returns a Future.
  • void findDeviceByAddress(long timeout, String address, Consumer<BluetoothLeDevice> consumer) searches for a BLE device with the specified address. The method will perform a BLE discovery for at most timeout seconds or until the specified device is found.
  • void findDeviceByAddress(long timeout, String address, Consumer<BluetoothLeDevice> consumer) searches for a BLE device with the specified name and returns the device to the provided consumer.
  • Future<List<BluetoothLeDevice>> findDevices(long timeout) and void findDevices(long timeout, Consumer<List<BluetoothLeDevice>> consumer) are similar to the methods above but they return or get, as parameter, a list of Bluetooth devices.

The following snippet shows how to perform a discovery of 10 seconds using findDevices method:

if (this.bluetoothLeAdapter.isDiscovering()) {
    try {
        this.bluetoothLeAdapter.stopDiscovery();
    } catch (KuraException e) {
        logger.error("Failed to stop discovery", e);
    }
}
Future<List<BluetoothLeDevice>> future = this.bluetoothLeAdapter.findDevices(10);
try {
    List<BluetoothLeDevice> devices = future.get();
} catch (InterruptedException | ExecutionException e) {
    logger.error("Scan for devices failed", e);
}

Get the GATT Services and Characteristics

To get the GATT services using the BluetoothLeDevice, use the following snippet:

try {
    List<BluetoothLeGattService> services = device.findServices();
} catch (KuraBluetoothResourceNotFoundException e) {
    logger.error("Unable to find GATT services", e);
}

A specific GATT service can be retrieved using its UUID:

try {
    BluetoothLeGattService service = device.findService(uuid);
} catch (KuraBluetoothResourceNotFoundException e) {
    logger.error("Unable to find GATT service", e);
}

Using the GATT service, it is possible to get a specific GATT characteristic (or the complete list) and the GATT descriptor from it:

try {
    BluetoothLeGattCharacteristic characteristic = service.findCharacteristic(characteristicUuid);
    BluetoothLeGattDescriptor descriptor = characteristic.findDescriptor(descriptorUuid);
} catch (KuraBluetoothResourceNotFoundException e) {
    logger.error("Unable to find GATT resources", e);
}

IO Operations on GATT Characteristics and Descriptors

The ESF BLE APIs provides methods to manage the IO operations on GATT characteristics and descriptors. The following snippet provides an example of​ how to read and write data to a characteristic.

try {
    byte[] valueRead = characteristic.readValue();
    byte[] valueWrite = { 0x01};
    characteristic.writeValue(valueWrite);
} catch (KuraBluetoothIOException e) {
    logger.error("IO operation failed", e);
}

In the following example, instead, a notification listener is configured to periodically receive the data from a GATT characteristic​ and print the first value of the given array. The period is internally set by the BLE device.

try {
    Consumer<byte[]> callback = valueBytes -> System.out.println((int) valueBytes[0]);
    characteristic.enableValueNotifications(callback);
} catch (KuraBluetoothNotificationException e) {
    logger.error();
}

Configure Bluez and TinyB on the Raspberry Pi

ESF provides the TinyB library already compiled for the Raspberry Pi (Raspbian). However, TinyB uses Bluez through DBUS under the hood and the latter library should be installed on the system. Moreover, the minimum supported version of Bluez is 5.42.

The Raspbian Stretch OS comes with Bluez 5.43, but older OS couldn’t have an updated Bluez version. In this case, it is possible to compile and install Bluez from sources using a Raspberry Pi. The Bluez sources can be found here. Proceed as follows:

  1. Install the packages needed for compile Bluez:
sudo apt-get install libusb-dev libdbus-1-dev libglib2.0-dev libudev-dev libical-dev libreadline-dev
  1. Download bluez-5.43.tar.xz (or newer version) from here

  2. Decompress the compressed archive:

tar -xf bluez-5.43.tar.x
  1. Compile the sources:
cd bluez-5.43
./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --enable-library -disable-systemd --enable-experimental --enable-maintainer-mod
make
make install