Files
MeshCore/src/helpers/MeshClockSync.h
T
mikecarper 1629725d7d Add safe ESP32 transports and Indicator UI
Reserve board-specific GPIOs, make ESP-NOW channel/format handling configurable, and coordinate WiFi, BLE, NTP, MQTT, and host actions safely.

Add the Indicator message page, exclusive transport selector, dynamic 480/320 rendering, larger transport typography, pairing cleanup, and corresponding tests and documentation.
2026-08-31 14:29:42 -07:00

132 lines
3.9 KiB
C++

#pragma once
#include <Mesh.h>
#include <helpers/ClientACL.h>
#include <helpers/IdentityStore.h>
#include <helpers/SensorManager.h>
namespace mesh {
class MeshClockSyncCallbacks {
public:
virtual ~MeshClockSyncCallbacks() = default;
// WiFi/NTP, or another authoritative source, owns the clock for this boot.
virtual bool hasAuthoritativeClock() const { return false; }
// Let a role repair any timestamp-based caches after the RTC moves.
virtual void onMeshClockAdjusted(uint32_t old_epoch, uint32_t new_epoch) {
(void)old_epoch;
(void)new_epoch;
}
};
// Autonomous LoRa clock consensus shared by sensor and room-server roles.
// Repeater firmware retains its extended implementation, which additionally
// supports delayed internet/NTP estimates and repeater-specific state repair.
class MeshClockSync {
public:
MeshClockSync(Radio& radio, MillisecondClock& millis, RTCClock& rtc,
ClientACL& acl, SensorManager& sensors,
const float& tx_delay_factor,
MeshClockSyncCallbacks* callbacks = nullptr);
void begin(FILESYSTEM* fs);
void loop();
void onManualClockSet();
void onInternetClockSet();
// Edge mode observes valid packets even when this node does not forward
// them. Normal/path mode observes only packets accepted for forwarding.
void observeVerifiedAdvert(const Packet* packet, const Identity& id,
uint32_t timestamp);
void observeGroupPacket(const Packet* packet);
void observeAcceptedFlood(const Packet* packet);
bool handleCommand(const char* command, char* reply, size_t reply_len = 160);
bool edgeMode() const { return _mesh_edge_enabled; }
bool collectionActive() const;
private:
static constexpr uint8_t SAMPLE_SLOTS = 16;
static constexpr uint8_t PATH_ID_SIZE = 8;
struct Sample {
bool active;
uint8_t source_kind;
uint8_t source_id[4];
uint8_t path_id[PATH_ID_SIZE];
uint32_t epoch;
uint32_t received_millis;
};
enum Suppression : uint8_t {
SUPPRESS_NONE = 0,
SUPPRESS_CLI = 1,
SUPPRESS_GPS = 2,
SUPPRESS_INTERNET = 3,
};
enum Result : uint8_t {
RESULT_WAITING = 0,
RESULT_COLLECTING = 1,
RESULT_INTERNET_UNAVAILABLE = 2,
RESULT_NO_CONSENSUS = 3,
RESULT_WITHIN_DRIFT = 4,
RESULT_CORRECTED_FORWARD = 5,
RESULT_CORRECTED_BACKWARD = 6,
};
Radio* _radio;
MillisecondClock* _millis;
RTCClock* _rtc;
ClientACL* _acl;
SensorManager* _sensors;
const float* _tx_delay_factor;
MeshClockSyncCallbacks* _callbacks;
FILESYSTEM* _fs;
Sample _samples[SAMPLE_SLOTS];
bool _mesh_enabled;
bool _mesh_edge_enabled;
bool _internet_enabled;
bool _complete;
bool _force_mesh_pending;
uint8_t _suppressed_by;
uint8_t _last_result;
uint8_t _last_source_sample_count;
uint8_t _last_fresh_count;
uint8_t _last_required_count;
uint8_t _required_samples;
uint32_t _drift_seconds;
uint32_t _last_estimate;
uint32_t _last_abs_drift;
uint32_t _last_millis;
uint64_t _uptime_millis;
uint64_t _next_attempt_uptime;
void resetDefaults();
void loadPrefs();
bool savePrefs();
void resetAttempt();
void suppressForBoot(uint8_t source);
static const char* suppressionName(uint8_t source);
void checkGpsOverride();
void checkClock();
uint32_t estimateTransitMillis(const Packet* packet) const;
void recordSample(uint8_t source_kind, const uint8_t source_id[4],
uint32_t epoch, const Packet* packet);
void recordPublicChannel(const Packet* packet);
bool estimate(uint32_t& epoch, uint8_t& fresh_count,
uint8_t& agreeing_count, uint8_t& required_count) const;
bool applyEstimate(uint32_t epoch, uint8_t sample_count);
void formatStatus(const char* args, char* reply, size_t reply_len) const;
void formatTable(char* reply, size_t reply_len) const;
void formatSample(int index, char* reply, size_t reply_len) const;
};
} // namespace mesh