Refactor and consolidate time window logic (#4826)

* Refactor and consolidate windowing logic

* Trunk

* Fixes

* More

* Fix braces and remove unused now variables.

There was a brace in src/mesh/RadioLibInterface.cpp that was breaking
compile on some architectures.

Additionally, there were some brace errors in
src/modules/Telemetry/AirQualityTelemetry.cpp
src/modules/Telemetry/EnvironmentTelemetry.cpp
src/mesh/wifi/WiFiAPClient.cpp

Move throttle include in WifiAPClient.cpp to top.

Add Default.h to sleep.cpp

rest of files just remove unused now variables.

* Remove a couple more meows

---------

Co-authored-by: Tom Fifield <tom@tomfifield.net>
This commit is contained in:
Ben Meadors 2024-09-23 08:58:14 -05:00 committed by GitHub
parent 1afd61698b
commit e8829b8f52
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 143 additions and 104 deletions

View File

@ -13,6 +13,7 @@
#include "power.h" #include "power.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "PowerFSM.h" #include "PowerFSM.h"
#include "Throttle.h"
#include "buzz/buzz.h" #include "buzz/buzz.h"
#include "configuration.h" #include "configuration.h"
#include "main.h" #include "main.h"
@ -30,6 +31,7 @@
#if HAS_WIFI #if HAS_WIFI
#include <WiFi.h> #include <WiFi.h>
#endif #endif
#endif #endif
#ifndef DELAY_FOREVER #ifndef DELAY_FOREVER
@ -244,7 +246,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
config.power.adc_multiplier_override > 0 ? config.power.adc_multiplier_override : ADC_MULTIPLIER; config.power.adc_multiplier_override > 0 ? config.power.adc_multiplier_override : ADC_MULTIPLIER;
// Do not call analogRead() often. // Do not call analogRead() often.
const uint32_t min_read_interval = 5000; const uint32_t min_read_interval = 5000;
if (millis() - last_read_time_ms > min_read_interval) { if (!Throttle::isWithinTimespanMs(last_read_time_ms, min_read_interval)) {
last_read_time_ms = millis(); last_read_time_ms = millis();
uint32_t raw = 0; uint32_t raw = 0;

View File

@ -1,6 +1,8 @@
#include "SerialConsole.h" #include "SerialConsole.h"
#include "Default.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "PowerFSM.h" #include "PowerFSM.h"
#include "Throttle.h"
#include "configuration.h" #include "configuration.h"
#include "time.h" #include "time.h"
@ -47,7 +49,7 @@ SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port), con
#if defined(ARCH_NRF52) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(ARCH_RP2040) #if defined(ARCH_NRF52) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(ARCH_RP2040)
time_t timeout = millis(); time_t timeout = millis();
while (!Port) { while (!Port) {
if ((millis() - timeout) < 5000) { if (Throttle::isWithinTimespanMs(timeout, FIVE_SECONDS_MS)) {
delay(100); delay(100);
} else { } else {
break; break;
@ -73,7 +75,7 @@ void SerialConsole::flush()
bool SerialConsole::checkIsConnected() bool SerialConsole::checkIsConnected()
{ {
uint32_t now = millis(); uint32_t now = millis();
return (now - lastContactMsec) < SERIAL_CONNECTION_TIMEOUT; return Throttle::isWithinTimespanMs(lastContactMsec, SERIAL_CONNECTION_TIMEOUT);
} }
/** /**

View File

@ -6,6 +6,7 @@
#include "NodeDB.h" #include "NodeDB.h"
#include "PowerMon.h" #include "PowerMon.h"
#include "RTC.h" #include "RTC.h"
#include "Throttle.h"
#include "main.h" // pmu_found #include "main.h" // pmu_found
#include "sleep.h" #include "sleep.h"
@ -206,7 +207,7 @@ GPS_RESPONSE GPS::getACKCas(uint8_t class_id, uint8_t msg_id, uint32_t waitMilli
// ACK-NACK| 0xBA | 0xCE | 0x04 | 0x00 | 0x05 | 0x00 | 0xXX | 0xXX | 0x00 | 0x00 | 0xXX | 0xXX | 0xXX | 0xXX | // ACK-NACK| 0xBA | 0xCE | 0x04 | 0x00 | 0x05 | 0x00 | 0xXX | 0xXX | 0x00 | 0x00 | 0xXX | 0xXX | 0xXX | 0xXX |
// ACK-ACK | 0xBA | 0xCE | 0x04 | 0x00 | 0x05 | 0x01 | 0xXX | 0xXX | 0x00 | 0x00 | 0xXX | 0xXX | 0xXX | 0xXX | // ACK-ACK | 0xBA | 0xCE | 0x04 | 0x00 | 0x05 | 0x01 | 0xXX | 0xXX | 0x00 | 0x00 | 0xXX | 0xXX | 0xXX | 0xXX |
while (millis() - startTime < waitMillis) { while (Throttle::isWithinTimespanMs(startTime, waitMillis)) {
if (_serial_gps->available()) { if (_serial_gps->available()) {
buffer[bufferPos++] = _serial_gps->read(); buffer[bufferPos++] = _serial_gps->read();
@ -275,7 +276,7 @@ GPS_RESPONSE GPS::getACK(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis)
buf[9] += buf[8]; buf[9] += buf[8];
} }
while (millis() - startTime < waitMillis) { while (Throttle::isWithinTimespanMs(startTime, waitMillis)) {
if (ack > 9) { if (ack > 9) {
#ifdef GPS_DEBUG #ifdef GPS_DEBUG
LOG_DEBUG("\n"); LOG_DEBUG("\n");
@ -332,7 +333,7 @@ int GPS::getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t
uint32_t startTime = millis(); uint32_t startTime = millis();
uint16_t needRead; uint16_t needRead;
while (millis() - startTime < waitMillis) { while (Throttle::isWithinTimespanMs(startTime, waitMillis)) {
if (_serial_gps->available()) { if (_serial_gps->available()) {
int c = _serial_gps->read(); int c = _serial_gps->read();
switch (ubxFrameCounter) { switch (ubxFrameCounter) {
@ -1420,16 +1421,15 @@ bool GPS::lookForTime()
#ifdef GNSS_AIROHA #ifdef GNSS_AIROHA
uint8_t fix = reader.fixQuality(); uint8_t fix = reader.fixQuality();
uint32_t now = millis();
if (fix > 0) { if (fix > 0) {
if (lastFixStartMsec > 0) { if (lastFixStartMsec > 0) {
if ((now - lastFixStartMsec) < GPS_FIX_HOLD_TIME) { if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) {
return false; return false;
} else { } else {
clearBuffer(); clearBuffer();
} }
} else { } else {
lastFixStartMsec = now; lastFixStartMsec = millis();
return false; return false;
} }
} else { } else {
@ -1473,16 +1473,15 @@ bool GPS::lookForLocation()
#ifdef GNSS_AIROHA #ifdef GNSS_AIROHA
if ((config.position.gps_update_interval * 1000) >= (GPS_FIX_HOLD_TIME * 2)) { if ((config.position.gps_update_interval * 1000) >= (GPS_FIX_HOLD_TIME * 2)) {
uint8_t fix = reader.fixQuality(); uint8_t fix = reader.fixQuality();
uint32_t now = millis();
if (fix > 0) { if (fix > 0) {
if (lastFixStartMsec > 0) { if (lastFixStartMsec > 0) {
if ((now - lastFixStartMsec) < GPS_FIX_HOLD_TIME) { if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) {
return false; return false;
} else { } else {
clearBuffer(); clearBuffer();
} }
} else { } else {
lastFixStartMsec = now; lastFixStartMsec = millis();
return false; return false;
} }
} else { } else {
@ -1712,4 +1711,4 @@ void GPS::toggleGpsMode()
enable(); enable();
} }
} }
#endif // Exclude GPS #endif // Exclude GPS

View File

@ -2,6 +2,7 @@
#include "configuration.h" #include "configuration.h"
#include "detect/ScanI2C.h" #include "detect/ScanI2C.h"
#include "main.h" #include "main.h"
#include <Throttle.h>
#include <sys/time.h> #include <sys/time.h>
#include <time.h> #include <time.h>
@ -127,7 +128,7 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate)
} else if (q == RTCQualityGPS) { } else if (q == RTCQualityGPS) {
shouldSet = true; shouldSet = true;
LOG_DEBUG("Reapplying GPS time: %ld secs\n", printableEpoch); LOG_DEBUG("Reapplying GPS time: %ld secs\n", printableEpoch);
} else if (q == RTCQualityNTP && (now - lastSetMsec) > (12 * 60 * 60 * 1000UL)) { } else if (q == RTCQualityNTP && !Throttle::isWithinTimespanMs(lastSetMsec, (12 * 60 * 60 * 1000UL))) {
// Every 12 hrs we will slam in a new NTP or Phone GPS / NTP time, to correct for local RTC clock drift // Every 12 hrs we will slam in a new NTP or Phone GPS / NTP time, to correct for local RTC clock drift
shouldSet = true; shouldSet = true;
LOG_DEBUG("Reapplying external time to correct clock drift %ld secs\n", printableEpoch); LOG_DEBUG("Reapplying external time to correct clock drift %ld secs\n", printableEpoch);

View File

@ -1,3 +1,4 @@
#include "Throttle.h"
#include "configuration.h" #include "configuration.h"
#if defined(USE_EINK) && defined(USE_EINK_DYNAMICDISPLAY) #if defined(USE_EINK) && defined(USE_EINK_DYNAMICDISPLAY)
@ -231,15 +232,13 @@ void EInkDynamicDisplay::checkForPromotion()
// Is it too soon for another frame of this type? // Is it too soon for another frame of this type?
void EInkDynamicDisplay::checkRateLimiting() void EInkDynamicDisplay::checkRateLimiting()
{ {
uint32_t now = millis();
// Sanity check: millis() overflow - just let the update run.. // Sanity check: millis() overflow - just let the update run..
if (previousRunMs > now) if (previousRunMs > millis())
return; return;
// Skip update: too soon for BACKGROUND // Skip update: too soon for BACKGROUND
if (frameFlags == BACKGROUND) { if (frameFlags == BACKGROUND) {
if (now - previousRunMs < EINK_LIMIT_RATE_BACKGROUND_SEC * 1000) { if (Throttle::isWithinTimespanMs(previousRunMs, EINK_LIMIT_RATE_BACKGROUND_SEC * 1000)) {
refresh = SKIPPED; refresh = SKIPPED;
reason = EXCEEDED_RATELIMIT_FULL; reason = EXCEEDED_RATELIMIT_FULL;
return; return;
@ -252,7 +251,7 @@ void EInkDynamicDisplay::checkRateLimiting()
// Skip update: too soon for RESPONSIVE // Skip update: too soon for RESPONSIVE
if (frameFlags & RESPONSIVE) { if (frameFlags & RESPONSIVE) {
if (now - previousRunMs < EINK_LIMIT_RATE_RESPONSIVE_SEC * 1000) { if (Throttle::isWithinTimespanMs(previousRunMs, EINK_LIMIT_RATE_RESPONSIVE_SEC * 1000)) {
refresh = SKIPPED; refresh = SKIPPED;
reason = EXCEEDED_RATELIMIT_FAST; reason = EXCEEDED_RATELIMIT_FAST;
LOG_DEBUG("refresh=SKIPPED, reason=EXCEEDED_RATELIMIT_FAST, frameFlags=0x%x\n", frameFlags); LOG_DEBUG("refresh=SKIPPED, reason=EXCEEDED_RATELIMIT_FAST, frameFlags=0x%x\n", frameFlags);

View File

@ -22,6 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "Screen.h" #include "Screen.h"
#include "../userPrefs.h" #include "../userPrefs.h"
#include "PowerMon.h" #include "PowerMon.h"
#include "Throttle.h"
#include "configuration.h" #include "configuration.h"
#if HAS_SCREEN #if HAS_SCREEN
#include <OLEDDisplay.h> #include <OLEDDisplay.h>
@ -117,6 +118,7 @@ static bool heartbeat = false;
#define SCREEN_HEIGHT display->getHeight() #define SCREEN_HEIGHT display->getHeight()
#include "graphics/ScreenFonts.h" #include "graphics/ScreenFonts.h"
#include <Throttle.h>
#define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2) #define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2)
@ -1949,7 +1951,7 @@ int32_t Screen::runOnce()
if (showingNormalScreen) { if (showingNormalScreen) {
// standard screen loop handling here // standard screen loop handling here
if (config.display.auto_screen_carousel_secs > 0 && if (config.display.auto_screen_carousel_secs > 0 &&
(millis() - lastScreenTransition) > (config.display.auto_screen_carousel_secs * 1000)) { !Throttle::isWithinTimespanMs(lastScreenTransition, config.display.auto_screen_carousel_secs * 1000)) {
// If an E-Ink display struggles with fast refresh, force carousel to use full refresh instead // If an E-Ink display struggles with fast refresh, force carousel to use full refresh instead
// Carousel is potentially a major source of E-Ink display wear // Carousel is potentially a major source of E-Ink display wear
@ -2442,8 +2444,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
// Draw our hardware ID to assist with bluetooth pairing. Either prefix with Info or S&F Logo // Draw our hardware ID to assist with bluetooth pairing. Either prefix with Info or S&F Logo
if (moduleConfig.store_forward.enabled) { if (moduleConfig.store_forward.enabled) {
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
if (millis() - storeForwardModule->lastHeartbeat > if (!Throttle::isWithinTimespanMs(storeForwardModule->lastHeartbeat,
(storeForwardModule->heartbeatInterval * 1200)) { // no heartbeat, overlap a bit (storeForwardModule->heartbeatInterval * 1200))) { // no heartbeat, overlap a bit
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || defined(ST7789_CS) || \ #if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || defined(ST7789_CS) || \
defined(USE_ST7789) || defined(HX8357_CS)) && \ defined(USE_ST7789) || defined(HX8357_CS)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS) !defined(DISPLAY_FORCE_SMALL_FONTS)

View File

@ -1,5 +1,5 @@
#include "ExpressLRSFiveWay.h" #include "ExpressLRSFiveWay.h"
#include "Throttle.h"
#ifdef INPUTBROKER_EXPRESSLRSFIVEWAY_TYPE #ifdef INPUTBROKER_EXPRESSLRSFIVEWAY_TYPE
@ -76,11 +76,10 @@ void ExpressLRSFiveWay::update(int *keyValue, bool *keyLongPressed)
*keyValue = NO_PRESS; *keyValue = NO_PRESS;
int newKey = readKey(); int newKey = readKey();
uint32_t now = millis();
if (keyInProcess == NO_PRESS) { if (keyInProcess == NO_PRESS) {
// New key down // New key down
if (newKey != NO_PRESS) { if (newKey != NO_PRESS) {
keyDownStart = now; keyDownStart = millis();
// DBGLN("down=%u", newKey); // DBGLN("down=%u", newKey);
} }
} else { } else {
@ -88,7 +87,7 @@ void ExpressLRSFiveWay::update(int *keyValue, bool *keyLongPressed)
if (newKey == NO_PRESS) { if (newKey == NO_PRESS) {
// DBGLN("up=%u", keyInProcess); // DBGLN("up=%u", keyInProcess);
if (!isLongPressed) { if (!isLongPressed) {
if ((now - keyDownStart) > KEY_DEBOUNCE_MS) { if (!Throttle::isWithinTimespanMs(keyDownStart, KEY_DEBOUNCE_MS)) {
*keyValue = keyInProcess; *keyValue = keyInProcess;
*keyLongPressed = false; *keyLongPressed = false;
} }
@ -101,7 +100,7 @@ void ExpressLRSFiveWay::update(int *keyValue, bool *keyLongPressed)
} }
// else still pressing, waiting for long if not already signaled // else still pressing, waiting for long if not already signaled
else if (!isLongPressed) { else if (!isLongPressed) {
if ((now - keyDownStart) > KEY_LONG_PRESS_MS) { if (!Throttle::isWithinTimespanMs(keyDownStart, KEY_LONG_PRESS_MS)) {
*keyValue = keyInProcess; *keyValue = keyInProcess;
*keyLongPressed = true; *keyLongPressed = true;
isLongPressed = true; isLongPressed = true;

View File

@ -6,6 +6,7 @@
#include "ScanAndSelect.h" #include "ScanAndSelect.h"
#include "modules/CannedMessageModule.h" #include "modules/CannedMessageModule.h"
#include <Throttle.h>
// Config // Config
static const char name[] = "scanAndSelect"; // should match "allow input source" string static const char name[] = "scanAndSelect"; // should match "allow input source" string
@ -75,7 +76,7 @@ int32_t ScanAndSelectInput::runOnce()
else { else {
// Duration enough for long press // Duration enough for long press
// Long press not yet fired (prevent repeat firing while held) // Long press not yet fired (prevent repeat firing while held)
if (!longPressFired && now - downSinceMs > durationLongMs) { if (!longPressFired && Throttle::isWithinTimespanMs(downSinceMs, durationLongMs)) {
longPressFired = true; longPressFired = true;
longPress(); longPress();
} }
@ -91,7 +92,7 @@ int32_t ScanAndSelectInput::runOnce()
// Long press event didn't already fire // Long press event didn't already fire
if (held && !longPressFired) { if (held && !longPressFired) {
// Duration enough for short press // Duration enough for short press
if (now - downSinceMs > durationShortMs) { if (!Throttle::isWithinTimespanMs(downSinceMs, durationShortMs)) {
shortPress(); shortPress();
} }
} }

View File

@ -1,5 +1,6 @@
#include "SerialKeyboard.h" #include "SerialKeyboard.h"
#include "configuration.h" #include "configuration.h"
#include <Throttle.h>
#ifdef INPUTBROKER_SERIAL_TYPE #ifdef INPUTBROKER_SERIAL_TYPE
#define CANNED_MESSAGE_MODULE_ENABLE 1 // in case it's not set in the variant file #define CANNED_MESSAGE_MODULE_ENABLE 1 // in case it's not set in the variant file
@ -73,7 +74,7 @@ int32_t SerialKeyboard::runOnce()
// Serial.print ("X"); // Serial.print ("X");
// Serial.println (shiftRegister2, BIN); // Serial.println (shiftRegister2, BIN);
if (millis() - lastPressTime > 500) { if (!Throttle::isWithinTimespanMs(lastPressTime, 500)) {
quickPress = 0; quickPress = 0;
} }

View File

@ -18,6 +18,7 @@
#include "Led.h" #include "Led.h"
#include "RTC.h" #include "RTC.h"
#include "SPILock.h" #include "SPILock.h"
#include "Throttle.h"
#include "concurrency/OSThread.h" #include "concurrency/OSThread.h"
#include "concurrency/Periodic.h" #include "concurrency/Periodic.h"
#include "detect/ScanI2C.h" #include "detect/ScanI2C.h"
@ -1122,7 +1123,7 @@ void loop()
#ifdef DEBUG_STACK #ifdef DEBUG_STACK
static uint32_t lastPrint = 0; static uint32_t lastPrint = 0;
if (millis() - lastPrint > 10 * 1000L) { if (!Throttle::isWithinTimespanMs(lastPrint, 10 * 1000L)) {
lastPrint = millis(); lastPrint = millis();
meshtastic::printThreadInfo("main"); meshtastic::printThreadInfo("main");
} }

View File

@ -1,7 +1,9 @@
#include "LR11x0Interface.h" #include "LR11x0Interface.h"
#include "Throttle.h"
#include "configuration.h" #include "configuration.h"
#include "error.h" #include "error.h"
#include "mesh/NodeDB.h" #include "mesh/NodeDB.h"
#ifdef ARCH_PORTDUINO #ifdef ARCH_PORTDUINO
#include "PortduinoGlue.h" #include "PortduinoGlue.h"
#endif #endif
@ -275,15 +277,15 @@ template <typename T> bool LR11x0Interface<T>::isActivelyReceiving()
bool detected = (irq & (RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID | RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED)); bool detected = (irq & (RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID | RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED));
// Handle false detections // Handle false detections
if (detected) { if (detected) {
uint32_t now = millis();
if (!activeReceiveStart) { if (!activeReceiveStart) {
activeReceiveStart = now; activeReceiveStart = millis();
} else if ((now - activeReceiveStart > 2 * preambleTimeMsec) && !(irq & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID)) { } else if (!Throttle::isWithinTimespanMs(activeReceiveStart, 2 * preambleTimeMsec) &&
!(irq & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID)) {
// The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag // The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag
activeReceiveStart = 0; activeReceiveStart = 0;
LOG_DEBUG("Ignore false preamble detection.\n"); LOG_DEBUG("Ignore false preamble detection.\n");
return false; return false;
} else if (now - activeReceiveStart > maxPacketTimeMsec) { } else if (!Throttle::isWithinTimespanMs(activeReceiveStart, maxPacketTimeMsec)) {
// We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag // We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag
activeReceiveStart = 0; activeReceiveStart = 0;
LOG_DEBUG("Ignore false header detection.\n"); LOG_DEBUG("Ignore false header detection.\n");

View File

@ -5,6 +5,7 @@
#ifdef ARCH_PORTDUINO #ifdef ARCH_PORTDUINO
#include "platform/portduino/PortduinoGlue.h" #include "platform/portduino/PortduinoGlue.h"
#endif #endif
#include "Throttle.h"
PacketHistory::PacketHistory() PacketHistory::PacketHistory()
{ {
@ -22,18 +23,17 @@ bool PacketHistory::wasSeenRecently(const meshtastic_MeshPacket *p, bool withUpd
return false; // Not a floodable message ID, so we don't care return false; // Not a floodable message ID, so we don't care
} }
uint32_t now = millis();
PacketRecord r; PacketRecord r;
r.id = p->id; r.id = p->id;
r.sender = getFrom(p); r.sender = getFrom(p);
r.rxTimeMsec = now; r.rxTimeMsec = millis();
auto found = recentPackets.find(r); auto found = recentPackets.find(r);
bool seenRecently = (found != recentPackets.end()); // found not equal to .end() means packet was seen recently bool seenRecently = (found != recentPackets.end()); // found not equal to .end() means packet was seen recently
if (seenRecently && (now - found->rxTimeMsec) >= FLOOD_EXPIRE_TIME) { // Check whether found packet has already expired if (seenRecently &&
recentPackets.erase(found); // Erase and pretend packet has not been seen recently !Throttle::isWithinTimespanMs(found->rxTimeMsec, FLOOD_EXPIRE_TIME)) { // Check whether found packet has already expired
recentPackets.erase(found); // Erase and pretend packet has not been seen recently
found = recentPackets.end(); found = recentPackets.end();
seenRecently = false; seenRecently = false;
} }
@ -64,12 +64,10 @@ bool PacketHistory::wasSeenRecently(const meshtastic_MeshPacket *p, bool withUpd
*/ */
void PacketHistory::clearExpiredRecentPackets() void PacketHistory::clearExpiredRecentPackets()
{ {
uint32_t now = millis();
LOG_DEBUG("recentPackets size=%ld\n", recentPackets.size()); LOG_DEBUG("recentPackets size=%ld\n", recentPackets.size());
for (auto it = recentPackets.begin(); it != recentPackets.end();) { for (auto it = recentPackets.begin(); it != recentPackets.end();) {
if ((now - it->rxTimeMsec) >= FLOOD_EXPIRE_TIME) { if (!Throttle::isWithinTimespanMs(it->rxTimeMsec, FLOOD_EXPIRE_TIME)) {
it = recentPackets.erase(it); // erase returns iterator pointing to element immediately following the one erased it = recentPackets.erase(it); // erase returns iterator pointing to element immediately following the one erased
} else { } else {
++it; ++it;

View File

@ -25,6 +25,7 @@
#if !MESHTASTIC_EXCLUDE_MQTT #if !MESHTASTIC_EXCLUDE_MQTT
#include "mqtt/MQTT.h" #include "mqtt/MQTT.h"
#endif #endif
#include "Throttle.h"
#include <RTC.h> #include <RTC.h>
PhoneAPI::PhoneAPI() PhoneAPI::PhoneAPI()
@ -561,12 +562,12 @@ bool PhoneAPI::handleToRadioPacket(meshtastic_MeshPacket &p)
{ {
printPacket("PACKET FROM PHONE", &p); printPacket("PACKET FROM PHONE", &p);
if (p.decoded.portnum == meshtastic_PortNum_TRACEROUTE_APP && lastPortNumToRadio[p.decoded.portnum] && if (p.decoded.portnum == meshtastic_PortNum_TRACEROUTE_APP && lastPortNumToRadio[p.decoded.portnum] &&
(millis() - lastPortNumToRadio[p.decoded.portnum]) < (THIRTY_SECONDS_MS)) { Throttle::isWithinTimespanMs(lastPortNumToRadio[p.decoded.portnum], THIRTY_SECONDS_MS)) {
LOG_WARN("Rate limiting portnum %d\n", p.decoded.portnum); LOG_WARN("Rate limiting portnum %d\n", p.decoded.portnum);
sendNotification(meshtastic_LogRecord_Level_WARNING, p.id, "TraceRoute can only be sent once every 30 seconds"); sendNotification(meshtastic_LogRecord_Level_WARNING, p.id, "TraceRoute can only be sent once every 30 seconds");
return false; return false;
} else if (p.decoded.portnum == meshtastic_PortNum_POSITION_APP && lastPortNumToRadio[p.decoded.portnum] && } else if (p.decoded.portnum == meshtastic_PortNum_POSITION_APP && lastPortNumToRadio[p.decoded.portnum] &&
(millis() - lastPortNumToRadio[p.decoded.portnum]) < (FIVE_SECONDS_MS)) { Throttle::isWithinTimespanMs(lastPortNumToRadio[p.decoded.portnum], FIVE_SECONDS_MS)) {
LOG_WARN("Rate limiting portnum %d\n", p.decoded.portnum); LOG_WARN("Rate limiting portnum %d\n", p.decoded.portnum);
sendNotification(meshtastic_LogRecord_Level_WARNING, p.id, "Position can only be sent once every 5 seconds"); sendNotification(meshtastic_LogRecord_Level_WARNING, p.id, "Position can only be sent once every 5 seconds");
return false; return false;

View File

@ -3,6 +3,7 @@
#include "NodeDB.h" #include "NodeDB.h"
#include "PowerMon.h" #include "PowerMon.h"
#include "SPILock.h" #include "SPILock.h"
#include "Throttle.h"
#include "configuration.h" #include "configuration.h"
#include "error.h" #include "error.h"
#include "main.h" #include "main.h"
@ -41,7 +42,7 @@ void LockingArduinoHal::spiTransfer(uint8_t *out, size_t len, uint8_t *in)
uint32_t start = millis(); uint32_t start = millis();
while (digitalRead(busy)) { while (digitalRead(busy)) {
if (millis() - start >= 2000) { if (!Throttle::isWithinTimespanMs(start, 2000)) {
LOG_ERROR("GPIO mid-transfer timeout, is it connected?"); LOG_ERROR("GPIO mid-transfer timeout, is it connected?");
return; return;
} }
@ -114,7 +115,7 @@ bool RadioLibInterface::canSendImmediately()
} }
// If we've been trying to send the same packet more than one minute and we haven't gotten a // If we've been trying to send the same packet more than one minute and we haven't gotten a
// TX IRQ from the radio, the radio is probably broken. // TX IRQ from the radio, the radio is probably broken.
if (busyTx && (millis() - lastTxStart > 60000)) { if (busyTx && !Throttle::isWithinTimespanMs(lastTxStart, 60000)) {
LOG_ERROR("Hardware Failure! busyTx for more than 60s\n"); LOG_ERROR("Hardware Failure! busyTx for more than 60s\n");
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_TRANSMIT_FAILED); RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_TRANSMIT_FAILED);
// reboot in 5 seconds when this condition occurs. // reboot in 5 seconds when this condition occurs.

View File

@ -6,6 +6,8 @@
#include "PortduinoGlue.h" #include "PortduinoGlue.h"
#endif #endif
#include "Throttle.h"
// Particular boards might define a different max power based on what their hardware can do, default to max power output if not // Particular boards might define a different max power based on what their hardware can do, default to max power output if not
// specified (may be dangerous if using external PA and SX126x power config forgotten) // specified (may be dangerous if using external PA and SX126x power config forgotten)
#ifndef SX126X_MAX_POWER #ifndef SX126X_MAX_POWER
@ -319,15 +321,15 @@ template <typename T> bool SX126xInterface<T>::isActivelyReceiving()
bool detected = (irq & (RADIOLIB_SX126X_IRQ_HEADER_VALID | RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED)); bool detected = (irq & (RADIOLIB_SX126X_IRQ_HEADER_VALID | RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED));
// Handle false detections // Handle false detections
if (detected) { if (detected) {
uint32_t now = millis();
if (!activeReceiveStart) { if (!activeReceiveStart) {
activeReceiveStart = now; activeReceiveStart = millis();
} else if ((now - activeReceiveStart > 2 * preambleTimeMsec) && !(irq & RADIOLIB_SX126X_IRQ_HEADER_VALID)) { } else if (!Throttle::isWithinTimespanMs(activeReceiveStart, 2 * preambleTimeMsec) &&
!(irq & RADIOLIB_SX126X_IRQ_HEADER_VALID)) {
// The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag // The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag
activeReceiveStart = 0; activeReceiveStart = 0;
LOG_DEBUG("Ignore false preamble detection.\n"); LOG_DEBUG("Ignore false preamble detection.\n");
return false; return false;
} else if (now - activeReceiveStart > maxPacketTimeMsec) { } else if (!Throttle::isWithinTimespanMs(activeReceiveStart, maxPacketTimeMsec)) {
// We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag // We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag
activeReceiveStart = 0; activeReceiveStart = 0;
LOG_DEBUG("Ignore false header detection.\n"); LOG_DEBUG("Ignore false header detection.\n");
@ -359,4 +361,4 @@ template <typename T> bool SX126xInterface<T>::sleep()
#endif #endif
return true; return true;
} }

View File

@ -1,4 +1,5 @@
#include "SX128xInterface.h" #include "SX128xInterface.h"
#include "Throttle.h"
#include "configuration.h" #include "configuration.h"
#include "error.h" #include "error.h"
#include "mesh/NodeDB.h" #include "mesh/NodeDB.h"
@ -294,15 +295,15 @@ template <typename T> bool SX128xInterface<T>::isActivelyReceiving()
// Handle false detections // Handle false detections
if (detected) { if (detected) {
uint32_t now = millis();
if (!activeReceiveStart) { if (!activeReceiveStart) {
activeReceiveStart = now; activeReceiveStart = millis();
} else if ((now - activeReceiveStart > 2 * preambleTimeMsec) && !(irq & RADIOLIB_SX128X_IRQ_HEADER_VALID)) { } else if (!Throttle::isWithinTimespanMs(activeReceiveStart, 2 * preambleTimeMsec) &&
!(irq & RADIOLIB_SX128X_IRQ_HEADER_VALID)) {
// The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag // The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag
activeReceiveStart = 0; activeReceiveStart = 0;
LOG_DEBUG("Ignore false preamble detection.\n"); LOG_DEBUG("Ignore false preamble detection.\n");
return false; return false;
} else if (now - activeReceiveStart > maxPacketTimeMsec) { } else if (Throttle::isWithinTimespanMs(activeReceiveStart, maxPacketTimeMsec)) {
// We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag // We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag
activeReceiveStart = 0; activeReceiveStart = 0;
LOG_DEBUG("Ignore false header detection.\n"); LOG_DEBUG("Ignore false header detection.\n");

View File

@ -1,6 +1,7 @@
#include "StreamAPI.h" #include "StreamAPI.h"
#include "PowerFSM.h" #include "PowerFSM.h"
#include "RTC.h" #include "RTC.h"
#include "Throttle.h"
#include "configuration.h" #include "configuration.h"
#define START1 0x94 #define START1 0x94
@ -20,10 +21,9 @@ int32_t StreamAPI::runOncePart()
*/ */
int32_t StreamAPI::readStream() int32_t StreamAPI::readStream()
{ {
uint32_t now = millis();
if (!stream->available()) { if (!stream->available()) {
// Nothing available this time, if the computer has talked to us recently, poll often, otherwise let CPU sleep a long time // Nothing available this time, if the computer has talked to us recently, poll often, otherwise let CPU sleep a long time
bool recentRx = (now - lastRxMsec) < 2000; bool recentRx = Throttle::isWithinTimespanMs(lastRxMsec, 2000);
return recentRx ? 5 : 250; return recentRx ? 5 : 250;
} else { } else {
while (stream->available()) { // Currently we never want to block while (stream->available()) { // Currently we never want to block
@ -71,7 +71,7 @@ int32_t StreamAPI::readStream()
} }
// we had bytes available this time, so assume we might have them next time also // we had bytes available this time, so assume we might have them next time also
lastRxMsec = now; lastRxMsec = millis();
return 0; return 0;
} }
} }

View File

@ -24,4 +24,12 @@ bool Throttle::execute(uint32_t *lastExecutionMs, uint32_t minumumIntervalMs, vo
onDefer(); onDefer();
} }
return false; return false;
}
/// @brief Check if the last execution time is within the interval
/// @param lastExecutionMs The last execution time in milliseconds
/// @param timeSpanMs The interval in milliseconds of the timespan
bool Throttle::isWithinTimespanMs(uint32_t lastExecutionMs, uint32_t timeSpanMs)
{
return (millis() - lastExecutionMs) < timeSpanMs;
} }

View File

@ -6,4 +6,5 @@ class Throttle
{ {
public: public:
static bool execute(uint32_t *lastExecutionMs, uint32_t minumumIntervalMs, void (*func)(void), void (*onDefer)(void) = NULL); static bool execute(uint32_t *lastExecutionMs, uint32_t minumumIntervalMs, void (*func)(void), void (*onDefer)(void) = NULL);
static bool isWithinTimespanMs(uint32_t lastExecutionMs, uint32_t intervalMs);
}; };

View File

@ -23,6 +23,7 @@ static void WiFiEvent(WiFiEvent_t event);
#endif #endif
#ifndef DISABLE_NTP #ifndef DISABLE_NTP
#include "Throttle.h"
#include <NTPClient.h> #include <NTPClient.h>
#endif #endif
@ -142,7 +143,7 @@ static int32_t reconnectWiFi()
} }
#ifndef DISABLE_NTP #ifndef DISABLE_NTP
if (WiFi.isConnected() && (((millis() - lastrun_ntp) > 43200000) || (lastrun_ntp == 0))) { // every 12 hours if (WiFi.isConnected() && (!Throttle::isWithinTimespanMs(lastrun_ntp, 43200000) || (lastrun_ntp == 0))) { // every 12 hours
LOG_DEBUG("Updating NTP time from %s\n", config.network.ntp_server); LOG_DEBUG("Updating NTP time from %s\n", config.network.ntp_server);
if (timeClient.update()) { if (timeClient.update()) {
LOG_DEBUG("NTP Request Success - Setting RTCQualityNTP if needed\n"); LOG_DEBUG("NTP Request Success - Setting RTCQualityNTP if needed\n");
@ -420,4 +421,4 @@ uint8_t getWifiDisconnectReason()
{ {
return wifiDisconnectReason; return wifiDisconnectReason;
} }
#endif #endif

View File

@ -27,6 +27,7 @@
#endif #endif
#include "graphics/ScreenFonts.h" #include "graphics/ScreenFonts.h"
#include <Throttle.h>
// Remove Canned message screen if no action is taken for some milliseconds // Remove Canned message screen if no action is taken for some milliseconds
#define INACTIVATE_AFTER_MS 20000 #define INACTIVATE_AFTER_MS 20000
@ -422,7 +423,7 @@ int32_t CannedMessageModule::runOnce()
this->notifyObservers(&e); this->notifyObservers(&e);
} else if (((this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT)) && } else if (((this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT)) &&
((millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS)) { !Throttle::isWithinTimespanMs(this->lastTouchMillis, INACTIVATE_AFTER_MS)) {
// Reset module // Reset module
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->currentMessageIndex = -1; this->currentMessageIndex = -1;

View File

@ -5,6 +5,7 @@
#include "PowerFSM.h" #include "PowerFSM.h"
#include "configuration.h" #include "configuration.h"
#include "main.h" #include "main.h"
#include <Throttle.h>
DetectionSensorModule *detectionSensorModule; DetectionSensorModule *detectionSensorModule;
#define GPIO_POLLING_INTERVAL 100 #define GPIO_POLLING_INTERVAL 100
@ -49,7 +50,8 @@ int32_t DetectionSensorModule::runOnce()
// LOG_DEBUG("Detection Sensor Module: Current pin state: %i\n", digitalRead(moduleConfig.detection_sensor.monitor_pin)); // LOG_DEBUG("Detection Sensor Module: Current pin state: %i\n", digitalRead(moduleConfig.detection_sensor.monitor_pin));
if ((millis() - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.detection_sensor.minimum_broadcast_secs) && if (!Throttle::isWithinTimespanMs(lastSentToMesh,
Default::getConfiguredOrDefaultMs(moduleConfig.detection_sensor.minimum_broadcast_secs)) &&
hasDetectionEvent()) { hasDetectionEvent()) {
sendDetectionMessage(); sendDetectionMessage();
return DELAYED_INTERVAL; return DELAYED_INTERVAL;
@ -58,8 +60,9 @@ int32_t DetectionSensorModule::runOnce()
// of heartbeat. We only do this if the minimum broadcast interval is greater than zero, otherwise we'll only broadcast state // of heartbeat. We only do this if the minimum broadcast interval is greater than zero, otherwise we'll only broadcast state
// change detections. // change detections.
else if (moduleConfig.detection_sensor.state_broadcast_secs > 0 && else if (moduleConfig.detection_sensor.state_broadcast_secs > 0 &&
(millis() - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.detection_sensor.state_broadcast_secs, !Throttle::isWithinTimespanMs(lastSentToMesh,
default_telemetry_broadcast_interval_secs)) { Default::getConfiguredOrDefaultMs(moduleConfig.detection_sensor.state_broadcast_secs,
default_telemetry_broadcast_interval_secs))) {
sendCurrentStateMessage(); sendCurrentStateMessage();
return DELAYED_INTERVAL; return DELAYED_INTERVAL;
} }

View File

@ -3,6 +3,7 @@
#include "MeshService.h" #include "MeshService.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "RTC.h" #include "RTC.h"
#include <Throttle.h>
NeighborInfoModule *neighborInfoModule; NeighborInfoModule *neighborInfoModule;
@ -87,7 +88,8 @@ void NeighborInfoModule::cleanUpNeighbors()
NodeNum my_node_id = nodeDB->getNodeNum(); NodeNum my_node_id = nodeDB->getNodeNum();
for (auto it = neighbors.rbegin(); it != neighbors.rend();) { for (auto it = neighbors.rbegin(); it != neighbors.rend();) {
// We will remove a neighbor if we haven't heard from them in twice the broadcast interval // We will remove a neighbor if we haven't heard from them in twice the broadcast interval
if ((now - it->last_rx_time > it->node_broadcast_interval_secs * 2) && (it->node_id != my_node_id)) { if (!Throttle::isWithinTimespanMs(it->last_rx_time, it->node_broadcast_interval_secs * 2) &&
(it->node_id != my_node_id)) {
LOG_DEBUG("Removing neighbor with node ID 0x%x\n", it->node_id); LOG_DEBUG("Removing neighbor with node ID 0x%x\n", it->node_id);
it = std::vector<meshtastic_Neighbor>::reverse_iterator( it = std::vector<meshtastic_Neighbor>::reverse_iterator(
neighbors.erase(std::next(it).base())); // Erase the element and update the iterator neighbors.erase(std::next(it).base())); // Erase the element and update the iterator

View File

@ -6,6 +6,7 @@
#include "Router.h" #include "Router.h"
#include "configuration.h" #include "configuration.h"
#include "main.h" #include "main.h"
#include <Throttle.h>
NodeInfoModule *nodeInfoModule; NodeInfoModule *nodeInfoModule;
@ -67,13 +68,12 @@ meshtastic_MeshPacket *NodeInfoModule::allocReply()
LOG_DEBUG("Skip sending NodeInfo due to > 40 percent channel util.\n"); LOG_DEBUG("Skip sending NodeInfo due to > 40 percent channel util.\n");
return NULL; return NULL;
} }
uint32_t now = millis();
// If we sent our NodeInfo less than 5 min. ago, don't send it again as it may be still underway. // If we sent our NodeInfo less than 5 min. ago, don't send it again as it may be still underway.
if (!shorterTimeout && lastSentToMesh && (now - lastSentToMesh) < (5 * 60 * 1000)) { if (!shorterTimeout && lastSentToMesh && Throttle::isWithinTimespanMs(lastSentToMesh, 5 * 60 * 1000)) {
LOG_DEBUG("Skip sending NodeInfo since we just sent it less than 5 minutes ago.\n"); LOG_DEBUG("Skip sending NodeInfo since we just sent it less than 5 minutes ago.\n");
ignoreRequest = true; // Mark it as ignored for MeshModule ignoreRequest = true; // Mark it as ignored for MeshModule
return NULL; return NULL;
} else if (shorterTimeout && lastSentToMesh && (now - lastSentToMesh) < (60 * 1000)) { } else if (shorterTimeout && lastSentToMesh && Throttle::isWithinTimespanMs(lastSentToMesh, 60 * 1000)) {
LOG_DEBUG("Skip sending actively requested NodeInfo since we just sent it less than 60 seconds ago.\n"); LOG_DEBUG("Skip sending actively requested NodeInfo since we just sent it less than 60 seconds ago.\n");
ignoreRequest = true; // Mark it as ignored for MeshModule ignoreRequest = true; // Mark it as ignored for MeshModule
return NULL; return NULL;
@ -82,7 +82,7 @@ meshtastic_MeshPacket *NodeInfoModule::allocReply()
meshtastic_User &u = owner; meshtastic_User &u = owner;
LOG_INFO("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name); LOG_INFO("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name);
lastSentToMesh = now; lastSentToMesh = millis();
return allocDataProtobuf(u); return allocDataProtobuf(u);
} }
} }

View File

@ -145,7 +145,7 @@ void PositionModule::trySetRtc(meshtastic_Position p, bool isLocal, bool forceUp
bool PositionModule::hasQualityTimesource() bool PositionModule::hasQualityTimesource()
{ {
bool setFromPhoneOrNtpToday = bool setFromPhoneOrNtpToday =
lastSetFromPhoneNtpOrGps == 0 ? false : (millis() - lastSetFromPhoneNtpOrGps) <= (SEC_PER_DAY * 1000UL); lastSetFromPhoneNtpOrGps == 0 ? false : Throttle::isWithinTimespanMs(lastSetFromPhoneNtpOrGps, SEC_PER_DAY * 1000UL);
#if MESHTASTIC_EXCLUDE_GPS #if MESHTASTIC_EXCLUDE_GPS
bool hasGpsOrRtc = (rtc_found.address != ScanI2C::ADDRESS_NONE.address); bool hasGpsOrRtc = (rtc_found.address != ScanI2C::ADDRESS_NONE.address);
#else #else

View File

@ -9,6 +9,7 @@
#include "main.h" #include "main.h"
#include "sleep.h" #include "sleep.h"
#include "target_specific.h" #include "target_specific.h"
#include <Throttle.h>
extern void printInfo(); extern void printInfo();
@ -114,7 +115,7 @@ int32_t PowerStressModule::runOnce()
break; break;
case meshtastic_PowerStressMessage_Opcode_CPU_FULLON: { case meshtastic_PowerStressMessage_Opcode_CPU_FULLON: {
uint32_t start_msec = millis(); uint32_t start_msec = millis();
while ((millis() - start_msec) < (uint32_t)sleep_msec) while (Throttle::isWithinTimespanMs(start_msec, sleep_msec))
; // Don't let CPU idle at all ; // Don't let CPU idle at all
sleep_msec = 0; // we already slept sleep_msec = 0; // we already slept
break; break;

View File

@ -19,6 +19,7 @@
#include "configuration.h" #include "configuration.h"
#include "gps/GeoCoord.h" #include "gps/GeoCoord.h"
#include <Arduino.h> #include <Arduino.h>
#include <Throttle.h>
RangeTestModule *rangeTestModule; RangeTestModule *rangeTestModule;
RangeTestModuleRadio *rangeTestModuleRadio; RangeTestModuleRadio *rangeTestModuleRadio;
@ -79,7 +80,7 @@ int32_t RangeTestModule::runOnce()
} }
// If we have been running for more than 8 hours, turn module back off // If we have been running for more than 8 hours, turn module back off
if (millis() - started > 28800000) { if (!Throttle::isWithinTimespanMs(started, 28800000)) {
LOG_INFO("Range Test Module - Disabling after 8 hours\n"); LOG_INFO("Range Test Module - Disabling after 8 hours\n");
return disable(); return disable();
} else { } else {

View File

@ -5,6 +5,7 @@
#include "Router.h" #include "Router.h"
#include "configuration.h" #include "configuration.h"
#include "main.h" #include "main.h"
#include <Throttle.h>
#define NUM_GPIOS 64 #define NUM_GPIOS 64
@ -118,11 +119,10 @@ bool RemoteHardwareModule::handleReceivedProtobuf(const meshtastic_MeshPacket &r
int32_t RemoteHardwareModule::runOnce() int32_t RemoteHardwareModule::runOnce()
{ {
if (moduleConfig.remote_hardware.enabled && watchGpios) { if (moduleConfig.remote_hardware.enabled && watchGpios) {
uint32_t now = millis();
if (now - lastWatchMsec >= WATCH_INTERVAL_MSEC) { if (!Throttle::isWithinTimespanMs(lastWatchMsec, WATCH_INTERVAL_MSEC)) {
uint64_t curVal = digitalReads(watchGpios); uint64_t curVal = digitalReads(watchGpios);
lastWatchMsec = now; lastWatchMsec = millis();
if (curVal != previousWatch) { if (curVal != previousWatch) {
previousWatch = curVal; previousWatch = curVal;

View File

@ -7,6 +7,7 @@
#include "Router.h" #include "Router.h"
#include "configuration.h" #include "configuration.h"
#include <Arduino.h> #include <Arduino.h>
#include <Throttle.h>
/* /*
SerialModule SerialModule
@ -97,8 +98,7 @@ SerialModuleRadio::SerialModuleRadio() : MeshModule("SerialModuleRadio")
*/ */
bool SerialModule::checkIsConnected() bool SerialModule::checkIsConnected()
{ {
uint32_t now = millis(); return Throttle::isWithinTimespanMs(lastContactMsec, SERIAL_CONNECTION_TIMEOUT);
return (now - lastContactMsec) < SERIAL_CONNECTION_TIMEOUT;
} }
int32_t SerialModule::runOnce() int32_t SerialModule::runOnce()
@ -182,13 +182,13 @@ int32_t SerialModule::runOnce()
return runOncePart(); return runOncePart();
} else if ((moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_NMEA) && HAS_GPS) { } else if ((moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_NMEA) && HAS_GPS) {
// in NMEA mode send out GGA every 2 seconds, Don't read from Port // in NMEA mode send out GGA every 2 seconds, Don't read from Port
if (millis() - lastNmeaTime > 2000) { if (!Throttle::isWithinTimespanMs(lastNmeaTime, 2000)) {
lastNmeaTime = millis(); lastNmeaTime = millis();
printGGA(outbuf, sizeof(outbuf), localPosition); printGGA(outbuf, sizeof(outbuf), localPosition);
serialPrint->printf("%s", outbuf); serialPrint->printf("%s", outbuf);
} }
} else if ((moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO) && HAS_GPS) { } else if ((moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO) && HAS_GPS) {
if (millis() - lastNmeaTime > 10000) { if (!Throttle::isWithinTimespanMs(lastNmeaTime, 10000)) {
lastNmeaTime = millis(); lastNmeaTime = millis();
uint32_t readIndex = 0; uint32_t readIndex = 0;
const meshtastic_NodeInfoLite *tempNodeInfo = nodeDB->readNextMeshNode(readIndex); const meshtastic_NodeInfoLite *tempNodeInfo = nodeDB->readNextMeshNode(readIndex);
@ -500,7 +500,7 @@ void SerialModule::processWXSerial()
LOG_INFO("WS85 : %i %.1fg%.1f %.1fv %.1fv\n", atoi(windDir), strtof(windVel, nullptr), strtof(windGust, nullptr), LOG_INFO("WS85 : %i %.1fg%.1f %.1fv %.1fv\n", atoi(windDir), strtof(windVel, nullptr), strtof(windGust, nullptr),
batVoltageF, capVoltageF); batVoltageF, capVoltageF);
} }
if (gotwind && millis() - lastAveraged > averageIntervalMillis) { if (gotwind && !Throttle::isWithinTimespanMs(lastAveraged, averageIntervalMillis)) {
// calulate averages and send to the mesh // calulate averages and send to the mesh
float velAvg = 1.0 * velSum / velCount; float velAvg = 1.0 * velSum / velCount;

View File

@ -12,6 +12,7 @@
#include "Router.h" #include "Router.h"
#include "detect/ScanI2CTwoWire.h" #include "detect/ScanI2CTwoWire.h"
#include "main.h" #include "main.h"
#include <Throttle.h>
int32_t AirQualityTelemetryModule::runOnce() int32_t AirQualityTelemetryModule::runOnce()
{ {
@ -60,15 +61,14 @@ int32_t AirQualityTelemetryModule::runOnce()
if (!moduleConfig.telemetry.air_quality_enabled) if (!moduleConfig.telemetry.air_quality_enabled)
return disable(); return disable();
uint32_t now = millis();
if (((lastSentToMesh == 0) || if (((lastSentToMesh == 0) ||
((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.air_quality_interval, !Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled(
default_telemetry_broadcast_interval_secs, moduleConfig.telemetry.air_quality_interval,
numOnlineNodes))) && default_telemetry_broadcast_interval_secs, numOnlineNodes))) &&
airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) && airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
airTime->isTxAllowedAirUtil()) { airTime->isTxAllowedAirUtil()) {
sendTelemetry(); sendTelemetry();
lastSentToMesh = now; lastSentToMesh = millis();
} else if (service->isToPhoneQueueEmpty()) { } else if (service->isToPhoneQueueEmpty()) {
// Just send to phone when it's not our time to send to mesh yet // Just send to phone when it's not our time to send to mesh yet
// Only send while queue is empty (phone assumed connected) // Only send while queue is empty (phone assumed connected)

View File

@ -64,6 +64,7 @@ T1000xSensor t1000xSensor;
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true #define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
#include "graphics/ScreenFonts.h" #include "graphics/ScreenFonts.h"
#include <Throttle.h>
int32_t EnvironmentTelemetryModule::runOnce() int32_t EnvironmentTelemetryModule::runOnce()
{ {
@ -155,21 +156,20 @@ int32_t EnvironmentTelemetryModule::runOnce()
result = bme680Sensor.runTrigger(); result = bme680Sensor.runTrigger();
} }
uint32_t now = millis();
if (((lastSentToMesh == 0) || if (((lastSentToMesh == 0) ||
((now - lastSentToMesh) >= !Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled(
Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.environment_update_interval, moduleConfig.telemetry.environment_update_interval,
default_telemetry_broadcast_interval_secs, numOnlineNodes))) && default_telemetry_broadcast_interval_secs, numOnlineNodes))) &&
airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) && airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
airTime->isTxAllowedAirUtil()) { airTime->isTxAllowedAirUtil()) {
sendTelemetry(); sendTelemetry();
lastSentToMesh = now; lastSentToMesh = millis();
} else if (((lastSentToPhone == 0) || ((now - lastSentToPhone) >= sendToPhoneIntervalMs)) && } else if (((lastSentToPhone == 0) || !Throttle::isWithinTimespanMs(lastSentToPhone, sendToPhoneIntervalMs)) &&
(service->isToPhoneQueueEmpty())) { (service->isToPhoneQueueEmpty())) {
// Just send to phone when it's not our time to send to mesh yet // Just send to phone when it's not our time to send to mesh yet
// Only send while queue is empty (phone assumed connected) // Only send while queue is empty (phone assumed connected)
sendTelemetry(NODENUM_BROADCAST, true); sendTelemetry(NODENUM_BROADCAST, true);
lastSentToPhone = now; lastSentToPhone = millis();
} }
} }
return min(sendToPhoneIntervalMs, result); return min(sendToPhoneIntervalMs, result);

View File

@ -19,6 +19,7 @@
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true #define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
#include "graphics/ScreenFonts.h" #include "graphics/ScreenFonts.h"
#include <Throttle.h>
int32_t PowerTelemetryModule::runOnce() int32_t PowerTelemetryModule::runOnce()
{ {
@ -69,20 +70,19 @@ int32_t PowerTelemetryModule::runOnce()
if (!moduleConfig.telemetry.power_measurement_enabled) if (!moduleConfig.telemetry.power_measurement_enabled)
return disable(); return disable();
uint32_t now = millis();
if (((lastSentToMesh == 0) || if (((lastSentToMesh == 0) ||
((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.power_update_interval, !Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled(
default_telemetry_broadcast_interval_secs, moduleConfig.telemetry.power_update_interval,
numOnlineNodes))) && default_telemetry_broadcast_interval_secs, numOnlineNodes))) &&
airTime->isTxAllowedAirUtil()) { airTime->isTxAllowedAirUtil()) {
sendTelemetry(); sendTelemetry();
lastSentToMesh = now; lastSentToMesh = millis();
} else if (((lastSentToPhone == 0) || ((now - lastSentToPhone) >= sendToPhoneIntervalMs)) && } else if (((lastSentToPhone == 0) || !Throttle::isWithinTimespanMs(lastSentToPhone, sendToPhoneIntervalMs)) &&
(service->isToPhoneQueueEmpty())) { (service->isToPhoneQueueEmpty())) {
// Just send to phone when it's not our time to send to mesh yet // Just send to phone when it's not our time to send to mesh yet
// Only send while queue is empty (phone assumed connected) // Only send while queue is empty (phone assumed connected)
sendTelemetry(NODENUM_BROADCAST, true); sendTelemetry(NODENUM_BROADCAST, true);
lastSentToPhone = now; lastSentToPhone = millis();
} }
} }
return min(sendToPhoneIntervalMs, result); return min(sendToPhoneIntervalMs, result);

View File

@ -7,6 +7,7 @@
#include "NAU7802Sensor.h" #include "NAU7802Sensor.h"
#include "SafeFile.h" #include "SafeFile.h"
#include "TelemetrySensor.h" #include "TelemetrySensor.h"
#include <Throttle.h>
#include <pb_decode.h> #include <pb_decode.h>
#include <pb_encode.h> #include <pb_encode.h>
@ -40,7 +41,7 @@ bool NAU7802Sensor::getMetrics(meshtastic_Telemetry *measurement)
uint32_t start = millis(); uint32_t start = millis();
while (!nau7802.available()) { while (!nau7802.available()) {
delay(100); delay(100);
if (millis() - start > 1000) { if (!Throttle::isWithinTimespanMs(start, 1000)) {
nau7802.powerDown(); nau7802.powerDown();
return false; return false;
} }

View File

@ -17,6 +17,7 @@
#include "NodeDB.h" #include "NodeDB.h"
#include "RTC.h" #include "RTC.h"
#include "Router.h" #include "Router.h"
#include "Throttle.h"
#include "airtime.h" #include "airtime.h"
#include "configuration.h" #include "configuration.h"
#include "memGet.h" #include "memGet.h"
@ -29,6 +30,9 @@
StoreForwardModule *storeForwardModule; StoreForwardModule *storeForwardModule;
uint32_t lastHeartbeat = 0;
uint32_t heartbeatInterval = 60; // Default to 60 seconds, adjust as needed
int32_t StoreForwardModule::runOnce() int32_t StoreForwardModule::runOnce()
{ {
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
@ -42,7 +46,7 @@ int32_t StoreForwardModule::runOnce()
this->busy = false; this->busy = false;
} }
} }
} else if (this->heartbeat && (millis() - lastHeartbeat > (heartbeatInterval * 1000)) && } else if (this->heartbeat && (!Throttle::isWithinTimespanMs(lastHeartbeat, heartbeatInterval * 1000)) &&
airTime->isTxAllowedChannelUtil(true)) { airTime->isTxAllowedChannelUtil(true)) {
lastHeartbeat = millis(); lastHeartbeat = millis();
LOG_INFO("*** Sending heartbeat\n"); LOG_INFO("*** Sending heartbeat\n");

View File

@ -21,6 +21,7 @@
#include "Default.h" #include "Default.h"
#include "serialization/JSON.h" #include "serialization/JSON.h"
#include "serialization/MeshPacketSerializer.h" #include "serialization/MeshPacketSerializer.h"
#include <Throttle.h>
#include <assert.h> #include <assert.h>
const int reconnectMax = 5; const int reconnectMax = 5;
@ -610,7 +611,7 @@ void MQTT::perhapsReportToMap()
if (!moduleConfig.mqtt.map_reporting_enabled || !(moduleConfig.mqtt.proxy_to_client_enabled || isConnectedDirectly())) if (!moduleConfig.mqtt.map_reporting_enabled || !(moduleConfig.mqtt.proxy_to_client_enabled || isConnectedDirectly()))
return; return;
if (millis() - last_report_to_map < map_publish_interval_msecs) { if (Throttle::isWithinTimespanMs(last_report_to_map, map_publish_interval_msecs)) {
return; return;
} else { } else {
if (map_position_precision == 0 || (localPosition.latitude_i == 0 && localPosition.longitude_i == 0)) { if (map_position_precision == 0 || (localPosition.latitude_i == 0 && localPosition.longitude_i == 0)) {

View File

@ -5,6 +5,7 @@
#endif #endif
#include "ButtonThread.h" #include "ButtonThread.h"
#include "Default.h"
#include "Led.h" #include "Led.h"
#include "MeshRadio.h" #include "MeshRadio.h"
#include "MeshService.h" #include "MeshService.h"
@ -28,6 +29,7 @@
esp_sleep_source_t wakeCause; // the reason we booted this time esp_sleep_source_t wakeCause; // the reason we booted this time
#endif #endif
#include "Throttle.h"
#ifndef INCLUDE_vTaskSuspend #ifndef INCLUDE_vTaskSuspend
#define INCLUDE_vTaskSuspend 0 #define INCLUDE_vTaskSuspend 0
@ -168,7 +170,8 @@ static void waitEnterSleep(bool skipPreflight = false)
while (!doPreflightSleep()) { while (!doPreflightSleep()) {
delay(100); // Kinda yucky - wait until radio says say we can shutdown (finished in process sends/receives) delay(100); // Kinda yucky - wait until radio says say we can shutdown (finished in process sends/receives)
if (millis() - now > 30 * 1000) { // If we wait too long just report an error and go to sleep if (!Throttle::isWithinTimespanMs(now,
THIRTY_SECONDS_MS)) { // If we wait too long just report an error and go to sleep
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_SLEEP_ENTER_WAIT); RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_SLEEP_ENTER_WAIT);
assert(0); // FIXME - for now we just restart, need to fix bug #167 assert(0); // FIXME - for now we just restart, need to fix bug #167
break; break;