From ca79760372c930b7cbf16937cff6dc9c80e0ae26 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 31 Aug 2025 21:08:58 -0500 Subject: [PATCH 1/9] Add support for the RV-3028 on native Linux (#7802) --- src/configuration.h | 4 ++-- src/gps/RTC.cpp | 8 ++++---- src/main.cpp | 2 +- variants/native/portduino/platformio.ini | 5 ++++- variants/native/portduino/variant.h | 5 ++++- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/configuration.h b/src/configuration.h index 8b4fd82c7..81632c89e 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -26,10 +26,10 @@ along with this program. If not, see . #include -#ifdef RV3028_RTC +#if __has_include("Melopero_RV3028.h") #include "Melopero_RV3028.h" #endif -#ifdef PCF8563_RTC +#if __has_include("pcf8563.h") #include "pcf8563.h" #endif diff --git a/src/gps/RTC.cpp b/src/gps/RTC.cpp index 185adacd9..ceb79eebf 100644 --- a/src/gps/RTC.cpp +++ b/src/gps/RTC.cpp @@ -55,9 +55,9 @@ RTCSetResult readFromRTC() LOG_DEBUG("Read RTC time from RV3028 getTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch); - timeStartMsec = now; - zeroOffsetSecs = tv.tv_sec; if (currentQuality == RTCQualityNone) { + timeStartMsec = now; + zeroOffsetSecs = tv.tv_sec; currentQuality = RTCQualityDevice; } return RTCSetResultSuccess; @@ -94,9 +94,9 @@ RTCSetResult readFromRTC() LOG_DEBUG("Read RTC time from PCF8563 getDateTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch); - timeStartMsec = now; - zeroOffsetSecs = tv.tv_sec; if (currentQuality == RTCQualityNone) { + timeStartMsec = now; + zeroOffsetSecs = tv.tv_sec; currentQuality = RTCQualityDevice; } return RTCSetResultSuccess; diff --git a/src/main.cpp b/src/main.cpp index bdabf5e37..31ce039f9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -421,7 +421,7 @@ void setup() struct timeval tv; tv.tv_sec = time(NULL); tv.tv_usec = 0; - perhapsSetRTC(RTCQualityNTP, &tv); + perhapsSetRTC(RTCQualityDevice, &tv); #endif powerMonInit(); diff --git a/variants/native/portduino/platformio.ini b/variants/native/portduino/platformio.ini index 62942a80e..c47ab8bf1 100644 --- a/variants/native/portduino/platformio.ini +++ b/variants/native/portduino/platformio.ini @@ -3,7 +3,10 @@ extends = portduino_base build_flags = ${portduino_base.build_flags} -I variants/native/portduino -I /usr/include board = cross_platform -lib_deps = ${portduino_base.lib_deps} +lib_deps = + ${portduino_base.lib_deps} + melopero/Melopero RV3028@^1.1.0 + build_src_filter = ${portduino_base.build_src_filter} [env:native] diff --git a/variants/native/portduino/variant.h b/variants/native/portduino/variant.h index ce7dbd865..a7ca865be 100644 --- a/variants/native/portduino/variant.h +++ b/variants/native/portduino/variant.h @@ -4,4 +4,7 @@ #define CANNED_MESSAGE_MODULE_ENABLE 1 #define HAS_GPS 1 #define MAX_RX_TOPHONE settingsMap[maxtophone] -#define MAX_NUM_NODES settingsMap[maxnodes] \ No newline at end of file +#define MAX_NUM_NODES settingsMap[maxnodes] + +// RAK12002 RTC Module +#define RV3028_RTC (uint8_t)0b1010010 \ No newline at end of file From 088be6bf6af84607a8131088461406f5ec8e3b11 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Tue, 2 Sep 2025 14:22:48 +1000 Subject: [PATCH 2/9] Fix device-install.bat baud rate (master --> develop) (#7816) * Upgrade trunk (#7763) Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com> * Fix device-install.bat baud rate As reported by @gruberaaron , work to improve the 1200bps reset for esptool caused all runs of device-install.bat to use 1200bps as the baud rate. This change removes the general SET "ESPTOOL_BAUD=1200" that was causing the issues and places the baud settings for reset mode inside the conditional. Fixes https://github.com/meshtastic/firmware/issues/7172 --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com> --- .trunk/trunk.yaml | 4 ++-- bin/device-install.bat | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index a0dcf2ff5..c57c16319 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -8,8 +8,8 @@ plugins: uri: https://github.com/trunk-io/plugins lint: enabled: - - checkov@3.2.465 - - renovate@41.82.10 + - checkov@3.2.467 + - renovate@41.88.0 - prettier@3.6.2 - trufflehog@3.90.5 - yamllint@1.37.1 diff --git a/bin/device-install.bat b/bin/device-install.bat index 12bfd4f6e..93b2fcec1 100755 --- a/bin/device-install.bat +++ b/bin/device-install.bat @@ -100,7 +100,6 @@ IF NOT "!FILENAME:update=!"=="!FILENAME!" ( ) :skip-filename -SET "ESPTOOL_BAUD=1200" CALL :LOG_MESSAGE DEBUG "Determine the correct esptool command to use..." IF NOT "__%PYTHON%__"=="____" ( @@ -142,7 +141,7 @@ CALL :LOG_MESSAGE INFO "Using esptool baud: !ESPTOOL_BAUD!." IF %BPS_RESET% EQU 1 ( @REM Attempt to change mode via 1200bps Reset. - CALL :RUN_ESPTOOL !ESPTOOL_BAUD! --after no_reset read_flash_status + CALL :RUN_ESPTOOL 1200 --after no_reset read_flash_status GOTO eof ) From d66665b96e23220e8f22be8b41ca0251aab101dd Mon Sep 17 00:00:00 2001 From: Manuel <71137295+mverch67@users.noreply.github.com> Date: Mon, 1 Sep 2025 14:57:49 +0200 Subject: [PATCH 3/9] fix: T-LoRa Pager / T-Deck Pro shutdown (#7792) * power down during LS and shutdown * fix T-Deck Pro shutdown * use device specific define * slightly rephrase the power off display message --- src/Power.cpp | 48 +++++++++++++++++++++++++----------------------- src/sleep.cpp | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 23 deletions(-) diff --git a/src/Power.cpp b/src/Power.cpp index bf74f6e53..a123fe984 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -128,6 +128,7 @@ RAK9154Sensor rak9154Sensor; #ifdef HAS_PPM // note: XPOWERS_CHIP_XXX must be defined in variant.h #include +XPowersPPM *PPM = NULL; #endif #ifdef HAS_BQ27220 @@ -681,7 +682,7 @@ bool Power::setup() found = true; } else if (lipoChargerInit()) { found = true; - } else if (meshSolarInit()) { + } else if (meshSolarInit()) { found = true; } else if (analogInit()) { found = true; @@ -745,7 +746,11 @@ void Power::shutdown() #if HAS_SCREEN if (screen) { +#ifdef T_DECK_PRO + screen->showSimpleBanner("Device is powered off.\nConnect USB to start!", 0); // T-Deck Pro has no power button +#else screen->showSimpleBanner("Shutting Down...", 0); // stays on screen +#endif } #endif #if !defined(ARCH_STM32WL) @@ -763,7 +768,7 @@ void Power::shutdown() #ifdef PIN_LED3 ledOff(PIN_LED3); #endif - doDeepSleep(DELAY_FOREVER, false, true); + doDeepSleep(DELAY_FOREVER, true, true); #elif defined(ARCH_PORTDUINO) exit(EXIT_SUCCESS); #else @@ -1320,7 +1325,6 @@ bool Power::lipoInit() class LipoCharger : public HasBatteryLevel { private: - XPowersPPM *ppm = nullptr; BQ27220 *bq = nullptr; public: @@ -1329,41 +1333,41 @@ class LipoCharger : public HasBatteryLevel */ bool runOnce() { - if (ppm == nullptr) { - ppm = new XPowersPPM; - bool result = ppm->init(Wire, I2C_SDA, I2C_SCL, BQ25896_ADDR); + if (PPM == nullptr) { + PPM = new XPowersPPM; + bool result = PPM->init(Wire, I2C_SDA, I2C_SCL, BQ25896_ADDR); if (result) { LOG_INFO("PPM BQ25896 init succeeded"); // Set the minimum operating voltage. Below this voltage, the PPM will protect - // ppm->setSysPowerDownVoltage(3100); + // PPM->setSysPowerDownVoltage(3100); // Set input current limit, default is 500mA - // ppm->setInputCurrentLimit(800); + // PPM->setInputCurrentLimit(800); // Disable current limit pin - // ppm->disableCurrentLimitPin(); + // PPM->disableCurrentLimitPin(); // Set the charging target voltage, Range:3840 ~ 4608mV ,step:16 mV - ppm->setChargeTargetVoltage(4288); + PPM->setChargeTargetVoltage(4288); // Set the precharge current , Range: 64mA ~ 1024mA ,step:64mA - // ppm->setPrechargeCurr(64); + // PPM->setPrechargeCurr(64); // The premise is that limit pin is disabled, or it will // only follow the maximum charging current set by limit pin. // Set the charging current , Range:0~5056mA ,step:64mA - ppm->setChargerConstantCurr(1024); + PPM->setChargerConstantCurr(1024); // To obtain voltage data, the ADC must be enabled first - ppm->enableMeasure(); + PPM->enableMeasure(); // Turn on charging function // If there is no battery connected, do not turn on the charging function - ppm->enableCharge(); + PPM->enableCharge(); } else { LOG_WARN("PPM BQ25896 init failed"); - delete ppm; - ppm = nullptr; + delete PPM; + PPM = nullptr; return false; } } @@ -1404,23 +1408,23 @@ class LipoCharger : public HasBatteryLevel /** * return true if there is a battery installed in this unit */ - virtual bool isBatteryConnect() override { return ppm->getBattVoltage() > 0; } + virtual bool isBatteryConnect() override { return PPM->getBattVoltage() > 0; } /** * return true if there is an external power source detected */ - virtual bool isVbusIn() override { return ppm->getVbusVoltage() > 0; } + virtual bool isVbusIn() override { return PPM->getVbusVoltage() > 0; } /** * return true if the battery is currently charging */ virtual bool isCharging() override { - bool isCharging = ppm->isCharging(); + bool isCharging = PPM->isCharging(); if (isCharging) { LOG_DEBUG("BQ27220 time to full charge: %d min", bq->getTimeToFull()); } else { - if (!ppm->isVbusIn()) { + if (!PPM->isVbusIn()) { LOG_DEBUG("BQ27220 time to empty: %d min (%d mAh)", bq->getTimeToEmpty(), bq->getRemainingCapacity()); } } @@ -1453,8 +1457,6 @@ bool Power::lipoChargerInit() } #endif - - #ifdef HELTEC_MESH_SOLAR #include "meshSolarApp.h" @@ -1492,7 +1494,7 @@ class meshSolarBatteryLevel : public HasBatteryLevel /** * return true if there is an external power source detected */ - virtual bool isVbusIn() override { return meshSolarIsVbusIn();} + virtual bool isVbusIn() override { return meshSolarIsVbusIn(); } /** * return true if the battery is currently charging diff --git a/src/sleep.cpp b/src/sleep.cpp index 1a5f246c5..bff318900 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -32,6 +32,16 @@ esp_sleep_source_t wakeCause; // the reason we booted this time #endif #include "Throttle.h" +#ifdef USE_XL9555 +#include "ExtensionIOXL9555.hpp" +extern ExtensionIOXL9555 io; +#endif + +#ifdef HAS_PPM +#include +extern XPowersPPM *PPM; +#endif + #ifndef INCLUDE_vTaskSuspend #define INCLUDE_vTaskSuspend 0 #endif @@ -297,6 +307,14 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false, bool skipSaveN #endif #endif +#ifdef HAS_PPM + if (PPM) { + LOG_INFO("PMM shutdown"); + console->flush(); + PPM->shutdown(); + } +#endif + #ifdef HAS_PMU if (pmu_found && PMU) { // Obsolete comment: from back when we we used to receive lora packets while CPU was in deep sleep. @@ -412,6 +430,16 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r if (pmu_found) gpio_wakeup_enable((gpio_num_t)PMU_IRQ, GPIO_INTR_LOW_LEVEL); // pmu irq #endif + +#ifdef T_LORA_PAGER + LOG_DEBUG("power down XL9555 io"); + io.digitalWrite(EXPANDS_DRV_EN, LOW); + io.digitalWrite(EXPANDS_AMP_EN, LOW); + io.digitalWrite(EXPANDS_KB_EN, LOW); + io.digitalWrite(EXPANDS_SD_EN, LOW); + io.digitalWrite(EXPANDS_GPIO_EN, LOW); +#endif + auto res = esp_sleep_enable_gpio_wakeup(); if (res != ESP_OK) { LOG_ERROR("esp_sleep_enable_gpio_wakeup result %d", res); @@ -452,6 +480,14 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r gpio_wakeup_disable((gpio_num_t)RF95_IRQ); } #endif +#ifdef T_LORA_PAGER + LOG_DEBUG("power up XL9555 io"); + io.digitalWrite(EXPANDS_DRV_EN, HIGH); + io.digitalWrite(EXPANDS_AMP_EN, HIGH); + io.digitalWrite(EXPANDS_KB_EN, HIGH); + io.digitalWrite(EXPANDS_SD_EN, HIGH); + io.digitalWrite(EXPANDS_GPIO_EN, HIGH); +#endif esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause(); notifyLightSleepEnd.notifyObservers(cause); // Button interrupts are reattached here From 102c447fe3871fa897d8506440bb1a8a4a00b241 Mon Sep 17 00:00:00 2001 From: Onyx Clawe <58921814+OnyxClawe@users.noreply.github.com> Date: Mon, 1 Sep 2025 05:57:15 -0700 Subject: [PATCH 4/9] Update variant.h (#7520) Updated ADC, Full charge now results in 100% charge being reported instead of 95% charge Co-authored-by: OnyxtheDragon <58921814+OnyxtheDragon@users.noreply.github.com> --- variants/nrf52840/heltec_mesh_node_t114/variant.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/nrf52840/heltec_mesh_node_t114/variant.h b/variants/nrf52840/heltec_mesh_node_t114/variant.h index f4f0baf13..b71106a53 100644 --- a/variants/nrf52840/heltec_mesh_node_t114/variant.h +++ b/variants/nrf52840/heltec_mesh_node_t114/variant.h @@ -208,7 +208,7 @@ No longer populated on PCB #undef AREF_VOLTAGE #define AREF_VOLTAGE 3.0 #define VBAT_AR_INTERNAL AR_INTERNAL_3_0 -#define ADC_MULTIPLIER (4.90F) +#define ADC_MULTIPLIER (4.99F) #define HAS_RTC 0 #ifdef __cplusplus From 16d7de5989a7627cf0c9c44d7abd0686e0afe641 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 07:58:01 -0500 Subject: [PATCH 5/9] Upgrade trunk (#7804) Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com> --- .trunk/trunk.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index c57c16319..651e25b2a 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -9,14 +9,14 @@ plugins: lint: enabled: - checkov@3.2.467 - - renovate@41.88.0 + - renovate@41.90.1 - prettier@3.6.2 - trufflehog@3.90.5 - yamllint@1.37.1 - bandit@1.8.6 - trivy@0.65.0 - taplo@0.10.0 - - ruff@0.12.10 + - ruff@0.12.11 - isort@6.0.1 - markdownlint@0.45.0 - oxipng@9.1.5 From b8d7222423e52098c67e04ea8dffebcd437a28f3 Mon Sep 17 00:00:00 2001 From: Jason P Date: Tue, 2 Sep 2025 05:55:57 -0500 Subject: [PATCH 6/9] If usePreset is False, show value as Custom (#7812) --- src/DisplayFormatters.cpp | 9 ++++++++- src/DisplayFormatters.h | 3 ++- src/graphics/draw/DebugRenderer.cpp | 8 +------- src/mesh/Channels.cpp | 5 +++-- src/mesh/RadioInterface.cpp | 3 ++- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/DisplayFormatters.cpp b/src/DisplayFormatters.cpp index 44bc0897b..d367aa661 100644 --- a/src/DisplayFormatters.cpp +++ b/src/DisplayFormatters.cpp @@ -1,7 +1,14 @@ #include "DisplayFormatters.h" -const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName) +const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName, + bool usePreset) { + + // If use_preset is false, always return "Custom" + if (!usePreset) { + return "Custom"; + } + switch (preset) { case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO: return useShortName ? "ShortT" : "ShortTurbo"; diff --git a/src/DisplayFormatters.h b/src/DisplayFormatters.h index f8ccfcbb6..2d7a3e8db 100644 --- a/src/DisplayFormatters.h +++ b/src/DisplayFormatters.h @@ -4,5 +4,6 @@ class DisplayFormatters { public: - static const char *getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName); + static const char *getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName, + bool usePreset); }; diff --git a/src/graphics/draw/DebugRenderer.cpp b/src/graphics/draw/DebugRenderer.cpp index 446fe7863..3e4030e0f 100644 --- a/src/graphics/draw/DebugRenderer.cpp +++ b/src/graphics/draw/DebugRenderer.cpp @@ -263,12 +263,6 @@ void drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t display->drawString(x + 1, y, "USB"); } - // auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, true); - - // display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode), y, mode); - // if (config.display.heading_bold) - // display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode) - 1, y, mode); - uint32_t currentMillis = millis(); uint32_t seconds = currentMillis / 1000; uint32_t minutes = seconds / 60; @@ -398,7 +392,7 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, display->drawString(nameX, getTextPositions(display)[line++], shortnameble); // === Second Row: Radio Preset === - auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false); + auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false, config.lora.use_preset); char regionradiopreset[25]; const char *region = myRegion ? myRegion->name : NULL; if (region != nullptr) { diff --git a/src/mesh/Channels.cpp b/src/mesh/Channels.cpp index 70e4127d8..4ef41ddfb 100644 --- a/src/mesh/Channels.cpp +++ b/src/mesh/Channels.cpp @@ -368,7 +368,7 @@ const char *Channels::getName(size_t chIndex) // Per mesh.proto spec, if bandwidth is specified we must ignore modemPreset enum, we assume that in that case // the app effed up and forgot to set channelSettings.name if (config.lora.use_preset) { - channelName = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false); + channelName = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false, config.lora.use_preset); } else { channelName = "Custom"; } @@ -382,7 +382,8 @@ bool Channels::isDefaultChannel(ChannelIndex chIndex) const auto &ch = getByIndex(chIndex); if (ch.settings.psk.size == 1 && ch.settings.psk.bytes[0] == 1) { const char *name = getName(chIndex); - const char *presetName = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false); + const char *presetName = + DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false, config.lora.use_preset); // Check if the name is the default derived from the modem preset if (strcmp(name, presetName) == 0) return true; diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index e721431b1..20a0bdbd1 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -589,7 +589,8 @@ void RadioInterface::applyModemConfig() // Check if we use the default frequency slot RadioInterface::uses_default_frequency_slot = - channel_num == hash(DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false)) % numChannels; + channel_num == + hash(DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false, config.lora.use_preset)) % numChannels; // Old frequency selection formula // float freq = myRegion->freqStart + ((((myRegion->freqEnd - myRegion->freqStart) / numChannels) / 2) * channel_num); From c5fad6cca1b1f68f9c47d3d9d260badbf9e7a3f9 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Tue, 2 Sep 2025 21:05:14 +1000 Subject: [PATCH 7/9] Hold for 20s after GPS lock (#7801) * Hold for >20s after GPS lock GPS chips are designed to stay locked for a while to download some data and save it. This data is important for speeding up future locks, and making them higher quality. Our present configuration could make every lock perform similar to first lock. This patch sets a hold of between 20s and 10% of the lock search time after lock is acquired. This should allow the GPS to finish its work before we turn it off. Fixes https://github.com/meshtastic/firmware/issues/7466 * Remove T1000E-specific GPS holds The new code does the same thing, for all devices. * Fix publishing settings * Cleanups, removing unused variables. * ifdef log line with GPS_DEBUG * fixQual is not a bool. --- src/gps/GPS.cpp | 73 +++++++-------------- src/gps/GPS.h | 2 +- variants/nrf52840/tracker-t1000-e/variant.h | 3 +- variants/nrf52840/wio-t1000-s/variant.h | 3 +- 4 files changed, 25 insertions(+), 56 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 881021975..9ae7ae97d 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -843,9 +843,6 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) setPowerPMU(true); // Power (PMU): on writePinStandby(false); // Standby (pin): awake (not standby) setPowerUBLOX(true); // Standby (UBLOX): awake -#ifdef GNSS_AIROHA - lastFixStartMsec = 0; -#endif break; case GPS_SOFTSLEEP: @@ -863,9 +860,7 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) writePinStandby(true); // Standby (pin): asleep (not awake) setPowerUBLOX(false, sleepTime); // Standby (UBLOX): asleep, timed #ifdef GNSS_AIROHA - if (config.position.gps_update_interval * 1000 >= GPS_FIX_HOLD_TIME * 2) { - digitalWrite(PIN_GPS_EN, LOW); - } + digitalWrite(PIN_GPS_EN, LOW); #endif break; @@ -877,9 +872,7 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) writePinStandby(true); // Standby (pin): asleep setPowerUBLOX(false, 0); // Standby (UBLOX): asleep, indefinitely #ifdef GNSS_AIROHA - if (config.position.gps_update_interval * 1000 >= GPS_FIX_HOLD_TIME * 2) { - digitalWrite(PIN_GPS_EN, LOW); - } + digitalWrite(PIN_GPS_EN, LOW); #endif break; } @@ -1062,6 +1055,8 @@ void GPS::down() } // If update interval long enough (or softsleep unsupported): hardsleep instead setPowerState(GPS_HARDSLEEP, sleepTime); + // Reset the fix quality to 0, since we're off. + fixQual = 0; } } @@ -1121,11 +1116,19 @@ int32_t GPS::runOnce() shouldPublish = true; } + uint8_t prev_fixQual = fixQual; bool gotLoc = lookForLocation(); if (gotLoc && !hasValidLocation) { // declare that we have location ASAP LOG_DEBUG("hasValidLocation RISING EDGE"); hasValidLocation = true; shouldPublish = true; + // Hold for 20secs after getting a lock to download ephemeris etc + fixHoldEnds = millis() + 20000; + } + + if (gotLoc && prev_fixQual == 0) { // just got a lock after turning back on. + fixHoldEnds = millis() + 20000; + shouldPublish = true; // Publish immediately, since next publish is at end of hold } bool tooLong = scheduling.searchedTooLong(); @@ -1134,8 +1137,7 @@ int32_t GPS::runOnce() // Once we get a location we no longer desperately want an update if ((gotLoc && gotTime) || tooLong) { - - if (tooLong) { + if (tooLong && !gotLoc) { // we didn't get a location during this ack window, therefore declare loss of lock if (hasValidLocation) { LOG_DEBUG("hasValidLocation FALLING EDGE"); @@ -1143,9 +1145,15 @@ int32_t GPS::runOnce() p = meshtastic_Position_init_default; hasValidLocation = false; } - - down(); - shouldPublish = true; // publish our update for this just finished acquisition window + if (millis() > fixHoldEnds) { + shouldPublish = true; // publish our update at the end of the lock hold + publishUpdate(); + down(); +#ifdef GPS_DEBUG + } else { + LOG_DEBUG("Holding for GPS data download: %d ms (numSats=%d)", fixHoldEnds - millis(), p.sats_in_view); +#endif + } } // If state has changed do a publish @@ -1508,24 +1516,6 @@ static int32_t toDegInt(RawDegrees d) */ bool GPS::lookForTime() { - -#ifdef GNSS_AIROHA - uint8_t fix = reader.fixQuality(); - if (fix >= 1 && fix <= 5) { - if (lastFixStartMsec > 0) { - if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) { - return false; - } else { - clearBuffer(); - } - } else { - lastFixStartMsec = millis(); - return false; - } - } else { - return false; - } -#endif auto ti = reader.time; auto d = reader.date; if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed @@ -1564,25 +1554,6 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s */ bool GPS::lookForLocation() { -#ifdef GNSS_AIROHA - if ((config.position.gps_update_interval * 1000) >= (GPS_FIX_HOLD_TIME * 2)) { - uint8_t fix = reader.fixQuality(); - if (fix >= 1 && fix <= 5) { - if (lastFixStartMsec > 0) { - if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) { - return false; - } else { - clearBuffer(); - } - } else { - lastFixStartMsec = millis(); - return false; - } - } else { - return false; - } - } -#endif // By default, TinyGPS++ does not parse GPGSA lines, which give us // the 2D/3D fixType (see NMEAGPS.h) // At a minimum, use the fixQuality indicator in GPGGA (FIXME?) diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 9be57017f..177cfe74b 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -159,7 +159,7 @@ class GPS : private concurrency::OSThread uint8_t fixType = 0; // fix type from GPGSA #endif - uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastFixStartMsec = 0; + uint32_t fixHoldEnds = 0; uint32_t rx_gpio = 0; uint32_t tx_gpio = 0; diff --git a/variants/nrf52840/tracker-t1000-e/variant.h b/variants/nrf52840/tracker-t1000-e/variant.h index 81b4ef3fb..403552ec0 100644 --- a/variants/nrf52840/tracker-t1000-e/variant.h +++ b/variants/nrf52840/tracker-t1000-e/variant.h @@ -124,8 +124,7 @@ extern "C" { #define GPS_RTC_INT (0 + 15) // P0.15, normal is LOW, wake by HIGH #define GPS_RESETB_OUT (32 + 14) // P1.14, always input pull_up -#define GPS_FIX_HOLD_TIME 15000 // ms -#define BATTERY_PIN 2 // P0.02/AIN0, BAT_ADC +#define BATTERY_PIN 2 // P0.02/AIN0, BAT_ADC #define BATTERY_IMMUTABLE #define ADC_MULTIPLIER (2.0F) // P0.04/AIN2 is VCC_ADC, P0.05/AIN3 is CHARGER_DET, P1.03 is CHARGE_STA, P1.04 is CHARGE_DONE diff --git a/variants/nrf52840/wio-t1000-s/variant.h b/variants/nrf52840/wio-t1000-s/variant.h index eb6a34d6c..02f8a20b2 100644 --- a/variants/nrf52840/wio-t1000-s/variant.h +++ b/variants/nrf52840/wio-t1000-s/variant.h @@ -123,7 +123,6 @@ extern "C" { #define GPS_RESETB_OUT (32 + 14) // P1.14, awlays input pull_up // #define GPS_THREAD_INTERVAL 50 -#define GPS_FIX_HOLD_TIME 15000 // ms #define BATTERY_PIN 2 // #define ADC_CHANNEL ADC1_GPIO2_CHANNEL @@ -157,4 +156,4 @@ extern "C" { * Arduino objects - C++ only *----------------------------------------------------------------------------*/ -#endif // _VARIANT_WIO_SDK_WM1110_ \ No newline at end of file +#endif // _VARIANT_WIO_SDK_WM1110_ From 7612799ef667c15e8e158605dcbd4e7dcf41dc14 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Tue, 2 Sep 2025 21:40:59 +1000 Subject: [PATCH 8/9] Fix GPS that hard code 2080 as the start time. (#7803) * Fix GPS that hard code 2080 as the start time. Some GPS chips, such as the AG3335 in T1000e and L96 have a hardcoded time of 2080-01-05 when they start up. To fix that in a way that seems permanent, let's ignore times that are more than 40 years since the firmware was built. We should followup in late 2039 to see if any changes are needed. Reported-By: @b8b8 * Update src/gps/RTC.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Put FORTY_YEARS in header and use in both places. * Restore Ben's nicer log lines. --------- Co-authored-by: Ben Meadors Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/gps/RTC.cpp | 8 ++++++++ src/gps/RTC.h | 3 +++ 2 files changed, 11 insertions(+) diff --git a/src/gps/RTC.cpp b/src/gps/RTC.cpp index ceb79eebf..e208e2df9 100644 --- a/src/gps/RTC.cpp +++ b/src/gps/RTC.cpp @@ -132,6 +132,10 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd if (tv->tv_sec < BUILD_EPOCH) { LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH); return RTCSetResultInvalidTime; + } else if (tv->tv_sec > (BUILD_EPOCH + FORTY_YEARS)) { + LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch, BUILD_EPOCH, + BUILD_EPOCH + FORTY_YEARS); + return RTCSetResultInvalidTime; } #endif @@ -250,6 +254,10 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t) if (tv.tv_sec < BUILD_EPOCH) { LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH); return RTCSetResultInvalidTime; + } else if (tv.tv_sec > (BUILD_EPOCH + FORTY_YEARS)) { + LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch, BUILD_EPOCH, + BUILD_EPOCH + FORTY_YEARS); + return RTCSetResultInvalidTime; } #endif diff --git a/src/gps/RTC.h b/src/gps/RTC.h index 010be6886..03350823c 100644 --- a/src/gps/RTC.h +++ b/src/gps/RTC.h @@ -55,3 +55,6 @@ time_t gm_mktime(struct tm *tm); #define SEC_PER_DAY 86400 #define SEC_PER_HOUR 3600 #define SEC_PER_MIN 60 +#ifdef BUILD_EPOCH +#define FORTY_YEARS (40UL * 365 * SEC_PER_DAY) // probably time to update your firmware +#endif From 0952007805e447a185ead329aceb88eb21f39d3d Mon Sep 17 00:00:00 2001 From: Chloe Bethel Date: Tue, 2 Sep 2025 13:08:57 +0100 Subject: [PATCH 9/9] Make ExternalNotification show up in excluded_modules, more STM32 modules (#7797) * Show ExternalNotification as excluded if it is * Enable ExternalNotification, SerialModule and RangeTest on STM32WL * Misc fixes for #7797 - ARCH_STM32 -> ARCH_STM32WL, use less flash by dropping weather station support for serialmodule, set tx/rx pins before begin * Enable Serial1 on RAK3172, make SerialModule use it (console is on LPUART1) * Fix SerialModule on RAK3172, fix board definition of RAK3172 to include the right pin mapping. --- boards/wiscore_rak3172.json | 2 +- src/main.cpp | 2 +- src/modules/ExternalNotificationModule.cpp | 3 ++- src/modules/Modules.cpp | 9 +++---- src/modules/RangeTestModule.cpp | 4 +-- src/modules/SerialModule.cpp | 30 +++++++++++++++++----- src/modules/SerialModule.h | 4 +-- variants/stm32/rak3172/platformio.ini | 4 +++ 8 files changed, 38 insertions(+), 20 deletions(-) diff --git a/boards/wiscore_rak3172.json b/boards/wiscore_rak3172.json index 714e09115..69ee506b4 100644 --- a/boards/wiscore_rak3172.json +++ b/boards/wiscore_rak3172.json @@ -5,7 +5,7 @@ }, "core": "stm32", "cpu": "cortex-m4", - "extra_flags": "-DSTM32WLxx -DSTM32WLE5xx -DARDUINO_GENERIC_WLE5CCUX", + "extra_flags": "-DSTM32WLxx -DSTM32WLE5xx -DARDUINO_RAK3172_MODULE", "f_cpu": "48000000L", "mcu": "stm32wle5ccu", "variant": "STM32WLxx/WL54CCU_WL55CCU_WLE4C(8-B-C)U_WLE5C(8-B-C)U", diff --git a/src/main.cpp b/src/main.cpp index 31ce039f9..73f68e95e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1531,7 +1531,7 @@ extern meshtastic_DeviceMetadata getDeviceMetadata() #if ((!HAS_SCREEN || NO_EXT_GPIO) || MESHTASTIC_EXCLUDE_CANNEDMESSAGES) && !defined(MESHTASTIC_INCLUDE_NICHE_GRAPHICS) deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_CANNEDMSG_CONFIG; #endif -#if NO_EXT_GPIO +#if NO_EXT_GPIO || MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_EXTNOTIF_CONFIG; #endif // Only edge case here is if we apply this a device with built in Accelerometer and want to detect interrupts diff --git a/src/modules/ExternalNotificationModule.cpp b/src/modules/ExternalNotificationModule.cpp index 1f871f87e..2f2934984 100644 --- a/src/modules/ExternalNotificationModule.cpp +++ b/src/modules/ExternalNotificationModule.cpp @@ -364,9 +364,10 @@ ExternalNotificationModule::ExternalNotificationModule() // moduleConfig.external_notification.alert_message_buzzer = true; if (moduleConfig.external_notification.enabled) { +#if !defined(MESHTASTIC_EXCLUDE_INPUTBROKER) if (inputBroker) // put our callback in the inputObserver list inputObserver.observe(inputBroker); - +#endif if (nodeDB->loadProto(rtttlConfigFile, meshtastic_RTTTLConfig_size, sizeof(meshtastic_RTTTLConfig), &meshtastic_RTTTLConfig_msg, &rtttlConfig) != LoadFileResult::LOAD_SUCCESS) { memset(rtttlConfig.ringtone, 0, sizeof(rtttlConfig.ringtone)); diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index 0d405fa81..b9b4dd3e5 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -88,7 +88,7 @@ #include "modules/StoreForwardModule.h" #endif #endif -#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040) || defined(ARCH_PORTDUINO) + #if !MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION #include "modules/ExternalNotificationModule.h" #endif @@ -98,7 +98,6 @@ #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !MESHTASTIC_EXCLUDE_SERIAL #include "modules/SerialModule.h" #endif -#endif #if !MESHTASTIC_EXCLUDE_DROPZONE #include "modules/DropzoneModule.h" @@ -246,8 +245,8 @@ void setupModules() #if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_POWER_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR new PowerTelemetryModule(); #endif -#if (defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040)) && !defined(CONFIG_IDF_TARGET_ESP32S2) && \ - !defined(CONFIG_IDF_TARGET_ESP32C3) +#if (defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040) || defined(ARCH_STM32WL)) && \ + !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) #if !MESHTASTIC_EXCLUDE_SERIAL if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) { new SerialModule(); @@ -268,13 +267,11 @@ void setupModules() storeForwardModule = new StoreForwardModule(); #endif #endif -#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040) || defined(ARCH_PORTDUINO) #if !MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION externalNotificationModule = new ExternalNotificationModule(); #endif #if !MESHTASTIC_EXCLUDE_RANGETEST && !MESHTASTIC_EXCLUDE_GPS new RangeTestModule(); -#endif #endif } else { #if !MESHTASTIC_EXCLUDE_ADMIN diff --git a/src/modules/RangeTestModule.cpp b/src/modules/RangeTestModule.cpp index 6f3d69acf..d1d2d9ead 100644 --- a/src/modules/RangeTestModule.cpp +++ b/src/modules/RangeTestModule.cpp @@ -31,7 +31,7 @@ uint32_t packetSequence = 0; int32_t RangeTestModule::runOnce() { -#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) +#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_STM32WL) || defined(ARCH_PORTDUINO) /* Uncomment the preferences below if you want to use the module @@ -130,7 +130,7 @@ void RangeTestModuleRadio::sendPayload(NodeNum dest, bool wantReplies) ProcessMessage RangeTestModuleRadio::handleReceived(const meshtastic_MeshPacket &mp) { -#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) +#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_STM32WL) || defined(ARCH_PORTDUINO) if (moduleConfig.range_test.enabled) { diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp index 880768839..7485f1c2d 100644 --- a/src/modules/SerialModule.cpp +++ b/src/modules/SerialModule.cpp @@ -49,8 +49,8 @@ #include "meshSolarApp.h" #endif -#if (defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040)) && !defined(CONFIG_IDF_TARGET_ESP32S2) && \ - !defined(CONFIG_IDF_TARGET_ESP32C3) +#if (defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040) || defined(ARCH_STM32WL)) && \ + !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) #define RX_BUFFER 256 #define TIMEOUT 250 @@ -67,7 +67,7 @@ SerialModuleRadio *serialModuleRadio; defined(ELECROW_ThinkNode_M5) || defined(HELTEC_MESH_SOLAR) || defined(T_ECHO_LITE) SerialModule::SerialModule() : StreamAPI(&Serial), concurrency::OSThread("Serial") {} static Print *serialPrint = &Serial; -#elif defined(CONFIG_IDF_TARGET_ESP32C6) +#elif defined(CONFIG_IDF_TARGET_ESP32C6) || defined(RAK3172) SerialModule::SerialModule() : StreamAPI(&Serial1), concurrency::OSThread("Serial") {} static Print *serialPrint = &Serial1; #else @@ -173,7 +173,18 @@ int32_t SerialModule::runOnce() Serial.begin(baud); Serial.setTimeout(moduleConfig.serial.timeout > 0 ? moduleConfig.serial.timeout : TIMEOUT); } - +#elif defined(ARCH_STM32WL) +#ifndef RAK3172 + HardwareSerial *serialInstance = &Serial2; +#else + HardwareSerial *serialInstance = &Serial1; +#endif + if (moduleConfig.serial.rxd && moduleConfig.serial.txd) { + serialInstance->setTx(moduleConfig.serial.txd); + serialInstance->setRx(moduleConfig.serial.rxd); + } + serialInstance->begin(baud); + serialInstance->setTimeout(moduleConfig.serial.timeout > 0 ? moduleConfig.serial.timeout : TIMEOUT); #elif defined(ARCH_ESP32) if (moduleConfig.serial.rxd && moduleConfig.serial.txd) { @@ -260,8 +271,13 @@ int32_t SerialModule::runOnce() while (Serial1.available()) { serialPayloadSize = Serial1.readBytes(serialBytes, meshtastic_Constants_DATA_PAYLOAD_LEN); #else - while (Serial2.available()) { - serialPayloadSize = Serial2.readBytes(serialBytes, meshtastic_Constants_DATA_PAYLOAD_LEN); +#ifndef RAK3172 + HardwareSerial *serialInstance = &Serial2; +#else + HardwareSerial *serialInstance = &Serial1; +#endif + while (serialInstance->available()) { + serialPayloadSize = serialInstance->readBytes(serialBytes, meshtastic_Constants_DATA_PAYLOAD_LEN); #endif serialModuleRadio->sendPayload(); } @@ -511,7 +527,7 @@ ParsedLine parseLine(const char *line) void SerialModule::processWXSerial() { #if !defined(TTGO_T_ECHO) && !defined(T_ECHO_LITE) && !defined(CANARYONE) && !defined(CONFIG_IDF_TARGET_ESP32C6) && \ - !defined(MESHLINK) && !defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M5) + !defined(MESHLINK) && !defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M5) && !defined(ARCH_STM32WL) static unsigned int lastAveraged = 0; static unsigned int averageIntervalMillis = 300000; // 5 minutes hard coded. static double dir_sum_sin = 0; diff --git a/src/modules/SerialModule.h b/src/modules/SerialModule.h index 1c74c927c..dbe4f75db 100644 --- a/src/modules/SerialModule.h +++ b/src/modules/SerialModule.h @@ -8,8 +8,8 @@ #include #include -#if (defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040)) && !defined(CONFIG_IDF_TARGET_ESP32S2) && \ - !defined(CONFIG_IDF_TARGET_ESP32C3) +#if (defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040) || defined(ARCH_STM32WL)) && \ + !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) class SerialModule : public StreamAPI, private concurrency::OSThread { diff --git a/variants/stm32/rak3172/platformio.ini b/variants/stm32/rak3172/platformio.ini index 4f9edbb92..a12b9f21c 100644 --- a/variants/stm32/rak3172/platformio.ini +++ b/variants/stm32/rak3172/platformio.ini @@ -6,6 +6,10 @@ board_upload.maximum_size = 233472 ; reserve the last 28KB for filesystem build_flags = ${stm32_base.build_flags} -Ivariants/stm32/rak3172 + -DRAK3172 + -DENABLE_HWSERIAL1 + -DPIN_SERIAL1_RX=PB7 + -DPIN_SERIAL1_TX=PB6 -DPIN_WIRE_SDA=PA11 -DPIN_WIRE_SCL=PA12 -DMESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR=1