From 1c61ed57a6d34307d6d6dabbdb93079347a848d1 Mon Sep 17 00:00:00 2001 From: mattzzw Date: Fri, 27 Feb 2026 09:06:32 +0100 Subject: [PATCH 01/17] Update README.md Add MeshCore-Evo description --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 5a094175..8aefbed6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,18 @@ + +## About MeshCore-Evo + +This is a friendly fork of the MeshCore project. +Its aim is to provide repeater firmwares with a few additional pending upstream PRs/improvements (i.e. PRs/improvements that are available to the MeshCore project but have not yet been merged in the upstream repo or PRs that might never be merged upstream for some reason). +These changes are intended to help mitigating challenges in big or dense meshes, e.g.: + +- Dealing with flood advert traffic +- fixing TX duty cycle on a rolling window basis +- improving `denyf *` handling + +This list might change any time. + + + ## About MeshCore MeshCore is a lightweight, portable C++ library that enables multi-hop packet routing for embedded projects using LoRa and other packet radios. It is designed for developers who want to create resilient, decentralized communication networks that work without the internet. From 9831fd14709db90cf712b320ec96f06a9905c2e7 Mon Sep 17 00:00:00 2001 From: mattzzw Date: Fri, 27 Feb 2026 09:11:52 +0100 Subject: [PATCH 02/17] Update README.md Add MeshCore-Evo description --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8aefbed6..8de26e1c 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ These changes are intended to help mitigating challenges in big or dense meshes, - improving `denyf *` handling This list might change any time. +Refer to the [release notes](https://github.com/mattzzw/MeshCore-Evo/releases) for an up-to-date list of the applied changes. From d16f465363eba283455b3c6e79d721ac3aa94877 Mon Sep 17 00:00:00 2001 From: mattzzw Date: Sun, 29 Mar 2026 12:14:47 +0200 Subject: [PATCH 03/17] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 9023b4c0..f21b53fa 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ Its aim is to provide repeater firmwares with a few additional pending upstream These changes are intended to help mitigating challenges in big or dense meshes, e.g.: - Dealing with flood advert traffic -- fixing TX duty cycle on a rolling window basis - improving `denyf *` handling This list might change any time. From 06e0273e1fca5296eb00ec0e62d658cf358d651d Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Tue, 21 Apr 2026 12:07:39 +1000 Subject: [PATCH 04/17] * CommonCLI: bounds check added to "unknown config:" replies --- src/helpers/CommonCLI.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index d495aada..1f03d915 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -2,6 +2,7 @@ #include "CommonCLI.h" #include "TxtDataHelpers.h" #include "AdvertDataHelpers.h" +#include "TxtDataHelpers.h" #include #ifndef BRIDGE_MAX_BAUD @@ -726,7 +727,8 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep strcpy(reply, "Error: unsupported by this board"); }; } else { - sprintf(reply, "unknown config: %s", config); + strcpy(reply, "unknown config: "); + StrHelper::strncpy(&reply[16], config, 160-17); } } From aa796e8623365fe1087bd610077a401c66d3c4a8 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Tue, 21 Apr 2026 12:12:47 +1000 Subject: [PATCH 05/17] * CommonCLI: more reply bounds checking --- src/helpers/CommonCLI.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 1f03d915..b71afc72 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -286,7 +286,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re // change admin password StrHelper::strncpy(_prefs->password, &command[9], sizeof(_prefs->password)); savePrefs(); - sprintf(reply, "password now: %s", _prefs->password); // echo back just to let admin know for sure!! + sprintf(reply, "password now: "); + StrHelper::strncpy(&reply[14], _prefs->password, 160-15); // echo back just to let admin know for sure!! } else if (memcmp(command, "clear stats", 11) == 0) { _callbacks->clearStats(); strcpy(reply, "(OK - stats reset)"); @@ -786,10 +787,11 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep } else if (memcmp(config, "direct.txdelay", 14) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor)); } else if (memcmp(config, "owner.info", 10) == 0) { + auto start = reply; *reply++ = '>'; *reply++ = ' '; const char* sp = _prefs->owner_info; - while (*sp) { + while (*sp && reply - start < 159) { *reply++ = (*sp == '\n') ? '|' : *sp; // translate newline back to orig '|' sp++; } From 321d5a7e987f92501788efa64af760b9590879b8 Mon Sep 17 00:00:00 2001 From: Marco Date: Sun, 19 Apr 2026 15:29:43 +0200 Subject: [PATCH 06/17] Add Heltec V4 set adc.multiplier --- variants/heltec_v4/HeltecV4Board.cpp | 2 +- variants/heltec_v4/HeltecV4Board.h | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/variants/heltec_v4/HeltecV4Board.cpp b/variants/heltec_v4/HeltecV4Board.cpp index 49580d2e..8f013797 100644 --- a/variants/heltec_v4/HeltecV4Board.cpp +++ b/variants/heltec_v4/HeltecV4Board.cpp @@ -73,7 +73,7 @@ void HeltecV4Board::begin() { digitalWrite(PIN_ADC_CTRL, LOW); - return (5.42 * (3.3 / 1024.0) * raw) * 1000; + return (adc_mult * (3.3 / 1024.0) * raw) * 1000; } const char* HeltecV4Board::getManufacturerName() const { diff --git a/variants/heltec_v4/HeltecV4Board.h b/variants/heltec_v4/HeltecV4Board.h index 4d5ee461..95def06c 100644 --- a/variants/heltec_v4/HeltecV4Board.h +++ b/variants/heltec_v4/HeltecV4Board.h @@ -5,8 +5,16 @@ #include #include #include "LoRaFEMControl.h" + +#ifndef ADC_MULTIPLIER + #define ADC_MULTIPLIER 5.42 +#endif + class HeltecV4Board : public ESP32Board { +protected: + float adc_mult = ADC_MULTIPLIER; + public: RefCountedDigitalPin periph_power; LoRaFEMControl loRaFEMControl; @@ -18,6 +26,14 @@ public: void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1); void powerOff() override; uint16_t getBattMilliVolts() override; - const char* getManufacturerName() const override ; - + bool setAdcMultiplier(float multiplier) override { + if (multiplier == 0.0f) { + adc_mult = ADC_MULTIPLIER; + } else { + adc_mult = multiplier; + } + return true; + } + float getAdcMultiplier() const override { return adc_mult; } + const char* getManufacturerName() const override; }; From 1f066ad9c4a66f190009730fed4b0f43a05aef39 Mon Sep 17 00:00:00 2001 From: Wessel Nieboer Date: Tue, 21 Apr 2026 21:29:18 +0200 Subject: [PATCH 07/17] Implement proper shutdown procedure for R1 neo fixes #2361 --- variants/muziworks_r1_neo/R1NeoBoard.cpp | 5 ++++- variants/muziworks_r1_neo/R1NeoBoard.h | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/variants/muziworks_r1_neo/R1NeoBoard.cpp b/variants/muziworks_r1_neo/R1NeoBoard.cpp index 616d255a..89cde02a 100644 --- a/variants/muziworks_r1_neo/R1NeoBoard.cpp +++ b/variants/muziworks_r1_neo/R1NeoBoard.cpp @@ -14,9 +14,12 @@ const PowerMgtConfig power_config = { void R1NeoBoard::initiateShutdown(uint8_t reason) { // Disable LoRa module power before shutdown - MESH_DEBUG_PRINTLN("R1Neo: shutting down"); digitalWrite(SX126X_POWER_EN, LOW); + // Signal IO controller that MCU is off, then release DCDC latch + digitalWrite(PIN_SOFT_SHUTDOWN, LOW); + digitalWrite(PIN_DCDC_EN_MCU_HOLD, LOW); + if (reason == SHUTDOWN_REASON_LOW_VOLTAGE || reason == SHUTDOWN_REASON_BOOT_PROTECT) { configureVoltageWake(power_config.lpcomp_ain_channel, power_config.lpcomp_refsel); diff --git a/variants/muziworks_r1_neo/R1NeoBoard.h b/variants/muziworks_r1_neo/R1NeoBoard.h index c27ea232..c56f1f5c 100644 --- a/variants/muziworks_r1_neo/R1NeoBoard.h +++ b/variants/muziworks_r1_neo/R1NeoBoard.h @@ -18,6 +18,12 @@ public: R1NeoBoard() : NRF52Board("R1NEO_OTA") {} void begin(); +#ifdef NRF52_POWER_MANAGEMENT + void powerOff() override { + initiateShutdown(SHUTDOWN_REASON_USER); + } +#endif + #if defined(P_LORA_TX_LED) void onBeforeTransmit() override { digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on From c33639ee3c7cdf3c160cfde62a8be9b418abdaa3 Mon Sep 17 00:00:00 2001 From: Wessel Nieboer Date: Fri, 13 Mar 2026 14:06:09 +0100 Subject: [PATCH 08/17] Add sanity build for LR1110 and SX1276 too --- .github/workflows/pr-build-check.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/pr-build-check.yml b/.github/workflows/pr-build-check.yml index 37f3701b..67468b56 100644 --- a/.github/workflows/pr-build-check.yml +++ b/.github/workflows/pr-build-check.yml @@ -39,6 +39,10 @@ jobs: - wio-e5-mini_repeater # ESP32-C6 - LilyGo_Tlora_C6_repeater_ + # LR1110 (nRF52) + - wio_wm1110_repeater + # SX1276 (ESP32) + - Tbeam_SX1276_repeater steps: - name: Clone Repo From b4f690bfebba360b73efb57cf23353cbceb94afe Mon Sep 17 00:00:00 2001 From: Wessel Nieboer Date: Mon, 20 Apr 2026 09:42:39 +0200 Subject: [PATCH 09/17] Fix FEM/LNA enbaled by default for Heltec T096, Heltec Wireless Tracker v2 Should only really be disabled when it causes issues. --- variants/heltec_t096/LoRaFEMControl.cpp | 2 +- variants/heltec_t096/LoRaFEMControl.h | 2 +- variants/heltec_tracker_v2/LoRaFEMControl.cpp | 2 +- variants/heltec_tracker_v2/LoRaFEMControl.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/variants/heltec_t096/LoRaFEMControl.cpp b/variants/heltec_t096/LoRaFEMControl.cpp index 9aeb8385..fb60a8f7 100644 --- a/variants/heltec_t096/LoRaFEMControl.cpp +++ b/variants/heltec_t096/LoRaFEMControl.cpp @@ -9,7 +9,7 @@ void LoRaFEMControl::init(void) pinMode(P_LORA_KCT8103L_PA_CSD, OUTPUT); digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH); pinMode(P_LORA_KCT8103L_PA_CTX, OUTPUT); - digitalWrite(P_LORA_KCT8103L_PA_CTX, HIGH); + digitalWrite(P_LORA_KCT8103L_PA_CTX, lna_enabled ? LOW : HIGH); setLnaCanControl(true); } diff --git a/variants/heltec_t096/LoRaFEMControl.h b/variants/heltec_t096/LoRaFEMControl.h index 2c50b742..fe61b7e4 100644 --- a/variants/heltec_t096/LoRaFEMControl.h +++ b/variants/heltec_t096/LoRaFEMControl.h @@ -16,6 +16,6 @@ class LoRaFEMControl void setLnaCanControl(bool can_control) { lna_can_control = can_control; } private: - bool lna_enabled = false; + bool lna_enabled = true; bool lna_can_control = false; }; diff --git a/variants/heltec_tracker_v2/LoRaFEMControl.cpp b/variants/heltec_tracker_v2/LoRaFEMControl.cpp index 3cf6c311..b846465d 100644 --- a/variants/heltec_tracker_v2/LoRaFEMControl.cpp +++ b/variants/heltec_tracker_v2/LoRaFEMControl.cpp @@ -14,7 +14,7 @@ void LoRaFEMControl::init(void) pinMode(P_LORA_KCT8103L_PA_CSD, OUTPUT); digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH); pinMode(P_LORA_KCT8103L_PA_CTX, OUTPUT); - digitalWrite(P_LORA_KCT8103L_PA_CTX, HIGH); + digitalWrite(P_LORA_KCT8103L_PA_CTX, lna_enabled ? LOW : HIGH); setLnaCanControl(true); } diff --git a/variants/heltec_tracker_v2/LoRaFEMControl.h b/variants/heltec_tracker_v2/LoRaFEMControl.h index 2c50b742..fe61b7e4 100644 --- a/variants/heltec_tracker_v2/LoRaFEMControl.h +++ b/variants/heltec_tracker_v2/LoRaFEMControl.h @@ -16,6 +16,6 @@ class LoRaFEMControl void setLnaCanControl(bool can_control) { lna_can_control = can_control; } private: - bool lna_enabled = false; + bool lna_enabled = true; bool lna_can_control = false; }; From 46b5076082c2098e4ecc9ef240d7e5bcbe52c740 Mon Sep 17 00:00:00 2001 From: Wessel Nieboer Date: Thu, 12 Mar 2026 05:32:27 +0100 Subject: [PATCH 10/17] Fix RAK4631 SX1262 hardware pin config --- variants/rak4631/variant.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/rak4631/variant.h b/variants/rak4631/variant.h index 142d93e9..38cc8868 100644 --- a/variants/rak4631/variant.h +++ b/variants/rak4631/variant.h @@ -147,7 +147,7 @@ extern "C" // LoRa radio module pins for RAK4631 #define P_LORA_DIO_1 (47) #define P_LORA_NSS (42) -#define P_LORA_RESET (-1) +#define P_LORA_RESET (38) #define P_LORA_BUSY (46) #define P_LORA_SCLK (43) #define P_LORA_MISO (45) From 8217a67f0cff130da5f38b07ffd201323342c3c8 Mon Sep 17 00:00:00 2001 From: pelgraine <140762863+pelgraine@users.noreply.github.com> Date: Fri, 10 Apr 2026 14:25:51 +1000 Subject: [PATCH 11/17] =?UTF-8?q?Fixes=20#1183=20=E2=80=94=20T-Echo=20Lite?= =?UTF-8?q?=20incorrect=20battery=20voltage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Battery (TechoBoard.h/cpp): Added PIN_VBAT_MEAS_EN (P0.31) — the T-Echo Lite has a gated voltage divider that must be enabled before reading. Also added pinMode(PIN_VBAT_READ, INPUT) before each ADC read to reclaim P0.02 from other peripherals. Pin definitions hardcoded from LilyGo's t_echo_lite_config.h. I2C (variant.h): Corrected SDA/SCL from P0.04/P0.02 to P1.04/P1.02 per LilyGo's IIC_1_SDA/IIC_1_SCL. The old P0.02 mapping conflicted with the battery ADC pin. GPS (variant.h): Corrected all five GPS pin assignments to match LilyGo's config — UART TX/RX, wake, PPS, and power enable were all scrambled. SPI (variant.h): Fixed SPI_INTERFACES_COUNT from _PINNUM(0, 2) to (2). Tested on T-Echo Lite Non-Shell (USB-C, no display). Battery readings match Heltec V4 reference within 10mV. --- variants/lilygo_techo_lite/TechoBoard.cpp | 43 +++++++++++++++++------ variants/lilygo_techo_lite/TechoBoard.h | 19 +++++----- variants/lilygo_techo_lite/variant.h | 21 +++++------ 3 files changed, 53 insertions(+), 30 deletions(-) diff --git a/variants/lilygo_techo_lite/TechoBoard.cpp b/variants/lilygo_techo_lite/TechoBoard.cpp index 81d3d0c9..a11d31b2 100644 --- a/variants/lilygo_techo_lite/TechoBoard.cpp +++ b/variants/lilygo_techo_lite/TechoBoard.cpp @@ -8,24 +8,47 @@ void TechoBoard::begin() { NRF52Board::begin(); + // Configure battery measurement control BEFORE Wire.begin() + // to ensure P0.02 is not claimed by another peripheral + pinMode(PIN_VBAT_MEAS_EN, OUTPUT); + digitalWrite(PIN_VBAT_MEAS_EN, LOW); + pinMode(PIN_VBAT_READ, INPUT); + Wire.begin(); pinMode(SX126X_POWER_EN, OUTPUT); digitalWrite(SX126X_POWER_EN, HIGH); - delay(10); // give sx1262 some time to power up + delay(10); } uint16_t TechoBoard::getBattMilliVolts() { - int adcvalue = 0; - + // Use LilyGo's exact ADC configuration analogReference(AR_INTERNAL_3_0); analogReadResolution(12); - delay(10); - // ADC range is 0..3000mV and resolution is 12-bit (0..4095) - adcvalue = analogRead(PIN_VBAT_READ); - // Convert the raw value to compensated mv, taking the resistor- - // divider into account (providing the actual LIPO voltage) - return (uint16_t)((float)adcvalue * REAL_VBAT_MV_PER_LSB); + // Enable battery voltage divider (MOSFET gate on P0.31) + pinMode(PIN_VBAT_MEAS_EN, OUTPUT); + digitalWrite(PIN_VBAT_MEAS_EN, HIGH); + + // Reclaim P0.02 for analog input (in case another peripheral touched it) + pinMode(PIN_VBAT_READ, INPUT); + delay(10); // let divider + ADC settle + + // Read and average (matching LilyGo's approach) + uint32_t sum = 0; + for (int i = 0; i < 8; i++) { + sum += analogRead(PIN_VBAT_READ); + delayMicroseconds(100); + } + uint16_t adc = sum / 8; + + // Disable divider to save power + digitalWrite(PIN_VBAT_MEAS_EN, LOW); + + // LilyGo's exact formula: adc * (3000.0 / 4096.0) * 2.0 + // = adc * 0.73242188 * 2.0 = adc * 1.46484375 + uint16_t millivolts = (uint16_t)((float)adc * (3000.0f / 4096.0f) * 2.0f); + + return millivolts; } -#endif +#endif \ No newline at end of file diff --git a/variants/lilygo_techo_lite/TechoBoard.h b/variants/lilygo_techo_lite/TechoBoard.h index fda393e7..7a43fd83 100644 --- a/variants/lilygo_techo_lite/TechoBoard.h +++ b/variants/lilygo_techo_lite/TechoBoard.h @@ -4,14 +4,12 @@ #include #include -// built-ins -#define VBAT_MV_PER_LSB (0.73242188F) // 3.0V ADC range and 12-bit ADC resolution = 3000mV/4096 - -#define VBAT_DIVIDER (0.5F) // 150K + 150K voltage divider on VBAT -#define VBAT_DIVIDER_COMP (2.0F) // Compensation factor for the VBAT divider - -#define PIN_VBAT_READ (4) -#define REAL_VBAT_MV_PER_LSB (VBAT_DIVIDER_COMP * VBAT_MV_PER_LSB) +// ============================================================ +// T-Echo Lite battery pins — hardcoded from LilyGo t_echo_lite_config.h +// NOT using any defines from variant.h for battery measurement +// ============================================================ +#define PIN_VBAT_READ _PINNUM(0, 2) // BATTERY_ADC_DATA +#define PIN_VBAT_MEAS_EN _PINNUM(0, 31) // BATTERY_MEASUREMENT_CONTROL class TechoBoard : public NRF52BoardDCDC { public: @@ -20,10 +18,11 @@ public: uint16_t getBattMilliVolts() override; const char* getManufacturerName() const override { - return "LilyGo T-Echo"; + return "LilyGo T-Echo Lite"; } void powerOff() override { + digitalWrite(PIN_VBAT_MEAS_EN, LOW); #ifdef LED_RED digitalWrite(LED_RED, LOW); #endif @@ -41,4 +40,4 @@ public: #endif sd_power_system_off(); } -}; +}; \ No newline at end of file diff --git a/variants/lilygo_techo_lite/variant.h b/variants/lilygo_techo_lite/variant.h index 16e0b5cb..07202165 100644 --- a/variants/lilygo_techo_lite/variant.h +++ b/variants/lilygo_techo_lite/variant.h @@ -24,7 +24,7 @@ #define PIN_PWR_EN _PINNUM(0, 30) // RT9080_EN #define BATTERY_PIN _PINNUM(0, 2) -#define ADC_MULTIPLIER (4.90F) +#define ADC_MULTIPLIER (2.0F) #define ADC_RESOLUTION (14) #define BATTERY_SENSE_RES (12) @@ -47,13 +47,13 @@ //////////////////////////////////////////////////////////////////////////////// // I2C pin definition -#define PIN_WIRE_SDA _PINNUM(0, 4) // (SDA) -#define PIN_WIRE_SCL _PINNUM(0, 2) // (SCL) +#define PIN_WIRE_SDA _PINNUM(1, 4) // (SDA) - per LilyGo IIC_1_SDA +#define PIN_WIRE_SCL _PINNUM(1, 2) // (SCL) - per LilyGo IIC_1_SCL //////////////////////////////////////////////////////////////////////////////// // SPI pin definition -#define SPI_INTERFACES_COUNT _PINNUM(0, 2) +#define SPI_INTERFACES_COUNT (2) #define PIN_SPI_MISO _PINNUM(0, 17) // (MISO) #define PIN_SPI_MOSI _PINNUM(0, 15) // (MOSI) @@ -149,10 +149,11 @@ extern const int SCK; #define PIN_DISPLAY_BUSY DISP_BUSY //////////////////////////////////////////////////////////////////////////////// -// GPS +// GPS — per LilyGo t_echo_lite_config.h +// PIN_GPS_TX/RX named from GPS module's perspective -#define PIN_GPS_RX _PINNUM(1, 13) // RXD -#define PIN_GPS_TX _PINNUM(1, 15) // TXD -#define GPS_EN _PINNUM(1, 11) // POWER_RT9080_EN -#define PIN_GPS_STANDBY _PINNUM(1, 10) -#define PIN_GPS_PPS _PINNUM(0, 29) // 1PPS +#define PIN_GPS_TX _PINNUM(0, 29) // GPS UART TX → MCU RX +#define PIN_GPS_RX _PINNUM(1, 10) // GPS UART RX ← MCU TX +#define GPS_EN _PINNUM(1, 11) // GPS RT9080 power enable +#define PIN_GPS_STANDBY _PINNUM(1, 13) // GPS wake-up +#define PIN_GPS_PPS _PINNUM(1, 15) // GPS 1PPS \ No newline at end of file From e902b8f121393008aaf9ab3540935e4ea1543e0b Mon Sep 17 00:00:00 2001 From: me Date: Mon, 20 Apr 2026 20:45:04 -0700 Subject: [PATCH 12/17] feat(techo-lite): add Non-Shell (screenless) companion BLE variant T-Echo Lite Non-Shell has no ePaper display, but the existing companion BLE env inherits DISPLAY_CLASS=GxEPDDisplay from the base env. This causes display.begin() to run on non-existent hardware, corrupting BLE initialization and generating a random PIN that cannot be displayed, making BLE connection impossible. Add LilyGo_T-Echo-Lite_non_shell_companion_radio_ble env that: - excludes DISPLAY_CLASS and ePaper-related build flags - removes GxEPDDisplay.cpp and ui-new from build sources - uses static BLE_PIN_CODE=123456 (avoids random PIN generation) Also fix boards/t-echo.json: - add nrfutil to upload protocols list - add use_1200bps_touch=true and wait_for_upload_port=true to enable reliable flashing via PlatformIO upload button --- boards/t-echo.json | 7 ++-- variants/lilygo_techo_lite/platformio.ini | 44 +++++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/boards/t-echo.json b/boards/t-echo.json index c974ca65..e0df6eac 100644 --- a/boards/t-echo.json +++ b/boards/t-echo.json @@ -53,11 +53,14 @@ "protocols": [ "jlink", "nrfjprog", + "nrfutil", "stlink", "cmsis-dap", "blackmagic" - ] + ], + "use_1200bps_touch": true, + "wait_for_upload_port": true }, "url": "https://os.mbed.com/platforms/Nordic-nRF52840-DK/", "vendor": "Nordic" -} \ No newline at end of file +} diff --git a/variants/lilygo_techo_lite/platformio.ini b/variants/lilygo_techo_lite/platformio.ini index 0ba6a197..88616858 100644 --- a/variants/lilygo_techo_lite/platformio.ini +++ b/variants/lilygo_techo_lite/platformio.ini @@ -96,3 +96,47 @@ build_src_filter = ${LilyGo_T-Echo-Lite.build_src_filter} lib_deps = ${LilyGo_T-Echo-Lite.lib_deps} densaugeo/base64 @ ~1.4.0 + +[env:LilyGo_T-Echo-Lite_non_shell_companion_radio_ble] +extends = LilyGo_T-Echo-Lite +upload_protocol = nrfutil +board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld +board_upload.maximum_size = 712704 +build_flags = + ${nrf52_base.build_flags} + -I variants/lilygo_techo_lite + -I src/helpers/nrf52 + -I lib/nrf52/s140_nrf52_6.1.1_API/include + -I lib/nrf52/s140_nrf52_6.1.1_API/include/nrf52 + -I src/helpers/ui + -I examples/companion_radio/ui-new + -D LILYGO_TECHO + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + -D SX126X_POWER_EN=30 + -D SX126X_CURRENT_LIMIT=140 + -D SX126X_RX_BOOSTED_GAIN=1 + -D P_LORA_TX_LED=LED_GREEN + -D DISABLE_DIAGNOSTIC_OUTPUT + -D ENV_INCLUDE_GPS=1 + -D GPS_BAUD_RATE=9600 + -D PIN_GPS_EN=GPS_EN + -D AUTO_OFF_MILLIS=0 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D OFFLINE_QUEUE_SIZE=256 + -D UI_RECENT_LIST_SIZE=9 + -D AUTO_SHUTDOWN_MILLIVOLTS=3300 +build_src_filter = ${nrf52_base.build_src_filter} + + + + + + + + + +<../variants/lilygo_techo_lite> + + + +<../examples/companion_radio/*.cpp> +lib_deps = + ${LilyGo_T-Echo-Lite.lib_deps} + densaugeo/base64 @ ~1.4.0 From 1cb6c7a9003c07814ccb46e012ebeb8ed987de7f Mon Sep 17 00:00:00 2001 From: Matthias Wientapper Date: Wed, 7 Jan 2026 21:24:25 +0100 Subject: [PATCH 13/17] Limit flood advert packet forwarding, implements #1223 --- examples/simple_repeater/MyMesh.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 666f79fc..9bd10693 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -447,6 +447,14 @@ bool MyMesh::allowPacketForward(const mesh::Packet *packet) { return false; } } + // Limit flood advert paket forwarding using a probabilistic reduction defined by P(h) = 0.308^(hops-1) + // https://github.com/meshcore-dev/MeshCore/issues/1223 + double_t roll_dice = (double)rand() / RAND_MAX; + double_t forw_prob = pow(0.308, packet->path_len - 1); + if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->isRouteFlood() && roll_dice > forw_prob) + return false; + + // all other packets return true; } From 3955a96df701462e241f2cff586c7a003167613a Mon Sep 17 00:00:00 2001 From: Matthias Wientapper Date: Fri, 9 Jan 2026 20:47:09 +0100 Subject: [PATCH 14/17] Limit flood advert packet forwarding for roomservers as well --- examples/simple_room_server/MyMesh.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index 145fb0fd..d97cf4ab 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -282,7 +282,16 @@ uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) { bool MyMesh::allowPacketForward(const mesh::Packet *packet) { if (_prefs.disable_fwd) return false; - if (packet->isRouteFlood() && packet->getPathHashCount() >= _prefs.flood_max) return false; + if (packet->isRouteFlood() && packet->path_len >= _prefs.flood_max) return false; + + // Limit flood advert paket forwarding using a probabilistic reduction defined by P(h) = 0.308^(hops-1) + // https://github.com/meshcore-dev/MeshCore/issues/1223 + double_t roll_dice = (double)rand() / RAND_MAX; + double_t forw_prob = pow(0.308, packet->path_len - 1); + if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->isRouteFlood() && roll_dice > forw_prob) + return false; + + // all other packets return true; } From fe7c388fdbc2854affacf313c7bd876dba6334b1 Mon Sep 17 00:00:00 2001 From: Matthias Wientapper Date: Tue, 13 Jan 2026 23:06:27 +0100 Subject: [PATCH 15/17] Add cli config flood.advert.base 0 = forwarding flood adverts off 1 = forwarding flood adverts on (unrestricted) 0.308 (default) = prob. forwarding according to #1338 --- examples/simple_repeater/MyMesh.cpp | 3 ++- examples/simple_room_server/MyMesh.cpp | 3 ++- src/helpers/CommonCLI.cpp | 20 ++++++++++++++++---- src/helpers/CommonCLI.h | 1 + 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 9bd10693..bd8b687f 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -450,7 +450,7 @@ bool MyMesh::allowPacketForward(const mesh::Packet *packet) { // Limit flood advert paket forwarding using a probabilistic reduction defined by P(h) = 0.308^(hops-1) // https://github.com/meshcore-dev/MeshCore/issues/1223 double_t roll_dice = (double)rand() / RAND_MAX; - double_t forw_prob = pow(0.308, packet->path_len - 1); + double_t forw_prob = pow(_prefs.flood_advert_base, packet->path_len - 1); if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->isRouteFlood() && roll_dice > forw_prob) return false; @@ -893,6 +893,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.tx_power_dbm = LORA_TX_POWER; _prefs.advert_interval = 1; // default to 2 minutes for NEW installs _prefs.flood_advert_interval = 12; // 12 hours + _prefs.flood_advert_base = 0.308f; _prefs.flood_max = 64; _prefs.interference_threshold = 0; // disabled diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index d97cf4ab..2b11a79d 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -287,7 +287,7 @@ bool MyMesh::allowPacketForward(const mesh::Packet *packet) { // Limit flood advert paket forwarding using a probabilistic reduction defined by P(h) = 0.308^(hops-1) // https://github.com/meshcore-dev/MeshCore/issues/1223 double_t roll_dice = (double)rand() / RAND_MAX; - double_t forw_prob = pow(0.308, packet->path_len - 1); + double_t forw_prob = pow(_prefs.flood_advert_base, packet->path_len - 1); if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->isRouteFlood() && roll_dice > forw_prob) return false; @@ -651,6 +651,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.disable_fwd = 1; _prefs.advert_interval = 1; // default to 2 minutes for NEW installs _prefs.flood_advert_interval = 12; // 12 hours + _prefs.flood_advert_base = 0.308f; _prefs.flood_max = 64; _prefs.interference_threshold = 0; // disabled #ifdef ROOM_PASSWORD diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index b71afc72..3bbef5fd 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -88,8 +88,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162 file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166 file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170 - file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290 - // next: 291 + file.read((uint8_t *)&_prefs->flood_advert_base, sizeof(_prefs->flood_advert_base)); // 290 + file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 294 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -119,6 +119,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { // sanitise settings _prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean + _prefs->flood_advert_base = constrain(_prefs->flood_advert_base, 0, 1); file.close(); } @@ -179,8 +180,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162 file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166 file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170 - file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290 - // next: 291 + file.write((uint8_t *)&_prefs->flood_advert_base, sizeof(_prefs->flood_advert_base)); // 290 + file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 294 file.close(); } @@ -607,6 +608,15 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } else { strcpy(reply, "Error, max 64"); } + } else if (memcmp(config, "flood.advert.base ", 18) == 0) { + float f = atof(&config[18]); + if (f >= 0.0f && f <= 1.0f) { + _prefs->flood_advert_base = f; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error: base must be between 0 and 1"); + } } else if (memcmp(config, "direct.txdelay ", 15) == 0) { float f = atof(&config[15]); if (f >= 0) { @@ -784,6 +794,8 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep sprintf(reply, "> %s", StrHelper::ftoa(_prefs->tx_delay_factor)); } else if (memcmp(config, "flood.max", 9) == 0) { sprintf(reply, "> %d", (uint32_t)_prefs->flood_max); + } else if (memcmp(config, "flood.advert.base", 17) == 0) { + sprintf(reply, "> %s", StrHelper::ftoa(_prefs->flood_advert_base)); } else if (memcmp(config, "direct.txdelay", 14) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor)); } else if (memcmp(config, "owner.info", 10) == 0) { diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index ffdc7c65..1defcd92 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -42,6 +42,7 @@ struct NodePrefs { // persisted to file uint8_t flood_max; uint8_t interference_threshold; uint8_t agc_reset_interval; // secs / 4 + float flood_advert_base; // Bridge settings uint8_t bridge_enabled; // boolean uint16_t bridge_delay; // milliseconds (default 500 ms) From d81626ece5e78c5727ffc72e9ed44f464c553ae2 Mon Sep 17 00:00:00 2001 From: mattzzw Date: Thu, 5 Mar 2026 08:22:22 +0100 Subject: [PATCH 16/17] Update examples/simple_repeater/MyMesh.cpp Fix pow() and rand() are calculated for every forwarded packet, but we only need it for flood advert. Also fixes 'paket' typo. Co-authored-by: Wessel --- examples/simple_repeater/MyMesh.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index bd8b687f..069fad49 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -449,12 +449,13 @@ bool MyMesh::allowPacketForward(const mesh::Packet *packet) { } // Limit flood advert paket forwarding using a probabilistic reduction defined by P(h) = 0.308^(hops-1) // https://github.com/meshcore-dev/MeshCore/issues/1223 - double_t roll_dice = (double)rand() / RAND_MAX; - double_t forw_prob = pow(_prefs.flood_advert_base, packet->path_len - 1); - if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->isRouteFlood() && roll_dice > forw_prob) - return false; + if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->isRouteFlood()) { + double roll_dice = (double)rand() / RAND_MAX; + double forw_prob = pow(_prefs.flood_advert_base, packet->path_len - 1); + if (roll_dice > forw_prob) + return false; + } - // all other packets return true; } From 2d2c0475ac8950687ae7b69e9b39b2933ba104c7 Mon Sep 17 00:00:00 2001 From: mattzzw Date: Thu, 5 Mar 2026 08:22:48 +0100 Subject: [PATCH 17/17] Update examples/simple_room_server/MyMesh.cpp Fix pow() and rand() are calculated for every forwarded packet, but we only need it for flood advert. Also fixes 'paket' typo. Co-authored-by: Wessel --- examples/simple_room_server/MyMesh.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index 2b11a79d..1822d00c 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -284,14 +284,15 @@ bool MyMesh::allowPacketForward(const mesh::Packet *packet) { if (_prefs.disable_fwd) return false; if (packet->isRouteFlood() && packet->path_len >= _prefs.flood_max) return false; - // Limit flood advert paket forwarding using a probabilistic reduction defined by P(h) = 0.308^(hops-1) + // Limit flood advert packet forwarding using a probabilistic reduction defined by P(h) = base^(hops-1) // https://github.com/meshcore-dev/MeshCore/issues/1223 - double_t roll_dice = (double)rand() / RAND_MAX; - double_t forw_prob = pow(_prefs.flood_advert_base, packet->path_len - 1); - if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->isRouteFlood() && roll_dice > forw_prob) - return false; + if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->isRouteFlood()) { + double roll_dice = (double)rand() / RAND_MAX; + double forw_prob = pow(_prefs.flood_advert_base, packet->path_len - 1); + if (roll_dice > forw_prob) + return false; + } - // all other packets return true; }