From d85ec5563e0767cea869d23121b518daf52ced90 Mon Sep 17 00:00:00 2001 From: Olexandr Tyshchenko Date: Fri, 30 Oct 2026 00:03:05 +0200 Subject: [PATCH] GPS: optional UART baud rate autodetection (GPS_BAUD_AUTODETECT) Serial GPS is opened at GPS_BAUD_RATE (default 9600) and treated as detected as soon as any byte arrives. A module running at another rate (e.g. 38400) still produces bytes, so it is 'detected', but the NMEA parser only ever sees garbage: 'gps diag' shows ok:0 with hundreds of bad sentences, and the node never gets a fix or a time sync. With GPS_BAUD_AUTODETECT defined, once data is seen the UART is probed at the configured rate first and then at 9600, 115200, 38400, 57600, 19200 and 4800, keeping the first rate that yields an NMEA sentence with a valid checksum (up to 2 s per rate). If none does, the configured rate is restored. Boards whose configured rate is already right pay no extra boot time; without the define nothing changes. --- .../sensors/EnvironmentSensorManager.cpp | 71 +++++++++++++++++-- 1 file changed, 66 insertions(+), 5 deletions(-) diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 0dc892c3a52..011aa612ba6 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -747,15 +747,66 @@ bool EnvironmentSensorManager::setSettingValue(const char* name, const char* val } #if ENV_INCLUDE_GPS +#ifndef GPS_BAUD_RATE + #define GPS_DEFAULT_BAUD 9600 +#else + #define GPS_DEFAULT_BAUD GPS_BAUD_RATE +#endif + +#ifdef GPS_BAUD_AUTODETECT +// True if a complete NMEA sentence with a valid checksum arrives within timeout_ms. +static bool gpsSeesValidNMEA(uint32_t timeout_ms) { + char line[100]; + int len = -1; // -1 = waiting for '$' + unsigned long start = millis(); + while (millis() - start < timeout_ms) { + if (!Serial1.available()) { delay(2); continue; } + char c = Serial1.read(); + if (c == '$') { len = 0; continue; } + if (len < 0) continue; + if (c == '\r' || c == '\n') { + line[len] = 0; + char* star = strchr(line, '*'); + if (star && strlen(star) >= 3) { + uint8_t sum = 0; + for (char* p = line; p < star; p++) sum ^= (uint8_t)*p; + if (sum == (uint8_t)strtoul(star + 1, NULL, 16)) return true; + } + len = -1; + } else if (len < (int)sizeof(line) - 1) { + line[len++] = c; + } else { + len = -1; // longer than any NMEA sentence: wrong baud rate + } + } + return false; +} + +// Probes common GNSS baud rates, starting with the configured one, and leaves +// Serial1 open at the first rate that yields valid NMEA. Falls back to the +// configured rate if none does. Returns the detected rate, or 0. +static uint32_t detectGPSBaud() { + static const uint32_t bauds[] = { GPS_DEFAULT_BAUD, 9600, 115200, 38400, 57600, 19200, 4800 }; + for (size_t i = 0; i < sizeof(bauds) / sizeof(bauds[0]); i++) { + bool tried = false; + for (size_t j = 0; j < i; j++) tried |= (bauds[j] == bauds[i]); + if (tried) continue; + Serial1.end(); + Serial1.begin(bauds[i]); + while (Serial1.available()) Serial1.read(); + if (gpsSeesValidNMEA(2000)) return bauds[i]; + } + Serial1.end(); + Serial1.begin(GPS_DEFAULT_BAUD); + return 0; +} +#endif + void EnvironmentSensorManager::initBasicGPS() { Serial1.setPins(PIN_GPS_TX, PIN_GPS_RX); - #ifdef GPS_BAUD_RATE - Serial1.begin(GPS_BAUD_RATE); - #else - Serial1.begin(9600); - #endif + Serial1.begin(GPS_DEFAULT_BAUD); // Try to detect if GPS is physically connected to determine if we should expose the setting _location->begin(); @@ -781,6 +832,16 @@ void EnvironmentSensorManager::initBasicGPS() { if (gps_detected) { MESH_DEBUG_PRINTLN("GPS detected"); + #ifdef GPS_BAUD_AUTODETECT + // Bytes arriving does not mean the baud rate is right: at the wrong rate the + // parser only sees garbage and never gets a fix. + uint32_t baud = detectGPSBaud(); + if (baud) { + MESH_DEBUG_PRINTLN("GPS baud rate: %lu", (unsigned long)baud); + } else { + MESH_DEBUG_PRINTLN("GPS baud rate not detected, using %lu", (unsigned long)GPS_DEFAULT_BAUD); + } + #endif #ifdef PERSISTANT_GPS gps_active = true; return;