esp32_Adafruit_GPS/Adafruit_GPS.cpp

717 lines
20 KiB
C++
Raw Normal View History

/**************************************************************************/
/*!
@file Adafruit_GPS.cpp
2012-03-27 22:56:07 +01:00
@mainpage Adafruit Ultimate GPS Breakout
@section intro Introduction
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
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
@section author Author
Written by Limor Fried/Ladyada for Adafruit Industries.
@section license License
BSD license, check license.txt for more information
All text above must be included in any redistribution
*/
/**************************************************************************/
2012-03-27 22:56:07 +01:00
#if (defined(__AVR__) || defined(ESP8266)) && defined(USE_SW_SERIAL)
// Only include software serial on AVR platforms and ESP8266 (i.e. not on Due).
#include <SoftwareSerial.h>
#endif
2012-03-27 22:56:07 +01:00
#include <Adafruit_GPS.h>
#define MAXLINELENGTH 120 ///< how long are max NMEA lines to parse?
static boolean strStartsWith(const char* str, const char* prefix);
volatile char line1[MAXLINELENGTH]; ///< We double buffer: read one line in and leave one for the main program
volatile char line2[MAXLINELENGTH]; ///< Second buffer
volatile uint8_t lineidx=0; ///< our index into filling the current line
volatile char *currentline; ///< Pointer to current line buffer
volatile char *lastline; ///< Pointer to previous line buffer
volatile boolean recvdflag; ///< Received flag
volatile boolean inStandbyMode; ///< In standby flag
/**************************************************************************/
/*!
@brief Parse a NMEA string
@param nmea Pointer to the NMEA string
@return True if we parsed it, false if it has an invalid checksum or invalid data
*/
/**************************************************************************/
2012-03-27 22:56:07 +01:00
boolean Adafruit_GPS::parse(char *nmea) {
// do checksum check
2012-03-27 22:56:07 +01:00
// first look if we even have one
size_t len = strlen(nmea);
if (nmea[len-5] == '*') {
uint16_t sum = parseHex(nmea[len-4]) * 16;
sum += parseHex(nmea[len-3]);
// check checksum
for (uint8_t i=1; i < (len-5); i++) {
sum ^= nmea[i];
}
if (sum != 0) {
// bad checksum :(
return false;
}
} else {
return false;
}
int32_t degree;
long minutes;
char degreebuff[10];
// look for a few common sentences
if (strStartsWith(nmea, "$GPGGA")) {
2012-03-27 22:56:07 +01:00
// 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;
if (',' != *p)
{
strncpy(degreebuff, p, 2);
p += 2;
degreebuff[2] = '\0';
degree = atol(degreebuff) * 10000000;
strncpy(degreebuff, p, 2); // minutes
p += 3; // skip decimal point
strncpy(degreebuff + 2, p, 4);
degreebuff[6] = '\0';
minutes = 50 * atol(degreebuff) / 3;
latitude_fixed = degree + minutes;
latitude = degree / 100000 + minutes * 0.000006F;
latitudeDegrees = (latitude-100*int(latitude/100))/60.0;
latitudeDegrees += int(latitude/100);
}
2012-03-27 22:56:07 +01:00
p = strchr(p, ',')+1;
if (',' != *p)
{
if (p[0] == 'S') latitudeDegrees *= -1.0;
if (p[0] == 'N') lat = 'N';
else if (p[0] == 'S') lat = 'S';
else if (p[0] == ',') lat = 0;
else return false;
}
2012-03-27 22:56:07 +01:00
// parse out longitude
p = strchr(p, ',')+1;
if (',' != *p)
{
strncpy(degreebuff, p, 3);
p += 3;
degreebuff[3] = '\0';
degree = atol(degreebuff) * 10000000;
strncpy(degreebuff, p, 2); // minutes
p += 3; // skip decimal point
strncpy(degreebuff + 2, p, 4);
degreebuff[6] = '\0';
minutes = 50 * atol(degreebuff) / 3;
longitude_fixed = degree + minutes;
longitude = degree / 100000 + minutes * 0.000006F;
longitudeDegrees = (longitude-100*int(longitude/100))/60.0;
longitudeDegrees += int(longitude/100);
}
2012-03-27 22:56:07 +01:00
p = strchr(p, ',')+1;
if (',' != *p)
{
if (p[0] == 'W') longitudeDegrees *= -1.0;
if (p[0] == 'W') lon = 'W';
else if (p[0] == 'E') lon = 'E';
else if (p[0] == ',') lon = 0;
else return false;
}
2012-03-27 22:56:07 +01:00
p = strchr(p, ',')+1;
if (',' != *p)
{
fixquality = atoi(p);
}
2012-03-27 22:56:07 +01:00
p = strchr(p, ',')+1;
if (',' != *p)
{
satellites = atoi(p);
}
2012-03-27 22:56:07 +01:00
p = strchr(p, ',')+1;
if (',' != *p)
{
HDOP = atof(p);
}
2012-03-27 22:56:07 +01:00
p = strchr(p, ',')+1;
if (',' != *p)
{
altitude = atof(p);
}
2012-03-27 22:56:07 +01:00
p = strchr(p, ',')+1;
p = strchr(p, ',')+1;
if (',' != *p)
{
geoidheight = atof(p);
}
2012-03-27 22:56:07 +01:00
return true;
}
if (strStartsWith(nmea, "$GPRMC")) {
// found RMC
2012-03-27 22:56:07 +01:00
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')
2012-03-27 22:56:07 +01:00
fix = true;
else if (p[0] == 'V')
fix = false;
else
return false;
// parse out latitude
p = strchr(p, ',')+1;
if (',' != *p)
{
strncpy(degreebuff, p, 2);
p += 2;
degreebuff[2] = '\0';
long degree = atol(degreebuff) * 10000000;
strncpy(degreebuff, p, 2); // minutes
p += 3; // skip decimal point
strncpy(degreebuff + 2, p, 4);
degreebuff[6] = '\0';
long minutes = 50 * atol(degreebuff) / 3;
latitude_fixed = degree + minutes;
latitude = degree / 100000 + minutes * 0.000006F;
latitudeDegrees = (latitude-100*int(latitude/100))/60.0;
latitudeDegrees += int(latitude/100);
}
2012-03-27 22:56:07 +01:00
p = strchr(p, ',')+1;
if (',' != *p)
{
if (p[0] == 'S') latitudeDegrees *= -1.0;
if (p[0] == 'N') lat = 'N';
else if (p[0] == 'S') lat = 'S';
else if (p[0] == ',') lat = 0;
else return false;
}
2012-03-27 22:56:07 +01:00
// parse out longitude
p = strchr(p, ',')+1;
if (',' != *p)
{
strncpy(degreebuff, p, 3);
p += 3;
degreebuff[3] = '\0';
degree = atol(degreebuff) * 10000000;
strncpy(degreebuff, p, 2); // minutes
p += 3; // skip decimal point
strncpy(degreebuff + 2, p, 4);
degreebuff[6] = '\0';
minutes = 50 * atol(degreebuff) / 3;
longitude_fixed = degree + minutes;
longitude = degree / 100000 + minutes * 0.000006F;
longitudeDegrees = (longitude-100*int(longitude/100))/60.0;
longitudeDegrees += int(longitude/100);
}
2012-03-27 22:56:07 +01:00
p = strchr(p, ',')+1;
if (',' != *p)
{
if (p[0] == 'W') longitudeDegrees *= -1.0;
if (p[0] == 'W') lon = 'W';
else if (p[0] == 'E') lon = 'E';
else if (p[0] == ',') lon = 0;
else return false;
}
2012-03-27 22:56:07 +01:00
// speed
p = strchr(p, ',')+1;
if (',' != *p)
{
speed = atof(p);
}
2012-03-27 22:56:07 +01:00
// angle
p = strchr(p, ',')+1;
if (',' != *p)
{
angle = atof(p);
}
2012-03-27 22:56:07 +01:00
p = strchr(p, ',')+1;
if (',' != *p)
{
uint32_t fulldate = atof(p);
day = fulldate / 10000;
month = (fulldate % 10000) / 100;
year = (fulldate % 100);
}
2012-03-27 22:56:07 +01:00
// we dont parse the remaining, yet!
return true;
}
if (strstr(nmea, "$GPGLL")) {
// found GLL
char *p = nmea;
// parse out latitude
p = strchr(p, ',')+1;
if (',' != *p)
{
strncpy(degreebuff, p, 2);
p += 2;
degreebuff[2] = '\0';
long degree = atol(degreebuff) * 10000000;
strncpy(degreebuff, p, 2); // minutes
p += 3; // skip decimal point
strncpy(degreebuff + 2, p, 4);
degreebuff[6] = '\0';
long minutes = 50 * atol(degreebuff) / 3;
latitude_fixed = degree + minutes;
latitude = degree / 100000 + minutes * 0.000006F;
latitudeDegrees = (latitude-100*int(latitude/100))/60.0;
latitudeDegrees += int(latitude/100);
}
p = strchr(p, ',')+1;
if (',' != *p)
{
if (p[0] == 'S') latitudeDegrees *= -1.0;
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;
if (',' != *p)
{
strncpy(degreebuff, p, 3);
p += 3;
degreebuff[3] = '\0';
degree = atol(degreebuff) * 10000000;
strncpy(degreebuff, p, 2); // minutes
p += 3; // skip decimal point
strncpy(degreebuff + 2, p, 4);
degreebuff[6] = '\0';
minutes = 50 * atol(degreebuff) / 3;
longitude_fixed = degree + minutes;
longitude = degree / 100000 + minutes * 0.000006F;
longitudeDegrees = (longitude-100*int(longitude/100))/60.0;
longitudeDegrees += int(longitude/100);
}
p = strchr(p, ',')+1;
if (',' != *p)
{
if (p[0] == 'W') longitudeDegrees *= -1.0;
if (p[0] == 'W') lon = 'W';
else if (p[0] == 'E') lon = 'E';
else if (p[0] == ',') lon = 0;
else return false;
}
// 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;
return true;
}
2012-03-27 22:56:07 +01:00
return false;
}
/**************************************************************************/
/*!
@brief Read one character from the GPS device
@return The character that we received, or 0 if nothing was available
*/
/**************************************************************************/
char Adafruit_GPS::read(void) {
char c = 0;
if (paused) return c;
2012-03-27 22:56:07 +01:00
#if (defined(__AVR__) || defined(ESP8266)) && defined(USE_SW_SERIAL)
if(gpsSwSerial) {
if(!gpsSwSerial->available()) return c;
c = gpsSwSerial->read();
} else
2014-06-08 20:46:05 +01:00
#endif
{
if(!gpsHwSerial->available()) return c;
c = gpsHwSerial->read();
}
2012-03-27 22:56:07 +01:00
//Serial.print(c);
2012-03-27 22:56:07 +01:00
// if (c == '$') { //please don't eat the dollar sign - rdl 9/15/14
// currentline[lineidx] = 0;
// lineidx = 0;
// }
currentline[lineidx++] = c;
if (lineidx >= MAXLINELENGTH)
lineidx = MAXLINELENGTH-1; // ensure there is someplace to put the next received character
if (c == '\n') {
currentline[lineidx] = 0;
if (currentline == line1) {
currentline = line2;
lastline = line1;
} else {
currentline = line1;
lastline = line2;
2012-03-27 22:56:07 +01:00
}
//Serial.println("----");
//Serial.println((char *)lastline);
//Serial.println("----");
lineidx = 0;
recvdflag = true;
2012-03-27 22:56:07 +01:00
}
return c;
2012-03-27 22:56:07 +01:00
}
/**************************************************************************/
/*!
@brief Constructor when using SoftwareSerial
@param ser Pointer to SoftwareSerial device
*/
/**************************************************************************/
#if (defined(__AVR__) || defined(ESP8266)) && defined(USE_SW_SERIAL)
2012-03-28 19:14:32 +01:00
Adafruit_GPS::Adafruit_GPS(SoftwareSerial *ser)
{
common_init(); // Set everything to common state, then...
gpsSwSerial = ser; // ...override gpsSwSerial with value passed.
2012-03-27 22:56:07 +01:00
}
2014-06-08 20:46:05 +01:00
#endif
2012-03-27 22:56:07 +01:00
/**************************************************************************/
/*!
@brief Constructor when using HardwareSerial
@param ser Pointer to a HardwareSerial object
*/
/**************************************************************************/
Adafruit_GPS::Adafruit_GPS(HardwareSerial *ser) {
common_init(); // Set everything to common state, then...
gpsHwSerial = ser; // ...override gpsHwSerial with value passed.
2012-03-27 22:56:07 +01:00
}
/**************************************************************************/
/*!
@brief Initialization code used by all constructor types
*/
/**************************************************************************/
2012-03-27 22:56:07 +01:00
void Adafruit_GPS::common_init(void) {
#if (defined(__AVR__) || defined(ESP8266)) && defined(USE_SW_SERIAL)
gpsSwSerial = NULL; // Set both to NULL, then override correct
2014-06-08 20:46:05 +01:00
#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
2012-03-27 22:56:07 +01:00
}
/**************************************************************************/
/*!
@brief Start the HW or SW serial port
@param baud Baud rate
*/
/**************************************************************************/
void Adafruit_GPS::begin(uint32_t baud)
{
#if (defined(__AVR__) || defined(ESP8266)) && defined(USE_SW_SERIAL)
if(gpsSwSerial)
2014-06-08 20:46:05 +01:00
gpsSwSerial->begin(baud);
else
2014-06-08 20:46:05 +01:00
#endif
gpsHwSerial->begin(baud);
2012-12-04 16:20:10 +00:00
delay(10);
}
2012-03-27 22:56:07 +01:00
/**************************************************************************/
/*!
@brief Send a command to the GPS device
@param str Pointer to a string holding the command to send
*/
/**************************************************************************/
void Adafruit_GPS::sendCommand(const char *str) {
#if (defined(__AVR__) || defined(ESP8266)) && defined(USE_SW_SERIAL)
if(gpsSwSerial)
2014-06-08 20:46:05 +01:00
gpsSwSerial->println(str);
else
2014-06-08 20:46:05 +01:00
#endif
gpsHwSerial->println(str);
2012-03-27 22:56:07 +01:00
}
/**************************************************************************/
/*!
@brief Check to see if a new NMEA line has been received
@return True if received, false if not
*/
/**************************************************************************/
2012-03-27 22:56:07 +01:00
boolean Adafruit_GPS::newNMEAreceived(void) {
return recvdflag;
}
/**************************************************************************/
/*!
@brief Pause/unpause receiving new data
@param p True = pause, false = unpause
*/
/**************************************************************************/
2012-03-27 22:56:07 +01:00
void Adafruit_GPS::pause(boolean p) {
paused = p;
}
/**************************************************************************/
/*!
@brief Returns the last NMEA line received and unsets the received flag
@return Pointer to the last line string
*/
/**************************************************************************/
2012-03-27 22:56:07 +01:00
char *Adafruit_GPS::lastNMEA(void) {
recvdflag = false;
return (char *)lastline;
}
/**************************************************************************/
/*!
@brief Parse a hex character and return the appropriate decimal value
@param c Hex character, e.g. '0' or 'B'
@return Integer value of the hex character. Returns 0 if c is not a proper character
*/
/**************************************************************************/
2012-03-27 22:56:07 +01:00
// 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;
// if (c > 'F')
return 0;
2012-03-27 22:56:07 +01:00
}
2012-03-28 19:14:32 +01:00
/**************************************************************************/
/*!
@brief Wait for a specified sentence from the device
@param wait4me Pointer to a string holding the desired response
@param max How long to wait, default is MAXWAITSENTENCE
@param usingInterrupts True if using interrupts to read from the GPS (default is false)
@return True if we got what we wanted, false otherwise
*/
/**************************************************************************/
boolean Adafruit_GPS::waitForSentence(const char *wait4me, uint8_t max, boolean usingInterrupts) {
2012-03-28 19:14:32 +01:00
uint8_t i=0;
while (i < max) {
if (!usingInterrupts)
read();
if (newNMEAreceived()) {
2012-03-28 19:14:32 +01:00
char *nmea = lastNMEA();
i++;
if (strStartsWith(nmea, wait4me))
return true;
2012-03-28 19:14:32 +01:00
}
}
return false;
}
/**************************************************************************/
/*!
@brief Start the LOCUS logger
@return True on success, false if it failed
*/
/**************************************************************************/
2012-03-28 19:14:32 +01:00
boolean Adafruit_GPS::LOCUS_StartLogger(void) {
sendCommand(PMTK_LOCUS_STARTLOG);
recvdflag = false;
2013-10-27 16:15:00 +00:00
return waitForSentence(PMTK_LOCUS_STARTSTOPACK);
}
/**************************************************************************/
/*!
@brief Stop the LOCUS logger
@return True on success, false if it failed
*/
/**************************************************************************/
2013-10-27 16:15:00 +00:00
boolean Adafruit_GPS::LOCUS_StopLogger(void) {
sendCommand(PMTK_LOCUS_STOPLOG);
recvdflag = false;
return waitForSentence(PMTK_LOCUS_STARTSTOPACK);
2012-03-28 19:14:32 +01:00
}
/**************************************************************************/
/*!
@brief Read the logger status
@return True if we read the data, false if there was no response
*/
/**************************************************************************/
2012-03-28 19:14:32 +01:00
boolean Adafruit_GPS::LOCUS_ReadStatus(void) {
sendCommand(PMTK_LOCUS_QUERY_STATUS);
2012-03-28 19:14:32 +01:00
if (! waitForSentence("$PMTKLOG"))
return false;
char *response = lastNMEA();
uint16_t parsed[10];
uint8_t i;
2012-03-28 19:14:32 +01:00
for (i=0; i<10; i++) parsed[i] = -1;
2012-03-28 19:14:32 +01:00
response = strchr(response, ',');
for (i=0; i<10; i++) {
if (!response || (response[0] == 0) || (response[0] == '*'))
2012-03-28 19:14:32 +01:00
break;
response++;
parsed[i]=0;
while ((response[0] != ',') &&
2012-03-28 19:14:32 +01:00
(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;
2012-03-28 19:14:32 +01:00
}
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;
}
/**************************************************************************/
/*!
@brief Standby Mode Switches
@return False if already in standby, true if it entered standby
*/
/**************************************************************************/
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;
}
}
/**************************************************************************/
/*!
@brief Wake the sensor up
@return True if woken up, false if not in standby or failed to wake
*/
/**************************************************************************/
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
}
2012-12-04 16:20:10 +00:00
}
/**************************************************************************/
/*!
@brief Checks whether a string starts with a specified prefix
@param str Pointer to a string
@param prefix Pointer to the prefix
@return True if str starts with prefix, false otherwise
*/
/**************************************************************************/
static boolean strStartsWith(const char* str, const char* prefix)
{
while (*prefix) {
if (*prefix++ != *str++)
return false;
}
return true;
}