Skip to content

Commit 4d9e093

Browse files
Wessel Nieboerweebl2000
authored andcommitted
Fix timestamp calculations when RTC clock is corrected backwards
When the RTC drifts ahead and is corrected via clock sync, stored timestamps can appear to be in the future, causing underflow in "time ago" calculations (wrapping to ~4 billion seconds). Changes: - Add safeElapsedSecs() helper that clamps to 0 if timestamp > now - Apply to neighbor "heard X ago" displays in simple_repeater - Apply to UI time displays in companion_radio - Apply to TimeSeriesData calculations in simple_sensor - Switch BaseChatMesh connection expiry from RTC to millis() The connection expiry change is the most important: using monotonic time (millis) makes it immune to RTC adjustments from GPS, NTP, or manual sync.
1 parent 84a8266 commit 4d9e093

5 files changed

Lines changed: 25 additions & 11 deletions

File tree

examples/companion_radio/ui-new/UITask.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ class HomeScreen : public UIScreen {
225225
for (int i = 0; i < UI_RECENT_LIST_SIZE; i++, y += 11) {
226226
auto a = &recent[i];
227227
if (a->name[0] == 0) continue; // empty slot
228-
int secs = _rtc->getCurrentTime() - a->recv_timestamp;
228+
uint32_t secs = safeElapsedSecs(_rtc->getCurrentTime(), a->recv_timestamp);
229229
if (secs < 60) {
230230
sprintf(tmp, "%ds", secs);
231231
} else if (secs < 60*60) {
@@ -488,7 +488,7 @@ class MsgPreviewScreen : public UIScreen {
488488

489489
auto p = &unread[head];
490490

491-
int secs = _rtc->getCurrentTime() - p->timestamp;
491+
uint32_t secs = safeElapsedSecs(_rtc->getCurrentTime(), p->timestamp);
492492
if (secs < 60) {
493493
sprintf(tmp, "%ds", secs);
494494
} else if (secs < 60*60) {

examples/simple_repeater/MyMesh.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ int MyMesh::handleRequest(ClientInfo *sender, uint32_t sender_timestamp, uint8_t
343343

344344
// add next neighbour to results
345345
auto neighbour = sorted_neighbours[index + offset];
346-
uint32_t heard_seconds_ago = getRTCClock()->getCurrentTime() - neighbour->heard_timestamp;
346+
uint32_t heard_seconds_ago = safeElapsedSecs(getRTCClock()->getCurrentTime(), neighbour->heard_timestamp);
347347
memcpy(&results_buffer[results_offset], neighbour->id.pub_key, pubkey_prefix_length); results_offset += pubkey_prefix_length;
348348
memcpy(&results_buffer[results_offset], &heard_seconds_ago, 4); results_offset += 4;
349349
memcpy(&results_buffer[results_offset], &neighbour->snr, 1); results_offset += 1;
@@ -934,7 +934,7 @@ void MyMesh::formatNeighborsReply(char *reply) {
934934
mesh::Utils::toHex(hex, neighbour->id.pub_key, 4);
935935

936936
// add next neighbour
937-
uint32_t secs_ago = getRTCClock()->getCurrentTime() - neighbour->heard_timestamp;
937+
uint32_t secs_ago = safeElapsedSecs(getRTCClock()->getCurrentTime(), neighbour->heard_timestamp);
938938
sprintf(dp, "%s:%d:%d", hex, secs_ago, neighbour->snr);
939939
while (*dp)
940940
dp++; // find end of string

examples/simple_sensor/TimeSeriesData.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "TimeSeriesData.h"
2+
#include <helpers/ArduinoHelpers.h>
23

34
void TimeSeriesData::recordData(mesh::RTCClock* clock, float value) {
45
uint32_t now = clock->getCurrentTime();
@@ -12,7 +13,7 @@ void TimeSeriesData::recordData(mesh::RTCClock* clock, float value) {
1213

1314
void TimeSeriesData::calcMinMaxAvg(mesh::RTCClock* clock, uint32_t start_secs_ago, uint32_t end_secs_ago, MinMaxAvg* dest, uint8_t channel, uint8_t lpp_type) const {
1415
int i = next, n = num_slots;
15-
uint32_t ago = clock->getCurrentTime() - last_timestamp;
16+
uint32_t ago = safeElapsedSecs(clock->getCurrentTime(), last_timestamp);
1617
int num_values = 0;
1718
float total = 0.0f;
1819

src/helpers/ArduinoHelpers.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@
33
#include <Mesh.h>
44
#include <Arduino.h>
55

6+
// Safe elapsed time calculation that handles clock corrections (when RTC is set backwards).
7+
// Returns 0 if recorded_timestamp is in the "future" relative to current_time.
8+
inline uint32_t safeElapsedSecs(uint32_t current_time, uint32_t recorded_timestamp) {
9+
if (recorded_timestamp > current_time) {
10+
return 0; // Clock was corrected backwards; treat as "just now"
11+
}
12+
return current_time - recorded_timestamp;
13+
}
14+
615
class VolatileRTCClock : public mesh::RTCClock {
716
uint32_t base_time;
817
uint64_t accumulator;

src/helpers/BaseChatMesh.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,7 @@ bool BaseChatMesh::startConnection(const ContactInfo& contact, uint16_t keep_ali
609609
uint32_t interval = connections[use_idx].keep_alive_millis = ((uint32_t)keep_alive_secs)*1000;
610610
connections[use_idx].next_ping = futureMillis(interval);
611611
connections[use_idx].expected_ack = 0;
612-
connections[use_idx].last_activity = getRTCClock()->getCurrentTime();
612+
connections[use_idx].last_activity = _ms->getMillis(); // use monotonic time for connection expiry
613613
return true; // success
614614
}
615615

@@ -635,7 +635,7 @@ bool BaseChatMesh::hasConnectionTo(const uint8_t* pub_key) {
635635
void BaseChatMesh::markConnectionActive(const ContactInfo& contact) {
636636
for (int i = 0; i < MAX_CONNECTIONS; i++) {
637637
if (connections[i].keep_alive_millis > 0 && connections[i].server_id.matches(contact.id)) {
638-
connections[i].last_activity = getRTCClock()->getCurrentTime();
638+
connections[i].last_activity = _ms->getMillis(); // use monotonic time for connection expiry
639639

640640
// re-schedule next KEEP_ALIVE, now that we have heard from server
641641
connections[i].next_ping = futureMillis(connections[i].keep_alive_millis);
@@ -649,7 +649,7 @@ ContactInfo* BaseChatMesh::checkConnectionsAck(const uint8_t* data) {
649649
if (connections[i].keep_alive_millis > 0 && memcmp(&connections[i].expected_ack, data, 4) == 0) {
650650
// yes, got an ack for our keep_alive request!
651651
connections[i].expected_ack = 0;
652-
connections[i].last_activity = getRTCClock()->getCurrentTime();
652+
connections[i].last_activity = _ms->getMillis(); // use monotonic time for connection expiry
653653

654654
// re-schedule next KEEP_ALIVE, now that we have heard from server
655655
connections[i].next_ping = futureMillis(connections[i].keep_alive_millis);
@@ -666,9 +666,13 @@ void BaseChatMesh::checkConnections() {
666666
for (int i = 0; i < MAX_CONNECTIONS; i++) {
667667
if (connections[i].keep_alive_millis == 0) continue; // unused slot
668668

669-
uint32_t now = getRTCClock()->getCurrentTime();
670-
uint32_t expire_secs = (connections[i].keep_alive_millis / 1000) * 5 / 2; // 2.5 x keep_alive interval
671-
if (now >= connections[i].last_activity + expire_secs) {
669+
// Use monotonic time (millis) for connection expiry - immune to RTC clock changes.
670+
// Note: This assumes light sleep mode where millis() continues to increment.
671+
// Deep sleep would reset millis(), but BaseChatMesh is only used by companion_radio
672+
// which uses light sleep.
673+
unsigned long now = _ms->getMillis();
674+
uint32_t expire_millis = (connections[i].keep_alive_millis * 5) / 2; // 2.5 x keep_alive interval
675+
if ((now - connections[i].last_activity) >= expire_millis) {
672676
// connection now lost
673677
connections[i].keep_alive_millis = 0;
674678
connections[i].next_ping = 0;

0 commit comments

Comments
 (0)