Get in touch
Thank you
We will get back to you as soon as possible
.pdf, .docx, .odt, .rtf, .txt, .pptx (max size 5 MB)

29.8.2014

6 min read

Bluetooth Low Energy for Android (Part 1)

Bluetooth low energy is a simple and powerful protocol for interaction between devices with a low rate of energy consumption. Android 4.3 introduces a built-in support for Bluetooth Low Energy, providing corresponding API. In this series of articles you will learn how to communicate with BLE devices, using Xamarin Monodroid platform. First article makes a quick overview of the BLE protocol and describes how to discover BLE devices and establish connection with them and what problems might occur. Introduction BLE is used for communication between devices. Each device, during communication, can have one of two major roles:

  • Central (GATT Client).
  • Peripheral (GATT Server)

It is similar to traditional client-server architecture, where peripheral has data, needed by other devices, and central makes requests for this data. Device that plays central role scans for peripheral devices, establishes connection, and interacts with peripheral services to obtain required data. Device playing peripheral role make itself visible for central by advertising. Peripheral advertises packets of data, containing information that may be useful for central. This is how central finds peripheral. After peripheral establishes connection, central device provides a set of services, used by peripheral to obtain data. What is service in terms of BLE? Service is an aggregate of characteristics, representing values. From the central point of view it is an address of data. Peripheral device can provide more than one service. For instance BLE enabled teapot may have temperature-service (providing temperature value), control-service (for control purposes), battery-service (providing current battery level) and so on. As previously said, service contains a set of characteristics. Characteristic contains a value and 0-n descriptors. For example, temperature-service may have the characteristics, representing current water temperature, maximum water temperature and so on. bondini binary_studio article binary studio  android 1.png Characteristics may be one of three types:

  • Allows reading - permits reading values;
  • Allows writing – permits writing values to the peripheral device (for example, for configuration, saving or other purposes);
  • Allows notifying - permits subscribing to the characteristic. Subscriber will be notified each time characteristic value changes.

This is basically all we need to know to exchange the data via BLE protocol. Let's take a look at the details of central role implementation.   Central role (Gatt Client) To use BLE in Android you should add the following permissions to AndroidManifest file:

<!--In order to initiate device discovery or manipulate Bluetooth settings-->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!--In order to use blutooth in application-->
<uses-permission android:name="android.permission.BLUETOOTH" />
<!--App is available not only to BLE-capable devices-->
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />

Bluetooth communication in Android starts from BluetoothAdapter object. You can obtain it via BluetoothManager: bondini binary_studio article binary studio  android 2.png BluetoothAdapter lets you manipulate Bluetooth power state via Enable/Disable methods:

if (!adapter.IsEnabled)
{
adapter.Enable();
}

However, be careful - enabling or disabling Bluetooth from the code may lead to Bluetooth error, which can be fixed only by rebooting your device. To avoid this make sure that other Bluetooth operations are not executing at the moment of enabling/disabling, and don't start executing right after enabling, before Bluetooth is fully initialized. Android Bluetooth API doesn't like concurrent operations and should be used in sequential manner.   Finding Devices Now that we have BluetoothAdapter instance, we can start scanning for the nearby devices.

public class BluetoothDeviceScanner : Java.Lang.Object,
BluetoothAdapter.ILeScanCallback
{
private readonly BluetoothAdapter _adapter;
public void StartScan()
{
_adapter.StartLeScan(this);
}
public void StopScan()
{
_adapter.StopLeScan(this);
}
public void OnLeScan(BluetoothDevice device, int rssi, byte[] scanRecord)
{
//Continue working with found device
}
}

For starting or stopping device discovery two methods are used - StartLeScan() and StopLeScan(). Both methods accept as argument an object, implementing OnLeScan() callback, called each time Bluetooth device is discovered. In callback we receive

  • BluetoothDevice object, used later for establishing connection;
  • RSSI value, indicating how far peripheral is from our device;
  • ScanRecord - byte array that contains peripheral advertising data.

StartLeScan() method also accepts  array of service UUIDs, used for filtering out the devices that do not support this services. Unfortunately it might not be working. If you have such problem a work around would be to parse the advertiseData contained in scanRecord. This would allow you to obtain an identifying service UUID of a found peripheral. You can use it then for a filtering purpose. Here you can read more about it.   Connecting to peripheral Now that we have discovered the peripheral device we are interested in, the next step would be to establish connection. To establish a connection we use ConnectGatt() method of the discovered BluetoothDevice instance.

public void Connect(BluetoothDevice device)
{
BluetoothGatt gatt = device.ConnectGatt(Application.Context, true,
GattClientObserver.Instance);
}

ConnectGatt() method returns BluetoothGatt instance, which will be used for further interactions with connected BLE device. Except context, ConnectGatt() accepts two arguments: boolean autoConnect and BluetoothGattCallback gattCallback. AutoConnect argument is useful if the BLE device is in range at the time of connection attempt. If you want to automatically connect as soon as it becomes available you should set autoConnect to true. Otherwise connection attempt would be made only once. However, from my experience, when autoConnect is set to true, connection might take 30+ seconds. I was able to reduce this delay by starting scanning process for a short period of time, right after connection attempt. GattCallback is used for observation of Bluetooth events. BluetoothGattCalback provides a set of methods to override, called each time BLE events occur, like connection state change, write/read/notify operations result and so on. Here is the example of the GattCallback object:

public class GattClientObserver : BluetoothGattCallback
{
public override void OnServicesDiscovered(BluetoothGatt gatt, GattStatus status)
{
}
public override void OnConnectionStateChange(BluetoothGatt gatt,
GattStatus status, ProfileState newState)
{
}
public override void OnCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, GattStatus status)
{
}
public override void OnDescriptorWrite(BluetoothGatt gatt,
BluetoothGattDescriptor descriptor, GattStatus status)
{
}
//And more...
}

Right now we are interested in OnConnectionStateChanged callback, because this is where we track all changes of connection state. Here we are presented with two new arguments GattStatus and ProfileState. GattStatus indicates the result of connect/disconnect operation. ProfileState shows current connection state. In the example below we output all connection state changes.

private void OnConnectionStateChanged(BluetoothGatt gatt, GattStatus status,
ProfileState newState)
{
switch (newState)
{
case ProfileState.Connected:
Debug.WriteLine("Connected peripheral: " + gatt.Device.Name);
break;
case ProfileState.Disconnected:
Debug.WriteLine("Disconnected peripheral: " + gatt.Device.Name);
break;
case ProfileState.Connecting:
Debug.WriteLine("Connecting peripheral: " + gatt.Device.Name);
break;
case ProfileState.Disconnecting:
Debug.WriteLine("Disconnecting peripheral: " + gatt.Device.Name);
break;
}
}

If you want to check current connection state of the specific device you can do this by getting list of the connected devices and checking if the device is there. This is where previously obtained BluetoothManager instance comes in handy. bondini binary_studio article binary studio  android 3.png Disconnection When we are finished working with the remote device, we can disconnect it directly using BluetoothGatt object. It is also preferable to close the BluetoothGatt afterwards.

bluetoothGatt.Disconnect();
//After connection state changed to disconnected close gatt
bluetoothGatt.Close();

Device will be also disconnected if it is out of range or Bluetooth is disabled. You can call bluetoothDevice.ConnectGatt() after disconnection with autoConnect set to true, in order to connect right after device becomes available. However this would not work if you do this after Bluetooth was disabled. In this case you should listen to Bluetooth state and try to connect when Bluetooth is enabled again.   Conclusion In this article you obtained some theoretical knowledge about BLE protocol and learned how to discover and establish connection with BLE devices on Android. All the code examples in this article are used in a demo project, which you can download here.

0 Comments
name *
email *