2019-03-12 21:21:45 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@file Adafruit_GPS.cpp
|
2012-03-27 22:56:07 +01:00
|
|
|
|
2019-03-12 21:21:45 +00: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
|
|
|
|
|
|
|
|
#include <Adafruit_GPS.h>
|
|
|
|
|
|
2020-01-17 21:18:09 +00:00
|
|
|
static boolean strStartsWith(const char *str, const char *prefix);
|
2019-04-10 00:57:35 +01:00
|
|
|
|
2020-01-08 16:04:21 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Check an NMEA string for basic format, valid source ID and valid
|
2020-01-17 21:18:09 +00:00
|
|
|
and valid sentence ID. Update the values of thisCheck, thisSource and
|
|
|
|
|
thisSentence.
|
2020-01-08 16:04:21 +00:00
|
|
|
@param nmea Pointer to the NMEA string
|
|
|
|
|
@return True if well formed, false if it has problems
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
boolean Adafruit_GPS::check(char *nmea) {
|
2020-01-17 21:18:09 +00:00
|
|
|
thisCheck = 0; // new check
|
|
|
|
|
if (*nmea != '$')
|
|
|
|
|
return false; // doesn't start with $
|
|
|
|
|
else
|
|
|
|
|
thisCheck += NMEA_HAS_DOLLAR;
|
|
|
|
|
// do checksum check -- first look if we even have one -- ignore all but last
|
|
|
|
|
// *
|
|
|
|
|
char *ast = nmea; // not strchr(nmea,'*'); for first *
|
|
|
|
|
while (*ast)
|
|
|
|
|
ast++; // go to the end
|
|
|
|
|
while (*ast != '*' && ast > nmea)
|
|
|
|
|
ast--; // then back to * if it's there
|
|
|
|
|
if (*ast != '*')
|
|
|
|
|
return false; // there is no asterisk
|
2020-01-08 16:04:21 +00:00
|
|
|
else {
|
2020-01-17 21:18:09 +00:00
|
|
|
uint16_t sum = parseHex(*(ast + 1)) * 16; // extract checksum
|
|
|
|
|
sum += parseHex(*(ast + 2));
|
|
|
|
|
char *p = nmea; // check checksum
|
|
|
|
|
for (char *p1 = p + 1; p1 < ast; p1++)
|
|
|
|
|
sum ^= *p1;
|
|
|
|
|
if (sum != 0)
|
|
|
|
|
return false; // bad checksum :(
|
|
|
|
|
else
|
|
|
|
|
thisCheck += NMEA_HAS_CHECKSUM;
|
2020-01-08 16:04:21 +00:00
|
|
|
}
|
|
|
|
|
// extract source of variable length
|
2020-01-17 21:18:09 +00:00
|
|
|
char *p = nmea + 1;
|
|
|
|
|
const char *src = tokenOnList(p, sources);
|
|
|
|
|
if (src) {
|
|
|
|
|
strcpy(thisSource, src);
|
2020-01-08 16:04:21 +00:00
|
|
|
thisCheck += NMEA_HAS_SOURCE;
|
2020-01-17 21:18:09 +00:00
|
|
|
} else
|
|
|
|
|
return false;
|
2020-01-08 16:04:21 +00:00
|
|
|
p += strlen(src);
|
|
|
|
|
// extract sentence id and check if parsed
|
2020-01-17 21:18:09 +00:00
|
|
|
const char *snc = tokenOnList(p, sentences_parsed);
|
|
|
|
|
if (snc) {
|
|
|
|
|
strcpy(thisSentence, snc);
|
2020-01-08 16:04:21 +00:00
|
|
|
thisCheck += NMEA_HAS_SENTENCE_P + NMEA_HAS_SENTENCE;
|
2020-01-17 21:18:09 +00:00
|
|
|
} else { // check if known
|
|
|
|
|
snc = tokenOnList(p, sentences_known);
|
|
|
|
|
if (snc) {
|
|
|
|
|
strcpy(thisSentence, snc);
|
2020-01-08 16:04:21 +00:00
|
|
|
thisCheck += NMEA_HAS_SENTENCE;
|
|
|
|
|
return false;
|
2020-01-17 21:18:09 +00:00
|
|
|
}
|
2019-10-31 21:05:02 +00:00
|
|
|
}
|
2020-01-17 21:18:09 +00:00
|
|
|
return true; // passed all the tests
|
2019-10-31 21:05:02 +00:00
|
|
|
}
|
|
|
|
|
|
2020-01-08 16:04:21 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Check if a token at the start of a string is on a list.
|
|
|
|
|
@param token Pointer to the string
|
|
|
|
|
@param list A list of strings, with the final entry starting "ZZ"
|
|
|
|
|
@return Pointer to the found token, or NULL if it fails
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
2020-01-17 21:18:09 +00:00
|
|
|
const char *Adafruit_GPS::tokenOnList(char *token, const char **list) {
|
|
|
|
|
int i = 0; // index in the list
|
|
|
|
|
while (strncmp(list[i], "ZZ", 2) &&
|
|
|
|
|
i < 1000) { // stop at terminator and don't crash without it
|
2020-01-08 16:04:21 +00:00
|
|
|
// test for a match on the sentence name
|
2020-01-17 21:18:09 +00:00
|
|
|
if (!strncmp((const char *)list[i], (const char *)token, strlen(list[i])))
|
|
|
|
|
return list[i];
|
2020-01-08 16:04:21 +00:00
|
|
|
i++;
|
|
|
|
|
}
|
2020-01-17 21:18:09 +00:00
|
|
|
return NULL; // couldn't find a match
|
2020-01-08 16:04:21 +00:00
|
|
|
}
|
2019-10-31 21:05:02 +00:00
|
|
|
|
2020-01-18 14:07:15 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
2020-01-18 15:23:14 +00:00
|
|
|
@brief Parse a string token from pointer p to the next comma, asterisk
|
2020-01-18 14:07:15 +00:00
|
|
|
or end of string.
|
|
|
|
|
@param buff Pointer to the buffer to store the string in
|
|
|
|
|
@param p Pointer into a string
|
|
|
|
|
@param n Max permitted size of string including terminating 0
|
|
|
|
|
@return Pointer to the string buffer
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
2020-01-18 15:23:14 +00:00
|
|
|
char *Adafruit_GPS::parseStr(char *buff, char *p, int n) {
|
2020-01-18 14:07:15 +00:00
|
|
|
char *e = strchr(p, ',');
|
|
|
|
|
int len = 0;
|
2020-01-18 15:23:14 +00:00
|
|
|
if (e) {
|
2020-01-18 14:07:15 +00:00
|
|
|
len = min(e - p, n - 1);
|
2020-01-18 15:23:14 +00:00
|
|
|
strncpy(buff, p, len); // copy up to the comma
|
2020-01-18 14:07:15 +00:00
|
|
|
buff[len] = 0;
|
|
|
|
|
} else {
|
2020-01-18 15:23:14 +00:00
|
|
|
e = strchr(p, '*');
|
|
|
|
|
if (e) {
|
2020-01-18 14:07:15 +00:00
|
|
|
len = min(e - p, n - 1);
|
2020-01-18 15:23:14 +00:00
|
|
|
strncpy(buff, p, len); // or up to the *
|
|
|
|
|
buff[e - p] = 0;
|
2020-01-18 14:07:15 +00:00
|
|
|
} else {
|
2020-01-18 15:23:14 +00:00
|
|
|
len = min((int)strlen(p), n - 1);
|
|
|
|
|
strncpy(buff, p, len); // or to the end or max capacity
|
2020-01-18 14:07:15 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return buff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Is the field empty, or should we try conversion? Won't work
|
2020-01-18 15:23:14 +00:00
|
|
|
for a text field that starts with an asterisk or a comma, but that
|
2020-01-18 14:07:15 +00:00
|
|
|
probably violates the NMEA-183 standard.
|
|
|
|
|
@param pStart Pointer to the location of the token in the NMEA string
|
|
|
|
|
@return true if empty field, false if something there
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
bool Adafruit_GPS::isEmpty(char *pStart) {
|
2020-01-18 15:23:14 +00:00
|
|
|
if (',' != *pStart && '*' != *pStart && pStart != NULL)
|
|
|
|
|
return false;
|
|
|
|
|
else
|
|
|
|
|
return true;
|
2020-01-18 14:07:15 +00:00
|
|
|
}
|
|
|
|
|
|
2020-01-08 16:04:21 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
2020-01-17 21:18:09 +00:00
|
|
|
@brief Add *CS where CS is the two character hex checksum for all but
|
2020-01-08 16:04:21 +00:00
|
|
|
the first character in the string. The checksum is the result of an
|
2020-01-17 21:18:09 +00:00
|
|
|
exclusive or of all the characters in the string. Also useful if you
|
2020-01-08 16:04:21 +00:00
|
|
|
are creating new PMTK strings for controlling a GPS module and need a
|
|
|
|
|
checksum added.
|
2020-01-17 21:18:09 +00:00
|
|
|
@param buff Pointer to the string, which must be long enough
|
2020-01-08 16:04:21 +00:00
|
|
|
@return none
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
2020-01-17 21:18:09 +00:00
|
|
|
void Adafruit_GPS::addChecksum(char *buff) {
|
2020-01-08 16:04:21 +00:00
|
|
|
char cs = 0;
|
|
|
|
|
int i = 1;
|
2020-01-17 21:18:09 +00:00
|
|
|
while (buff[i]) {
|
2020-01-08 16:04:21 +00:00
|
|
|
cs ^= buff[i];
|
|
|
|
|
i++;
|
|
|
|
|
}
|
2020-01-17 21:18:09 +00:00
|
|
|
sprintf(buff, "%s*%02X", buff, cs);
|
2019-04-18 03:37:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Parse a part of an NMEA string for time
|
|
|
|
|
@param p Pointer to the location of the token in the NMEA string
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
void Adafruit_GPS::parseTime(char *p) {
|
2020-01-17 21:18:09 +00:00
|
|
|
// get time
|
|
|
|
|
uint32_t time = atol(p);
|
|
|
|
|
hour = time / 10000;
|
|
|
|
|
minute = (time % 10000) / 100;
|
|
|
|
|
seconds = (time % 100);
|
|
|
|
|
|
|
|
|
|
p = strchr(p, '.') + 1;
|
|
|
|
|
milliseconds = atoi(p);
|
|
|
|
|
lastTime = sentTime;
|
2019-04-18 03:37:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Parse a part of an NMEA string for latitude angle
|
|
|
|
|
@param p Pointer to the location of the token in the NMEA string
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
void Adafruit_GPS::parseLat(char *p) {
|
|
|
|
|
int32_t degree;
|
|
|
|
|
long minutes;
|
|
|
|
|
char degreebuff[10];
|
2020-01-18 14:07:15 +00:00
|
|
|
if (!isEmpty(p)) {
|
2020-01-17 21:18:09 +00:00
|
|
|
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);
|
|
|
|
|
}
|
2019-04-18 03:37:47 +01:00
|
|
|
}
|
2019-04-15 01:25:59 +01:00
|
|
|
|
2019-04-18 03:37:47 +01:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Parse a part of an NMEA string for latitude direction
|
|
|
|
|
@param p Pointer to the location of the token in the NMEA string
|
|
|
|
|
@return True if we parsed it, false if it has invalid data
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
boolean Adafruit_GPS::parseLatDir(char *p) {
|
2020-01-17 21:18:09 +00:00
|
|
|
if (p[0] == 'S') {
|
|
|
|
|
lat = 'S';
|
|
|
|
|
latitudeDegrees *= -1.0;
|
|
|
|
|
latitude_fixed *= -1;
|
|
|
|
|
} else if (p[0] == 'N') {
|
|
|
|
|
lat = 'N';
|
|
|
|
|
} else if (p[0] == ',') {
|
|
|
|
|
lat = 0;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
2019-04-18 03:37:47 +01:00
|
|
|
}
|
2019-04-15 01:25:59 +01:00
|
|
|
|
2019-04-18 03:37:47 +01:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Parse a part of an NMEA string for longitude angle
|
|
|
|
|
@param p Pointer to the location of the token in the NMEA string
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
void Adafruit_GPS::parseLon(char *p) {
|
|
|
|
|
int32_t degree;
|
|
|
|
|
long minutes;
|
|
|
|
|
char degreebuff[10];
|
2020-01-18 14:07:15 +00:00
|
|
|
if (!isEmpty(p)) {
|
2020-01-17 21:18:09 +00:00
|
|
|
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);
|
|
|
|
|
}
|
2019-04-18 03:37:47 +01:00
|
|
|
}
|
2019-04-15 01:25:59 +01:00
|
|
|
|
2019-04-18 03:37:47 +01:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Parse a part of an NMEA string for longitude direction
|
|
|
|
|
@param p Pointer to the location of the token in the NMEA string
|
|
|
|
|
@return True if we parsed it, false if it has invalid data
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
boolean Adafruit_GPS::parseLonDir(char *p) {
|
2020-01-18 14:07:15 +00:00
|
|
|
if (!isEmpty(p)) {
|
2020-01-17 21:18:09 +00:00
|
|
|
if (p[0] == 'W') {
|
|
|
|
|
lon = 'W';
|
|
|
|
|
longitudeDegrees *= -1.0;
|
|
|
|
|
longitude_fixed *= -1;
|
|
|
|
|
} else if (p[0] == 'E') {
|
|
|
|
|
lon = 'E';
|
|
|
|
|
} else if (p[0] == ',') {
|
|
|
|
|
lon = 0;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
2019-04-15 01:25:59 +01:00
|
|
|
}
|
2020-01-17 21:18:09 +00:00
|
|
|
}
|
|
|
|
|
return true;
|
2019-04-18 03:37:47 +01:00
|
|
|
}
|
2019-04-15 01:25:59 +01:00
|
|
|
|
2019-04-18 03:37:47 +01:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Parse a part of an NMEA string for whether there is a fix
|
|
|
|
|
@param p Pointer to the location of the token in the NMEA string
|
|
|
|
|
@return True if we parsed it, false if it has invalid data
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
boolean Adafruit_GPS::parseFix(char *p) {
|
2020-01-17 21:18:09 +00:00
|
|
|
if (p[0] == 'A') {
|
|
|
|
|
fix = true;
|
|
|
|
|
lastFix = sentTime;
|
|
|
|
|
} else if (p[0] == 'V')
|
|
|
|
|
fix = false;
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
2019-04-18 03:37:47 +01:00
|
|
|
}
|
2012-03-27 22:56:07 +01:00
|
|
|
|
2019-04-18 03:37:47 +01:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
2019-05-16 16:55:42 +01:00
|
|
|
@brief Time in seconds since the last position fix was obtained. Will
|
2019-04-20 18:51:00 +01:00
|
|
|
fail by rolling over to zero after one millis() cycle, about 6-1/2 weeks.
|
2019-04-18 03:37:47 +01:00
|
|
|
@return float value in seconds since last fix.
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
2020-01-17 21:18:09 +00:00
|
|
|
float Adafruit_GPS::secondsSinceFix() { return (millis() - lastFix) / 1000.; }
|
2012-03-27 22:56:07 +01:00
|
|
|
|
2019-04-20 18:51:00 +01:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
2019-05-16 16:55:42 +01:00
|
|
|
@brief Time in seconds since the last GPS time was obtained. Will fail
|
2019-04-20 18:51:00 +01:00
|
|
|
by rolling over to zero after one millis() cycle, about 6-1/2 weeks.
|
|
|
|
|
@return float value in seconds since last GPS time.
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
2020-01-17 21:18:09 +00:00
|
|
|
float Adafruit_GPS::secondsSinceTime() { return (millis() - lastTime) / 1000.; }
|
2019-04-20 18:51:00 +01:00
|
|
|
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
2019-05-16 16:55:42 +01:00
|
|
|
@brief Time in seconds since the last GPS date was obtained. Will fail
|
2019-04-20 18:51:00 +01:00
|
|
|
by rolling over to zero after one millis() cycle, about 6-1/2 weeks.
|
|
|
|
|
@return float value in seconds since last GPS date.
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
2020-01-17 21:18:09 +00:00
|
|
|
float Adafruit_GPS::secondsSinceDate() { return (millis() - lastDate) / 1000.; }
|
2019-04-20 18:51:00 +01:00
|
|
|
|
2020-01-19 13:06:26 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Fakes time of receipt of a sentence. Use between build() and parse()
|
|
|
|
|
to make the timing look like the sentence arrived from the GPS.
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
void Adafruit_GPS::resetSentTime() { sentTime = millis(); }
|
|
|
|
|
|
2019-10-14 02:32:50 +01:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
2020-01-17 21:18:09 +00:00
|
|
|
@brief How many bytes are available to read - part of 'Print'-class
|
|
|
|
|
functionality
|
2019-10-14 02:32:50 +01:00
|
|
|
@return Bytes available, 0 if none
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
2019-08-30 07:18:01 +01:00
|
|
|
size_t Adafruit_GPS::available(void) {
|
2020-01-17 21:18:09 +00:00
|
|
|
if (paused)
|
|
|
|
|
return 0;
|
2019-08-30 07:18:01 +01:00
|
|
|
|
|
|
|
|
#if (defined(__AVR__) || defined(ESP8266)) && defined(USE_SW_SERIAL)
|
|
|
|
|
if (gpsSwSerial) {
|
|
|
|
|
return gpsSwSerial->available();
|
2019-10-31 21:05:02 +00:00
|
|
|
}
|
2019-08-30 07:18:01 +01:00
|
|
|
#endif
|
|
|
|
|
if (gpsHwSerial) {
|
|
|
|
|
return gpsHwSerial->available();
|
|
|
|
|
}
|
2019-11-17 00:06:07 +00:00
|
|
|
if (gpsI2C || gpsSPI) {
|
2020-01-17 21:18:09 +00:00
|
|
|
return 1; // I2C/SPI doesnt have 'availability' so always has a byte at
|
|
|
|
|
// least to read!
|
2019-08-30 07:18:01 +01:00
|
|
|
}
|
2019-10-09 21:51:17 +01:00
|
|
|
return 0;
|
2019-08-30 07:18:01 +01:00
|
|
|
}
|
|
|
|
|
|
2019-10-14 02:32:50 +01:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
2020-01-17 21:18:09 +00:00
|
|
|
@brief Write a byte to the underlying transport - part of 'Print'-class
|
|
|
|
|
functionality
|
2019-10-14 02:32:50 +01:00
|
|
|
@param c A single byte to send
|
|
|
|
|
@return Bytes written - 1 on success, 0 on failure
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
2019-08-30 07:18:01 +01:00
|
|
|
size_t Adafruit_GPS::write(uint8_t c) {
|
|
|
|
|
#if (defined(__AVR__) || defined(ESP8266)) && defined(USE_SW_SERIAL)
|
|
|
|
|
if (gpsSwSerial) {
|
|
|
|
|
return gpsSwSerial->write(c);
|
2019-10-31 21:05:02 +00:00
|
|
|
}
|
2019-08-30 07:18:01 +01:00
|
|
|
#endif
|
|
|
|
|
if (gpsHwSerial) {
|
|
|
|
|
return gpsHwSerial->write(c);
|
|
|
|
|
}
|
|
|
|
|
if (gpsI2C) {
|
|
|
|
|
gpsI2C->beginTransmission(_i2caddr);
|
|
|
|
|
if (gpsI2C->write(c) != 1) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (gpsI2C->endTransmission(true) == 0) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-11-17 00:06:07 +00:00
|
|
|
if (gpsSPI) {
|
2020-01-17 21:18:09 +00:00
|
|
|
gpsSPI->beginTransaction(gpsSPI_settings);
|
2019-11-17 00:06:07 +00:00
|
|
|
if (gpsSPI_cs >= 0) {
|
|
|
|
|
digitalWrite(gpsSPI_cs, LOW);
|
|
|
|
|
}
|
|
|
|
|
c = gpsSPI->transfer(c);
|
|
|
|
|
if (gpsSPI_cs >= 0) {
|
|
|
|
|
digitalWrite(gpsSPI_cs, HIGH);
|
|
|
|
|
}
|
|
|
|
|
gpsSPI->endTransaction();
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-30 07:18:01 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-12 21:21:45 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Read one character from the GPS device
|
|
|
|
|
@return The character that we received, or 0 if nothing was available
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
2012-03-28 17:20:38 +01:00
|
|
|
char Adafruit_GPS::read(void) {
|
2020-01-17 21:18:09 +00:00
|
|
|
static uint32_t firstChar = 0; // first character received in current sentence
|
|
|
|
|
uint32_t tStart = millis(); // as close as we can get to time char was sent
|
2012-03-28 17:20:38 +01:00
|
|
|
char c = 0;
|
2019-03-12 21:21:45 +00:00
|
|
|
|
2020-01-17 21:18:09 +00:00
|
|
|
if (paused)
|
|
|
|
|
return c;
|
2012-03-27 22:56:07 +01:00
|
|
|
|
2019-03-14 18:22:28 +00:00
|
|
|
#if (defined(__AVR__) || defined(ESP8266)) && defined(USE_SW_SERIAL)
|
2020-01-17 21:18:09 +00:00
|
|
|
if (gpsSwSerial) {
|
2019-10-31 21:05:02 +00:00
|
|
|
if (!gpsSwSerial->available())
|
2019-08-30 07:18:01 +01:00
|
|
|
return c;
|
2012-03-28 17:20:38 +01:00
|
|
|
c = gpsSwSerial->read();
|
2019-10-31 21:05:02 +00:00
|
|
|
}
|
2014-06-08 20:46:05 +01:00
|
|
|
#endif
|
2019-08-30 07:18:01 +01:00
|
|
|
if (gpsHwSerial) {
|
2019-10-31 21:05:02 +00:00
|
|
|
if (!gpsHwSerial->available())
|
2019-08-30 07:18:01 +01:00
|
|
|
return c;
|
2012-04-27 21:49:18 +01:00
|
|
|
c = gpsHwSerial->read();
|
|
|
|
|
}
|
2019-08-30 07:18:01 +01:00
|
|
|
if (gpsI2C) {
|
2019-11-17 00:06:07 +00:00
|
|
|
if (_buff_idx <= _buff_max) {
|
|
|
|
|
c = _i2cbuffer[_buff_idx];
|
|
|
|
|
_buff_idx++;
|
2019-08-30 07:18:01 +01:00
|
|
|
} else {
|
|
|
|
|
// refill the buffer!
|
2020-01-17 21:18:09 +00:00
|
|
|
if (gpsI2C->requestFrom(0x10, GPS_MAX_I2C_TRANSFER, true) ==
|
|
|
|
|
GPS_MAX_I2C_TRANSFER) {
|
|
|
|
|
// got data!
|
|
|
|
|
_buff_max = 0;
|
|
|
|
|
char curr_char = 0;
|
|
|
|
|
for (int i = 0; i < GPS_MAX_I2C_TRANSFER; i++) {
|
|
|
|
|
curr_char = gpsI2C->read();
|
|
|
|
|
if ((curr_char == 0x0A) && (last_char != 0x0D)) {
|
|
|
|
|
// skip duplicate 0x0A's - but keep as part of a CRLF
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
last_char = curr_char;
|
|
|
|
|
_i2cbuffer[_buff_max] = curr_char;
|
|
|
|
|
_buff_max++;
|
|
|
|
|
}
|
|
|
|
|
_buff_max--; // back up to the last valid slot
|
|
|
|
|
if ((_buff_max == 0) && (_i2cbuffer[0] == 0x0A)) {
|
|
|
|
|
_buff_max = -1; // ahh there was nothing to read after all
|
|
|
|
|
}
|
|
|
|
|
_buff_idx = 0;
|
2019-08-30 07:18:01 +01:00
|
|
|
}
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-10-31 21:05:02 +00:00
|
|
|
|
2019-11-17 00:06:07 +00:00
|
|
|
if (gpsSPI) {
|
|
|
|
|
do {
|
2020-01-17 21:18:09 +00:00
|
|
|
gpsSPI->beginTransaction(gpsSPI_settings);
|
2019-11-17 00:06:07 +00:00
|
|
|
if (gpsSPI_cs >= 0) {
|
2020-01-17 21:18:09 +00:00
|
|
|
digitalWrite(gpsSPI_cs, LOW);
|
2019-11-17 00:06:07 +00:00
|
|
|
}
|
|
|
|
|
c = gpsSPI->transfer(0xFF);
|
|
|
|
|
if (gpsSPI_cs >= 0) {
|
2020-01-17 21:18:09 +00:00
|
|
|
digitalWrite(gpsSPI_cs, HIGH);
|
2019-11-17 00:06:07 +00:00
|
|
|
}
|
|
|
|
|
gpsSPI->endTransaction();
|
|
|
|
|
// skip duplicate 0x0A's - but keep as part of a CRLF
|
2020-01-17 21:18:09 +00:00
|
|
|
} while (((c == 0x0A) && (last_char != 0x0D)) ||
|
|
|
|
|
(!isprint(c) && !isspace(c)));
|
2019-11-17 00:06:07 +00:00
|
|
|
last_char = c;
|
|
|
|
|
}
|
2020-01-17 21:18:09 +00:00
|
|
|
// Serial.print(c);
|
2019-10-31 21:05:02 +00:00
|
|
|
|
2019-04-10 00:57:35 +01:00
|
|
|
currentline[lineidx++] = c;
|
|
|
|
|
if (lineidx >= MAXLINELENGTH)
|
2020-01-17 21:18:09 +00:00
|
|
|
lineidx = MAXLINELENGTH -
|
|
|
|
|
1; // ensure there is someplace to put the next received character
|
2019-04-10 00:57:35 +01:00
|
|
|
|
2012-04-27 21:49:18 +01:00
|
|
|
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
|
|
|
}
|
2012-04-27 21:49:18 +01:00
|
|
|
|
2020-01-17 21:18:09 +00:00
|
|
|
// Serial.println("----");
|
|
|
|
|
// Serial.println((char *)lastline);
|
|
|
|
|
// Serial.println("----");
|
2012-04-27 21:49:18 +01:00
|
|
|
lineidx = 0;
|
|
|
|
|
recvdflag = true;
|
2020-01-17 21:18:09 +00:00
|
|
|
recvdTime = millis(); // time we got the end of the string
|
2019-09-29 17:26:01 +01:00
|
|
|
sentTime = firstChar;
|
2020-01-17 21:18:09 +00:00
|
|
|
firstChar = 0; // there are no characters yet
|
|
|
|
|
return c; // wait until next character to set time
|
2012-03-27 22:56:07 +01:00
|
|
|
}
|
2012-04-27 21:49:18 +01:00
|
|
|
|
2020-01-17 21:18:09 +00:00
|
|
|
if (firstChar == 0)
|
|
|
|
|
firstChar = tStart;
|
2012-03-28 17:20:38 +01:00
|
|
|
return c;
|
2012-03-27 22:56:07 +01:00
|
|
|
}
|
|
|
|
|
|
2019-03-12 21:21:45 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
2019-03-14 18:22:28 +00:00
|
|
|
@brief Constructor when using SoftwareSerial
|
|
|
|
|
@param ser Pointer to SoftwareSerial device
|
2019-03-12 21:21:45 +00:00
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
2019-03-14 18:22:28 +00:00
|
|
|
#if (defined(__AVR__) || defined(ESP8266)) && defined(USE_SW_SERIAL)
|
2020-01-17 21:18:09 +00:00
|
|
|
Adafruit_GPS::Adafruit_GPS(SoftwareSerial *ser) {
|
2012-04-27 21:49:18 +01:00
|
|
|
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
|
|
|
|
2019-03-12 21:21:45 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Constructor when using HardwareSerial
|
|
|
|
|
@param ser Pointer to a HardwareSerial object
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
2012-04-27 21:49:18 +01:00
|
|
|
Adafruit_GPS::Adafruit_GPS(HardwareSerial *ser) {
|
2020-01-17 21:18:09 +00:00
|
|
|
common_init(); // Set everything to common state, then...
|
2012-04-27 21:49:18 +01:00
|
|
|
gpsHwSerial = ser; // ...override gpsHwSerial with value passed.
|
2012-03-27 22:56:07 +01:00
|
|
|
}
|
|
|
|
|
|
2019-08-30 07:18:01 +01:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Constructor when using I2C
|
|
|
|
|
@param theWire Pointer to an I2C TwoWire object
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
Adafruit_GPS::Adafruit_GPS(TwoWire *theWire) {
|
2020-01-17 21:18:09 +00:00
|
|
|
common_init(); // Set everything to common state, then...
|
2019-08-30 07:18:01 +01:00
|
|
|
gpsI2C = theWire; // ...override gpsI2C
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-17 00:06:07 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Constructor when using SPI
|
|
|
|
|
@param theSPI Pointer to an SPI device object
|
|
|
|
|
@param cspin The pin connected to the GPS CS, can be -1 if unused
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
|
|
|
|
Adafruit_GPS::Adafruit_GPS(SPIClass *theSPI, int8_t cspin) {
|
2020-01-17 21:18:09 +00:00
|
|
|
common_init(); // Set everything to common state, then...
|
2019-11-17 00:06:07 +00:00
|
|
|
gpsSPI = theSPI; // ...override gpsSPI
|
|
|
|
|
gpsSPI_cs = cspin;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-12 21:21:45 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Initialization code used by all constructor types
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
2012-03-27 22:56:07 +01:00
|
|
|
void Adafruit_GPS::common_init(void) {
|
2019-03-14 18:22:28 +00:00
|
|
|
#if (defined(__AVR__) || defined(ESP8266)) && defined(USE_SW_SERIAL)
|
2012-04-27 21:49:18 +01:00
|
|
|
gpsSwSerial = NULL; // Set both to NULL, then override correct
|
2014-06-08 20:46:05 +01:00
|
|
|
#endif
|
2012-04-27 21:49:18 +01:00
|
|
|
gpsHwSerial = NULL; // port pointer in corresponding constructor
|
2020-01-17 21:18:09 +00:00
|
|
|
gpsI2C = NULL;
|
|
|
|
|
gpsSPI = NULL;
|
|
|
|
|
recvdflag = false;
|
|
|
|
|
paused = false;
|
|
|
|
|
lineidx = 0;
|
2012-04-27 21:49:18 +01:00
|
|
|
currentline = line1;
|
2020-01-17 21:18:09 +00:00
|
|
|
lastline = line2;
|
2012-04-27 21:49:18 +01:00
|
|
|
|
2020-01-17 21:18:09 +00:00
|
|
|
hour = minute = seconds = year = month = day = fixquality = fixquality_3d =
|
|
|
|
|
satellites = 0; // uint8_t
|
2012-04-27 21:49:18 +01:00
|
|
|
lat = lon = mag = 0; // char
|
2020-01-17 21:18:09 +00:00
|
|
|
fix = false; // boolean
|
|
|
|
|
milliseconds = 0; // uint16_t
|
|
|
|
|
latitude = longitude = geoidheight = altitude = speed = angle = magvariation =
|
|
|
|
|
HDOP = VDOP = PDOP = 0.0; // float
|
2012-03-27 22:56:07 +01:00
|
|
|
}
|
|
|
|
|
|
2019-03-12 21:21:45 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Start the HW or SW serial port
|
2019-08-30 07:18:01 +01:00
|
|
|
@param baud_or_i2caddr Baud rate if using serial, I2C address if using I2C
|
|
|
|
|
@returns True on successful hardware init, False on failure
|
2019-03-12 21:21:45 +00:00
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
2020-01-17 21:18:09 +00:00
|
|
|
bool Adafruit_GPS::begin(uint32_t baud_or_i2caddr) {
|
2019-03-14 18:22:28 +00:00
|
|
|
#if (defined(__AVR__) || defined(ESP8266)) && defined(USE_SW_SERIAL)
|
2020-01-17 21:18:09 +00:00
|
|
|
if (gpsSwSerial) {
|
2019-08-30 07:18:01 +01:00
|
|
|
gpsSwSerial->begin(baud_or_i2caddr);
|
2019-10-31 21:05:02 +00:00
|
|
|
}
|
2014-06-08 20:46:05 +01:00
|
|
|
#endif
|
2019-08-30 07:18:01 +01:00
|
|
|
if (gpsHwSerial) {
|
|
|
|
|
gpsHwSerial->begin(baud_or_i2caddr);
|
|
|
|
|
}
|
|
|
|
|
if (gpsI2C) {
|
|
|
|
|
gpsI2C->begin();
|
|
|
|
|
if (baud_or_i2caddr > 0x7F) {
|
|
|
|
|
_i2caddr = GPS_DEFAULT_I2C_ADDR;
|
|
|
|
|
} else {
|
|
|
|
|
_i2caddr = baud_or_i2caddr;
|
|
|
|
|
}
|
|
|
|
|
// A basic scanner, see if it ACK's
|
|
|
|
|
gpsI2C->beginTransmission(_i2caddr);
|
2020-01-17 21:18:09 +00:00
|
|
|
return (gpsI2C->endTransmission() == 0);
|
2019-08-30 07:18:01 +01:00
|
|
|
}
|
2019-11-17 00:06:07 +00:00
|
|
|
if (gpsSPI) {
|
|
|
|
|
gpsSPI->begin();
|
|
|
|
|
gpsSPI_settings = SPISettings(baud_or_i2caddr, MSBFIRST, SPI_MODE0);
|
|
|
|
|
if (gpsSPI_cs >= 0) {
|
|
|
|
|
pinMode(gpsSPI_cs, OUTPUT);
|
|
|
|
|
digitalWrite(gpsSPI_cs, HIGH);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-04 16:20:10 +00:00
|
|
|
delay(10);
|
2019-08-30 07:18:01 +01:00
|
|
|
return true;
|
2012-04-27 21:49:18 +01:00
|
|
|
}
|
2012-03-27 22:56:07 +01:00
|
|
|
|
2019-03-12 21:21:45 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Send a command to the GPS device
|
|
|
|
|
@param str Pointer to a string holding the command to send
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
2020-01-17 21:18:09 +00:00
|
|
|
void Adafruit_GPS::sendCommand(const char *str) { println(str); }
|
2012-03-27 22:56:07 +01:00
|
|
|
|
2019-03-12 21:21:45 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Check to see if a new NMEA line has been received
|
|
|
|
|
@return True if received, false if not
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
2020-01-17 21:18:09 +00:00
|
|
|
boolean Adafruit_GPS::newNMEAreceived(void) { return recvdflag; }
|
2012-03-27 22:56:07 +01:00
|
|
|
|
2019-03-12 21:21:45 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Pause/unpause receiving new data
|
|
|
|
|
@param p True = pause, false = unpause
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
2020-01-17 21:18:09 +00:00
|
|
|
void Adafruit_GPS::pause(boolean p) { paused = p; }
|
2012-03-27 22:56:07 +01:00
|
|
|
|
2019-03-12 21:21:45 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@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;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-12 21:21:45 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Parse a hex character and return the appropriate decimal value
|
|
|
|
|
@param c Hex character, e.g. '0' or 'B'
|
2020-01-17 21:18:09 +00:00
|
|
|
@return Integer value of the hex character. Returns 0 if c is not a proper
|
|
|
|
|
character
|
2019-03-12 21:21:45 +00:00
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
2012-03-27 22:56:07 +01:00
|
|
|
// read a Hex value and return the decimal equivalent
|
|
|
|
|
uint8_t Adafruit_GPS::parseHex(char c) {
|
2020-01-17 21:18:09 +00:00
|
|
|
if (c < '0')
|
|
|
|
|
return 0;
|
|
|
|
|
if (c <= '9')
|
|
|
|
|
return c - '0';
|
|
|
|
|
if (c < 'A')
|
2014-10-18 23:30:28 +01:00
|
|
|
return 0;
|
2020-01-17 21:18:09 +00:00
|
|
|
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
|
|
|
|
2019-03-12 21:21:45 +00: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
|
2020-01-17 21:18:09 +00:00
|
|
|
@param usingInterrupts True if using interrupts to read from the GPS
|
|
|
|
|
(default is false)
|
2019-03-12 21:21:45 +00:00
|
|
|
@return True if we got what we wanted, false otherwise
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
2020-01-17 21:18:09 +00:00
|
|
|
boolean Adafruit_GPS::waitForSentence(const char *wait4me, uint8_t max,
|
|
|
|
|
boolean usingInterrupts) {
|
|
|
|
|
uint8_t i = 0;
|
2012-03-28 19:14:32 +01:00
|
|
|
while (i < max) {
|
2019-04-10 00:57:35 +01:00
|
|
|
if (!usingInterrupts)
|
|
|
|
|
read();
|
2017-08-02 04:27:56 +01:00
|
|
|
|
2019-03-12 21:21:45 +00:00
|
|
|
if (newNMEAreceived()) {
|
2012-03-28 19:14:32 +01:00
|
|
|
char *nmea = lastNMEA();
|
|
|
|
|
i++;
|
|
|
|
|
|
2019-04-10 00:57:35 +01:00
|
|
|
if (strStartsWith(nmea, wait4me))
|
2020-01-17 21:18:09 +00:00
|
|
|
return true;
|
2012-03-28 19:14:32 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-12 21:21:45 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@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);
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-12 21:21:45 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@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
|
|
|
}
|
|
|
|
|
|
2019-03-12 21:21:45 +00: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);
|
2019-03-12 21:21:45 +00:00
|
|
|
|
2020-01-17 21:18:09 +00:00
|
|
|
if (!waitForSentence("$PMTKLOG"))
|
2012-03-28 19:14:32 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
char *response = lastNMEA();
|
|
|
|
|
uint16_t parsed[10];
|
|
|
|
|
uint8_t i;
|
2019-03-12 21:21:45 +00:00
|
|
|
|
2020-01-17 21:18:09 +00:00
|
|
|
for (i = 0; i < 10; i++)
|
|
|
|
|
parsed[i] = -1;
|
2019-03-12 21:21:45 +00:00
|
|
|
|
2012-03-28 19:14:32 +01:00
|
|
|
response = strchr(response, ',');
|
2020-01-17 21:18:09 +00:00
|
|
|
for (i = 0; i < 10; i++) {
|
2019-03-12 21:21:45 +00:00
|
|
|
if (!response || (response[0] == 0) || (response[0] == '*'))
|
2012-03-28 19:14:32 +01:00
|
|
|
break;
|
|
|
|
|
response++;
|
2020-01-17 21:18:09 +00:00
|
|
|
parsed[i] = 0;
|
|
|
|
|
while ((response[0] != ',') && (response[0] != '*') && (response[0] != 0)) {
|
2012-03-28 19:14:32 +01:00
|
|
|
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])) {
|
2019-03-12 21:21:45 +00:00
|
|
|
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;
|
|
|
|
|
}
|
2012-04-30 02:46:21 +01:00
|
|
|
|
2019-03-12 21:21:45 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Standby Mode Switches
|
|
|
|
|
@return False if already in standby, true if it entered standby
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
2012-04-30 02:46:21 +01:00
|
|
|
boolean Adafruit_GPS::standby(void) {
|
|
|
|
|
if (inStandbyMode) {
|
2020-01-17 21:18:09 +00:00
|
|
|
return false; // Returns false if already in standby mode, so that you do
|
|
|
|
|
// not wake it up by sending commands to GPS
|
|
|
|
|
} else {
|
2012-04-30 02:46:21 +01:00
|
|
|
inStandbyMode = true;
|
|
|
|
|
sendCommand(PMTK_STANDBY);
|
2020-01-17 21:18:09 +00:00
|
|
|
// return waitForSentence(PMTK_STANDBY_SUCCESS); // don't seem to be fast
|
|
|
|
|
// enough to catch the message, or something else just is not working
|
2012-04-30 02:46:21 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-12 21:21:45 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
|
/*!
|
|
|
|
|
@brief Wake the sensor up
|
|
|
|
|
@return True if woken up, false if not in standby or failed to wake
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
2012-04-30 02:46:21 +01:00
|
|
|
boolean Adafruit_GPS::wakeup(void) {
|
|
|
|
|
if (inStandbyMode) {
|
2020-01-17 21:18:09 +00:00
|
|
|
inStandbyMode = false;
|
|
|
|
|
sendCommand(""); // send byte to wake it up
|
2012-04-30 02:46:21 +01:00
|
|
|
return waitForSentence(PMTK_AWAKE);
|
2020-01-17 21:18:09 +00:00
|
|
|
} else {
|
|
|
|
|
return false; // Returns false if not in standby mode, nothing to wakeup
|
2012-04-30 02:46:21 +01:00
|
|
|
}
|
2012-12-04 16:20:10 +00:00
|
|
|
}
|
2020-01-08 17:05:07 +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
|
|
|
|
|
*/
|
|
|
|
|
/**************************************************************************/
|
2020-01-17 21:18:09 +00:00
|
|
|
static boolean strStartsWith(const char *str, const char *prefix) {
|
2020-01-08 17:05:07 +00:00
|
|
|
while (*prefix) {
|
|
|
|
|
if (*prefix++ != *str++)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
2020-01-18 14:07:15 +00:00
|
|
|
}
|