From 8d660243a089801c8024b3bd15a18ed9f5e18695 Mon Sep 17 00:00:00 2001 From: ladyada Date: Tue, 15 Sep 2026 10:55:34 -0400 Subject: [PATCH 1/2] Store GPS source and sentence tables in flash Use shared PROGMEM tables with flash-aware lookup and copying. Preserve the source IDs, sentence IDs and matching order while saving 110 bytes of RAM in the Uno SD logger. Co-authored-by: Limor Fried --- src/Adafruit_GPS.h | 23 +++++------------------ src/NMEA_parse.cpp | 39 +++++++++++++++++++++++++++------------ 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/Adafruit_GPS.h b/src/Adafruit_GPS.h index 4b4dd80..6da7517 100644 --- a/src/Adafruit_GPS.h +++ b/src/Adafruit_GPS.h @@ -243,7 +243,7 @@ private: // NMEA_data.cpp void data_init(); // NMEA_parse.cpp - const char *tokenOnList(char *token, const char **list); + const char *tokenOnList(char *token, const char list[][4]); bool parseCoord(char *p, nmea_float_t *angleDegrees = NULL, nmea_float_t *angle = NULL, int32_t *angle_fixed = NULL, char *dir = NULL); @@ -253,23 +253,10 @@ private: bool parseAntenna(char *); bool isEmpty(char *pStart); - // used by check() for validity tests, room for future expansion - const char *sources[9] = {"II", "WI", "GP", "PG", "GL", - "GA", "GN", "P", "ZZZ"}; ///< valid source ids -#ifdef NMEA_EXTENSIONS - const char *sentences_parsed[21] = {"GGA", "GLL", "GSA", "RMC", "DBT", "HDM", - "HDT", "MDA", "MTW", "MWV", "RMB", "TOP", - "TXT", "VHW", "VLW", "VPW", "VWR", "WCV", - "XTE", "ZZZ"}; ///< parseable sentence ids - const char *sentences_known[15] = { - "APB", "DPT", "GSV", "HDG", "MWD", "ROT", - "RPM", "RSA", "VDR", "VTG", "ZDA", "ZZZ"}; ///< known, but not parseable -#else // make the lists short to save memory - const char *sentences_parsed[6] = {"GGA", "GLL", "GSA", "RMC", - "TOP", "ZZZ"}; ///< parseable sentence ids - const char *sentences_known[4] = {"DBT", "HDM", "HDT", - "ZZZ"}; ///< known, but not parseable -#endif + // Shared flash tables avoid RAM copies of both the IDs and their pointers. + static const char sources[][4] PROGMEM; ///< valid source ids + static const char sentences_parsed[][4] PROGMEM; ///< parseable sentence ids + static const char sentences_known[][4] PROGMEM; ///< known, but not parseable // Make all of these times far in the past by setting them near the middle of // the millis() range. Timing assumes that sentences are parsed promptly. diff --git a/src/NMEA_parse.cpp b/src/NMEA_parse.cpp index e9285b0..6806260 100644 --- a/src/NMEA_parse.cpp +++ b/src/NMEA_parse.cpp @@ -21,6 +21,22 @@ /**************************************************************************/ #include + +const char Adafruit_GPS::sources[][4] PROGMEM = {"II", "WI", "GP", "PG", "GL", + "GA", "GN", "P", "ZZZ"}; +#ifdef NMEA_EXTENSIONS +const char Adafruit_GPS::sentences_parsed[][4] PROGMEM = { + "GGA", "GLL", "GSA", "RMC", "DBT", "HDM", "HDT", "MDA", "MTW", "MWV", + "RMB", "TOP", "TXT", "VHW", "VLW", "VPW", "VWR", "WCV", "XTE", "ZZZ"}; +const char Adafruit_GPS::sentences_known[][4] PROGMEM = { + "APB", "DPT", "GSV", "HDG", "MWD", "ROT", + "RPM", "RSA", "VDR", "VTG", "ZDA", "ZZZ"}; +#else // make the lists short to save flash on small boards +const char Adafruit_GPS::sentences_parsed[][4] PROGMEM = {"GGA", "GLL", "GSA", + "RMC", "TOP", "ZZZ"}; +const char Adafruit_GPS::sentences_known[][4] PROGMEM = {"DBT", "HDM", "HDT", + "ZZZ"}; +#endif #include /**************************************************************************/ @@ -648,20 +664,20 @@ bool Adafruit_GPS::check(char *nmea) { char *p = nmea + 1; const char *src = tokenOnList(p, sources); if (src) { - strcpy(thisSource, src); + strcpy_P(thisSource, src); thisCheck += NMEA_HAS_SOURCE; } else return false; - p += strlen(src); + p += strlen_P(src); // extract sentence id and check if parsed const char *snc = tokenOnList(p, sentences_parsed); if (snc) { - strcpy(thisSentence, snc); + strcpy_P(thisSentence, snc); thisCheck += NMEA_HAS_SENTENCE_P + NMEA_HAS_SENTENCE; } else { // check if known snc = tokenOnList(p, sentences_known); if (snc) { - strcpy(thisSentence, snc); + strcpy_P(thisSentence, snc); thisCheck += NMEA_HAS_SENTENCE; return false; // known but not parsed } else { @@ -676,18 +692,17 @@ bool Adafruit_GPS::check(char *nmea) { /*! @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 + @param list A table in program memory, with the final entry starting "ZZ" + @return Program-memory pointer to the found token, or NULL if it fails */ /**************************************************************************/ -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 +const char *Adafruit_GPS::tokenOnList(char *token, const char list[][4]) { + for (int i = 0; i < 1000; i++) { + if (pgm_read_byte(list[i]) == 'Z' && pgm_read_byte(list[i] + 1) == 'Z') + break; // stop at terminator // test for a match on the sentence name - if (!strncmp((const char *)list[i], (const char *)token, strlen(list[i]))) + if (!strncmp_P(token, list[i], strlen_P(list[i]))) return list[i]; - i++; } return NULL; // couldn't find a match } From 38ad3ffcaccb20381c4fd9ec6f47b4cee68365cb Mon Sep 17 00:00:00 2001 From: ladyada Date: Tue, 15 Sep 2026 11:13:37 -0400 Subject: [PATCH 2/2] Keep flash table declarations compatible with Doxygen Co-authored-by: Limor Fried --- src/Adafruit_GPS.h | 6 +++--- src/NMEA_parse.cpp | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Adafruit_GPS.h b/src/Adafruit_GPS.h index 6da7517..9acddca 100644 --- a/src/Adafruit_GPS.h +++ b/src/Adafruit_GPS.h @@ -254,9 +254,9 @@ private: bool isEmpty(char *pStart); // Shared flash tables avoid RAM copies of both the IDs and their pointers. - static const char sources[][4] PROGMEM; ///< valid source ids - static const char sentences_parsed[][4] PROGMEM; ///< parseable sentence ids - static const char sentences_known[][4] PROGMEM; ///< known, but not parseable + static const char PROGMEM sources[][4]; ///< valid source ids + static const char PROGMEM sentences_parsed[][4]; ///< parseable sentence ids + static const char PROGMEM sentences_known[][4]; ///< known, but not parseable // Make all of these times far in the past by setting them near the middle of // the millis() range. Timing assumes that sentences are parsed promptly. diff --git a/src/NMEA_parse.cpp b/src/NMEA_parse.cpp index 6806260..a984dba 100644 --- a/src/NMEA_parse.cpp +++ b/src/NMEA_parse.cpp @@ -22,19 +22,19 @@ #include -const char Adafruit_GPS::sources[][4] PROGMEM = {"II", "WI", "GP", "PG", "GL", +const char PROGMEM Adafruit_GPS::sources[][4] = {"II", "WI", "GP", "PG", "GL", "GA", "GN", "P", "ZZZ"}; #ifdef NMEA_EXTENSIONS -const char Adafruit_GPS::sentences_parsed[][4] PROGMEM = { +const char PROGMEM Adafruit_GPS::sentences_parsed[][4] = { "GGA", "GLL", "GSA", "RMC", "DBT", "HDM", "HDT", "MDA", "MTW", "MWV", "RMB", "TOP", "TXT", "VHW", "VLW", "VPW", "VWR", "WCV", "XTE", "ZZZ"}; -const char Adafruit_GPS::sentences_known[][4] PROGMEM = { +const char PROGMEM Adafruit_GPS::sentences_known[][4] = { "APB", "DPT", "GSV", "HDG", "MWD", "ROT", "RPM", "RSA", "VDR", "VTG", "ZDA", "ZZZ"}; #else // make the lists short to save flash on small boards -const char Adafruit_GPS::sentences_parsed[][4] PROGMEM = {"GGA", "GLL", "GSA", +const char PROGMEM Adafruit_GPS::sentences_parsed[][4] = {"GGA", "GLL", "GSA", "RMC", "TOP", "ZZZ"}; -const char Adafruit_GPS::sentences_known[][4] PROGMEM = {"DBT", "HDM", "HDT", +const char PROGMEM Adafruit_GPS::sentences_known[][4] = {"DBT", "HDM", "HDT", "ZZZ"}; #endif #include