Serial on Raspberry Pi Arch Linux

So the new version of Arch Linux doesn’t have runlevels, rc.d or any of that nonsense any more. It just has systemd. Super simple if you know how to use it, but a right pain in the backside if you don’t.

I have a little serial GPS module hooked up to my Raspberry Pi via the hardware serial port (ttyAMA0). My old instructions for getting this to work aren’t much use any more. Here’s the new procedure for getting serial data with the minimum of fuss:

1. Disable serial output during boot

Edit /boot/cmdline.txt using your favourite editor. I like nano these days.

sudo nano /boot/cmdline.txt

Remove all chunks of text that mention ttyAMA0 but leave the rest of the line intact. Bits to remove look like:

console=ttyAMA0,115200 kgdboc=ttyAMA0,115200

2. Disable the console on the serial port

This was the new bit for me. The process used to involve commenting out a line in /etc/innitab but that file is long gone.

Systemd uses links in /etc to decide what to start up, so once you find the right one, removing it is easy. You can find the files associated with consoles by doing:

ls /etc/systemd/system/getty.target.wants/

One of the entries clearly refers to ttyAMA0. It can be removed using the following command:

sudo systemd disable serial-getty@ttyAMA0.service

3. Check you’re getting data

I used minicom for this as it’s very simple to use. First of all, make sure you plug in your device (with the power off, if you’re as clumsy as me!).

sudo pacman -S minicom
minicom -b 4800 -o -D /dev/ttyAMA0

You should see a lovely stream of data. I my case it was a screen full of NMEA sentences. Great stuff!

WiFi, Raspberry Pi, My oh my

Downloaded and installed the latest Arch Linux image for the Raspberry Pi today, only to find that the whole world has changed since I last looked at wireless connectivity!

There’s no rc.d any more, there’s a totally new way to manage networks (wireless and otherwise) and everything I thought I knew is wrong. I think I’m getting old!

After hours of faffing about, it turns out I could have had this nailed with two very simple commands.

Assuming you have a recent Arch Linux, I think you just need to:

1. Use the GUI tool to connect to your WiFi

sudo wifi-menu -o

Select the right network and enter the password when prompted. When you exit the application you should be connected to your router and the tool will have saved a config file to /etc/netctl/wlan0-YourNetwork

2. Change to a static IP address

Weirdly, the above step fails first time with a dhcp error. It saves a valid config file but doesn’t manage to connect. It seems to want to bring up dhcp against eth0 not wlan0 as I’d expect/prefer. Didn’t manage to find out why this is or how to fix it (yet) so just swapped to a static IP!

Use your favorite editor (I like nano) to change the config file just generated removing…

IP=dhcp

…and adding this in it’s place…

IP=static
Address=('192.168.1.188/24')
Gateway=('192.168.1.254')
DNS=('8.8.8.8')

After you’ve done that, repeat step 1 and proceed to step 3. Note that when you run wifi-menu the second time it will show that there is an existing config for your network. You might also find that step 1 works and you don’t need to do step 2 at all, because you might have downloaded a fixed Arch image. In which case, I salute your fortitude!

3. Install the config

Since you probably want to reconnect the wireless after reboots, you need to install the config using the funky new “netctl” tool.

sudo netctl enable wlan0-YourNetwork

Now you should have a working wireless connection after reboots and a raspberry pi that works like your old one did!

Rant: Who moved my cheese?

People always moan about Windows versions that are different to old Windows versions – why Windows 8 took away the start button, why Windows Vista did security totally differently, blah blah blah…

Today I spent several hours digging around looking for something that should be easy, only to find it is indeed very easy… when you know how.

So today I’m quite happy to argue that Linux does exactly the same thing as Windows. I like Arch Linux a lot, and I accept that it’s not for newbies, but things change as often in Linux world as they do in Windows world. On both sides of the fence, Google is your only hope if you want to keep up to date with the changing software that underpins everything you do.

The ASCII Speedometer

Once I’d got Mono up and running, the first little project I did with the Raspberry Pi was to hook up an old GPS module and use it to create a text based speedometer for the car.  It was the first step I took towards making The Duke sentient building an on-board computer for my Land Rover.  I was fun to do and raised a smile with the people who I told about it, so I thought I’d bung the details online.

The Hardware Bit

First step is to get the serial port working.  If you have a USB GPS module then it’s just a case of plugging it in, but mine is a 3.3v logic-level serial module that I bought about eight years ago to use with my Gumstix in a linux-powered walk logger for a couple of big charity walks we did back in 2004 and 2006.  It’s safe to say that hooking it up to the Raspberry Pi was a much simpler affair!

There are some great guides out there about soldering up the right pins (here and here).  I hooked up 3.3v power, ground and connected TX on the GPS to RX on the Raspberry Pi.  I used a little bit of veroboard, a 0.1″ header, some ribbon cable and made the GPS pluggable.

Out of the box the Raspberry has a console running on the serial port.  You need to disable this before you can do anything with the port.  Very easy to do: Edit /etc/inittab and remove the line that refers to /dev/ttyAMA0;  Edit /boot/cmdline.txt and remove the chunks of text that refer to /dev/ttyAMA0.  After a reboot the terminal will be gone.

Reading from the Serial Port in Mono

Right, now we’re ready to write some code.  First off I wrote this simple bit of C# to test that I was getting messages. The ReadData method reads text from the serial port one character at a time, detecting end of line characters to return a string for each line. The main method loops forever reading these lines and printing them to the console if they start with the NMEA “Recommended Minimum Content” message $GPRMC.

using System;
using System.IO.Ports;

public class Serial
{
   public static void Main()
   {
      SerialPort serial = new SerialPort("/dev/ttyAMA0", 4800);
      serial.Open();
      serial.ReadTimeout = 1000;

      while(true)
      {
         string data = ReadData(serial);
         if(!string.IsNullOrEmpty(data) && data.StartsWith("$GPRMC"))
         {
             Console.WriteLine(data);
         }
      }
   }

   public static string ReadData(SerialPort serial)
   {
      byte tmpByte;
      string rxString = "";

      do
      {
         tmpByte = (byte) serial.ReadByte();
         rxString += ((char) tmpByte);
         tmpByte = (byte) serial.ReadByte();
      }while (tmpByte != 13 && tmpByte != 10);

      return rxString.Trim();
   }
}

Parsing the NMEA Data

I then did a bit of a Test Driven Development exercise to write a proper parser for NMEA messages. To do this in a test driven way I got my hands on some data files containing raw NMEA data and used that to create a Mock serial port reader. I could then pass these messages through my parser and test that I had managed to extract the right data.

Unit testing and using mocks was a great way to develop this part of the application. I could use recorded routes with real movement to test the parsing of speed data – since coding in a moving car seemed silly. I could also do all the coding work in Visual Studio on my Windows machine. This meant I could make the most of a nice big screen, code completion, resharper’s excellent testing interface and so on, then just push the code onto the Pi when it was done; I didn’t have to worry that “/dev/ttyAMA0” is “COM3” in Windows land, because I wasn’t using a real serial port to do 99% of the development.

A typical test for parsing of individual messages (hand typed!):

[TestCase("$GPRMC,005959,V,4807.038,N,11130.00,E,022.4,084.4,010101,003.1,W*4E", 48.1173, 111.5)]
public void CanParseLatLongFromRmcMessage(string input, double expectedLat, double expectedLong)
{
    NmeaParser parser = new NmeaParser();
    GpsMeasurement measurement = parser.Parse(input);

    Assert.That(measurement.Latitude, Is.EqualTo(expectedLat));
    Assert.That(measurement.Longitude, Is.EqualTo(expectedLong));
}

The mock serial reader class:

public class MockSerialPortReader : IPortReader
{
    private readonly string filename;
    private readonly int sleep;

    public MockSerialPortReader(string filename, int sleep)
    {
        this.filename = filename;
        this.sleep = sleep;
    }

    public IEnumerable Lines
    {
        get
        {
            foreach (string line in File.ReadAllLines(filename))
            {
                Thread.Sleep(sleep);
                yield return line;
            }
        }
    }
}

A typical unit test using the mock reader:

[Test]
public void CanGetMeasurementsFromMockReaderDataSet1()
{
    NmeaParser parser = new NmeaParser();

    IEnumerable measurements = parser.ParseFrom(new MockSerialPortReader(dataSet1, 0));
    Assert.That(measurements, Is.Not.Null);
    CollectionAssert.IsNotEmpty(measurements);
    CollectionAssert.AllItemsAreInstancesOfType(measurements, typeof(GpsMeasurement));
    CollectionAssert.AllItemsAreNotNull(measurements);

    Console.WriteLine("{0}, {1}", measurements.Last().Latitude, measurements.Last().Longitude);
}

