-
-
Save jonmagic/1e41bbc9918e1e1031b487777deaee1a to your computer and use it in GitHub Desktop.
Spark GPS demo with Adafruit Ultimate GPS breakout
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*********************************** | |
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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*********************************** | |
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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment