Skip to content

Instantly share code, notes, and snippets.

@dmiddlecamp
Last active April 21, 2018 05:07
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save dmiddlecamp/6460ea2109826141948c to your computer and use it in GitHub Desktop.
Save dmiddlecamp/6460ea2109826141948c to your computer and use it in GitHub Desktop.
Spark GPS demo with Adafruit Ultimate GPS breakout
/***********************************
This is our GPS library
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, check license.txt for more information
All text above must be included in any redistribution
****************************************/
#include "Adafruit_GPS.h"
#include "math.h"
#include <ctype.h>
// how long are max NMEA lines to parse?
#define MAXLINELENGTH 120
// we double buffer: read one line in and leave one for the main program
volatile char line1[MAXLINELENGTH];
volatile char line2[MAXLINELENGTH];
// our index into filling the current line
volatile uint8_t lineidx=0;
// pointers to the double buffers
volatile char *currentline;
volatile char *lastline;
volatile boolean recvdflag;
volatile boolean inStandbyMode;
boolean Adafruit_GPS::parse(char *nmea) {
// do checksum check
// first look if we even have one
if (nmea[strlen(nmea)-4] == '*') {
uint16_t sum = parseHex(nmea[strlen(nmea)-3]) * 16;
sum += parseHex(nmea[strlen(nmea)-2]);
// check checksum
for (uint8_t i=1; i < (strlen(nmea)-4); i++) {
sum ^= nmea[i];
}
if (sum != 0) {
// bad checksum :(
//return false;
Spark.publish("GPS", "{ error: \"bad checksum\"}", 60, PRIVATE );
}
}
// look for a few common sentences
if (strstr(nmea, "$GPGGA")) {
// found GGA
char *p = nmea;
// get time
p = strchr(p, ',')+1;
float timef = atof(p);
uint32_t time = timef;
hour = time / 10000;
minute = (time % 10000) / 100;
seconds = (time % 100);
milliseconds = fmod(timef, 1.0) * 1000;
// parse out latitude
p = strchr(p, ',')+1;
latitude = atof(p);
p = strchr(p, ',')+1;
if (p[0] == 'N') lat = 'N';
else if (p[0] == 'S') lat = 'S';
else if (p[0] == ',') lat = 0;
else return false;
// parse out longitude
p = strchr(p, ',')+1;
longitude = atof(p);
p = strchr(p, ',')+1;
if (p[0] == 'W') lon = 'W';
else if (p[0] == 'E') lon = 'E';
else if (p[0] == ',') lon = 0;
else return false;
p = strchr(p, ',')+1;
fixquality = atoi(p);
p = strchr(p, ',')+1;
satellites = atoi(p);
p = strchr(p, ',')+1;
HDOP = atof(p);
p = strchr(p, ',')+1;
altitude = atof(p);
p = strchr(p, ',')+1;
p = strchr(p, ',')+1;
geoidheight = atof(p);
return true;
}
if (strstr(nmea, "$GPRMC")) {
// found RMC
char *p = nmea;
// get time
p = strchr(p, ',')+1;
float timef = atof(p);
uint32_t time = timef;
hour = time / 10000;
minute = (time % 10000) / 100;
seconds = (time % 100);
milliseconds = fmod(timef, 1.0) * 1000;
p = strchr(p, ',')+1;
// Serial.println(p);
if (p[0] == 'A')
fix = true;
else if (p[0] == 'V')
fix = false;
else
return false;
// parse out latitude
p = strchr(p, ',')+1;
latitude = atof(p);
p = strchr(p, ',')+1;
if (p[0] == 'N') lat = 'N';
else if (p[0] == 'S') lat = 'S';
else if (p[0] == ',') lat = 0;
else return false;
// parse out longitude
p = strchr(p, ',')+1;
longitude = atof(p);
p = strchr(p, ',')+1;
if (p[0] == 'W') lon = 'W';
else if (p[0] == 'E') lon = 'E';
else if (p[0] == ',') lon = 0;
else return false;
// speed
p = strchr(p, ',')+1;
speed = atof(p);
// angle
p = strchr(p, ',')+1;
angle = atof(p);
p = strchr(p, ',')+1;
uint32_t fulldate = atof(p);
day = fulldate / 10000;
month = (fulldate % 10000) / 100;
year = (fulldate % 100);
// we dont parse the remaining, yet!
return true;
}
return false;
}
char Adafruit_GPS::read(void) {
char c = 0;
if (paused) return c;
#ifdef __AVR__
if(gpsSwSerial) {
if(!gpsSwSerial->available()) return c;
c = gpsSwSerial->read();
} else
#endif
{
if(!gpsHwSerial->available()) return c;
c = gpsHwSerial->read();
}
//Serial.print(c);
if (c == '$') {
currentline[lineidx] = 0;
lineidx = 0;
}
if (c == '\n') {
currentline[lineidx] = 0;
if (currentline == line1) {
currentline = line2;
lastline = line1;
} else {
currentline = line1;
lastline = line2;
}
//Serial.println("----");
//Serial.println((char *)lastline);
//Serial.println("----");
lineidx = 0;
recvdflag = true;
}
currentline[lineidx++] = c;
if (lineidx >= MAXLINELENGTH)
lineidx = MAXLINELENGTH-1;
return c;
}
#ifdef __AVR__
// Constructor when using SoftwareSerial or NewSoftSerial
#if ARDUINO >= 100
Adafruit_GPS::Adafruit_GPS(SoftwareSerial *ser)
#else
Adafruit_GPS::Adafruit_GPS(NewSoftSerial *ser)
#endif
{
common_init(); // Set everything to common state, then...
gpsSwSerial = ser; // ...override gpsSwSerial with value passed.
}
#endif
// Constructor when using HardwareSerial
Adafruit_GPS::Adafruit_GPS(Stream *ser) {
common_init(); // Set everything to common state, then...
gpsHwSerial = ser; // ...override gpsHwSerial with value passed.
}
// Initialization code used by all constructor types
void Adafruit_GPS::common_init(void) {
#ifdef __AVR__
gpsSwSerial = NULL; // Set both to NULL, then override correct
#endif
gpsHwSerial = NULL; // port pointer in corresponding constructor
recvdflag = false;
paused = false;
lineidx = 0;
currentline = line1;
lastline = line2;
hour = minute = seconds = year = month = day =
fixquality = satellites = 0; // uint8_t
lat = lon = mag = 0; // char
fix = false; // boolean
milliseconds = 0; // uint16_t
latitude = longitude = geoidheight = altitude =
speed = angle = magvariation = HDOP = 0.0; // float
}
void Adafruit_GPS::begin(uint16_t baud)
{
#ifdef __AVR__
if(gpsSwSerial)
gpsSwSerial->begin(baud);
else
gpsHwSerial->begin(baud);
#endif
delay(10);
}
void Adafruit_GPS::sendCommand(char *str) {
#ifdef __AVR__
if(gpsSwSerial)
gpsSwSerial->println(str);
else
#endif
gpsHwSerial->println(str);
}
boolean Adafruit_GPS::newNMEAreceived(void) {
return recvdflag;
}
void Adafruit_GPS::pause(boolean p) {
paused = p;
}
char *Adafruit_GPS::lastNMEA(void) {
recvdflag = false;
return (char *)lastline;
}
// read a Hex value and return the decimal equivalent
uint8_t Adafruit_GPS::parseHex(char c) {
if (c < '0')
return 0;
if (c <= '9')
return c - '0';
if (c < 'A')
return 0;
if (c <= 'F')
return (c - 'A')+10;
return 0;
}
boolean Adafruit_GPS::waitForSentence(char *wait4me, uint8_t max) {
char str[20];
uint8_t i=0;
while (i < max) {
if (newNMEAreceived()) {
char *nmea = lastNMEA();
strncpy(str, nmea, 20);
str[19] = 0;
i++;
if (strstr(str, wait4me))
return true;
}
}
return false;
}
boolean Adafruit_GPS::LOCUS_StartLogger(void) {
sendCommand(PMTK_LOCUS_STARTLOG);
recvdflag = false;
return waitForSentence(PMTK_LOCUS_LOGSTARTED);
}
boolean Adafruit_GPS::LOCUS_ReadStatus(void) {
sendCommand(PMTK_LOCUS_QUERY_STATUS);
if (! waitForSentence("$PMTKLOG"))
return false;
char *response = lastNMEA();
uint16_t parsed[10];
uint8_t i;
for (i=0; i<10; i++) parsed[i] = -1;
response = strchr(response, ',');
for (i=0; i<10; i++) {
if (!response || (response[0] == 0) || (response[0] == '*'))
break;
response++;
parsed[i]=0;
while ((response[0] != ',') && (response[0] != '*') && (response[0] != 0))
{
parsed[i] *= 10;
char c = response[0];
if (isdigit(c))
parsed[i] += c - '0';
else
parsed[i] = c;
response++;
}
}
LOCUS_serial = parsed[0];
LOCUS_type = parsed[1];
if (isalpha(parsed[2])) {
parsed[2] = parsed[2] - 'a' + 10;
}
LOCUS_mode = parsed[2];
LOCUS_config = parsed[3];
LOCUS_interval = parsed[4];
LOCUS_distance = parsed[5];
LOCUS_speed = parsed[6];
LOCUS_status = !parsed[7];
LOCUS_records = parsed[8];
LOCUS_percent = parsed[9];
return true;
}
// Standby Mode Switches
boolean Adafruit_GPS::standby(void) {
if (inStandbyMode) {
return false; // Returns false if already in standby mode, so that you do not wake it up by sending commands to GPS
}
else {
inStandbyMode = true;
sendCommand(PMTK_STANDBY);
//return waitForSentence(PMTK_STANDBY_SUCCESS); // don't seem to be fast enough to catch the message, or something else just is not working
return true;
}
}
boolean Adafruit_GPS::wakeup(void) {
if (inStandbyMode) {
inStandbyMode = false;
sendCommand(""); // send byte to wake it up
return waitForSentence(PMTK_AWAKE);
}
else {
return false; // Returns false if not in standby mode, nothing to wakeup
}
}
/***********************************
This is the Adafruit GPS library - the ultimate GPS library
for the ultimate GPS module!
Tested and works great with the Adafruit Ultimate GPS module
using MTK33x9 chipset
------> http://www.adafruit.com/products/746
Pick one up today at the Adafruit electronics shop
and help support open source hardware & software! -ada
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, check license.txt for more information
All text above must be included in any redistribution
****************************************/
#ifndef _ADAFRUIT_GPS_H
#define _ADAFRUIT_GPS_H
#ifdef __AVR__
#if ARDUINO >= 100
#include <SoftwareSerial.h>
#else
#include <NewSoftSerial.h>
#endif
#endif
// different commands to set the update rate from once a second (1 Hz) to 10 times a second (10Hz)
#define PMTK_SET_NMEA_UPDATE_1HZ "$PMTK220,1000*1F"
#define PMTK_SET_NMEA_UPDATE_5HZ "$PMTK220,200*2C"
#define PMTK_SET_NMEA_UPDATE_10HZ "$PMTK220,100*2F"
#define PMTK_SET_BAUD_57600 "$PMTK251,57600*2C"
#define PMTK_SET_BAUD_9600 "$PMTK251,9600*17"
// turn on only the second sentence (GPRMC)
#define PMTK_SET_NMEA_OUTPUT_RMCONLY "$PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29"
// turn on GPRMC and GGA
#define PMTK_SET_NMEA_OUTPUT_RMCGGA "$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28"
// turn on ALL THE DATA
#define PMTK_SET_NMEA_OUTPUT_ALLDATA "$PMTK314,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0*28"
// turn off output
#define PMTK_SET_NMEA_OUTPUT_OFF "$PMTK314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28"
// to generate your own sentences, check out the MTK command datasheet and use a checksum calculator
// such as the awesome http://www.hhhh.org/wiml/proj/nmeaxor.html
#define PMTK_LOCUS_STARTLOG "$PMTK185,0*22"
#define PMTK_LOCUS_LOGSTARTED "$PMTK001,185,3*3C"
#define PMTK_LOCUS_QUERY_STATUS "$PMTK183*38"
#define PMTK_LOCUS_ERASE_FLASH "$PMTK184,1*22"
#define LOCUS_OVERLAP 0
#define LOCUS_FULLSTOP 1
// standby command & boot successful message
#define PMTK_STANDBY "$PMTK161,0*28"
#define PMTK_STANDBY_SUCCESS "$PMTK001,161,3*36" // Not needed currently
#define PMTK_AWAKE "$PMTK010,002*2D"
// ask for the release and version
#define PMTK_Q_RELEASE "$PMTK605*31"
// request for updates on antenna status
#define PGCMD_ANTENNA "$PGCMD,33,1*6C"
#define PGCMD_NOANTENNA "$PGCMD,33,0*6C"
// how long to wait when we're looking for a response
#define MAXWAITSENTENCE 5
// #if ARDUINO >= 100
// #include "Arduino.h"
// #if defined (__AVR__) && !defined(__AVR_ATmega32U4__)
// #include "SoftwareSerial.h"
// #endif
// #else
// #include "WProgram.h"
// #include "NewSoftSerial.h"
// #endif
#include "application.h"
class Adafruit_GPS {
public:
void begin(uint16_t baud);
// #ifdef __AVR__
// #if ARDUINO >= 100
// Adafruit_GPS(SoftwareSerial *ser); // Constructor when using SoftwareSerial
// #else
// Adafruit_GPS(Serial *ser); // Constructor when using NewSoftSerial
// #endif
// #endif
Adafruit_GPS(Stream *ser); // Constructor when using HardwareSerial
char *lastNMEA(void);
boolean newNMEAreceived();
void common_init(void);
void sendCommand(char *);
void pause(boolean b);
boolean parseNMEA(char *response);
uint8_t parseHex(char c);
char read(void);
boolean parse(char *);
void interruptReads(boolean r);
boolean wakeup(void);
boolean standby(void);
uint8_t hour, minute, seconds, year, month, day;
uint16_t milliseconds;
float latitude, longitude, geoidheight, altitude;
float speed, angle, magvariation, HDOP;
char lat, lon, mag;
boolean fix;
uint8_t fixquality, satellites;
boolean waitForSentence(char *wait, uint8_t max = MAXWAITSENTENCE);
boolean LOCUS_StartLogger(void);
boolean LOCUS_ReadStatus(void);
uint16_t LOCUS_serial, LOCUS_records;
uint8_t LOCUS_type, LOCUS_mode, LOCUS_config, LOCUS_interval, LOCUS_distance, LOCUS_speed, LOCUS_status, LOCUS_percent;
private:
boolean paused;
uint8_t parseResponse(char *response);
// #ifdef __AVR__
// #if ARDUINO >= 100
// SoftwareSerial *gpsSwSerial;
// #else
// NewSoftSerial *gpsSwSerial;
// #endif
// #endif
Stream *gpsHwSerial;
};
#endif
// This #include statement was automatically added by the Spark IDE.
#include "Adafruit_GPS.h"
#include <math.h>
#define mySerial Serial1
Adafruit_GPS GPS(&mySerial);
// Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console
// Set to 'true' if you want to debug and listen to the raw GPS sentences.
#define GPSECHO false
// this keeps track of whether we're using the interrupt
// off by default!
boolean usingInterrupt = false;
#define APP_VERSION 10
byte bufferSize = 64;
byte bufferIndex = 0;
char buffer[65];
char c;
uint32_t timer;
void setup() {
// 9600 NMEA is the default baud rate for Adafruit MTK GPS's- some use 4800
GPS.begin(9600);
mySerial.begin(9600);
Serial.begin(115200);
// uncomment this line to turn on RMC (recommended minimum) and GGA (fix data) including altitude
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
// Set the update rate
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate
// For the parsing code to work nicely and have time to sort thru the data, and
// print it out we don't suggest using anything higher than 1 Hz
// Request updates on antenna status, comment out to keep quiet
GPS.sendCommand(PGCMD_NOANTENNA);
// the nice thing about this code is you can have a timer0 interrupt go off
// every 1 millisecond, and read data from the GPS for you. that makes the
// loop code a heck of a lot easier!
delay(1000);
// Ask for firmware version
mySerial.println(PMTK_Q_RELEASE);
timer = millis();
Spark.publish("GPS", "{ status: \"started up! "+String(APP_VERSION)+"\"}", 60, PRIVATE );
IPAddress myIP = WiFi.localIP();
Spark.publish("MY_IP",
String(myIP[0]) + "." + String(myIP[1]) + "." + String(myIP[2]) + "." + String(myIP[3]),
60, PRIVATE );
}
void loop() {
// in case you are not using the interrupt above, you'll
// need to 'hand query' the GPS, not suggested :(
if (! usingInterrupt) {
// read data from the GPS in the 'main loop'
char c = GPS.read();
// if you want to debug, this is a good time to do it!
if (GPSECHO)
if (c) Serial.print(c);
}
// if a sentence is received, we can check the checksum, parse it...
if (GPS.newNMEAreceived()) {
// a tricky thing here is if we print the NMEA sentence, or data
// we end up not listening and catching other sentences!
// so be very wary if using OUTPUT_ALLDATA and trytng to print out data
//Serial.println(GPS.lastNMEA()); // this also sets the newNMEAreceived() flag to false
if (!GPS.parse(GPS.lastNMEA())) {
// this also sets the newNMEAreceived() flag to false
if (millis() - timer > 10000) {
Spark.publish("GPS", "{ last: \""+String(GPS.lastNMEA())+"\"}", 60, PRIVATE );
Spark.publish("GPS", "{ error: \"failed to parse\"}", 60, PRIVATE );
}
return; // we can fail to parse a sentence in which case we should just wait for another
}
}
// if millis() or timer wraps around, we'll just reset it
if (timer > millis()) timer = millis();
// approximately every 2 seconds or so, print out the current stats
if (millis() - timer > 10000) {
timer = millis(); // reset the timer
Serial.print("\nTime: ");
Serial.print(GPS.hour, DEC); Serial.print(':');
Serial.print(GPS.minute, DEC); Serial.print(':');
Serial.print(GPS.seconds, DEC); Serial.print('.');
Serial.println(GPS.milliseconds);
Serial.print("Date: ");
Serial.print(GPS.day, DEC); Serial.print('/');
Serial.print(GPS.month, DEC); Serial.print("/20");
Serial.println(GPS.year, DEC);
Serial.print("Fix: "); Serial.print((int)GPS.fix);
Serial.print(" quality: "); Serial.println((int)GPS.fixquality);
//if (GPS.fix)
//{
Serial.print("Location: ");
Serial.print(GPS.latitude, 4);
Serial.print(GPS.lat);
Serial.print(", ");
Serial.print(GPS.longitude, 4);
Serial.println(GPS.lon);
Serial.print("Speed (knots): "); Serial.println(GPS.speed);
Serial.print("Angle: "); Serial.println(GPS.angle);
Serial.print("Altitude: "); Serial.println(GPS.altitude);
Serial.print("Satellites: "); Serial.println((int)GPS.satellites);
//TODO: BETTER +/- for longitude
Spark.publish("GPS",
"{lat: " + String(convertDegMinToDecDeg(GPS.latitude))
+ ", lon: -" + String(convertDegMinToDecDeg(GPS.longitude))
+ ", a: " + String(GPS.altitude)
+ " }",
60, PRIVATE
);
Spark.publish("GPS",
+ "{ q: " + String(GPS.fixquality)
+ ", s: " + String((int)GPS.satellites)
+ " }",
60, PRIVATE
);
//}
//else
//{
Spark.publish("GPS_RAW", String(GPS.lastNMEA()), 60, PRIVATE );
//}
}
}
//http://arduinodev.woofex.net/2013/02/06/adafruit_gps_forma/
double convertDegMinToDecDeg (float degMin) {
double min = 0.0;
double decDeg = 0.0;
//get the minutes, fmod() requires double
min = fmod((double)degMin, 100.0);
//rebuild coordinates in decimal degrees
degMin = (int) ( degMin / 100 );
decDeg = degMin + ( min / 60 );
return decDeg;
}
@zomerfeld
Copy link

Is this something you're going to submit to the particle web IDE?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment