Interfacing Raspberry Pi Sensors with Dart and Flutter using D-Bus

Moritz Theis
Snapp Embedded
Published in
4 min readNov 23, 2023
Cover Image

Intro

The Raspberry Pi, with its low cost and versatility, has become a popular choice for hobbyists and developers alike. It’s particularly well-suited for embedded systems and Internet of Things (IoT) projects. One of the challenges in these projects is often data acquisition from sensors. This is where D-Bus comes in handy.

D-Bus is a message-based inter-process communication system for Unix-like operating systems. It allows different applications to communicate with each other, regardless of their programming language or location on the network. This makes it an ideal tool for connecting sensors and actuators to a Raspberry Pi running a Dart script.

In this blog post, we’ll demonstrate how to use D-Bus to read data from a CO2 sensor connected to a Raspberry Pi via I2C. We’ll use a Python script to expose the sensor data as a D-Bus service, and then consume that data in Dart script.

Disclaimer: This article is a rather short-breathed and practical guide using D-Bus with Flutter and Dart. The possibilities with D-Bus are way beyond what is shown here and to understand it you should read the official documentation which can be found under: https://www.freedesktop.org/wiki/Software/dbus/ Also it should be mentioned that most of the time one would create D-Bus interfaces by using a D-Bus interface definition file. This can be done with Dart as well as all other supported languages, check out https://pub.dev/packages/dbus for an example.

Hardware Setup

For this project, you’ll need the following hardware:

Connect the CO2 sensor to the Raspberry Pi’s I2C pins according to the sensor’s datasheet.

Software Setup

Prerequisites:

  • Raspberry Pi running Raspberry Pi OS 64 bit (2023–10–10 or 2023–05–03)

To install and start D-Bus we have to do, … nothing! It’s already installed and running on Raspberry Pi OS since it is used for numerous inter-procecess-communication on it. So luckily we have that part already setup and can just use the existing D-Bus to send data to and from our applications.

So first we will download snapp_installer for an automatic 1-click setup of Flutter. (You could also do it as described on https://docs.flutter.dev/get-started/install/linux, this script just does the same, but faster.)

bash <(curl -fSL https://snappembedded.io/installer) && source ~/.bashrc

Now we use snapp_installer to install Flutter and all dependencies needed. (Run the command and go grab a coffee, this is going to take a few minutes)

snapp_installer install

After using snapp_installer we have to source our .bashrc

source ~/.bashrc

Next, we activate the I2C-Bus and install the following software packages:

sudo raspi-config nonint do_i2c 0
sudo apt-get install dbus python3-dbus python3-pip
sudo apt-get install libdbus-1-dev

Install the Pimorini SCD41 library: (Official Repo)

pip3 install scd4x

Create a new Dart-Application and add dbus as dependency:

dart create dart_dbus
cd dart_dbus
dart pub add dbus

Open and edit the bin/dart_dbus.dart to

import 'package:dbus/dbus.dart';

Future<void> main() async {
final client = DBusClient.session();
final object = DBusRemoteObject(client,
name: 'de.snapp.CoSensorService', path: DBusObjectPath('/Sensor'));

final response = await object.callMethod(
'de.snapp.SensorInterface', 'GetSensorValue', [],
replySignature: DBusSignature('as'));

/// convert DBusArray to List
final result = List.from(response.values[0].asStringArray());

print('temp: ${result[0]}');
print('hum: ${result[1]}');
print('co2: ${result[2]}');

await client.close();
}

Now we can create our python_script with:

touch sensor_server.py

Open and edit it to:

#!/usr/bin/env python3

from __future__ import print_function
from datetime import datetime, timezone
from gi.repository import GLib

import dbus
import dbus.service
import dbus.mainloop.glib
from scd4x import SCD4X

usage = """
Sensor Server Started Successfully

Wait for the client to connect:
"""

class Sensor(dbus.service.Object):

@dbus.service.method("de.snapp.SensorInterface",
in_signature='', out_signature='as')
def GetSensorValue(self):
# print the name of the bus name we are connected to
print("GetSensorValue request:", session_bus.get_unique_name())
# call the scd4x sensor and return the values
co2, temperature, relative_humidity, timestamp = device.measure()
return [f"Temperature: {temperature:.4f}°C", f"Humidity: {relative_humidity:.2f}%", f"CO2: {co2:.2f}PPM",
session_bus.get_unique_name()]

@dbus.service.method("de.snapp.SensorInterface",
in_signature='', out_signature='')
def Exit(self):
mainloop.quit()


if __name__ == '__main__':
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)


device = SCD4X(quiet=False)
device.start_periodic_measurement()

session_bus = dbus.SessionBus()
name = dbus.service.BusName("de.snapp.CoSensorService", session_bus)
object = Sensor(session_bus, '/Sensor')

mainloop = GLib.MainLoop()
print(usage)
mainloop.run()

If we now open a first terminal and in it start the sensor_server.py

python sensor_server.py
Starting sensor_server.py

We can open a second terminal and execute our dart script with:

dart run

And it will print the sensor data acquired in the Python script in our second terminal window which is running in Dart. The same way we can use and access the sensor data in our Flutter-Apps.

Result of “dart run” showing the sensor values

Conclusion

D-Bus provides a powerful mechanism for interfacing sensors and actuators with Dart and Flutter on the Raspberry Pi. It allows different components of your system to communicate seamlessly, regardless of their programming language or location. This makes it a valuable tool for IoT projects and embedded systems development.

The repository accompanying this article, including a working example can be found under: https://github.com/Snapp-Embedded/dart_dbus

--

--

Moritz Theis
Snapp Embedded

Flutter Dev, Founder & CEO @ Snapp X, Founder @ Snapp Embedded, Co-Organizer of Flutter Munich