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.