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.
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.
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:
<!--Inorder toinitiate device discovery ormanipulate Bluetooth settings-->
BluetoothAdapter lets you manipulate Bluetooth power state via Enable/Disable methods:
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.
Now that we have BluetoothAdapter instance, we can start scanning for the nearby devices.
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.
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:
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.
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.
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.
//After connection state changed to disconnected close gatt
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.
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.
Enjoyed this article?
you might want to subscribe for our newsletter to get more content like this: