awk driven IoT

Making toy prototypes with traditional unix tools

July 05, 2017

With a Raspberry Pi or other modern single-board computers, you can make very simple toys. I started with the hello world of interactive apps: the soundboard. But even then, I was too ambitious.

The soundpad

Since the main target was kids. I wanted a simple, screen-less toy that would teach the basics of interactivity, as well as serve as a platform for learning. A simple soundboard can be quite useful to learn animal calls for instance, so I was set.

But I also wanted this toy to be wireless and interact with the house. For the first part, I decided to hook an old bluetooth game controller I had lying around. I was able to detect its keys with evtest pretty quickly and make an inventory of all buttons keycodes:

304
305
306
307
308
312
313
314
315
316
317
318

For the sounds, I reused the sounds present in the default raspbian scratch installation. There are a few wave files in /usr/share/scratch/Media/Sounds/ that proved useful. I made a few directories with symbolic links to the samples I was interested in. Combining vidir and the previous keycode list, I ensured each wave file name started with a keycode, like this for the Animal sounds:

304-Bird.wav -> /usr/share/scratch/Media/Sounds/Animal/Bird.wav
305-Cricket.wav -> /usr/share/scratch/Media/Sounds/Animal/Cricket.wav
306-Crickets.wav -> /usr/share/scratch/Media/Sounds/Animal/Crickets.wav
307-Dog1.wav -> /usr/share/scratch/Media/Sounds/Animal/Dog1.wav
308-Dog2.wav -> /usr/share/scratch/Media/Sounds/Animal/Dog2.wav
312-Duck.wav -> /usr/share/scratch/Media/Sounds/Animal/Duck.wav
313-Goose.wav -> /usr/share/scratch/Media/Sounds/Animal/Goose.wav
314-Horse.wav -> /usr/share/scratch/Media/Sounds/Animal/Horse.wav
315-HorseGallop.wav -> /usr/share/scratch/Media/Sounds/Animal/HorseGallop.wav
316-Kitten.wav -> /usr/share/scratch/Media/Sounds/Animal/Kitten.wav
317-Meow.wav -> /usr/share/scratch/Media/Sounds/Animal/Meow.wav
318-Owl.wav -> /usr/share/scratch/Media/Sounds/Animal/Owl.wav

In order to interact with the house, I paired a bluetooth soundbar to the raspberry pi.

Once all of this is setup, this is the entirety of the code for the first iteration of the working soundpad (soundboard + joypad):

#!/bin/bash
cd $1
stdbuf -o0 evtest /dev/input/event0| awk -W interactive '
/EV_KEY/ { if ( $NF == 1) { system("paplay " $8 "-*.wav&") }}'
  • stdbuf is very useful when playing with pipes where the input command is blocking, but you still want interactivity. It allows you to control i/o buffering.
  • evtest parses input events.
  • awk -W interactive has the same role as stdbuf -i0, but for mawk's internal buffering (it's not needed for GNU awk).
  • when a matching line is found, paplay is used to play the audio through pulseaudio's bluez sink, that was previously configured as default. The filename corresponds to the button keycode.

The last iteration has the same core code, but with a bit more setup: using bluetoothctl and pactl to make sure the controller and the soundbars are properly connected and configured mainly.

It worked, for the most part, but was far from plug-and-play. The soundbar needed to be turned on and put in bluetooth mode. The wireless joypad had to be turned on. It needed constant re-setup of the bluetooth connections, because it lost the pairings regularly. And sometimes the audio would stutter horribly. I tried compiling a more recent version of bluez, to no avail.

So after a few day of demos and sample playing, I binned this project about 9 months ago.

The music portal

Fast forward today, I had this thing bothering me about modern music and rhymes for kids. With Deezer & Spotify, we have access to a library we could only dream of. But it's impossible for 2 year old child to operate, or even desirable.

Even without online services, the only alternative would be to go back to the audio CDs. But the only functional CD player in our house is the CD-ROM drive in my Desktop computer; I therefore backup all our audio CDs in audio files. Playing those has the same level of complexity (and screen-interaction) as interoperating with streaming services, so it's back to square one.

That's where the music portal comes in. It's a combination of a Violet Mir:ror I had lying around, and a Raspberry PI with a speaker hooked up.

The Mir:ror is a very simple RFID reader. It's basically plug-and-play on Linux, since it sends raw HID events, with the full ID of the tags it reads, and it has audio and visual feedback. I also evaluated using a Skylanders portal, which also sent raw HID events, but its data was much less detailed, with only two bytes of information in the HID events, and the need to do more work to get the full data, and has no audio or visual feedback.

So here is the code of the first version:

#!/bin/bash
sudo stdbuf -o0 hexdump -C /dev/hidraw0 | awk -W interactive '
/02 01 00 00 08 d0 02 1a  03 52 c1 1a 01 00 00 00/ { print "file1 "; play=1; file="file1.mp3" ; }
/02 01 00 00 08 d0 02 1a  03 52 c1 4b ad 00 00 00/ { print "file2 "; play=1; file="file2.mp3" ; }
/02 01 00 00 04 3f d7 5f  35 00 00 00 00 00 00 00/ { print "dir1 "; play=1; file="dir1/*.mp3" ; }
/  02 02 00 00 0. |01 05 00 00 00 00 00 00  00 00 00 00 00 00 00 00/ { print "stop"; system("killall -q mpg321"); }
{
if (play) {
        system("mpg321 -q " file " & ");
        }
play=0 ;
}
'
  • we use the same stdbuf and awk -W interactive trick as before. Fun fact: I rediscovered this mawk argument by reading the man page while doing this project because I had forgotten about it in only 9 months. I don't think I'll forget it again.
  • Here we're matching full HID event lines. We don't even bother decoding the payload size, etc. Since it all fits on a line matchable by awk.
  • I used mpg321 because it has the less footprint when compared to mpg123, gst-launch, mplayer, vlc, and others.
  • I used the same symbolic link structure because it's much easier than putting the full file names in the script.
  • We handle "tag" removal as well as portal shutdown. The Mir:ror automatically shuts down when turned face down.
  • There are race conditions hiding here. It's not a big deal, it's just a prototype.

What could I use after I setup the two included Nanoztags ? I could put RFID stickers on objects; or I could use my visa card; or anything that has an RFID/NFC feature (like my phone). But there are better, available off-the-shelf choices: toys-to-life like Skylanders ! There are already made for kids, are very sturdy, and I managed to snag a few on clearance at ~1€ a piece !

Make sure the Raspberry Pi is connected to your wireless network, so you can add new songs remotely, and throw in a systemd.service for automatic starting, and the toy is finished:

[Unit]
Description=Music Portal

[Service]
Type=simple
ExecStart=/home/pi/musicportal.sh
User=pi
Group=pi
WorkingDirectory=/home/pi
StandardOutput=journal+console
StandardError=journal+console
Restart=always
RestartSec=3


[Install]
WantedBy=multi-user.target

And it's truly plug-and-play: you just need to plug the Raspberry Pi, and it powers the speaker through USB, as well as the Mir:ror.

Here's a video of the final result:

Last but not least, the title of this article is awk driven IoT. So I integrated librespot, and I can now play songs and rhymes from this online streaming service ! Success ✔

Continue reading→