diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 6f363d7f96..c3f9cc0dfe 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -77,7 +77,7 @@ class SplashScreen : public UIScreen { } void poll() override { - if (millis() >= dismiss_after) { + if (millis_passed(dismiss_after)) { _task->gotoHomeScreen(); } } @@ -153,10 +153,11 @@ class HomeScreen : public UIScreen { int sensors_nb = 0; bool sensors_scroll = false; int sensors_scroll_offset = 0; - int next_sensors_refresh = 0; + unsigned long next_sensors_refresh = 0; + void refresh_sensors() { - if (millis() > next_sensors_refresh) { + if (millis_passed(next_sensors_refresh)) { sensors_lpp.reset(); sensors_nb = 0; sensors_lpp.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f); @@ -652,8 +653,8 @@ void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, i void UITask::userLedHandler() { #ifdef PIN_STATUS_LED - int cur_time = millis(); - if (cur_time > next_led_change) { + unsigned long cur_time = millis(); + if (millis_passed(next_led_change)) { if (led_state == 0) { led_state = 1; if (_msgcount > 0) { @@ -764,7 +765,7 @@ void UITask::loop() { } #endif #if defined(BACKLIGHT_BTN) - if (millis() > next_backlight_btn_check) { + if (millis_passed(next_backlight_btn_check)) { bool touch_state = digitalRead(PIN_BUTTON2); #if defined(DISP_BACKLIGHT) digitalWrite(DISP_BACKLIGHT, !touch_state); @@ -790,10 +791,10 @@ void UITask::loop() { if (curr) curr->poll(); if (_display != NULL && _display->isOn()) { - if (millis() >= _next_refresh && curr) { + if (millis_passed(_next_refresh) && curr) { _display->startFrame(); int delay_millis = curr->render(*_display); - if (millis() < _alert_expiry) { // render alert popup + if (!millis_passed(_alert_expiry)) { // render alert popup _display->setTextSize(1); int y = _display->height() / 3; int p = _display->height() / 32; @@ -809,7 +810,7 @@ void UITask::loop() { _display->endFrame(); } #if AUTO_OFF_MILLIS > 0 - if (millis() > _auto_off) { + if (millis_passed(_auto_off)) { _display->turnOff(); } #endif @@ -820,7 +821,7 @@ void UITask::loop() { #endif #ifdef AUTO_SHUTDOWN_MILLIVOLTS - if (millis() > next_batt_chck) { + if (millis_passed(next_batt_chck)) { uint16_t milliVolts = getBattMilliVolts(); if (milliVolts > 0 && milliVolts < AUTO_SHUTDOWN_MILLIVOLTS) { diff --git a/examples/companion_radio/ui-new/UITask.h b/examples/companion_radio/ui-new/UITask.h index a77ad6e7ec..e0aebd6698 100644 --- a/examples/companion_radio/ui-new/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -37,10 +37,10 @@ class UITask : public AbstractUITask { unsigned long _alert_expiry; int _msgcount; unsigned long ui_started_at, next_batt_chck; - int next_backlight_btn_check = 0; + unsigned long next_backlight_btn_check = 0; #ifdef PIN_STATUS_LED int led_state = 0; - int next_led_change = 0; + unsigned long next_led_change = 0; int last_led_increment = 0; #endif diff --git a/examples/companion_radio/ui-orig/UITask.cpp b/examples/companion_radio/ui-orig/UITask.cpp index 12a374d91d..6136dbd905 100644 --- a/examples/companion_radio/ui-orig/UITask.cpp +++ b/examples/companion_radio/ui-orig/UITask.cpp @@ -264,11 +264,11 @@ void UITask::renderCurrScreen() { void UITask::userLedHandler() { #ifdef PIN_STATUS_LED static int state = 0; - static int next_change = 0; + static unsigned long next_change = 0; static int last_increment = 0; - int cur_time = millis(); - if (cur_time > next_change) { + unsigned long cur_time = millis(); + if (millis_passed(next_change)) { if (state == 0) { state = 1; if (_msgcount > 0) { @@ -335,14 +335,14 @@ void UITask::loop() { _need_refresh = true; _firstBoot = false; } - if (millis() >= _next_refresh && _need_refresh) { + if (millis_passed(_next_refresh) && _need_refresh) { _display->startFrame(); renderCurrScreen(); _display->endFrame(); _next_refresh = millis() + 1000; // refresh every second } - if (millis() > _auto_off) { + if (millis_passed(_auto_off)) { _display->turnOff(); } } diff --git a/examples/simple_repeater/UITask.cpp b/examples/simple_repeater/UITask.cpp index 6a85143887..a16e79794d 100644 --- a/examples/simple_repeater/UITask.cpp +++ b/examples/simple_repeater/UITask.cpp @@ -1,6 +1,7 @@ #include "UITask.h" #include #include +#include #ifndef USER_BTN_PRESSED #define USER_BTN_PRESSED LOW @@ -46,7 +47,7 @@ void UITask::begin(NodePrefs* node_prefs, const char* build_date, const char* fi void UITask::renderCurrScreen() { char tmp[80]; - if (millis() < BOOT_SCREEN_MILLIS) { // boot screen + if (!millis_passed(BOOT_SCREEN_MILLIS)) { // boot screen // meshcore logo _display->setColor(DisplayDriver::BLUE); int logoWidth = 128; @@ -94,7 +95,7 @@ void UITask::renderCurrScreen() { void UITask::loop() { #ifdef PIN_USER_BTN - if (millis() >= _next_read) { + if (millis_passed(_next_read)) { int btnState = digitalRead(PIN_USER_BTN); if (btnState != _prevBtnState) { if (btnState == USER_BTN_PRESSED) { // pressed? @@ -112,14 +113,14 @@ void UITask::loop() { #endif if (_display->isOn()) { - if (millis() >= _next_refresh) { + if (millis_passed(_next_refresh)) { _display->startFrame(); renderCurrScreen(); _display->endFrame(); _next_refresh = millis() + 1000; // refresh every second } - if (millis() > _auto_off) { + if (millis_passed(_auto_off)) { _display->turnOff(); } } diff --git a/examples/simple_room_server/UITask.cpp b/examples/simple_room_server/UITask.cpp index 640a1d2d10..054dec1975 100644 --- a/examples/simple_room_server/UITask.cpp +++ b/examples/simple_room_server/UITask.cpp @@ -1,6 +1,7 @@ #include "UITask.h" #include #include +#include #ifndef USER_BTN_PRESSED #define USER_BTN_PRESSED LOW @@ -46,7 +47,7 @@ void UITask::begin(NodePrefs* node_prefs, const char* build_date, const char* fi void UITask::renderCurrScreen() { char tmp[80]; - if (millis() < BOOT_SCREEN_MILLIS) { // boot screen + if (!millis_passed(BOOT_SCREEN_MILLIS)) { // boot screen // meshcore logo _display->setColor(DisplayDriver::BLUE); int logoWidth = 128; @@ -94,7 +95,7 @@ void UITask::renderCurrScreen() { void UITask::loop() { #ifdef PIN_USER_BTN - if (millis() >= _next_read) { + if (millis_passed(_next_read)) { int btnState = digitalRead(PIN_USER_BTN); if (btnState != _prevBtnState) { if (btnState == USER_BTN_PRESSED) { // pressed? @@ -112,14 +113,14 @@ void UITask::loop() { #endif if (_display->isOn()) { - if (millis() >= _next_refresh) { + if (millis_passed(_next_refresh)) { _display->startFrame(); renderCurrScreen(); _display->endFrame(); _next_refresh = millis() + 1000; // refresh every second } - if (millis() > _auto_off) { + if (millis_passed(_auto_off)) { _display->turnOff(); } } diff --git a/examples/simple_sensor/UITask.cpp b/examples/simple_sensor/UITask.cpp index 757ea1dc60..20923eee91 100644 --- a/examples/simple_sensor/UITask.cpp +++ b/examples/simple_sensor/UITask.cpp @@ -1,6 +1,7 @@ #include "UITask.h" #include #include +#include #ifndef USER_BTN_PRESSED #define USER_BTN_PRESSED LOW @@ -46,7 +47,7 @@ void UITask::begin(NodePrefs* node_prefs, const char* build_date, const char* fi void UITask::renderCurrScreen() { char tmp[80]; - if (millis() < BOOT_SCREEN_MILLIS) { // boot screen + if (!millis_passed(BOOT_SCREEN_MILLIS)) { // boot screen // meshcore logo _display->setColor(DisplayDriver::BLUE); int logoWidth = 128; @@ -94,7 +95,7 @@ void UITask::renderCurrScreen() { void UITask::loop() { #ifdef PIN_USER_BTN - if (millis() >= _next_read) { + if (millis_passed(_next_read)) { int btnState = digitalRead(PIN_USER_BTN); if (btnState != _prevBtnState) { if (btnState == USER_BTN_PRESSED) { // pressed? @@ -112,14 +113,14 @@ void UITask::loop() { #endif if (_display->isOn()) { - if (millis() >= _next_refresh) { + if (millis_passed(_next_refresh)) { _display->startFrame(); renderCurrScreen(); _display->endFrame(); _next_refresh = millis() + 1000; // refresh every second } - if (millis() > _auto_off) { + if (millis_passed(_auto_off)) { _display->turnOff(); } } diff --git a/src/Dispatcher.cpp b/src/Dispatcher.cpp index 9d7a11131d..b0f9491c9d 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -20,7 +20,16 @@ void Dispatcher::begin() { n_sent_flood = n_sent_direct = 0; n_recv_flood = n_recv_direct = 0; _err_flags = 0; - radio_nonrx_start = _ms->getMillis(); + + unsigned long now = _ms->getMillis(); + radio_nonrx_start = now; + // Initialize timers to "just passed" so millisHasNowPassed() returns true + // immediately. Using 0 breaks when millis is in the upper half of uint32 + // range (near the 49-day wrap), because the signed comparison trick + // interprets 0 as a future timestamp. + next_tx_time = now; + next_floor_calib_time = now; + next_agc_reset_time = now; duty_cycle_window_ms = getDutyCycleWindowMs(); float duty_cycle = 1.0f / (1.0f + getAirtimeBudgetFactor()); diff --git a/src/helpers/ArduinoHelpers.h b/src/helpers/ArduinoHelpers.h index 97596daa31..4e80f5c122 100644 --- a/src/helpers/ArduinoHelpers.h +++ b/src/helpers/ArduinoHelpers.h @@ -24,6 +24,17 @@ class ArduinoMillis : public mesh::MillisecondClock { unsigned long getMillis() override { return millis(); } }; +/** + * \brief Wrap-safe millis deadline check, handling the 49-day millis() overflow. + * \param target The deadline timestamp obtained from millis() + delay. + * \returns true when the deadline has passed. + * \note Use this instead of \c millis()>=target which fails near the 32-bit wrap. + * Works via signed subtraction (2's complement). + */ +inline bool millis_passed(unsigned long target) { + return (long)(millis() - target) > 0; +} + class StdRNG : public mesh::RNG { public: void begin(long seed) { randomSeed(seed); } diff --git a/src/helpers/NRF52Board.cpp b/src/helpers/NRF52Board.cpp index 2c8753d464..7bcd8f0f81 100644 --- a/src/helpers/NRF52Board.cpp +++ b/src/helpers/NRF52Board.cpp @@ -281,7 +281,7 @@ void NRF52Board::sleep(uint32_t secs) { float NRF52Board::getMCUTemperature() { NRF_TEMP->TASKS_START = 1; // Start temperature measurement - long startTime = millis(); + unsigned long startTime = millis(); while (NRF_TEMP->EVENTS_DATARDY == 0) { // Wait for completion. Should complete in 50us if(millis() - startTime > 5) { // To wait 5ms just in case NRF_TEMP->TASKS_STOP = 1; diff --git a/src/helpers/esp32/SerialBLEInterface.cpp b/src/helpers/esp32/SerialBLEInterface.cpp index dcfa0e1e34..f02840ac6a 100644 --- a/src/helpers/esp32/SerialBLEInterface.cpp +++ b/src/helpers/esp32/SerialBLEInterface.cpp @@ -1,5 +1,6 @@ #include "SerialBLEInterface.h" #include "esp_mac.h" +#include // See the following for generating UUIDs: // https://www.uuidgenerator.net/ @@ -183,12 +184,12 @@ size_t SerialBLEInterface::writeFrame(const uint8_t src[], size_t len) { #define BLE_WRITE_MIN_INTERVAL 60 bool SerialBLEInterface::isWriteBusy() const { - return millis() < _last_write + BLE_WRITE_MIN_INTERVAL; // still too soon to start another write? + return !millis_passed(_last_write + BLE_WRITE_MIN_INTERVAL); // still too soon to start another write? } size_t SerialBLEInterface::checkRecvFrame(uint8_t dest[]) { if (send_queue_len > 0 // first, check send queue - && millis() >= _last_write + BLE_WRITE_MIN_INTERVAL // space the writes apart + && millis_passed(_last_write + BLE_WRITE_MIN_INTERVAL) // space the writes apart ) { _last_write = millis(); pTxCharacteristic->setValue(send_queue[0].buf, send_queue[0].len); @@ -238,7 +239,7 @@ size_t SerialBLEInterface::checkRecvFrame(uint8_t dest[]) { oldDeviceConnected = deviceConnected; } - if (adv_restart_time && millis() >= adv_restart_time) { + if (adv_restart_time && millis_passed(adv_restart_time)) { if (pServer->getConnectedCount() == 0) { BLE_DEBUG_PRINTLN("SerialBLEInterface -> re-starting advertising"); pServer->getAdvertising()->start(); // re-Start advertising diff --git a/src/helpers/radiolib/CustomSX1276.h b/src/helpers/radiolib/CustomSX1276.h index bee2527431..087a6784d1 100644 --- a/src/helpers/radiolib/CustomSX1276.h +++ b/src/helpers/radiolib/CustomSX1276.h @@ -1,6 +1,7 @@ #pragma once #include +#include #define RH_RF95_MODEM_STATUS_CLEAR 0x10 #define RH_RF95_MODEM_STATUS_HEADER_INFO_VALID 0x08 @@ -79,7 +80,7 @@ class CustomSX1276 : public SX1276 { // wait for channel activity detected or timeout unsigned long timeout = millis() + 16; - while(!this->mod->hal->digitalRead(this->mod->getIrq()) && millis() < timeout) { + while(!this->mod->hal->digitalRead(this->mod->getIrq()) && !millis_passed(timeout)) { this->mod->hal->yield(); if(this->mod->hal->digitalRead(this->mod->getGpio())) { return(RADIOLIB_PREAMBLE_DETECTED); diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index b5e23b3fe8..0a30c0e8b7 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -1,4 +1,5 @@ #include "EnvironmentSensorManager.h" +#include #include @@ -800,13 +801,13 @@ void EnvironmentSensorManager::stop_gps() { } void EnvironmentSensorManager::loop() { - static long next_gps_update = 0; + static unsigned long next_gps_update = 0; #if ENV_INCLUDE_GPS if (gps_active) { _location->loop(); } - if (millis() > next_gps_update) { + if (millis_passed(next_gps_update)) { if(gps_active){ #ifdef RAK_WISBLOCK_GPS diff --git a/src/helpers/sensors/MicroNMEALocationProvider.h b/src/helpers/sensors/MicroNMEALocationProvider.h index eec466d3aa..17c293ac7d 100644 --- a/src/helpers/sensors/MicroNMEALocationProvider.h +++ b/src/helpers/sensors/MicroNMEALocationProvider.h @@ -4,6 +4,7 @@ #include #include #include +#include #ifndef GPS_EN #ifdef PIN_GPS_EN @@ -42,7 +43,7 @@ class MicroNMEALocationProvider : public LocationProvider { int8_t _claims = 0; int _pin_reset; int _pin_en; - long next_check = 0; + unsigned long next_check = 0; long time_valid = 0; unsigned long _last_time_sync = 0; static const unsigned long TIME_SYNC_INTERVAL = 1800000; // Re-sync every 30 minutes @@ -143,7 +144,7 @@ public : if (!isValid()) time_valid = 0; - if (millis() > next_check) { + if (millis_passed(next_check)) { next_check = millis() + 1000; // Re-enable time sync periodically when GPS has valid fix if (!_time_sync_needed && _clock != NULL && (millis() - _last_time_sync) > TIME_SYNC_INTERVAL) { diff --git a/variants/heltec_mesh_solar/target.cpp b/variants/heltec_mesh_solar/target.cpp index d140864cd1..19ab711681 100644 --- a/variants/heltec_mesh_solar/target.cpp +++ b/variants/heltec_mesh_solar/target.cpp @@ -65,11 +65,11 @@ bool SolarSensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& } void SolarSensorManager::loop() { - static long next_gps_update = 0; + static unsigned long next_gps_update = 0; _location->loop(); - if (millis() > next_gps_update) { + if (millis_passed(next_gps_update)) { if (_location->isValid()) { node_lat = ((double)_location->getLatitude())/1000000.; node_lon = ((double)_location->getLongitude())/1000000.; diff --git a/variants/heltec_tracker/target.cpp b/variants/heltec_tracker/target.cpp index f32c41ff47..a85772eb4a 100644 --- a/variants/heltec_tracker/target.cpp +++ b/variants/heltec_tracker/target.cpp @@ -1,6 +1,7 @@ #include #include "target.h" +#include #include HeltecV3Board board; @@ -71,11 +72,11 @@ bool HWTSensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& t } void HWTSensorManager::loop() { - static long next_gps_update = 0; + static unsigned long next_gps_update = 0; _location->loop(); - if (millis() > next_gps_update) { + if (millis_passed(next_gps_update)) { if (gps_active && _location->isValid()) { node_lat = ((double)_location->getLatitude())/1000000.; node_lon = ((double)_location->getLongitude())/1000000.; diff --git a/variants/meshadventurer/target.cpp b/variants/meshadventurer/target.cpp index 8795a4cbe7..5e9cc346e3 100644 --- a/variants/meshadventurer/target.cpp +++ b/variants/meshadventurer/target.cpp @@ -1,6 +1,7 @@ #include #include "target.h" +#include #include MeshadventurerBoard board; @@ -65,9 +66,9 @@ bool MASensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& te } void MASensorManager::loop() { - static long next_gps_update = 0; + static unsigned long next_gps_update = 0; _location->loop(); - if(millis() > next_gps_update && gps_active) { + if(millis_passed(next_gps_update) && gps_active) { if(_location->isValid()) { node_lat = ((double)_location->getLatitude()) / 1000000.; node_lon = ((double)_location->getLongitude()) / 1000000.; diff --git a/variants/nano_g2_ultra/target.cpp b/variants/nano_g2_ultra/target.cpp index 69a2772ccb..8813449287 100644 --- a/variants/nano_g2_ultra/target.cpp +++ b/variants/nano_g2_ultra/target.cpp @@ -73,7 +73,7 @@ bool NanoG2UltraSensorManager::querySensors(uint8_t requester_permissions, Cayen } void NanoG2UltraSensorManager::loop() { - static long next_gps_update = 0; + static unsigned long next_gps_update = 0; if (!gps_active) { return; // GPS is not active, skip further processing @@ -81,7 +81,7 @@ void NanoG2UltraSensorManager::loop() { _location->loop(); - if (millis() > next_gps_update) { + if (millis_passed(next_gps_update)) { if (_location->isValid()) { node_lat = ((double)_location->getLatitude()) / 1000000.; node_lon = ((double)_location->getLongitude()) / 1000000.; diff --git a/variants/t1000-e/target.cpp b/variants/t1000-e/target.cpp index 4253282708..7e8f7500d5 100644 --- a/variants/t1000-e/target.cpp +++ b/variants/t1000-e/target.cpp @@ -1,6 +1,7 @@ #include #include "t1000e_sensors.h" #include "target.h" +#include #include T1000eBoard board; @@ -147,11 +148,11 @@ bool T1000SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& } void T1000SensorManager::loop() { - static long next_gps_update = 0; + static unsigned long next_gps_update = 0; _nmea->loop(); - if (millis() > next_gps_update) { + if (millis_passed(next_gps_update)) { if (gps_active && _nmea->isValid()) { node_lat = ((double)_nmea->getLatitude())/1000000.; node_lon = ((double)_nmea->getLongitude())/1000000.; diff --git a/variants/thinknode_m1/target.cpp b/variants/thinknode_m1/target.cpp index 69306fc0e1..d151df3208 100644 --- a/variants/thinknode_m1/target.cpp +++ b/variants/thinknode_m1/target.cpp @@ -69,11 +69,11 @@ bool ThinkNodeM1SensorManager::querySensors(uint8_t requester_permissions, Cayen } void ThinkNodeM1SensorManager::loop() { - static long next_gps_update = 0; - static long last_switch_check = 0; + static unsigned long next_gps_update = 0; + static unsigned long last_switch_check = 0; // Check GPS switch state every second - if (millis() - last_switch_check > 1000) { + if (millis_passed(last_switch_check + 1000)) { bool current_switch_state = digitalRead(PIN_GPS_SWITCH); // Detect switch state change @@ -98,7 +98,7 @@ void ThinkNodeM1SensorManager::loop() { _location->loop(); - if (millis() > next_gps_update) { + if (millis_passed(next_gps_update)) { if (_location->isValid()) { node_lat = ((double)_location->getLatitude())/1000000.; node_lon = ((double)_location->getLongitude())/1000000.;