Merge pull request #177 from ladyada-eagleclaw/fix-wakeup-timeout

Bound GPS response waits and allow wakeup retries
This commit is contained in:
Limor "Ladyada" Fried authored and GitHub committed 2026-09-15 11:49:06 -04:00
commit 6d811e98f0
4 files changed
+136 -16

No files matched your search

@@ -0,0 +1,107 @@
#include <Adafruit_GPS.h>
// Supply test sentences without changing the configuration of a real GPS.
class TestStream : public Stream {
public:
const char *input = "";
int available() { return *input != 0; }
int read() {
if (*input) {
return *input++;
}
return -1;
}
int peek() { return *input ? *input : -1; }
void flush() {}
size_t write(uint8_t) { return 1; }
};
TestStream stream;
Adafruit_GPS GPS((Stream *)&stream);
bool testsPassed = false;
void setup() {
Serial.begin(115200);
// Wait briefly for the Serial Monitor on native USB boards.
while (!Serial && millis() < 5000) {
delay(10);
}
delay(250);
Serial.println(F("Adafruit GPS response timeout regression"));
uint32_t start = millis();
if (GPS.waitForSentence(PMTK_AWAKE, 10, false, 100) ||
millis() - start < 100 || millis() - start > 500) {
Serial.println(F("FAIL: silent stream timeout"));
return;
}
Serial.println(F("PASS: silent stream times out"));
start = millis();
if (GPS.waitForSentence(PMTK_AWAKE, 10, true, 100) ||
millis() - start < 100 || millis() - start > 500) {
Serial.println(F("FAIL: interrupt-driven timeout"));
return;
}
Serial.println(F("PASS: interrupt-driven wait times out"));
stream.input = "$PMTK010";
if (GPS.waitForSentence(PMTK_AWAKE, 10, false, 100)) {
Serial.println(F("FAIL: incomplete sentence was accepted"));
return;
}
Serial.println(F("PASS: incomplete sentence times out"));
// Finish and discard the partial line before testing complete sentences.
stream.input = "\r\n";
while (stream.available()) {
GPS.read();
}
GPS.lastNMEA();
stream.input = "$OTHER\r\n" PMTK_AWAKE "\r\n";
if (GPS.waitForSentence(PMTK_AWAKE, 1, false, 100)) {
Serial.println(F("FAIL: sentence limit was ignored"));
return;
}
if (!GPS.waitForSentence(PMTK_AWAKE, 1, false, 100)) {
Serial.println(F("FAIL: matching response was not accepted"));
return;
}
Serial.println(F("PASS: sentence limit and matching response"));
stream.input = PMTK_AWAKE "\r\n";
if (GPS.waitForSentence(PMTK_AWAKE, 0, false, 100) ||
GPS.waitForSentence(PMTK_AWAKE, 1, false, 0) ||
!GPS.waitForSentence(PMTK_AWAKE, 1, false, 100)) {
Serial.println(F("FAIL: zero limits consumed a response"));
return;
}
Serial.println(F("PASS: zero limits return without consuming data"));
GPS.standby();
start = millis();
Serial.println(F("Testing failed wakeup (10 seconds)..."));
if (GPS.wakeup() || millis() - start < 10000 ||
millis() - start > 10500) {
Serial.println(F("FAIL: wakeup did not time out"));
return;
}
Serial.println(F("PASS: failed wakeup returns after 10 seconds"));
stream.input = PMTK_AWAKE "\r\n";
if (!GPS.wakeup() || GPS.wakeup()) {
Serial.println(F("FAIL: wakeup retry or standby state"));
return;
}
Serial.println(F("PASS: failed wakeup can be retried successfully"));
testsPassed = true;
}
void loop() {
if (testsPassed) {
Serial.println(F("PASS: response timeout regression"));
} else {
Serial.println(F("FAIL: response timeout regression"));
}
delay(2000);
}
+27 -14
View File
@@ -429,18 +429,24 @@ char *Adafruit_GPS::lastNMEA(void) {
/**************************************************************************/
/*!
@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
*/
* @brief Wait for a specified sentence from the device
* @param wait4me
* Pointer to a string holding the desired response
* @param max Sentence limit
* (default MAXWAITSENTENCE)
* @param usingInterrupts True if interrupts read
* the GPS (default false)
* @param timeout Time limit in milliseconds (default
* 10000)
* @return True on a match, false when either limit is reached
*/
/**************************************************************************/
bool Adafruit_GPS::waitForSentence(const char *wait4me, uint8_t max,
bool usingInterrupts) {
bool usingInterrupts, uint32_t timeout) {
uint8_t i = 0;
while (i < max) {
uint32_t start = millis();
// A silent GPS, including an I2C device that NAKs, never advances i.
while (i < max && (uint32_t)(millis() - start) < timeout) {
if (!usingInterrupts)
read();
@@ -451,6 +457,7 @@ bool Adafruit_GPS::waitForSentence(const char *wait4me, uint8_t max,
if (strStartsWith(nmea, wait4me))
return true;
}
yield();
}
return false;
@@ -553,15 +560,21 @@ bool Adafruit_GPS::standby(void) {
/**************************************************************************/
/*!
@brief Wake the sensor up
@return True if woken up, false if not in standby or failed to wake
*/
* @brief Wake the sensor up
* @return True if woken up, false if not in
* standby or failed to wake
* @note A failed wakeup preserves standby state
* for retries.
*/
/**************************************************************************/
bool Adafruit_GPS::wakeup(void) {
if (inStandbyMode) {
inStandbyMode = false;
sendCommand(""); // send byte to wake it up
return waitForSentence(PMTK_AWAKE);
if (waitForSentence(PMTK_AWAKE)) {
inStandbyMode = false;
return true;
}
return false;
} else {
return false; // Returns false if not in standby mode, nothing to wakeup
}
+1 -1
View File
@@ -106,7 +106,7 @@ public:
void pause(bool b);
char *lastNMEA(void);
bool waitForSentence(const char *wait, uint8_t max = MAXWAITSENTENCE,
bool usingInterrupts = false);
bool usingInterrupts = false, uint32_t timeout = 10000);
bool LOCUS_StartLogger(void);
bool LOCUS_StopLogger(void);
bool LOCUS_ReadStatus(void);
+1 -1
View File
@@ -111,6 +111,6 @@
#define PGCMD_NOANTENNA "$PGCMD,33,0*6D" ///< don't show antenna status messages
#define MAXWAITSENTENCE \
10 ///< how long to wait when we're looking for a response
10 ///< maximum number of sentences to examine when looking for a response
/**************************************************************************/
#endif