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
9 changes: 2 additions & 7 deletions examples/companion_radio/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1003,13 +1003,8 @@ void MyMesh::handleCmdFrame(size_t len) {
} else if (cmd_frame[0] == CMD_SET_DEVICE_TIME && len >= 5) {
uint32_t secs;
memcpy(&secs, &cmd_frame[1], 4);
uint32_t curr = getRTCClock()->getCurrentTime();
if (secs >= curr) {
getRTCClock()->setCurrentTime(secs);
writeOKFrame();
} else {
writeErrFrame(ERR_CODE_ILLEGAL_ARG);
}
getRTCClock()->setCurrentTime(secs);
writeOKFrame();
} else if (cmd_frame[0] == CMD_SEND_SELF_ADVERT) {
mesh::Packet* pkt;
if (_prefs.advert_loc_policy == ADVERT_LOC_NONE) {
Expand Down
4 changes: 2 additions & 2 deletions examples/companion_radio/ui-new/UITask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ class HomeScreen : public UIScreen {
for (int i = 0; i < UI_RECENT_LIST_SIZE; i++, y += 11) {
auto a = &recent[i];
if (a->name[0] == 0) continue; // empty slot
int secs = _rtc->getCurrentTime() - a->recv_timestamp;
uint32_t secs = safeElapsedSecs(_rtc->getCurrentTime(), a->recv_timestamp);
if (secs < 60) {
sprintf(tmp, "%ds", secs);
} else if (secs < 60*60) {
Expand Down Expand Up @@ -480,7 +480,7 @@ class MsgPreviewScreen : public UIScreen {

auto p = &unread[0];

int secs = _rtc->getCurrentTime() - p->timestamp;
uint32_t secs = safeElapsedSecs(_rtc->getCurrentTime(), p->timestamp);
if (secs < 60) {
sprintf(tmp, "%ds", secs);
} else if (secs < 60*60) {
Expand Down
4 changes: 2 additions & 2 deletions examples/simple_repeater/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ int MyMesh::handleRequest(ClientInfo *sender, uint32_t sender_timestamp, uint8_t

// add next neighbour to results
auto neighbour = sorted_neighbours[index + offset];
uint32_t heard_seconds_ago = getRTCClock()->getCurrentTime() - neighbour->heard_timestamp;
uint32_t heard_seconds_ago = safeElapsedSecs(getRTCClock()->getCurrentTime(), neighbour->heard_timestamp);
memcpy(&results_buffer[results_offset], neighbour->id.pub_key, pubkey_prefix_length); results_offset += pubkey_prefix_length;
memcpy(&results_buffer[results_offset], &heard_seconds_ago, 4); results_offset += 4;
memcpy(&results_buffer[results_offset], &neighbour->snr, 1); results_offset += 1;
Expand Down Expand Up @@ -852,7 +852,7 @@ void MyMesh::formatNeighborsReply(char *reply) {
mesh::Utils::toHex(hex, neighbour->id.pub_key, 4);

// add next neighbour
uint32_t secs_ago = getRTCClock()->getCurrentTime() - neighbour->heard_timestamp;
uint32_t secs_ago = safeElapsedSecs(getRTCClock()->getCurrentTime(), neighbour->heard_timestamp);
sprintf(dp, "%s:%d:%d", hex, secs_ago, neighbour->snr);
while (*dp)
dp++; // find end of string
Expand Down
9 changes: 2 additions & 7 deletions examples/simple_secure_chat/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,8 @@ class MyMesh : public BaseChatMesh, ContactVisitor {
}

void setClock(uint32_t timestamp) {
uint32_t curr = getRTCClock()->getCurrentTime();
if (timestamp > curr) {
getRTCClock()->setCurrentTime(timestamp);
Serial.println(" (OK - clock set!)");
} else {
Serial.println(" (ERR: clock cannot go backwards)");
}
getRTCClock()->setCurrentTime(timestamp);
Serial.println(" (OK - clock set!)");
}

void importCard(const char* command) {
Expand Down
3 changes: 2 additions & 1 deletion examples/simple_sensor/TimeSeriesData.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "TimeSeriesData.h"
#include <helpers/ArduinoHelpers.h>

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

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 {
int i = next, n = num_slots;
uint32_t ago = clock->getCurrentTime() - last_timestamp;
uint32_t ago = safeElapsedSecs(clock->getCurrentTime(), last_timestamp);
int num_values = 0;
float total = 0.0f;

Expand Down
9 changes: 9 additions & 0 deletions src/helpers/ArduinoHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
#include <Mesh.h>
#include <Arduino.h>

// Safe elapsed time calculation that handles clock corrections (when RTC is set backwards).
// Returns 0 if recorded_timestamp is in the "future" relative to current_time.
inline uint32_t safeElapsedSecs(uint32_t current_time, uint32_t recorded_timestamp) {
if (recorded_timestamp > current_time) {
return 0; // Clock was corrected backwards; treat as "just now"
}
return current_time - recorded_timestamp;
}

class VolatileRTCClock : public mesh::RTCClock {
uint32_t base_time;
uint64_t accumulator;
Expand Down
16 changes: 10 additions & 6 deletions src/helpers/BaseChatMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ bool BaseChatMesh::startConnection(const ContactInfo& contact, uint16_t keep_ali
uint32_t interval = connections[use_idx].keep_alive_millis = ((uint32_t)keep_alive_secs)*1000;
connections[use_idx].next_ping = futureMillis(interval);
connections[use_idx].expected_ack = 0;
connections[use_idx].last_activity = getRTCClock()->getCurrentTime();
connections[use_idx].last_activity = _ms->getMillis(); // use monotonic time for connection expiry
return true; // success
}

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

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

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

uint32_t now = getRTCClock()->getCurrentTime();
uint32_t expire_secs = (connections[i].keep_alive_millis / 1000) * 5 / 2; // 2.5 x keep_alive interval
if (now >= connections[i].last_activity + expire_secs) {
// Use monotonic time (millis) for connection expiry - immune to RTC clock changes.
// Note: This assumes light sleep mode where millis() continues to increment.
// Deep sleep would reset millis(), but BaseChatMesh is only used by companion_radio
// which uses light sleep.
unsigned long now = _ms->getMillis();
uint32_t expire_millis = (connections[i].keep_alive_millis * 5) / 2; // 2.5 x keep_alive interval
if ((now - connections[i].last_activity) >= expire_millis) {
// connection now lost
connections[i].keep_alive_millis = 0;
connections[i].next_ping = 0;
Expand Down
26 changes: 8 additions & 18 deletions src/helpers/CommonCLI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,15 +190,10 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
_callbacks->sendSelfAdvertisement(1500); // longer delay, give CLI response time to be sent first
strcpy(reply, "OK - Advert sent");
} else if (memcmp(command, "clock sync", 10) == 0) {
uint32_t curr = getRTCClock()->getCurrentTime();
if (sender_timestamp > curr) {
getRTCClock()->setCurrentTime(sender_timestamp + 1);
uint32_t now = getRTCClock()->getCurrentTime();
DateTime dt = DateTime(now);
sprintf(reply, "OK - clock set: %02d:%02d - %d/%d/%d UTC", dt.hour(), dt.minute(), dt.day(), dt.month(), dt.year());
} else {
strcpy(reply, "ERR: clock cannot go backwards");
}
getRTCClock()->setCurrentTime(sender_timestamp + 1);
uint32_t now = getRTCClock()->getCurrentTime();
DateTime dt = DateTime(now);
sprintf(reply, "OK - clock set: %02d:%02d - %d/%d/%d UTC", dt.hour(), dt.minute(), dt.day(), dt.month(), dt.year());
} else if (memcmp(command, "start ota", 9) == 0) {
if (!_board->startOTAUpdate(_prefs->node_name, reply)) {
strcpy(reply, "Error");
Expand All @@ -209,15 +204,10 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
sprintf(reply, "%02d:%02d - %d/%d/%d UTC", dt.hour(), dt.minute(), dt.day(), dt.month(), dt.year());
} else if (memcmp(command, "time ", 5) == 0) { // set time (to epoch seconds)
uint32_t secs = _atoi(&command[5]);
uint32_t curr = getRTCClock()->getCurrentTime();
if (secs > curr) {
getRTCClock()->setCurrentTime(secs);
uint32_t now = getRTCClock()->getCurrentTime();
DateTime dt = DateTime(now);
sprintf(reply, "OK - clock set: %02d:%02d - %d/%d/%d UTC", dt.hour(), dt.minute(), dt.day(), dt.month(), dt.year());
} else {
strcpy(reply, "(ERR: clock cannot go backwards)");
}
getRTCClock()->setCurrentTime(secs);
uint32_t now = getRTCClock()->getCurrentTime();
DateTime dt = DateTime(now);
sprintf(reply, "OK - clock set: %02d:%02d - %d/%d/%d UTC", dt.hour(), dt.minute(), dt.day(), dt.month(), dt.year());
} else if (memcmp(command, "neighbors", 9) == 0) {
_callbacks->formatNeighborsReply(reply);
} else if (memcmp(command, "neighbor.remove ", 16) == 0) {
Expand Down