Displaying the Speedo Text

The final stage of this little mini-project was to knock up a user interface. I spent a while looking at how to get something working under X Windows, then decided to go back to the Old School and just use ASCII art.

First thing was to find a quick and dirty way to define how each big number would look. Each number is made up of a grid of characters, defined in a class:

public class TextConstants
{
    public static readonly string[] Zero = new[]
        {
            "   000000",
            "  00000000",
            " 000    000",
            " 000    000",
            " 000    000",
            " 000    000",
            "  00000000",
            "   000000"
        };

   ...etc etc...
}

Writing this out then becomes an exercise in text placement:

public void DrawSpeed(int speed)
{
    Console.Clear();

    DrawOutline();

    char[] asciiSpeed = speed.ToString("00").ToCharArray();

    int xOffset = 20;
    foreach (char c in asciiSpeed)
    {
        DrawNumber(TextConstants.For(c), xOffset, 8);
        xOffset += 15;
    }
}

private void DrawNumber(IEnumerable lines, int xOffset, int yOffset)
{
    int lineNo = 0;
    foreach (string line in lines)
    {
        WriteAt(line, xOffset, yOffset + lineNo);
        lineNo++;
    }
}

Did it Work?

Well, yes! The main issue was the update speed. This is because the old GPS module outputs data very very slowly and has quite a slow refresh rate. As a speedometer it wasn’t much good – it generally showed the speed I was doing about 15 seconds ago. As a project it worked brilliantly though.

I extended the code slightly to add some logging. This saved the location data to a set of simple, size-limited, CSV files on the Pi’s flash card. I then knocked up some more code to turn the measurements into a “Trip Report” using the Google Maps API. Top Notch!

Starting sakis3g from an rc.d script

Have been working on The Duke’s brain today.  Needed to get the mobile internet connection to start automatically when the Raspberry Pi boots.  I’m using a Huawei E220 dongle with a Giff Gaff SIM. The easiest way to get it connected to the internet is to use the fantastic sakis3g script.

The best way to get anything to run at startup is with an rc script, but even after searching for some time I couldn’t find any examples.  So here’s one I crafted myself, in case anyone finds it useful. Obviously you’ll have to change the “ARGS” line to set the correct APN and so on for your connection.  You’ll also need to copy the sakis3g script itself to /usr/sbin.

/etc/rc.d/sakis:

#!/bin/bash

. /etc/rc.conf
. /etc/rc.d/functions

ARGS="APN=\"CUSTOM_APN\" CUSTOM_APN=\"giffgaff.com\" FORCE_APN=\"giffgaff.com\" APN_USER=\"giffgaff\" APN_PASS=\"password\""

PID=$(get_pid sakis)

case "$1" in
 start)
   stat_busy "Starting sakis"
   [ -z "$PID" ] && /usr/sbin/sakis3g connect $ARGS &>/dev/null
   if [ $? = 0 ]; then
     add_daemon sakis
     stat_done
   else
     stat_fail
     exit 1
   fi
   ;;
 stop)
   stat_busy "Stopping sakis"
   /usr/sbin/sakis3g disconnect &>/dev/null
   if [ $? = 0 ]; then
     rm_daemon sakis
     stat_done
   else
     stat_fail
     exit 1
   fi
   ;;
 restart)
   $0 stop
   sleep 1
   $0 start
   ;;
 *)
   echo "usage: $0 {start|stop|restart}"
esac

This can be started from rc.conf by adding “sakis” toi your daemons line.

/etc/rc.conf

DAEMONS=(!hwclock syslog-ng @network @net-profiles @dhcpd @openntpd @netfs @crond @sshd @samba @sakis)

There are some downsides to using sakis3g for connecting to the internet though – especially for my project.  I have both a 3g and a WiFi dongle attached to the RaspberryPi, but sakis sets up the routes in such a way once you’re connected via the mobile network, you always route that way.  Even though the wireless is faster, when I run a package upgrade or download a file it comes over 3g, not WiFi.  Worse than that, if you stop sakis, it doesn’t restore your routes, so you can’t connect to the internet at all without issuing some route commands.