Operating software

There are N pieces of software that comprise this music system:

Here's the overall system operation:

Power on: system boots normally. User 'hornet' is auto-logged-in, then runs the customary .bashrc startup script.

.bashrc invokes wifilinkup, which manages the wifi networking. It runs silently in the background, and is controlled by semaphore files created/deleted by radioplayer.

.bashrc then invokes radioplayer, which is the main control program. It manages the radio display, playing music as instructed. It also gets car-ignition on/off status from the radio controller; this is the key to power management in this system.

When the car is turned off, radioplayer pauses playing and saves internal state, schedules a system shutdown, and waits. If the car remains powered off long enough, the shutdown cleanly powers off the computer. If the car is powered on before shutdown begins, radioplayer exits, user hornet logs out, and the process is restarted from autologin.

Notes on reliability

Things always go wrong in car environments! Or any embedded environment, for that matter.

For an embedded system like this, error-detection, correction, logging and reliable methods dominate. Approximately half the code written is to detect, log, and recover from errors. The main task itself -- user interface, feed .mp3 files to a decoder -- is easy!

Sensible power management requires care. From what I can tell, most car computers ignore it, requiring manual power control. radioplayer keeps a schedule; time-to-system-power-off depends on the time of day; when I park and turn the car off in the morning, there's a good change I will be driving it again soon. Under this circumstance, the shutdown schedule is two hours. When I turn off the car after midnight, likely I'm home for the night; the system powers off in 30 minutes. However, if upon arriving home there is new music to copy into the car from my home computer (podcasts, etc gathered during the day) shutdown is deferred until transfers complete, so that in the morning on power up, the music collection is automatically updated.

radioplayer depends on the radio control head for car power on/off, as well as user interface; if this link fails then it cannot know power state. An internal timer schedules a shutdown should the radio controller remain silent for (currently) 30 minutes.

The radio controller is tied to the linux box via USB. Commands (knobs turned, etc as well as power status) are sent to the host, and display-update (current song, etc) sent to the radio. The radioplayer software detects USB link failures (eg. unplugged, reset, etc) at any time, and will reestablish the USB connect automatically -- you can unplug and replug the radio while driving with no ill effects.

radioplayer (perl script)

The computer in the trunk runs linux (SuSE 10.0), and is pretty much out-of-the-box. Some minor system configuration covered below. The computer exists to store the mp3 music collection, and to run the radioplayer software, described below.

There is one single-source Perl program that runs the whole music system and does power management. It's called radioplayer. There are a couple of support programs that do other things, explained below.

radioplayer attempts to be very, very robust. I've been writing code for a long time; radioplayer is a personal technique refinement that looks pretty crude at first glance. More on that later.

The heart of this music system is an open source music player called mpg321. It is a command-line mp3 decoder. radioplayer runs it in the background, feeds it mp3 files to make sounds, and converts mpg321's obscurantist mutterings into a radio-like display and controls.

The radioplayer program is completely invisible, taking commands from the hacked radio in the dash, and making audio that feeds the amps that feed the speakers. Unless there's a VGA display and keyboard attached, the only interface to the "computer" is via the radio.

Because this is linux (unix would do, too) there's easy full control of the system; when the car is turned off radioplayer schedules a shutdown (soon, instead, "hibernate"), cancelling any shutdown if the car is restarted, and all sorts of things that make the system transparent.

As far as radioplayer is concerned, the radio control head just sends single-character commands for each action, like a keyboard. In fact, the character codes map to those produced by a numeric keypad, vastly simplifying testing.

Besides having the knobs to play music, the "radio" has a wire that monitors "ignition", eg. if the car is turned on or not. It sends codes to radioplayer telling it ignition status. Therefore, radioplayer knows when and how to pause music, shutdown resources, etc when the car is turned off, and when to schedule automated computer maintenance, updating the music collection, etc.

ACPI is no good for operational system power management needs like this.

The in-dash hacked radio controller is very simple, software-wise: from the radioplayer software's point of view, it issues single-character commands, which map to numeric-keypad arrow keys:

Function Keypad Radio ASCII
Play/pause ENTER VOL press \r (CR)
Choose + TUNE press +
Vol up 8 VOL CW 8
Vol down 2 VOL CCW 2
Next 6 TUNE cw slow 6
Prev 4 TUNE ccw slow 4
Next fast 9 TUNE cw fast 9
Prev fast 3 TUNE ccw fast 3
Ignition offn/a yellow wire E (see text)
Ignition onn/a yellow wire F (see text)
External offn/a green wire G (see text)
External onn/a green wire H (see text)

ACPI powerbutton events -- not useful here

During the development phase, I added code ("powerfail") to the ACPI chain to capture "power switch press" events, but it's less effective than you'd think; basically, it can't distinguish between "pressed to turn off" vs. "pressed to turn on", so there are certain times that ACPI loses events, mainly during the boot process. It's no longer used for anything, but I'll describe it for you anyways.

