From f9ad34f7bf9c9c4949fa07edebbda0ac3b154c36 Mon Sep 17 00:00:00 2001 From: pelgraine <140762863+pelgraine@users.noreply.github.com> Date: Tue, 28 Apr 2026 08:15:15 +1000 Subject: [PATCH 1/3] Add opportunistic clock sync from verified advert timestamps GPS-less repeaters that lose their clock on power cycle can now automatically sync from signature-verified advert timestamps. Opt-in via shouldSyncClockFromMesh() override (default: false). Only fires when local clock is clearly wrong (before Jan 2025). Closes #1329 --- examples/simple_repeater/MyMesh.h | 4 +++- src/Mesh.cpp | 14 ++++++++++++++ src/Mesh.h | 11 ++++++++++- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 8ed0317e69..9f623337eb 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -157,6 +157,8 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { return _prefs.multi_acks; } + bool shouldSyncClockFromMesh() const override { return true; } + #if ENV_INCLUDE_GPS == 1 void applyGpsPrefs() { sensors.setSettingValue("gps", _prefs.gps_enabled?"1":"0"); @@ -252,4 +254,4 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { #if defined(USE_SX1262) || defined(USE_SX1268) void setRxBoostedGain(bool enable) override; #endif -}; +}; \ No newline at end of file diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 57fee14036..5772ff0bed 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -267,6 +267,20 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { } if (is_ok) { MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): valid advertisement received!", getLogDateTime()); + + // Opportunistic clock sync: if local clock is clearly wrong and the + // verified advert carries a sane timestamp, adopt it. Only fires + // when the sub-class opts in (e.g. repeaters without hardware RTC). + if (shouldSyncClockFromMesh()) { + uint32_t local_time = _rtc->getCurrentTime(); + if (local_time < 1735689600UL // local clock before Jan 2025 (clearly wrong) + && timestamp > 1735689600UL // advert after Jan 2025 + && timestamp < 2082758400UL) { // advert before Jan 2036 + _rtc->setCurrentTime(timestamp); + MESH_DEBUG_PRINTLN(" clock synced from advert: %lu", (unsigned long)timestamp); + } + } + onAdvertRecv(pkt, id, timestamp, app_data, app_data_len); action = routeRecvPacket(pkt); } else { diff --git a/src/Mesh.h b/src/Mesh.h index f9f8786320..7a3af9c85b 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -49,6 +49,15 @@ class Mesh : public Dispatcher { */ virtual bool filterRecvFloodPacket(Packet* packet) { return false; } + /** + * \brief Whether to opportunistically sync the local RTC from verified advert timestamps. + * When enabled, if the local clock is clearly wrong (before Jan 2025) and a + * signature-verified advert carries a sane timestamp, the local clock is updated. + * Intended for GPS-less repeaters that lose their clock on power cycle. + * \returns true to enable advert-based clock sync (default: false) + */ + virtual bool shouldSyncClockFromMesh() const { return false; } + /** * \brief Check whether this packet should be forwarded (re-transmitted) or not. * Is sub-classes responsibility to make sure given packet is only transmitted ONCE (by this node) @@ -222,4 +231,4 @@ class Mesh : public Dispatcher { }; -} +} \ No newline at end of file From e8e79ab6223e47a3cace6b8c61f4196edc2005a3 Mon Sep 17 00:00:00 2001 From: pelgraine <140762863+pelgraine@users.noreply.github.com> Date: Tue, 28 Apr 2026 08:54:32 +1000 Subject: [PATCH 2/3] Clean up comments --- src/Mesh.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Mesh.h b/src/Mesh.h index 7a3af9c85b..3ac512abe9 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -49,13 +49,7 @@ class Mesh : public Dispatcher { */ virtual bool filterRecvFloodPacket(Packet* packet) { return false; } - /** - * \brief Whether to opportunistically sync the local RTC from verified advert timestamps. - * When enabled, if the local clock is clearly wrong (before Jan 2025) and a - * signature-verified advert carries a sane timestamp, the local clock is updated. - * Intended for GPS-less repeaters that lose their clock on power cycle. - * \returns true to enable advert-based clock sync (default: false) - */ + // sync clock from verified advert if local clock is clearly wrong virtual bool shouldSyncClockFromMesh() const { return false; } /** From 0570b844d40f2a20b76ae119933ef51d358c32f0 Mon Sep 17 00:00:00 2001 From: pelgraine <140762863+pelgraine@users.noreply.github.com> Date: Tue, 28 Apr 2026 08:57:10 +1000 Subject: [PATCH 3/3] Clean up comments --- src/Mesh.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 5772ff0bed..b4acf08dd7 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -268,9 +268,7 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) { if (is_ok) { MESH_DEBUG_PRINTLN("%s Mesh::onRecvPacket(): valid advertisement received!", getLogDateTime()); - // Opportunistic clock sync: if local clock is clearly wrong and the - // verified advert carries a sane timestamp, adopt it. Only fires - // when the sub-class opts in (e.g. repeaters without hardware RTC). + // sync clock from verified advert if local clock is clearly wrong (for repeaters without hardware RTC) if (shouldSyncClockFromMesh()) { uint32_t local_time = _rtc->getCurrentTime(); if (local_time < 1735689600UL // local clock before Jan 2025 (clearly wrong)