RPi2 with Hardware OpenGL & OpenCPN Recipe

On and off for the past few months I’ve been trying to figure out a problem with OpenGL and the Raspberry Pi 2. Quite simply I want to be able to build, from bare metal, a chartplotter system on a RPi2 that has AIS, GPS, Vector charts, and 10fps. Based on the hardware specs, it should be very possible. However, in practice, I have seen only 1-2fps.

The very good folks on cruisersforum have been hard at work trying to solve this problem, and over the past few days I resurrected this project took a stab at using the latest versions.

On a new board and SD card, the recipe below was used to create a working version of the RPi2 that meets these performance goals. With this setup, I get very usable performance with OpenGL enabled, less than 5% CPU with a satisfactorily responsive UI, USB AIS and GPS, and Vector Charts.

Hardware

  1. Raspberry Pi 2
  2. 16G micro SD card
  3. Tontec 7″ HDMI screen
  4. USB keyboard and touchpad
  5. USB WiFi Dongle
  6. USB GPS Puck
  7. dAISy USB AIS receiver

Build the Pi with a Jessie Distribution and the open source OpenGL drivers

Image the most recent version of Raspian onto a class-10 sd card. I used Raspbian Jessie, with a build date of 3-18-2016.

Once booted, connect to the internet, and then in Raspi-config:

  • Expand file system to full card
  • Change user password
  • Automatically boot to desktop
  • Set overclock to high
  • Set language, keyboard, and locale appropriately.
  • Enable the experimental GL driver in Advanced Options

Reboot, and then:
sudo apt-get upgrade
sudo apt-get update
sudo rpi-update
sudo ldconfig
sudo reboot

Install required libraries

apt-get install cmake gettext gpsd gpsd-clients libgps-dev wx-common libwxgtk3.0-dev libgtk2.0-dev wx3.0-headers libbz2-dev libtinyxml-dev portaudio19-dev libcurl4-openssl-dev libcairo2-dev mesa-utils

Build OpenCPN

git clone git://github.com/OpenCPN/OpenCPN.git
cd OpenCPN

Optionally, get the tides & currents, and high-resolution shoreline data.

  • From  http://sourceforge.net/projects/opencpnplugins/files/opencpn_packaging_data/ download the latest opencpn-gshhs and opencpn-tcdata files
  • Extract their contents into data/gshhs

mkdir build
cd build
cmake -DBUNDLE_DOCS=ON -DBUNDLE_TCDATA=ON -DBUNDLE_GSHHS=FULL ../
make
sudo make install

Download the appropriate charts:

  • For all US waters, it’s very easy to find appropriate charts at NOAA
  • I decided to try ENC vector charts this time.
  • Expand into an appropriate directory. I used /usr/local/include/Charts
  • run opencpn with the -unit_test_1 flag to ingest and process all the charts that were downloaded

Run OpenCPN and set the chart directory to wherever you’ve downloaded the NOAA charts. Exit OpenCPN and then edit .opencpn/opencpn.conf and add the following lines:

MEMCacheLimit=20
NCacheLimit=5

Setup USB

  • Start the gpsd service
    • sudo gpsd /dev/ttyUSB0 -F /var/run/gpsd.sock
  • Launch OpenCPN
  • Add a connection for the gpsd service
    • Settings -> Connections -> Add Connection
      • Network
      • GPSD protocol
      • localhost address
      • 2947 port
  • To get GPSD to run on startup automatically, modify the /etc/default/gpsd file like so:
    START_DAEMON="true"
    GPSD_OPTIONS="-n"
    DEVICES="/dev/gps0"
    USBAUTO="false"
    GPSD_SOCKET="/var/run/gpsd.sock"

Setup AIS

  • sudo apt-get install screen
    sudo usermod -a -G dialout $USER
    screen /dev/ttyACM0
  • CTRL-A, K exists screen
  • Start OpenCPN and go to Options -> Connections | Data Connections, and Add Connection. Select a Serial message, and set the DataPort to the dAISy USB message port (generally /dev/ttyACM0). Set the Baudrate to 9600, and uncheck Control checksum. Apply and return to OpenCPN.

Finalize the Setup

  • Install VNC Server so you can easily access the device from other computers on the network
    • apt-get install tightvncserver
    • tightvncserver :1
      • enter a password twice, and optionally a view-only password
      • Then you can VNC into the device from a computer running on the attached network
  • Remove extraneous packages
    • sudo apt-get  remove --purge wolfram-engine minecraft-pi libreoffice xscreensaver
  • Disable screen blanking
    • sudo nano /etc/lightdm/lightdm.conf
      [SeatDefault]
      xserver-command=X -s 0 dpms

References

AIS on the CPN Pi-lot project

First of all, what is AIS?

Automatic Identification System, or AIS for short, is a system for automatically displaying location, tracks and information about ships (and other large vessels) It works automatically, transmitting the data over standard VHF frequencies. The information provided is then generally rendered by a chartplotter or other software.

There are some very interesting websites on the web that display AIS information in near realtime. MarineTraffic.com is one of my favorites.

I wanted to add the capability to receive real-time AIS information and display it on the CPN Pi-lot project I’ve been building. Fortunately, once again, the hard work and research has already been performed by other people across the world. They have discovered that USB software defined radio dongles (SDR-RTL) have the capability to extract AIS information using cheap and readily-available hardware and software.

I recently purchased a usb dongle from Amazon that was listed to work with SDR-RTL, and wanted to see if I could get any usable data out of it.

AIS on the Raspberry Pi

I followed the instructions on a few different blogs and forums. The following is the steps I performed to get AIS around all the small gotchas:

sudo apt-get install git-core
sudo apt-get install cmake
sudo apt-get install libusb-1.0-0-dev
git clone git://git.osmocom.org/rtl-sdr.git
cd rtl-sdr
mkdir build
cd build
cmake ../ -DINSTALL_UDEV_RULES=ON
make
sudo make install
sudo ldconfig
cd ~
sudo cp ./rtl-sdr/rtl-sdr.rules /etc/udev/rules.d/
sudo reboot
rtl_test -t

At this point, the rtl_test program displayed the following error:

Kernel driver is active, or device is claimed by second instance of librtlsdr.
In the first case, please either detach or blacklist the kernel module
(dvb_usb_rtl28xxu), or enable automatic detaching at compile time.
usb_claim_interface error -6
Failed to open rtlsdr device #0.

To resolve it, I needed to edit the /etc/modprobe.d/raspi-blacklist.conf file, by adding the following lines:

blacklist dvb_usb_rtl28xxu
blacklist rtl2832
blacklist rtl2830

After a reboot, the rtl_test program confirmed that I was receiving samples from the USB dongle.

The next step was to install an AIS receiver:

sudo  apt-get  install  libasound-dev  libpulse-dev
wget  http://www.aishub.net/downloads/aisdecoder.tar.gz
tar  zxvf  aisdecoder.tar.gz
cd  aisdecoder
mkdir  build
cd  build
cmake  ../ -DCMAKE_BUILD_TYPE=Release
make

Then, open up two terminal windows. In the first terminal, type:

mkfifo /tmp/aisdata
rtl_fm -f 161975000 -g 40 -p 95 -s 48k -r 48k /tmp/aisdata

and in the second termnial, type:

./aisdecoder -h 127.0.0.1 -p 10110 -a file -c mono -d -l -f /tmp/aisdata

At this point, it appears I’m getting data flowing through, as I see the following:

Level on ch 0: 50 %
Level on ch 0: 50 %
Level on ch 0: 50 %
.
.
.

Since I am currently at home, and not within range of any ships that should be broadcasting AIS, I believe this is a success. However, I will need to bring all this gear down to the boat, test it there (where I know there are AIS signals) to confirm.

References

Chartplotter/Autopilot Design Overview

Over the past few weeks I haven’t had much opportunity to work on this project, but I have tossed around some ideas.

I have found that the project is now complicated enough that I needed to mock up a design for what I plan to do. Here’s the current design, mocked up on Fritzing :
Design

This way I can make sure that the required (and desired) components would be available without needing to add expansion boards. For example, by doing this exercise, I have discovered that a future capability – to be able to consume AIS signals – would require additional components. Most likely in the form of a powered USB hub, but possibly as additional Arduino-based devices to save on power.

They Can Talk?

The next big hurdle in this DIY chartplotter project (which I need to name, because that’s too long to type), is to get the Raspberry Pi microprocessor to communicate with the Arduino motor control unit in the stern.

After talking to a few people, including Al Thomason (who has much more experience in Arduino and microprocessors than I), I have decided that the communication architecture will be NMEA 0183 over the I2C bus. I chose NMEA 0183 is a very easy, standardized messaging protocol, with many devices supporting it. It makes sense that this project, in the end, should be able to communicate with other devices. The new NMEA 2000 format has all sorts of political and copyright issues that I don’t want to deal with until it gets figured out by others. I chose I2C because it’s only 3 wires, and you can have over 100 devices on the bus (ignoring details like capacitance, interference, and device support). With a repeater, or something similar, the distances between devices shouldn’t be a problem either.

So, with that in mind, I decided to perform a test to see if I could get the Arduino device and the Raspberry Pi device to communicate with each other. For this test, the communication is going to be “backwards” – that is, the Arduino is going to send a message to the Raspberry Pi, rather than the other way around.

Borrowing heavily from pages on the web , I wrote a simple Arduino sketch that counts from 0 to 360 and back to 0 again, every second, infinitely, to generate a fake compass heading. I then generate an NMEA $HCHDG sentence with that heading inserted – once consumed by OpenCPN, the heading should slowly rotate around the compass rose back and forth. The code for the sketch is here:


#include <Wire.h>

#define SLAVE_ADDRESS 0x04
int heading = 0;
bool increasing;
String buffer;

/*
    Setup Method
*/
void setup()
{
    //Setup Arduino for I2C Slave
    pinMode(13, OUTPUT);
    Serial.begin(9600);
    Wire.begin(SLAVE_ADDRESS);
    Wire.onReceive(receiveData);
    Wire.onRequest(sendData);

    Serial.println("I2C Configured");

    //Setup communications
    buffer = "";
    increasing = true;
    heading = 0;

}

/*
    Loop Method
*/
void loop()
{
    //Get compass heading
    getHeading();

    //Send Compass heading message across wire
    sendData();

    delay(200);
}

/*
    Method to receive data across I2C Bus
*/
void receiveData(int byteCount)
{
    while (Wire.available())
    {
        char c = (char) Wire.read();
        buffer += c;
        if (buffer.endsWith("\r\n"))
        {
            Serial.println(buffer);
            buffer = "";
        }
    }
}

/*
    Method to send data across I2C bus
*/
void sendData()
{
    Wire.beginTransmission(SLAVE_ADDRESS);
    Wire.write(getNMEA());
    Wire.endTransmission();
}

/*
    Method to return current heading as an NMEA0183 HCHDG message
*/
char *getNMEA()
{
    char nmeastr[32] = "$HCHDG";
    char intStr[8];

    strcat(nmeastr, itoa(heading, intStr, 10));
    strcat(nmeastr, ",,,,");

    //Generate checksum
    int checksum = 0;
    for(int i=0; i&lt;strlen(nmeastr); i++)
    {
        checksum ^= nmeastr[i];
    }

    strcat(nmeastr, itoa(checksum, intStr, 10));
    strcat(nmeastr, "\r\n");

    Serial.println(nmeastr);
    return nmeastr;
}

/*
    Method to get the current compass heading
*/
void getHeading()
{
    //TODO: Pull from digital magnetometer device. Currently just makes up a heading
    if (increasing)
    {
        if (heading < 360)
        {
            heading++;
        }
        else
        {
            increasing = false;
        }
    }
    else
    {
        if (heading > 0)
        {
            heading--;
        }
        else
        {
            increasing = true;
        }

    }
    Serial.print("Current Heading : ");
    Serial.println(heading);
}

As for the physical connection between the devices, I decided to go with standard CAT-5 cable. I had a couple extra breakout boards for Ethernet jacks, so I wired those up directly to the SDA and SCL lines on both devices, using only three of the 8 wires available:

IMG_20130829_104530

Now, to see if they can talk.

References:

A DIY Chartplotter

It only took two evenings to get a Raspberry Pi chart plotter built out.

Yesterday I got OpenCPN up and running following instructions found on the web, and today I finished the process to the point that the hardware is a fully functional chart plotter.

