Geofence Application: Parsing NMEA Data

The data we saw previously is specified by the NMEA 0183 specification. We could parse this ourselves, but a far more convenient option would be to use the Arduino NMEA parsing library called TinyGPS++.

To install the TinyGPS++ library download it from the project's GitHub releases page and install the files into hardware/pic32/libraries. This project uses version 0.94b (the latest version at the time of writing), but newer versions may be compatible.

To install the library extract it to the libraries folder

Then rename it to TinyGPSPlus


Using this library we can easily extract the desired properties from the GPS's data stream. This version of the sketch can be found in commit eef28b6 or seen below.

#include <TinyGPS++.h>

  /* We want to map pins 5 and 7 to Serial2 (for RX and TX 
     respectively).

     Serial2 uses using the WiFire's UART6 and both pin 5 and 7 support
     being mapped to the relevant peripheral (U6RX and U6TX).

     Note that pin 5 is chosen (actually it is already mapped to U6RX 
     by default) as it is a 5v tolerant pin, so the UART chip connected
     may operate at 5v levels. It is also necessary that the UART chip
     accepts 3v3 as a logic HIGH as this is the logic HIGH provided by
     pin 7.

     For this example the MAX232 is used, which operates at 5v but 
     accepts 3v3 as HIGH 
   */
#define SERIAL2_RX_PIN (5)
#define SERIAL2_TX_PIN (7)

// The GPS module we will be using uses a 9600-baud RS232 connection
#define GPSBaud (9600)

// Object for the tinyGPS++ library we are using
TinyGPSPlus gps;

void setup()
{
    // use the same baud rate as the boot console so that we 
    // don't have to change the serial connection baud rate
    Serial.begin(115200);

    // We are not reading from Serial and don't mind sharing
    // it with libappbase
    g_EnableConsole = true;
    g_EnableConsoleInput = true;

    Serial.println("RS232_GPS.ino  ");

    Serial.print("Bringing up GPS serial connection... ");  

    // remap our desired RX and TX pins to U6 (Serial2)
    mapPps(SERIAL2_RX_PIN, PPS_IN_U6RX);
    mapPps(SERIAL2_TX_PIN, PPS_OUT_U6TX);
    
    Serial2.begin(GPSBaud);
    Serial.println("OK!");

    /* pin 29 is by default used for PPS_OUT_U6TX. We can 
     * leave it as this as well as pin 7, remap it to some 
     * other peripheral or we can just use it as a GPIO with
    pinMode(29, OUTPUT);
     * or 
    pinMode(29, INPUT);
     */

    Serial.println();
    Serial.println();
}

void logLocation()
{
    Serial.print("latitude: ");
    Serial.println(gps.location.lat());
    Serial.print("longitude: ");
    Serial.println(gps.location.lng());
    Serial.print("reading age: ");
    Serial.println(gps.location.age());

    Serial.print("satellites: ");
    Serial.println(gps.satellites.value());

    Serial.print("altitude: ");
    Serial.println(gps.altitude.meters());

    Serial.print("speed: ");
    Serial.println(gps.speed.mps());

    Serial.print("course: ");
    Serial.println(gps.course.deg());

    Serial.print("hdop: ");
    Serial.println(gps.hdop.value());

    Serial.println();
    Serial.println();
}

void loop()
{
    // log every 5 seconds
    #define LOGGING_PERIOD 5000
    static long lastLog = -2*LOGGING_PERIOD;

    while (Serial2.available() > 0)
    {
        if (gps.encode(Serial2.read()))
        {

            // if the configured period of time has passed then save
            // a new reading
            if (millis() - lastLog > LOGGING_PERIOD)
            {
                lastLog = millis();

                logLocation();
            }
        }
    }

    // if the sketch has been running for 5s and the GPS isn't
    // working then give up
    if (millis() > 5000 && gps.charsProcessed() < 10)
    {
        Serial.println("No GPS detected: check wiring then reset.");
        for(;;);
    }

}

We should see output similar to the following, updating every 5 seconds.

latitude: -41.21
longitude: 174.90
reading age: 3
satellites: 5
altitude: -23.40
speed: 0.00
course: 329.10
hdop: 370

While Serial.println is only showing us decimal numbers to 2 decimal places the library is able to report a greater precision than this.

Now that we have data, we can look in to how we can record this in the Flow datastore.