If an ACPI event ("ignition off") occurs while booting, before the ACPI code is fully running, the event gets lost. This is not rare; it's entirely common that you turn the ignition off 1 to 5 minutes after turning it on: stop to open the trunk, go back to get forgotten item, etc. When an event is missed, the computer then thinks the next event is it's opposite: if it missed "ignition off", when you start the car it thinks that event is "ignition off" and the software remains in "waiting for ignition on", inoperable.

A 100% reliable solution is easy, but requires hardware that the computer does not have: an external input with which userland software can watch "ignition on" state. It requires hardware. So I added that to the radio controller in the dash. It has a wire connected to switched battery ("ignition"), and watches for changes on that wire. It outputs to the radioplayer software, every 5 seconds, the state of that wire. Brutally simple and reliable; even restarting radioplayer will not confuse it. Ignition-on/ignition off appears to the software as a single-character "command" in the table above.

The obvious potential problem is if the radioplayer software is not running. In this case the fallback failsafe is the M1-ATX power supply; it does a hard-power-off should the car batter voltage drop to 11 volts.

Linux system mods

I am exceedingly lazy, and making changes to operating systems is all too easy, and too hard to record or back up. I therefore try to not make them in the first place.

For CPU BIOS settings, I enabled USB keyboard, first boot device CDROM, second hard disk, and disabled net boot; this last was a pain in this CPU, as it takes more than a minute to time out.

I use SuSE linux, and just upgraded to 10.1 (see notes on wireless networking); it comes with a lot of crap already installed. I turned off all non-critical services, firewalled off all services except ssh, and added autologin user 'hornet'. All other code is in userland, run by user hornet.

Here are my notes on the configuration:

Install Hornet car sound software
SuSE 10.1

Add YAST repository packman.unixheads.com:/suse/updates/10.1

install ncurses, zlib, zlib-devel, libao, libao-devel, 
Readkey (TermReadkey),
(on DVD)

Install Device::SerialPort, from www.CPAN.org
from README:
cd Device*
perl Makefile.PL
sudo make install

install lame, mpg321 (packman)
(drags in id3-something, libmad)
get, install libmad, libid3tag, mpg321 from freshmeat (many
found at ftp://ftp.mars.org/pub/mpeg/)

get perl-curses from CPAN
	hornet:/home/tomj # more Curses-1.13/INSTALL.tomj 
	tomj 4 May 2006
	It's easy.

	$ cp hints/c-none.h c-config.h

	$ cd /usr/include
	$ mkdir ncurses
	$ mv curses.h ncurses.h form.h menu.h panel.h ncurses

	then do perl Makefile.PL, make, etc as per INSTALL

chmod a+rwx /dev/snd/*
chmod a+rwx /dev/mixer

hornet ALL=(ALL) NOPASSWD: /usr/bin/renice, /sbin/ifrenew, /sbin/ifconfig, /usr/sbin/iwconfig, /usr/sbin/iwlist, /bin/kill, /sbin/shutdown

do autologin (how-to/autologin)
int main() {
   execlp( "login", "login", "-f", "hornet", 0);

$ cc -o autologin autologin.c
$ mkdir -p /usr/local/sbin
$ cp autologin /usr/local/sbin

1:2345:respawn:/sbin/agetty -n -l /usr/local/sbin/autologin 38400 tty1


disable powersave's power button management


and this is probably a good default behavior until we do something else:

disable powersave (stop capturing ACPI events)

add to /boot/grub/menu.lst:
... noresume nosmp noapic vga=normal edd=off 3

User hornet's login script

This little detail ties it all together. It's fairly simple, if it determines that it is the first "console" login, it invokes the wifi and music code. (Other terminals would be ssh or local virtual console.)

The flag files are deleted for safety (radioplayer and wifilinkup create them as needed), and previous wifilink is killed off, and unless the file name STOP exists, the radioplayer starts. When radioplayer exits (power off, then power on) hornet logs out, and autologin restarts.

(For software testing, I ssh in, touch file STOP, then kill radioplayer; now I can run and test code without autologin invoking programs. I have to manually delete STOP to enable automatic functioning again.)

It seems a little crude to logout every time; it is. In fact for the first six months radioplayer simply remained running the entire time. However, this requires less code; it allows well-tested, reliable system code to terminate processes and return memory to the pool, and should be slightly more tolerant of programmer errors.

# .bashrc for hornet
# assumes autologin, starts the music
# player after a short delay.

# $Id$

# -g network-enabled on ignition-off


# (Needs quotes; ssh terminals don't have tty set.)
if [ "$tty" == "/dev/tty1" ] ; then
        killall wifilinkup
        ./Bin/wifilinkup -i rausb0 -l ~/wifi.log &

        if [ -e "STOP" ] ; then
                echo "STOP file exists"

                echo "Starting radioplayer in 3 seconds."
                sleep 3
                ./Bin/radioplayer $OPTS