Skip to content

next_gps_update incorrectly handles millis() wrapping across firmware variants #2878

Description

@spinda

Various hardware variants use some variety of the following code for scheduling GPS updates:

static long next_gps_update = 0;
_nmea->loop();
if (millis() > next_gps_update) {
if (gps_active && _nmea->isValid()) {
node_lat = ((double)_nmea->getLatitude())/1000000.;
node_lon = ((double)_nmea->getLongitude())/1000000.;
node_altitude = ((double)_nmea->getAltitude()) / 1000.0;
//Serial.printf("lat %f lon %f\r\n", _lat, _lon);
}
next_gps_update = millis() + 1000;
}

There are two mismatches between this and Arduino's documentation for millis():

  1. millis() returns an unsigned long, not a signed long. This doesn't end up having much of an effect in practice because C++ automatically casts next_gps_update to an unsigned long before computing the millis() > next_gps_update check.
  2. Arduino documents the following warning for millis():

    millis() will wrap around to 0 after about 50 days (micros in about 70 minutes).

~50 days is not a terribly unusual amount of uptime for a mesh node. What happens when we hit that mark? This snippet in Ideone demonstrates the issue. If we're currently within 999 milliseconds of the max unsigned 32-bit integer value (4,294,967,295), then the computed value of next_gps_update wraps around to 0. This means that until millis() itself also wraps back around to 0, the millis() > next_gpu_update check passes every single loop iteration.


What would a correct version look like? I would say:

  1. Define next_gps_update as unsigned long, not long, to properly match the return value of millis().
  2. Use modular arithmetic to perform the test, so long(millis() - next_gps_update) >= 0 instead of millis() > next_gps_update, like Dispatcher already does. This is robust to wrapping.

I whipped up a quick Ideone snippet here to demonstrate the above.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions