Wassup?
Enable desktop notifications in Raspbian, and display the notifications on an external Arduino LCD display.
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.
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!
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.
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
notify.py
01 import glib 02 import dbus 03 from dbus.mainloop.glib import DBusGMainLoop 04 import serial 05 06 port = serial.Serial ( "/dev/ttyACM0" , 9600 ) 07 08 def notifications(bus, message): 09 global port 10 11 args = message.get_args_list() 12 13 title = "" 14 body = "" 15 level = "" 16 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 23 24 port.write ( "C" + level + "\n" ) 25 port.write ( "1" + title + " " * 16 + "\n" ) 26 port.write ( "2" + body + " " * 16 + "\n" ) 27 28 DBusGMainLoop(set_as_default=True) 29 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) 33 34 mainloop = glib.MainLoop() 35 mainloop.run()
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 from
… import
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.
Price $15.99
(incl. VAT)