fix: harden CLI and GPS handling

This commit is contained in:
mikecarper committed 2026-08-27 17:59:44 -07:00
1 parent b1f8945e1c
commit 74002db4e5
9 files changed
+192 -94

No files matched your search

+76 -75
View File
@@ -261,6 +261,11 @@ static bool configKeyMatches(const char* config, const char* key) {
&& (config[key_len] == 0 || config[key_len] == ' ' || config[key_len] == '.');
}
static bool configKeyEquals(const char* config, const char* key) {
return mesh::cli::matchNoArgCommand(config, key)
== mesh::cli::NoArgCommandMatch::Exact;
}
static bool isAdvancedRetryConfig(const char* config) {
return configKeyMatches(config, "direct.retry.heard")
|| configKeyMatches(config, "direct.retry.margin")
@@ -3594,16 +3599,11 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
sprintf(reply, "OK - powersaving %s", enabled ? "on" : "off");
} else if (memcmp(config, "radio ", 6) == 0) {
strcpy(tmp, &config[6]);
const char *parts[4];
int num = mesh::Utils::parseTextParts(tmp, parts, 4);
float freq = 0.0f;
float bw = 0.0f;
uint8_t sf = num > 2 ? atoi(parts[2]) : 0;
uint8_t cr = num > 3 ? atoi(parts[3]) : 0;
if (num == 4
&& mesh::cli::parseDecimalStrict(parts[0], freq)
&& mesh::cli::parseDecimalStrict(parts[1], bw)
uint8_t sf = 0;
uint8_t cr = 0;
if (mesh::cli::parseRadioTupleStrict(&config[6], freq, bw, sf, cr)
&& freq >= 150.0f && freq <= 2500.0f
&& sf >= 5 && sf <= 12 && cr >= 5 && cr <= 8
&& isValidLoRaBandwidth(bw)) {
@@ -4315,7 +4315,7 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
mesh::cli::RecentRepeaterGetQuery recent_query;
const mesh::cli::RecentRepeaterGetMatch recent_query_match =
mesh::cli::parseRecentRepeaterGet(config, recent_query);
if (memcmp(config, "dutycycle", 9) == 0) {
if (configKeyEquals(config, "dutycycle")) {
float dc = 100.0f / (_prefs->airtime_factor + 1.0f);
int dc_int = (int)dc;
int dc_frac = (int)((dc - dc_int) * 10.0f + 0.5f);
@@ -4326,48 +4326,48 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
#else
strcpy(reply, "Error: unsupported on this platform");
#endif
} else if (memcmp(config, "af", 2) == 0) {
} else if (configKeyEquals(config, "af")) {
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->airtime_factor));
} else if (memcmp(config, "int.thresh", 10) == 0) {
} else if (configKeyEquals(config, "int.thresh")) {
sprintf(reply, "> %d", (uint32_t) _prefs->interference_threshold);
} else if (memcmp(config, "cad", 3) == 0) {
} else if (configKeyEquals(config, "cad")) {
sprintf(reply, "> %s. # channel busy: %u", _prefs->cad_enabled ? "on" : "off", _board->n_cad_busy);
} else if (memcmp(config, "agc.reset.interval", 18) == 0) {
} else if (configKeyEquals(config, "agc.reset.interval")) {
sprintf(reply, "> %d", ((uint32_t) _prefs->agc_reset_interval) * 4);
} else if (memcmp(config, "multi.acks", 10) == 0) {
} else if (configKeyEquals(config, "multi.acks")) {
sprintf(reply, "> %d", (uint32_t) _prefs->multi_acks);
} else if (memcmp(config, "allow.read.only", 15) == 0) {
} else if (configKeyEquals(config, "allow.read.only")) {
sprintf(reply, "> %s", _prefs->allow_read_only ? "on" : "off");
} else if (memcmp(config, "telemetry.access", 16) == 0) {
} else if (configKeyEquals(config, "telemetry.access")) {
sprintf(reply, "> %s", _prefs->telemetry_access == TELEMETRY_ACCESS_ACL ? "acl" : "all");
} else if (memcmp(config, "flood.advert.interval", 21) == 0) {
} else if (configKeyEquals(config, "flood.advert.interval")) {
sprintf(reply, "> %d", ((uint32_t) _prefs->flood_advert_interval));
} else if (memcmp(config, "advert.interval", 15) == 0) {
} else if (configKeyEquals(config, "advert.interval")) {
sprintf(reply, "> %d", ((uint32_t) _prefs->advert_interval) * 2);
} else if (memcmp(config, "guest.password", 14) == 0) {
} else if (configKeyEquals(config, "guest.password")) {
sprintf(reply, "> %s", _prefs->guest_password);
} else if (sender_timestamp == 0 && memcmp(config, "prv.key", 7) == 0) { // from serial command line only
} else if (sender_timestamp == 0 && configKeyEquals(config, "prv.key")) { // from serial command line only
uint8_t prv_key[PRV_KEY_SIZE];
int len = _callbacks->getSelfId().writeTo(prv_key, PRV_KEY_SIZE);
mesh::Utils::toHex(tmp, prv_key, len);
sprintf(reply, "> %s", tmp);
} else if (memcmp(config, "name", 4) == 0) {
} else if (configKeyEquals(config, "name")) {
sprintf(reply, "> %s", _prefs->node_name);
} else if (memcmp(config, "repeat", 6) == 0) {
} else if (configKeyEquals(config, "repeat")) {
sprintf(reply, "> %s", _prefs->disable_fwd ? "off" : "on");
} else if (memcmp(config, "lat", 3) == 0) {
} else if (configKeyEquals(config, "lat")) {
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->node_lat));
} else if (memcmp(config, "lon", 3) == 0) {
} else if (configKeyEquals(config, "lon")) {
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->node_lon));
} else if (memcmp(config, "radio.rxgain", 12) == 0) {
} else if (configKeyEquals(config, "radio.rxgain")) {
sprintf(reply, "> %s", _prefs->rx_boosted_gain ? "on" : "off");
} else if (memcmp(config, "radio.fem.rxgain", 16) == 0) {
} else if (configKeyEquals(config, "radio.fem.rxgain")) {
if (!_board->canControlLoRaFemLna()) {
strcpy(reply, "Error: unsupported");
} else {
sprintf(reply, "> %s", _board->isLoRaFemLnaEnabled() ? "on" : "off");
}
} else if (memcmp(config, "radio.fem.txgain", 16) == 0) {
} else if (configKeyEquals(config, "radio.fem.txgain")) {
if (!_board->canControlLoRaFemPaGain()) {
strcpy(reply, "Error: unsupported");
} else {
@@ -4391,22 +4391,22 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
(unsigned)_prefs->rx_ps_preamble,
(unsigned long)_prefs->rx_ps_rx_us,
(unsigned long)_prefs->rx_ps_sleep_us);
} else if (memcmp(config, "radio.rxps", 10) == 0) { // RX PowerSaving
} else if (configKeyEquals(config, "radio.rxps")) { // RX PowerSaving
ensureRxPowerSavingDefaults(&_prefs->rx_ps_rx_us, &_prefs->rx_ps_sleep_us);
sprintf(reply, "> %s,%lu,%lu", _prefs->rx_powersaving_enabled ? "on" : "off",
(unsigned long)_prefs->rx_ps_rx_us, (unsigned long)_prefs->rx_ps_sleep_us);
} else if (memcmp(config, "rxps.wd", 7) == 0) {
} else if (configKeyEquals(config, "rxps.wd")) {
uint32_t wd_soft, wd_hard;
_callbacks->getRxPsWatchdogCounts(&wd_soft, &wd_hard);
sprintf(reply, "> soft=%lu,hard=%lu", (unsigned long)wd_soft, (unsigned long)wd_hard);
} else if (memcmp(config, "radio", 5) == 0) {
} else if (configKeyEquals(config, "radio")) {
char freq[16], bw[16];
strcpy(freq, StrHelper::ftoa(_prefs->freq));
strcpy(bw, StrHelper::ftoa3(_prefs->bw));
sprintf(reply, "> %s,%s,%d,%d", freq, bw, (uint32_t)_prefs->sf, (uint32_t)_prefs->cr);
} else if (memcmp(config, "rxdelay", 7) == 0) {
} else if (configKeyEquals(config, "rxdelay")) {
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->rx_delay_base));
} else if (memcmp(config, "txdelay", 7) == 0) {
} else if (configKeyEquals(config, "txdelay")) {
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->tx_delay_factor));
} else if (strcmp(config, "flood.channel.data.hops") == 0) {
char hops[8];
@@ -4416,59 +4416,60 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
char hops[8];
formatFloodChannelHops(hops, _prefs->flood_channel_data_max_hops);
sprintf(reply, "> %s %s", _prefs->flood_channel_data_enabled ? "on" : "off", hops);
} else if (memcmp(config, "flood.max.advert", 16) == 0) {
} else if (configKeyEquals(config, "flood.max.advert")) {
sprintf(reply, "> %d", (uint32_t)_prefs->flood_max_advert);
} else if (memcmp(config, "flood.max.unscoped", 18) == 0) {
} else if (configKeyEquals(config, "flood.max.unscoped")) {
sprintf(reply, "> %d", (uint32_t)_prefs->flood_max_unscoped);
} else if (memcmp(config, "flood.max", 9) == 0) {
} else if (configKeyEquals(config, "flood.max")) {
sprintf(reply, "> %d", (uint32_t)_prefs->flood_max);
} else if (memcmp(config, "direct.txdelay", 14) == 0) {
} else if (configKeyEquals(config, "direct.txdelay")) {
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor));
} else if (memcmp(config, "retry.preset", 12) == 0) {
} else if (configKeyEquals(config, "retry.preset")) {
sprintf(reply, "> %s", retryPresetName(_prefs->retry_preset));
} else if (memcmp(config, "direct.retry", 12) == 0 && (config[12] == 0 || config[12] == ' ')) {
} else if (configKeyEquals(config, "direct.retry")) {
sprintf(reply, "> %s", _prefs->direct_retry_enabled ? "on" : "off");
} else if (memcmp(config, "direct.retry.heard", 18) == 0) {
} else if (configKeyEquals(config, "direct.retry.heard")) {
sprintf(reply, "> %s", _prefs->direct_retry_recent_enabled ? "on" : "off");
} else if (memcmp(config, "direct.retry.margin", 19) == 0) {
} else if (configKeyEquals(config, "direct.retry.margin")) {
char margin[12];
formatSnrDbX4(margin, sizeof(margin), _prefs->direct_retry_snr_margin_x4);
sprintf(reply, "> %s", margin);
} else if (memcmp(config, "direct.retry.count", 18) == 0) {
} else if (configKeyEquals(config, "direct.retry.count")) {
sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_attempts);
} else if (memcmp(config, "direct.retry.base", 17) == 0) {
} else if (configKeyEquals(config, "direct.retry.base")) {
sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_base_ms);
} else if (memcmp(config, "direct.retry.step", 17) == 0) {
} else if (configKeyEquals(config, "direct.retry.step")) {
sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_step_ms);
} else if (memcmp(config, "flood.retry.count", 17) == 0) {
} else if (configKeyEquals(config, "flood.retry.count")) {
sprintf(reply, "> %d", (uint32_t)_prefs->flood_retry_attempts);
} else if (memcmp(config, "flood.retry.path", 16) == 0) {
} else if (configKeyEquals(config, "flood.retry.path")) {
char path_gate[8];
formatFloodRetryPathGate(path_gate, _prefs->flood_retry_max_path);
sprintf(reply, "> %s", path_gate);
} else if (memcmp(config, "flood.retry.group.path", 22) == 0) {
} else if (configKeyEquals(config, "flood.retry.group.path")) {
char path_gate[8];
formatFloodRetryPathGate(path_gate, _prefs->flood_retry_group_max_path);
sprintf(reply, "> %s", path_gate);
} else if (memcmp(config, "flood.retry.prefixes", 20) == 0) {
} else if (configKeyEquals(config, "flood.retry.prefixes")) {
formatFloodRetryPrefixList(tmp, _prefs->flood_retry_prefixes, FLOOD_RETRY_PREFIX_SLOTS);
sprintf(reply, "> %s", tmp[0] ? tmp : "none");
} else if (memcmp(config, "flood.retry.ignore", 18) == 0) {
} else if (configKeyEquals(config, "flood.retry.ignore")) {
formatFloodRetryPrefixList(tmp, _prefs->flood_retry_ignore_prefixes, FLOOD_RETRY_IGNORE_PREFIXES);
sprintf(reply, "> %s", tmp[0] ? tmp : "none");
} else if (memcmp(config, "flood.retry.advert", 18) == 0) {
} else if (configKeyEquals(config, "flood.retry.advert")) {
sprintf(reply, "> %s", _prefs->flood_retry_advert_enabled ? "on" : "off");
} else if (memcmp(config, "flood.retry.bridge", 18) == 0) {
} else if (configKeyEquals(config, "flood.retry.bridge")) {
sprintf(reply, "> %s", _prefs->flood_retry_bridge_enabled ? "on" : "off");
} else if (memcmp(config, "flood.retry.bucket.", 19) == 0) {
uint8_t bucket = atoi(&config[19]);
if (bucket >= 1 && bucket <= FLOOD_RETRY_BRIDGE_BUCKETS) {
int32_t bucket = 0;
if (mesh::cli::parseIntegerStrict(&config[19], bucket)
&& bucket >= 1 && bucket <= FLOOD_RETRY_BRIDGE_BUCKETS) {
formatFloodRetryPrefixList(tmp, _prefs->flood_retry_bridge_buckets[bucket - 1], FLOOD_RETRY_BUCKET_PREFIXES);
sprintf(reply, "> %s", tmp[0] ? tmp : "none");
} else {
sprintf(reply, "Error, bucket 1-%d", FLOOD_RETRY_BRIDGE_BUCKETS);
}
} else if (memcmp(config, "direct.retry.cr", 15) == 0) {
} else if (configKeyEquals(config, "direct.retry.cr")) {
if (!_prefs->direct_retry_cr_enabled) {
strcpy(reply, "> off");
} else if (!_callbacks->supportsAdvancedRetryConfig()) {
@@ -4488,7 +4489,7 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
recent_query.search_prefix_len);
} else if (recent_query_match == mesh::cli::RecentRepeaterGetMatch::Invalid) {
strcpy(reply, "Error, use: get recent.repeaters [page] | search <2|4|6 hex> [page]");
} else if (memcmp(config, "owner.info", 10) == 0) {
} else if (configKeyEquals(config, "owner.info")) {
*reply++ = '>';
*reply++ = ' ';
const char* sp = _prefs->owner_info;
@@ -4497,9 +4498,9 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
sp++;
}
*reply = 0; // set null terminator
} else if (memcmp(config, "path.hash.mode", 14) == 0) {
} else if (configKeyEquals(config, "path.hash.mode")) {
sprintf(reply, "> %d", (uint32_t)_prefs->path_hash_mode);
} else if (memcmp(config, "loop.detect", 11) == 0) {
} else if (configKeyEquals(config, "loop.detect")) {
if (_prefs->loop_detect == LOOP_DETECT_OFF) {
strcpy(reply, "> off");
} else if (_prefs->loop_detect == LOOP_DETECT_MINIMAL) {
@@ -4509,16 +4510,16 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
} else {
strcpy(reply, "> strict");
}
} else if (memcmp(config, "tx", 2) == 0 && (config[2] == 0 || config[2] == ' ')) {
} else if (configKeyEquals(config, "tx")) {
sprintf(reply, "> %d", (int32_t) _prefs->tx_power_dbm);
} else if (memcmp(config, "freq", 4) == 0) {
} else if (configKeyEquals(config, "freq")) {
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->freq));
} else if (memcmp(config, "public.key", 10) == 0) {
} else if (configKeyEquals(config, "public.key")) {
strcpy(reply, "> ");
mesh::Utils::toHex(&reply[2], _callbacks->getSelfId().pub_key, PUB_KEY_SIZE);
} else if (memcmp(config, "role", 4) == 0) {
} else if (configKeyEquals(config, "role")) {
sprintf(reply, "> %s", _callbacks->getRole());
} else if (memcmp(config, "bridge.type", 11) == 0) {
} else if (configKeyEquals(config, "bridge.type")) {
sprintf(reply, "> %s",
#ifdef WITH_RS232_BRIDGE
"rs232"
@@ -4531,26 +4532,26 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
#endif
);
#ifdef WITH_BRIDGE
} else if (memcmp(config, "bridge.enabled", 14) == 0) {
} else if (configKeyEquals(config, "bridge.enabled")) {
sprintf(reply, "> %s", _prefs->bridge_enabled ? "on" : "off");
} else if (memcmp(config, "bridge.delay", 12) == 0) {
} else if (configKeyEquals(config, "bridge.delay")) {
sprintf(reply, "> %d", (uint32_t)_prefs->bridge_delay);
} else if (memcmp(config, "bridge.source", 13) == 0) {
} else if (configKeyEquals(config, "bridge.source")) {
sprintf(reply, "> %s", _prefs->bridge_pkt_src ? "logRx" : "logTx");
#endif
#ifdef WITH_RS232_BRIDGE
} else if (memcmp(config, "bridge.baud", 11) == 0) {
} else if (configKeyEquals(config, "bridge.baud")) {
sprintf(reply, "> %d", (uint32_t)_prefs->bridge_baud);
} else if (memcmp(config, "bridge.uart", 11) == 0) {
} else if (configKeyEquals(config, "bridge.uart")) {
sprintf(reply, "> %d", (uint32_t)_prefs->bridge_uart);
#endif
#ifdef WITH_ESPNOW_BRIDGE
} else if (memcmp(config, "bridge.channel", 14) == 0) {
} else if (configKeyEquals(config, "bridge.channel")) {
sprintf(reply, "> %d", (uint32_t)_prefs->bridge_channel);
} else if (memcmp(config, "bridge.secret", 13) == 0) {
} else if (configKeyEquals(config, "bridge.secret")) {
sprintf(reply, "> %s", _prefs->bridge_secret);
#endif
} else if (memcmp(config, "bootloader.ver", 14) == 0) {
} else if (configKeyEquals(config, "bootloader.ver")) {
#ifdef NRF52_PLATFORM
char ver[32];
if (_board->getBootloaderVersion(ver, sizeof(ver))) {
@@ -4561,7 +4562,7 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
#else
strcpy(reply, "Error: unsupported");
#endif
} else if (memcmp(config, "adc.multiplier", 14) == 0) {
} else if (configKeyEquals(config, "adc.multiplier")) {
float adc_mult = _board->getAdcMultiplier();
if (adc_mult == 0.0f) {
strcpy(reply, "Error: unsupported");
@@ -4569,29 +4570,29 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
sprintf(reply, "> %.3f", adc_mult);
}
// Power management commands
} else if (memcmp(config, "pwrmgt.support", 14) == 0) {
} else if (configKeyEquals(config, "pwrmgt.support")) {
#ifdef NRF52_POWER_MANAGEMENT
strcpy(reply, "> supported");
#else
strcpy(reply, "> unsupported");
#endif
} else if (memcmp(config, "pwrmgt.source", 13) == 0) {
} else if (configKeyEquals(config, "pwrmgt.source")) {
#ifdef NRF52_POWER_MANAGEMENT
strcpy(reply, _board->isExternalPowered() ? "> external" : "> battery");
#else
strcpy(reply, "ERROR: Power management not supported");
#endif
} else if (memcmp(config, "pwrmgt.bootreason", 17) == 0) {
} else if (configKeyEquals(config, "pwrmgt.bootreason")) {
sprintf(reply, "> Reset: %s; Shutdown: %s",
_board->getResetReasonString(_board->getResetReason()),
_board->getShutdownReasonString(_board->getShutdownReason()));
} else if (memcmp(config, "pwrmgt.bootmv", 13) == 0) {
} else if (configKeyEquals(config, "pwrmgt.bootmv")) {
#ifdef NRF52_POWER_MANAGEMENT
sprintf(reply, "> %u mV", _board->getBootVoltage());
#else
strcpy(reply, "ERROR: Power management not supported");
#endif
} else if (memcmp(config, "reboot.interval", 15) == 0) {
} else if (configKeyEquals(config, "reboot.interval")) {
if (_prefs->reboot_interval == 0) {
strcpy(reply, "disabled");
} else {
+44
View File
@@ -0,0 +1,44 @@
#pragma once
#include <stdint.h>
namespace mesh {
namespace gps {
inline bool isLeapYear(uint16_t year) {
return (year % 4U) == 0U
&& ((year % 100U) != 0U || (year % 400U) == 0U);
}
inline uint8_t daysInMonth(uint16_t year, uint8_t month) {
static const uint8_t days[] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
};
if (month < 1 || month > 12) return 0;
if (month == 2 && isLeapYear(year)) return 29;
return days[month - 1];
}
// Reject the zero/partial dates emitted before an NMEA receiver has acquired
// UTC. The upper bound also keeps LocationProvider's signed-long timestamp
// valid on 32-bit targets.
inline bool isValidNmeaDateTime(uint16_t year, uint8_t month, uint8_t day,
uint8_t hour, uint8_t minute,
uint8_t second) {
if (year < 2020 || year > 2038
|| month < 1 || month > 12
|| day < 1 || day > daysInMonth(year, month)
|| hour > 23 || minute > 59 || second > 59) {
return false;
}
if (year < 2038) return true;
// 2,147,483,647 is 2038-01-19 03:14:07 UTC.
if (month != 1 || day > 19) return false;
if (day < 19 || hour < 3) return true;
if (hour > 3 || minute > 14) return false;
return minute < 14 || second <= 7;
}
} // namespace gps
} // namespace mesh
@@ -4,6 +4,11 @@
#include <MicroNMEA.h>
#include <RTClib.h>
#include <helpers/RefCountedDigitalPin.h>
#include <helpers/sensors/GpsTimeValidation.h>
#ifndef GPS_LOOP_MAX_BYTES
#define GPS_LOOP_MAX_BYTES 96
#endif
#ifndef GPS_EN
#ifdef PIN_GPS_EN
@@ -133,9 +138,20 @@ public :
long satellitesCount() override { return nmea.getNumSatellites(); }
bool isValid() override { return nmea.isValid(); }
long getTimestamp() override {
DateTime dt(nmea.getYear(), nmea.getMonth(),nmea.getDay(),nmea.getHour(),nmea.getMinute(),nmea.getSecond());
return dt.unixtime();
long getTimestamp() override {
const uint16_t year = nmea.getYear();
const uint8_t month = nmea.getMonth();
const uint8_t day = nmea.getDay();
const uint8_t hour = nmea.getHour();
const uint8_t minute = nmea.getMinute();
const uint8_t second = nmea.getSecond();
if (!mesh::gps::isValidNmeaDateTime(year, month, day, hour, minute,
second)) {
return 0;
}
DateTime dt(year, month, day, hour, minute, second);
const uint32_t timestamp = dt.unixtime();
return timestamp <= 0x7FFFFFFFUL ? static_cast<long>(timestamp) : 0;
}
void sendSentence(const char *sentence) override {
@@ -144,12 +160,14 @@ public :
void loop() override {
while (_gps_serial->available()) {
size_t processed = 0;
while (_gps_serial->available() && processed < GPS_LOOP_MAX_BYTES) {
char c = _gps_serial->read();
#ifdef GPS_NMEA_DEBUG
if (mesh::isUsbLoggingEnabled()) mesh::usbLoggingPort().print(c);
#endif
nmea.process(c);
processed++;
}
if (!isValid()) time_valid = 0;
@@ -160,18 +178,21 @@ public :
if (!_time_sync_needed && _clock != NULL && (millis() - _last_time_sync) > TIME_SYNC_INTERVAL) {
_time_sync_needed = true;
}
const long timestamp = getTimestamp();
if (isValid() && satellitesCount() >= 5 && timestamp > 0) {
time_valid++;
} else {
time_valid = 0;
}
if (_time_sync_needed && time_valid > 2) {
if (_clock != NULL) {
_clock->setCurrentTime(getTimestamp());
_clock->setCurrentTime(timestamp);
markTimeSyncApplied();
_time_sync_needed = false;
_last_time_sync = millis();
_last_valid_time_sync = _clock->getCurrentTime();
}
}
if (isValid() && satellitesCount() >= 5) {
time_valid ++;
}
}
}
};