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)

8.6.2015

4 min read

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.

public bool DiscoverServices(BluetoothGatt gatt) { 
    //Start peripheral service discoveryreturn gatt.DiscoverServices();
}

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.

public override void OnServicesDiscovered(BluetoothGatt gatt, GattStatus status) {
    Debug.WriteLine(status != GattStatus.Success ? "Failed to discover device services" : "Successfully discovered device services");
    if (status != GattStatus.Success) {
        return;
    }
    foreach(var service in gatt.Services) {
        Debug.WriteLine("Service with adress: " + service.Uuid);
        foreach(var characteristic in service.Characteristics) {
            Debug.WriteLine("Has characteristic with adress: " + characteristic.Uuid);
        }
    }
}

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:

public bool IsEncryptedAndWritable(BluetoothGattCharacteristic characteristic) {
    var isWritable = characteristic.Properties.HasFlag(GattProperty.Write);
    var isEncrypted = characteristic.Permissions.HasFlag(GattPermission.WriteEncrypted);
    return isWritable && isEncrypted;
}

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.

public bool Read(BluetoothGattCharacteristic readCharacteristic, BluetoothGatt gatt) {
    return gatt.ReadCharacteristic(readCharacteristic);
}

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.

public override void OnCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, GattStatus status) {
    if (status != GattStatus.Success) {
        Debug.WriteLine("Failed to read characteristic: " + characteristic.Uuid);
        return;
    } 
    //Read value from device 
    byte[] readValue = characteristic.GetValue(); 
    //Process received value...
}

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

public bool ReadDescriptor(BluetoothGattDescriptor readDescriptor, BluetoothGatt gatt) {
    return gatt.ReadDescriptor(readDescriptor);
}
public override void OnDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, GattStatus status) {
    if (status != GattStatus.Success) {
        Debug.WriteLine("Failed to read descriptor");
        return;
    }
    byte[] readValue = descriptor.GetValue();
}

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.

private void WriteValueInternal(byte[] buffer, BluetoothGatt gattBluetoothGattCharacteristic characteristic) { 
    //Set value that will be written 
    characteristic.SetValue(buffer); 
    //Set writing type 
    characteristic.WriteType = GattWriteType.NoResponse; 
    gatt.WriteCharacteristic(characteristic); 
}

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.

public override void OnCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, GattStatus status) {
    if (status == GattStatus.Success) {
        Debug.WriteLine("Writing was successfull");
    } else {
        var errorCode = status; 
        //Process error... 
    } 
}

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.

public void SubscribeCharacteristic(BluetoothGattCharacteristic characteristic, BluetoothGatt gatt) {
    gatt.SetCharacteristicNotification(characteristic, true);
    var descriptor = characteristic.GetDescriptor(UUID.FromString("00002902-0000-1000-8000-00805f9b34fb"));
    descriptor.SetValue(BluetoothGattDescriptor.EnableNotificationValue.ToArray());
    gatt.WriteDescriptor(descriptor);
}

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.

public override void OnCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
    var newValue = characteristic.GetValue(); 
    //Process value...
}

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.

0 Comments
name *
email *