From 390f0c8248a4aa6f12a4d0e595a4c0a0ca3c3a5f Mon Sep 17 00:00:00 2001 From: Jason P Date: Thu, 8 Jan 2026 16:44:05 -0600 Subject: [PATCH 1/7] Screenless Devices want to mute too! (#9210) * Screenless Devices want to mute too! * Add logging for actions * Gate to screenless devices only * WisMesh Tag was missing HAS_SCREEN 0 --------- Co-authored-by: Ben Meadors --- src/buzz/buzz.cpp | 22 ++++++++++++++++++++++ src/buzz/buzz.h | 2 ++ src/input/ButtonThread.cpp | 16 +++++++++++++++- variants/nrf52840/rak_wismeshtag/variant.h | 2 ++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/buzz/buzz.cpp b/src/buzz/buzz.cpp index 89d752c08..6fb28a6ac 100644 --- a/src/buzz/buzz.cpp +++ b/src/buzz/buzz.cpp @@ -35,6 +35,14 @@ struct ToneDuration { #define NOTE_G6 1568 #define NOTE_E7 2637 +#define NOTE_C4 262 +#define NOTE_E4 330 +#define NOTE_G4 392 +#define NOTE_A4 440 +#define NOTE_C5 523 +#define NOTE_E5 659 +#define NOTE_G5 784 + const int DURATION_1_16 = 62; // 1/16 note const int DURATION_1_8 = 125; // 1/8 note const int DURATION_1_4 = 250; // 1/4 note @@ -189,3 +197,17 @@ void playComboTune() }; playTones(melody, sizeof(melody) / sizeof(ToneDuration)); } + +void play4ClickDown() +{ + ToneDuration melody[] = {{NOTE_G5, 55}, {NOTE_E5, 55}, {NOTE_C5, 60}, {NOTE_A4, 55}, {NOTE_G4, 55}, + {NOTE_E4, 65}, {NOTE_C4, 80}, {NOTE_G3, 120}, {NOTE_E3, 160}, {NOTE_SILENT, 120}}; + playTones(melody, sizeof(melody) / sizeof(ToneDuration)); +} + +void play4ClickUp() +{ + // Quick high-pitched notes with trills + ToneDuration melody[] = {{NOTE_F5, 50}, {NOTE_G6, 45}, {NOTE_E7, 60}}; + playTones(melody, sizeof(melody) / sizeof(ToneDuration)); +} \ No newline at end of file diff --git a/src/buzz/buzz.h b/src/buzz/buzz.h index fa0c8c496..1b97e24de 100644 --- a/src/buzz/buzz.h +++ b/src/buzz/buzz.h @@ -7,6 +7,8 @@ void playShutdownMelody(); void playGPSEnableBeep(); void playGPSDisableBeep(); void playComboTune(); +void play4ClickDown(); +void play4ClickUp(); void playBoop(); void playChirp(); void playClick(); diff --git a/src/input/ButtonThread.cpp b/src/input/ButtonThread.cpp index 63fd8b0d7..0d835a3a9 100644 --- a/src/input/ButtonThread.cpp +++ b/src/input/ButtonThread.cpp @@ -248,7 +248,21 @@ int32_t ButtonThread::runOnce() this->notifyObservers(&evt); playComboTune(); break; - +#if !HAS_SCREEN + case 4: + if (moduleConfig.external_notification.enabled && externalNotificationModule) { + externalNotificationModule->setMute(!externalNotificationModule->getMute()); + IF_SCREEN(if (!externalNotificationModule->getMute()) externalNotificationModule->stopNow();) + if (externalNotificationModule->getMute()) { + LOG_INFO("Temporarily Muted"); + play4ClickDown(); // Disable tone + } else { + LOG_INFO("Unmuted"); + play4ClickUp(); // Enable tone + } + } + break; +#endif // No valid multipress action default: break; diff --git a/variants/nrf52840/rak_wismeshtag/variant.h b/variants/nrf52840/rak_wismeshtag/variant.h index 159cabf07..fa3e252ab 100644 --- a/variants/nrf52840/rak_wismeshtag/variant.h +++ b/variants/nrf52840/rak_wismeshtag/variant.h @@ -234,6 +234,8 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG #define RAK_4631 1 +#define HAS_SCREEN 0 + #ifdef __cplusplus } #endif From 489de09375b0591138f7b548775573bb1dc8fc86 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Thu, 8 Jan 2026 19:19:17 -0600 Subject: [PATCH 2/7] Use correct name for ALT_BUTTON_PIN (#9225) --- variants/esp32s3/t-beam-1w/variant.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/esp32s3/t-beam-1w/variant.h b/variants/esp32s3/t-beam-1w/variant.h index 00301fdfd..dbe1620e2 100644 --- a/variants/esp32s3/t-beam-1w/variant.h +++ b/variants/esp32s3/t-beam-1w/variant.h @@ -15,7 +15,7 @@ // Buttons #define BUTTON_PIN 0 // BUTTON 1 -#define BUTTON_PIN_ALT 17 // BUTTON 2 +#define ALT_BUTTON_PIN 17 // BUTTON 2 // SPI (shared by LoRa and SD) #define SPI_MOSI 11 From 6b8e5e9d7b1aeb421c0e96e14e181cef7e7d840b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 9 Jan 2026 05:41:19 -0600 Subject: [PATCH 3/7] Upgrade trunk (#9229) 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 fb2d93563..54a803206 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -9,14 +9,14 @@ plugins: lint: enabled: - checkov@3.2.497 - - renovate@42.74.2 + - renovate@42.75.0 - prettier@3.7.4 - trufflehog@3.92.4 - yamllint@1.37.1 - bandit@1.9.2 - trivy@0.68.2 - taplo@0.10.0 - - ruff@0.14.10 + - ruff@0.14.11 - isort@7.0.0 - markdownlint@0.47.0 - oxipng@10.0.0 From b12acba44f08a607ad48aa6c596a4a0a70410a25 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 9 Jan 2026 05:42:00 -0600 Subject: [PATCH 4/7] CH341 MAC address derivation from serial and product string (#9226) Updated MAC address derivation logic to include product string in hashing. --- src/platform/portduino/PortduinoGlue.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index f2b58f333..3fa473d2d 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -419,9 +419,11 @@ void portduinoSetup() ch341Hal->getProductString(product_string, 95); std::cout << "CH341 Product " << product_string << std::endl; if (strlen(serial) == 8 && portduino_config.mac_address.length() < 12) { - uint8_t hash[32] = {0}; + std::cout << "Deriving MAC address from Serial and Product String" << std::endl; + uint8_t hash[104] = {0}; memcpy(hash, serial, 8); - crypto->hash(hash, 8); + memcpy(hash + 8, product_string, strlen(product_string)); + crypto->hash(hash, 8 + strlen(product_string)); dmac[0] = (hash[0] << 4) | 2; dmac[1] = hash[1]; dmac[2] = hash[2]; From 925381ef7b809c347e6f86e52890c950b25fc9db Mon Sep 17 00:00:00 2001 From: Jason P Date: Fri, 9 Jan 2026 10:30:49 -0600 Subject: [PATCH 5/7] Fix TFT_MESH settings across setting and recalling (#9234) * Fix TFT_MESH settings across setting and recalling * Fix a word in documentation * Update src/graphics/Screen.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/graphics/Screen.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/graphics/Screen.cpp | 11 +++++++++++ src/graphics/draw/MenuHandler.cpp | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 91a9ed593..28f17f962 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -312,6 +312,7 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O // Only validate the combined value once if (rawRGB > 0 && rawRGB <= 255255255) { + LOG_INFO("Setting screen RGB color to user chosen: 0x%06X", rawRGB); // Extract each component as a normal int first int r = (rawRGB >> 16) & 0xFF; int g = (rawRGB >> 8) & 0xFF; @@ -319,6 +320,16 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) { TFT_MESH = COLOR565(static_cast(r), static_cast(g), static_cast(b)); } +#ifdef TFT_MESH_OVERRIDE + } else if (rawRGB == 0) { + LOG_INFO("Setting screen RGB color to TFT_MESH_OVERRIDE: 0x%04X", TFT_MESH_OVERRIDE); + // Default to TFT_MESH_OVERRIDE if available + TFT_MESH = TFT_MESH_OVERRIDE; +#endif + } else { + // Default best readable yellow color + LOG_INFO("Setting screen RGB color to default: (255,255,128)"); + TFT_MESH = COLOR565(255, 255, 128); } #if defined(USE_SH1106) || defined(USE_SH1107) || defined(USE_SH1107_128_64) diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index 138995ebe..7c17c8b92 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -1840,7 +1840,7 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display) static const ScreenColorOption colorOptions[] = { {"Back", OptionsAction::Back}, {"Default", OptionsAction::Select, ScreenColor(0, 0, 0, true)}, - {"Meshtastic Green", OptionsAction::Select, ScreenColor(103, 234, 148)}, + {"Meshtastic Green", OptionsAction::Select, ScreenColor(0x67, 0xEA, 0x94)}, {"Yellow", OptionsAction::Select, ScreenColor(255, 255, 128)}, {"Red", OptionsAction::Select, ScreenColor(255, 64, 64)}, {"Orange", OptionsAction::Select, ScreenColor(255, 160, 20)}, @@ -1890,7 +1890,7 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display) #ifdef TFT_MESH_OVERRIDE TFT_MESH = TFT_MESH_OVERRIDE; #else - TFT_MESH = COLOR565(0x67, 0xEA, 0x94); + TFT_MESH = COLOR565(255, 255, 128); #endif } else { TFT_MESH = COLOR565(r, g, b); From b002844aa014fe9c965b8099080efe335277a552 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 9 Jan 2026 11:36:53 -0600 Subject: [PATCH 6/7] Add Rak 6421 autoconf (#9010) * Add Rak 6421 autoconf * Minor memory safety hardening --- bin/config.d/lora-hat-rak-6421-pi-hat.yaml | 11 +++++++ src/platform/portduino/PortduinoGlue.cpp | 35 +++++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 bin/config.d/lora-hat-rak-6421-pi-hat.yaml diff --git a/bin/config.d/lora-hat-rak-6421-pi-hat.yaml b/bin/config.d/lora-hat-rak-6421-pi-hat.yaml new file mode 100644 index 000000000..066e36a10 --- /dev/null +++ b/bin/config.d/lora-hat-rak-6421-pi-hat.yaml @@ -0,0 +1,11 @@ +Lora: + + ### RAK13300in Slot 1 + Module: sx1262 + IRQ: 22 #IO6 + Reset: 16 # IO4 + Busy: 24 # IO5 + # Ant_sw: 13 # IO3 + DIO3_TCXO_VOLTAGE: true + DIO2_AS_RF_SWITCH: true + spidev: spidev0.0 diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index ea9e2de67..986e1c340 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -6,6 +6,7 @@ #include "target_specific.h" #include "PortduinoGlue.h" +#include "SHA256.h" #include "api/ServerAPI.h" #include "linux/gpio/LinuxGPIOPin.h" #include "meshUtils.h" @@ -270,7 +271,39 @@ void portduinoSetup() } std::cout << "autoconf: Found Pi HAT+ " << hat_vendor << " " << autoconf_product << " at /proc/device-tree/hat" << std::endl; - found_hat = true; + + // potential TODO: Validate that this is a real UUID + std::ifstream hatUUID("/proc/device-tree/hat/uuid"); + char uuid[38] = {0}; + if (hatUUID.is_open()) { + hatUUID.read(uuid, 37); + hatUUID.close(); + std::cout << "autoconf: UUID " << uuid << std::endl; + SHA256 uuid_hash; + uint8_t uuid_hash_bytes[32] = {0}; + + uuid_hash.reset(); + uuid_hash.update(uuid, 37); + uuid_hash.finalize(uuid_hash_bytes, 32); + + for (int j = 0; j < 16; j++) { + portduino_config.device_id[j] = uuid_hash_bytes[j]; + } + portduino_config.has_device_id = true; + uint8_t dmac[6] = {0}; + dmac[0] = (uuid_hash_bytes[17] << 4) | 2; + dmac[1] = uuid_hash_bytes[18]; + dmac[2] = uuid_hash_bytes[19]; + dmac[3] = uuid_hash_bytes[20]; + dmac[4] = uuid_hash_bytes[21]; + dmac[5] = uuid_hash_bytes[22]; + char macBuf[13] = {0}; + snprintf(macBuf, sizeof(macBuf), "%02X%02X%02X%02X%02X%02X", dmac[0], dmac[1], dmac[2], dmac[3], dmac[4], + dmac[5]); + portduino_config.mac_address = macBuf; + found_hat = true; + } + } else { std::cout << "autoconf: Could not locate Pi HAT+ at /proc/device-tree/hat" << std::endl; } From 214c76ce1b58856371cb26210154353119947e07 Mon Sep 17 00:00:00 2001 From: Manuel <71137295+mverch67@users.noreply.github.com> Date: Fri, 9 Jan 2026 18:48:27 +0100 Subject: [PATCH 7/7] T-Watch S3 Plus GPS support (#9235) * Upgrade trunk (#9229) Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com> * support T-Watch S3 Plus GPS --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com> --- .trunk/trunk.yaml | 4 ++-- src/Power.cpp | 6 +++--- src/gps/GPS.cpp | 5 ++++- variants/esp32s3/t-watch-s3/variant.h | 7 ++++--- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index fb2d93563..54a803206 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -9,14 +9,14 @@ plugins: lint: enabled: - checkov@3.2.497 - - renovate@42.74.2 + - renovate@42.75.0 - prettier@3.7.4 - trufflehog@3.92.4 - yamllint@1.37.1 - bandit@1.9.2 - trivy@0.68.2 - taplo@0.10.0 - - ruff@0.14.10 + - ruff@0.14.11 - isort@7.0.0 - markdownlint@0.47.0 - oxipng@10.0.0 diff --git a/src/Power.cpp b/src/Power.cpp index 33dda8e11..e9cde0eb6 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -1149,11 +1149,11 @@ bool Power::axpChipInit() PMU->setPowerChannelVoltage(XPOWERS_ALDO1, 3300); PMU->enablePowerOutput(XPOWERS_ALDO1); - // sdcard power channel + // sdcard (T-Beam S3) / gnns (T-Watch S3 Plus) power channel PMU->setPowerChannelVoltage(XPOWERS_BLDO1, 3300); +#ifndef T_WATCH_S3 PMU->enablePowerOutput(XPOWERS_BLDO1); - -#ifdef T_WATCH_S3 +#else // DRV2605 power channel PMU->setPowerChannelVoltage(XPOWERS_BLDO2, 3300); PMU->enablePowerOutput(XPOWERS_BLDO2); diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index a61a71dde..f53ffe5e4 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -934,8 +934,11 @@ void GPS::setPowerPMU(bool on) // t-beam v1.2 GNSS power channel on ? PMU->enablePowerOutput(XPOWERS_ALDO3) : PMU->disablePowerOutput(XPOWERS_ALDO3); } else if (HW_VENDOR == meshtastic_HardwareModel_LILYGO_TBEAM_S3_CORE) { - // t-beam-s3-core GNSS power channel + // t-beam-s3-core GNSS power channel on ? PMU->enablePowerOutput(XPOWERS_ALDO4) : PMU->disablePowerOutput(XPOWERS_ALDO4); + } else if (HW_VENDOR == meshtastic_HardwareModel_T_WATCH_S3) { + // t-watch-s3-plus GNSS power channel + on ? PMU->enablePowerOutput(XPOWERS_BLDO1) : PMU->disablePowerOutput(XPOWERS_BLDO1); } } else if (model == XPOWERS_AXP192) { // t-beam v1.1 GNSS power channel diff --git a/variants/esp32s3/t-watch-s3/variant.h b/variants/esp32s3/t-watch-s3/variant.h index 7a302f978..dfd219391 100644 --- a/variants/esp32s3/t-watch-s3/variant.h +++ b/variants/esp32s3/t-watch-s3/variant.h @@ -53,9 +53,10 @@ #define HAS_BMA423 1 #define BMA4XX_INT 14 // Interrupt for BMA_423 axis sensor -#define HAS_GPS 0 -#undef GPS_RX_PIN -#undef GPS_TX_PIN +#define GPS_DEFAULT_NOT_PRESENT 1 +#define GPS_BAUDRATE 38400 +#define GPS_RX_PIN 42 +#define GPS_TX_PIN 41 #define USE_SX1262 #define USE_SX1268