Using Bluetooth Low Energy between Raspberry Pi and Flutter

Months ago, I built a printed lighthouse weather station. It used Classical Bluetooth to communicate between a Raspberry Pi in Python and a mobile application in Flutter. I migrated both to support Bluetooth Low Energy.

The great thing about Flutter is to create apps for different platforms, as Android, iOS and web. However, on iOS, you must use BLE (Bluetooth Low Energy) protocol.

My previous project with a Flutter application and a Raspberry Pi was using Classical Bluetooth to communicate between each other. It worked great… However, as gift for my dad — which has an iPhone — he could not properly use it. So it was a bit frustrating.

This is what I made to migrate to BLE in Python and Flutter.

1. Bluetooth Low Energy

First of my migration steps, understand BLE and prepare my requirements.

Adafruit wrote a well-documented topic on this: Introduction to Bluetooth Low Energy. If you don’t know what BLE works, this article is a pretty good start.

BLE communication uses GAP (Generic Access Profile) to define roles for devices. In this project, the RPi station is the Peripheral device — connecting to wifi, connecting to mobile phone, fetching and returning weather data over a remote api— and the mobile phone is the Central device — displaying data received, sending commands.

To communicate through BLE, we use GATT (Generic Attributes Profiles). This defines how two devices talk to each other by using Profiles, Services and Characteristics. I will not describe all the protocol in this topic but, to explain quickly, Profiles are Services collections, Services are the features of the devices (like “weather service", “color service”) and Characteristics are actions of a service (for example “weather service” has several actions as “retrieve the weather data”, “resume/pause fetching weather data”, “change the city of weather”).

Services and Characteristics need a unique UUID (some of them are assigned). I generated them on a website. To easily identify them, I defined the same generated sequences of a Service’s UUIDs but change the first part with a simple incrementation for each related actions:

WEATHER SERVICE  - 00000000-8cb1-44ce-9a66-001dca0941a6
RETRIEVE WEATHER - 00000001-8cb1-44ce-9a66-001dca0941a6
RESUME WEATHER - 00000002-8cb1-44ce-9a66-001dca0941a6
CHANGE CITY ID - 00000003-8cb1-44ce-9a66-001dca0941a6
COLOR SERVICE - 00000000-8194-4451-aaf5-7874c7c16a27
CHANGE COLOR - 00000001-8194-4451-aaf5-7874c7c16a27

Finally, I had to specify which Characteristics can read, write and notify:

RETRIEVE WEATHER - read/notify
CHANGE CITY ID - read/write

Enable read means the clients will be able to read Characteristics values. Write allows the clients to write values to a Characteristics. And notify lets the servers telling the clients that Characteristics values have changed.

2. Raspberry Pi

Migrate the Raspberry Pi includes to change the Python script from Bluetooth Socket over RFCOMM to BLE.

I found a simple project to get current temperature from a RPi sensor in Python (from Douglas Otwell). He was based on BlueZ experimental version and created modules to deal with BLE. I used it with my own Services and Characteristics.

Since the GATT server he made was excellent and clear, I copied his application configuration, advertising and tools classes. I moved them into a specific package ble_gatt_server:

The is my Application’s declaration. It instantiates the Advertisement as a Peripheral device:

It creates the Services and attaches Characteristics as follows:

Following by Characteristic’s constructor:

And their specific functions, like getting data (degrees, weather_id, city_id) from the Service:

Or notifying new values to the clients:

Finally, in, I declare the BLE application, add my services and update the value to notify the client that, for example, degrees has changed:

3. Flutter Application

I made the mobile application with flutter_bluetooth_serial package. I had to change the communication process with another library: flutter_blue.

My Flutter application was designed with BLoC pattern. I did not change this architecture and kept all the screens as they were. Only the BLoC classes were updated to deal with the new library.

With BLE, the mobile app needs to discover the Services when connected and to store them in bleServices list into BleBloc class.

In respective BLoC constructors, I pass each Service with their UUIDs:

Retrieving by this function to find the first occurrence inside the list:

Same for their Characteristics:

Finally, the mobile app listens to updates and decodes data — as it already did before — when the RPi notifies that property has changed:

4. Final Results

My migration to BLE is finished. Starting on the RPi, I see now this output:

$ sudo python3 main.pyGATT application running
GATT application registered
GATT advertisement registered
Update weather
Request response: 18°C (weather: 803)
Color selected: (255, 255, 0)
Update weather
Request response: 18°C (weather: 803)
Color selected: (255, 255, 0)

As it did before, every 5 seconds the script fetches the weather data, displays “Update weather” informations and notifies that data has changed. When a device is connected, then the output is:

Update weather
Request response: 18°C (weather: 803)
Color selected: (255, 255, 0)
Sending: D=19,W=803,C=6454880
Update weather
Request response: 18°C (weather: 803)
Color selected: (255, 255, 0)
Sending: D=19,W=803,C=6454880

The main advantage of BLE — aside it uses low power consumption — is to split your features in better ways than Classical Bluetooth. Each of your features is a specific Service. It is more readable and cleaner.

As a final thought, updating was less painful than I expected. Passing from Classical Bluetooth to BLE was only a changing of library on both side. Well, almost.



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Florent Blot

Florent Blot

Maker, Mobile developer, Kotlin & Dart enthusiast, IoT & DIY addict. @Geev: