Raspberry Pi, libnotify, and Arduino LCD

Lead Image © Gal Amar, 123RF.com


Enable desktop notifications in Raspbian, and display the notifications on an external Arduino LCD display.

Just about every x86 implementation of a Linux window manager includes a pop-up system so applications can pass messages and information to users, but with Raspberry Pis (Rasp Pis) finding their way into all sorts of projects that don't always have conventional monitors, a pop-up notification might not be very helpful.

Linux notifications make use of dbus (D-Bus), an interprocess communication and remote procedure call handler. The goal of D-Bus is to allow processes that provide services (e.g., pop-up messages) to connect to one central system instead of creating a separate connection for every process that wants to communicate. For example, a process that handles communication with USB devices might announce that a new flash drive has been connected to the system. Any listening processes can send requests and commands over D-Bus to call exposed objects that manipulate the devices.

Raspberry Pi vs. Arduino

A question often asked by makers when they're starting a project is: "Which is better, Arduino or Raspberry Pi?" Unfortunately it's not really a fair question. Both platforms are perfectly valid for projects, but they fill different roles.

Because an Arduino [1] doesn't have an operating system (OS), code is executed directly on the chip, with no overhead from competing priorities. Timing, then, is very precise and reliable between operations. Hardware counters and timers also make a lot of things happen "behind the scenes" that you don't ever have to worry about.

Rasp Pis have the advantage of being a full computer. With multiple choices of OS and associated programming languages, you can work in an environment in which you are comfortable and take advantage of the power of a full computer. HDMI displays, sound files, and network and INTERNET access are just a few things the Rasp Pi does that aren't exactly trivial on the Arduino. However, timing on the Rasp Pi isn't as precise as on an Arduino, and although the GPIO pins available on a Rasp Pi offer quite a bit more than a traditional desktop, it can still be limiting.

By using both platforms in a project, each platform can work to its strengths. USB/serial communication between the two makes it easy to trade data back and forth as well (Figure 1). Using both computers is often an elegant and cost-effective solution!

Figure 1: The Arduino with the LCD shield installed connected to a Raspberry Pi 3 via USB.

To get started, make sure you're in a terminal and run apt-get from a terminal to install a few libraries:

sudo apt-get install libnotify-bin notification-daemon dbus

Once that's done, you can test notifications by using the command-line utility notify-send:

notify-send "Urgent Message" "Something quite pressing..."

You should be rewarded with a pop-up window (Figure 2)! By adding notify-send to your shell scripts, you can let them keep you up to date as they run. Even if your scripts are running in the background, they can let you know what's happening.

Figure 2: The pop-up notification created by calling notify-send. Depending on your notification library and window manager, the appearance could be slightly different.

The software has two parts: the Arduino code to run the LCD and the Python script to listen for notifications. I'll start with the Python script (Listing 1).

Listing 1


01 import glib
02 import dbus
03 from dbus.mainloop.glib import DBusGMainLoop
04 import serial
06 port = serial.Serial ( "/dev/ttyACM0" , 9600 )
08 def notifications(bus, message):
09     global port
11     args = message.get_args_list()
13     title = ""
14     body = ""
15     level = ""
17     cnt = 0
18     for arg in args:
19        if cnt == 3: title = str ( arg )
20        elif cnt == 4: body = arg
21        elif cnt == 6: level = str ( int ( arg [ 'urgency' ] ) )
22        cnt += 1
24     port.write ( "C" + level + "\n" )
25     port.write ( "1" + title + " " * 16 + "\n" )
26     port.write ( "2" + body + " " * 16 + "\n" )
28 DBusGMainLoop(set_as_default=True)
30 bus = dbus.SessionBus()
31 bus.add_match_string_non_blocking("eavesdrop=true, interface='org.freedesktop.Notifications', member='Notify'")
32 bus.add_message_filter(notifications)
34 mainloop = glib.MainLoop()
35 mainloop.run()

Rasp Pi Python Script

In lines 1-4, import is Python's method of including external libraries. In this case, I'm importing glib, dbus, and serial. Line 3, which starts with from instead of import, is a little different. Python library files can have multiple modules inside each file. When I say import dbus, I'm only importing the default modules. By using the fromimport syntax, I explicitly ask Python to import another module within the same library.

Line 6 opens the serial port to connect to the Arduino with serial.Serial, which requires two arguments. The first is the address of the serial port and the second is the baud rate. Other configuration options for the serial port are accessible using serial.Serial if needed. Note that if you have more than one Arduino connected to the Rasp Pi, your serial port address might be different.

The notifications function (lines 8-27) is called each time a message is transmitted. The bus argument is the name of the bus on which the message was transmitted and message has its details.

The global keyword in line 9 allows the reach of the port variable defined on line 6 to extend outside of the function; then, line 11 retrieves the arguments of the received message with message.get_args_list. Lines 13-15 initialize the text strings that will ultimately store the details of the message, and line 17 initializes cnt as an argument counter. Looping over each argument (lines 18-22) saves the title, body, and level (urgency) of the message as each argument is encountered and increments the count for the next round of the loop.

Once all of the details of the message have been extracted, lines 24-26 send them to the LCD. Line 24 sends a capital C and the urgency level of the message, which can be low, normal, or critical (expressed as priority level 0, 1, or 2). In this example, it will set the color of the backlight. Line 25 sends a 1 to tell the LCD to write the title of the message on the first line, followed by 16 spaces and a newline. The title is usually the program that's sending the message or what the message is announcing (e.g., "new email"). Line 26 sends a 2 to tell the LCD it has the text for the second line; then, it sends the body of the message and appends 16 spaces. These extra spaces make sure that old data is overwritten in the LCD memory for at least the width of the display. The body of the message has other details about the message, like the subject of an email or the amount of battery remaining.

Lines 28-32 take care of connecting to D-Bus. First, the set_as_default argument is set to True, making it the default loop handler. Line 30 instantiates dbus.SessionBus, and line 31 lets dbus know which message(s) I'm interested in. The eavesdrop=true argument tells D-Bus to look at all messages, not just those specifically directed to my process. The interface argument identifies the specific bus to listen to, and member provides the name of the event(s) to capture. Finally, add_message_filter on line 32 specifies notifications as the function to call when a matching message is received.

Now that all the programming is done, line 34 creates an instance of glib.MainLoop and the last line runs it.

Buy Raspberry Pi Geek

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content