From d93d68d31e527580a2096436ff9e9dfd911b3971 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 20 Dec 2025 14:09:05 -0600 Subject: [PATCH 01/28] Fix -ota.zip in manifest and build output --- bin/build-nrf52.sh | 3 ++- bin/platformio-custom.py | 7 ++++++- src/modules/PositionModule.cpp | 2 ++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/bin/build-nrf52.sh b/bin/build-nrf52.sh index e3a421865..edcc2add2 100755 --- a/bin/build-nrf52.sh +++ b/bin/build-nrf52.sh @@ -21,13 +21,14 @@ rm -f $BUILDDIR/firmware* export APP_VERSION=$VERSION basename=firmware-$1-$VERSION +ota_basename=${basename}-ota pio run --environment $1 -t mtjson # -v cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf echo "Copying NRF52 dfu (OTA) file" -cp $BUILDDIR/$basename.zip $OUTDIR/$basename.zip +cp $BUILDDIR/$basename.zip $OUTDIR/$ota_basename.zip echo "Copying NRF52 UF2 file" cp $BUILDDIR/$basename.uf2 $OUTDIR/$basename.uf2 diff --git a/bin/platformio-custom.py b/bin/platformio-custom.py index 3fdbffb70..b6560f35b 100644 --- a/bin/platformio-custom.py +++ b/bin/platformio-custom.py @@ -17,6 +17,8 @@ lfsbin = f"{progname.replace('firmware-', 'littlefs-')}.bin" def manifest_gather(source, target, env): out = [] + board_platform = env.BoardConfig().get("platform") + needs_ota_suffix = board_platform == "nordicnrf52" check_paths = [ progname, f"{progname}.elf", @@ -32,8 +34,11 @@ def manifest_gather(source, target, env): for p in check_paths: f = env.File(env.subst(f"$BUILD_DIR/{p}")) if f.exists(): + manifest_name = p + if needs_ota_suffix and p == f"{progname}.zip": + manifest_name = f"{progname}-ota.zip" d = { - "name": p, + "name": manifest_name, "md5": f.get_content_hash(), # Returns MD5 hash "bytes": f.get_size() # Returns file size in bytes } diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index 0fa09df74..f7116e701 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -429,7 +429,9 @@ int32_t PositionModule::runOnce() if (lastGpsSend == 0 || msSinceLastSend >= intervalMs) { if (waitingForFreshPosition) { +#ifdef GPS_DEBUG LOG_DEBUG("Skip initial position send; no fresh position since boot"); +#endif } else if (nodeDB->hasValidPosition(node)) { lastGpsSend = now; From 83c6161ac60a71bc1044fe1ee7ba24a2de8f0281 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 20 Dec 2025 14:10:02 -0600 Subject: [PATCH 02/28] Revert "Automated version bumps (#9025)" This reverts commit 1021d967dadcc24135aeab88f81ba30dc1c023cd. --- bin/org.meshtastic.meshtasticd.metainfo.xml | 3 --- debian/changelog | 6 ------ version.properties | 2 +- 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/bin/org.meshtastic.meshtasticd.metainfo.xml b/bin/org.meshtastic.meshtasticd.metainfo.xml index 5779167ab..140ac3e2a 100644 --- a/bin/org.meshtastic.meshtasticd.metainfo.xml +++ b/bin/org.meshtastic.meshtasticd.metainfo.xml @@ -87,9 +87,6 @@ - - https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.18 - https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.17 diff --git a/debian/changelog b/debian/changelog index ccaffa3cf..b9212c1be 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,9 +1,3 @@ -meshtasticd (2.7.18.0) unstable; urgency=medium - - * Version 2.7.18 - - -- GitHub Actions Sat, 20 Dec 2025 15:47:25 +0000 - meshtasticd (2.7.17.0) unstable; urgency=medium * Version 2.7.17 diff --git a/version.properties b/version.properties index 0a028eff0..8e40687e9 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 7 -build = 18 +build = 17 From d609d056986afe9c1b355b7ef5d0b1a08386abf7 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Tue, 23 Dec 2025 07:48:55 -0600 Subject: [PATCH 03/28] In statusLEDModule, also detect isCharging (#9050) --- src/modules/StatusLEDModule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/StatusLEDModule.cpp b/src/modules/StatusLEDModule.cpp index 04cd7326f..8738c16ca 100644 --- a/src/modules/StatusLEDModule.cpp +++ b/src/modules/StatusLEDModule.cpp @@ -20,7 +20,7 @@ int StatusLEDModule::handleStatusUpdate(const meshtastic::Status *arg) switch (arg->getStatusType()) { case STATUS_TYPE_POWER: { meshtastic::PowerStatus *powerStatus = (meshtastic::PowerStatus *)arg; - if (powerStatus->getHasUSB()) { + if (powerStatus->getHasUSB() || powerStatus->getIsCharging()) { power_state = charging; if (powerStatus->getBatteryChargePercent() >= 100) { power_state = charged; From a4f6f4515a0f5b50793daa2ca7e4d5c3bdcb151b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 23 Dec 2025 18:55:37 -0600 Subject: [PATCH 04/28] Update meshtastic-esp8266-oled-ssd1306 digest to b34c681 (#9062) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 9cef4f375..8e07ef33c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -64,7 +64,7 @@ monitor_speed = 115200 monitor_filters = direct lib_deps = # renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master - https://github.com/meshtastic/esp8266-oled-ssd1306/archive/2887bf4a19f64d92c984dcc8fd5ca7429e425e4a.zip + https://github.com/meshtastic/esp8266-oled-ssd1306/archive/b34c6817c25d6faabb3a8a162b5d14fb75395433.zip # renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip # renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master From 3a7093a973c1b16d2d978576f1f880ed4c8d7386 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 23 Dec 2025 18:55:54 -0600 Subject: [PATCH 05/28] Upgrade trunk (#9047) 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 c20066f7f..5075bb749 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -9,9 +9,9 @@ plugins: lint: enabled: - checkov@3.2.495 - - renovate@42.64.1 + - renovate@42.66.0 - prettier@3.7.4 - - trufflehog@3.92.3 + - trufflehog@3.92.4 - yamllint@1.37.1 - bandit@1.9.2 - trivy@0.68.2 From 33f18659c86a9dd7faf9aacfc2db232e90c56b24 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 24 Dec 2025 05:20:22 -0600 Subject: [PATCH 06/28] Upgrade trunk (#9067) Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com> --- .trunk/trunk.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 5075bb749..093c4c2a2 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -9,7 +9,7 @@ plugins: lint: enabled: - checkov@3.2.495 - - renovate@42.66.0 + - renovate@42.66.2 - prettier@3.7.4 - trufflehog@3.92.4 - yamllint@1.37.1 From 54a928f47f1449eb5eb0016cc9cb4650408fd414 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Wed, 24 Dec 2025 07:48:14 -0600 Subject: [PATCH 07/28] M6 shutdown and LEDs work (#9065) Co-authored-by: Ben Meadors --- src/mesh/NodeDB.cpp | 3 ++- .../nrf52840/ELECROW-ThinkNode-M6/variant.cpp | 27 +++++++++++++++++++ .../nrf52840/ELECROW-ThinkNode-M6/variant.h | 4 ++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 2d4bad854..9052ee17c 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -805,7 +805,8 @@ void NodeDB::installDefaultModuleConfig() moduleConfig.external_notification.output_ms = 500; moduleConfig.external_notification.nag_timeout = 2; #endif -#if defined(RAK4630) || defined(RAK11310) || defined(RAK3312) || defined(MUZI_BASE) || defined(ELECROW_ThinkNode_M3) +#if defined(RAK4630) || defined(RAK11310) || defined(RAK3312) || defined(MUZI_BASE) || defined(ELECROW_ThinkNode_M3) || \ + defined(ELECROW_ThinkNode_M6) // Default to PIN_LED2 for external notification output (LED color depends on device variant) moduleConfig.external_notification.enabled = true; moduleConfig.external_notification.output = PIN_LED2; diff --git a/variants/nrf52840/ELECROW-ThinkNode-M6/variant.cpp b/variants/nrf52840/ELECROW-ThinkNode-M6/variant.cpp index 09872d409..9c7b521ef 100644 --- a/variants/nrf52840/ELECROW-ThinkNode-M6/variant.cpp +++ b/variants/nrf52840/ELECROW-ThinkNode-M6/variant.cpp @@ -41,3 +41,30 @@ void initVariant() pinMode(VDD_FLASH_EN, OUTPUT); digitalWrite(VDD_FLASH_EN, HIGH); } + +// called from main-nrf52.cpp during the cpuDeepSleep() function +void variant_shutdown() +{ + // This sets the pin to OUTPUT and LOW for the pins *not* in the if block. + for (int pin = 0; pin < 48; pin++) { + if (pin == PIN_GPS_EN || pin == ADC_CTRL || pin == PIN_BUTTON1 || pin == PIN_SPI_MISO || pin == PIN_SPI_MOSI || + pin == PIN_SPI_SCK) { + continue; + } + pinMode(pin, OUTPUT); + digitalWrite(pin, LOW); + if (pin >= 32) { + NRF_P1->DIRCLR = (1 << (pin - 32)); + } else { + NRF_GPIO->DIRCLR = (1 << pin); + } + } + + digitalWrite(PIN_GPS_EN, LOW); + digitalWrite(ADC_CTRL, LOW); + // digitalWrite(RTC_POWER, LOW); + + nrf_gpio_cfg_input(PIN_BUTTON1, NRF_GPIO_PIN_PULLUP); // Configure the pin to be woken up as an input + nrf_gpio_pin_sense_t sense1 = NRF_GPIO_PIN_SENSE_LOW; + nrf_gpio_cfg_sense_set(PIN_BUTTON1, sense1); +} diff --git a/variants/nrf52840/ELECROW-ThinkNode-M6/variant.h b/variants/nrf52840/ELECROW-ThinkNode-M6/variant.h index d30b88d66..28b659282 100644 --- a/variants/nrf52840/ELECROW-ThinkNode-M6/variant.h +++ b/variants/nrf52840/ELECROW-ThinkNode-M6/variant.h @@ -44,8 +44,10 @@ extern "C" { #define LED_BLUE -1 #define LED_CHARGE (12) #define LED_PAIRING (7) +#define PIN_LED2 LED_PAIRING -#define LED_STATE_ON 1 +#define LED_STATE_ON HIGH +#define LED_STATE_OFF LOW // USB power detection #define EXT_PWR_DETECT (13) From b2c82bdc415684825733261e3dc73346e37def15 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 25 Dec 2025 06:34:38 -0600 Subject: [PATCH 08/28] Upgrade trunk (#9072) Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com> --- .trunk/trunk.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 093c4c2a2..211f3912b 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -9,7 +9,7 @@ plugins: lint: enabled: - checkov@3.2.495 - - renovate@42.66.2 + - renovate@42.66.4 - prettier@3.7.4 - trufflehog@3.92.4 - yamllint@1.37.1 From 9dc7ef612e3dddfea3a85e8fe99e1abb431ce233 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 26 Dec 2025 14:33:17 -0600 Subject: [PATCH 09/28] In autoconf, don't probe Wire unless i2c device is set (#9081) Found another bit of code that crashes my desktop, by probing the wrong i2c bus. --- src/platform/portduino/PortduinoGlue.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index 1b601f9b4..ea9e2de67 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -279,7 +279,7 @@ void portduinoSetup() // RAK6421-13300-S1:aabbcc123456:5ba85807d92138b7519cfb60460573af:3061e8d8 // :mac address :<16 random unique bytes in hexidecimal> : crc32 // crc32 is calculated on the eeprom string up to but not including the final colon - if (strlen(autoconf_product) < 6) { + if (strlen(autoconf_product) < 6 && portduino_config.i2cdev != "") { try { char *mac_start = nullptr; char *devID_start = nullptr; @@ -867,4 +867,4 @@ void readGPIOFromYaml(YAML::Node sourceNode, pinMapping &destPin, int pinDefault destPin.line = destPin.pin; destPin.gpiochip = portduino_config.lora_default_gpiochip; } -} \ No newline at end of file +} From 33e1f58f6ea530ff3d3aafaa2a76e9a43308531a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 26 Dec 2025 17:45:57 -0600 Subject: [PATCH 10/28] Upgrade trunk (#9076) Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com> --- .trunk/trunk.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 211f3912b..3656ae32c 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -9,7 +9,7 @@ plugins: lint: enabled: - checkov@3.2.495 - - renovate@42.66.4 + - renovate@42.66.8 - prettier@3.7.4 - trufflehog@3.92.4 - yamllint@1.37.1 From 52fd362720a6edb13fc82d99603cb846d27a4f1c Mon Sep 17 00:00:00 2001 From: Tom <116762865+NomDeTom@users.noreply.github.com> Date: Wed, 24 Dec 2025 18:04:28 +0000 Subject: [PATCH 11/28] Fix gps pin defs for various NRF variants. (#9034) * fix on nrf52_promicro * try fix for GPS issue * fix GPS pin assignment in variant.h * cleared up some comments and confirmed pinouts from schematics --------- Co-authored-by: macvenez --- .../nrf52840/diy/nrf52_promicro_diy_tcxo/variant.h | 8 ++++---- variants/nrf52840/nano-g2-ultra/variant.h | 8 ++++---- variants/nrf52840/seeed_solar_node/variant.h | 8 ++++---- variants/nrf52840/seeed_wio_tracker_L1/variant.h | 10 ++++------ variants/nrf52840/seeed_wio_tracker_L1_eink/variant.h | 10 ++++------ variants/nrf52840/seeed_xiao_nrf52840_kit/variant.h | 8 ++++---- 6 files changed, 24 insertions(+), 28 deletions(-) diff --git a/variants/nrf52840/diy/nrf52_promicro_diy_tcxo/variant.h b/variants/nrf52840/diy/nrf52_promicro_diy_tcxo/variant.h index 7eeb26e65..63af1fe79 100644 --- a/variants/nrf52840/diy/nrf52_promicro_diy_tcxo/variant.h +++ b/variants/nrf52840/diy/nrf52_promicro_diy_tcxo/variant.h @@ -90,16 +90,16 @@ NRF52 PRO MICRO PIN ASSIGNMENT #define BUTTON_PIN (32 + 0) // P1.00 // GPS -#define PIN_GPS_TX (0 + 20) // P0.20 - This is data from the MCU -#define PIN_GPS_RX (0 + 22) // P0.22 - This is data from the GNSS +#define GPS_TX_PIN (0 + 20) // P0.20 - This is data from the MCU +#define GPS_RX_PIN (0 + 22) // P0.22 - This is data from the GNSS #define PIN_GPS_EN (0 + 24) // P0.24 #define GPS_UBLOX // define GPS_DEBUG // UART interfaces -#define PIN_SERIAL1_TX PIN_GPS_TX -#define PIN_SERIAL1_RX PIN_GPS_RX +#define PIN_SERIAL1_TX GPS_TX_PIN +#define PIN_SERIAL1_RX GPS_RX_PIN #define PIN_SERIAL2_RX (0 + 6) // P0.06 #define PIN_SERIAL2_TX (0 + 8) // P0.08 diff --git a/variants/nrf52840/nano-g2-ultra/variant.h b/variants/nrf52840/nano-g2-ultra/variant.h index fd837f66e..2039a72f4 100644 --- a/variants/nrf52840/nano-g2-ultra/variant.h +++ b/variants/nrf52840/nano-g2-ultra/variant.h @@ -132,13 +132,13 @@ External serial flash W25Q16JV_IQ #define GPS_L76K #define PIN_GPS_STANDBY (0 + 13) // An output to wake GPS, low means allow sleep, high means force wake STANDBY -#define PIN_GPS_TX (0 + 10) // This is for bits going TOWARDS the CPU -#define PIN_GPS_RX (0 + 9) // This is for bits going TOWARDS the GPS +#define GPS_TX_PIN (0 + 10) // This is for bits going FROM the CPU +#define GPS_RX_PIN (0 + 9) // This is for bits going FROM the GPS // #define GPS_THREAD_INTERVAL 50 -#define PIN_SERIAL1_TX PIN_GPS_TX -#define PIN_SERIAL1_RX PIN_GPS_RX +#define PIN_SERIAL1_TX GPS_TX_PIN +#define PIN_SERIAL1_RX GPS_RX_PIN // PCF8563 RTC Module #define PCF8563_RTC 0x51 diff --git a/variants/nrf52840/seeed_solar_node/variant.h b/variants/nrf52840/seeed_solar_node/variant.h index 7b7738547..b2a1e6dff 100644 --- a/variants/nrf52840/seeed_solar_node/variant.h +++ b/variants/nrf52840/seeed_solar_node/variant.h @@ -116,13 +116,13 @@ static const uint8_t SCL = PIN_WIRE_SCL; // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #define GPS_L76K #ifdef GPS_L76K -#define PIN_GPS_TX D6 // 44 -#define PIN_GPS_RX D7 // 43 +#define GPS_TX_PIN D6 // 44 +#define GPS_RX_PIN D7 // 43 #define HAS_GPS 1 #define GPS_BAUDRATE 9600 #define GPS_THREAD_INTERVAL 50 -#define PIN_SERIAL1_TX PIN_GPS_TX -#define PIN_SERIAL1_RX PIN_GPS_RX +#define PIN_SERIAL1_TX GPS_TX_PIN +#define PIN_SERIAL1_RX GPS_RX_PIN #define PIN_GPS_STANDBY D0 #define GPS_EN D18 // P1.05 #endif diff --git a/variants/nrf52840/seeed_wio_tracker_L1/variant.h b/variants/nrf52840/seeed_wio_tracker_L1/variant.h index c5647caa8..b62b65161 100644 --- a/variants/nrf52840/seeed_wio_tracker_L1/variant.h +++ b/variants/nrf52840/seeed_wio_tracker_L1/variant.h @@ -119,16 +119,14 @@ static const uint8_t SCL = PIN_WIRE_SCL; // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #define GPS_L76K #ifdef GPS_L76K -#define PIN_GPS_RX D6 // P0.26 -#define PIN_GPS_TX D7 +#define GPS_TX_PIN D6 // P0.26 - This is data from the MCU +#define GPS_RX_PIN D7 // P0.27 - This is data from the GNSS #define HAS_GPS 1 #define GPS_BAUDRATE 9600 #define GPS_THREAD_INTERVAL 50 -#define PIN_SERIAL1_RX PIN_GPS_TX -#define PIN_SERIAL1_TX PIN_GPS_RX +#define PIN_SERIAL1_RX GPS_RX_PIN +#define PIN_SERIAL1_TX GPS_TX_PIN -#define GPS_RX_PIN PIN_GPS_TX -#define GPS_TX_PIN PIN_GPS_RX #define PIN_GPS_STANDBY D0 // #define GPS_DEBUG diff --git a/variants/nrf52840/seeed_wio_tracker_L1_eink/variant.h b/variants/nrf52840/seeed_wio_tracker_L1_eink/variant.h index 09fefc7f2..ae20f3c36 100644 --- a/variants/nrf52840/seeed_wio_tracker_L1_eink/variant.h +++ b/variants/nrf52840/seeed_wio_tracker_L1_eink/variant.h @@ -129,16 +129,14 @@ static const uint8_t SCL = PIN_WIRE_SCL; // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #define GPS_L76K #ifdef GPS_L76K -#define PIN_GPS_RX D6 // P0.26 -#define PIN_GPS_TX D7 +#define GPS_TX_PIN D6 // P0.26 - This is data from the MCU +#define GPS_RX_PIN D7 // P0.27 - This is data from the GNSS #define HAS_GPS 1 #define GPS_BAUDRATE 9600 #define GPS_THREAD_INTERVAL 50 -#define PIN_SERIAL1_RX PIN_GPS_TX -#define PIN_SERIAL1_TX PIN_GPS_RX +#define PIN_SERIAL1_RX GPS_RX_PIN +#define PIN_SERIAL1_TX GPS_TX_PIN -#define GPS_RX_PIN PIN_GPS_TX -#define GPS_TX_PIN PIN_GPS_RX #define PIN_GPS_STANDBY D0 // #define GPS_DEBUG diff --git a/variants/nrf52840/seeed_xiao_nrf52840_kit/variant.h b/variants/nrf52840/seeed_xiao_nrf52840_kit/variant.h index fb112a302..0844595da 100644 --- a/variants/nrf52840/seeed_xiao_nrf52840_kit/variant.h +++ b/variants/nrf52840/seeed_xiao_nrf52840_kit/variant.h @@ -147,12 +147,12 @@ static const uint8_t SCK = PIN_SPI_SCK; */ // GPS L76K #ifdef GPS_L76K -#define PIN_GPS_TX D6 -#define PIN_GPS_RX D7 +#define GPS_TX_PIN D6 // This is data from the MCU +#define GPS_RX_PIN D7 // This is data from the GNSS module #define HAS_GPS 1 #define GPS_THREAD_INTERVAL 50 -#define PIN_SERIAL1_TX PIN_GPS_TX -#define PIN_SERIAL1_RX PIN_GPS_RX +#define PIN_SERIAL1_TX GPS_TX_PIN +#define PIN_SERIAL1_RX GPS_RX_PIN #define PIN_GPS_STANDBY D0 #else #define PIN_SERIAL1_RX (-1) From 5510dae8d3c728e811ee9bda899375c247de8998 Mon Sep 17 00:00:00 2001 From: Jason P Date: Fri, 26 Dec 2025 07:34:25 -0600 Subject: [PATCH 12/28] Implement HAS_PHYSICAL_KEYBOARD for devices with physical keyboards (#9071) - Implement HAS_PHYSICAL_KEYBOARD for devices with physical keyboards - Add HAS_PHYSICAL_KEYBOARD to variant.h for: - TDeck - TLora Pager - TDeck Pro --- src/input/RotaryEncoderInterruptImpl1.cpp | 2 ++ src/input/TrackballInterruptBase.cpp | 2 ++ src/input/UpDownInterruptImpl1.cpp | 2 ++ src/main.cpp | 2 ++ variants/esp32s3/t-deck-pro/variant.h | 2 ++ variants/esp32s3/t-deck/variant.h | 1 + variants/esp32s3/tlora-pager/variant.h | 1 + 7 files changed, 12 insertions(+) diff --git a/src/input/RotaryEncoderInterruptImpl1.cpp b/src/input/RotaryEncoderInterruptImpl1.cpp index 12cbc36fb..1da2ea008 100644 --- a/src/input/RotaryEncoderInterruptImpl1.cpp +++ b/src/input/RotaryEncoderInterruptImpl1.cpp @@ -27,7 +27,9 @@ bool RotaryEncoderInterruptImpl1::init() RotaryEncoderInterruptImpl1::handleIntA, RotaryEncoderInterruptImpl1::handleIntB, RotaryEncoderInterruptImpl1::handleIntPressed); inputBroker->registerSource(this); +#ifndef HAS_PHYSICAL_KEYBOARD osk_found = true; +#endif return true; } diff --git a/src/input/TrackballInterruptBase.cpp b/src/input/TrackballInterruptBase.cpp index d2025c192..bbd07e199 100644 --- a/src/input/TrackballInterruptBase.cpp +++ b/src/input/TrackballInterruptBase.cpp @@ -45,7 +45,9 @@ void TrackballInterruptBase::init(uint8_t pinDown, uint8_t pinUp, uint8_t pinLef LOG_DEBUG("Trackball GPIO initialized - UP:%d DOWN:%d LEFT:%d RIGHT:%d PRESS:%d", this->_pinUp, this->_pinDown, this->_pinLeft, this->_pinRight, pinPress); +#ifndef HAS_PHYSICAL_KEYBOARD osk_found = true; +#endif this->setInterval(100); } diff --git a/src/input/UpDownInterruptImpl1.cpp b/src/input/UpDownInterruptImpl1.cpp index 9b0b1f39e..906dcd2a8 100644 --- a/src/input/UpDownInterruptImpl1.cpp +++ b/src/input/UpDownInterruptImpl1.cpp @@ -29,7 +29,9 @@ bool UpDownInterruptImpl1::init() eventDownLong, UpDownInterruptImpl1::handleIntDown, UpDownInterruptImpl1::handleIntUp, UpDownInterruptImpl1::handleIntPressed); inputBroker->registerSource(this); +#ifndef HAS_PHYSICAL_KEYBOARD osk_found = true; +#endif return true; } diff --git a/src/main.cpp b/src/main.cpp index a86ef556a..245f06e05 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1458,8 +1458,10 @@ void setup() #endif #if defined(HAS_TRACKBALL) || (defined(INPUTDRIVER_ENCODER_TYPE) && INPUTDRIVER_ENCODER_TYPE == 2) +#ifndef HAS_PHYSICAL_KEYBOARD osk_found = true; #endif +#endif #if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WEBSERVER // Start web server thread. diff --git a/variants/esp32s3/t-deck-pro/variant.h b/variants/esp32s3/t-deck-pro/variant.h index 35cb99435..36a1310f1 100644 --- a/variants/esp32s3/t-deck-pro/variant.h +++ b/variants/esp32s3/t-deck-pro/variant.h @@ -100,3 +100,5 @@ #define MODEM_DTR 8 #define MODEM_RX 10 #define MODEM_TX 11 + +#define HAS_PHYSICAL_KEYBOARD 1 \ No newline at end of file diff --git a/variants/esp32s3/t-deck/variant.h b/variants/esp32s3/t-deck/variant.h index ece0cdeaf..8d2996131 100644 --- a/variants/esp32s3/t-deck/variant.h +++ b/variants/esp32s3/t-deck/variant.h @@ -23,6 +23,7 @@ #define SCREEN_TRANSITION_FRAMERATE 5 #define BRIGHTNESS_DEFAULT 130 // Medium Low Brightness #define USE_TFTDISPLAY 1 +#define HAS_PHYSICAL_KEYBOARD 1 #define HAS_TOUCHSCREEN 1 #define SCREEN_TOUCH_INT 16 diff --git a/variants/esp32s3/tlora-pager/variant.h b/variants/esp32s3/tlora-pager/variant.h index fe563cded..42cd7f502 100644 --- a/variants/esp32s3/tlora-pager/variant.h +++ b/variants/esp32s3/tlora-pager/variant.h @@ -21,6 +21,7 @@ #define SCREEN_TRANSITION_FRAMERATE 5 #define BRIGHTNESS_DEFAULT 130 // Medium Low Brightness #define USE_TFTDISPLAY 1 +#define HAS_PHYSICAL_KEYBOARD 1 #define I2C_SDA SDA #define I2C_SCL SCL From 757f7b68d6b4e6b0b6623c3e67ebb690d694e801 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 29 Dec 2025 13:35:31 +1100 Subject: [PATCH 13/28] Update meshtastic/device-ui digest to caff403 (#9104) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 8e07ef33c..a250f1d5a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -123,7 +123,7 @@ lib_deps = [device-ui_base] lib_deps = # renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master - https://github.com/meshtastic/device-ui/archive/862ed040c4ab44f0dfbbe492691f144886102588.zip + https://github.com/meshtastic/device-ui/archive/caff403d2bdb8c92e5f505db97c490689a242dd0.zip ; Common libs for environmental measurements in telemetry module [environmental_base] From 9673cfb0b230b4d31bc1747fae6827cc2744d41a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 29 Dec 2025 06:03:03 -0600 Subject: [PATCH 14/28] Upgrade trunk (#9106) 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 3656ae32c..2d534189c 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.495 - - renovate@42.66.8 + - checkov@3.2.496 + - renovate@42.66.11 - prettier@3.7.4 - trufflehog@3.92.4 - yamllint@1.37.1 From b9a0015149ae477ce8e8c7614295a785244c30ae Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 29 Dec 2025 06:50:12 -0600 Subject: [PATCH 15/28] chore(deps): update meshtastic/device-ui digest to d234bd9 (#9108) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index a250f1d5a..3dadee33b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -123,7 +123,7 @@ lib_deps = [device-ui_base] lib_deps = # renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master - https://github.com/meshtastic/device-ui/archive/caff403d2bdb8c92e5f505db97c490689a242dd0.zip + https://github.com/meshtastic/device-ui/archive/d234bd98c7d293d5c17fbf6dfe6797238d39805e.zip ; Common libs for environmental measurements in telemetry module [environmental_base] From ef30fd850d3e233fae68856b92c3c021de94c8bd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 29 Dec 2025 19:09:44 +0100 Subject: [PATCH 16/28] Update meshtastic/device-ui digest to 7656d49 (#9111) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 3dadee33b..ca23b55c3 100644 --- a/platformio.ini +++ b/platformio.ini @@ -123,7 +123,7 @@ lib_deps = [device-ui_base] lib_deps = # renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master - https://github.com/meshtastic/device-ui/archive/d234bd98c7d293d5c17fbf6dfe6797238d39805e.zip + https://github.com/meshtastic/device-ui/archive/7656d49ea2bdb81a43afc1e8552f1cc86b8dce3c.zip ; Common libs for environmental measurements in telemetry module [environmental_base] From ac571d5dd2b1132a93ffbbf6a89ece03f3ba2067 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 30 Dec 2025 07:10:36 -0600 Subject: [PATCH 17/28] Upgrade trunk (#9121) Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com> --- .trunk/trunk.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 2d534189c..705f0177c 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -9,7 +9,7 @@ plugins: lint: enabled: - checkov@3.2.496 - - renovate@42.66.11 + - renovate@42.66.14 - prettier@3.7.4 - trufflehog@3.92.4 - yamllint@1.37.1 From 1b83501ee2cbbbaa5298f1b4524da41785beec9d Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 30 Dec 2025 17:23:50 -0600 Subject: [PATCH 18/28] Revert "Upgrade all esp32 targets to NimBLE 2.X (#9003)" (#9125) This reverts commit 40f1f91c0d6b7ff859fbbf4d67511000548d74ee. --- src/nimble/NimbleBluetooth.cpp | 207 ++++++++++++------ src/nimble/NimbleBluetooth.h | 5 + variants/esp32/esp32.ini | 3 +- variants/esp32c3/esp32c3.ini | 5 - .../esp32c6/m5stack_unitc6l/platformio.ini | 3 +- variants/esp32s3/esp32s3.ini | 5 - 6 files changed, 146 insertions(+), 82 deletions(-) diff --git a/src/nimble/NimbleBluetooth.cpp b/src/nimble/NimbleBluetooth.cpp index b6533fc6a..3b98eca3d 100644 --- a/src/nimble/NimbleBluetooth.cpp +++ b/src/nimble/NimbleBluetooth.cpp @@ -14,11 +14,11 @@ #include #include +#ifdef NIMBLE_TWO #include "NimBLEAdvertising.h" -#ifdef CONFIG_BT_NIMBLE_EXT_ADV #include "NimBLEExtAdvertising.h" -#endif #include "PowerStatus.h" +#endif #if defined(CONFIG_NIMBLE_CPP_IDF) #include "host/ble_gap.h" @@ -26,12 +26,15 @@ #include "nimble/nimble/host/include/host/ble_gap.h" #endif +#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6) + namespace { constexpr uint16_t kPreferredBleMtu = 517; constexpr uint16_t kPreferredBleTxOctets = 251; constexpr uint16_t kPreferredBleTxTimeUs = (kPreferredBleTxOctets + 14) * 8; } // namespace +#endif // Debugging options: careful, they slow things down quite a bit! // #define DEBUG_NIMBLE_ON_READ_TIMING // uncomment to time onRead duration @@ -310,9 +313,11 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread { PhoneAPI::onNowHasData(fromRadioNum); -#ifdef DEBUG_NIMBLE_NOTIFY int currentNotifyCount = notifyCount.fetch_add(1); + uint8_t cc = bleServer->getConnectedCount(); + +#ifdef DEBUG_NIMBLE_NOTIFY // This logging slows things down when there are lots of packets going to the phone, like initial connection: LOG_DEBUG("BLE notify(%d) fromNum: %d connections: %d", currentNotifyCount, fromRadioNum, cc); #endif @@ -321,7 +326,13 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread put_le32(val, fromRadioNum); fromNumCharacteristic->setValue(val, sizeof(val)); +#ifdef NIMBLE_TWO + // NOTE: I don't have any NIMBLE_TWO devices, but this line makes me suspicious, and I suspect it needs to just be + // notify(). fromNumCharacteristic->notify(val, sizeof(val), BLE_HS_CONN_HANDLE_NONE); +#else + fromNumCharacteristic->notify(); +#endif } /// Check the current underlying physical link to see if the client is currently connected @@ -386,7 +397,12 @@ static uint8_t lastToRadio[MAX_TO_FROM_RADIO_SIZE]; class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks { - void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &) override +#ifdef NIMBLE_TWO + virtual void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo) +#else + virtual void onWrite(NimBLECharacteristic *pCharacteristic) + +#endif { // CAUTION: This callback runs in the NimBLE task!!! Don't do anything except communicate with the main task's runOnce. // Assumption: onWrite is serialized by NimBLE, so we don't need to lock here against multiple concurrent onWrite calls. @@ -433,7 +449,11 @@ class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks { - void onRead(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &) override +#ifdef NIMBLE_TWO + virtual void onRead(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo) +#else + virtual void onRead(NimBLECharacteristic *pCharacteristic) +#endif { // CAUTION: This callback runs in the NimBLE task!!! Don't do anything except communicate with the main task's runOnce. @@ -541,27 +561,32 @@ class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks class NimbleBluetoothServerCallback : public NimBLEServerCallbacks { +#ifdef NIMBLE_TWO public: - explicit NimbleBluetoothServerCallback(NimbleBluetooth *ble) : ble(ble) {} + NimbleBluetoothServerCallback(NimbleBluetooth *ble) { this->ble = ble; } private: NimbleBluetooth *ble; - uint32_t onPassKeyDisplay() override + virtual uint32_t onPassKeyDisplay() +#else + virtual uint32_t onPassKeyRequest() +#endif { uint32_t passkey = config.bluetooth.fixed_pin; if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN) { LOG_INFO("Use random passkey"); + // This is the passkey to be entered on peer - we pick a number >100,000 to ensure 6 digits passkey = random(100000, 999999); } - LOG_INFO("*** Enter passkey %06u on the peer side ***", passkey); + LOG_INFO("*** Enter passkey %d on the peer side ***", passkey); powerFSM.trigger(EVENT_BLUETOOTH_PAIR); meshtastic::BluetoothStatus newStatus(std::to_string(passkey)); bluetoothStatus->updateStatus(&newStatus); -#if HAS_SCREEN +#if HAS_SCREEN // Todo: migrate this display code back into Screen class, and observe bluetoothStatus if (screen) { screen->startAlert([passkey](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void { char btPIN[16] = "888888"; @@ -590,29 +615,39 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks }); } #endif - passkeyShowing = true; + return passkey; } - void onAuthenticationComplete(NimBLEConnInfo &connInfo) override +#ifdef NIMBLE_TWO + virtual void onAuthenticationComplete(NimBLEConnInfo &connInfo) +#else + virtual void onAuthenticationComplete(ble_gap_conn_desc *desc) +#endif { LOG_INFO("BLE authentication complete"); meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::CONNECTED); bluetoothStatus->updateStatus(&newStatus); + // Todo: migrate this display code back into Screen class, and observe bluetoothStatus if (passkeyShowing) { passkeyShowing = false; - if (screen) { + if (screen) screen->endAlert(); - } } + // Store the connection handle for future use +#ifdef NIMBLE_TWO nimbleBluetoothConnHandle = connInfo.getConnHandle(); +#else + nimbleBluetoothConnHandle = desc->conn_handle; +#endif } - void onConnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo) override +#ifdef NIMBLE_TWO + virtual void onConnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo) { LOG_INFO("BLE incoming connection %s", connInfo.getAddress().toString().c_str()); @@ -637,12 +672,21 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks LOG_INFO("BLE conn %u initial MTU %u (target %u)", connHandle, connInfo.getMTU(), kPreferredBleMtu); pServer->updateConnParams(connHandle, 6, 12, 0, 200); } +#endif - void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason) override +#ifdef NIMBLE_TWO + virtual void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason) { LOG_INFO("BLE disconnect reason: %d", reason); +#else + virtual void onDisconnect(NimBLEServer *pServer, ble_gap_conn_desc *desc) + { + LOG_INFO("BLE disconnect"); +#endif +#ifdef NIMBLE_TWO if (ble->isDeInit) return; +#endif meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED); bluetoothStatus->updateStatus(&newStatus); @@ -666,69 +710,35 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks bluetoothPhoneAPI->writeCount = 0; } + // Clear the last ToRadio packet buffer to avoid rejecting first packet from new connection memset(lastToRadio, 0, sizeof(lastToRadio)); - nimbleBluetoothConnHandle = BLE_HS_CONN_HANDLE_NONE; + nimbleBluetoothConnHandle = BLE_HS_CONN_HANDLE_NONE; // BLE_HS_CONN_HANDLE_NONE means "no connection" +#ifdef NIMBLE_TWO + // Restart Advertising ble->startAdvertising(); +#else + NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); + if (!pAdvertising->start(0)) { + if (pAdvertising->isAdvertising()) { + LOG_DEBUG("BLE advertising already running"); + } else { + LOG_ERROR("BLE failed to restart advertising"); + } + } +#endif } }; static NimbleBluetoothToRadioCallback *toRadioCallbacks; static NimbleBluetoothFromRadioCallback *fromRadioCallbacks; -void NimbleBluetooth::startAdvertising() -{ -#if defined(CONFIG_BT_NIMBLE_EXT_ADV) - NimBLEExtAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); - NimBLEExtAdvertisement legacyAdvertising; - - legacyAdvertising.setLegacyAdvertising(true); - legacyAdvertising.setScannable(true); - legacyAdvertising.setConnectable(true); - legacyAdvertising.setFlags(BLE_HS_ADV_F_DISC_GEN); - if (powerStatus->getHasBattery() == 1) { - legacyAdvertising.setCompleteServices(NimBLEUUID((uint16_t)0x180f)); - } - legacyAdvertising.setCompleteServices(NimBLEUUID(MESH_SERVICE_UUID)); - legacyAdvertising.setMinInterval(500); - legacyAdvertising.setMaxInterval(1000); - - NimBLEExtAdvertisement legacyScanResponse; - legacyScanResponse.setLegacyAdvertising(true); - legacyScanResponse.setConnectable(true); - legacyScanResponse.setName(getDeviceName()); - - if (!pAdvertising->setInstanceData(0, legacyAdvertising)) { - LOG_ERROR("BLE failed to set legacyAdvertising"); - } else if (!pAdvertising->setScanResponseData(0, legacyScanResponse)) { - LOG_ERROR("BLE failed to set legacyScanResponse"); - } else if (!pAdvertising->start(0, 0, 0)) { - LOG_ERROR("BLE failed to start legacyAdvertising"); - } -#else - NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); - pAdvertising->reset(); - pAdvertising->addServiceUUID(MESH_SERVICE_UUID); - if (powerStatus->getHasBattery() == 1) { - pAdvertising->addServiceUUID(NimBLEUUID((uint16_t)0x180f)); - } - - NimBLEAdvertisementData scan; - scan.setName(getDeviceName()); - pAdvertising->setScanResponseData(scan); - pAdvertising->enableScanResponse(true); - - if (!pAdvertising->start(0)) { - LOG_ERROR("BLE failed to start advertising"); - } -#endif - LOG_DEBUG("BLE Advertising started"); -} - void NimbleBluetooth::shutdown() { + // No measurable power saving for ESP32 during light-sleep(?) #ifndef ARCH_ESP32 + // Shutdown bluetooth for minimum power draw LOG_INFO("Disable bluetooth"); NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); pAdvertising->reset(); @@ -736,6 +746,7 @@ void NimbleBluetooth::shutdown() #endif } +// Proper shutdown for ESP32. Needs reboot to reverse. void NimbleBluetooth::deinit() { #ifdef ARCH_ESP32 @@ -749,17 +760,21 @@ void NimbleBluetooth::deinit() digitalWrite(BLE_LED, LOW); #endif #endif +#ifndef NIMBLE_TWO + NimBLEDevice::deinit(); +#endif #endif } +// Has initial setup been completed bool NimbleBluetooth::isActive() { - return bleServer != nullptr; + return bleServer; } bool NimbleBluetooth::isConnected() { - return bleServer && bleServer->getConnectedCount() > 0; + return bleServer->getConnectedCount() > 0; } int NimbleBluetooth::getRssi() @@ -803,7 +818,7 @@ void NimbleBluetooth::setup() LOG_INFO("Init the NimBLE bluetooth module"); NimBLEDevice::init(getDeviceName()); - NimBLEDevice::setPower(9); + NimBLEDevice::setPower(ESP_PWR_LVL_P9); #if NIMBLE_ENABLE_2M_PHY && (defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6)) int mtuResult = NimBLEDevice::setMTU(kPreferredBleMtu); @@ -836,7 +851,11 @@ void NimbleBluetooth::setup() NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY); } bleServer = NimBLEDevice::createServer(); - auto *serverCallbacks = new NimbleBluetoothServerCallback(this); +#ifdef NIMBLE_TWO + NimbleBluetoothServerCallback *serverCallbacks = new NimbleBluetoothServerCallback(this); +#else + NimbleBluetoothServerCallback *serverCallbacks = new NimbleBluetoothServerCallback(); +#endif bleServer->setCallbacks(serverCallbacks, true); setupService(); startAdvertising(); @@ -881,7 +900,11 @@ void NimbleBluetooth::setupService() NimBLEService *batteryService = bleServer->createService(NimBLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service BatteryCharacteristic = batteryService->createCharacteristic( // 0x2A19 is the Battery Level characteristic) (uint16_t)0x2a19, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY, 1); +#ifdef NIMBLE_TWO NimBLE2904 *batteryLevelDescriptor = BatteryCharacteristic->create2904(); +#else + NimBLE2904 *batteryLevelDescriptor = (NimBLE2904 *)BatteryCharacteristic->createDescriptor((uint16_t)0x2904); +#endif batteryLevelDescriptor->setFormat(NimBLE2904::FORMAT_UINT8); batteryLevelDescriptor->setNamespace(1); batteryLevelDescriptor->setUnit(0x27ad); @@ -889,12 +912,54 @@ void NimbleBluetooth::setupService() batteryService->start(); } +void NimbleBluetooth::startAdvertising() +{ +#ifdef NIMBLE_TWO + NimBLEExtAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); + NimBLEExtAdvertisement legacyAdvertising; + + legacyAdvertising.setLegacyAdvertising(true); + legacyAdvertising.setScannable(true); + legacyAdvertising.setConnectable(true); + legacyAdvertising.setFlags(BLE_HS_ADV_F_DISC_GEN); + if (powerStatus->getHasBattery() == 1) { + legacyAdvertising.setCompleteServices(NimBLEUUID((uint16_t)0x180f)); + } + legacyAdvertising.setCompleteServices(NimBLEUUID(MESH_SERVICE_UUID)); + legacyAdvertising.setMinInterval(500); + legacyAdvertising.setMaxInterval(1000); + + NimBLEExtAdvertisement legacyScanResponse; + legacyScanResponse.setLegacyAdvertising(true); + legacyScanResponse.setConnectable(true); + legacyScanResponse.setName(getDeviceName()); + + if (!pAdvertising->setInstanceData(0, legacyAdvertising)) { + LOG_ERROR("BLE failed to set legacyAdvertising"); + } else if (!pAdvertising->setScanResponseData(0, legacyScanResponse)) { + LOG_ERROR("BLE failed to set legacyScanResponse"); + } else if (!pAdvertising->start(0, 0, 0)) { + LOG_ERROR("BLE failed to start legacyAdvertising"); + } +#else + NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); + pAdvertising->reset(); + pAdvertising->addServiceUUID(MESH_SERVICE_UUID); + pAdvertising->addServiceUUID(NimBLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service + pAdvertising->start(0); +#endif +} + /// Given a level between 0-100, update the BLE attribute void updateBatteryLevel(uint8_t level) { if ((config.bluetooth.enabled == true) && bleServer && nimbleBluetooth->isConnected()) { BatteryCharacteristic->setValue(&level, 1); +#ifdef NIMBLE_TWO BatteryCharacteristic->notify(&level, 1, BLE_HS_CONN_HANDLE_NONE); +#else + BatteryCharacteristic->notify(); +#endif } } @@ -909,7 +974,11 @@ void NimbleBluetooth::sendLog(const uint8_t *logMessage, size_t length) if (!bleServer || !isConnected() || length > 512) { return; } +#ifdef NIMBLE_TWO logRadioCharacteristic->notify(logMessage, length, BLE_HS_CONN_HANDLE_NONE); +#else + logRadioCharacteristic->notify(logMessage, length, true); +#endif } void clearNVS() diff --git a/src/nimble/NimbleBluetooth.h b/src/nimble/NimbleBluetooth.h index 2956fe6d0..458fa4a67 100644 --- a/src/nimble/NimbleBluetooth.h +++ b/src/nimble/NimbleBluetooth.h @@ -12,11 +12,16 @@ class NimbleBluetooth : BluetoothApi bool isConnected(); int getRssi(); void sendLog(const uint8_t *logMessage, size_t length); +#if defined(NIMBLE_TWO) void startAdvertising(); +#endif bool isDeInit = false; private: void setupService(); +#if !defined(NIMBLE_TWO) + void startAdvertising(); +#endif }; void setBluetoothEnable(bool enable); diff --git a/variants/esp32/esp32.ini b/variants/esp32/esp32.ini index 85a85165e..4bc48cebb 100644 --- a/variants/esp32/esp32.ini +++ b/variants/esp32/esp32.ini @@ -38,7 +38,6 @@ build_flags = -DAXP_DEBUG_PORT=Serial -DCONFIG_BT_NIMBLE_ENABLED -DCONFIG_BT_NIMBLE_MAX_BONDS=6 # default is 3 - -DCONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED -DCONFIG_NIMBLE_CPP_LOG_LEVEL=2 -DCONFIG_BT_NIMBLE_MAX_CCCDS=20 -DCONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=8192 @@ -60,7 +59,7 @@ lib_deps = # renovate: datasource=git-refs depName=meshtastic-esp32_https_server packageName=https://github.com/meshtastic/esp32_https_server gitBranch=master https://github.com/meshtastic/esp32_https_server/archive/3223704846752e6d545139204837bdb2a55459ca.zip # renovate: datasource=custom.pio depName=NimBLE-Arduino packageName=h2zero/library/NimBLE-Arduino - h2zero/NimBLE-Arduino@^2.3.7 + h2zero/NimBLE-Arduino@^1.4.3 # renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip # renovate: datasource=github-tags depName=XPowersLib packageName=lewisxhe/XPowersLib diff --git a/variants/esp32c3/esp32c3.ini b/variants/esp32c3/esp32c3.ini index 07f8bcdd1..2ba3036d0 100644 --- a/variants/esp32c3/esp32c3.ini +++ b/variants/esp32c3/esp32c3.ini @@ -4,8 +4,3 @@ custom_esp32_kind = esp32c3 monitor_speed = 115200 monitor_filters = esp32_c3_exception_decoder - -build_flags = - ${esp32_base.build_flags} - -DCONFIG_BT_NIMBLE_EXT_ADV=1 - -DCONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES=2 diff --git a/variants/esp32c6/m5stack_unitc6l/platformio.ini b/variants/esp32c6/m5stack_unitc6l/platformio.ini index ac6b90336..9992ab2bf 100644 --- a/variants/esp32c6/m5stack_unitc6l/platformio.ini +++ b/variants/esp32c6/m5stack_unitc6l/platformio.ini @@ -13,7 +13,7 @@ build_unflags = lib_deps = ${esp32c6_base.lib_deps} adafruit/Adafruit NeoPixel@^1.12.3 - h2zero/NimBLE-Arduino@^2.3.7 + h2zero/NimBLE-Arduino@^2.3.6 build_flags = ${esp32c6_base.build_flags} -D M5STACK_UNITC6L @@ -24,6 +24,7 @@ build_flags = -D HAS_BLUETOOTH=1 -DCONFIG_BT_NIMBLE_EXT_ADV=1 -DCONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES=2 + -D NIMBLE_TWO monitor_speed=115200 lib_ignore = NonBlockingRTTTL diff --git a/variants/esp32s3/esp32s3.ini b/variants/esp32s3/esp32s3.ini index 3230323ec..8d8b6899e 100644 --- a/variants/esp32s3/esp32s3.ini +++ b/variants/esp32s3/esp32s3.ini @@ -3,8 +3,3 @@ extends = esp32_base custom_esp32_kind = esp32s3 monitor_speed = 115200 - -build_flags = - ${esp32_base.build_flags} - -DCONFIG_BT_NIMBLE_EXT_ADV=1 - -DCONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES=2 From 9058ccecf9c5257513ed91add49b5f96738bb083 Mon Sep 17 00:00:00 2001 From: Eric Severance Date: Tue, 30 Dec 2025 13:31:35 -0800 Subject: [PATCH 19/28] Calculate hops correctly even when hop_start==0 (#9120) * Calculate hops correctly even when hop_start==0. * Use the same type (int8_t) in the loop, avoiding signed/unsigned mismatches. * Clarify defaultIfUnknown is returned for encrypted packets. --- .../User/Positions/PositionsApplet.cpp | 5 +++-- src/mesh/MeshModule.cpp | 4 ++-- src/mesh/MeshService.cpp | 7 ++---- src/mesh/NextHopRouter.cpp | 5 ++--- src/mesh/NodeDB.cpp | 22 +++++++++++++++++-- src/mesh/NodeDB.h | 4 ++++ src/mesh/ReliableRouter.cpp | 11 +++++----- src/mesh/Router.cpp | 3 +-- src/modules/NeighborInfoModule.cpp | 2 +- src/modules/RoutingModule.cpp | 9 ++++---- src/modules/RoutingModule.h | 2 +- src/modules/TraceRouteModule.cpp | 9 ++++---- src/serialization/MeshPacketSerializer.cpp | 10 +++++---- .../MeshPacketSerializer_nRF52.cpp | 10 +++++---- 14 files changed, 63 insertions(+), 40 deletions(-) diff --git a/src/graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.cpp b/src/graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.cpp index 88bed998d..ad0f9fc47 100644 --- a/src/graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.cpp +++ b/src/graphics/niche/InkHUD/Applets/User/Positions/PositionsApplet.cpp @@ -1,6 +1,7 @@ #ifdef MESHTASTIC_INCLUDE_INKHUD #include "./PositionsApplet.h" +#include "NodeDB.h" using namespace NicheGraphics; @@ -49,8 +50,8 @@ ProcessMessage InkHUD::PositionsApplet::handleReceived(const meshtastic_MeshPack if (!hasPosition) return ProcessMessage::CONTINUE; - bool hasHopsAway = (mp.hop_start != 0 && mp.hop_limit <= mp.hop_start); // From NodeDB::updateFrom - uint8_t hopsAway = mp.hop_start - mp.hop_limit; + const int8_t hopsAway = getHopsAway(mp); + const bool hasHopsAway = hopsAway >= 0; // Determine if the position packet would change anything on-screen // ----------------------------------------------------------------- diff --git a/src/mesh/MeshModule.cpp b/src/mesh/MeshModule.cpp index c5748a560..83b64a873 100644 --- a/src/mesh/MeshModule.cpp +++ b/src/mesh/MeshModule.cpp @@ -195,7 +195,7 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src) // but opted NOT TO. Because it is not a good idea to let remote nodes 'probe' to find out which PSKs were "good" vs // bad. routingModule->sendAckNak(meshtastic_Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel, - routingModule->getHopLimitForResponse(mp.hop_start, mp.hop_limit)); + routingModule->getHopLimitForResponse(mp)); } } @@ -235,7 +235,7 @@ void setReplyTo(meshtastic_MeshPacket *p, const meshtastic_MeshPacket &to) assert(p->which_payload_variant == meshtastic_MeshPacket_decoded_tag); // Should already be set by now p->to = getFrom(&to); // Make sure that if we are sending to the local node, we use our local node addr, not 0 p->channel = to.channel; // Use the same channel that the request came in on - p->hop_limit = routingModule->getHopLimitForResponse(to.hop_start, to.hop_limit); + p->hop_limit = routingModule->getHopLimitForResponse(to); // No need for an ack if we are just delivering locally (it just generates an ignored ack) p->want_ack = (to.from != 0) ? to.want_ack : false; diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 297404747..368642bf8 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -93,11 +93,8 @@ int MeshService::handleFromRadio(const meshtastic_MeshPacket *mp) } else if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && !nodeDB->getMeshNode(mp->from)->has_user && nodeInfoModule && !isPreferredRebroadcaster && !nodeDB->isFull()) { if (airTime->isTxAllowedChannelUtil(true)) { - // Hops used by the request. If somebody in between running modified firmware modified it, ignore it - auto hopStart = mp->hop_start; - auto hopLimit = mp->hop_limit; - uint8_t hopsUsed = hopStart < hopLimit ? config.lora.hop_limit : hopStart - hopLimit; - if (hopsUsed > config.lora.hop_limit + 2) { + const int8_t hopsUsed = getHopsAway(*mp, config.lora.hop_limit); + if (hopsUsed > (int32_t)(config.lora.hop_limit + 2)) { LOG_DEBUG("Skip send NodeInfo: %d hops away is too far away", hopsUsed); } else { LOG_INFO("Heard new node on ch. %d, send NodeInfo and ask for response", mp->channel); diff --git a/src/mesh/NextHopRouter.cpp b/src/mesh/NextHopRouter.cpp index afdb4d096..5230e5b85 100644 --- a/src/mesh/NextHopRouter.cpp +++ b/src/mesh/NextHopRouter.cpp @@ -64,7 +64,7 @@ bool NextHopRouter::shouldFilterReceived(const meshtastic_MeshPacket *p) perhapsRebroadcast(p); } } else { - bool isRepeated = p->hop_start > 0 && p->hop_start == p->hop_limit; + bool isRepeated = getHopsAway(*p) == 0; // If repeated and not in Tx queue anymore, try relaying again, or if we are the destination, send the ACK again if (isRepeated) { if (!findInTxQueue(p->from, p->id)) { @@ -101,8 +101,7 @@ void NextHopRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtast bool wasAlreadyRelayer = wasRelayer(p->relay_node, p->decoded.request_id, p->to); bool weWereSoleRelayer = false; bool weWereRelayer = wasRelayer(ourRelayID, p->decoded.request_id, p->to, &weWereSoleRelayer); - if ((weWereRelayer && wasAlreadyRelayer) || - (p->hop_start != 0 && p->hop_start == p->hop_limit && weWereSoleRelayer)) { + if ((weWereRelayer && wasAlreadyRelayer) || (getHopsAway(*p) == 0 && weWereSoleRelayer)) { if (origTx->next_hop != p->relay_node) { // Not already set LOG_INFO("Update next hop of 0x%x to 0x%x based on ACK/reply (was relayer %d we were sole %d)", p->from, p->relay_node, wasAlreadyRelayer, weWereSoleRelayer); diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 9052ee17c..3c408f01f 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1549,6 +1549,23 @@ uint32_t sinceReceived(const meshtastic_MeshPacket *p) return delta; } +int8_t getHopsAway(const meshtastic_MeshPacket &p, int8_t defaultIfUnknown) +{ + // Firmware prior to 2.3.0 (585805c) lacked a hop_start field. Firmware version 2.5.0 (bf34329) introduced a + // bitfield that is always present. Use the presence of the bitfield to determine if the origin's firmware + // version is guaranteed to have hop_start populated. Note that this can only be done for decoded packets as + // the bitfield is encrypted under the channel encryption key. For encrypted packets, this returns + // defaultIfUnknown when hop_start is 0. + if (p.hop_start == 0 && !(p.which_payload_variant == meshtastic_MeshPacket_decoded_tag && p.decoded.has_bitfield)) + return defaultIfUnknown; // Cannot reliably determine the number of hops. + + // Guard against invalid values. + if (p.hop_start < p.hop_limit) + return defaultIfUnknown; + + return p.hop_start - p.hop_limit; +} + #define NUM_ONLINE_SECS (60 * 60 * 2) // 2 hrs to consider someone offline size_t NodeDB::getNumOnlineMeshNodes(bool localOnly) @@ -1801,9 +1818,10 @@ void NodeDB::updateFrom(const meshtastic_MeshPacket &mp) info->via_mqtt = mp.via_mqtt; // Store if we received this packet via MQTT // If hopStart was set and there wasn't someone messing with the limit in the middle, add hopsAway - if (mp.hop_start != 0 && mp.hop_limit <= mp.hop_start) { + const int8_t hopsAway = getHopsAway(mp); + if (hopsAway >= 0) { info->has_hops_away = true; - info->hops_away = mp.hop_start - mp.hop_limit; + info->hops_away = hopsAway; } sortMeshDB(); } diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index 6fd8deb87..817e31617 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -110,6 +110,10 @@ uint32_t sinceLastSeen(const meshtastic_NodeInfoLite *n); /// Given a packet, return how many seconds in the past (vs now) it was received uint32_t sinceReceived(const meshtastic_MeshPacket *p); +/// Given a packet, return the number of hops used to reach this node. +/// Returns defaultIfUnknown if the number of hops couldn't be determined. +int8_t getHopsAway(const meshtastic_MeshPacket &p, int8_t defaultIfUnknown = -1); + enum LoadFileResult { // Successfully opened the file LOAD_SUCCESS = 1, diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index 7619fc106..2b9b17183 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -1,6 +1,7 @@ #include "ReliableRouter.h" #include "Default.h" #include "MeshTypes.h" +#include "NodeDB.h" #include "configuration.h" #include "memGet.h" #include "mesh-pb-constants.h" @@ -108,12 +109,12 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas // If this packet should always be ACKed reliably with want_ack back to the original sender, make sure we // do that unconditionally. sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, - routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit), true); + routingModule->getHopLimitForResponse(*p), true); } else if (!p->decoded.request_id && !p->decoded.reply_id) { // If it's not an ACK or a reply, send an ACK. sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, - routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit)); - } else if ((p->hop_start > 0 && p->hop_start == p->hop_limit) || p->next_hop != NO_NEXT_HOP_PREFERENCE) { + routingModule->getHopLimitForResponse(*p)); + } else if ((getHopsAway(*p) == 0) || p->next_hop != NO_NEXT_HOP_PREFERENCE) { // If we received the packet directly from the original sender, send a 0-hop ACK since the original sender // won't overhear any implicit ACKs. If we received the packet via NextHopRouter, also send a 0-hop ACK to // stop the immediate relayer's retransmissions. @@ -123,11 +124,11 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas (nodeDB->getMeshNode(p->from) == nullptr || nodeDB->getMeshNode(p->from)->user.public_key.size == 0)) { LOG_INFO("PKI packet from unknown node, send PKI_UNKNOWN_PUBKEY"); sendAckNak(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY, getFrom(p), p->id, channels.getPrimaryIndex(), - routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit)); + routingModule->getHopLimitForResponse(*p)); } else { // Send a 'NO_CHANNEL' error on the primary channel if want_ack packet destined for us cannot be decoded sendAckNak(meshtastic_Routing_Error_NO_CHANNEL, getFrom(p), p->id, channels.getPrimaryIndex(), - routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit)); + routingModule->getHopLimitForResponse(*p)); } } else if (p->next_hop == nodeDB->getLastByteOfNodeNum(getNodeNum()) && p->hop_limit > 0) { // No wantAck, but we need to ACK with hop limit of 0 if we were the next hop to stop their retransmissions diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index ad0c0be6f..92bf2b420 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -81,8 +81,7 @@ Router::Router() : concurrency::OSThread("Router"), fromRadioQueue(MAX_RX_FROMRA bool Router::shouldDecrementHopLimit(const meshtastic_MeshPacket *p) { // First hop MUST always decrement to prevent retry issues - bool isFirstHop = (p->hop_start != 0 && p->hop_start == p->hop_limit); - if (isFirstHop) { + if (getHopsAway(*p) == 0) { return true; // Always decrement on first hop } diff --git a/src/modules/NeighborInfoModule.cpp b/src/modules/NeighborInfoModule.cpp index 936a7b44a..2cd8ec5ed 100644 --- a/src/modules/NeighborInfoModule.cpp +++ b/src/modules/NeighborInfoModule.cpp @@ -170,7 +170,7 @@ bool NeighborInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, } else { LOG_DEBUG(" Ignoring dummy neighbor info packet (single neighbor with nodeId 0, snr 0)"); } - } else if (mp.hop_start != 0 && mp.hop_start == mp.hop_limit) { + } else if (getHopsAway(mp) == 0) { LOG_DEBUG("Get or create neighbor: %u with snr %f", mp.from, mp.rx_snr); // If the hopLimit is the same as hopStart, then it is a neighbor getOrCreateNeighbor(mp.from, mp.from, 0, diff --git a/src/modules/RoutingModule.cpp b/src/modules/RoutingModule.cpp index 662f5379a..e9e1fc786 100644 --- a/src/modules/RoutingModule.cpp +++ b/src/modules/RoutingModule.cpp @@ -58,12 +58,11 @@ void RoutingModule::sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketI router->sendLocal(p); // we sometimes send directly to the local node } -uint8_t RoutingModule::getHopLimitForResponse(uint8_t hopStart, uint8_t hopLimit) +uint8_t RoutingModule::getHopLimitForResponse(const meshtastic_MeshPacket &mp) { - if (hopStart != 0) { - // Hops used by the request. If somebody in between running modified firmware modified it, ignore it - uint8_t hopsUsed = hopStart < hopLimit ? config.lora.hop_limit : hopStart - hopLimit; - if (hopsUsed > config.lora.hop_limit) { + const int8_t hopsUsed = getHopsAway(mp); + if (hopsUsed >= 0) { + if (hopsUsed > (int32_t)(config.lora.hop_limit)) { // In event mode, we never want to send packets with more than our default 3 hops. #if !(EVENTMODE) // This falls through to the default. return hopsUsed; // If the request used more hops than the limit, use the same amount of hops diff --git a/src/modules/RoutingModule.h b/src/modules/RoutingModule.h index 5d4b9596f..2ac42f447 100644 --- a/src/modules/RoutingModule.h +++ b/src/modules/RoutingModule.h @@ -20,7 +20,7 @@ class RoutingModule : public ProtobufModule uint8_t hopLimit = 0); // Given the hopStart and hopLimit upon reception of a request, return the hop limit to use for the response - uint8_t getHopLimitForResponse(uint8_t hopStart, uint8_t hopLimit); + uint8_t getHopLimitForResponse(const meshtastic_MeshPacket &mp); protected: friend class Router; diff --git a/src/modules/TraceRouteModule.cpp b/src/modules/TraceRouteModule.cpp index 87a2f1bd2..41dc02cd1 100644 --- a/src/modules/TraceRouteModule.cpp +++ b/src/modules/TraceRouteModule.cpp @@ -1,5 +1,6 @@ #include "TraceRouteModule.h" #include "MeshService.h" +#include "NodeDB.h" #include "graphics/Screen.h" #include "graphics/ScreenFonts.h" #include "graphics/SharedUIDisplay.h" @@ -359,10 +360,10 @@ void TraceRouteModule::insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_Ro } // Only insert unknown hops if hop_start is valid - if (p.hop_start != 0 && p.hop_limit <= p.hop_start) { - uint8_t hopsTaken = p.hop_start - p.hop_limit; + const int8_t hopsTaken = getHopsAway(p); + if (hopsTaken >= 0) { int8_t diff = hopsTaken - *route_count; - for (uint8_t i = 0; i < diff; i++) { + for (int8_t i = 0; i < diff; i++) { if (*route_count < ROUTE_SIZE) { route[*route_count] = NODENUM_BROADCAST; // This will represent an unknown hop *route_count += 1; @@ -370,7 +371,7 @@ void TraceRouteModule::insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_Ro } // Add unknown SNR values if necessary diff = *route_count - *snr_count; - for (uint8_t i = 0; i < diff; i++) { + for (int8_t i = 0; i < diff; i++) { if (*snr_count < ROUTE_SIZE) { snr_list[*snr_count] = INT8_MIN; // This will represent an unknown SNR *snr_count += 1; diff --git a/src/serialization/MeshPacketSerializer.cpp b/src/serialization/MeshPacketSerializer.cpp index b31d2dc2e..a12972cb0 100644 --- a/src/serialization/MeshPacketSerializer.cpp +++ b/src/serialization/MeshPacketSerializer.cpp @@ -418,8 +418,9 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, jsonObj["rssi"] = new JSONValue((int)mp->rx_rssi); if (mp->rx_snr != 0) jsonObj["snr"] = new JSONValue((float)mp->rx_snr); - if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start) { - jsonObj["hops_away"] = new JSONValue((unsigned int)(mp->hop_start - mp->hop_limit)); + const int8_t hopsAway = getHopsAway(*mp); + if (hopsAway >= 0) { + jsonObj["hops_away"] = new JSONValue((unsigned int)(hopsAway)); jsonObj["hop_start"] = new JSONValue((unsigned int)(mp->hop_start)); } @@ -450,8 +451,9 @@ std::string MeshPacketSerializer::JsonSerializeEncrypted(const meshtastic_MeshPa jsonObj["rssi"] = new JSONValue((int)mp->rx_rssi); if (mp->rx_snr != 0) jsonObj["snr"] = new JSONValue((float)mp->rx_snr); - if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start) { - jsonObj["hops_away"] = new JSONValue((unsigned int)(mp->hop_start - mp->hop_limit)); + const int8_t hopsAway = getHopsAway(*mp); + if (hopsAway >= 0) { + jsonObj["hops_away"] = new JSONValue((unsigned int)(hopsAway)); jsonObj["hop_start"] = new JSONValue((unsigned int)(mp->hop_start)); } jsonObj["size"] = new JSONValue((unsigned int)mp->encrypted.size); diff --git a/src/serialization/MeshPacketSerializer_nRF52.cpp b/src/serialization/MeshPacketSerializer_nRF52.cpp index 353c710a1..41f505b94 100644 --- a/src/serialization/MeshPacketSerializer_nRF52.cpp +++ b/src/serialization/MeshPacketSerializer_nRF52.cpp @@ -358,8 +358,9 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, jsonObj["rssi"] = (int)mp->rx_rssi; if (mp->rx_snr != 0) jsonObj["snr"] = (float)mp->rx_snr; - if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start) { - jsonObj["hops_away"] = (unsigned int)(mp->hop_start - mp->hop_limit); + const int8_t hopsAway = getHopsAway(*mp); + if (hopsAway >= 0) { + jsonObj["hops_away"] = (unsigned int)(hopsAway); jsonObj["hop_start"] = (unsigned int)(mp->hop_start); } @@ -393,8 +394,9 @@ std::string MeshPacketSerializer::JsonSerializeEncrypted(const meshtastic_MeshPa jsonObj["rssi"] = (int)mp->rx_rssi; if (mp->rx_snr != 0) jsonObj["snr"] = (float)mp->rx_snr; - if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start) { - jsonObj["hops_away"] = (unsigned int)(mp->hop_start - mp->hop_limit); + const int8_t hopsAway = getHopsAway(*mp); + if (hopsAway >= 0) { + jsonObj["hops_away"] = (unsigned int)(hopsAway); jsonObj["hop_start"] = (unsigned int)(mp->hop_start); } jsonObj["size"] = (unsigned int)mp->encrypted.size; From 25acce2a8d4596db3b9aff99803649ecb7b7516d Mon Sep 17 00:00:00 2001 From: Jason P Date: Wed, 31 Dec 2025 09:19:05 -0600 Subject: [PATCH 20/28] Add Temporary Mute to Home frame and unbury Notification Options (#9097) * Add Temporary Mute to Home frame and unbury Notifications * Only show Temporary Mute if it applies * Remove banner notification, we display the icon immediately * Remove extranous isMuted, there are better ways! --- src/graphics/SharedUIDisplay.cpp | 8 ++-- src/graphics/SharedUIDisplay.h | 1 - src/graphics/draw/MenuHandler.cpp | 55 +++++++++++----------------- src/graphics/draw/MenuHandler.h | 2 - src/modules/CannedMessageModule.cpp | 1 - src/modules/SystemCommandsModule.cpp | 7 ++-- 6 files changed, 29 insertions(+), 45 deletions(-) diff --git a/src/graphics/SharedUIDisplay.cpp b/src/graphics/SharedUIDisplay.cpp index f5ca6ed03..8f06fcf9f 100644 --- a/src/graphics/SharedUIDisplay.cpp +++ b/src/graphics/SharedUIDisplay.cpp @@ -8,6 +8,7 @@ #include "graphics/draw/UIRenderer.h" #include "main.h" #include "meshtastic/config.pb.h" +#include "modules/ExternalNotificationModule.h" #include "power.h" #include #include @@ -56,7 +57,6 @@ void decomposeTime(uint32_t rtc_sec, int &hour, int &minute, int &second) // === Shared External State === bool hasUnreadMessage = false; -bool isMuted = false; ScreenResolution currentResolution = ScreenResolution::Low; // === Internal State === @@ -306,7 +306,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti } display->drawXbm(iconX, iconY, mail_width, mail_height, mail); } - } else if (isMuted) { + } else if (externalNotificationModule->getMute()) { if (currentResolution == ScreenResolution::High) { int iconX = iconRightEdge - mute_symbol_big_width; int iconY = textY + (FONT_HEIGHT_SMALL - mute_symbol_big_height) / 2; @@ -325,7 +325,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti int iconX = iconRightEdge - mute_symbol_width; int iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2; - if (isInverted) { + if (isInverted && !force_no_invert) { display->setColor(WHITE); display->fillRect(iconX - 1, iconY - 1, mute_symbol_width + 2, mute_symbol_height + 2); display->setColor(BLACK); @@ -383,7 +383,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti int iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2; display->drawXbm(iconX, iconY, mail_width, mail_height, mail); } - } else if (isMuted) { + } else if (externalNotificationModule->getMute()) { if (currentResolution == ScreenResolution::High) { int iconX = iconRightEdge - mute_symbol_big_width; int iconY = textY + (FONT_HEIGHT_SMALL - mute_symbol_big_height) / 2; diff --git a/src/graphics/SharedUIDisplay.h b/src/graphics/SharedUIDisplay.h index af0d8dac1..a8ecdfada 100644 --- a/src/graphics/SharedUIDisplay.h +++ b/src/graphics/SharedUIDisplay.h @@ -41,7 +41,6 @@ namespace graphics // Shared state (declare inside namespace) extern bool hasUnreadMessage; -extern bool isMuted; enum class ScreenResolution : uint8_t { UltraLow = 0, Low = 1, High = 2 }; extern ScreenResolution currentResolution; ScreenResolution determineScreenResolution(int16_t screenheight, int16_t screenwidth); diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index d9db84c35..7eafcdb46 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -20,8 +20,8 @@ #include "mesh/MeshTypes.h" #include "modules/AdminModule.h" #include "modules/CannedMessageModule.h" +#include "modules/ExternalNotificationModule.h" #include "modules/KeyVerificationModule.h" - #include "modules/TraceRouteModule.h" #include #include @@ -843,12 +843,21 @@ void menuHandler::messageViewModeMenu() void menuHandler::homeBaseMenu() { - enum optionsNumbers { Back, Backlight, Position, Preset, Freetext, Sleep, enumEnd }; + enum optionsNumbers { Back, Mute, Backlight, Position, Preset, Freetext, Sleep, enumEnd }; static const char *optionsArray[enumEnd] = {"Back"}; static int optionsEnumArray[enumEnd] = {Back}; int options = 1; + if (moduleConfig.external_notification.enabled && externalNotificationModule && + config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DISABLED) { + if (!externalNotificationModule->getMute()) { + optionsArray[options] = "Temporarily Mute"; + } else { + optionsArray[options] = "Unmute"; + } + optionsEnumArray[options++] = Mute; + } #if defined(PIN_EINK_EN) || defined(PCA_PIN_EINK_EN) optionsArray[options] = "Toggle Backlight"; optionsEnumArray[options++] = Backlight; @@ -872,7 +881,13 @@ void menuHandler::homeBaseMenu() bannerOptions.optionsEnumPtr = optionsEnumArray; bannerOptions.optionsCount = options; bannerOptions.bannerCallback = [](int selected) -> void { - if (selected == Backlight) { + if (selected == Mute) { + if (moduleConfig.external_notification.enabled && externalNotificationModule) { + externalNotificationModule->setMute(!externalNotificationModule->getMute()); + IF_SCREEN(if (!externalNotificationModule->getMute()) externalNotificationModule->stopNow();) + } + } else if (selected == Backlight) { + screen->setOn(false); #if defined(PIN_EINK_EN) if (uiconfig.screen_brightness == 1) { uiconfig.screen_brightness = 0; @@ -949,6 +964,7 @@ void menuHandler::systemBaseMenu() optionsArray[options] = "Notifications"; optionsEnumArray[options++] = Notifications; + optionsArray[options] = "Display Options"; optionsEnumArray[options++] = ScreenOptions; @@ -985,7 +1001,7 @@ void menuHandler::systemBaseMenu() bannerOptions.optionsEnumPtr = optionsEnumArray; bannerOptions.bannerCallback = [](int selected) -> void { if (selected == Notifications) { - menuHandler::menuQueue = menuHandler::notifications_menu; + menuHandler::menuQueue = menuHandler::buzzermodemenupicker; screen->runNow(); } else if (selected == ScreenOptions) { menuHandler::menuQueue = menuHandler::screen_options_menu; @@ -1604,9 +1620,9 @@ void menuHandler::BluetoothToggleMenu() void menuHandler::BuzzerModeMenu() { - static const char *optionsArray[] = {"All Enabled", "Disabled", "Notifications", "System Only", "DMs Only"}; + static const char *optionsArray[] = {"All Enabled", "All Disabled", "Notifications", "System Only", "DMs Only"}; BannerOverlayOptions bannerOptions; - bannerOptions.message = "Buzzer Mode"; + bannerOptions.message = "Notification Sounds"; bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsCount = 5; bannerOptions.bannerCallback = [](int selected) -> void { @@ -1981,30 +1997,6 @@ void menuHandler::wifiToggleMenu() screen->showOverlayBanner(bannerOptions); } -void menuHandler::notificationsMenu() -{ - enum optionsNumbers { Back, BuzzerActions }; - static const char *optionsArray[] = {"Back", "Buzzer Actions"}; - static int optionsEnumArray[] = {Back, BuzzerActions}; - int options = 2; - - BannerOverlayOptions bannerOptions; - bannerOptions.message = "Notifications"; - bannerOptions.optionsArrayPtr = optionsArray; - bannerOptions.optionsCount = options; - bannerOptions.optionsEnumPtr = optionsEnumArray; - bannerOptions.bannerCallback = [](int selected) -> void { - if (selected == BuzzerActions) { - menuHandler::menuQueue = menuHandler::buzzermodemenupicker; - screen->runNow(); - } else { - menuQueue = system_base_menu; - screen->runNow(); - } - }; - screen->showOverlayBanner(bannerOptions); -} - void menuHandler::screenOptionsMenu() { // Check if brightness is supported @@ -2422,9 +2414,6 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display) case bluetooth_toggle_menu: BluetoothToggleMenu(); break; - case notifications_menu: - notificationsMenu(); - break; case screen_options_menu: screenOptionsMenu(); break; diff --git a/src/graphics/draw/MenuHandler.h b/src/graphics/draw/MenuHandler.h index bda744a66..a9a18b8e3 100644 --- a/src/graphics/draw/MenuHandler.h +++ b/src/graphics/draw/MenuHandler.h @@ -39,7 +39,6 @@ class menuHandler number_test, wifi_toggle_menu, bluetooth_toggle_menu, - notifications_menu, screen_options_menu, power_menu, system_base_menu, @@ -98,7 +97,6 @@ class menuHandler static void numberTest(); static void wifiBaseMenu(); static void wifiToggleMenu(); - static void notificationsMenu(); static void screenOptionsMenu(); static void powerMenu(); static void nodeNameLengthMenu(); diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 5af9ebaac..8d1ba6346 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -115,7 +115,6 @@ namespace graphics extern int bannerSignalBars; } extern ScanI2C::DeviceAddress cardkb_found; -extern bool graphics::isMuted; extern bool osk_found; static const char *cannedMessagesConfigFile = "/prefs/cannedConf.proto"; diff --git a/src/modules/SystemCommandsModule.cpp b/src/modules/SystemCommandsModule.cpp index b347b2f5d..51543eab6 100644 --- a/src/modules/SystemCommandsModule.cpp +++ b/src/modules/SystemCommandsModule.cpp @@ -45,10 +45,9 @@ int SystemCommandsModule::handleInputEvent(const InputEvent *event) // Mute case INPUT_BROKER_MSG_MUTE_TOGGLE: if (moduleConfig.external_notification.enabled && externalNotificationModule) { - bool isMuted = externalNotificationModule->getMute(); - externalNotificationModule->setMute(!isMuted); - IF_SCREEN(graphics::isMuted = !isMuted; if (!isMuted) externalNotificationModule->stopNow(); - screen->showSimpleBanner(isMuted ? "Notifications\nEnabled" : "Notifications\nDisabled", 3000);) + externalNotificationModule->setMute(externalNotificationModule->getMute()); + IF_SCREEN(if (!externalNotificationModule->getMute()) externalNotificationModule->stopNow(); screen->showSimpleBanner( + externalNotificationModule->getMute() ? "Notifications\nEnabled" : "Notifications\nDisabled", 3000);) } return 0; // Bluetooth From eaab8f04b54073119ff7ce5d00f62609a3c126b2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 1 Jan 2026 10:58:56 +1100 Subject: [PATCH 21/28] chore(deps): update meshtastic/device-ui digest to 940ba85 (#9129) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index ca23b55c3..3426e5c2f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -123,7 +123,7 @@ lib_deps = [device-ui_base] lib_deps = # renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master - https://github.com/meshtastic/device-ui/archive/7656d49ea2bdb81a43afc1e8552f1cc86b8dce3c.zip + https://github.com/meshtastic/device-ui/archive/940ba8570f59c59c3508643f4d72840de716ce20.zip ; Common libs for environmental measurements in telemetry module [environmental_base] From 4f1a56d4809545b7780360ce31642af4072db9e7 Mon Sep 17 00:00:00 2001 From: Ford Jones <107664313+ford-jones@users.noreply.github.com> Date: Thu, 1 Jan 2026 14:23:24 +1300 Subject: [PATCH 22/28] Rak3112 support (#8591) * Add rak3112 to board variants * Add rak3112 to architecture definitions * Disable SDcard support * Update comments * Remove duplicate definitions and use expected SPI naming for SDcard module * SDcard module serial interface chip set definition * Refactor modular variant into existing environment * Make requested changes * Extend 3312 variants * Remove duplicate architecture definition * Fix definition naming --- variants/esp32s3/rak3312/pins_arduino.h | 49 +++++++++++++++++++++++++ variants/esp32s3/rak3312/platformio.ini | 13 +++++++ variants/esp32s3/rak3312/variant.h | 31 ++++++++++++---- 3 files changed, 85 insertions(+), 8 deletions(-) diff --git a/variants/esp32s3/rak3312/pins_arduino.h b/variants/esp32s3/rak3312/pins_arduino.h index 15a26e991..dc5d30d61 100644 --- a/variants/esp32s3/rak3312/pins_arduino.h +++ b/variants/esp32s3/rak3312/pins_arduino.h @@ -17,6 +17,8 @@ static const uint8_t MOSI = 11; static const uint8_t MISO = 10; static const uint8_t SCK = 13; +#define SPI_INTERFACES_COUNT 1 + #define SPI_MOSI (11) #define SPI_SCK (13) #define SPI_MISO (10) @@ -25,4 +27,51 @@ static const uint8_t SCK = 13; // LEDs #define LED_BUILTIN LED_GREEN +#ifdef _VARIANT_RAK3112_ +/* + * Serial interfaces + */ +// TXD1 RXD1 on Base Board +#define PIN_SERIAL1_RX (44) +#define PIN_SERIAL1_TX (43) + +/* + * Internal SPI to LoRa transceiver + */ +#define LORA_SX126X_SCK 5 +#define LORA_SX126X_MISO 3 +#define LORA_SX126X_MOSI 6 + +/* + * Analog pins + */ +#define PIN_A0 (21) +#define PIN_A1 (14) + +/* + * Wire Interfaces + */ +#define WIRE_INTERFACES_COUNT 2 + +#define PIN_WIRE1_SDA (17) +#define PIN_WIRE1_SCL (18) + +/* + * GPIO's + */ +#define WB_IO1 21 +#define WB_IO2 2 +// #define WB_IO2 14 +#define WB_IO3 41 +#define WB_IO4 42 +#define WB_IO5 38 +#define WB_IO6 39 +// #define WB_SW1 35 NC +#define WB_A0 1 +#define WB_A1 2 +#define WB_CS 12 +#define WB_LED1 46 +#define WB_LED2 45 +#endif + #endif /* Pins_Arduino_h */ diff --git a/variants/esp32s3/rak3312/platformio.ini b/variants/esp32s3/rak3312/platformio.ini index 65eba93e6..1f95c3b73 100644 --- a/variants/esp32s3/rak3312/platformio.ini +++ b/variants/esp32s3/rak3312/platformio.ini @@ -9,3 +9,16 @@ build_flags = ${esp32s3_base.build_flags} -D RAK3312 -I variants/esp32s3/rak3312 + +[env:rak3112] +extends = esp32s3_base +board = wiscore_rak3312 +board_level = pr +board_check = true +upload_protocol = esptool + +build_flags = + ${esp32_base.build_flags} + -D RAK3312 + -D _VARIANT_RAK3112_ + -I variants/esp32s3/rak3312 diff --git a/variants/esp32s3/rak3312/variant.h b/variants/esp32s3/rak3312/variant.h index dfdf4de71..1f8eb9e39 100644 --- a/variants/esp32s3/rak3312/variant.h +++ b/variants/esp32s3/rak3312/variant.h @@ -18,11 +18,6 @@ #define SX126X_DIO3_TCXO_VOLTAGE 1.8 #endif -#define SX126X_POWER_EN (4) - -#define PIN_POWER_EN PIN_3V3_EN -#define PIN_3V3_EN (14) - #define LED_GREEN 46 #define LED_BLUE 45 @@ -35,10 +30,30 @@ #define LED_STATE_ON 1 // State when LED is litted +#define BATTERY_PIN 1 +#define ADC_CHANNEL ADC1_GPIO1_CHANNEL + +#ifdef _VARIANT_RAK3112_ // Modular variant (stamp) +#define ADC_MULTIPLIER 2.11 + +#define BUTTON_NEED_PULLUP + +#define HAS_SDCARD +#define SDCARD_USE_SPI1 +#define SDCARD_CS SPI_CS + +#define I2C_SDA1 PIN_WIRE1_SDA +#define I2C_SCL1 PIN_WIRE1_SCL +#else // Generic 3312 variant (40-pin standard connector) +#define ADC_MULTIPLIER 1.667 + +#define SX126X_POWER_EN (4) + +#define PIN_POWER_EN PIN_3V3_EN +#define PIN_3V3_EN (14) + #define HAS_GPS 1 #define GPS_TX_PIN 43 #define GPS_RX_PIN 44 -#define BATTERY_PIN 1 -#define ADC_CHANNEL ADC1_GPIO1_CHANNEL -#define ADC_MULTIPLIER 1.667 \ No newline at end of file +#endif \ No newline at end of file From 7fb95841e4dbe84f349b73313b1f7599d8690e6b Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Thu, 1 Jan 2026 08:25:33 -0600 Subject: [PATCH 23/28] Apparently I marked board level extra on the wrong tbeam target --- variants/esp32/tbeam/platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/variants/esp32/tbeam/platformio.ini b/variants/esp32/tbeam/platformio.ini index ddb8e9c9f..206ecbd7e 100644 --- a/variants/esp32/tbeam/platformio.ini +++ b/variants/esp32/tbeam/platformio.ini @@ -2,7 +2,7 @@ [env:tbeam] extends = esp32_base board = ttgo-t-beam -board_level = extra + board_check = true lib_deps = ${esp32_base.lib_deps} build_flags = ${esp32_base.build_flags} @@ -14,7 +14,7 @@ upload_speed = 921600 [env:tbeam-displayshield] extends = env:tbeam - +board_level = extra build_flags = ${env:tbeam.build_flags} -D USE_ST7796 From a5b2d4a9d53600691e245006c1e1fd45310788dc Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Thu, 1 Jan 2026 13:53:36 -0600 Subject: [PATCH 24/28] Add null check for p_encrypted before MQTT publish (#9136) * Add null check for p_encrypted before MQTT publish A user on BayMesh observed a strange crash in MQTT::onSend that seemed to be a null pointer dereference of this value. * Trunk --- src/mesh/Router.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 92bf2b420..3ed4a898a 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -744,15 +744,19 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src) MeshModule::callModules(*p, src); #if !MESHTASTIC_EXCLUDE_MQTT - // Mark as pki_encrypted if it is not yet decoded and MQTT encryption is also enabled, hash matches and it's a DM not to - // us (because we would be able to decrypt it) - if (decodedState == DecodeState::DECODE_FAILURE && moduleConfig.mqtt.encryption_enabled && p->channel == 0x00 && - !isBroadcast(p->to) && !isToUs(p)) - p_encrypted->pki_encrypted = true; - // After potentially altering it, publish received message to MQTT if we're not the original transmitter of the packet - if ((decodedState == DecodeState::DECODE_SUCCESS || p_encrypted->pki_encrypted) && moduleConfig.mqtt.enabled && - !isFromUs(p) && mqtt) - mqtt->onSend(*p_encrypted, *p, p->channel); + if (p_encrypted == nullptr) { + LOG_WARN("p_encrypted is null, skipping MQTT publish"); + } else { + // Mark as pki_encrypted if it is not yet decoded and MQTT encryption is also enabled, hash matches and it's a DM not + // to us (because we would be able to decrypt it) + if (decodedState == DecodeState::DECODE_FAILURE && moduleConfig.mqtt.encryption_enabled && p->channel == 0x00 && + !isBroadcast(p->to) && !isToUs(p)) + p_encrypted->pki_encrypted = true; + // After potentially altering it, publish received message to MQTT if we're not the original transmitter of the packet + if ((decodedState == DecodeState::DECODE_SUCCESS || p_encrypted->pki_encrypted) && moduleConfig.mqtt.enabled && + !isFromUs(p) && mqtt) + mqtt->onSend(*p_encrypted, *p, p->channel); + } #endif } From 45335532ca83ac31837139f716178c5feb5f9171 Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Fri, 2 Jan 2026 08:57:13 +1100 Subject: [PATCH 25/28] Syntax fix for first timer welcome bot. (#9144) URL formatting was inverted. --- .github/workflows/first_time_contributor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/first_time_contributor.yml b/.github/workflows/first_time_contributor.yml index cfb7f1d43..272ce4d10 100644 --- a/.github/workflows/first_time_contributor.yml +++ b/.github/workflows/first_time_contributor.yml @@ -22,7 +22,7 @@ jobs: ### @{fc-author}, Welcome to Meshtastic! :wave: Thanks for opening your first issue. If it's helpful, an easy way - to get logs is the "Open Serial Monitor" button on the (Web Flasher)[https://flasher.meshtastic.org]. + to get logs is the "Open Serial Monitor" button on the (Web Flasher](https://flasher.meshtastic.org). If you have ideas for features, note that we often debate big ideas in the [discussions tab](https://github.com/meshtastic/firmware/discussions/categories/ideas) From a2ce4c7f18ed1701f33f1ebfdcc468f2ba647a9e Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Fri, 2 Jan 2026 09:03:05 +1100 Subject: [PATCH 26/28] KZ_863 is not wide lora (#9075) KZ_863 was set to wide_lora = true. This was a mistake, induced because the regulations would allow SHORT_TURBO. However, that interpretation of the code was incorrect and wide_lora has a different meaning. Fixes https://github.com/meshtastic/firmware/issues/9054 --- src/mesh/RadioInterface.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index f7daf1122..9ed07a8e8 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -171,7 +171,8 @@ const RegionInfo regions[] = { 863 - 868 MHz <25 mW EIRP, 500kHz channels allowed, must not be used at airfields https://github.com/meshtastic/firmware/issues/7204 */ - RDEF(KZ_433, 433.075f, 434.775f, 100, 0, 10, true, false, false), RDEF(KZ_863, 863.0f, 868.0f, 100, 0, 30, true, false, true), + RDEF(KZ_433, 433.075f, 434.775f, 100, 0, 10, true, false, false), + RDEF(KZ_863, 863.0f, 868.0f, 100, 0, 30, true, false, false), /* Nepal From f9c9350f453315fcc296476e511e0f0ea2aafa1c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 1 Jan 2026 18:03:27 -0600 Subject: [PATCH 27/28] chore(deps): update meshtastic/device-ui digest to a8e2f94 (#9140) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 3426e5c2f..5c3bb9730 100644 --- a/platformio.ini +++ b/platformio.ini @@ -123,7 +123,7 @@ lib_deps = [device-ui_base] lib_deps = # renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master - https://github.com/meshtastic/device-ui/archive/940ba8570f59c59c3508643f4d72840de716ce20.zip + https://github.com/meshtastic/device-ui/archive/a8e2f947f7abaf0c5ac8e6dd189a22156335beaa.zip ; Common libs for environmental measurements in telemetry module [environmental_base] From 11b5f1a4fe0cd90a40640dffd9577de429fcc88f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 1 Jan 2026 18:04:13 -0600 Subject: [PATCH 28/28] chore(deps): update dorny/test-reporter action to v2.4.0 (#9135) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/test_native.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_native.yml b/.github/workflows/test_native.yml index cabe0dd97..40fcd7109 100644 --- a/.github/workflows/test_native.yml +++ b/.github/workflows/test_native.yml @@ -143,7 +143,7 @@ jobs: merge-multiple: true - name: Test Report - uses: dorny/test-reporter@v2.3.0 + uses: dorny/test-reporter@v2.4.0 with: name: PlatformIO Tests path: testreport.xml