The Essentials: Core BLE Concepts and Flutter Blue Plus

Services and Characteristics: The Building Blocks of BLE Communication

Sparkleo Technologies
4 min readMay 16, 2024

Think of BLE communication like a restaurant menu. The services are the different sections of the menu (e.g., appetizers, entrees, desserts). Each service contains a collection of characteristics, which are like the individual dishes listed under each section.

In the BLE world:

  • Services: A service is a collection of data and associated behaviors that work together to fulfill a specific function. For example, a heart rate monitor might have a Heart Rate Service, and a battery-powered device might have a Battery Service.
  • Characteristics: Characteristics represent individual data points within a service. They hold the actual values you want to read from or write to the device. For instance, the Heart Rate Service might have a Heart Rate Measurement characteristic.

Discovering Services

Once connected to a BLE device, the first step is to discover the services it offers. This is done using the discoverServices() method on the BluetoothDevice object:

List<BluetoothService> services = await device.discoverServices();

This asynchronous method returns a list of BluetoothService objects. You can then iterate through this list to access the services available on the device.

Exploring Characteristics

Each BluetoothService object has a list of its associated characteristics. You can access this list using the characteristics property of the service object:

for (BluetoothService service in services) {
List<BluetoothCharacteristic> characteristics = service.characteristics;
for (BluetoothCharacteristic characteristic in characteristics) {
// Access characteristic properties (UUID, value, etc.)
}
}

In this example, we iterate through each service and then through each characteristic within the service.

Reading Characteristic Values

To read the current value of a characteristic, you can use the read() method on the BluetoothCharacteristic object:

List<int> value = await characteristic.read();

The read() method returns a Future that completes with a list of integers representing the raw data of the characteristic's value. You'll often need to interpret this data based on the specific characteristic's format.

Writing to Characteristics

To send data to a BLE device, you write to its characteristics. Use the write() method on the BluetoothCharacteristic object:

await characteristic.write([0x01, 0x02, 0x03]); // Example data

The write() method takes a list of integers as input, representing the data you want to send.

UUIDs: Unique Identifiers

Every service and characteristic is identified by a UUID (Universally Unique Identifier). UUIDs are 128-bit values represented as a string of hexadecimal digits. You’ll often encounter UUIDs in the BLE specification documents, and you’ll need to use them to identify the specific services and characteristics you want to interact with.

Example: The Heart Rate Service has a UUID of 0x180D, and the Heart Rate Measurement characteristic has a UUID of 0x2A37.

Notifications and Indications

there are two primary ways for a peripheral device (like a heart rate monitor or temperature sensor) to send data to your Flutter app: notifications and indications. While both mechanisms deliver updates from the device, they have subtle differences.

  • Notifications: These are unsolicited messages sent by the peripheral. Your app doesn’t need to acknowledge them, making them ideal for frequent updates where reliability isn’t critical (e.g., battery level updates).
  • Indications: These are similar to notifications, but your app must acknowledge receiving them. This ensures reliable delivery and is suitable for critical data (e.g., important sensor readings).

Subscribing to Notifications/Indications

Flutter Blue Plus makes subscribing to notifications or indications straightforward. Let’s assume you have a BluetoothCharacteristic object representing the characteristic you want to receive updates from:

BluetoothCharacteristic characteristic; // Your target characteristic
await characteristic.setNotifyValue(true);

The setNotifyValue(true) method enables notifications (or indications, depending on the characteristic's properties).

Receiving Updates

Once subscribed, you’ll receive updates on the characteristic.onValueReceived stream:

characteristic.onValueReceived.listen((value) {
// Process the received data (value) here
});

Example: Heart Rate Monitor

Let’s say you’re working with a heart rate monitor that sends notifications of the current heart rate.

heartRateCharacteristic.onValueReceived.listen((value) {
int heartRate = value[1]; // Assuming heart rate is in the second byte
print('Heart rate: $heartRate bpm');
// Update your UI or perform other actions based on the heart rate
});

Unsubscribing

To stop receiving notifications or indications, simply set the notifyValue to false:

await characteristic.setNotifyValue(false);

lastValueStream: A Convenient Alternative

Flutter Blue Plus also offers the lastValueStream property on characteristics. This stream emits the latest value of the characteristic, regardless of whether it was read, written, or received through a notification. It can be a convenient way to track the most recent state of a characteristic.

Descriptors: Enhancing Characteristic Behavior

Think of descriptors as metadata for characteristics. They provide additional details about a characteristic’s behavior or value. For example:

  • Client Characteristic Configuration Descriptor (CCCD): This crucial descriptor controls whether notifications or indications are enabled for a characteristic.
  • User Description Descriptor: This descriptor provides a human-readable description of the characteristic.
  • Format Descriptor: This descriptor specifies the data type and format of the characteristic’s value.

Reading and Writing Descriptors

You can read and write descriptors using the same methods (read() and write()) you use for characteristics. The only difference is that you'll be working with BluetoothDescriptor objects instead of BluetoothCharacteristic objects.

BluetoothDescriptor descriptor; // Your target descriptor
// Read
List<int> value = await descriptor.read();
// Write
await descriptor.write([0x01, 0x00]); // Example data (enabling notifications)

In the next section, we will learn about Advanced BLE Development with Flutter Blue Plus.

--

--

Sparkleo Technologies

We provide the bridge between the real and digital world by building IoT based solutions. - https://www.sparkleo.io/