The first improvement that had to be made was getting charts installed in OpenCPN. Thankfully NOAA provides free versions of nautical charts for US Waters and most frequented coasts regionally. It was simply a matter a downloading the appropriate charts (Washington State, and Region 15, Puget Sound) and unzipping them to a folder. I then added that folder to OpenCPN using the built-in options screen, and after a few minutes of compiling, the charts displayed.

IMG_20130821_174054

Then I needed to add GPS capability. First, I hacked together a few wires to an existing Venus GPS Module that I had from a previous Arduino project. This was a simple matter of soldering 3.3v power, ground, and TX/RX digital inputs between the items:

IMG_20130822_181042

Then I installed the GPSD service from the web, run the service, create a socket for it, and verify that the hardware is receiving the signal. Currently I need to hand-type this sequence each time I reboot the microcomputer: 
$ sudo service gpsd restart
$ sudo killall gpsd
$ sudo gpsd /dev/ttyAMA0 -F /var/run/gpsd.sock

Eventually this will be added to a script that will run on boot.

And then to test: 
$ sudo cgps
or
$ sudo xgps

And behold, signals from outer space:

IMG_20130821_191632

Now that all the pieces are in place, the final step is to add this GPS device to OpenCPN so that it can read the information:

IMG_20130822_180807

In this picture you should be able to see a rendered nautical chart of Puget sound, Heading, GPS satellite status, GPS location, and  Compass gauges on the left, and my “boat”, sitting off in Issaquah, since that’s where my GPS device is located.

It’s running at about 80% of the Raspberry Pi’s capability, at 1280×1024 with no overclocking.

And boom goes the dynamite.

It’s ALIVE!

My Raspberry Pi board showed up in the mail today. After a few minutes downloading and expanding the operating system, I got the machine up and running:

IMG_20130821_132454

Thanks to the very helpful walkthrough/tutorial at http://www.agurney.com/raspberry-pi/pi-chart , I now have version 3.2.0 of OpenCPN running on a brand new Raspberry Pi Model B. At this point I just have it up and running… No charts have been installed, GPS is not hooked up, there are no plugins installed, etc. But OpenCPN is clearly running:

IMG_20130821_143807

Meanwhile, on the Arduino side of the system, I was playing around with a 128×64 Graphic LCD screen that I had laying around. I plugged it in and found a library written for it that worked with very few changes. I doubt that the Arduino will actually drive an LCD, as it’s not in a convenient location to do so (down near the rudder post), but it was good to see that it worked regardless.

IMG_20130821_145943

Homebuilt Integrated Chartplotter and Autopilot

I’m going to try something bigger next.

My plan is to build an integrated chart plotter with a microprocessor controlled autopilot using open sourced hardware and software. That means, in the end, I should spend less than $500, total, for a fully functional autopilot and chart plotter.

Chart plotter Parts List

  1. Raspberry Pi
  2. OpenCPN
  3. GPS Module
  4. 10″ Display (by far the most expensive item)
  5. LCD display
  6. Controls (buttons, etc)
  7. Case/mounting

Autopilot Parts List

  1. Arduino ATMega 328
  2. Relay board
  3. Ethernet board
  4. Digital Compass
  5. Ethernet crossover cable

As far as implementation is concerned, at this point I have only done some basic research and tests.

I have already hooked up the existing hydraulic pump directly to a 12v source, as you can see below:

The next steps include:

  1. Installing OpenCPN on a Raspberry Pi device and getting that to run. Once installed on the boat, just that will be a nice addition, since I currently use a small PC hooked up to the inverter to power OpenCPN on board.
  2. Build an install a relay shield on my Arduino, and code it to keep a compass heading (I already have a 3-axis compass breakout).
  3. Build and add an Ethernet shield to my Arduino, and get it to communicate with Raspberry Pi
  4. Add a rudder angle sensor to the Arduino module, and display the information on the LCD screen in the cockpit
  5. Extract the NMEA messages (crosstrack, etc) from OpenCPN, and set that as the heading to hold on the Arduino module
  6. Have a drink and let the boat drive itself

There are of course additional features I am planning:

  • Configurable alarm to remind the “helmsman” to do a safety sweep
  • Connect GPS output to VHF radio for DSC service
  • Engine sensors (RPM, temperature, etc)
  • Cameras
  • TBD