Blaine's World

Software Simplification

Published

I decided to simplify the user-facing software of the radio by implementing a small mpd client I’m calling radplay. I originally intended to use an existing client called ncmpc, but it is far too advanced for the simple user-control I have in mind. Ideally I would like to reduce the controls of the radio to two rotary encoders – one to select a station and the other to change the volume.

It took a couple weeks of on and off hacking, but I came up with a Python program that acts as a front-end to mpd with a couple bonus features. On top of controlling playback, radplay loads station configuration from external storage and manages the WiFi connection of the Pi via wpa_cli. The radplay program also controls a GPIO pin that is wired to a relay that interrupts the TV’s power supply. This allows the screen to be put to sleep when there is no activity after a few minutes. Only the station control knob of the radio will wake the screen; the volume can be changed without toggling the TV state. As of now radplay gets user input from getch() within curses so I have been using a standard USB keyboard for controlling the radio. I plan to build dedicated controls using rotary encoders and use a hardware overlay to emulate key presses.

To make configuration as user-friendly as possible I combined all user controlled options into a single single file called radio.conf that is loaded from a USB drive. The user configuration is loaded once when radplay is started and whenever the SIGHUP signal is received from an mdev script that automatically mounts USB storage devices. This makes it possible to change stations or WiFi settings without rebooting the radio. Moving the user configuration to external storage also allows for a completely read-only setup of the Pi’s operating system which means the radio can but turned off at any point without worrying about corrupting the SD card contents.

Like ncmpc, radplay is a curses application targeting console use. The default font in use by Alpine Linux is a little small for use on my tiny TV so I set up a large variant of terminus in /etc/conf.d/consolefont and set it to be activated at boot using the consolefont service.

To make everything feel a bit more polished I set up a splash screen to be shown during boot. I managed to find a nice example on the Alpine Wiki that made use of BusyBox’s fbsplash program which is enabled in the Raspberry Pi build of Alpine. The fbsplash program loads a file called fbsplash.ppm directly from the FAT32 partition of the SD card and shows it on the framebuffer in place of the console.

One problem I found with this approach is that fbsplash does not relinquish the framebuffer after boot and the console is not restored. I played around with many console related commands and finally found that killing fbsplash and using chvt 1 to switch back to tty1 would clear the splash and return to a blanked console that could be awoken by pressing a key on the keyboard. After more experimentation and searching I found someone else with a similar problem and an incantation that will unblank the framebuffer and show the contents of the console attached to it: echo 0 > /sys/class/graphics/fb0/blank.

With the splash screen in place I still had a couple of minor distractions during boot. The contents of the boot log was visible for a few seconds after the splash was cleared just before radplay was started. I changed the console kernel option in cmdline.txt to tty2 to hide all boot output from view. I also added consoleblank=0 to prevent the console from blanking after a timeout and vt.global_cursor_default=0 to hide the blinking cursor that appeared whenever the console was empty.

Now that I have a fairly complete software stack I can focus on building out the remaining hardware of the radio. All I have left to do is to add rotary encoders and a nice faceplate to tie everything together.