Saveliy Bondini.NET Developer

Bluetooth Low Energy for Android (Part 2)


In the second article about our BLE journey you will know how to discover BLE services, obtain characteristics and how to make characteristics work.

For the sake of clarity we will consider that our android application communicates with BLE kettle, which plays peripheral role. We have already discovered it and established connection. Time to move on.

Discovering services

Before we can use our kettle, we need to discover what it can do, and how. As you have already learnt, all communications are done via services and their characteristics. So we need to discover kettle services first.

We initiate service discovery by calling DiscoverServices() method of BluetoothGatt instance.

Discovery may take some time, after which OnServicesDiscovered() callback will be called. If discovery succeeds we have access to BluetoothGattService and BluetoothGatCharacteristic objects, which are android representation of BLE characteristics and services.

If service discovery was successful we can iterate over all services and their characteristics of connected peripheral.

You can also investigate what type of characteristic were discovered, by checking its permission and property fields. Permission field lets you see is characteristic access is encrypted. By checking property of characteristic you determine its features like writing, reading and so on.

For instance we can check if discovered characteristic supports writing and has encryption in the following way:

Some characteristics may not be visible through means of service discovery until you pair with device. We will discuss pairing in the next part of this article.

Reading value
Now we can check the temperature of water in a kettle, by reading out characteristic responsible for that.

For reading values from the peripheral device we use readCharacteristic() method of BluetoothGatt instance. As an argument this method accepts BluetoothGattCharacteristic instance, representing previously discovered readable characteristic.

The result of reading operation is observed in a gatt OnCharacteristicRead() callback. First we need to check the status of operation. If operation succeeded we obtain read value from characteristic object.

The algorithm for reading descriptors is the same, what differs is the names of reading method and gatt callback.

Writing value
We have read temperature of our kettle, but it’s not hot enough. We want to remotely turn on our teapot for boiling. To do that we need to send data, considered by teapot as “ON command”.

For writing values to peripheral device, BluetoothGatt instance provides writeCharacteristic() method, accepting as an argument discovered writable BluetoothGattCharacteristic.

Before writing we need to configure write-characteristic, providing data for sending, and some other settings.

Now we can tell BLE-Teapot to turn off and on. But how do we actually know that writing was successful? Specifically for this case you can specify what type of write you want to perform:

  • Without response
  • With response

Previously you saw writing without response. When you write with response, you tell the peripheral device, that you expect from it some kind of acknowledgment of operation success. That kind of write specified by setting characteristic WriteType property as GattWriteType.Default.

Write acknowledgment is received by standard callback mechanism as previous Bluetooth events. In case of operation failure, response code can be obtained from status argument.

Algorithm for writing descriptors is the same, except for calling methods.

Value Notification

Okay know we want to know when water in our kettle is boiled. Constantly polling current water temperature would be inefficient. Alternatively there are characteristics to which we can subscribe and be notified only when characteristic value changes.

Subscribing to characteristic notification is done via SetCharacteristicNofication() method of BluetoothGattInstance object. As an arguments method accepts discovered BluetoothGattCharacteristic instance and a boolean, telling to enable or disable subscription.

The result of subscribtion operation you can track in gatt OnDescriptorWritten() callback. If callback status is GattStatus.Success then we are successfully subscribed to characteristic notification.

Now each time characteristic value changes OnCharacteristicChanged callback will be triggered.

When we no longer need to be notified we can unsubscribe from characteristic by setting enable argument of SetCharacteristicNotification() method to false.

Finale

Now you have an idea of how services, characteristic and descriptors are presented in android and how to use them to get some data.

All code examples presented in this article are used in a demo project, which you can download here.