Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions BirdsEye.ino
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,13 @@ struct GpsData {

volatile bool gpsDataFresh = false; // Set by PVT callback, cleared by GPS_LOOP()

// GPS wake validation: tracks whether GPS is producing data after wake from sleep.
// GPS_WAKE() sets gpsWakeTime and clears gpsWakeValidated. GPS_LOOP() sets
// gpsWakeValidated=true on first PVT arrival. If 5 seconds pass without PVT,
// GPS_LOOP() triggers baud recovery and reconfiguration.
unsigned long gpsWakeTime = 0;
bool gpsWakeValidated = true; // Start true (validated at boot by GPS_SETUP)

double crossingPointALat = 0.00;
double crossingPointALng = 0.00;
double crossingPointBLat = 0.00;
Expand Down
17 changes: 14 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ loop() ~250 Hz
- **Legacy CSV** (`.dove`): flat CSV with track/layout/direction in filename.
- Time helpers: `getGpsTimeInMilliseconds()`, `getGpsUnixTimestampMillis()`.
- 64-bit timestamps are manually converted to strings (Arduino lacks `%llu`).
- **Sleep wake hardening**: `GPS_WAKE()` clears stale `gpsDataFresh`/`gpsData.fix`,
re-applies VALSET config via `GPS_RECONFIGURE()`, and arms a 5 s PVT watchdog.
If no PVT arrives, `GPS_BAUD_RECOVERY()` re-negotiates baud (9600→57600) and
reconfigures. The SAM-M10Q has no flash; all config is volatile RAM only.

### 2. Tachometer (`tachometer.ino`)

Expand Down Expand Up @@ -289,9 +293,16 @@ loop() ~250 Hz
- **RPM wake**: tach ISR fires → `exitSleepMode(true)` → straight into
race mode with logging enabled, Lap Anything CourseManager created.
- **Button wake**: any button → `exitSleepMode(false)` → main menu.
- **`exitSleepMode()`**: re-enables IMU, GPS wake (0xFF + 100 ms), display
on, GPS serial timer restarted. RPM wake skips menu and goes directly
to race mode.
- **`exitSleepMode()`**: re-enables IMU, GPS wake, display on, GPS serial
timer restarted. RPM wake skips menu and goes directly to race mode.
- **GPS wake hardening** (`GPS_WAKE()`): clears stale `gpsDataFresh` and
`gpsData.fix`, wakes module (0xFF + 100 ms), re-applies full VALSET
config via `GPS_RECONFIGURE()` (idempotent), starts PVT watchdog.
The SAM-M10Q has no flash — all config is volatile RAM. If V_BCKP drops
during backup (cranking brownout, loose connector), module reverts to
9600 baud NMEA. The watchdog in `GPS_LOOP()` detects missing PVT after
5 s and triggers `GPS_BAUD_RECOVERY()` which re-negotiates baud rate
(tries 57600, falls back to 9600 → switch → reconfigure).
- **Charging mode**: USB detected during sleep → show battery screen for
10 s, then display off. Button re-shows for another 10 s. GPS periodic
checks and WFE skipped while charging.
Expand Down
133 changes: 133 additions & 0 deletions gps_functions.ino
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,30 @@ void GPS_LOOP() {
myGNSS.checkUblox();
myGNSS.checkCallbacks();

#ifdef ENABLE_NEW_UI
// PVT arrival watchdog: after GPS_WAKE(), if no PVT data arrives within
// 5 seconds, the module likely lost its config during backup (V_BCKP
// dropped → reverted to 9600 baud NMEA). Attempt baud recovery.
if (!gpsWakeValidated) {
if (gpsDataFresh) {
// PVT arrived — module is alive and configured correctly
gpsWakeValidated = true;
debugln(F("GPS wake validated: PVT received"));
} else if (millis() - gpsWakeTime >= 5000) {
debugln(F("GPS wake FAILED: no PVT after 5s, attempting recovery"));
if (GPS_BAUD_RECOVERY()) {
// Recovery succeeded — restart the watchdog for validation
gpsWakeTime = millis();
debugln(F("GPS recovery succeeded, waiting for PVT..."));
} else {
// Recovery failed — give up, mark validated to stop retrying
gpsWakeValidated = true;
debugln(F("GPS recovery FAILED — GPS unavailable this session"));
}
}
}
#endif

if (gpsDataFresh) {
gpsDataFresh = false;

Expand Down Expand Up @@ -557,17 +581,126 @@ void GPS_SLEEP() {
myGNSS.powerOff(0); // 0 = indefinite sleep until woken
}

/**
* @brief Re-apply all GPS module configuration via VALSET.
*
* The SAM-M10Q has no flash — all config lives in volatile RAM. If V_BCKP
* drops during backup mode (battery sag, loose connector, cranking brownout),
* the module reverts to factory defaults (9600 baud, NMEA, 1Hz). This
* function re-applies the full configuration. It's fast (~50ms total) and
* idempotent — safe to call even if config was retained.
*/
void GPS_RECONFIGURE() {
myGNSS.setUART1Output(COM_TYPE_UBX);
myGNSS.setNavigationFrequency(GPS_NAV_RATE_HZ);
myGNSS.setDynamicModel(DYN_MODEL_AUTOMOTIVE);
myGNSS.enableGNSS(true, SFE_UBLOX_GNSS_ID_GPS);
myGNSS.enableGNSS(false, SFE_UBLOX_GNSS_ID_SBAS);
myGNSS.enableGNSS(false, SFE_UBLOX_GNSS_ID_GALILEO);
myGNSS.enableGNSS(false, SFE_UBLOX_GNSS_ID_BEIDOU);
myGNSS.enableGNSS(false, SFE_UBLOX_GNSS_ID_GLONASS);
debugln(F("GPS config re-applied (VALSET)"));
}

/**
* @brief Attempt to recover GPS communication when module has reverted to 9600 baud.
*
* If V_BCKP was lost during backup, the module reverts to 9600 baud.
* Our UART is at 57600 so communication is broken. This function:
* 1. Stops the serial timer (switch to direct Serial1 mode)
* 2. Tries to talk at 57600 first (maybe module is fine)
* 3. If that fails, switches to 9600, sends baud change command, switches back
* 4. Re-applies full config and restarts the serial timer
*
* @return true if communication was recovered, false if unrecoverable
*/
bool GPS_BAUD_RECOVERY() {
debugln(F("GPS baud recovery: attempting..."));

// Stop timer so GpsBufferedStream passes through to Serial1 directly.
// The SparkFun library needs direct serial access for begin()/setSerialRate().
stopGpsSerialTimer();

// First: try at current baud — module might be fine, just slow to start
if (myGNSS.begin(gpsStream)) {
debugln(F("GPS baud recovery: module responding at 57600"));
GPS_RECONFIGURE();
myGNSS.setAutoPVTcallbackPtr(&onPVTReceived); // begin() resets library state
gpsRxHead = 0;
gpsRxTail = 0;
startGpsSerialTimer();
return true;
}

// Module not responding at 57600 — try 9600 (factory default)
debugln(F("GPS baud recovery: trying 9600..."));
GPS_SERIAL.end();
delay(50);
GPS_SERIAL.begin(9600);
delay(100);

if (myGNSS.begin(gpsStream)) {
// Found at 9600 — switch it back to 57600
debugln(F("GPS baud recovery: found at 9600, switching to 57600"));
myGNSS.setSerialRate(GPS_BAUD_RATE);
delay(100);
GPS_SERIAL.end();
delay(50);
GPS_SERIAL.begin(GPS_BAUD_RATE);
delay(100);

if (myGNSS.begin(gpsStream)) {
debugln(F("GPS baud recovery: reconnected at 57600"));
GPS_RECONFIGURE();
myGNSS.setAutoPVTcallbackPtr(&onPVTReceived);
gpsRxHead = 0;
gpsRxTail = 0;
startGpsSerialTimer();
return true;
}
debugln(F("GPS baud recovery: lost after baud switch!"));
} else {
debugln(F("GPS baud recovery: not found at 9600 either"));
}

// Restore 57600 even on failure so other code doesn't break
GPS_SERIAL.end();
delay(50);
GPS_SERIAL.begin(GPS_BAUD_RATE);
delay(50);
gpsRxHead = 0;
gpsRxTail = 0;
startGpsSerialTimer();
return false;
}

void GPS_WAKE() {
if (!gpsInitialized) return;

// Clear stale state from before sleep / periodic checks
gpsDataFresh = false;
gpsData.fix = false;

// Any UART activity on RX wakes u-blox from powerOff backup mode
GPS_SERIAL.write(0xFF);
delay(100);

// Reset buffer pointers (stale data from before sleep is useless)
gpsRxHead = 0;
gpsRxTail = 0;

startGpsSerialTimer(); // Resume serial drain ISR

// Re-apply configuration in case module lost RAM during backup.
// This is idempotent — if config was retained, these are no-ops.
GPS_RECONFIGURE();

myGNSS.checkUblox();

// Start PVT arrival watchdog. GPS_LOOP() will validate within 5 seconds,
// and trigger baud recovery if no PVT data arrives.
gpsWakeTime = millis();
gpsWakeValidated = false;
}

void GPS_SLEEP_PERIODIC_CHECK() {
Expand Down