From 0f96bd7a26e5a435e90f03492d934121346cf5d2 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Thu, 3 Jul 2025 21:31:09 -0500 Subject: [PATCH 01/72] Add Kazakhstan to the BaseUI LoRa chooser (#7224) --- src/graphics/draw/MenuHandler.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index 6dbba853e..7e5063fef 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -48,12 +48,14 @@ void menuHandler::LoraRegionPicker(uint32_t duration) "PH_433", "PH_868", "PH_915", - "ANZ_433"}; + "ANZ_433", + "KZ_433", + "KZ_863"}; BannerOverlayOptions bannerOptions; bannerOptions.message = "Set the LoRa region"; bannerOptions.durationMs = duration; bannerOptions.optionsArrayPtr = optionsArray; - bannerOptions.optionsCount = 23; + bannerOptions.optionsCount = 25; bannerOptions.InitialSelected = 0; bannerOptions.bannerCallback = [](int selected) -> void { if (selected != 0 && config.lora.region != _meshtastic_Config_LoRaConfig_RegionCode(selected)) { From abbeb4874d6dcf983f298554c4a40f0874f2d634 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 4 Jul 2025 13:12:24 +0800 Subject: [PATCH 02/72] chore(deps): update xpowerslib to v0.3.0 (#7210) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- arch/esp32/esp32.ini | 2 +- arch/esp32/esp32c6.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini index faeca342f..6b9ebcb24 100644 --- a/arch/esp32/esp32.ini +++ b/arch/esp32/esp32.ini @@ -55,7 +55,7 @@ lib_deps = # renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip # renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib - lewisxhe/XPowersLib@^0.2.7 + lewisxhe/XPowersLib@0.3.0 # renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip # renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto diff --git a/arch/esp32/esp32c6.ini b/arch/esp32/esp32c6.ini index 26b5c0f5b..1afb9b547 100644 --- a/arch/esp32/esp32c6.ini +++ b/arch/esp32/esp32c6.ini @@ -28,7 +28,7 @@ lib_deps = ${environmental_extra.lib_deps} ${radiolib_base.lib_deps} # renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib - lewisxhe/XPowersLib@^0.2.7 + lewisxhe/XPowersLib@0.3.0 # renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip # renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto From f35ca812a32f26750aff1008428128f2ca8388f3 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 4 Jul 2025 05:30:56 -0500 Subject: [PATCH 03/72] Add a WiFi menu that can toggle back to Bluetooth (#7226) * Add Kazakhstan to the BaseUI LoRa chooser * Add a WiFi menu that can toggle back to Bluetooth --- src/graphics/Screen.cpp | 2 ++ src/graphics/draw/MenuHandler.cpp | 41 +++++++++++++++++++++++++++++++ src/graphics/draw/MenuHandler.h | 7 +++--- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 067e4418f..ed8119738 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -1373,6 +1373,8 @@ int Screen::handleInputEvent(const InputEvent *event) this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_hopsignal || this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_bearings) { menuHandler::nodeListMenu(); + } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.wifi) { + menuHandler::wifiBaseMenu(); } } else if (event->inputEvent == INPUT_BROKER_BACK) { showPrevFrame(); diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index 7e5063fef..43c226896 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -831,6 +831,44 @@ void menuHandler::numberTest() [](int number_picked) -> void { LOG_WARN("Nodenum: %u", number_picked); }); } +void menuHandler::wifiBaseMenu() +{ + enum optionsNumbers { Back, Wifi_toggle }; + + static const char *optionsArray[] = {"Back", "WiFi Toggle"}; + BannerOverlayOptions bannerOptions; + bannerOptions.message = "WiFi Menu"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = 2; + bannerOptions.bannerCallback = [](int selected) -> void { + if (selected == Wifi_toggle) { + menuQueue = wifi_toggle_menu; + screen->runNow(); + } + }; + screen->showOverlayBanner(bannerOptions); +} + +void menuHandler::wifiToggleMenu() +{ + enum optionsNumbers { Back, Wifi_toggle }; + + static const char *optionsArray[] = {"Back", "Disable"}; + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Disable Wifi and\nEnable Bluetooth?"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = 2; + bannerOptions.bannerCallback = [](int selected) -> void { + if (selected == Wifi_toggle) { + config.network.wifi_enabled = false; + config.bluetooth.enabled = true; + service->reloadConfig(SEGMENT_CONFIG); + rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000); + } + }; + screen->showOverlayBanner(bannerOptions); +} + void menuHandler::handleMenuSwitch(OLEDDisplay *display) { if (menuQueue != menu_none) @@ -894,6 +932,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display) case number_test: numberTest(); break; + case wifi_toggle_menu: + wifiToggleMenu(); + break; } menuQueue = menu_none; } diff --git a/src/graphics/draw/MenuHandler.h b/src/graphics/draw/MenuHandler.h index 09279b041..8824e38ed 100644 --- a/src/graphics/draw/MenuHandler.h +++ b/src/graphics/draw/MenuHandler.h @@ -13,9 +13,7 @@ class menuHandler clock_face_picker, clock_menu, position_base_menu, -#if !MESHTASTIC_EXCLUDE_GPS gps_toggle_menu, -#endif compass_point_north_menu, reset_node_db_menu, buzzermodemenupicker, @@ -26,7 +24,8 @@ class menuHandler add_favorite, remove_favorite, test_menu, - number_test + number_test, + wifi_toggle_menu }; static screenMenus menuQueue; @@ -54,6 +53,8 @@ class menuHandler static void removeFavoriteMenu(); static void testMenu(); static void numberTest(); + static void wifiBaseMenu(); + static void wifiToggleMenu(); }; } // namespace graphics \ No newline at end of file From 1994bb3cd193c2b3a107ad986bb0a9918841df52 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 4 Jul 2025 08:10:23 -0500 Subject: [PATCH 04/72] automated bumps (#7227) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- bin/org.meshtastic.meshtasticd.metainfo.xml | 3 +++ debian/changelog | 7 +++++-- version.properties | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/bin/org.meshtastic.meshtasticd.metainfo.xml b/bin/org.meshtastic.meshtasticd.metainfo.xml index ed57386a3..47082718a 100644 --- a/bin/org.meshtastic.meshtasticd.metainfo.xml +++ b/bin/org.meshtastic.meshtasticd.metainfo.xml @@ -87,6 +87,9 @@ + + https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.2 + https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.1 diff --git a/debian/changelog b/debian/changelog index 70a01bab4..42488692b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -meshtasticd (2.7.1.0) UNRELEASED; urgency=medium +meshtasticd (2.7.2.0) UNRELEASED; urgency=medium [ Austin Lane ] * Initial packaging @@ -25,4 +25,7 @@ meshtasticd (2.7.1.0) UNRELEASED; urgency=medium [ ] * GitHub Actions Automatic version bump - -- Fri, 27 Jun 2025 20:12:21 +0000 + [ ] + * GitHub Actions Automatic version bump + + -- Fri, 04 Jul 2025 11:58:01 +0000 diff --git a/version.properties b/version.properties index 3fe1aa385..69f2d6af5 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 7 -build = 1 +build = 2 From 29893e0c281c7266eeeffe956a5b1c367dfc9417 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 4 Jul 2025 14:22:59 -0500 Subject: [PATCH 05/72] Don't run ble getFromRadio() unless the phone has requested a packet (#7231) --- src/nimble/NimbleBluetooth.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/nimble/NimbleBluetooth.cpp b/src/nimble/NimbleBluetooth.cpp index 8f53c9229..834184292 100644 --- a/src/nimble/NimbleBluetooth.cpp +++ b/src/nimble/NimbleBluetooth.cpp @@ -29,6 +29,7 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread uint8_t fromRadioBytes[meshtastic_FromRadio_size] = {0}; size_t numBytes = 0; bool hasChecked = false; + bool phoneWants = false; protected: virtual int32_t runOnce() override @@ -38,10 +39,10 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread for (uint8_t i = 0; i < queue_size; i++) { handleToRadio(nimble_queue.at(i).data(), nimble_queue.at(i).length()); } - LOG_WARN("Queue_size %u", queue_size); + LOG_DEBUG("Queue_size %u", queue_size); queue_size = 0; } - if (hasChecked == false) { + if (hasChecked == false && phoneWants == true) { numBytes = getFromRadio(fromRadioBytes); hasChecked = true; } @@ -98,9 +99,12 @@ class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks { virtual void onRead(NimBLECharacteristic *pCharacteristic) { - while (!bluetoothPhoneAPI->hasChecked) { + int tries = 0; + bluetoothPhoneAPI->phoneWants = true; + while (!bluetoothPhoneAPI->hasChecked && tries < 100) { bluetoothPhoneAPI->setIntervalFromNow(0); delay(20); + tries++; } std::lock_guard guard(bluetoothPhoneAPI->nimble_mutex); std::string fromRadioByteString(bluetoothPhoneAPI->fromRadioBytes, @@ -111,6 +115,7 @@ class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks bluetoothPhoneAPI->setIntervalFromNow(0); bluetoothPhoneAPI->numBytes = 0; bluetoothPhoneAPI->hasChecked = false; + bluetoothPhoneAPI->phoneWants = false; } }; @@ -186,7 +191,12 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks new meshtastic::BluetoothStatus(meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED)); if (bluetoothPhoneAPI) { + std::lock_guard guard(bluetoothPhoneAPI->nimble_mutex); bluetoothPhoneAPI->close(); + bluetoothPhoneAPI->hasChecked = false; + bluetoothPhoneAPI->phoneWants = false; + bluetoothPhoneAPI->numBytes = 0; + bluetoothPhoneAPI->queue_size = 0; } } }; From 798b1f4d861e756ecd2324083726c098cfb7325e Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 5 Jul 2025 07:35:20 -0500 Subject: [PATCH 06/72] Add HWIDs for T1000-E in DFU mode (#7235) --- boards/tracker-t1000-e.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/boards/tracker-t1000-e.json b/boards/tracker-t1000-e.json index 2be716e22..9e8870041 100644 --- a/boards/tracker-t1000-e.json +++ b/boards/tracker-t1000-e.json @@ -11,7 +11,8 @@ ["0x239A", "0x8029"], ["0x239A", "0x0029"], ["0x239A", "0x002A"], - ["0x239A", "0x802A"] + ["0x239A", "0x802A"], + ["0x2886", "0x0057"] ], "usb_product": "T1000-E-BOOT", "mcu": "nrf52840", From 98d010761e5a8c0f5651974268adcb09ef2f6639 Mon Sep 17 00:00:00 2001 From: Aiden Fox Ivey Date: Fri, 20 Jun 2025 16:07:23 -0400 Subject: [PATCH 07/72] Add constant time compare to AES-CCM Signed-off-by: Aiden Fox Ivey --- src/mesh/aes-ccm.cpp | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/mesh/aes-ccm.cpp b/src/mesh/aes-ccm.cpp index a650ba2fc..a73c9473e 100644 --- a/src/mesh/aes-ccm.cpp +++ b/src/mesh/aes-ccm.cpp @@ -10,6 +10,31 @@ #include "aes-ccm.h" #if !MESHTASTIC_EXCLUDE_PKI +/** + * Constant-time comparison of two byte arrays + * + * @param a First byte array to compare + * @param b Second byte array to compare + * @param len Number of bytes to compare + * @return 0 if arrays are equal, 1 if different or if inputs are invalid + */ +static int constant_time_compare(const void *a_, const void *b_, size_t len) +{ + // Cast to volatile to prevent the compiler from optimizing out their comparison. + const volatile uint8_t *volatile a = (const volatile uint8_t *volatile) a_; + const volatile uint8_t *volatile b = (const volatile uint8_t *volatile) b_; + if (len == 0) + return 0; + if (a == NULL || b == NULL) + return 1; + size_t i; + volatile uint8_t d = 0U; + for (i = 0U; i < len; i++) { + d |= (a[i] ^ b[i]); + } + return (1 & ((d - 1) >> 8)) - 1; +} + static void WPA_PUT_BE16(uint8_t *a, uint16_t val) { a[0] = val >> 8; @@ -146,7 +171,7 @@ bool aes_ccm_ad(const uint8_t *key, size_t key_len, const uint8_t *nonce, size_t aes_ccm_encr(L, crypt, crypt_len, plain, a); aes_ccm_auth_start(M, L, nonce, aad, aad_len, crypt_len, x); aes_ccm_auth(plain, crypt_len, x); - if (memcmp(x, t, M) != 0) { // FIXME make const comp + if (constant_time_compare(x, t, M) != 0) { return false; } return true; From 13786572066c570d053a9669a59c1804c2ca7aa8 Mon Sep 17 00:00:00 2001 From: Aiden Fox Ivey Date: Sat, 21 Jun 2025 00:12:09 -0400 Subject: [PATCH 08/72] Fix documentation comments. --- src/mesh/aes-ccm.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/mesh/aes-ccm.cpp b/src/mesh/aes-ccm.cpp index a73c9473e..e1c65c7aa 100644 --- a/src/mesh/aes-ccm.cpp +++ b/src/mesh/aes-ccm.cpp @@ -16,22 +16,23 @@ * @param a First byte array to compare * @param b Second byte array to compare * @param len Number of bytes to compare - * @return 0 if arrays are equal, 1 if different or if inputs are invalid + * @return 0 if arrays are equal, -1 if different or if inputs are invalid */ static int constant_time_compare(const void *a_, const void *b_, size_t len) { - // Cast to volatile to prevent the compiler from optimizing out their comparison. + /* Cast to volatile to prevent the compiler from optimizing out their comparison. */ const volatile uint8_t *volatile a = (const volatile uint8_t *volatile) a_; const volatile uint8_t *volatile b = (const volatile uint8_t *volatile) b_; if (len == 0) return 0; if (a == NULL || b == NULL) - return 1; + return -1; size_t i; volatile uint8_t d = 0U; for (i = 0U; i < len; i++) { d |= (a[i] ^ b[i]); } + /* Constant time bit arithmetic to convert d > 0 to -1 and d = 0 to 0. */ return (1 & ((d - 1) >> 8)) - 1; } From 2a2620988928ceaf78f61b025529cf6d6eb13fea Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 5 Jul 2025 12:56:29 -0500 Subject: [PATCH 09/72] Trunk --- src/mesh/aes-ccm.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh/aes-ccm.cpp b/src/mesh/aes-ccm.cpp index e1c65c7aa..420d80e9a 100644 --- a/src/mesh/aes-ccm.cpp +++ b/src/mesh/aes-ccm.cpp @@ -21,8 +21,8 @@ static int constant_time_compare(const void *a_, const void *b_, size_t len) { /* Cast to volatile to prevent the compiler from optimizing out their comparison. */ - const volatile uint8_t *volatile a = (const volatile uint8_t *volatile) a_; - const volatile uint8_t *volatile b = (const volatile uint8_t *volatile) b_; + const volatile uint8_t *volatile a = (const volatile uint8_t *volatile)a_; + const volatile uint8_t *volatile b = (const volatile uint8_t *volatile)b_; if (len == 0) return 0; if (a == NULL || b == NULL) From 708978911ba64c97af25751e1b4927a771a5d84d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 5 Jul 2025 19:26:20 -0500 Subject: [PATCH 10/72] chore(deps): update meshtastic/device-ui digest to 8c7092c (#7238) 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 795f86eb9..5ba5e63e0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -109,7 +109,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/4b7bf369adfa5a7bd419fa8293d21206576d52d0.zip + https://github.com/meshtastic/device-ui/archive/8c7092c73425adfda1aac8c6960df06cd85f6d92.zip ; Common libs for environmental measurements in telemetry module [environmental_base] From 40c586ca97dfd10aeaeb73fc9b5d7ebf54fab60e Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 6 Jul 2025 16:36:22 -0500 Subject: [PATCH 11/72] Automatically bail user out of displaymode_color when not HAS_TFT (#7248) --- src/mesh/NodeDB.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 5630a4ea3..a20acfda0 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -369,6 +369,14 @@ NodeDB::NodeDB() config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY; } +#if !HAS_TFT + if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_COLOR) { + // On a device without MUI, this display mode makes no sense, and will break logic. + config.display.displaymode = meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT; + config.bluetooth.enabled = true; + } +#endif + if (devicestateCRC != crc32Buffer(&devicestate, sizeof(devicestate))) saveWhat |= SEGMENT_DEVICESTATE; if (nodeDatabaseCRC != crc32Buffer(&nodeDatabase, sizeof(nodeDatabase))) From 09d4ee1ea7ab6bc77b59ad943008ac831f8afdfc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 7 Jul 2025 06:37:15 -0500 Subject: [PATCH 12/72] Upgrade trunk (#7254) Co-authored-by: sachaw <11172820+sachaw@users.noreply.github.com> --- .trunk/trunk.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 2ddebdf1d..1dfff137a 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -9,14 +9,14 @@ plugins: lint: enabled: - checkov@3.2.447 - - renovate@41.17.2 + - renovate@41.19.0 - prettier@3.6.2 - trufflehog@3.89.2 - yamllint@1.37.1 - bandit@1.8.5 - - trivy@0.64.0 + - trivy@0.64.1 - taplo@0.9.3 - - ruff@0.12.1 + - ruff@0.12.2 - isort@6.0.1 - markdownlint@0.45.0 - oxipng@9.1.5 From f95c77b8bd8babd071e7cc2b36f0e3952bf4ed92 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 7 Jul 2025 15:50:13 +0300 Subject: [PATCH 13/72] Fast fix, remove saving tx power inside limitPower() (#7255) --- src/mesh/RadioInterface.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 3632378a5..faa67a1c2 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -645,10 +645,6 @@ void RadioInterface::limitPower(int8_t loraMaxPower) if (power > loraMaxPower) // Clamp power to maximum defined level power = loraMaxPower; - if (TX_GAIN_LORA == 0) { // Setting power in config with defined TX_GAIN_LORA will cause decreasing power on each reboot - config.lora.tx_power = power; // Set limited power in config - } - LOG_INFO("Final Tx power: %d dBm", power); } From f2fb473ecf1a0e0c19b12134c5250f76efdf252d Mon Sep 17 00:00:00 2001 From: Austin Date: Mon, 7 Jul 2025 20:34:25 -0400 Subject: [PATCH 14/72] GitHub Actions faster!! (#7244) Use new meshtastic/gh-action-firmware Action Co-authored-by: Ben Meadors --- .github/workflows/build_esp32.yml | 33 +++++++++++---------- .github/workflows/build_esp32_c3.yml | 33 +++++++++++---------- .github/workflows/build_esp32_c6.yml | 33 +++++++++++---------- .github/workflows/build_esp32_s3.yml | 33 +++++++++++---------- .github/workflows/build_nrf52.yml | 24 ++++++++++----- .github/workflows/build_rpi2040.yml | 22 ++++++++++---- .github/workflows/build_stm32.yml | 22 ++++++++++---- .github/workflows/main_matrix.yml | 1 + .github/workflows/nightly.yml | 2 ++ .github/workflows/sec_sast_semgrep_cron.yml | 1 + .github/workflows/stale_bot.yml | 1 + .github/workflows/tests.yml | 2 ++ bin/{build-rpi2040.sh => build-rp2xx0.sh} | 0 bin/{build-stm32.sh => build-stm32wl.sh} | 0 14 files changed, 127 insertions(+), 80 deletions(-) rename bin/{build-rpi2040.sh => build-rp2xx0.sh} (100%) rename bin/{build-stm32.sh => build-stm32wl.sh} (100%) diff --git a/.github/workflows/build_esp32.yml b/.github/workflows/build_esp32.yml index 616f51746..4ec5d12db 100644 --- a/.github/workflows/build_esp32.yml +++ b/.github/workflows/build_esp32.yml @@ -15,23 +15,26 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Get release version string + shell: bash + run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + - name: Build ESP32 id: build - uses: ./.github/actions/build-variant + uses: meshtastic/gh-action-firmware@main with: - github_token: ${{ secrets.GITHUB_TOKEN }} - board: ${{ inputs.board }} - remove-debug-flags: >- - ./arch/esp32/esp32.ini - ./arch/esp32/esp32s2.ini - ./arch/esp32/esp32s3.ini - ./arch/esp32/esp32c3.ini - ./arch/esp32/esp32c6.ini - build-script-path: bin/build-esp32.sh - ota-firmware-source: firmware.bin - ota-firmware-target: release/bleota.bin - artifact-paths: | + pio_platform: esp32 + pio_env: ${{ inputs.board }} + pio_target: build + ota_firmware_source: firmware.bin + ota_firmware_target: release/bleota.bin + + - name: Store binaries as an artifact + uses: actions/upload-artifact@v4 + with: + name: firmware-esp32-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip + overwrite: true + path: | release/*.bin release/*.elf - #include-web-ui: true - arch: esp32 diff --git a/.github/workflows/build_esp32_c3.yml b/.github/workflows/build_esp32_c3.yml index 1b6b832e9..335acaa2e 100644 --- a/.github/workflows/build_esp32_c3.yml +++ b/.github/workflows/build_esp32_c3.yml @@ -15,23 +15,26 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Get release version string + shell: bash + run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + - name: Build ESP32-C3 id: build - uses: ./.github/actions/build-variant + uses: meshtastic/gh-action-firmware@main with: - github_token: ${{ secrets.GITHUB_TOKEN }} - board: ${{ inputs.board }} - remove-debug-flags: >- - ./arch/esp32/esp32.ini - ./arch/esp32/esp32s2.ini - ./arch/esp32/esp32s3.ini - ./arch/esp32/esp32c3.ini - ./arch/esp32/esp32c6.ini - build-script-path: bin/build-esp32.sh - ota-firmware-source: firmware-c3.bin - ota-firmware-target: release/bleota-c3.bin - artifact-paths: | + pio_platform: esp32 + pio_env: ${{ inputs.board }} + pio_target: build + ota_firmware_source: firmware-c3.bin + ota_firmware_target: release/bleota-c3.bin + + - name: Store binaries as an artifact + uses: actions/upload-artifact@v4 + with: + name: firmware-esp32c3-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip + overwrite: true + path: | release/*.bin release/*.elf - #include-web-ui: true - arch: esp32c3 diff --git a/.github/workflows/build_esp32_c6.yml b/.github/workflows/build_esp32_c6.yml index 29dac51e1..6ab588dde 100644 --- a/.github/workflows/build_esp32_c6.yml +++ b/.github/workflows/build_esp32_c6.yml @@ -15,23 +15,26 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Get release version string + shell: bash + run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + - name: Build ESP32-C6 id: build - uses: ./.github/actions/build-variant + uses: meshtastic/gh-action-firmware@main with: - github_token: ${{ secrets.GITHUB_TOKEN }} - board: ${{ inputs.board }} - remove-debug-flags: >- - ./arch/esp32/esp32.ini - ./arch/esp32/esp32s2.ini - ./arch/esp32/esp32s3.ini - ./arch/esp32/esp32c3.ini - ./arch/esp32/esp32c6.ini - build-script-path: bin/build-esp32.sh - ota-firmware-source: firmware-c3.bin - ota-firmware-target: release/bleota-c3.bin - artifact-paths: | + pio_platform: esp32 + pio_env: ${{ inputs.board }} + pio_target: build + ota_firmware_source: firmware-c3.bin + ota_firmware_target: release/bleota-c3.bin + + - name: Store binaries as an artifact + uses: actions/upload-artifact@v4 + with: + name: firmware-esp32c6-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip + overwrite: true + path: | release/*.bin release/*.elf - #include-web-ui: true - arch: esp32c6 diff --git a/.github/workflows/build_esp32_s3.yml b/.github/workflows/build_esp32_s3.yml index 7e0373503..dba0d7999 100644 --- a/.github/workflows/build_esp32_s3.yml +++ b/.github/workflows/build_esp32_s3.yml @@ -15,23 +15,26 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Get release version string + shell: bash + run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + - name: Build ESP32-S3 id: build - uses: ./.github/actions/build-variant + uses: meshtastic/gh-action-firmware@main with: - github_token: ${{ secrets.GITHUB_TOKEN }} - board: ${{ inputs.board }} - remove-debug-flags: >- - ./arch/esp32/esp32.ini - ./arch/esp32/esp32s2.ini - ./arch/esp32/esp32s3.ini - ./arch/esp32/esp32c3.ini - ./arch/esp32/esp32c6.ini - build-script-path: bin/build-esp32.sh - ota-firmware-source: firmware-s3.bin - ota-firmware-target: release/bleota-s3.bin - artifact-paths: | + pio_platform: esp32 + pio_env: ${{ inputs.board }} + pio_target: build + ota_firmware_source: firmware-s3.bin + ota_firmware_target: release/bleota-s3.bin + + - name: Store binaries as an artifact + uses: actions/upload-artifact@v4 + with: + name: firmware-esp32s3-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip + overwrite: true + path: | release/*.bin release/*.elf - #include-web-ui: true - arch: esp32s3 diff --git a/.github/workflows/build_nrf52.yml b/.github/workflows/build_nrf52.yml index 786508f86..bafaf2fb2 100644 --- a/.github/workflows/build_nrf52.yml +++ b/.github/workflows/build_nrf52.yml @@ -15,16 +15,24 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Get release version string + shell: bash + run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + - name: Build NRF52 id: build - uses: ./.github/actions/build-variant + uses: meshtastic/gh-action-firmware@main with: - github_token: ${{ secrets.GITHUB_TOKEN }} - board: ${{ inputs.board }} - build-script-path: bin/build-nrf52.sh - artifact-paths: | - release/*.hex + pio_platform: nrf52 + pio_env: ${{ inputs.board }} + pio_target: build + + - name: Store binaries as an artifact + uses: actions/upload-artifact@v4 + with: + name: firmware-nrf52840-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip + overwrite: true + path: | release/*.uf2 release/*.elf - release/*.zip - arch: nrf52840 diff --git a/.github/workflows/build_rpi2040.yml b/.github/workflows/build_rpi2040.yml index 53fee34d2..2aa0c477a 100644 --- a/.github/workflows/build_rpi2040.yml +++ b/.github/workflows/build_rpi2040.yml @@ -15,14 +15,24 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Get release version string + shell: bash + run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + - name: Build Raspberry Pi 2040 id: build - uses: ./.github/actions/build-variant + uses: meshtastic/gh-action-firmware@main with: - github_token: ${{ secrets.GITHUB_TOKEN }} - board: ${{ inputs.board }} - build-script-path: bin/build-rpi2040.sh - artifact-paths: | + pio_platform: rp2xx0 + pio_env: ${{ inputs.board }} + pio_target: build + + - name: Store binaries as an artifact + uses: actions/upload-artifact@v4 + with: + name: firmware-rp2040-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip + overwrite: true + path: | release/*.uf2 release/*.elf - arch: rp2040 diff --git a/.github/workflows/build_stm32.yml b/.github/workflows/build_stm32.yml index dc469d994..dd14d9d0f 100644 --- a/.github/workflows/build_stm32.yml +++ b/.github/workflows/build_stm32.yml @@ -15,15 +15,25 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Get release version string + shell: bash + run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + - name: Build STM32WL id: build - uses: ./.github/actions/build-variant + uses: meshtastic/gh-action-firmware@main with: - github_token: ${{ secrets.GITHUB_TOKEN }} - board: ${{ inputs.board }} - build-script-path: bin/build-stm32.sh - artifact-paths: | + pio_platform: stm32wl + pio_env: ${{ inputs.board }} + pio_target: build + + - name: Store binaries as an artifact + uses: actions/upload-artifact@v4 + with: + name: firmware-stm32-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip + overwrite: true + path: | release/*.hex release/*.bin release/*.elf - arch: stm32 diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index 03e61d572..a6112e0e4 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -135,6 +135,7 @@ jobs: board: ${{ matrix.board }} build-debian-src: + if: github.repository == 'meshtastic/firmware' uses: ./.github/workflows/build_debian_src.yml with: series: UNRELEASED diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 36ec22f17..309772b12 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -8,6 +8,7 @@ permissions: read-all jobs: trunk_check: + if: github.repository == 'meshtastic/firmware' name: Trunk Check and Upload runs-on: ubuntu-24.04 @@ -21,6 +22,7 @@ jobs: trunk-token: ${{ secrets.TRUNK_TOKEN }} trunk_upgrade: + if: github.repository == 'meshtastic/firmware' # See: https://github.com/trunk-io/trunk-action/blob/v1/readme.md#automatic-upgrades name: Trunk Upgrade (PR) runs-on: ubuntu-24.04 diff --git a/.github/workflows/sec_sast_semgrep_cron.yml b/.github/workflows/sec_sast_semgrep_cron.yml index d7eef29b4..e391aa07b 100644 --- a/.github/workflows/sec_sast_semgrep_cron.yml +++ b/.github/workflows/sec_sast_semgrep_cron.yml @@ -13,6 +13,7 @@ permissions: jobs: semgrep-full: + if: github.repository == 'meshtastic/firmware' runs-on: ubuntu-24.04 container: image: semgrep/semgrep diff --git a/.github/workflows/stale_bot.yml b/.github/workflows/stale_bot.yml index 5ae6bdfc9..5a11fdfa8 100644 --- a/.github/workflows/stale_bot.yml +++ b/.github/workflows/stale_bot.yml @@ -11,6 +11,7 @@ permissions: jobs: stale_issues: + if: github.repository == 'meshtastic/firmware' name: Close Stale Issues runs-on: ubuntu-latest diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 28b6a40a5..34b28b39c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,9 +12,11 @@ permissions: jobs: native-tests: + if: github.repository == 'meshtastic/firmware' uses: ./.github/workflows/test_native.yml hardware-tests: + if: github.repository == 'meshtastic/firmware' runs-on: test-runner steps: - name: Checkout code diff --git a/bin/build-rpi2040.sh b/bin/build-rp2xx0.sh similarity index 100% rename from bin/build-rpi2040.sh rename to bin/build-rp2xx0.sh diff --git a/bin/build-stm32.sh b/bin/build-stm32wl.sh similarity index 100% rename from bin/build-stm32.sh rename to bin/build-stm32wl.sh From 415dc4aa471640cd4e55967e4381fb62ff59203d Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 7 Jul 2025 19:35:57 -0500 Subject: [PATCH 15/72] Try-fix: L76K spamming bad times can crash nodes (#7261) * Try-fix: Clear GPS buffer when we encounter a bad time in NMEA * Fix signed int warnings --- src/gps/GPS.cpp | 5 ++++- src/gps/RTC.cpp | 12 ++++++------ src/gps/RTC.h | 13 +++++++++++-- src/graphics/draw/NotificationRenderer.cpp | 9 ++++++--- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 142241c43..345c738d6 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -1536,7 +1536,10 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s if (t.tm_mon > -1) { LOG_DEBUG("NMEA GPS time %02d-%02d-%02d %02d:%02d:%02d age %d", d.year(), d.month(), t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, ti.age()); - perhapsSetRTC(RTCQualityGPS, t); + if (perhapsSetRTC(RTCQualityGPS, t) == RTCSetResultInvalidTime) { + // Clear the GPS buffer if we got an invalid time + clearBuffer(); + } return true; } else return false; diff --git a/src/gps/RTC.cpp b/src/gps/RTC.cpp index 219a593e0..5054be3f0 100644 --- a/src/gps/RTC.cpp +++ b/src/gps/RTC.cpp @@ -105,7 +105,7 @@ void readFromRTC() * * If we haven't yet set our RTC this boot, set it from a GPS derived time */ -bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate) +RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate) { static uint32_t lastSetMsec = 0; uint32_t now = millis(); @@ -113,7 +113,7 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate) #ifdef BUILD_EPOCH if (tv->tv_sec < BUILD_EPOCH) { LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH); - return false; + return RTCSetResultInvalidTime; } #endif @@ -184,9 +184,9 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate) readFromRTC(); #endif - return true; + return RTCSetResultSuccess; } else { - return false; + return RTCSetResultNotSet; // RTC was already set with a higher quality time } } @@ -215,7 +215,7 @@ const char *RtcName(RTCQuality quality) * @param t The time to potentially set the RTC to. * @return True if the RTC was set to the provided time, false otherwise. */ -bool perhapsSetRTC(RTCQuality q, struct tm &t) +RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t) { /* Convert to unix time The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970 @@ -231,7 +231,7 @@ bool perhapsSetRTC(RTCQuality q, struct tm &t) // LOG_DEBUG("Got time from GPS month=%d, year=%d, unixtime=%ld", t.tm_mon, t.tm_year, tv.tv_sec); if (t.tm_year < 0 || t.tm_year >= 300) { // LOG_DEBUG("Ignore invalid GPS month=%d, year=%d, unixtime=%ld", t.tm_mon, t.tm_year, tv.tv_sec); - return false; + return RTCSetResultInvalidTime; } else { return perhapsSetRTC(q, &tv); } diff --git a/src/gps/RTC.h b/src/gps/RTC.h index caa48dc06..96dec575b 100644 --- a/src/gps/RTC.h +++ b/src/gps/RTC.h @@ -22,13 +22,22 @@ enum RTCQuality { RTCQualityGPS = 4 }; +/// The RTC set result codes +/// Used to indicate the result of an attempt to set the RTC. +enum RTCSetResult { + RTCSetResultNotSet = 0, ///< RTC was set successfully + RTCSetResultSuccess = 1, ///< RTC was set successfully + RTCSetResultInvalidTime = 3, ///< The provided time was invalid (e.g., before the build epoch) + RTCSetResultError = 4 ///< An error occurred while setting the RTC +}; + RTCQuality getRTCQuality(); extern uint32_t lastSetFromPhoneNtpOrGps; /// If we haven't yet set our RTC this boot, set it from a GPS derived time -bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate = false); -bool perhapsSetRTC(RTCQuality q, struct tm &t); +RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate = false); +RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t); /// Return a string name for the quality const char *RtcName(RTCQuality quality); diff --git a/src/graphics/draw/NotificationRenderer.cpp b/src/graphics/draw/NotificationRenderer.cpp index 3b682cc55..057c91008 100644 --- a/src/graphics/draw/NotificationRenderer.cpp +++ b/src/graphics/draw/NotificationRenderer.cpp @@ -42,7 +42,7 @@ uint32_t NotificationRenderer::currentNumber = 0; uint32_t pow_of_10(uint32_t n) { uint32_t ret = 1; - for (int i = 0; i < n; i++) { + for (uint32_t i = 0; i < n; i++) { ret *= 10; } return ret; @@ -80,6 +80,9 @@ void NotificationRenderer::drawBannercallback(OLEDDisplay *display, OLEDDisplayU if (!isOverlayBannerShowing() || pauseBanner) return; switch (current_notification_type) { + case notificationTypeEnum::none: + // Do nothing - no notification to display + break; case notificationTypeEnum::text_banner: case notificationTypeEnum::selection_picker: drawAlertBannerOverlay(display, state); @@ -144,12 +147,12 @@ void NotificationRenderer::drawNumberPicker(OLEDDisplay *display, OLEDDisplayUiS const char *linePointers[totalLines + 1] = {0}; // this is sort of a dynamic allocation // copy the linestarts to display to the linePointers holder - for (int i = 0; i < lineCount; i++) { + for (uint16_t i = 0; i < lineCount; i++) { linePointers[i] = lineStarts[i]; } std::string digits = " "; std::string arrowPointer = " "; - for (int i = 0; i < numDigits; i++) { + for (uint16_t i = 0; i < numDigits; i++) { // Modulo minus modulo to return just the current number digits += std::to_string((currentNumber % (pow_of_10(numDigits - i))) / (pow_of_10(numDigits - i - 1))) + " "; if (curSelected == i) { From e1f40c2db91b753831be2f29ce074274bd029f6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ing=2E=20Jan=20Kal=C3=A1b?= Date: Tue, 8 Jul 2025 02:36:21 +0200 Subject: [PATCH 16/72] Fix install script (#7259) This partially reverse 2ab717c (#7143), fixing the install script. It looks like a bad/missed copy/paste. --- bin/device-install.sh | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/bin/device-install.sh b/bin/device-install.sh index 42d0c4089..4674113b6 100755 --- a/bin/device-install.sh +++ b/bin/device-install.sh @@ -7,12 +7,7 @@ MCU="" # Variant groups BIGDB_8MB=( - # Check if FILENAME contains "-tft-" and set target partitionScheme accordingly. -if [[ $FILENAME == *"-tft-"* ]]; then - TFT_BUILD=true -fi - -# Extract BASENAME from %FILENAME% for later use.r-s3" + "picomputer-s3" "unphone" "seeed-sensecap-indicator" "crowpanel-esp32s3" From fa23be442444497c1aa729faa90cea9ae9c3ecde Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 7 Jul 2025 19:50:44 -0500 Subject: [PATCH 17/72] Revert "GitHub Actions faster!! (#7244)" (#7262) This reverts commit f2fb473ecf1a0e0c19b12134c5250f76efdf252d. --- .github/workflows/build_esp32.yml | 33 ++++++++++----------- .github/workflows/build_esp32_c3.yml | 33 ++++++++++----------- .github/workflows/build_esp32_c6.yml | 33 ++++++++++----------- .github/workflows/build_esp32_s3.yml | 33 ++++++++++----------- .github/workflows/build_nrf52.yml | 24 +++++---------- .github/workflows/build_rpi2040.yml | 22 ++++---------- .github/workflows/build_stm32.yml | 22 ++++---------- .github/workflows/main_matrix.yml | 1 - .github/workflows/nightly.yml | 2 -- .github/workflows/sec_sast_semgrep_cron.yml | 1 - .github/workflows/stale_bot.yml | 1 - .github/workflows/tests.yml | 2 -- bin/{build-rp2xx0.sh => build-rpi2040.sh} | 0 bin/{build-stm32wl.sh => build-stm32.sh} | 0 14 files changed, 80 insertions(+), 127 deletions(-) rename bin/{build-rp2xx0.sh => build-rpi2040.sh} (100%) rename bin/{build-stm32wl.sh => build-stm32.sh} (100%) diff --git a/.github/workflows/build_esp32.yml b/.github/workflows/build_esp32.yml index 4ec5d12db..616f51746 100644 --- a/.github/workflows/build_esp32.yml +++ b/.github/workflows/build_esp32.yml @@ -15,26 +15,23 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Get release version string - shell: bash - run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - name: Build ESP32 id: build - uses: meshtastic/gh-action-firmware@main + uses: ./.github/actions/build-variant with: - pio_platform: esp32 - pio_env: ${{ inputs.board }} - pio_target: build - ota_firmware_source: firmware.bin - ota_firmware_target: release/bleota.bin - - - name: Store binaries as an artifact - uses: actions/upload-artifact@v4 - with: - name: firmware-esp32-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip - overwrite: true - path: | + github_token: ${{ secrets.GITHUB_TOKEN }} + board: ${{ inputs.board }} + remove-debug-flags: >- + ./arch/esp32/esp32.ini + ./arch/esp32/esp32s2.ini + ./arch/esp32/esp32s3.ini + ./arch/esp32/esp32c3.ini + ./arch/esp32/esp32c6.ini + build-script-path: bin/build-esp32.sh + ota-firmware-source: firmware.bin + ota-firmware-target: release/bleota.bin + artifact-paths: | release/*.bin release/*.elf + #include-web-ui: true + arch: esp32 diff --git a/.github/workflows/build_esp32_c3.yml b/.github/workflows/build_esp32_c3.yml index 335acaa2e..1b6b832e9 100644 --- a/.github/workflows/build_esp32_c3.yml +++ b/.github/workflows/build_esp32_c3.yml @@ -15,26 +15,23 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Get release version string - shell: bash - run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - name: Build ESP32-C3 id: build - uses: meshtastic/gh-action-firmware@main + uses: ./.github/actions/build-variant with: - pio_platform: esp32 - pio_env: ${{ inputs.board }} - pio_target: build - ota_firmware_source: firmware-c3.bin - ota_firmware_target: release/bleota-c3.bin - - - name: Store binaries as an artifact - uses: actions/upload-artifact@v4 - with: - name: firmware-esp32c3-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip - overwrite: true - path: | + github_token: ${{ secrets.GITHUB_TOKEN }} + board: ${{ inputs.board }} + remove-debug-flags: >- + ./arch/esp32/esp32.ini + ./arch/esp32/esp32s2.ini + ./arch/esp32/esp32s3.ini + ./arch/esp32/esp32c3.ini + ./arch/esp32/esp32c6.ini + build-script-path: bin/build-esp32.sh + ota-firmware-source: firmware-c3.bin + ota-firmware-target: release/bleota-c3.bin + artifact-paths: | release/*.bin release/*.elf + #include-web-ui: true + arch: esp32c3 diff --git a/.github/workflows/build_esp32_c6.yml b/.github/workflows/build_esp32_c6.yml index 6ab588dde..29dac51e1 100644 --- a/.github/workflows/build_esp32_c6.yml +++ b/.github/workflows/build_esp32_c6.yml @@ -15,26 +15,23 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Get release version string - shell: bash - run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - name: Build ESP32-C6 id: build - uses: meshtastic/gh-action-firmware@main + uses: ./.github/actions/build-variant with: - pio_platform: esp32 - pio_env: ${{ inputs.board }} - pio_target: build - ota_firmware_source: firmware-c3.bin - ota_firmware_target: release/bleota-c3.bin - - - name: Store binaries as an artifact - uses: actions/upload-artifact@v4 - with: - name: firmware-esp32c6-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip - overwrite: true - path: | + github_token: ${{ secrets.GITHUB_TOKEN }} + board: ${{ inputs.board }} + remove-debug-flags: >- + ./arch/esp32/esp32.ini + ./arch/esp32/esp32s2.ini + ./arch/esp32/esp32s3.ini + ./arch/esp32/esp32c3.ini + ./arch/esp32/esp32c6.ini + build-script-path: bin/build-esp32.sh + ota-firmware-source: firmware-c3.bin + ota-firmware-target: release/bleota-c3.bin + artifact-paths: | release/*.bin release/*.elf + #include-web-ui: true + arch: esp32c6 diff --git a/.github/workflows/build_esp32_s3.yml b/.github/workflows/build_esp32_s3.yml index dba0d7999..7e0373503 100644 --- a/.github/workflows/build_esp32_s3.yml +++ b/.github/workflows/build_esp32_s3.yml @@ -15,26 +15,23 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Get release version string - shell: bash - run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - name: Build ESP32-S3 id: build - uses: meshtastic/gh-action-firmware@main + uses: ./.github/actions/build-variant with: - pio_platform: esp32 - pio_env: ${{ inputs.board }} - pio_target: build - ota_firmware_source: firmware-s3.bin - ota_firmware_target: release/bleota-s3.bin - - - name: Store binaries as an artifact - uses: actions/upload-artifact@v4 - with: - name: firmware-esp32s3-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip - overwrite: true - path: | + github_token: ${{ secrets.GITHUB_TOKEN }} + board: ${{ inputs.board }} + remove-debug-flags: >- + ./arch/esp32/esp32.ini + ./arch/esp32/esp32s2.ini + ./arch/esp32/esp32s3.ini + ./arch/esp32/esp32c3.ini + ./arch/esp32/esp32c6.ini + build-script-path: bin/build-esp32.sh + ota-firmware-source: firmware-s3.bin + ota-firmware-target: release/bleota-s3.bin + artifact-paths: | release/*.bin release/*.elf + #include-web-ui: true + arch: esp32s3 diff --git a/.github/workflows/build_nrf52.yml b/.github/workflows/build_nrf52.yml index bafaf2fb2..786508f86 100644 --- a/.github/workflows/build_nrf52.yml +++ b/.github/workflows/build_nrf52.yml @@ -15,24 +15,16 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Get release version string - shell: bash - run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - name: Build NRF52 id: build - uses: meshtastic/gh-action-firmware@main + uses: ./.github/actions/build-variant with: - pio_platform: nrf52 - pio_env: ${{ inputs.board }} - pio_target: build - - - name: Store binaries as an artifact - uses: actions/upload-artifact@v4 - with: - name: firmware-nrf52840-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip - overwrite: true - path: | + github_token: ${{ secrets.GITHUB_TOKEN }} + board: ${{ inputs.board }} + build-script-path: bin/build-nrf52.sh + artifact-paths: | + release/*.hex release/*.uf2 release/*.elf + release/*.zip + arch: nrf52840 diff --git a/.github/workflows/build_rpi2040.yml b/.github/workflows/build_rpi2040.yml index 2aa0c477a..53fee34d2 100644 --- a/.github/workflows/build_rpi2040.yml +++ b/.github/workflows/build_rpi2040.yml @@ -15,24 +15,14 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Get release version string - shell: bash - run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - name: Build Raspberry Pi 2040 id: build - uses: meshtastic/gh-action-firmware@main + uses: ./.github/actions/build-variant with: - pio_platform: rp2xx0 - pio_env: ${{ inputs.board }} - pio_target: build - - - name: Store binaries as an artifact - uses: actions/upload-artifact@v4 - with: - name: firmware-rp2040-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip - overwrite: true - path: | + github_token: ${{ secrets.GITHUB_TOKEN }} + board: ${{ inputs.board }} + build-script-path: bin/build-rpi2040.sh + artifact-paths: | release/*.uf2 release/*.elf + arch: rp2040 diff --git a/.github/workflows/build_stm32.yml b/.github/workflows/build_stm32.yml index dd14d9d0f..dc469d994 100644 --- a/.github/workflows/build_stm32.yml +++ b/.github/workflows/build_stm32.yml @@ -15,25 +15,15 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Get release version string - shell: bash - run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT - id: version - - name: Build STM32WL id: build - uses: meshtastic/gh-action-firmware@main + uses: ./.github/actions/build-variant with: - pio_platform: stm32wl - pio_env: ${{ inputs.board }} - pio_target: build - - - name: Store binaries as an artifact - uses: actions/upload-artifact@v4 - with: - name: firmware-stm32-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip - overwrite: true - path: | + github_token: ${{ secrets.GITHUB_TOKEN }} + board: ${{ inputs.board }} + build-script-path: bin/build-stm32.sh + artifact-paths: | release/*.hex release/*.bin release/*.elf + arch: stm32 diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index a6112e0e4..03e61d572 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -135,7 +135,6 @@ jobs: board: ${{ matrix.board }} build-debian-src: - if: github.repository == 'meshtastic/firmware' uses: ./.github/workflows/build_debian_src.yml with: series: UNRELEASED diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 309772b12..36ec22f17 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -8,7 +8,6 @@ permissions: read-all jobs: trunk_check: - if: github.repository == 'meshtastic/firmware' name: Trunk Check and Upload runs-on: ubuntu-24.04 @@ -22,7 +21,6 @@ jobs: trunk-token: ${{ secrets.TRUNK_TOKEN }} trunk_upgrade: - if: github.repository == 'meshtastic/firmware' # See: https://github.com/trunk-io/trunk-action/blob/v1/readme.md#automatic-upgrades name: Trunk Upgrade (PR) runs-on: ubuntu-24.04 diff --git a/.github/workflows/sec_sast_semgrep_cron.yml b/.github/workflows/sec_sast_semgrep_cron.yml index e391aa07b..d7eef29b4 100644 --- a/.github/workflows/sec_sast_semgrep_cron.yml +++ b/.github/workflows/sec_sast_semgrep_cron.yml @@ -13,7 +13,6 @@ permissions: jobs: semgrep-full: - if: github.repository == 'meshtastic/firmware' runs-on: ubuntu-24.04 container: image: semgrep/semgrep diff --git a/.github/workflows/stale_bot.yml b/.github/workflows/stale_bot.yml index 5a11fdfa8..5ae6bdfc9 100644 --- a/.github/workflows/stale_bot.yml +++ b/.github/workflows/stale_bot.yml @@ -11,7 +11,6 @@ permissions: jobs: stale_issues: - if: github.repository == 'meshtastic/firmware' name: Close Stale Issues runs-on: ubuntu-latest diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 34b28b39c..28b6a40a5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,11 +12,9 @@ permissions: jobs: native-tests: - if: github.repository == 'meshtastic/firmware' uses: ./.github/workflows/test_native.yml hardware-tests: - if: github.repository == 'meshtastic/firmware' runs-on: test-runner steps: - name: Checkout code diff --git a/bin/build-rp2xx0.sh b/bin/build-rpi2040.sh similarity index 100% rename from bin/build-rp2xx0.sh rename to bin/build-rpi2040.sh diff --git a/bin/build-stm32wl.sh b/bin/build-stm32.sh similarity index 100% rename from bin/build-stm32wl.sh rename to bin/build-stm32.sh From 19af2d9e3b1b3ea7912cc04b03052c3686be8ff8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 8 Jul 2025 06:22:24 -0500 Subject: [PATCH 18/72] Upgrade trunk (#7266) Co-authored-by: sachaw <11172820+sachaw@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 1dfff137a..0986e6eb0 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -9,11 +9,11 @@ plugins: lint: enabled: - checkov@3.2.447 - - renovate@41.19.0 + - renovate@41.23.4 - prettier@3.6.2 - trufflehog@3.89.2 - yamllint@1.37.1 - - bandit@1.8.5 + - bandit@1.8.6 - trivy@0.64.1 - taplo@0.9.3 - ruff@0.12.2 From 88b299dd416480a0ccdde8a1dccf23a3a065e9a3 Mon Sep 17 00:00:00 2001 From: HarukiToreda <116696711+HarukiToreda@users.noreply.github.com> Date: Tue, 8 Jul 2025 07:22:57 -0400 Subject: [PATCH 19/72] Modules and favorite screen fix (#7264) * T-watch screen misalignment fix * Trunk fix * Fix for favorite frame when module screen is enabled --- src/graphics/Screen.cpp | 49 +++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index ed8119738..d8a60f1f4 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -944,22 +944,6 @@ void Screen::setFrames(FrameFocus focus) indicatorIcons.push_back(digital_icon_clock); #endif - // We don't show the node info of our node (if we have it yet - we should) - size_t numMeshNodes = nodeDB->getNumMeshNodes(); - if (numMeshNodes > 0) - numMeshNodes--; - - for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) { - const meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i); - if (n && n->num != nodeDB->getNodeNum() && n->is_favorite) { - if (fsi.positions.firstFavorite == 255) - fsi.positions.firstFavorite = numframes; - fsi.positions.lastFavorite = numframes; - normalFrames[numframes++] = graphics::UIRenderer::drawNodeInfo; - indicatorIcons.push_back(icon_node); - } - } - #if HAS_WIFI && !defined(ARCH_PORTDUINO) if (!dismissedFrames.wifi && isWifiAvailable()) { fsi.positions.wifi = numframes; @@ -969,7 +953,7 @@ void Screen::setFrames(FrameFocus focus) #endif // Beware of what changes you make in this code! - // We pass numfames into GetMeshModulesWithUIFrames() which is highly important! + // We pass numframes into GetMeshModulesWithUIFrames() which is highly important! // Inside of that callback, goes over to MeshModule.cpp and we run // modulesWithUIFrames.resize(startIndex, nullptr), to insert nullptr // entries until we're ready to start building the matching entries. @@ -998,6 +982,34 @@ void Screen::setFrames(FrameFocus focus) LOG_DEBUG("Added modules. numframes: %d", numframes); + // We don't show the node info of our node (if we have it yet - we should) + size_t numMeshNodes = nodeDB->getNumMeshNodes(); + if (numMeshNodes > 0) + numMeshNodes--; + + // Temporary array to hold favorite node frames + std::vector favoriteFrames; + + for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) { + const meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i); + if (n && n->num != nodeDB->getNodeNum() && n->is_favorite) { + favoriteFrames.push_back(graphics::UIRenderer::drawNodeInfo); + } + } + + // Insert favorite frames *after* collecting them all + if (!favoriteFrames.empty()) { + fsi.positions.firstFavorite = numframes; + for (auto &f : favoriteFrames) { + normalFrames[numframes++] = f; + indicatorIcons.push_back(icon_node); + } + fsi.positions.lastFavorite = numframes - 1; + } else { + fsi.positions.firstFavorite = 255; + fsi.positions.lastFavorite = 255; + } + fsi.frameCount = numframes; // Total framecount is used to apply FOCUS_PRESERVE this->frameCount = numframes; // ✅ Save frame count for use in custom overlay LOG_DEBUG("Finished build frames. numframes: %d", numframes); @@ -1009,8 +1021,7 @@ void Screen::setFrames(FrameFocus focus) static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback}; ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0])); - prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list - // just changed) + prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list just changed) // Focus on a specific frame, in the frame set we just created switch (focus) { From 9c08220d247011fc30aa6a02dd73158ac9c8d31f Mon Sep 17 00:00:00 2001 From: Jason P Date: Tue, 8 Jul 2025 06:24:12 -0500 Subject: [PATCH 20/72] TFT_MESH Fixes Across Various Devices (#7247) * Rename "r,g,b" variables to having a TFT_MESH_ prefix * Reboot and then user options * Restore TFT_MESH on any ST7789Spi driver --- src/graphics/Screen.cpp | 14 +++---- src/graphics/draw/MenuHandler.cpp | 68 +++++++++++++++---------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index d8a60f1f4..5d33feb4d 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -294,13 +294,13 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O LOG_INFO("Protobuf Value uiconfig.screen_rgb_color: %d", uiconfig.screen_rgb_color); int32_t rawRGB = uiconfig.screen_rgb_color; if (rawRGB > 0 && rawRGB <= 255255255) { - uint8_t r = (rawRGB >> 16) & 0xFF; - uint8_t g = (rawRGB >> 8) & 0xFF; - uint8_t b = rawRGB & 0xFF; - LOG_INFO("Values of r,g,b: %d, %d, %d", r, g, b); + uint8_t TFT_MESH_r = (rawRGB >> 16) & 0xFF; + uint8_t TFT_MESH_g = (rawRGB >> 8) & 0xFF; + uint8_t TFT_MESH_b = rawRGB & 0xFF; + LOG_INFO("Values of r,g,b: %d, %d, %d", TFT_MESH_r, TFT_MESH_g, TFT_MESH_b); - if (r <= 255 && g <= 255 && b <= 255) { - TFT_MESH = COLOR565(r, g, b); + if (TFT_MESH_r <= 255 && TFT_MESH_g <= 255 && TFT_MESH_b <= 255) { + TFT_MESH = COLOR565(TFT_MESH_r, TFT_MESH_g, TFT_MESH_b); } } @@ -313,8 +313,8 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O ST7789_MISO, ST7789_SCK); #else dispdev = new ST7789Spi(&SPI1, ST7789_RESET, ST7789_RS, ST7789_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT); - static_cast(dispdev)->setRGB(TFT_MESH); #endif + static_cast(dispdev)->setRGB(TFT_MESH); #elif defined(USE_SSD1306) dispdev = new SSD1306Wire(address.address, -1, -1, geometry, (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE); diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index 43c226896..a66ccd983 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -358,6 +358,9 @@ void menuHandler::systemBaseMenu() static int optionsEnumArray[7] = {Back}; int options = 1; + optionsArray[options] = "Reboot"; + optionsEnumArray[options++] = Reboot; + optionsArray[options] = "Beeps Action"; optionsEnumArray[options++] = Beeps; @@ -366,9 +369,6 @@ void menuHandler::systemBaseMenu() optionsEnumArray[options++] = Brightness; } - optionsArray[options] = "Reboot"; - optionsEnumArray[options++] = Reboot; - #if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT optionsArray[options] = "Screen Color"; optionsEnumArray[options++] = Color; @@ -677,52 +677,52 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display) bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsCount = 10; bannerOptions.bannerCallback = [display](int selected) -> void { - uint8_t r = 0; - uint8_t g = 0; - uint8_t b = 0; + uint8_t TFT_MESH_r = 0; + uint8_t TFT_MESH_g = 0; + uint8_t TFT_MESH_b = 0; if (selected == 1) { LOG_INFO("Setting color to system default or defined variant"); // Given just before we set all these to zero, we will allow this to go through } else if (selected == 2) { LOG_INFO("Setting color to Meshtastic Green"); - r = 103; - g = 234; - b = 148; + TFT_MESH_r = 103; + TFT_MESH_g = 234; + TFT_MESH_b = 148; } else if (selected == 3) { LOG_INFO("Setting color to Yellow"); - r = 255; - g = 255; - b = 128; + TFT_MESH_r = 255; + TFT_MESH_g = 255; + TFT_MESH_b = 128; } else if (selected == 4) { LOG_INFO("Setting color to Red"); - r = 255; - g = 64; - b = 64; + TFT_MESH_r = 255; + TFT_MESH_g = 64; + TFT_MESH_b = 64; } else if (selected == 5) { LOG_INFO("Setting color to Orange"); - r = 255; - g = 160; - b = 20; + TFT_MESH_r = 255; + TFT_MESH_g = 160; + TFT_MESH_b = 20; } else if (selected == 6) { LOG_INFO("Setting color to Purple"); - r = 204; - g = 153; - b = 255; + TFT_MESH_r = 204; + TFT_MESH_g = 153; + TFT_MESH_b = 255; } else if (selected == 7) { LOG_INFO("Setting color to Teal"); - r = 64; - g = 224; - b = 208; + TFT_MESH_r = 64; + TFT_MESH_g = 224; + TFT_MESH_b = 208; } else if (selected == 8) { LOG_INFO("Setting color to Pink"); - r = 255; - g = 105; - b = 180; + TFT_MESH_r = 255; + TFT_MESH_g = 105; + TFT_MESH_b = 180; } else if (selected == 9) { LOG_INFO("Setting color to White"); - r = 255; - g = 255; - b = 255; + TFT_MESH_r = 255; + TFT_MESH_g = 255; + TFT_MESH_b = 255; } #if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT @@ -731,14 +731,14 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display) display->fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); display->setColor(WHITE); - if (r == 0 && g == 0 && b == 0) { + if (TFT_MESH_r == 0 && TFT_MESH_g == 0 && TFT_MESH_b == 0) { #ifdef TFT_MESH_OVERRIDE TFT_MESH = TFT_MESH_OVERRIDE; #else TFT_MESH = COLOR565(0x67, 0xEA, 0x94); #endif } else { - TFT_MESH = COLOR565(r, g, b); + TFT_MESH = COLOR565(TFT_MESH_r, TFT_MESH_g, TFT_MESH_b); } #if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) @@ -746,10 +746,10 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display) #endif screen->setFrames(graphics::Screen::FOCUS_SYSTEM); - if (r == 0 && g == 0 && b == 0) { + if (TFT_MESH_r == 0 && TFT_MESH_g == 0 && TFT_MESH_b == 0) { uiconfig.screen_rgb_color = 0; } else { - uiconfig.screen_rgb_color = (r << 16) | (g << 8) | b; + uiconfig.screen_rgb_color = (TFT_MESH_r << 16) | (TFT_MESH_g << 8) | TFT_MESH_b; } LOG_INFO("Storing Value of %d to uiconfig.screen_rgb_color", uiconfig.screen_rgb_color); nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig); From db4e4e6e5382baeef579556947c2b96d299b58eb Mon Sep 17 00:00:00 2001 From: todd-herbert Date: Wed, 9 Jul 2025 06:01:48 +1200 Subject: [PATCH 21/72] Heltec Wireless Paper, VM-E213 Hardware Revisions (#7258) * Tests to identify display model * (InkHUD) SSD1682 controller IC Has a few quirks, gets its own base class * (InkHUD) E0213A367 Display For Heltec Wireless Paper V1.1.1, V1.2 For Heltec VM-E213 V1.1 * (InkHUD) Select display model at boot * (BaseUI) Wrapper to combine multiple GxEPD2 drivers Workaround for issue of GxEPD2_BW objects not having a shared base class. Allows us to select a driver at runtime. https://github.com/meshtastic/firmware/issues/6851#issuecomment-2905353447 * (BaseUI) Select E-Ink model at boot * (InkHUD) SSD1682 deep sleep * (InkHUD) No deep sleep for SSD1682 * (InkHUD) Fully no-op deep sleep for SSD1682 --- src/graphics/EInkDisplay2.cpp | 26 +++- src/graphics/EInkDisplay2.h | 13 +- src/graphics/GxEPD2Multi.h | 135 ++++++++++++++++++ src/graphics/niche/Drivers/EInk/E0213A367.cpp | 84 +++++++++++ src/graphics/niche/Drivers/EInk/E0213A367.h | 41 ++++++ src/graphics/niche/Drivers/EInk/SSD1682.cpp | 41 ++++++ src/graphics/niche/Drivers/EInk/SSD1682.h | 31 ++++ .../heltec_vision_master_e213/einkDetect.h | 35 +++++ .../heltec_vision_master_e213/nicheGraphics.h | 24 +++- .../heltec_vision_master_e213/platformio.ini | 5 +- variants/heltec_wireless_paper/einkDetect.h | 35 +++++ .../heltec_wireless_paper/nicheGraphics.h | 22 ++- variants/heltec_wireless_paper/platformio.ini | 5 +- 13 files changed, 483 insertions(+), 14 deletions(-) create mode 100644 src/graphics/GxEPD2Multi.h create mode 100644 src/graphics/niche/Drivers/EInk/E0213A367.cpp create mode 100644 src/graphics/niche/Drivers/EInk/E0213A367.h create mode 100644 src/graphics/niche/Drivers/EInk/SSD1682.cpp create mode 100644 src/graphics/niche/Drivers/EInk/SSD1682.h create mode 100644 variants/heltec_vision_master_e213/einkDetect.h create mode 100644 variants/heltec_wireless_paper/einkDetect.h diff --git a/src/graphics/EInkDisplay2.cpp b/src/graphics/EInkDisplay2.cpp index 5a2749482..66c7938b5 100644 --- a/src/graphics/EInkDisplay2.cpp +++ b/src/graphics/EInkDisplay2.cpp @@ -6,6 +6,10 @@ #include "main.h" #include +#ifdef GXEPD2_DRIVER_0 +#include "einkDetect.h" +#endif + /* The macros EINK_DISPLAY_MODEL, EINK_WIDTH, and EINK_HEIGHT are defined as build_flags in a variant's platformio.ini Previously, these macros were defined at the top of this file. @@ -174,9 +178,8 @@ bool EInkDisplay::connect() } } -#elif defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_VISION_MASTER_E213) || \ - defined(HELTEC_VISION_MASTER_E290) || defined(TLORA_T3S3_EPAPER) || defined(CROWPANEL_ESP32S3_5_EPAPER) || \ - defined(CROWPANEL_ESP32S3_4_EPAPER) || defined(CROWPANEL_ESP32S3_2_EPAPER) +#elif defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_VISION_MASTER_E290) || defined(TLORA_T3S3_EPAPER) || \ + defined(CROWPANEL_ESP32S3_5_EPAPER) || defined(CROWPANEL_ESP32S3_4_EPAPER) || defined(CROWPANEL_ESP32S3_2_EPAPER) { // Start HSPI hspi = new SPIClass(HSPI); @@ -232,6 +235,23 @@ bool EInkDisplay::connect() adafruitDisplay->init(); adafruitDisplay->setRotation(3); } +#elif defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_VISION_MASTER_E213) + + // Detect display model, before starting SPI + EInkDetectionResult displayModel = detectEInk(); + + // Start HSPI + hspi = new SPIClass(HSPI); + hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); // SCLK, MISO, MOSI, SS + + // Create GxEPD2 object + adafruitDisplay = new GxEPD2_Multi((uint8_t)displayModel, PIN_EINK_CS, PIN_EINK_DC, + PIN_EINK_RES, PIN_EINK_BUSY, *hspi); + + // Init GxEPD2 + adafruitDisplay->init(); + adafruitDisplay->setRotation(3); + #endif return true; diff --git a/src/graphics/EInkDisplay2.h b/src/graphics/EInkDisplay2.h index 93be197b0..284337627 100644 --- a/src/graphics/EInkDisplay2.h +++ b/src/graphics/EInkDisplay2.h @@ -5,6 +5,10 @@ #include "GxEPD2_BW.h" #include +#ifdef GXEPD2_DRIVER_0 // If variant has multiple possible display models +#include "GxEPD2Multi.h" +#endif + /** * An adapter class that allows using the GxEPD2 library as if it was an OLEDDisplay implementation. * @@ -63,8 +67,15 @@ class EInkDisplay : public OLEDDisplay // Connect to the display virtual bool connect() override; - // AdafruitGFX display object - instantiated in connect(), variant specific +#ifdef GXEPD2_DRIVER_0 + // AdafruitGFX display object - wrapper for multiple drivers + // Allows runtime detection of multiple displays + // Avoid this situation if possible! + GxEPD2_Multi *adafruitDisplay = NULL; +#else + // AdafruitGFX display object (for single display model) - instantiated in connect(), variant specific GxEPD2_BW *adafruitDisplay = NULL; +#endif // If display uses HSPI #if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_VISION_MASTER_E213) || \ diff --git a/src/graphics/GxEPD2Multi.h b/src/graphics/GxEPD2Multi.h new file mode 100644 index 000000000..f3807c9de --- /dev/null +++ b/src/graphics/GxEPD2Multi.h @@ -0,0 +1,135 @@ +// Wrapper class for GxEPD2_BW + +// Generic signature at build-time, so that we can detect display model at run-time +// Workaround for issue of GxEPD2_BW objects not having a shared base class +// Only exposes methods which we are actually using + +template class GxEPD2_Multi +{ + public: + void drawPixel(int16_t x, int16_t y, uint16_t color) + { + if (which == 0) + driver0->drawPixel(x, y, color); + else + driver1->drawPixel(x, y, color); + } + + bool nextPage() + { + if (which == 0) + return driver0->nextPage(); + else + return driver1->nextPage(); + } + + void hibernate() + { + if (which == 0) + driver0->hibernate(); + else + driver1->hibernate(); + } + + void init(uint32_t serial_diag_bitrate = 0) + { + if (which == 0) + driver0->init(serial_diag_bitrate); + else + driver1->init(serial_diag_bitrate); + } + + void init(uint32_t serial_diag_bitrate, bool initial, uint16_t reset_duration = 20, bool pulldown_rst_mode = false) + { + if (which == 0) + driver0->init(serial_diag_bitrate, initial, reset_duration, pulldown_rst_mode); + else + driver1->init(serial_diag_bitrate, initial, reset_duration, pulldown_rst_mode); + } + + void setRotation(uint8_t x) + { + if (which == 0) + driver0->setRotation(x); + else + driver1->setRotation(x); + } + + void setPartialWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h) + { + if (which == 0) + driver0->setPartialWindow(x, y, w, h); + else + driver1->setPartialWindow(x, y, w, h); + } + + void setFullWindow() + { + if (which == 0) + driver0->setFullWindow(); + else + driver1->setFullWindow(); + } + + int16_t width() + { + if (which == 0) + return driver0->width(); + else + return driver1->width(); + } + + int16_t height() + { + if (which == 0) + return driver0->height(); + else + return driver1->height(); + } + + void clearScreen(uint8_t value = 0xFF) + { + if (which == 0) + driver0->clearScreen(); + else + driver1->clearScreen(); + } + + void endAsyncFull() + { + if (which == 0) + driver0->endAsyncFull(); + else + driver1->endAsyncFull(); + } + + // Exposes methods of the GxEPD2_EPD object which is usually available as GxEPD2_BW::epd + class Epd2Wrapper + { + public: + bool isBusy() { return m_epd2->isBusy(); } + GxEPD2_EPD *m_epd2; + } epd2; + + // Constructor + // Select driver by passing whichDriver as 0 or 1 + GxEPD2_Multi(uint8_t whichDriver, int16_t cs, int16_t dc, int16_t rst, int16_t busy, SPIClass &spi) + { + assert(whichDriver == 0 || whichDriver == 1); + which = whichDriver; + LOG_DEBUG("GxEPD2_Multi driver: %d", which); + + if (which == 0) { + driver0 = new GxEPD2_BW(Driver0(cs, dc, rst, busy, spi)); + epd2.m_epd2 = &(driver0->epd2); + } else if (which == 1) { + driver1 = new GxEPD2_BW(Driver1(cs, dc, rst, busy, spi)); + epd2.m_epd2 = &(driver1->epd2); + } + } + + private: + uint8_t which; + GxEPD2_BW *driver0; + GxEPD2_BW *driver1; +}; \ No newline at end of file diff --git a/src/graphics/niche/Drivers/EInk/E0213A367.cpp b/src/graphics/niche/Drivers/EInk/E0213A367.cpp new file mode 100644 index 000000000..f19cb4ff7 --- /dev/null +++ b/src/graphics/niche/Drivers/EInk/E0213A367.cpp @@ -0,0 +1,84 @@ +#include "./E0213A367.h" + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +using namespace NicheGraphics::Drivers; + +// Map the display controller IC's output to the connected panel +void E0213A367::configScanning() +{ + // "Driver output control" + // Scan gates from 0 to 249 (vertical resolution 250px) + sendCommand(0x01); + sendData(0xF9); + sendData(0x00); +} + +// Specify which information is used to control the sequence of voltages applied to move the pixels +void E0213A367::configWaveform() +{ + // This command (0x37) is poorly documented + // As of July 2025, the datasheet for this display's controller IC is unavailable + // The values are supplied by Heltec, who presumably have privileged access to information from the display manufacturer + // Datasheet for the similar SSD1680 IC hints at the function of this command: + + // "Spare VCOM OTP selection": + // Unclear why 0x40 is set. Sane values for related SSD1680 seem to be 0x80 or 0x00. + // Maybe value is redundant? No noticeable impact when set to 0x00. + // We'll leave it set to 0x40, following Heltec's lead, just in case. + + // "Display Mode" + // Seems to specify whether a waveform stored in OTP should use display mode 1 or 2 (full refresh or differential refresh) + + // Unusual that waveforms are programmed to OTP, but this meta information is not ..? + + sendCommand(0x37); // "Write Register for Display Option" ? + sendData(0x40); // "Spare VCOM OTP selection" ? + sendData(0x80); // "Display Mode for WS[7:0]" ? + sendData(0x03); // "Display Mode for WS[15:8]" ? + sendData(0x0E); // "Display Mode [23:16]" ? + + switch (updateType) { + case FAST: + sendCommand(0x3C); // Border waveform: + sendData(0x81); // As specified by Heltec. Actually VCOM (0x80)?. Bit 0 seems redundant here. + break; + case FULL: + default: + sendCommand(0x3C); // Border waveform: + sendData(0x01); // Follow LUT 1 (blink same as white pixels) + break; + } +} + +// Tell controller IC which operations to run +void E0213A367::configUpdateSequence() +{ + switch (updateType) { + case FAST: + sendCommand(0x22); // Set "update sequence" + sendData(0xFF); // Will load LUT from OTP memory, Display mode 2 "differential refresh" + break; + case FULL: + default: + sendCommand(0x22); // Set "update sequence" + sendData(0xF7); // Will load LUT from OTP memory, Display mode 1 "full refresh" + break; + } +} + +// Once the refresh operation has been started, +// begin periodically polling the display to check for completion, using the normal Meshtastic threading code +// Only used when refresh is "async" +void E0213A367::detachFromUpdate() +{ + switch (updateType) { + case FAST: + return beginPolling(50, 500); // At least 500ms for fast refresh + case FULL: + default: + return beginPolling(100, 1500); // At least 1.5 seconds for full refresh + } +} + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS \ No newline at end of file diff --git a/src/graphics/niche/Drivers/EInk/E0213A367.h b/src/graphics/niche/Drivers/EInk/E0213A367.h new file mode 100644 index 000000000..a36fcb407 --- /dev/null +++ b/src/graphics/niche/Drivers/EInk/E0213A367.h @@ -0,0 +1,41 @@ +/* + +E-Ink display driver + - SSD1682 + - Manufacturer: SEEKINK + - Size: 2.13 inch + - Resolution: 122px x 255px + - Flex connector marking: HINK-E0213A162-A1 (hidden, printed on reverse) + +*/ + +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "configuration.h" + +#include "./SSD1682.h" + +namespace NicheGraphics::Drivers +{ +class E0213A367 : public SSD1682 +{ + // Display properties + private: + static constexpr uint32_t width = 122; + static constexpr uint32_t height = 250; + static constexpr UpdateTypes supported = (UpdateTypes)(FULL | FAST); + + public: + E0213A367() : SSD1682(width, height, supported, 0) {} + + protected: + void configScanning() override; + void configWaveform() override; + void configUpdateSequence() override; + void detachFromUpdate() override; +}; + +} // namespace NicheGraphics::Drivers +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS \ No newline at end of file diff --git a/src/graphics/niche/Drivers/EInk/SSD1682.cpp b/src/graphics/niche/Drivers/EInk/SSD1682.cpp new file mode 100644 index 000000000..c3d7f7786 --- /dev/null +++ b/src/graphics/niche/Drivers/EInk/SSD1682.cpp @@ -0,0 +1,41 @@ +#include "./SSD1682.h" + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +using namespace NicheGraphics::Drivers; + +SSD1682::SSD1682(uint16_t width, uint16_t height, EInk::UpdateTypes supported, uint8_t bufferOffsetX) + : SSD16XX(width, height, supported, bufferOffsetX) +{ +} + +// SSD1682 only accepts single-byte x and y values +// This causes an incompatibility with the default SSD16XX::configFullscreen +void SSD1682::configFullscreen() +{ + // Define the boundaries of the "fullscreen" region, for the controller IC + static const uint8_t sx = bufferOffsetX; // Notice the offset + static const uint8_t sy = 0; + static const uint8_t ex = bufferRowSize + bufferOffsetX - 1; // End is "max index", not "count". Minus 1 handles this + static const uint8_t ey = height; + + // Data entry mode - Left to Right, Top to Bottom + sendCommand(0x11); + sendData(0x03); + + // Select controller IC memory region to display a fullscreen image + sendCommand(0x44); // Memory X start - end + sendData(sx); + sendData(ex); + sendCommand(0x45); // Memory Y start - end + sendData(sy); + sendData(ey); + + // Place the cursor at the start of this memory region, ready to send image data x=0 y=0 + sendCommand(0x4E); // Memory cursor X + sendData(sx); + sendCommand(0x4F); // Memory cursor y + sendData(sy); +} + +#endif \ No newline at end of file diff --git a/src/graphics/niche/Drivers/EInk/SSD1682.h b/src/graphics/niche/Drivers/EInk/SSD1682.h new file mode 100644 index 000000000..ba3008537 --- /dev/null +++ b/src/graphics/niche/Drivers/EInk/SSD1682.h @@ -0,0 +1,31 @@ +/* + +E-Ink base class for displays based on SSD1682 + +SSD1682 has a few quirks. We're implementing them here in a new base class, +to avoid re-implementing them every time we need to add a new SSD1682-based display. + +*/ + +#pragma once + +#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS + +#include "configuration.h" + +#include "./SSD16XX.h" + +namespace NicheGraphics::Drivers +{ + +class SSD1682 : public SSD16XX +{ + public: + SSD1682(uint16_t width, uint16_t height, EInk::UpdateTypes supported, uint8_t bufferOffsetX = 0); + virtual void configFullscreen(); // Select memory region on controller IC + virtual void deepSleep() {} // Not usable (image memory not retained) +}; + +} // namespace NicheGraphics::Drivers + +#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS \ No newline at end of file diff --git a/variants/heltec_vision_master_e213/einkDetect.h b/variants/heltec_vision_master_e213/einkDetect.h new file mode 100644 index 000000000..35140db60 --- /dev/null +++ b/variants/heltec_vision_master_e213/einkDetect.h @@ -0,0 +1,35 @@ +#pragma once + +#include "configuration.h" + +enum class EInkDetectionResult : uint8_t { + LCMEN213EFC1 = 0, // Initial version + E0213A367 = 1, // E213 PCB marked V1.1 (Mid 2025) +}; + +EInkDetectionResult detectEInk() +{ + // Test 1: Logic of BUSY pin + + // Determines controller IC manufacturer + // Fitipower: busy when LOW + // Solomon Systech: busy when HIGH + + // Force display BUSY by holding reset pin active + pinMode(PIN_EINK_RES, OUTPUT); + digitalWrite(PIN_EINK_RES, LOW); + + delay(10); + + // Read whether pin is HIGH or LOW while busy + pinMode(PIN_EINK_BUSY, INPUT); + bool busyLogic = digitalRead(PIN_EINK_BUSY); + + // Test complete. Release pin + pinMode(PIN_EINK_RES, INPUT); + + if (busyLogic == LOW) + return EInkDetectionResult::LCMEN213EFC1; + else // busy HIGH + return EInkDetectionResult::E0213A367; +} \ No newline at end of file diff --git a/variants/heltec_vision_master_e213/nicheGraphics.h b/variants/heltec_vision_master_e213/nicheGraphics.h index 5f443e4da..6a75ad90d 100644 --- a/variants/heltec_vision_master_e213/nicheGraphics.h +++ b/variants/heltec_vision_master_e213/nicheGraphics.h @@ -18,16 +18,22 @@ // Shared NicheGraphics components // -------------------------------- +#include "graphics/niche/Drivers/EInk/E0213A367.h" #include "graphics/niche/Drivers/EInk/LCMEN2R13EFC1.h" #include "graphics/niche/Inputs/TwoButton.h" -// Button feedback -#include "buzz.h" +#include "buzz.h" // Button feedback +#include "einkDetect.h" // Detect display model at runtime void setupNicheGraphics() { using namespace NicheGraphics; + // Detect E-Ink Model + // ------------------- + + EInkDetectionResult displayModel = detectEInk(); + // SPI // ----------------------------- @@ -38,7 +44,13 @@ void setupNicheGraphics() // E-Ink Driver // ----------------------------- - Drivers::EInk *driver = new Drivers::LCMEN213EFC1; + Drivers::EInk *driver; + + if (displayModel == EInkDetectionResult::LCMEN213EFC1) // V1 (unmarked) + driver = new Drivers::LCMEN213EFC1; + else if (displayModel == EInkDetectionResult::E0213A367) // V1.1 + driver = new Drivers::E0213A367; + driver->begin(hspi, PIN_EINK_DC, PIN_EINK_CS, PIN_EINK_BUSY, PIN_EINK_RES); // InkHUD @@ -51,7 +63,11 @@ void setupNicheGraphics() // Set how many FAST updates per FULL update // Set how unhealthy additional FAST updates beyond this number are - inkhud->setDisplayResilience(10, 1.5); + + if (displayModel == EInkDetectionResult::LCMEN213EFC1) // V1 (unmarked) + inkhud->setDisplayResilience(10, 1.5); + else if (displayModel == EInkDetectionResult::E0213A367) // V1.1 + inkhud->setDisplayResilience(15, 3); // Select fonts InkHUD::Applet::fontLarge = FREESANS_12PT_WIN1252; diff --git a/variants/heltec_vision_master_e213/platformio.ini b/variants/heltec_vision_master_e213/platformio.ini index 34cebb6e3..028caaeff 100644 --- a/variants/heltec_vision_master_e213/platformio.ini +++ b/variants/heltec_vision_master_e213/platformio.ini @@ -7,7 +7,8 @@ build_flags = -Ivariants/heltec_vision_master_e213 -DHELTEC_VISION_MASTER_E213 -DUSE_EINK - -DEINK_DISPLAY_MODEL=GxEPD2_213_FC1 + -DGXEPD2_DRIVER_0=GxEPD2_213_FC1 + -DGXEPD2_DRIVER_1=GxEPD2_213_E0213A367 -DEINK_WIDTH=250 -DEINK_HEIGHT=122 -DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk @@ -16,7 +17,7 @@ build_flags = -DEINK_HASQUIRK_GHOSTING ; Display model is identified as "prone to ghosting" lib_deps = ${esp32s3_base.lib_deps} - https://github.com/meshtastic/GxEPD2/archive/b202ebfec6a4821e098cf7a625ba0f6f2400292d.zip + https://github.com/meshtastic/GxEPD2/archive/1655054ba298e0e29fc2044741940f927f9c2a43.zip lewisxhe/PCF8563_Library@^1.0.1 upload_speed = 115200 diff --git a/variants/heltec_wireless_paper/einkDetect.h b/variants/heltec_wireless_paper/einkDetect.h new file mode 100644 index 000000000..93b3f86e3 --- /dev/null +++ b/variants/heltec_wireless_paper/einkDetect.h @@ -0,0 +1,35 @@ +#pragma once + +#include "configuration.h" + +enum class EInkDetectionResult : uint8_t { + LCMEN213EFC1 = 0, // V1.1 + E0213A367 = 1, // V1.1.1, V1.2 +}; + +EInkDetectionResult detectEInk() +{ + // Test 1: Logic of BUSY pin + + // Determines controller IC manufacturer + // Fitipower: busy when LOW + // Solomon Systech: busy when HIGH + + // Force display BUSY by holding reset pin active + pinMode(PIN_EINK_RES, OUTPUT); + digitalWrite(PIN_EINK_RES, LOW); + + delay(10); + + // Read whether pin is HIGH or LOW while busy + pinMode(PIN_EINK_BUSY, INPUT); + bool busyLogic = digitalRead(PIN_EINK_BUSY); + + // Test complete. Release pin + pinMode(PIN_EINK_RES, INPUT); + + if (busyLogic == LOW) + return EInkDetectionResult::LCMEN213EFC1; + else // busy HIGH + return EInkDetectionResult::E0213A367; +} \ No newline at end of file diff --git a/variants/heltec_wireless_paper/nicheGraphics.h b/variants/heltec_wireless_paper/nicheGraphics.h index cbf80bc5e..445b57714 100644 --- a/variants/heltec_wireless_paper/nicheGraphics.h +++ b/variants/heltec_wireless_paper/nicheGraphics.h @@ -18,13 +18,21 @@ // Shared NicheGraphics components // -------------------------------- +#include "graphics/niche/Drivers/EInk/E0213A367.h" #include "graphics/niche/Drivers/EInk/LCMEN2R13EFC1.h" #include "graphics/niche/Inputs/TwoButton.h" +#include "einkDetect.h" // Detect display model at runtime + void setupNicheGraphics() { using namespace NicheGraphics; + // Detect E-Ink Model + // ------------------- + + EInkDetectionResult displayModel = detectEInk(); + // SPI // ----------------------------- @@ -35,7 +43,13 @@ void setupNicheGraphics() // E-Ink Driver // ----------------------------- - Drivers::EInk *driver = new Drivers::LCMEN213EFC1; + Drivers::EInk *driver; + + if (displayModel == EInkDetectionResult::LCMEN213EFC1) // V1.1 + driver = new Drivers::LCMEN213EFC1; + else if (displayModel == EInkDetectionResult::E0213A367) // V1.1.1, V1.2 + driver = new Drivers::E0213A367; + driver->begin(hspi, PIN_EINK_DC, PIN_EINK_CS, PIN_EINK_BUSY, PIN_EINK_RES); // InkHUD @@ -48,7 +62,11 @@ void setupNicheGraphics() // Set how many FAST updates per FULL update // Set how unhealthy additional FAST updates beyond this number are - inkhud->setDisplayResilience(10, 1.5); + + if (displayModel == EInkDetectionResult::LCMEN213EFC1) // V1.1 (unmarked) + inkhud->setDisplayResilience(10, 1.5); + else if (displayModel == EInkDetectionResult::E0213A367) // V1.1.1, V1.2 + inkhud->setDisplayResilience(15, 3); // Select fonts InkHUD::Applet::fontLarge = FREESANS_12PT_WIN1252; diff --git a/variants/heltec_wireless_paper/platformio.ini b/variants/heltec_wireless_paper/platformio.ini index ce5b5e533..790646056 100644 --- a/variants/heltec_wireless_paper/platformio.ini +++ b/variants/heltec_wireless_paper/platformio.ini @@ -7,7 +7,8 @@ build_flags = ${esp32s3_base.build_flags} -I variants/heltec_wireless_paper -D HELTEC_WIRELESS_PAPER - -D EINK_DISPLAY_MODEL=GxEPD2_213_FC1 + -D GXEPD2_DRIVER_0=GxEPD2_213_FC1 + -D GXEPD2_DRIVER_1=GxEPD2_213_E0213A367 -D EINK_WIDTH=250 -D EINK_HEIGHT=122 -D USE_EINK @@ -17,7 +18,7 @@ build_flags = -D EINK_HASQUIRK_GHOSTING ; Display model is identified as "prone to ghosting" lib_deps = ${esp32s3_base.lib_deps} - https://github.com/meshtastic/GxEPD2/archive/b202ebfec6a4821e098cf7a625ba0f6f2400292d.zip + https://github.com/meshtastic/GxEPD2/archive/1655054ba298e0e29fc2044741940f927f9c2a43.zip lewisxhe/PCF8563_Library@^1.0.1 upload_speed = 115200 From 916587c2a6344675a4ddd4701a2501f078f7b6a8 Mon Sep 17 00:00:00 2001 From: Jason P Date: Tue, 8 Jul 2025 13:38:07 -0500 Subject: [PATCH 22/72] Update Bluetooth Toggle to match other variants (#7269) --- src/graphics/draw/MenuHandler.cpp | 24 ++++++++++++++++++++++-- src/graphics/draw/MenuHandler.h | 4 +++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index a66ccd983..978700fd7 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -337,8 +337,8 @@ void menuHandler::homeBaseMenu() } else if (selected == Freetext) { cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST); } else if (selected == Bluetooth) { - InputEvent event = {.inputEvent = (input_broker_event)170, .kbchar = 170, .touchX = 0, .touchY = 0}; - inputBroker->injectInputEvent(&event); + menuQueue = bluetooth_toggle_menu; + screen->runNow(); } }; screen->showOverlayBanner(bannerOptions); @@ -587,6 +587,23 @@ void menuHandler::GPSToggleMenu() } #endif +void menuHandler::BluetoothToggleMenu() +{ + static const char *optionsArray[] = {"Back", "Enabled", "Disabled"}; + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Toggle Bluetooth"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = 3; + bannerOptions.bannerCallback = [](int selected) -> void { + if (selected == 1 || selected == 2) { + InputEvent event = {.inputEvent = (input_broker_event)170, .kbchar = 170, .touchX = 0, .touchY = 0}; + inputBroker->injectInputEvent(&event); + } + }; + bannerOptions.InitialSelected = config.bluetooth.enabled ? 1 : 2; + screen->showOverlayBanner(bannerOptions); +} + void menuHandler::BuzzerModeMenu() { static const char *optionsArray[] = {"All Enabled", "Disabled", "Notifications", "System Only"}; @@ -935,6 +952,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display) case wifi_toggle_menu: wifiToggleMenu(); break; + case bluetooth_toggle_menu: + BluetoothToggleMenu(); + break; } menuQueue = menu_none; } diff --git a/src/graphics/draw/MenuHandler.h b/src/graphics/draw/MenuHandler.h index 8824e38ed..d2169ca3c 100644 --- a/src/graphics/draw/MenuHandler.h +++ b/src/graphics/draw/MenuHandler.h @@ -25,7 +25,8 @@ class menuHandler remove_favorite, test_menu, number_test, - wifi_toggle_menu + wifi_toggle_menu, + bluetooth_toggle_menu }; static screenMenus menuQueue; @@ -55,6 +56,7 @@ class menuHandler static void numberTest(); static void wifiBaseMenu(); static void wifiToggleMenu(); + static void BluetoothToggleMenu(); }; } // namespace graphics \ No newline at end of file From 999e1207a5a959bd6aca1a2732915b54523981ca Mon Sep 17 00:00:00 2001 From: Jason P Date: Tue, 8 Jul 2025 14:38:38 -0500 Subject: [PATCH 23/72] Show user which option is currently elected (#7271) --- src/graphics/draw/MenuHandler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index 978700fd7..c750b72c9 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -136,6 +136,7 @@ void menuHandler::ClockFacePicker() screen->setFrames(Screen::FOCUS_CLOCK); } }; + bannerOptions.InitialSelected = uiconfig.is_clockface_analog ? 2 : 1; screen->showOverlayBanner(bannerOptions); } From 354f14933884d916ca29424e004a6ffc3b6372c6 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Tue, 8 Jul 2025 15:12:44 -0500 Subject: [PATCH 24/72] Make PacketHistory logging less chatty (#7272) --- boards/heltec_mesh_node_t114.json | 3 ++- src/mesh/PacketHistory.cpp | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/boards/heltec_mesh_node_t114.json b/boards/heltec_mesh_node_t114.json index d516c9701..eda0ac3df 100644 --- a/boards/heltec_mesh_node_t114.json +++ b/boards/heltec_mesh_node_t114.json @@ -10,7 +10,8 @@ "hwids": [ ["0x239A", "0x4405"], ["0x239A", "0x0029"], - ["0x239A", "0x002A"] + ["0x239A", "0x002A"], + ["0x2886", "0x1667"] ], "usb_product": "HT-n5262", "mcu": "nrf52840", diff --git a/src/mesh/PacketHistory.cpp b/src/mesh/PacketHistory.cpp index f42b151c8..8cac31a3e 100644 --- a/src/mesh/PacketHistory.cpp +++ b/src/mesh/PacketHistory.cpp @@ -246,8 +246,10 @@ void PacketHistory::insert(PacketRecord &r) #if RECENT_WARN_AGE > 0 if (tu->rxTimeMsec && (OldtrxTimeMsec < RECENT_WARN_AGE)) { if (!(tu->id == r.id && tu->sender == r.sender)) { +#if VERBOSE_PACKET_HISTORY LOG_WARN("Packet History - insert: Reusing slot aged %ds < %ds RECENT_WARN_AGE", OldtrxTimeMsec / 1000, RECENT_WARN_AGE / 1000); +#endif } else { // debug only #if VERBOSE_PACKET_HISTORY @@ -275,7 +277,9 @@ void PacketHistory::insert(PacketRecord &r) #endif if (r.rxTimeMsec == 0) { +#if VERBOSE_PACKET_HISTORY LOG_WARN("Packet History - insert: I will not store packet with rxTimeMsec = 0."); +#endif return; // Return early if we can't update the history } From 00495140bd8f2651158fb268bb175a2190110d74 Mon Sep 17 00:00:00 2001 From: Austin Date: Tue, 8 Jul 2025 16:14:05 -0400 Subject: [PATCH 25/72] GitHub Actions faster!! (#7268) Use new meshtastic/gh-action-firmware Action Co-authored-by: Ben Meadors --- .github/workflows/build_esp32.yml | 35 +++++++++++---------- .github/workflows/build_esp32_c3.yml | 35 +++++++++++---------- .github/workflows/build_esp32_c6.yml | 35 +++++++++++---------- .github/workflows/build_esp32_s3.yml | 35 +++++++++++---------- .github/workflows/build_nrf52.yml | 26 +++++++++------ .github/workflows/build_rpi2040.yml | 24 +++++++++----- .github/workflows/build_stm32.yml | 24 +++++++++----- .github/workflows/main_matrix.yml | 3 +- .github/workflows/nightly.yml | 2 ++ .github/workflows/sec_sast_semgrep_cron.yml | 1 + .github/workflows/stale_bot.yml | 1 + .github/workflows/tests.yml | 2 ++ bin/{build-rpi2040.sh => build-rp2xx0.sh} | 0 bin/{build-stm32.sh => build-stm32wl.sh} | 0 14 files changed, 135 insertions(+), 88 deletions(-) rename bin/{build-rpi2040.sh => build-rp2xx0.sh} (100%) rename bin/{build-stm32.sh => build-stm32wl.sh} (100%) diff --git a/.github/workflows/build_esp32.yml b/.github/workflows/build_esp32.yml index 616f51746..32cd45000 100644 --- a/.github/workflows/build_esp32.yml +++ b/.github/workflows/build_esp32.yml @@ -11,27 +11,30 @@ permissions: read-all jobs: build-esp32: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 + - name: Get release version string + shell: bash + run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + - name: Build ESP32 id: build - uses: ./.github/actions/build-variant + uses: meshtastic/gh-action-firmware@main with: - github_token: ${{ secrets.GITHUB_TOKEN }} - board: ${{ inputs.board }} - remove-debug-flags: >- - ./arch/esp32/esp32.ini - ./arch/esp32/esp32s2.ini - ./arch/esp32/esp32s3.ini - ./arch/esp32/esp32c3.ini - ./arch/esp32/esp32c6.ini - build-script-path: bin/build-esp32.sh - ota-firmware-source: firmware.bin - ota-firmware-target: release/bleota.bin - artifact-paths: | + pio_platform: esp32 + pio_env: ${{ inputs.board }} + pio_target: build + ota_firmware_source: firmware.bin + ota_firmware_target: release/bleota.bin + + - name: Store binaries as an artifact + uses: actions/upload-artifact@v4 + with: + name: firmware-esp32-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip + overwrite: true + path: | release/*.bin release/*.elf - #include-web-ui: true - arch: esp32 diff --git a/.github/workflows/build_esp32_c3.yml b/.github/workflows/build_esp32_c3.yml index 1b6b832e9..161786f99 100644 --- a/.github/workflows/build_esp32_c3.yml +++ b/.github/workflows/build_esp32_c3.yml @@ -11,27 +11,30 @@ permissions: read-all jobs: build-esp32-c3: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 + - name: Get release version string + shell: bash + run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + - name: Build ESP32-C3 id: build - uses: ./.github/actions/build-variant + uses: meshtastic/gh-action-firmware@main with: - github_token: ${{ secrets.GITHUB_TOKEN }} - board: ${{ inputs.board }} - remove-debug-flags: >- - ./arch/esp32/esp32.ini - ./arch/esp32/esp32s2.ini - ./arch/esp32/esp32s3.ini - ./arch/esp32/esp32c3.ini - ./arch/esp32/esp32c6.ini - build-script-path: bin/build-esp32.sh - ota-firmware-source: firmware-c3.bin - ota-firmware-target: release/bleota-c3.bin - artifact-paths: | + pio_platform: esp32 + pio_env: ${{ inputs.board }} + pio_target: build + ota_firmware_source: firmware-c3.bin + ota_firmware_target: release/bleota-c3.bin + + - name: Store binaries as an artifact + uses: actions/upload-artifact@v4 + with: + name: firmware-esp32c3-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip + overwrite: true + path: | release/*.bin release/*.elf - #include-web-ui: true - arch: esp32c3 diff --git a/.github/workflows/build_esp32_c6.yml b/.github/workflows/build_esp32_c6.yml index 29dac51e1..90cdcc78e 100644 --- a/.github/workflows/build_esp32_c6.yml +++ b/.github/workflows/build_esp32_c6.yml @@ -11,27 +11,30 @@ permissions: read-all jobs: build-esp32-c6: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 + - name: Get release version string + shell: bash + run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + - name: Build ESP32-C6 id: build - uses: ./.github/actions/build-variant + uses: meshtastic/gh-action-firmware@main with: - github_token: ${{ secrets.GITHUB_TOKEN }} - board: ${{ inputs.board }} - remove-debug-flags: >- - ./arch/esp32/esp32.ini - ./arch/esp32/esp32s2.ini - ./arch/esp32/esp32s3.ini - ./arch/esp32/esp32c3.ini - ./arch/esp32/esp32c6.ini - build-script-path: bin/build-esp32.sh - ota-firmware-source: firmware-c3.bin - ota-firmware-target: release/bleota-c3.bin - artifact-paths: | + pio_platform: esp32 + pio_env: ${{ inputs.board }} + pio_target: build + ota_firmware_source: firmware-c3.bin + ota_firmware_target: release/bleota-c3.bin + + - name: Store binaries as an artifact + uses: actions/upload-artifact@v4 + with: + name: firmware-esp32c6-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip + overwrite: true + path: | release/*.bin release/*.elf - #include-web-ui: true - arch: esp32c6 diff --git a/.github/workflows/build_esp32_s3.yml b/.github/workflows/build_esp32_s3.yml index 7e0373503..e5ed48e3e 100644 --- a/.github/workflows/build_esp32_s3.yml +++ b/.github/workflows/build_esp32_s3.yml @@ -11,27 +11,30 @@ permissions: read-all jobs: build-esp32-s3: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 + - name: Get release version string + shell: bash + run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + - name: Build ESP32-S3 id: build - uses: ./.github/actions/build-variant + uses: meshtastic/gh-action-firmware@main with: - github_token: ${{ secrets.GITHUB_TOKEN }} - board: ${{ inputs.board }} - remove-debug-flags: >- - ./arch/esp32/esp32.ini - ./arch/esp32/esp32s2.ini - ./arch/esp32/esp32s3.ini - ./arch/esp32/esp32c3.ini - ./arch/esp32/esp32c6.ini - build-script-path: bin/build-esp32.sh - ota-firmware-source: firmware-s3.bin - ota-firmware-target: release/bleota-s3.bin - artifact-paths: | + pio_platform: esp32 + pio_env: ${{ inputs.board }} + pio_target: build + ota_firmware_source: firmware-s3.bin + ota_firmware_target: release/bleota-s3.bin + + - name: Store binaries as an artifact + uses: actions/upload-artifact@v4 + with: + name: firmware-esp32s3-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip + overwrite: true + path: | release/*.bin release/*.elf - #include-web-ui: true - arch: esp32s3 diff --git a/.github/workflows/build_nrf52.yml b/.github/workflows/build_nrf52.yml index 786508f86..5fe00abed 100644 --- a/.github/workflows/build_nrf52.yml +++ b/.github/workflows/build_nrf52.yml @@ -11,20 +11,28 @@ permissions: read-all jobs: build-nrf52: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 + - name: Get release version string + shell: bash + run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + - name: Build NRF52 id: build - uses: ./.github/actions/build-variant + uses: meshtastic/gh-action-firmware@main with: - github_token: ${{ secrets.GITHUB_TOKEN }} - board: ${{ inputs.board }} - build-script-path: bin/build-nrf52.sh - artifact-paths: | - release/*.hex + pio_platform: nrf52 + pio_env: ${{ inputs.board }} + pio_target: build + + - name: Store binaries as an artifact + uses: actions/upload-artifact@v4 + with: + name: firmware-nrf52840-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip + overwrite: true + path: | release/*.uf2 release/*.elf - release/*.zip - arch: nrf52840 diff --git a/.github/workflows/build_rpi2040.yml b/.github/workflows/build_rpi2040.yml index 53fee34d2..2abd7a839 100644 --- a/.github/workflows/build_rpi2040.yml +++ b/.github/workflows/build_rpi2040.yml @@ -11,18 +11,28 @@ permissions: read-all jobs: build-rpi2040: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 + - name: Get release version string + shell: bash + run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + - name: Build Raspberry Pi 2040 id: build - uses: ./.github/actions/build-variant + uses: meshtastic/gh-action-firmware@main with: - github_token: ${{ secrets.GITHUB_TOKEN }} - board: ${{ inputs.board }} - build-script-path: bin/build-rpi2040.sh - artifact-paths: | + pio_platform: rp2xx0 + pio_env: ${{ inputs.board }} + pio_target: build + + - name: Store binaries as an artifact + uses: actions/upload-artifact@v4 + with: + name: firmware-rp2040-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip + overwrite: true + path: | release/*.uf2 release/*.elf - arch: rp2040 diff --git a/.github/workflows/build_stm32.yml b/.github/workflows/build_stm32.yml index dc469d994..10680f422 100644 --- a/.github/workflows/build_stm32.yml +++ b/.github/workflows/build_stm32.yml @@ -11,19 +11,29 @@ permissions: read-all jobs: build-stm32: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 + - name: Get release version string + shell: bash + run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT + id: version + - name: Build STM32WL id: build - uses: ./.github/actions/build-variant + uses: meshtastic/gh-action-firmware@main with: - github_token: ${{ secrets.GITHUB_TOKEN }} - board: ${{ inputs.board }} - build-script-path: bin/build-stm32.sh - artifact-paths: | + pio_platform: stm32wl + pio_env: ${{ inputs.board }} + pio_target: build + + - name: Store binaries as an artifact + uses: actions/upload-artifact@v4 + with: + name: firmware-stm32-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip + overwrite: true + path: | release/*.hex release/*.bin release/*.elf - arch: stm32 diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index 03e61d572..a676efa1e 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -135,6 +135,7 @@ jobs: board: ${{ matrix.board }} build-debian-src: + if: github.repository == 'meshtastic/firmware' uses: ./.github/workflows/build_debian_src.yml with: series: UNRELEASED @@ -425,7 +426,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} publish-firmware: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: ${{ github.event_name == 'workflow_dispatch' }} needs: [release-firmware] env: diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 36ec22f17..309772b12 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -8,6 +8,7 @@ permissions: read-all jobs: trunk_check: + if: github.repository == 'meshtastic/firmware' name: Trunk Check and Upload runs-on: ubuntu-24.04 @@ -21,6 +22,7 @@ jobs: trunk-token: ${{ secrets.TRUNK_TOKEN }} trunk_upgrade: + if: github.repository == 'meshtastic/firmware' # See: https://github.com/trunk-io/trunk-action/blob/v1/readme.md#automatic-upgrades name: Trunk Upgrade (PR) runs-on: ubuntu-24.04 diff --git a/.github/workflows/sec_sast_semgrep_cron.yml b/.github/workflows/sec_sast_semgrep_cron.yml index d7eef29b4..e391aa07b 100644 --- a/.github/workflows/sec_sast_semgrep_cron.yml +++ b/.github/workflows/sec_sast_semgrep_cron.yml @@ -13,6 +13,7 @@ permissions: jobs: semgrep-full: + if: github.repository == 'meshtastic/firmware' runs-on: ubuntu-24.04 container: image: semgrep/semgrep diff --git a/.github/workflows/stale_bot.yml b/.github/workflows/stale_bot.yml index 5ae6bdfc9..5a11fdfa8 100644 --- a/.github/workflows/stale_bot.yml +++ b/.github/workflows/stale_bot.yml @@ -11,6 +11,7 @@ permissions: jobs: stale_issues: + if: github.repository == 'meshtastic/firmware' name: Close Stale Issues runs-on: ubuntu-latest diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 28b6a40a5..34b28b39c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,9 +12,11 @@ permissions: jobs: native-tests: + if: github.repository == 'meshtastic/firmware' uses: ./.github/workflows/test_native.yml hardware-tests: + if: github.repository == 'meshtastic/firmware' runs-on: test-runner steps: - name: Checkout code diff --git a/bin/build-rpi2040.sh b/bin/build-rp2xx0.sh similarity index 100% rename from bin/build-rpi2040.sh rename to bin/build-rp2xx0.sh diff --git a/bin/build-stm32.sh b/bin/build-stm32wl.sh similarity index 100% rename from bin/build-stm32.sh rename to bin/build-stm32wl.sh From 19d831d20d30a19375c1d198f40d70f1dead4d91 Mon Sep 17 00:00:00 2001 From: Austin Date: Tue, 8 Jul 2025 21:33:59 -0400 Subject: [PATCH 26/72] Whoops! Re-Add nRF52 OTA zips (#7275) --- .github/workflows/build_nrf52.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build_nrf52.yml b/.github/workflows/build_nrf52.yml index 5fe00abed..0ff3ce934 100644 --- a/.github/workflows/build_nrf52.yml +++ b/.github/workflows/build_nrf52.yml @@ -36,3 +36,4 @@ jobs: path: | release/*.uf2 release/*.elf + release/*-ota.zip From f6d378255c3bec3b116bfd19c901b9cd80e89d9c Mon Sep 17 00:00:00 2001 From: Austin Date: Tue, 8 Jul 2025 23:53:51 -0400 Subject: [PATCH 27/72] Actions: Re-Add nrf52 hex release (rak4631) (#7276) --- .github/workflows/build_nrf52.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build_nrf52.yml b/.github/workflows/build_nrf52.yml index 0ff3ce934..312aeb372 100644 --- a/.github/workflows/build_nrf52.yml +++ b/.github/workflows/build_nrf52.yml @@ -36,4 +36,5 @@ jobs: path: | release/*.uf2 release/*.elf + release/*.hex release/*-ota.zip From a7e516d6f61abc7a8cf5d2649b0ab51afec579f9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 9 Jul 2025 15:11:05 +0800 Subject: [PATCH 28/72] Update Adafruit INA260 to v1.5.3 (#7270) 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 5ba5e63e0..89720f0ad 100644 --- a/platformio.ini +++ b/platformio.ini @@ -129,7 +129,7 @@ lib_deps = # renovate: datasource=custom.pio depName=Adafruit MCP9808 packageName=adafruit/library/Adafruit MCP9808 Library adafruit/Adafruit MCP9808 Library@2.0.2 # renovate: datasource=custom.pio depName=Adafruit INA260 packageName=adafruit/library/Adafruit INA260 Library - adafruit/Adafruit INA260 Library@1.5.2 + adafruit/Adafruit INA260 Library@1.5.3 # renovate: datasource=custom.pio depName=Adafruit INA219 packageName=adafruit/library/Adafruit INA219 adafruit/Adafruit INA219@1.2.3 # renovate: datasource=custom.pio depName=Adafruit PM25 AQI Sensor packageName=adafruit/library/Adafruit PM25 AQI Sensor From 0795b21c2b24addab178d26bc4880f2d97ec625c Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Thu, 10 Jul 2025 09:45:36 -0500 Subject: [PATCH 29/72] Key verification flow on BaseUI (#7240) --- src/graphics/Screen.cpp | 5 +- src/graphics/Screen.h | 9 +- src/graphics/draw/MenuHandler.cpp | 155 +++++++++++++-------- src/graphics/draw/MenuHandler.h | 12 +- src/graphics/draw/NotificationRenderer.cpp | 72 +++++++--- src/graphics/draw/NotificationRenderer.h | 3 +- src/meshUtils.h | 5 +- src/modules/CannedMessageModule.cpp | 6 +- src/modules/KeyVerificationModule.cpp | 62 ++++----- src/modules/KeyVerificationModule.h | 7 +- variants/t-deck/variant.h | 1 + 11 files changed, 211 insertions(+), 126 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 5d33feb4d..2bade47b2 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -171,7 +171,7 @@ void Screen::showOverlayBanner(BannerOverlayOptions banner_overlay_options) } // Called to trigger a banner with custom message and duration -void Screen::showNodePicker(const char *message, uint32_t durationMs, std::function bannerCallback) +void Screen::showNodePicker(const char *message, uint32_t durationMs, std::function bannerCallback) { #ifdef USE_EINK EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus @@ -196,7 +196,6 @@ void Screen::showNodePicker(const char *message, uint32_t durationMs, std::funct void Screen::showNumberPicker(const char *message, uint32_t durationMs, uint8_t digits, std::function bannerCallback) { - LOG_WARN("Show Number Picker"); #ifdef USE_EINK EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus #endif @@ -1330,7 +1329,7 @@ int Screen::handleInputEvent(const InputEvent *event) setFastFramerate(); // Draw ASAP #endif if (NotificationRenderer::isOverlayBannerShowing()) { - NotificationRenderer::inEvent = event->inputEvent; + NotificationRenderer::inEvent = *event; static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback}; ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0])); setFastFramerate(); // Draw ASAP diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index a486f99f8..4deeb7395 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -92,6 +92,7 @@ class Screen #include "commands.h" #include "concurrency/LockGuard.h" #include "concurrency/OSThread.h" +#include "graphics/draw/MenuHandler.h" #include "input/InputBroker.h" #include "mesh/MeshModule.h" #include "modules/AdminModule.h" @@ -308,9 +309,15 @@ class Screen : public concurrency::OSThread void showSimpleBanner(const char *message, uint32_t durationMs = 0); void showOverlayBanner(BannerOverlayOptions); - void showNodePicker(const char *message, uint32_t durationMs, std::function bannerCallback); + void showNodePicker(const char *message, uint32_t durationMs, std::function bannerCallback); void showNumberPicker(const char *message, uint32_t durationMs, uint8_t digits, std::function bannerCallback); + void requestMenu(graphics::menuHandler::screenMenus menuToShow) + { + graphics::menuHandler::menuQueue = menuToShow; + runNow(); + } + void startFirmwareUpdateScreen() { ScreenCmd cmd; diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index c750b72c9..c3a035c4f 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -13,6 +13,7 @@ #include "main.h" #include "modules/AdminModule.h" #include "modules/CannedMessageModule.h" +#include "modules/KeyVerificationModule.h" extern uint16_t TFT_MESH; @@ -237,27 +238,25 @@ void menuHandler::clockMenu() void menuHandler::messageResponseMenu() { + enum optionsNumbers { Back = 0, Dismiss = 1, Preset = 2, Freetext = 3, Aloud = 4, enumEnd = 5 }; + + static const char *optionsArray[enumEnd] = {"Back", "Dismiss", "Reply via Preset"}; + static int optionsEnumArray[enumEnd] = {Back, Dismiss, Preset}; + int options = 3; - static const char **optionsArrayPtr; - int options; - enum optionsNumbers { Back = 0, Dismiss = 1, Preset = 2, Freetext = 3 }; if (kb_found) { - static const char *optionsArray[] = {"Back", "Dismiss", "Reply via Preset", "Reply via Freetext"}; - optionsArrayPtr = optionsArray; - options = 4; - } else { - static const char *optionsArray[] = {"Back", "Dismiss", "Reply via Preset"}; - optionsArrayPtr = optionsArray; - options = 3; + optionsArray[options] = "Reply via Freetext"; + optionsEnumArray[options++] = Freetext; } + #ifdef HAS_I2S - static const char *optionsArray[] = {"Back", "Dismiss", "Reply via Preset", "Reply via Freetext", "Read Aloud"}; - optionsArrayPtr = optionsArray; - options = 5; + optionsArray[options] = "Read Aloud"; + optionsEnumArray[options++] = Aloud; #endif BannerOverlayOptions bannerOptions; bannerOptions.message = "Message Action"; - bannerOptions.optionsArrayPtr = optionsArrayPtr; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsEnumPtr = optionsEnumArray; bannerOptions.optionsCount = options; bannerOptions.bannerCallback = [](int selected) -> void { if (selected == Dismiss) { @@ -276,7 +275,7 @@ void menuHandler::messageResponseMenu() } } #ifdef HAS_I2S - else if (selected == 4) { + else if (selected == Aloud) { const meshtastic_MeshPacket &mp = devicestate.rx_text_message; const char *msg = reinterpret_cast(mp.decoded.payload.bytes); @@ -289,10 +288,10 @@ void menuHandler::messageResponseMenu() void menuHandler::homeBaseMenu() { - enum optionsNumbers { Back, Backlight, Position, Preset, Freetext, Bluetooth, Sleep }; + enum optionsNumbers { Back, Backlight, Position, Preset, Freetext, Bluetooth, Sleep, enumEnd }; - static const char *optionsArray[6] = {"Back"}; - static int optionsEnumArray[6] = {Back}; + static const char *optionsArray[enumEnd] = {"Back"}; + static int optionsEnumArray[enumEnd] = {Back}; int options = 1; #ifdef PIN_EINK_EN @@ -354,9 +353,9 @@ void menuHandler::systemBaseMenu() hasSupportBrightness = true; #endif - enum optionsNumbers { Back, Beeps, Brightness, Reboot, Color, MUI, Test }; - static const char *optionsArray[7] = {"Back"}; - static int optionsEnumArray[7] = {Back}; + enum optionsNumbers { Back, Beeps, Brightness, Reboot, Color, MUI, Test, enumEnd }; + static const char *optionsArray[enumEnd] = {"Back"}; + static int optionsEnumArray[enumEnd] = {Back}; int options = 1; optionsArray[options] = "Reboot"; @@ -419,21 +418,22 @@ void menuHandler::systemBaseMenu() void menuHandler::favoriteBaseMenu() { - int options; - static const char **optionsArrayPtr; + enum optionsNumbers { Back, Preset, Freetext, Remove, enumEnd }; + static const char *optionsArray[enumEnd] = {"Back", "New Preset Msg"}; + static int optionsEnumArray[enumEnd] = {Back, Preset}; + int options = 2; if (kb_found) { - static const char *optionsArray[] = {"Back", "New Preset Msg", "New Freetext Msg", "Remove Favorite"}; - optionsArrayPtr = optionsArray; - options = 4; - } else { - static const char *optionsArray[] = {"Back", "New Preset Msg", "Remove Favorite"}; - optionsArrayPtr = optionsArray; - options = 3; + optionsArray[options] = "New Freetext Msg"; + optionsEnumArray[options++] = Freetext; } + optionsArray[options] = "Remove Favorite"; + optionsEnumArray[options++] = Remove; + BannerOverlayOptions bannerOptions; bannerOptions.message = "Favorites Action"; - bannerOptions.optionsArrayPtr = optionsArrayPtr; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsEnumPtr = optionsEnumArray; bannerOptions.optionsCount = options; bannerOptions.bannerCallback = [](int selected) -> void { if (selected == 1) { @@ -450,34 +450,29 @@ void menuHandler::favoriteBaseMenu() void menuHandler::positionBaseMenu() { - int options; - static const char **optionsArrayPtr; - static const char *optionsArray[] = {"Back", "GPS Toggle", "Compass"}; - static const char *optionsArrayCalibrate[] = {"Back", "GPS Toggle", "Compass", "Compass Calibrate"}; + enum optionsNumbers { Back, GPSToggle, CompassMenu, CompassCalibrate, enumEnd }; + + static const char *optionsArray[enumEnd] = {"Back", "GPS Toggle", "Compass"}; + static int optionsEnumArray[enumEnd] = {Back, GPSToggle, CompassMenu}; + int options = 3; if (accelerometerThread) { - optionsArrayPtr = optionsArrayCalibrate; - options = 4; - } else { - optionsArrayPtr = optionsArray; - options = 3; + optionsArray[options] = "Compass Calibrate"; + optionsEnumArray[options++] = CompassCalibrate; } BannerOverlayOptions bannerOptions; bannerOptions.message = "Position Action"; - bannerOptions.optionsArrayPtr = optionsArrayPtr; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsEnumPtr = optionsEnumArray; bannerOptions.optionsCount = options; bannerOptions.bannerCallback = [](int selected) -> void { - if (selected == 1) { -#if MESHTASTIC_EXCLUDE_GPS - menuQueue = menu_none; -#else + if (selected == GPSToggle) { menuQueue = gps_toggle_menu; screen->runNow(); -#endif - } else if (selected == 2) { + } else if (selected == CompassMenu) { menuQueue = compass_point_north_menu; screen->runNow(); - } else if (selected == 3) { + } else if (selected == CompassCalibrate) { accelerometerThread->calibrate(30); } }; @@ -486,16 +481,20 @@ void menuHandler::positionBaseMenu() void menuHandler::nodeListMenu() { - static const char *optionsArray[] = {"Back", "Add Favorite", "Reset NodeDB"}; + enum optionsNumbers { Back, Favorite, Verify, Reset }; + static const char *optionsArray[] = {"Back", "Add Favorite", "Key Verification", "Reset NodeDB"}; BannerOverlayOptions bannerOptions; bannerOptions.message = "Node Action"; bannerOptions.optionsArrayPtr = optionsArray; - bannerOptions.optionsCount = 3; + bannerOptions.optionsCount = 4; bannerOptions.bannerCallback = [](int selected) -> void { - if (selected == 1) { + if (selected == Favorite) { menuQueue = add_favorite; screen->runNow(); - } else if (selected == 2) { + } else if (selected == Verify) { + menuQueue = key_verification_init; + screen->runNow(); + } else if (selected == Reset) { menuQueue = reset_node_db_menu; screen->runNow(); } @@ -523,6 +522,7 @@ void menuHandler::resetNodeDBMenu() void menuHandler::compassNorthMenu() { + enum optionsNumbers { Back, Dynamic, Fixed, Freeze }; static const char *optionsArray[] = {"Back", "Dynamic", "Fixed Ring", "Freeze Heading"}; BannerOverlayOptions bannerOptions; bannerOptions.message = "North Directions?"; @@ -530,28 +530,28 @@ void menuHandler::compassNorthMenu() bannerOptions.optionsCount = 4; bannerOptions.InitialSelected = uiconfig.compass_mode + 1; bannerOptions.bannerCallback = [](int selected) -> void { - if (selected == 1) { + if (selected == Dynamic) { if (uiconfig.compass_mode != meshtastic_CompassMode_DYNAMIC) { uiconfig.compass_mode = meshtastic_CompassMode_DYNAMIC; nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig); screen->setFrames(graphics::Screen::FOCUS_PRESERVE); } - } else if (selected == 2) { + } else if (selected == Fixed) { if (uiconfig.compass_mode != meshtastic_CompassMode_FIXED_RING) { uiconfig.compass_mode = meshtastic_CompassMode_FIXED_RING; nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig); screen->setFrames(graphics::Screen::FOCUS_PRESERVE); } - } else if (selected == 3) { + } else if (selected == Freeze) { if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING) { uiconfig.compass_mode = meshtastic_CompassMode_FREEZE_HEADING; nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig); screen->setFrames(graphics::Screen::FOCUS_PRESERVE); } - } else if (selected == 0) { + } else if (selected == Back) { menuQueue = position_base_menu; screen->runNow(); } @@ -562,6 +562,7 @@ void menuHandler::compassNorthMenu() #if !MESHTASTIC_EXCLUDE_GPS void menuHandler::GPSToggleMenu() { + static const char *optionsArray[] = {"Back", "Enabled", "Disabled"}; BannerOverlayOptions bannerOptions; bannerOptions.message = "Toggle GPS"; @@ -796,7 +797,7 @@ void menuHandler::rebootMenu() void menuHandler::addFavoriteMenu() { - screen->showNodePicker("Node To Favorite", 30000, [](int nodenum) -> void { + screen->showNodePicker("Node To Favorite", 30000, [](uint32_t nodenum) -> void { LOG_WARN("Nodenum: %u", nodenum); nodeDB->set_favorite(true, nodenum); screen->setFrames(graphics::Screen::FOCUS_PRESERVE); @@ -887,6 +888,37 @@ void menuHandler::wifiToggleMenu() screen->showOverlayBanner(bannerOptions); } +void menuHandler::keyVerificationInitMenu() +{ + screen->showNodePicker("Node to Verify", 30000, + [](uint32_t selected) -> void { keyVerificationModule->sendInitialRequest(selected); }); +} + +void menuHandler::keyVerificationFinalPrompt() +{ + char message[40] = {0}; + memset(message, 0, sizeof(message)); + sprintf(message, "Verification: \n"); + keyVerificationModule->generateVerificationCode(message + 15); // send the toPhone packet + + if (screen) { + static const char *optionsArray[] = {"Reject", "Accept"}; + graphics::BannerOverlayOptions options; + options.message = message; + options.durationMs = 30000; + options.optionsArrayPtr = optionsArray; + options.optionsCount = 2; + options.notificationType = graphics::notificationTypeEnum::selection_picker; + options.bannerCallback = [=](int selected) { + if (selected == 1) { + auto remoteNodePtr = nodeDB->getMeshNode(keyVerificationModule->getCurrentRemoteNode()); + remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK; + } + }; + screen->showOverlayBanner(options); + } +} + void menuHandler::handleMenuSwitch(OLEDDisplay *display) { if (menuQueue != menu_none) @@ -953,9 +985,18 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display) case wifi_toggle_menu: wifiToggleMenu(); break; + case key_verification_init: + keyVerificationInitMenu(); + break; + case key_verification_final_prompt: + keyVerificationFinalPrompt(); + break; case bluetooth_toggle_menu: BluetoothToggleMenu(); break; + case throttle_message: + screen->showSimpleBanner("Too Many Attempts\nTry again in 60 seconds.", 5000); + break; } menuQueue = menu_none; } diff --git a/src/graphics/draw/MenuHandler.h b/src/graphics/draw/MenuHandler.h index d2169ca3c..5846a3c91 100644 --- a/src/graphics/draw/MenuHandler.h +++ b/src/graphics/draw/MenuHandler.h @@ -1,3 +1,5 @@ +#pragma once +#if HAS_SCREEN #include "configuration.h" namespace graphics { @@ -26,7 +28,10 @@ class menuHandler test_menu, number_test, wifi_toggle_menu, - bluetooth_toggle_menu + key_verification_init, + key_verification_final_prompt, + bluetooth_toggle_menu, + throttle_message }; static screenMenus menuQueue; @@ -56,7 +61,10 @@ class menuHandler static void numberTest(); static void wifiBaseMenu(); static void wifiToggleMenu(); + static void keyVerificationInitMenu(); + static void keyVerificationFinalPrompt(); static void BluetoothToggleMenu(); }; -} // namespace graphics \ No newline at end of file +} // namespace graphics +#endif \ No newline at end of file diff --git a/src/graphics/draw/NotificationRenderer.cpp b/src/graphics/draw/NotificationRenderer.cpp index 057c91008..7350c204f 100644 --- a/src/graphics/draw/NotificationRenderer.cpp +++ b/src/graphics/draw/NotificationRenderer.cpp @@ -26,7 +26,7 @@ extern bool hasUnreadMessage; namespace graphics { -char NotificationRenderer::inEvent = INPUT_BROKER_NONE; +InputEvent NotificationRenderer::inEvent; int8_t NotificationRenderer::curSelected = 0; char NotificationRenderer::alertBannerMessage[256] = {0}; uint32_t NotificationRenderer::alertBannerUntil = 0; // 0 is a special case meaning forever @@ -72,11 +72,25 @@ void NotificationRenderer::resetBanner() { alertBannerMessage[0] = '\0'; current_notification_type = notificationTypeEnum::none; + + inEvent.inputEvent = INPUT_BROKER_NONE; + inEvent.kbchar = 0; + curSelected = 0; + alertBannerOptions = 0; // last x lines are seelctable options + optionsArrayPtr = nullptr; + optionsEnumPtr = nullptr; + alertBannerCallback = NULL; + pauseBanner = false; + numDigits = 0; + currentNumber = 0; + nodeDB->pause_sort(false); } void NotificationRenderer::drawBannercallback(OLEDDisplay *display, OLEDDisplayUiState *state) { + if (!isOverlayBannerShowing() && alertBannerMessage[0] != '\0') + resetBanner(); if (!isOverlayBannerShowing() || pauseBanner) return; switch (current_notification_type) { @@ -115,31 +129,40 @@ void NotificationRenderer::drawNumberPicker(OLEDDisplay *display, OLEDDisplayUiS // modulo to extract uint8_t this_digit = (currentNumber % (pow_of_10(numDigits - curSelected))) / (pow_of_10(numDigits - curSelected - 1)); // Handle input - if (inEvent == INPUT_BROKER_UP || inEvent == INPUT_BROKER_ALT_PRESS) { + if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) { if (this_digit == 9) { currentNumber -= 9 * (pow_of_10(numDigits - curSelected - 1)); } else { currentNumber += (pow_of_10(numDigits - curSelected - 1)); } - } else if (inEvent == INPUT_BROKER_DOWN || inEvent == INPUT_BROKER_USER_PRESS) { + } else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS) { if (this_digit == 0) { currentNumber += 9 * (pow_of_10(numDigits - curSelected - 1)); } else { currentNumber -= (pow_of_10(numDigits - curSelected - 1)); } - } else if (inEvent == INPUT_BROKER_SELECT || inEvent == INPUT_BROKER_RIGHT) { + } else if (inEvent.inputEvent == INPUT_BROKER_ANYKEY) { + if (inEvent.kbchar > 47 && inEvent.kbchar < 58) { // have a digit + currentNumber -= this_digit * (pow_of_10(numDigits - curSelected - 1)); + currentNumber += (inEvent.kbchar - 48) * (pow_of_10(numDigits - curSelected - 1)); + curSelected++; + } + } else if (inEvent.inputEvent == INPUT_BROKER_SELECT || inEvent.inputEvent == INPUT_BROKER_RIGHT) { curSelected++; - } else if (inEvent == INPUT_BROKER_LEFT) { + } else if (inEvent.inputEvent == INPUT_BROKER_LEFT) { curSelected--; - } else if ((inEvent == INPUT_BROKER_CANCEL || inEvent == INPUT_BROKER_ALT_LONG) && alertBannerUntil != 0) { + } else if ((inEvent.inputEvent == INPUT_BROKER_CANCEL || inEvent.inputEvent == INPUT_BROKER_ALT_LONG) && + alertBannerUntil != 0) { resetBanner(); + return; } if (curSelected == numDigits) { - resetBanner(); alertBannerCallback(currentNumber); + resetBanner(); + return; } - inEvent = INPUT_BROKER_NONE; + inEvent.inputEvent = INPUT_BROKER_NONE; if (alertBannerMessage[0] == '\0') return; @@ -193,16 +216,18 @@ void NotificationRenderer::drawNodePicker(OLEDDisplay *display, OLEDDisplayUiSta } // Handle input - if (inEvent == INPUT_BROKER_UP || inEvent == INPUT_BROKER_ALT_PRESS) { + if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) { curSelected--; - } else if (inEvent == INPUT_BROKER_DOWN || inEvent == INPUT_BROKER_USER_PRESS) { + } else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS) { curSelected++; - } else if (inEvent == INPUT_BROKER_SELECT) { - resetBanner(); + } else if (inEvent.inputEvent == INPUT_BROKER_SELECT) { alertBannerCallback(selectedNodenum); - - } else if ((inEvent == INPUT_BROKER_CANCEL || inEvent == INPUT_BROKER_ALT_LONG) && alertBannerUntil != 0) { resetBanner(); + return; + } else if ((inEvent.inputEvent == INPUT_BROKER_CANCEL || inEvent.inputEvent == INPUT_BROKER_ALT_LONG) && + alertBannerUntil != 0) { + resetBanner(); + return; } if (curSelected == -1) @@ -210,7 +235,7 @@ void NotificationRenderer::drawNodePicker(OLEDDisplay *display, OLEDDisplayUiSta if (curSelected == alertBannerOptions) curSelected = 0; - inEvent = INPUT_BROKER_NONE; + inEvent.inputEvent = INPUT_BROKER_NONE; if (alertBannerMessage[0] == '\0') return; @@ -308,11 +333,11 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp // Handle input if (alertBannerOptions > 0) { - if (inEvent == INPUT_BROKER_UP || inEvent == INPUT_BROKER_ALT_PRESS) { + if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) { curSelected--; - } else if (inEvent == INPUT_BROKER_DOWN || inEvent == INPUT_BROKER_USER_PRESS) { + } else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS) { curSelected++; - } else if (inEvent == INPUT_BROKER_SELECT) { + } else if (inEvent.inputEvent == INPUT_BROKER_SELECT) { if (optionsEnumPtr != nullptr) { alertBannerCallback(optionsEnumPtr[curSelected]); optionsEnumPtr = nullptr; @@ -320,8 +345,11 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp alertBannerCallback(curSelected); } resetBanner(); - } else if ((inEvent == INPUT_BROKER_CANCEL || inEvent == INPUT_BROKER_ALT_LONG) && alertBannerUntil != 0) { + return; + } else if ((inEvent.inputEvent == INPUT_BROKER_CANCEL || inEvent.inputEvent == INPUT_BROKER_ALT_LONG) && + alertBannerUntil != 0) { resetBanner(); + return; } if (curSelected == -1) @@ -329,12 +357,14 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp if (curSelected == alertBannerOptions) curSelected = 0; } else { - if (inEvent == INPUT_BROKER_SELECT || inEvent == INPUT_BROKER_ALT_LONG || inEvent == INPUT_BROKER_CANCEL) { + if (inEvent.inputEvent == INPUT_BROKER_SELECT || inEvent.inputEvent == INPUT_BROKER_ALT_LONG || + inEvent.inputEvent == INPUT_BROKER_CANCEL) { resetBanner(); + return; } } - inEvent = INPUT_BROKER_NONE; + inEvent.inputEvent = INPUT_BROKER_NONE; if (alertBannerMessage[0] == '\0') return; diff --git a/src/graphics/draw/NotificationRenderer.h b/src/graphics/draw/NotificationRenderer.h index 97a404d11..9c30b329c 100644 --- a/src/graphics/draw/NotificationRenderer.h +++ b/src/graphics/draw/NotificationRenderer.h @@ -11,7 +11,8 @@ namespace graphics class NotificationRenderer { public: - static char inEvent; + static InputEvent inEvent; + static char inKeypress; static int8_t curSelected; static char alertBannerMessage[256]; static uint32_t alertBannerUntil; // 0 is a special case meaning forever diff --git a/src/meshUtils.h b/src/meshUtils.h index 35b88e8b2..9fcf6f8a8 100644 --- a/src/meshUtils.h +++ b/src/meshUtils.h @@ -13,8 +13,9 @@ template constexpr const T &clamp(const T &v, const T &lo, const T &hi #if HAS_SCREEN #define IF_SCREEN(X) \ - if (screen) \ - X; + if (screen) { \ + X; \ + } #else #define IF_SCREEN(...) #endif diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 1ab4af02d..06a4993a7 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -716,7 +716,7 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event) } // Backspace - if (event->inputEvent == INPUT_BROKER_BACK) { + if (event->inputEvent == INPUT_BROKER_BACK && this->freetext.length() > 0) { payload = 0x08; lastTouchMillis = millis(); runOnce(); @@ -739,7 +739,8 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event) } // Cancel (dismiss freetext screen) - if (event->inputEvent == INPUT_BROKER_CANCEL || event->inputEvent == INPUT_BROKER_ALT_LONG) { + if (event->inputEvent == INPUT_BROKER_CANCEL || event->inputEvent == INPUT_BROKER_ALT_LONG || + (event->inputEvent == INPUT_BROKER_BACK && this->freetext.length() == 0)) { runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; freetext = ""; cursor = 0; @@ -989,6 +990,7 @@ int32_t CannedMessageModule::runOnce() } this->cursor--; } + } else { } break; case INPUT_BROKER_MSG_TAB: // Tab key: handled by input handler diff --git a/src/modules/KeyVerificationModule.cpp b/src/modules/KeyVerificationModule.cpp index 408d29126..574f231eb 100644 --- a/src/modules/KeyVerificationModule.cpp +++ b/src/modules/KeyVerificationModule.cpp @@ -2,6 +2,7 @@ #include "KeyVerificationModule.h" #include "MeshService.h" #include "RTC.h" +#include "graphics/draw/MenuHandler.h" #include "main.h" #include "modules/AdminModule.h" #include @@ -48,18 +49,22 @@ AdminMessageHandleResult KeyVerificationModule::handleAdminMessageForModule(cons bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_KeyVerification *r) { updateState(); - if (mp.pki_encrypted == false) + if (mp.pki_encrypted == false) { return false; - if (mp.from != currentRemoteNode) // because the inital connection request is handled in allocReply() + } + if (mp.from != currentRemoteNode) { // because the inital connection request is handled in allocReply() return false; + } if (currentState == KEY_VERIFICATION_IDLE) { return false; // if we're idle, the only acceptable message is an init, which should be handled by allocReply() + } - } else if (currentState == KEY_VERIFICATION_SENDER_HAS_INITIATED && r->nonce == currentNonce && r->hash2.size == 32 && - r->hash1.size == 0) { + if (currentState == KEY_VERIFICATION_SENDER_HAS_INITIATED && r->nonce == currentNonce && r->hash2.size == 32 && + r->hash1.size == 0) { memcpy(hash2, r->hash2.bytes, 32); - if (screen) - screen->showSimpleBanner("Enter Security Number", 30000); + IF_SCREEN(screen->showNumberPicker("Enter Security Number", 60000, 6, [](int number_picked) -> void { + keyVerificationModule->processSecurityNumber(number_picked); + });) meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed(); cn->level = meshtastic_LogRecord_Level_WARNING; @@ -79,23 +84,19 @@ bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket & memset(message, 0, sizeof(message)); sprintf(message, "Verification: \n"); generateVerificationCode(message + 15); - static const char *optionsArray[] = {"ACCEPT", "REJECT"}; + static const char *optionsArray[] = {"Reject", "Accept"}; LOG_INFO("Hash1 matches!"); - if (screen) { - graphics::BannerOverlayOptions options; - options.message = message; - options.durationMs = 30000; - options.optionsArrayPtr = optionsArray; - options.optionsCount = 2; - options.notificationType = graphics::notificationTypeEnum::selection_picker; - options.bannerCallback = [=](int selected) { - if (selected == 0) { - auto remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode); - remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK; - } - }; - screen->showOverlayBanner(options); - } + IF_SCREEN(graphics::BannerOverlayOptions options; options.message = message; options.durationMs = 30000; + options.optionsArrayPtr = optionsArray; options.optionsCount = 2; + options.notificationType = graphics::notificationTypeEnum::selection_picker; + options.bannerCallback = + [=](int selected) { + if (selected == 1) { + auto remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode); + remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK; + } + }; + screen->showOverlayBanner(options);) meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed(); cn->level = meshtastic_LogRecord_Level_WARNING; sprintf(cn->message, "Final confirmation for incoming manual key verification %s", message); @@ -120,6 +121,7 @@ bool KeyVerificationModule::sendInitialRequest(NodeNum remoteNode) // generate nonce updateState(); if (currentState != KEY_VERIFICATION_IDLE) { + graphics::menuHandler::menuQueue = graphics::menuHandler::throttle_message; return false; } currentNonce = random(); @@ -190,11 +192,8 @@ meshtastic_MeshPacket *KeyVerificationModule::allocReply() responsePacket = allocDataProtobuf(response); responsePacket->pki_encrypted = true; - if (screen) { - snprintf(message, 25, "Security Number \n%03u %03u", currentSecurityNumber / 1000, currentSecurityNumber % 1000); - screen->showSimpleBanner(message, 30000); - LOG_WARN("%s", message); - } + IF_SCREEN(snprintf(message, 25, "Security Number \n%03u %03u", currentSecurityNumber / 1000, currentSecurityNumber % 1000); + screen->showSimpleBanner(message, 30000); LOG_WARN("%s", message);) meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed(); cn->level = meshtastic_LogRecord_Level_WARNING; sprintf(cn->message, "Incoming Key Verification.\nSecurity Number\n%03u %03u", currentSecurityNumber / 1000, @@ -258,12 +257,7 @@ void KeyVerificationModule::processSecurityNumber(uint32_t incomingNumber) p->priority = meshtastic_MeshPacket_Priority_HIGH; service->sendToMesh(p, RX_SRC_LOCAL, true); currentState = KEY_VERIFICATION_SENDER_AWAITING_USER; - memset(message, 0, sizeof(message)); - sprintf(message, "Verification: \n"); - generateVerificationCode(message + 15); // send the toPhone packet - if (screen) { - screen->showSimpleBanner(message, 30000); - } + IF_SCREEN(screen->requestMenu(graphics::menuHandler::key_verification_final_prompt);) meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed(); cn->level = meshtastic_LogRecord_Level_WARNING; sprintf(cn->message, "Final confirmation for outgoing manual key verification %s", message); @@ -282,7 +276,7 @@ void KeyVerificationModule::processSecurityNumber(uint32_t incomingNumber) void KeyVerificationModule::updateState() { if (currentState != KEY_VERIFICATION_IDLE) { - // check for the 30 second timeout + // check for the 60 second timeout if (currentNonceTimestamp < getTime() - 60) { resetToIdle(); } else { diff --git a/src/modules/KeyVerificationModule.h b/src/modules/KeyVerificationModule.h index f659e961a..d5dba01d7 100644 --- a/src/modules/KeyVerificationModule.h +++ b/src/modules/KeyVerificationModule.h @@ -27,6 +27,8 @@ class KeyVerificationModule : public ProtobufModule }*/ virtual bool wantUIFrame() { return false; }; bool sendInitialRequest(NodeNum remoteNode); + void generateVerificationCode(char *); // fills char with the user readable verification code + uint32_t getCurrentRemoteNode() { return currentRemoteNode; } protected: /* Called to handle a particular incoming message @@ -56,9 +58,8 @@ class KeyVerificationModule : public ProtobufModule char message[40] = {0}; void processSecurityNumber(uint32_t); - void updateState(); // check the timeouts and maybe reset the state to idle - void resetToIdle(); // Zero out module state - void generateVerificationCode(char *); // fills char with the user readable verification code + void updateState(); // check the timeouts and maybe reset the state to idle + void resetToIdle(); // Zero out module state }; extern KeyVerificationModule *keyVerificationModule; \ No newline at end of file diff --git a/variants/t-deck/variant.h b/variants/t-deck/variant.h index 9fa0018ec..9b0de631a 100644 --- a/variants/t-deck/variant.h +++ b/variants/t-deck/variant.h @@ -68,6 +68,7 @@ #define TB_LEFT 1 #define TB_RIGHT 2 #define TB_PRESS 0 // BUTTON_PIN +#define TB_DIRECTION FALLING // microphone #define ES7210_SCK 47 From 107dec22bdd899501a5686a8c7f9eb445e625f0c Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Thu, 10 Jul 2025 10:12:02 -0500 Subject: [PATCH 30/72] Remove bogus validation check --- src/modules/AdminModule.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 0ba0e1164..8d3e710df 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -1327,12 +1327,6 @@ void AdminModule::handleSendInputEvent(const meshtastic_AdminMessage_InputEvent LOG_DEBUG("Processing input event: event_code=%u, kb_char=%u, touch_x=%u, touch_y=%u", inputEvent.event_code, inputEvent.kb_char, inputEvent.touch_x, inputEvent.touch_y); - // Validate input parameters - if (inputEvent.event_code > INPUT_BROKER_ANYKEY) { - LOG_WARN("Invalid input event code: %u", inputEvent.event_code); - return; - } - // Create InputEvent for injection InputEvent event = {.inputEvent = (input_broker_event)inputEvent.event_code, .kbchar = (unsigned char)inputEvent.kb_char, From 74c735d5fb71ebf36570ce8754b52a6f1a4c39f2 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Thu, 10 Jul 2025 10:20:44 -0500 Subject: [PATCH 31/72] Gate screen code behind IF_SCREEN() --- src/modules/KeyVerificationModule.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/modules/KeyVerificationModule.cpp b/src/modules/KeyVerificationModule.cpp index 574f231eb..f0ede345f 100644 --- a/src/modules/KeyVerificationModule.cpp +++ b/src/modules/KeyVerificationModule.cpp @@ -84,11 +84,10 @@ bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket & memset(message, 0, sizeof(message)); sprintf(message, "Verification: \n"); generateVerificationCode(message + 15); - static const char *optionsArray[] = {"Reject", "Accept"}; LOG_INFO("Hash1 matches!"); - IF_SCREEN(graphics::BannerOverlayOptions options; options.message = message; options.durationMs = 30000; - options.optionsArrayPtr = optionsArray; options.optionsCount = 2; - options.notificationType = graphics::notificationTypeEnum::selection_picker; + IF_SCREEN(static const char *optionsArray[] = {"Reject", "Accept"}; graphics::BannerOverlayOptions options; + options.message = message; options.durationMs = 30000; options.optionsArrayPtr = optionsArray; + options.optionsCount = 2; options.notificationType = graphics::notificationTypeEnum::selection_picker; options.bannerCallback = [=](int selected) { if (selected == 1) { @@ -121,7 +120,7 @@ bool KeyVerificationModule::sendInitialRequest(NodeNum remoteNode) // generate nonce updateState(); if (currentState != KEY_VERIFICATION_IDLE) { - graphics::menuHandler::menuQueue = graphics::menuHandler::throttle_message; + IF_SCREEN(graphics::menuHandler::menuQueue = graphics::menuHandler::throttle_message;) return false; } currentNonce = random(); From 5f5698ccc00777213784002f49162fc2ff147940 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Thu, 10 Jul 2025 10:29:33 -0500 Subject: [PATCH 32/72] Explicitly include meshUtils.h --- src/modules/KeyVerificationModule.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/KeyVerificationModule.cpp b/src/modules/KeyVerificationModule.cpp index f0ede345f..b1e23e807 100644 --- a/src/modules/KeyVerificationModule.cpp +++ b/src/modules/KeyVerificationModule.cpp @@ -4,6 +4,7 @@ #include "RTC.h" #include "graphics/draw/MenuHandler.h" #include "main.h" +#include "meshUtils.h" #include "modules/AdminModule.h" #include From 6d8c815558f6288b817d8d2c2c53d2c89ce3bbde Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 11:31:40 -0500 Subject: [PATCH 33/72] automated bumps (#7293) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- bin/org.meshtastic.meshtasticd.metainfo.xml | 3 +++ debian/changelog | 7 +++++-- version.properties | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/bin/org.meshtastic.meshtasticd.metainfo.xml b/bin/org.meshtastic.meshtasticd.metainfo.xml index 47082718a..291fe7a7c 100644 --- a/bin/org.meshtastic.meshtasticd.metainfo.xml +++ b/bin/org.meshtastic.meshtasticd.metainfo.xml @@ -87,6 +87,9 @@ + + https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.3 + https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.2 diff --git a/debian/changelog b/debian/changelog index 42488692b..b5009028a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -meshtasticd (2.7.2.0) UNRELEASED; urgency=medium +meshtasticd (2.7.3.0) UNRELEASED; urgency=medium [ Austin Lane ] * Initial packaging @@ -28,4 +28,7 @@ meshtasticd (2.7.2.0) UNRELEASED; urgency=medium [ ] * GitHub Actions Automatic version bump - -- Fri, 04 Jul 2025 11:58:01 +0000 + [ Ubuntu ] + * GitHub Actions Automatic version bump + + -- Ubuntu Thu, 10 Jul 2025 16:29:27 +0000 diff --git a/version.properties b/version.properties index 69f2d6af5..5de810523 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 7 -build = 2 +build = 3 From 6030bf50e04dc0e7d1c657a5dba50bd5aad0a09f Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Thu, 10 Jul 2025 11:39:38 -0500 Subject: [PATCH 34/72] Unbreak the macro --- src/modules/KeyVerificationModule.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/modules/KeyVerificationModule.cpp b/src/modules/KeyVerificationModule.cpp index b1e23e807..3b8225763 100644 --- a/src/modules/KeyVerificationModule.cpp +++ b/src/modules/KeyVerificationModule.cpp @@ -86,9 +86,11 @@ bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket & sprintf(message, "Verification: \n"); generateVerificationCode(message + 15); LOG_INFO("Hash1 matches!"); - IF_SCREEN(static const char *optionsArray[] = {"Reject", "Accept"}; graphics::BannerOverlayOptions options; - options.message = message; options.durationMs = 30000; options.optionsArrayPtr = optionsArray; - options.optionsCount = 2; options.notificationType = graphics::notificationTypeEnum::selection_picker; + static const char *optionsArray[] = {"Reject", "Accept"}; + // Don't try to put the array definition in the macro. Does not work with curly braces. + IF_SCREEN(graphics::BannerOverlayOptions options; options.message = message; options.durationMs = 30000; + options.optionsArrayPtr = optionsArray; options.optionsCount = 2; + options.notificationType = graphics::notificationTypeEnum::selection_picker; options.bannerCallback = [=](int selected) { if (selected == 1) { From 57c1c9286b9baa07047300cb0fba1f62d103a1de Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 17:11:00 -0500 Subject: [PATCH 35/72] Update RadioLib to v7.2.1 (#7287) 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 89720f0ad..59349139b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -104,7 +104,7 @@ lib_deps = [radiolib_base] lib_deps = # renovate: datasource=custom.pio depName=RadioLib packageName=jgromes/library/RadioLib - jgromes/RadioLib@7.2.0 + jgromes/RadioLib@7.2.1 [device-ui_base] lib_deps = From 1aad442ccc253133467c0cb5824445a9887523e1 Mon Sep 17 00:00:00 2001 From: Kongduino Date: Fri, 11 Jul 2025 06:11:19 +0800 Subject: [PATCH 36/72] Update platformio.ini (#7289) The link to the product should point to the vendor's website, not a random distributor. Co-authored-by: Ben Meadors Co-authored-by: Jonathan Bennett --- variants/seeed_xiao_nrf52840_kit/platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/seeed_xiao_nrf52840_kit/platformio.ini b/variants/seeed_xiao_nrf52840_kit/platformio.ini index 8c4c5a57b..0e1e94cd5 100644 --- a/variants/seeed_xiao_nrf52840_kit/platformio.ini +++ b/variants/seeed_xiao_nrf52840_kit/platformio.ini @@ -1,4 +1,4 @@ -; Seeed Xiao BLE: https://www.digikey.com/en/products/detail/seeed-technology-co-ltd/102010448/16652893 +; Seeed Xiao BLE: https://wiki.seeedstudio.com/XIAO_BLE/ [env:seeed_xiao_nrf52840_kit] extends = nrf52840_base board = xiao_ble_sense From fe534eae3784dbc5aa89d1cb1b016a332cf70f91 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 17:12:25 -0500 Subject: [PATCH 37/72] Update Adafruit BusIO to v1.17.2 (#7277) 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 59349139b..eb6d4f683 100644 --- a/platformio.ini +++ b/platformio.ini @@ -115,7 +115,7 @@ lib_deps = [environmental_base] lib_deps = # renovate: datasource=custom.pio depName=Adafruit BusIO packageName=adafruit/library/Adafruit BusIO - adafruit/Adafruit BusIO@1.17.1 + adafruit/Adafruit BusIO@1.17.2 # renovate: datasource=custom.pio depName=Adafruit Unified Sensor packageName=adafruit/library/Adafruit Unified Sensor adafruit/Adafruit Unified Sensor@1.1.15 # renovate: datasource=custom.pio depName=Adafruit BMP280 packageName=adafruit/library/Adafruit BMP280 Library From 093868f3edb11b71326c882eeab682af2b64e00b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 17:12:39 -0500 Subject: [PATCH 38/72] Update dorny/test-reporter action to v2.1.1 (#7284) 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 536d93665..dc05959fd 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.1.0 + uses: dorny/test-reporter@v2.1.1 with: name: PlatformIO Tests path: testreport.xml From be75f111560086e1bef91e49743fb919e287e084 Mon Sep 17 00:00:00 2001 From: Jason P Date: Thu, 10 Jul 2025 19:49:15 -0500 Subject: [PATCH 39/72] Update Screen Wake Default Behavior (#7282) * feat(display): enable screen wake on received messages * feat(menu): add Screen Wakeup option in system menu * feat(ui): update wake on message configuration and refactor save logic * feat(TextMessageModule): conditionally trigger screen wake on received message * Refactoring system menu options for notification and screen. * Fix MUI options in the system menu. * Build out Reboot/Shutdown Menu and consolidate options within it * Trunk fixes * Protobuf ref * Revert generated files * Update plumbing for screen_wakeup_menu * Begin work on crafting a method to stop screen wake for received messages * SharedUIDisplay.cpp doesn't need ExternalNotificationModule.h * Stop screen wake if External Notification is enabled * Removing extra log lines * Add role and battery state checks for not waking screen. Menu updates to resolve some Back options not being linked * Resolve some additional merge conflict related issues * Shouldn't throttle the power menu * Finalize renames of some menus * Flip Flop MUI Menu to avoid accidental clicks * NULL check for powerStatus * Remove "Wakeup" eNum * Update src/graphics/Screen.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * CoPilot was close this should fix the builds --------- Co-authored-by: whywilson Co-authored-by: Ben Meadors Co-authored-by: Jonathan Bennett Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/graphics/Screen.cpp | 79 +++++++---- src/graphics/Screen.h | 4 + src/graphics/draw/MenuHandler.cpp | 229 ++++++++++++++++++++++++------ src/graphics/draw/MenuHandler.h | 14 +- src/modules/TextMessageModule.cpp | 7 +- 5 files changed, 261 insertions(+), 72 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 2bade47b2..f670225c3 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -83,6 +83,29 @@ extern uint16_t TFT_MESH; #include "platform/portduino/PortduinoGlue.h" #endif +bool shouldWakeOnReceivedMessage() +{ + /* + The goal here is to determine when we do NOT wake up the screen on message received: + - Any ext. notifications are turned on + - If role is not client / client_mute + - If the battery level is very low + */ + if (moduleConfig.external_notification.enabled) { + return false; + } + if (config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT && + config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_MUTE) { + return false; + } + if (powerStatus && powerStatus->getBatteryChargePercent() < 10) { + return false; + } + return true; +} + +bool wake_on_received_message = shouldWakeOnReceivedMessage(); // Master Switch to enable here + using namespace meshtastic; /** @todo remove */ namespace graphics @@ -1257,40 +1280,46 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet) devicestate.has_rx_text_message = true; // Needed to include the message frame hasUnreadMessage = true; // Enables mail icon in the header setFrames(FOCUS_PRESERVE); // Refresh frame list without switching view - forceDisplay(); // Forces screen redraw - // === Prepare banner content === - const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from); - const char *longName = (node && node->has_user) ? node->user.long_name : nullptr; + // Only wake/force display if the configuration allows it + wake_on_received_message = shouldWakeOnReceivedMessage(); + if (wake_on_received_message) { + setOn(true); // Wake up the screen first + forceDisplay(); // Forces screen redraw - const char *msgRaw = reinterpret_cast(packet->decoded.payload.bytes); + // === Prepare banner content === + const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from); + const char *longName = (node && node->has_user) ? node->user.long_name : nullptr; - char banner[256]; + const char *msgRaw = reinterpret_cast(packet->decoded.payload.bytes); - // Check for bell character in message to determine alert type - bool isAlert = false; - for (size_t i = 0; i < packet->decoded.payload.size && i < 100; i++) { - if (msgRaw[i] == '\x07') { - isAlert = true; - break; + char banner[256]; + + // Check for bell character in message to determine alert type + bool isAlert = false; + for (size_t i = 0; i < packet->decoded.payload.size && i < 100; i++) { + if (msgRaw[i] == '\x07') { + isAlert = true; + break; + } } - } - if (isAlert) { - if (longName && longName[0]) { - snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName); + if (isAlert) { + if (longName && longName[0]) { + snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName); + } else { + strcpy(banner, "Alert Received"); + } } else { - strcpy(banner, "Alert Received"); + if (longName && longName[0]) { + snprintf(banner, sizeof(banner), "New Message from\n%s", longName); + } else { + strcpy(banner, "New Message"); + } } - } else { - if (longName && longName[0]) { - snprintf(banner, sizeof(banner), "New Message from\n%s", longName); - } else { - strcpy(banner, "New Message"); - } - } - screen->showSimpleBanner(banner, 3000); + screen->showSimpleBanner(banner, 3000); + } } } diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index 4deeb7395..19d14ecca 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -26,6 +26,8 @@ struct BannerOverlayOptions { }; } // namespace graphics +bool shouldWakeOnReceivedMessage(); + #if !HAS_SCREEN #include "power.h" namespace graphics @@ -123,6 +125,8 @@ class Screen #define SEGMENT_WIDTH 16 #define SEGMENT_HEIGHT 4 +extern bool wake_on_received_message; + /// Convert an integer GPS coords to a floating point #define DegD(i) (i * 1e-7) extern bool hasUnreadMessage; diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index c3a035c4f..f6b250ebc 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -129,11 +129,11 @@ void menuHandler::ClockFacePicker() screen->runNow(); } else if (selected == Digital) { uiconfig.is_clockface_analog = false; - nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig); + saveUIConfig(); screen->setFrames(Screen::FOCUS_CLOCK); } else { uiconfig.is_clockface_analog = true; - nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig); + saveUIConfig(); screen->setFrames(Screen::FOCUS_CLOCK); } }; @@ -346,37 +346,28 @@ void menuHandler::homeBaseMenu() void menuHandler::systemBaseMenu() { - // Check if brightness is supported bool hasSupportBrightness = false; #if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || HAS_TFT hasSupportBrightness = true; #endif - enum optionsNumbers { Back, Beeps, Brightness, Reboot, Color, MUI, Test, enumEnd }; + enum optionsNumbers { Back, Notifications, ScreenOptions, PowerMenu, Test, enumEnd }; static const char *optionsArray[enumEnd] = {"Back"}; static int optionsEnumArray[enumEnd] = {Back}; int options = 1; - optionsArray[options] = "Reboot"; - optionsEnumArray[options++] = Reboot; - - optionsArray[options] = "Beeps Action"; - optionsEnumArray[options++] = Beeps; - - if (hasSupportBrightness) { - optionsArray[options] = "Brightness"; - optionsEnumArray[options++] = Brightness; - } - -#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT - optionsArray[options] = "Screen Color"; - optionsEnumArray[options++] = Color; -#endif -#if HAS_TFT - optionsArray[options] = "Switch to MUI"; - optionsEnumArray[options++] = MUI; + optionsArray[options] = "Notifications"; + optionsEnumArray[options++] = Notifications; +#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || \ + defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT + optionsArray[options] = "Screen Options"; + optionsEnumArray[options++] = ScreenOptions; #endif + + optionsArray[options] = "Reboot/Shutdown"; + optionsEnumArray[options++] = PowerMenu; + if (test_enabled) { optionsArray[options] = "Test Menu"; optionsEnumArray[options++] = Test; @@ -388,20 +379,14 @@ void menuHandler::systemBaseMenu() bannerOptions.optionsCount = options; bannerOptions.optionsEnumPtr = optionsEnumArray; bannerOptions.bannerCallback = [](int selected) -> void { - if (selected == Beeps) { - menuHandler::menuQueue = menuHandler::buzzermodemenupicker; + if (selected == Notifications) { + menuHandler::menuQueue = menuHandler::notifications_menu; screen->runNow(); - } else if (selected == Brightness) { - menuHandler::menuQueue = menuHandler::brightness_picker; + } else if (selected == ScreenOptions) { + menuHandler::menuQueue = menuHandler::screen_options_menu; screen->runNow(); - } else if (selected == Reboot) { - menuHandler::menuQueue = menuHandler::reboot_menu; - screen->runNow(); - } else if (selected == MUI) { - menuHandler::menuQueue = menuHandler::mui_picker; - screen->runNow(); - } else if (selected == Color) { - menuHandler::menuQueue = menuHandler::tftcolormenupicker; + } else if (selected == PowerMenu) { + menuHandler::menuQueue = menuHandler::power_menu; screen->runNow(); } else if (selected == Test) { menuHandler::menuQueue = menuHandler::test_menu; @@ -533,22 +518,19 @@ void menuHandler::compassNorthMenu() if (selected == Dynamic) { if (uiconfig.compass_mode != meshtastic_CompassMode_DYNAMIC) { uiconfig.compass_mode = meshtastic_CompassMode_DYNAMIC; - nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, - &uiconfig); + saveUIConfig(); screen->setFrames(graphics::Screen::FOCUS_PRESERVE); } } else if (selected == Fixed) { if (uiconfig.compass_mode != meshtastic_CompassMode_FIXED_RING) { uiconfig.compass_mode = meshtastic_CompassMode_FIXED_RING; - nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, - &uiconfig); + saveUIConfig(); screen->setFrames(graphics::Screen::FOCUS_PRESERVE); } } else if (selected == Freeze) { if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING) { uiconfig.compass_mode = meshtastic_CompassMode_FREEZE_HEADING; - nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, - &uiconfig); + saveUIConfig(); screen->setFrames(graphics::Screen::FOCUS_PRESERVE); } } else if (selected == Back) { @@ -610,7 +592,7 @@ void menuHandler::BuzzerModeMenu() { static const char *optionsArray[] = {"All Enabled", "Disabled", "Notifications", "System Only"}; BannerOverlayOptions bannerOptions; - bannerOptions.message = "Beep Action"; + bannerOptions.message = "Buzzer Mode"; bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsCount = 4; bannerOptions.bannerCallback = [](int selected) -> void { @@ -660,7 +642,7 @@ void menuHandler::BrightnessPickerMenu() #endif // Save to device - nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig); + saveUIConfig(); LOG_INFO("Screen brightness set to %d", uiconfig.screen_brightness); } @@ -671,13 +653,13 @@ void menuHandler::BrightnessPickerMenu() void menuHandler::switchToMUIMenu() { - static const char *optionsArray[] = {"Yes", "No"}; + static const char *optionsArray[] = {"No", "Yes"}; BannerOverlayOptions bannerOptions; bannerOptions.message = "Switch to MUI?"; bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsCount = 2; bannerOptions.bannerCallback = [](int selected) -> void { - if (selected == 0) { + if (selected == 1) { config.display.displaymode = meshtastic_Config_DisplayConfig_DisplayMode_COLOR; config.bluetooth.enabled = false; service->reloadConfig(SEGMENT_CONFIG); @@ -742,6 +724,9 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display) TFT_MESH_r = 255; TFT_MESH_g = 255; TFT_MESH_b = 255; + } else { + menuQueue = system_base_menu; + screen->runNow(); } #if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT @@ -771,7 +756,7 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display) uiconfig.screen_rgb_color = (TFT_MESH_r << 16) | (TFT_MESH_g << 8) | TFT_MESH_b; } LOG_INFO("Storing Value of %d to uiconfig.screen_rgb_color", uiconfig.screen_rgb_color); - nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig); + saveUIConfig(); } #endif }; @@ -790,6 +775,29 @@ void menuHandler::rebootMenu() IF_SCREEN(screen->showSimpleBanner("Rebooting...", 0)); nodeDB->saveToDisk(); rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000; + } else { + menuQueue = power_menu; + screen->runNow(); + } + }; + screen->showOverlayBanner(bannerOptions); +} + +void menuHandler::shutdownMenu() +{ + static const char *optionsArray[] = {"Back", "Confirm"}; + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Shutdown Device?"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = 2; + bannerOptions.bannerCallback = [](int selected) -> void { + if (selected == 1) { + IF_SCREEN(screen->showSimpleBanner("Shutting Down...", 0)); + nodeDB->saveToDisk(); + power->shutdown(); + } else { + menuQueue = power_menu; + screen->runNow(); } }; screen->showOverlayBanner(bannerOptions); @@ -888,6 +896,117 @@ 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 + bool hasSupportBrightness = false; +#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || HAS_TFT + hasSupportBrightness = true; +#endif + + enum optionsNumbers { Back, Brightness, ScreenColor }; + static const char *optionsArray[4] = {"Back"}; + static int optionsEnumArray[4] = {Back}; + int options = 1; + + // Only show brightness for B&W displays + if (hasSupportBrightness && !HAS_TFT) { + optionsArray[options] = "Brightness"; + optionsEnumArray[options++] = Brightness; + } + + // Only show screen color for TFT displays +#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT + optionsArray[options] = "Screen Color"; + optionsEnumArray[options++] = ScreenColor; +#endif + + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Screen Options"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = options; + bannerOptions.optionsEnumPtr = optionsEnumArray; + bannerOptions.bannerCallback = [](int selected) -> void { + if (selected == Brightness) { + menuHandler::menuQueue = menuHandler::brightness_picker; + screen->runNow(); + } else if (selected == ScreenColor) { + menuHandler::menuQueue = menuHandler::tftcolormenupicker; + screen->runNow(); + } else { + menuQueue = system_base_menu; + screen->runNow(); + } + }; + screen->showOverlayBanner(bannerOptions); +} + +void menuHandler::powerMenu() +{ + + enum optionsNumbers { Back, Reboot, Shutdown, MUI }; + static const char *optionsArray[4] = {"Back"}; + static int optionsEnumArray[4] = {Back}; + int options = 1; + + optionsArray[options] = "Reboot"; + optionsEnumArray[options++] = Reboot; + + optionsArray[options] = "Shutdown"; + optionsEnumArray[options++] = Shutdown; + +#if HAS_TFT + optionsArray[options] = "Switch to MUI"; + optionsEnumArray[options++] = MUI; +#endif + + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Reboot / Shutdown"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = options; + bannerOptions.optionsEnumPtr = optionsEnumArray; + bannerOptions.bannerCallback = [](int selected) -> void { + if (selected == Reboot) { + menuHandler::menuQueue = menuHandler::reboot_menu; + screen->runNow(); + } else if (selected == Shutdown) { + menuHandler::menuQueue = menuHandler::shutdown_menu; + screen->runNow(); + } else if (selected == MUI) { + menuHandler::menuQueue = menuHandler::mui_picker; + screen->runNow(); + } else { + menuQueue = system_base_menu; + screen->runNow(); + } + }; + screen->showOverlayBanner(bannerOptions); +} + void menuHandler::keyVerificationInitMenu() { screen->showNodePicker("Node to Verify", 30000, @@ -941,6 +1060,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display) case clock_menu: clockMenu(); break; + case system_base_menu: + systemBaseMenu(); + break; case position_base_menu: positionBaseMenu(); break; @@ -970,6 +1092,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display) case reboot_menu: rebootMenu(); break; + case shutdown_menu: + shutdownMenu(); + break; case add_favorite: addFavoriteMenu(); break; @@ -994,6 +1119,15 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display) case bluetooth_toggle_menu: BluetoothToggleMenu(); break; + case notifications_menu: + notificationsMenu(); + break; + case screen_options_menu: + screenOptionsMenu(); + break; + case power_menu: + powerMenu(); + break; case throttle_message: screen->showSimpleBanner("Too Many Attempts\nTry again in 60 seconds.", 5000); break; @@ -1001,6 +1135,11 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display) menuQueue = menu_none; } +void menuHandler::saveUIConfig() +{ + nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig); +} + } // namespace graphics #endif \ No newline at end of file diff --git a/src/graphics/draw/MenuHandler.h b/src/graphics/draw/MenuHandler.h index 5846a3c91..2273dbbed 100644 --- a/src/graphics/draw/MenuHandler.h +++ b/src/graphics/draw/MenuHandler.h @@ -23,14 +23,19 @@ class menuHandler tftcolormenupicker, brightness_picker, reboot_menu, + shutdown_menu, add_favorite, remove_favorite, test_menu, number_test, wifi_toggle_menu, + bluetooth_toggle_menu, + notifications_menu, + screen_options_menu, + power_menu, + system_base_menu, key_verification_init, key_verification_final_prompt, - bluetooth_toggle_menu, throttle_message }; static screenMenus menuQueue; @@ -55,12 +60,19 @@ class menuHandler static void resetNodeDBMenu(); static void BrightnessPickerMenu(); static void rebootMenu(); + static void shutdownMenu(); static void addFavoriteMenu(); static void removeFavoriteMenu(); static void testMenu(); static void numberTest(); static void wifiBaseMenu(); static void wifiToggleMenu(); + static void notificationsMenu(); + static void screenOptionsMenu(); + static void powerMenu(); + + private: + static void saveUIConfig(); static void keyVerificationInitMenu(); static void keyVerificationFinalPrompt(); static void BluetoothToggleMenu(); diff --git a/src/modules/TextMessageModule.cpp b/src/modules/TextMessageModule.cpp index f1d01ad16..f0835073b 100644 --- a/src/modules/TextMessageModule.cpp +++ b/src/modules/TextMessageModule.cpp @@ -4,6 +4,7 @@ #include "PowerFSM.h" #include "buzz.h" #include "configuration.h" +#include "graphics/Screen.h" TextMessageModule *textMessageModule; ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp) @@ -17,7 +18,11 @@ ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp devicestate.rx_text_message = mp; devicestate.has_rx_text_message = true; - powerFSM.trigger(EVENT_RECEIVED_MSG); + wake_on_received_message = shouldWakeOnReceivedMessage(); + // Only trigger screen wake if configuration allows it + if (wake_on_received_message) { + powerFSM.trigger(EVENT_RECEIVED_MSG); + } notifyObservers(&mp); return ProcessMessage::CONTINUE; // Let others look at this message also if they want From 4bab148e3b75a51bdcd8aaa91ba0f904cf8a4a95 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Thu, 10 Jul 2025 22:51:43 -0500 Subject: [PATCH 40/72] Make the shouldWake function always available, and remove the bool (#7300) --- src/graphics/Screen.cpp | 46 ++++++++++++++----------------- src/graphics/Screen.h | 2 -- src/modules/TextMessageModule.cpp | 3 +- 3 files changed, 22 insertions(+), 29 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index f670225c3..59888c938 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -83,29 +83,6 @@ extern uint16_t TFT_MESH; #include "platform/portduino/PortduinoGlue.h" #endif -bool shouldWakeOnReceivedMessage() -{ - /* - The goal here is to determine when we do NOT wake up the screen on message received: - - Any ext. notifications are turned on - - If role is not client / client_mute - - If the battery level is very low - */ - if (moduleConfig.external_notification.enabled) { - return false; - } - if (config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT && - config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_MUTE) { - return false; - } - if (powerStatus && powerStatus->getBatteryChargePercent() < 10) { - return false; - } - return true; -} - -bool wake_on_received_message = shouldWakeOnReceivedMessage(); // Master Switch to enable here - using namespace meshtastic; /** @todo remove */ namespace graphics @@ -1282,8 +1259,7 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet) setFrames(FOCUS_PRESERVE); // Refresh frame list without switching view // Only wake/force display if the configuration allows it - wake_on_received_message = shouldWakeOnReceivedMessage(); - if (wake_on_received_message) { + if (shouldWakeOnReceivedMessage()) { setOn(true); // Wake up the screen first forceDisplay(); // Forces screen redraw @@ -1452,3 +1428,23 @@ bool Screen::isOverlayBannerShowing() #else graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {} #endif // HAS_SCREEN + +bool shouldWakeOnReceivedMessage() +{ + /* + The goal here is to determine when we do NOT wake up the screen on message received: + - Any ext. notifications are turned on + - If role is not client / client_mute + - If the battery level is very low + */ + if (moduleConfig.external_notification.enabled) { + return false; + } + if (!meshtastic_Config_DeviceConfig_Role_CLIENT && !meshtastic_Config_DeviceConfig_Role_CLIENT_MUTE) { + return false; + } + if (powerStatus && powerStatus->getBatteryChargePercent() < 10) { + return false; + } + return true; +} \ No newline at end of file diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index 19d14ecca..265900131 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -125,8 +125,6 @@ class Screen #define SEGMENT_WIDTH 16 #define SEGMENT_HEIGHT 4 -extern bool wake_on_received_message; - /// Convert an integer GPS coords to a floating point #define DegD(i) (i * 1e-7) extern bool hasUnreadMessage; diff --git a/src/modules/TextMessageModule.cpp b/src/modules/TextMessageModule.cpp index f0835073b..970f4429c 100644 --- a/src/modules/TextMessageModule.cpp +++ b/src/modules/TextMessageModule.cpp @@ -18,9 +18,8 @@ ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp devicestate.rx_text_message = mp; devicestate.has_rx_text_message = true; - wake_on_received_message = shouldWakeOnReceivedMessage(); // Only trigger screen wake if configuration allows it - if (wake_on_received_message) { + if (shouldWakeOnReceivedMessage()) { powerFSM.trigger(EVENT_RECEIVED_MSG); } notifyObservers(&mp); From 13ac182142ca4f5691aec8aa0c31f8c6b794c7cf Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Thu, 10 Jul 2025 23:19:58 -0500 Subject: [PATCH 41/72] Pick up nodedb.h in Screen.cpp regardless of HAS_SCREEN state --- src/graphics/Screen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 59888c938..57ea64fa9 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -21,6 +21,7 @@ along with this program. If not, see . */ #include "Screen.h" +#include "NodeDB.h" #include "PowerMon.h" #include "Throttle.h" #include "configuration.h" @@ -44,7 +45,6 @@ along with this program. If not, see . #endif #include "FSCommon.h" #include "MeshService.h" -#include "NodeDB.h" #include "RadioLibInterface.h" #include "error.h" #include "gps/GeoCoord.h" From 1063ef903495659a769290f36e9b2d1fabb2f4ce Mon Sep 17 00:00:00 2001 From: todd-herbert Date: Fri, 11 Jul 2025 17:30:48 +1200 Subject: [PATCH 42/72] Shorter audio feedback for InkHUD buttons (#7301) --- src/graphics/niche/InkHUD/Events.cpp | 8 ++++---- variants/ELECROW-ThinkNode-M1/nicheGraphics.h | 4 ++-- variants/heltec_vision_master_e213/nicheGraphics.h | 2 +- variants/heltec_vision_master_e290/nicheGraphics.h | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/graphics/niche/InkHUD/Events.cpp b/src/graphics/niche/InkHUD/Events.cpp index 2abe30793..cdda1638d 100644 --- a/src/graphics/niche/InkHUD/Events.cpp +++ b/src/graphics/niche/InkHUD/Events.cpp @@ -39,8 +39,8 @@ void InkHUD::Events::begin() void InkHUD::Events::onButtonShort() { // Audio feedback (via buzzer) - // Short low tone - playBoop(); + // Short tone + playChirp(); // Cancel any beeping, buzzing, blinking // Some button handling suppressed if we are dismissing an external notification (see below) bool dismissedExt = dismissExternalNotification(); @@ -64,8 +64,8 @@ void InkHUD::Events::onButtonShort() void InkHUD::Events::onButtonLong() { // Audio feedback (via buzzer) - // Low tone, longer than playBoop - playBeep(); + // Slightly longer than playChirp + playBoop(); // Check which system applet wants to handle the button press (if any) SystemApplet *consumer = nullptr; diff --git a/variants/ELECROW-ThinkNode-M1/nicheGraphics.h b/variants/ELECROW-ThinkNode-M1/nicheGraphics.h index b4395114f..f64de9d07 100644 --- a/variants/ELECROW-ThinkNode-M1/nicheGraphics.h +++ b/variants/ELECROW-ThinkNode-M1/nicheGraphics.h @@ -104,11 +104,11 @@ void setupNicheGraphics() buttons->setHandlerDown(1, [backlight]() { backlight->peek(); }); buttons->setHandlerLongPress(1, [backlight]() { backlight->latch(); - playBeep(); + playBoop(); }); buttons->setHandlerShortPress(1, [backlight]() { backlight->off(); - playBoop(); + playChirp(); }); // Begin handling button events diff --git a/variants/heltec_vision_master_e213/nicheGraphics.h b/variants/heltec_vision_master_e213/nicheGraphics.h index 6a75ad90d..1b1291424 100644 --- a/variants/heltec_vision_master_e213/nicheGraphics.h +++ b/variants/heltec_vision_master_e213/nicheGraphics.h @@ -107,7 +107,7 @@ void setupNicheGraphics() buttons->setWiring(1, PIN_BUTTON2); buttons->setHandlerShortPress(1, [inkhud]() { inkhud->nextTile(); - playBoop(); + playChirp(); }); // Begin handling button events diff --git a/variants/heltec_vision_master_e290/nicheGraphics.h b/variants/heltec_vision_master_e290/nicheGraphics.h index f29873c15..61b08c740 100644 --- a/variants/heltec_vision_master_e290/nicheGraphics.h +++ b/variants/heltec_vision_master_e290/nicheGraphics.h @@ -104,7 +104,7 @@ void setupNicheGraphics() buttons->setWiring(1, PIN_BUTTON2); buttons->setHandlerShortPress(1, [inkhud]() { inkhud->nextTile(); - playBoop(); + playChirp(); }); // Begin handling button events From f7ecf141b53428327448b6c5ff80867db307ff43 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 06:45:18 -0500 Subject: [PATCH 43/72] Update meshtastic/device-ui digest to 404c6e0 (#7302) 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 eb6d4f683..352d7e8d4 100644 --- a/platformio.ini +++ b/platformio.ini @@ -109,7 +109,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/8c7092c73425adfda1aac8c6960df06cd85f6d92.zip + https://github.com/meshtastic/device-ui/archive/404c6e06ecfda8dd2dc9e6d5fe417ae028f8029f.zip ; Common libs for environmental measurements in telemetry module [environmental_base] From 72f3d19d5af3b4b525a5cb5edc7e351be566c08c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 06:51:33 -0500 Subject: [PATCH 44/72] Upgrade trunk (#7278) Co-authored-by: sachaw <11172820+sachaw@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 0986e6eb0..f0271c856 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.447 - - renovate@41.23.4 + - checkov@3.2.450 + - renovate@41.29.1 - prettier@3.6.2 - trufflehog@3.89.2 - yamllint@1.37.1 From d42bde135f3b6308064b3313f64ef9db6bfd9003 Mon Sep 17 00:00:00 2001 From: Mictronics Date: Fri, 11 Jul 2025 13:54:37 +0200 Subject: [PATCH 45/72] Support native configuration Waveshare Pico LoRa module on Orange Pi Zero3 (#7295) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix LED pinout for T-Echo board marked v1.0, date 2021-6-28 * Merge PR #420 * Fixed double and missing Default class. * Use correct format specifier and fixed typo. * Removed duplicate code. * Fix error: #if with no expression * Fix warning: extra tokens at end of #endif directive. * Fix antenna switching logic. Complementary-pin control logic is required on the rp2040-lora board. * Fix deprecated macros. * Set RP2040 in dormant mode when deep sleep is triggered. * Fix array out of bounds read. * Admin key count needs to be set otherwise the key will be zero loaded after reset. * Don't reset the admin key size when loading defaults. Preserve an existing key in config if possible. * Remove log spam when reading INA voltage sensor. * Remove static declaration for admin keys from userPrefs.h. Load hard coded admin keys in case config file has empty slots. * Removed newlines from log. * Fix issue #5665. * Fix build for Pico2 RP2350 platform. * Enable Wifi client on Pico2W. * Use correct processor on Pico2. * Fix deprecated warning. * Update platform and framework for RP2350. * Added Pico2W variant including Wifi support. * Fix typo in used variant. * Remove obsolete define. * Fix for native Linux build. * Simplify RP2350 platform tag reference. Co-authored-by: Austin * Cast user prefs strings. * Update to last successfully building platform package. * Define I2C GPIOs to ensure usage of both ports. Possibly fixes #5361 * RAK11310 support for RAK12002 RTC added. * Update platform and framework packages to 4.4.3. * Use RP2040 base platform and framework package. Use RAK11300 board definition in arduino-pico framework. * Use RAK11300 board definition in arduino-pico framework. * Fix build when MESHTASTIC_EXCLUDE_GPS is defined. * Added configuration for Waveshare Pico LoRa module in combination with Orange Pi Zero3. * Equal to upstream master. --------- Co-authored-by: Ben Meadors Co-authored-by: Thomas Göttgens Co-authored-by: GUVWAF <78759985+GUVWAF@users.noreply.github.com> Co-authored-by: Austin Co-authored-by: Tom Fifield --- ...lora-ws-raspberry-pico-to-orangepi-03.yaml | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 bin/config.d/lora-ws-raspberry-pico-to-orangepi-03.yaml diff --git a/bin/config.d/lora-ws-raspberry-pico-to-orangepi-03.yaml b/bin/config.d/lora-ws-raspberry-pico-to-orangepi-03.yaml new file mode 100644 index 000000000..37d7e27d2 --- /dev/null +++ b/bin/config.d/lora-ws-raspberry-pico-to-orangepi-03.yaml @@ -0,0 +1,52 @@ +# https://www.waveshare.com/pico-lora-sx1262-868m.htm +# http://www.orangepi.org/html/hardWare/computerAndMicrocontrollers/details/Orange-Pi-Zero-3.html +# +# See Orange Pi Zero3 manual, chapter 3.16, page 124 for 26-pin header pinout +# +# Pin Connection +# Waveshare Orange Pi Zero3 +# 36 3.3V 17 +# 15 MOSI 19 +# 16 MISO 21 +# 14 CLK 23 +# 38 GND 25 +# 4 BUSY 18 +# 20 RESET 22 +# 5 CS 24 +# 26 DIO1/IRQ 26 + +Lora: + Module: sx1262 # Waveshare Raspberry Pico Lora module + DIO2_AS_RF_SWITCH: true + DIO3_TCXO_VOLTAGE: true + # Specify either the spidev1_1 or the CS below, not both! + # On DietPi Linux, when using the user overlay dietpi-spi1_1.dtbo, CS will be configured with spidev1.1 + spidev: spidev1.1 # See Orange Pi Zero3 manual, chapter 3.18.3, page 130 +# CS: # CS PIN_24 -> chip 1, line 233 +# pin: 24 +# gpiochip: 1 +# line: 233 + SCK: # SCK PIN_23 -> chip 1, line 230 + pin: 23 + gpiochip: 1 + line: 230 + Busy: # BUSY PIN_18 -> chip 1, line 78 + pin: 18 + gpiochip: 1 + line: 78 + MOSI: # MOSI PIN_19 -> chip 1, line 231 + pin: 19 + gpiochip: 1 + line: 231 + MISO: # MISO PIN_21 -> chip 1, line 232 + pin: 21 + gpiochip: 1 + line: 232 + Reset: # NRST PIN_22 -> chip 1, line 71 + pin: 22 + gpiochip: 1 + line: 71 + IRQ: # DIO1 PIN_26 -> chip 1, line 74 + pin: 26 + gpiochip: 1 + line: 74 From e9a551ae903bdd1269dbce5a1b7ee6dd9357f250 Mon Sep 17 00:00:00 2001 From: Austin Date: Fri, 11 Jul 2025 09:09:46 -0400 Subject: [PATCH 46/72] Load ringtone from userPrefs (#7298) * Load ringtone from userPrefs * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Ben Meadors Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/modules/ExternalNotificationModule.cpp | 6 +++--- userPrefs.jsonc | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/modules/ExternalNotificationModule.cpp b/src/modules/ExternalNotificationModule.cpp index 956508ce5..c17fcc4fa 100644 --- a/src/modules/ExternalNotificationModule.cpp +++ b/src/modules/ExternalNotificationModule.cpp @@ -362,9 +362,9 @@ ExternalNotificationModule::ExternalNotificationModule() if (nodeDB->loadProto(rtttlConfigFile, meshtastic_RTTTLConfig_size, sizeof(meshtastic_RTTTLConfig), &meshtastic_RTTTLConfig_msg, &rtttlConfig) != LoadFileResult::LOAD_SUCCESS) { memset(rtttlConfig.ringtone, 0, sizeof(rtttlConfig.ringtone)); - strncpy(rtttlConfig.ringtone, - "24:d=32,o=5,b=565:f6,p,f6,4p,p,f6,p,f6,2p,p,b6,p,b6,p,b6,p,b6,p,b,p,b,p,b,p,b,p,b,p,b,p,b,p,b,1p.,2p.,p", - sizeof(rtttlConfig.ringtone)); + // The default ringtone is always loaded from userPrefs.jsonc + strncpy(rtttlConfig.ringtone, USERPREFS_RINGTONE, sizeof(rtttlConfig.ringtone)); + rtttlConfig.ringtone[sizeof(rtttlConfig.ringtone) - 1] = '\0'; // Ensure null termination } LOG_INFO("Init External Notification Module"); diff --git a/userPrefs.jsonc b/userPrefs.jsonc index fc9e6ed72..c32bc7841 100644 --- a/userPrefs.jsonc +++ b/userPrefs.jsonc @@ -53,5 +53,6 @@ // "USERPREFS_MQTT_ENCRYPTION_ENABLED": "true", // "USERPREFS_MQTT_TLS_ENABLED": "false", // "USERPREFS_MQTT_ROOT_TOPIC": "event/REPLACEME", + "USERPREFS_RINGTONE": "24:d=32,o=5,b=565:f6,p,f6,4p,p,f6,p,f6,2p,p,b6,p,b6,p,b6,p,b6,p,b,p,b,p,b,p,b,p,b,p,b,p,b,p,b,1p.,2p.,p", "USERPREFS_TZ_STRING": "tzplaceholder " } From 9798a91e7b464e92cadd5ae9e1763b5799a1f030 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Fri, 11 Jul 2025 08:22:50 -0500 Subject: [PATCH 47/72] Delete ringtone.proto file for factory reset (#7303) --- src/mesh/NodeDB.cpp | 3 +++ src/modules/ExternalNotificationModule.cpp | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index a20acfda0..212b0dc33 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -474,6 +474,9 @@ bool NodeDB::factoryReset(bool eraseBleBonds) if (FSCom.exists("/static/rangetest.csv") && !FSCom.remove("/static/rangetest.csv")) { LOG_ERROR("Could not remove rangetest.csv file"); } + if (FSCom.exists("/prefs/ringtone.proto") && !FSCom.remove("/prefs/ringtone.proto")) { + LOG_ERROR("Could not remove ringtone.proto file"); + } #endif spiLock->unlock(); // second, install default state (this will deal with the duplicate mac address issue) diff --git a/src/modules/ExternalNotificationModule.cpp b/src/modules/ExternalNotificationModule.cpp index c17fcc4fa..76566d4da 100644 --- a/src/modules/ExternalNotificationModule.cpp +++ b/src/modules/ExternalNotificationModule.cpp @@ -364,7 +364,6 @@ ExternalNotificationModule::ExternalNotificationModule() memset(rtttlConfig.ringtone, 0, sizeof(rtttlConfig.ringtone)); // The default ringtone is always loaded from userPrefs.jsonc strncpy(rtttlConfig.ringtone, USERPREFS_RINGTONE, sizeof(rtttlConfig.ringtone)); - rtttlConfig.ringtone[sizeof(rtttlConfig.ringtone) - 1] = '\0'; // Ensure null termination } LOG_INFO("Init External Notification Module"); From 5ae8021aa6d01f1a36ff509c594d0481517f11ec Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Fri, 11 Jul 2025 08:28:21 -0500 Subject: [PATCH 48/72] I'm dumb --- src/mesh/NodeDB.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 212b0dc33..a20acfda0 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -474,9 +474,6 @@ bool NodeDB::factoryReset(bool eraseBleBonds) if (FSCom.exists("/static/rangetest.csv") && !FSCom.remove("/static/rangetest.csv")) { LOG_ERROR("Could not remove rangetest.csv file"); } - if (FSCom.exists("/prefs/ringtone.proto") && !FSCom.remove("/prefs/ringtone.proto")) { - LOG_ERROR("Could not remove ringtone.proto file"); - } #endif spiLock->unlock(); // second, install default state (this will deal with the duplicate mac address issue) From 1ca0584ba0db95a53136d4838f69bf46dff8a844 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 11 Jul 2025 16:09:59 -0500 Subject: [PATCH 49/72] Add first config override for Native (#7306) --- bin/config-dist.yaml | 4 ++++ src/mesh/NodeDB.cpp | 7 +++++++ src/platform/portduino/PortduinoGlue.cpp | 15 +++++++++++++++ src/platform/portduino/PortduinoGlue.h | 4 +++- 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml index b40fb85a5..b4cc81792 100644 --- a/bin/config-dist.yaml +++ b/bin/config-dist.yaml @@ -199,6 +199,10 @@ HostMetrics: # UserStringCommand: cat /sys/firmware/devicetree/base/serial-number # Command to execute, to send the results as the userString +Config: +# DisplayMode: TWOCOLOR # uncomment to force BaseUI +# DisplayMode: COLOR # uncomment to force MUI + General: MaxNodes: 200 MaxMessageQueue: 100 diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index a20acfda0..5ca515dd4 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1309,6 +1309,13 @@ void NodeDB::loadFromDisk() saveToDisk(SEGMENT_MODULECONFIG); } +#if ARCH_PORTDUINO + // set any config overrides + if (settingsMap[has_configDisplayMode]) { + config.display.displaymode = (_meshtastic_Config_DisplayConfig_DisplayMode)settingsMap[configDisplayMode]; + } + +#endif } /** Save a protobuf from a file, return true for success */ diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index 49d1acb4c..4ece2418d 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -665,6 +665,21 @@ bool loadConfig(const char *configPath) settingsStrings[hostMetrics_user_command] = (yamlConfig["HostMetrics"]["UserStringCommand"]).as(""); } + if (yamlConfig["Config"]) { + if (yamlConfig["Config"]["DisplayMode"]) { + settingsMap[has_configDisplayMode] = true; + if ((yamlConfig["Config"]["DisplayMode"]).as("") == "TWOCOLOR") { + settingsMap[configDisplayMode] = meshtastic_Config_DisplayConfig_DisplayMode_TWOCOLOR; + } else if ((yamlConfig["Config"]["DisplayMode"]).as("") == "INVERTED") { + settingsMap[configDisplayMode] = meshtastic_Config_DisplayConfig_DisplayMode_INVERTED; + } else if ((yamlConfig["Config"]["DisplayMode"]).as("") == "COLOR") { + settingsMap[configDisplayMode] = meshtastic_Config_DisplayConfig_DisplayMode_COLOR; + } else { + settingsMap[configDisplayMode] = meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT; + } + } + } + if (yamlConfig["General"]) { settingsMap[maxnodes] = (yamlConfig["General"]["MaxNodes"]).as(200); settingsMap[maxtophone] = (yamlConfig["General"]["MaxMessageQueue"]).as(100); diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index e404b7f1c..288870eef 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -109,7 +109,9 @@ enum configNames { mac_address, hostMetrics_interval, hostMetrics_channel, - hostMetrics_user_command + hostMetrics_user_command, + configDisplayMode, + has_configDisplayMode }; enum { no_screen, x11, fb, st7789, st7735, st7735s, st7796, ili9341, ili9342, ili9486, ili9488, hx8357d }; enum { no_touchscreen, xpt2046, stmpe610, gt911, ft5x06 }; From 05c32c99e44339eed83119eca6f49515d8c9801c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 18:46:29 -0500 Subject: [PATCH 50/72] Update meshtastic/device-ui digest to 86a09a7 (#7308) 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 352d7e8d4..b1f89e5b4 100644 --- a/platformio.ini +++ b/platformio.ini @@ -109,7 +109,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/404c6e06ecfda8dd2dc9e6d5fe417ae028f8029f.zip + https://github.com/meshtastic/device-ui/archive/86a09a7360f92d10053fbbf8d74f67f85b0ceb09.zip ; Common libs for environmental measurements in telemetry module [environmental_base] From deed6cd96a47404855c6dbe9cd22bf0caaa51c78 Mon Sep 17 00:00:00 2001 From: Austin Date: Sat, 12 Jul 2025 07:27:45 -0400 Subject: [PATCH 51/72] STM32: Properly ignore OneButton (#7311) --- arch/stm32/stm32.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/stm32/stm32.ini b/arch/stm32/stm32.ini index 1a0890b8a..be1ed662f 100644 --- a/arch/stm32/stm32.ini +++ b/arch/stm32/stm32.ini @@ -47,4 +47,4 @@ lib_deps = https://github.com/caveman99/Crypto/archive/eae9c768054118a9399690f8af202853d1ae8516.zip lib_ignore = - mathertel/OneButton@2.6.1 + OneButton From cb47325f0805ad2930ce3b433ff05df7c29782e9 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 12 Jul 2025 12:36:44 -0500 Subject: [PATCH 52/72] Seesaw Rotary (#7310) * Initial add of Adafruit seesaw encoder * Fully wire up seesaw * Trunk * Add #include configuration.h back to unbreak logging * Tryfix the dumb compilation error --------- Co-authored-by: Ben Meadors --- arch/portduino/portduino.ini | 7 ++- src/input/SeesawRotary.cpp | 83 ++++++++++++++++++++++++++++++++++++ src/input/SeesawRotary.h | 29 +++++++++++++ src/modules/Modules.cpp | 7 ++- 4 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 src/input/SeesawRotary.cpp create mode 100644 src/input/SeesawRotary.h diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini index 429e010f5..874f0c868 100644 --- a/arch/portduino/portduino.ini +++ b/arch/portduino/portduino.ini @@ -30,6 +30,8 @@ lib_deps = lovyan03/LovyanGFX@^1.2.0 # renovate: datasource=git-refs depName=libch341-spi-userspace packageName=https://github.com/pine64/libch341-spi-userspace gitBranch=main https://github.com/pine64/libch341-spi-userspace/archive/af9bc27c9c30fa90772279925b7c5913dff789b4.zip + # renovate: datasource=custom.pio depName=adafruit/Adafruit seesaw Library packageName=adafruit/library/Adafruit seesaw Library + adafruit/Adafruit seesaw Library@1.7.9 build_flags = ${arduino_base.build_flags} @@ -48,4 +50,7 @@ build_flags = -std=gnu17 -std=c++17 -lib_ignore = Adafruit NeoPixel +lib_ignore = + Adafruit NeoPixel + Adafruit ST7735 and ST7789 Library + SD diff --git a/src/input/SeesawRotary.cpp b/src/input/SeesawRotary.cpp new file mode 100644 index 000000000..c212773c4 --- /dev/null +++ b/src/input/SeesawRotary.cpp @@ -0,0 +1,83 @@ +#ifdef ARCH_PORTDUINO +#include "SeesawRotary.h" +#include "input/InputBroker.h" + +using namespace concurrency; + +SeesawRotary *seesawRotary; + +SeesawRotary::SeesawRotary(const char *name) : OSThread(name) +{ + _originName = name; +} + +bool SeesawRotary::init() +{ + if (inputBroker) + inputBroker->registerSource(this); + + if (!ss.begin(SEESAW_ADDR)) { + return false; + } + // attachButtonInterrupts(); + + uint32_t version = ((ss.getVersion() >> 16) & 0xFFFF); + if (version != 4991) { + LOG_WARN("Wrong firmware loaded? %u", version); + } else { + LOG_INFO("Found Product 4991"); + } + /* + #ifdef ARCH_ESP32 + // Register callbacks for before and after lightsleep + // Used to detach and reattach interrupts + lsObserver.observe(¬ifyLightSleep); + lsEndObserver.observe(¬ifyLightSleepEnd); + #endif + */ + ss.pinMode(SS_SWITCH, INPUT_PULLUP); + + // get starting position + encoder_position = ss.getEncoderPosition(); + + ss.setGPIOInterrupts((uint32_t)1 << SS_SWITCH, 1); + ss.enableEncoderInterrupt(); + canSleep = true; // Assume we should not keep the board awake + + return true; +} + +int32_t SeesawRotary::runOnce() +{ + InputEvent e; + e.inputEvent = INPUT_BROKER_NONE; + bool currentlyPressed = !ss.digitalRead(SS_SWITCH); + + if (currentlyPressed && !wasPressed) { + e.inputEvent = INPUT_BROKER_SELECT; + } + wasPressed = currentlyPressed; + + int32_t new_position = ss.getEncoderPosition(); + // did we move arounde? + if (encoder_position != new_position) { + if (encoder_position == 0 && new_position != 1) { + e.inputEvent = INPUT_BROKER_ALT_PRESS; + } else if (new_position == 0 && encoder_position != 1) { + e.inputEvent = INPUT_BROKER_USER_PRESS; + } else if (new_position > encoder_position) { + e.inputEvent = INPUT_BROKER_USER_PRESS; + } else { + e.inputEvent = INPUT_BROKER_ALT_PRESS; + } + encoder_position = new_position; + } + if (e.inputEvent != INPUT_BROKER_NONE) { + e.source = this->_originName; + e.kbchar = 0x00; + this->notifyObservers(&e); + } + + return 50; +} +#endif \ No newline at end of file diff --git a/src/input/SeesawRotary.h b/src/input/SeesawRotary.h new file mode 100644 index 000000000..3812b130a --- /dev/null +++ b/src/input/SeesawRotary.h @@ -0,0 +1,29 @@ +#pragma once +#ifdef ARCH_PORTDUINO + +#include "Adafruit_seesaw.h" +#include "InputBroker.h" +#include "concurrency/OSThread.h" +#include "configuration.h" + +#define SS_SWITCH 24 +#define SS_NEOPIX 6 + +#define SEESAW_ADDR 0x36 + +class SeesawRotary : public Observable, public concurrency::OSThread +{ + public: + const char *_originName; + bool init(); + SeesawRotary(const char *name); + int32_t runOnce() override; + + private: + Adafruit_seesaw ss; + int32_t encoder_position; + bool wasPressed = false; +}; + +extern SeesawRotary *seesawRotary; +#endif \ No newline at end of file diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index 403f36a04..3528f57f5 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -53,6 +53,7 @@ #endif #if ARCH_PORTDUINO #include "input/LinuxInputImpl.h" +#include "input/SeesawRotary.h" #include "modules/Telemetry/HostMetrics.h" #if !MESHTASTIC_EXCLUDE_STOREFORWARD #include "modules/StoreForwardModule.h" @@ -163,7 +164,6 @@ void setupModules() // Example: Put your module here // new ReplyModule(); #if (HAS_BUTTON || ARCH_PORTDUINO) && !MESHTASTIC_EXCLUDE_INPUTBROKER - if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) { rotaryEncoderInterruptImpl1 = new RotaryEncoderInterruptImpl1(); if (!rotaryEncoderInterruptImpl1->init()) { @@ -189,6 +189,11 @@ void setupModules() #endif // HAS_BUTTON #if ARCH_PORTDUINO if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) { + seesawRotary = new SeesawRotary("SeesawRotary"); + if (!seesawRotary->init()) { + delete seesawRotary; + seesawRotary = nullptr; + } aLinuxInputImpl = new LinuxInputImpl(); aLinuxInputImpl->init(); } From 41f52a65664966636b3084afce680ecabfa45b88 Mon Sep 17 00:00:00 2001 From: Austin Date: Sat, 12 Jul 2025 15:35:57 -0400 Subject: [PATCH 53/72] Build: Update platformio with `pkg install` (#7315) --- bin/build-esp32.sh | 2 +- bin/build-native.sh | 2 +- bin/build-nrf52.sh | 2 +- bin/build-rp2xx0.sh | 2 +- bin/build-stm32wl.sh | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bin/build-esp32.sh b/bin/build-esp32.sh index 96578e914..92836db23 100755 --- a/bin/build-esp32.sh +++ b/bin/build-esp32.sh @@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware* rm -r $OUTDIR/* || true # Important to pull latest version of libs into all device flavors, otherwise some devices might be stale -platformio pkg update -e $1 +platformio pkg install -e $1 echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS" rm -f .pio/build/$1/firmware.* diff --git a/bin/build-native.sh b/bin/build-native.sh index 51379ad76..fff86e87e 100755 --- a/bin/build-native.sh +++ b/bin/build-native.sh @@ -25,7 +25,7 @@ mkdir -p $OUTDIR/ rm -r $OUTDIR/* || true # Important to pull latest version of libs into all device flavors, otherwise some devices might be stale -pio pkg update --environment "$PIO_ENV" || platformioFailed +pio pkg install --environment "$PIO_ENV" || platformioFailed pio run --environment "$PIO_ENV" || platformioFailed cp ".pio/build/$PIO_ENV/program" "$OUTDIR/meshtasticd_linux_$(uname -m)" cp bin/native-install.* $OUTDIR diff --git a/bin/build-nrf52.sh b/bin/build-nrf52.sh index 9d0b3dfdd..deca209d2 100755 --- a/bin/build-nrf52.sh +++ b/bin/build-nrf52.sh @@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware* rm -r $OUTDIR/* || true # Important to pull latest version of libs into all device flavors, otherwise some devices might be stale -platformio pkg update -e $1 +platformio pkg install -e $1 echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS" rm -f .pio/build/$1/firmware.* diff --git a/bin/build-rp2xx0.sh b/bin/build-rp2xx0.sh index dad6a7e67..cb4865914 100755 --- a/bin/build-rp2xx0.sh +++ b/bin/build-rp2xx0.sh @@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware* rm -r $OUTDIR/* || true # Important to pull latest version of libs into all device flavors, otherwise some devices might be stale -platformio pkg update -e $1 +platformio pkg install -e $1 echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS" rm -f .pio/build/$1/firmware.* diff --git a/bin/build-stm32wl.sh b/bin/build-stm32wl.sh index 76c5a75fb..f62df4842 100755 --- a/bin/build-stm32wl.sh +++ b/bin/build-stm32wl.sh @@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware* rm -r $OUTDIR/* || true # Important to pull latest version of libs into all device flavors, otherwise some devices might be stale -platformio pkg update -e $1 +platformio pkg install -e $1 echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS" rm -f .pio/build/$1/firmware.* From 4342d51f5aef8868b80aca98abda38423d32e6fc Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 12 Jul 2025 14:44:58 -0500 Subject: [PATCH 54/72] Bump Framework-native and set version string. (#7317) --- arch/portduino/portduino.ini | 2 +- src/platform/portduino/PortduinoGlue.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini index 874f0c868..03a8a6583 100644 --- a/arch/portduino/portduino.ini +++ b/arch/portduino/portduino.ini @@ -2,7 +2,7 @@ [portduino_base] platform = # renovate: datasource=git-refs depName=platform-native packageName=https://github.com/meshtastic/platform-native gitBranch=develop - https://github.com/meshtastic/platform-native/archive/681ee029207e9fd040afa223df6e54074cbbe084.zip + https://github.com/meshtastic/platform-native/archive/6cb7a455b440dd0738e8ed74a18136ed5cf7ea63.zip framework = arduino build_src_filter = diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index 4ece2418d..685f0d077 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -35,6 +35,8 @@ char *configPath = nullptr; char *optionMac = nullptr; bool forceSimulated = false; +const char *argp_program_version = optstr(APP_VERSION); + // FIXME - move setBluetoothEnable into a HALPlatform class void setBluetoothEnable(bool enable) { From 86be2ac12fe2668a7fda8ddce098e827a8592b57 Mon Sep 17 00:00:00 2001 From: Austin Date: Sat, 12 Jul 2025 17:26:25 -0400 Subject: [PATCH 55/72] userPrefs: Set default ringtone nag time (#7314) --- src/mesh/Default.h | 5 +++++ src/mesh/NodeDB.cpp | 8 ++++---- src/modules/ExternalNotificationModule.cpp | 2 +- userPrefs.jsonc | 3 ++- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/mesh/Default.h b/src/mesh/Default.h index 7a38e21f1..2f05da98d 100644 --- a/src/mesh/Default.h +++ b/src/mesh/Default.h @@ -24,6 +24,11 @@ #define min_node_info_broadcast_secs 60 * 60 // No regular broadcasts of more than once an hour #define min_neighbor_info_broadcast_secs 4 * 60 * 60 #define default_map_publish_interval_secs 60 * 60 +#ifdef USERPREFS_RINGTONE_NAG_SECS +#define default_ringtone_nag_secs USERPREFS_RINGTONE_NAG_SECS +#else +#define default_ringtone_nag_secs 60 +#endif #define default_mqtt_address "mqtt.meshtastic.org" #define default_mqtt_username "meshdev" diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 5ca515dd4..270db6b2c 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -778,7 +778,7 @@ void NodeDB::installDefaultModuleConfig() moduleConfig.external_notification.output_buzzer = PIN_BUZZER; moduleConfig.external_notification.use_pwm = true; moduleConfig.external_notification.alert_message_buzzer = true; - moduleConfig.external_notification.nag_timeout = 60; + moduleConfig.external_notification.nag_timeout = default_ringtone_nag_secs; #endif #if defined(RAK4630) || defined(RAK11310) || defined(RAK3312) // Default to RAK led pin 2 (blue) @@ -787,7 +787,7 @@ void NodeDB::installDefaultModuleConfig() moduleConfig.external_notification.active = true; moduleConfig.external_notification.alert_message = true; moduleConfig.external_notification.output_ms = 1000; - moduleConfig.external_notification.nag_timeout = 60; + moduleConfig.external_notification.nag_timeout = default_ringtone_nag_secs; #endif #ifdef HAS_I2S @@ -796,10 +796,10 @@ void NodeDB::installDefaultModuleConfig() moduleConfig.external_notification.use_i2s_as_buzzer = true; moduleConfig.external_notification.alert_message_buzzer = true; #if HAS_TFT - if (moduleConfig.external_notification.nag_timeout == 60) + if (moduleConfig.external_notification.nag_timeout == default_ringtone_nag_secs) moduleConfig.external_notification.nag_timeout = 0; #else - moduleConfig.external_notification.nag_timeout = 60; + moduleConfig.external_notification.nag_timeout = default_ringtone_nag_secs; #endif #endif #ifdef NANO_G2_ULTRA diff --git a/src/modules/ExternalNotificationModule.cpp b/src/modules/ExternalNotificationModule.cpp index 76566d4da..5d7233279 100644 --- a/src/modules/ExternalNotificationModule.cpp +++ b/src/modules/ExternalNotificationModule.cpp @@ -363,7 +363,7 @@ ExternalNotificationModule::ExternalNotificationModule() &meshtastic_RTTTLConfig_msg, &rtttlConfig) != LoadFileResult::LOAD_SUCCESS) { memset(rtttlConfig.ringtone, 0, sizeof(rtttlConfig.ringtone)); // The default ringtone is always loaded from userPrefs.jsonc - strncpy(rtttlConfig.ringtone, USERPREFS_RINGTONE, sizeof(rtttlConfig.ringtone)); + strncpy(rtttlConfig.ringtone, USERPREFS_RINGTONE_RTTTL, sizeof(rtttlConfig.ringtone)); } LOG_INFO("Init External Notification Module"); diff --git a/userPrefs.jsonc b/userPrefs.jsonc index c32bc7841..3da8e7ba6 100644 --- a/userPrefs.jsonc +++ b/userPrefs.jsonc @@ -53,6 +53,7 @@ // "USERPREFS_MQTT_ENCRYPTION_ENABLED": "true", // "USERPREFS_MQTT_TLS_ENABLED": "false", // "USERPREFS_MQTT_ROOT_TOPIC": "event/REPLACEME", - "USERPREFS_RINGTONE": "24:d=32,o=5,b=565:f6,p,f6,4p,p,f6,p,f6,2p,p,b6,p,b6,p,b6,p,b6,p,b,p,b,p,b,p,b,p,b,p,b,p,b,p,b,1p.,2p.,p", + // "USERPREFS_RINGTONE_NAG_SECS": "60", + "USERPREFS_RINGTONE_RTTTL": "24:d=32,o=5,b=565:f6,p,f6,4p,p,f6,p,f6,2p,p,b6,p,b6,p,b6,p,b6,p,b,p,b,p,b,p,b,p,b,p,b,p,b,p,b,1p.,2p.,p", "USERPREFS_TZ_STRING": "tzplaceholder " } From 77768e9023f533789f6e9493f73d763b89683623 Mon Sep 17 00:00:00 2001 From: Austin Date: Sun, 13 Jul 2025 00:39:20 -0400 Subject: [PATCH 56/72] Remove Ubuntu oracular (#7322) --- .github/workflows/daily_packaging.yml | 2 +- .github/workflows/release_channels.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/daily_packaging.yml b/.github/workflows/daily_packaging.yml index 18939d567..63d24687b 100644 --- a/.github/workflows/daily_packaging.yml +++ b/.github/workflows/daily_packaging.yml @@ -30,7 +30,7 @@ jobs: strategy: fail-fast: false matrix: - series: [plucky, oracular, noble, jammy] + series: [plucky, noble, jammy] uses: ./.github/workflows/package_ppa.yml with: ppa_repo: ppa:meshtastic/daily diff --git a/.github/workflows/release_channels.yml b/.github/workflows/release_channels.yml index aac57fcbf..ed2de1717 100644 --- a/.github/workflows/release_channels.yml +++ b/.github/workflows/release_channels.yml @@ -20,7 +20,7 @@ jobs: strategy: fail-fast: false matrix: - series: [plucky, oracular, noble, jammy] + series: [plucky, noble, jammy] uses: ./.github/workflows/package_ppa.yml with: ppa_repo: |- From fd414ed1499a9245fd829bfe97f7e9d3c8b2c559 Mon Sep 17 00:00:00 2001 From: Andrew Yong Date: Sun, 13 Jul 2025 15:58:01 +0800 Subject: [PATCH 57/72] feat: DIY Seeed XIAO nRF52840 + EBYTE E22 variants, pin-compatible with Wio-SX1262 kit (#7105) These DIY builds are functionally similar to the legacy xiao_ble variant, but use a pinout harmonized with the officially-supported XIAO nRF52840 & Wio-SX1262 Kit for Meshtastic (SKU 102010710). An additional E22-900M33S variant is provided to ensure SX1262 transmit power is set below the maximum PA input for that module, to avoid damaging it. - seeed_xiao_nrf52840_e22_900m30s: - XIAO nRF52840 + EBYTE E22-900M30S - EBYTE E22 pinout matching Wio-SX1262 (SKU 113010003) - I2C - SDA: D6 - SCL: D7 - User button: D0 - Gain is programmed in firmware - user Tx power setting is the desired final output power - seeed_xiao_nrf52840_e22_900m33s: - XIAO nRF52840 + EBYTE E22-900M33S - EBYTE E22 pinout matching Wio-SX1262 (SKU 113010003) - I2C - SDA: D6 - SCL: D7 - User button: D0 - Gain is programmed in firmware - user Tx power setting is the desired final output power Signed-off-by: Andrew Yong --- variants/diy/platformio.ini | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/variants/diy/platformio.ini b/variants/diy/platformio.ini index 153796daf..f6b1c6766 100644 --- a/variants/diy/platformio.ini +++ b/variants/diy/platformio.ini @@ -96,6 +96,20 @@ board_level = extra build_flags = ${env:seeed_xiao_nrf52840_kit.build_flags} -D PRIVATE_HW -DXIAO_BLE_LEGACY_PINOUT -DEBYTE_E22 -DEBYTE_E22_900M30S build_unflags = -DGPS_L76K +; Seeed XIAO nRF52840 + EBYTE E22-900M30S - Pinout matching Wio-SX1262 (SKU 113010003) +[env:seeed_xiao_nrf52840_e22_900m30s] +extends = env:seeed_xiao_nrf52840_kit +board_level = extra +build_flags = ${env:seeed_xiao_nrf52840_kit.build_flags} -D PRIVATE_HW -DEBYTE_E22 -DEBYTE_E22_900M30S +build_unflags = -DGPS_L76K + +; Seeed XIAO nRF52840 + EBYTE E22-900M33S - Pinout matching Wio-SX1262 (SKU 113010003) +[env:seeed_xiao_nrf52840_e22_900m33s] +extends = env:seeed_xiao_nrf52840_kit +board_level = extra +build_flags = ${env:seeed_xiao_nrf52840_kit.build_flags} -D PRIVATE_HW -DEBYTE_E22 -DEBYTE_E22_900M33S +build_unflags = -DGPS_L76K + ; Seeed XIAO nRF52840 + XIAO Wio SX1262 DIY [env:seeed-xiao-nrf52840-wio-sx1262] board = xiao_ble_sense From 0133c5dc9e536a35f85f64319e52482fa0dbbd69 Mon Sep 17 00:00:00 2001 From: Andrew Yong Date: Sun, 13 Jul 2025 19:12:24 +0800 Subject: [PATCH 58/72] feat: New variant esp32c3_super_mini (#7133) https://www.espboards.dev/esp32/esp32-c3-super-mini/ DIY build by @AntonKartajaya on Meshtastic Discord and a PCB version WIP by https://github.com/NomDeTom. - I2C - I2C_SDA: 1 - I2C_SCL: 0 - OLED: SSD1306 - GPS - GPS_RX_PIN: 20 - GPS_TX_PIN: 21 - Button - BUTTON_PIN: 9 - SPI - SCK: 10 - MISO: 6 - MOSI: 7 - CS: 8 - LoRa: SX1262 - LORA_RESET: 5 - LORA_DIO1: 3 - LORA_RXEN: 2 - LORA_BUSY: 4 Signed-off-by: Andrew Yong --- .../diy/esp32c3_super_mini/pins_arduino.h | 24 ++++++++ variants/diy/esp32c3_super_mini/variant.h | 61 +++++++++++++++++++ variants/diy/platformio.ini | 13 ++++ 3 files changed, 98 insertions(+) create mode 100644 variants/diy/esp32c3_super_mini/pins_arduino.h create mode 100644 variants/diy/esp32c3_super_mini/variant.h diff --git a/variants/diy/esp32c3_super_mini/pins_arduino.h b/variants/diy/esp32c3_super_mini/pins_arduino.h new file mode 100644 index 000000000..a325b81eb --- /dev/null +++ b/variants/diy/esp32c3_super_mini/pins_arduino.h @@ -0,0 +1,24 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +static const uint8_t TX = 21; +static const uint8_t RX = 20; + +static const uint8_t SDA = 1; +static const uint8_t SCL = 0; + +static const uint8_t SS = 8; +static const uint8_t MOSI = 7; +static const uint8_t MISO = 6; +static const uint8_t SCK = 10; + +static const uint8_t A0 = 0; +static const uint8_t A1 = 1; +static const uint8_t A2 = 2; +static const uint8_t A3 = 3; +static const uint8_t A4 = 4; +static const uint8_t A5 = 5; + +#endif /* Pins_Arduino_h */ diff --git a/variants/diy/esp32c3_super_mini/variant.h b/variants/diy/esp32c3_super_mini/variant.h new file mode 100644 index 000000000..48c275912 --- /dev/null +++ b/variants/diy/esp32c3_super_mini/variant.h @@ -0,0 +1,61 @@ +#ifndef _VARIANT_ESP32C3_SUPER_MINI_ +#define _VARIANT_ESP32C3_SUPER_MINI_ + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// I2C (Wire) & OLED +#define WIRE_INTERFACES_COUNT (1) +#define I2C_SDA (1) +#define I2C_SCL (0) + +#define USE_SSD1306 + +// GPS +#undef GPS_RX_PIN +#undef GPS_TX_PIN +#define GPS_RX_PIN (20) +#define GPS_TX_PIN (21) + +// Button +#define BUTTON_PIN (9) // BOOT button + +// LoRa +#define USE_LLCC68 +#define USE_SX1262 +// #define USE_RF95 +#define USE_SX1268 + +#define LORA_DIO0 RADIOLIB_NC +#define LORA_RESET (5) +#define LORA_DIO1 (3) +#define LORA_RXEN (2) +#define LORA_BUSY (4) +#define LORA_SCK (10) +#define LORA_MISO (6) +#define LORA_MOSI (7) +#define LORA_CS (8) + +#define SX126X_CS LORA_CS +#define SX126X_DIO1 LORA_DIO1 +#define SX126X_BUSY LORA_BUSY +#define SX126X_RESET LORA_RESET +#define SX126X_RXEN LORA_RXEN + +#define SX126X_DIO3_TCXO_VOLTAGE (1.8) +#define TCXO_OPTIONAL // make it so that the firmware can try both TCXO and XTAL + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ + +#endif diff --git a/variants/diy/platformio.ini b/variants/diy/platformio.ini index f6b1c6766..1f0f6d126 100644 --- a/variants/diy/platformio.ini +++ b/variants/diy/platformio.ini @@ -141,3 +141,16 @@ build_flags = -D ARDUINO_USB_MODE=0 -D ARDUINO_USB_CDC_ON_BOOT=1 -I variants/diy/t-energy-s3_e22 + +; ESP32 C3 Super Mini Development Board +; https://www.espboards.dev/esp32/esp32-c3-super-mini/ +[env:esp32c3_super_mini] +extends = esp32c3_base +board = esp32-c3-devkitm-1 +build_flags = + ${esp32_base.build_flags} + -D PRIVATE_HW + -I variants/diy/esp32c3_super_mini + -D ARDUINO_USB_MODE=1 + -D ARDUINO_USB_CDC_ON_BOOT=1 +board_level = extra From b49e59b9048dbcc86ed7af9fd41e3fbe892ba40e Mon Sep 17 00:00:00 2001 From: Andrew Yong Date: Sun, 13 Jul 2025 19:17:50 +0800 Subject: [PATCH 59/72] xiao_ble README.md updates (#7283) * docs(xiao_ble): Simplify building and flashing instructions - **Update Bootloader** - deleted this section, as Meshtastic now builds-in a compatible SoftDevice - **PlatformIO Environment Preparation** - deleted this section, as Meshtastic now builds-in a compatible SoftDevice - **Build Meshtastic** - simplified it greatly by referring to Meshtastic documentation - **Flash the firmware to the Xiao BLE** - simplified it greatly as Meshtastic now builds firmware.uf2; added some observations for a succesful flash Light cleanup of Markdown and renumbering of sections. Signed-off-by: Andrew Yong * docs(xiao_ble): Replace some HTML with Markdown, cleanup Markdown Signed-off-by: Andrew Yong * docs(xiao_ble): Update SX126X_TXEN definition location Signed-off-by: Andrew Yong * docs(xiao_ble): Fresher information about E22 modules Signed-off-by: Andrew Yong * docs(xiao_ble): Instructions for E22...M33S modules Also re-order the Build section to come after the Wiring section since the build instructions require special attention if the wiring/modules differ from the variant's expected pins/module. Signed-off-by: Andrew Yong * docs(xiao_ble): Rename all XIAO BLE to XIAO nRF52840 Signed-off-by: Andrew Yong * docs(xiao_ble): Remove note about Linux since shell script is gone Signed-off-by: Andrew Yong * docs(xiao_ble): trunk fmt and fix links Signed-off-by: Andrew Yong --------- Signed-off-by: Andrew Yong --- variants/diy/xiao_ble/README.md | 308 +++++++++++--------------------- 1 file changed, 106 insertions(+), 202 deletions(-) diff --git a/variants/diy/xiao_ble/README.md b/variants/diy/xiao_ble/README.md index 2a08138ba..fe6dcba2d 100644 --- a/variants/diy/xiao_ble/README.md +++ b/variants/diy/xiao_ble/README.md @@ -1,264 +1,168 @@ -# +# XIAO nrf52840/nrf52840 Sense + Ebyte E22-900M30S -

- Xiao BLE/BLE Sense + Ebyte E22-900M30S -

- -

- A step-by-step guide for macOS and Linux -

+_A step-by-step guide for macOS and Linux._ ## Introduction -This guide will walk you through everything needed to get the Xiao BLE (or BLE Sense) running Meshtastic using an Ebyte E22-900M30S LoRa module. The combination of the E22 with an nRF52840 MCU is desirable because it allows for both very low idle (Rx) power draw and high transmit power. The Xiao BLE is a small but surprisingly well-appointed nRF52840 board, with enough GPIO for most Meshtastic applications and a built-in LiPo charger. The E22, on the other hand, is a famously inscrutable and mysterious beast. It is one of the more readily available LoRa modules capable of transmitting at 30 dBm, and includes an LNA to boost its Rx sensitivity a few dB beyond that of the SX1262. However, its documentation is relatively sparse overall, and seems to merely hint at (or completely omit) several key details regarding its functionality. Thus, much of what follows is a synthesis of my observations and inferences over the course of many hours of trial and error. +This guide will walk you through everything needed to get the XIAO nrf52840 (or XIAO nrf52840 Sense) running Meshtastic using an Ebyte E22-900M30S LoRa module. The combination of the E22 with an nRF52840 MCU is desirable because it allows for both very low idle (Rx) power draw _and_ high transmit power. -

Acknowledgement and friendly disclaimer

+The XIAO nrf52840 is a small but surprisingly well-appointed nRF52840 board, with enough GPIO for most Meshtastic applications and a built-in LiPo charger. + +The E22, on the other hand, is a famously inscrutable and mysterious beast. It is one of the more readily available LoRa modules capable of transmitting at 30 dBm, and includes an LNA to boost its Rx sensitivity a few dB beyond that of the SX1262. + +However, its documentation is relatively sparse overall, and seems to merely hint at (or completely omit) several key details regarding its functionality. Thus, much of what follows is a synthesis of my observations and inferences over the course of many hours of trial and error. + +### Acknowledgement and Friendly Disclaimer Huge thanks to those in the community who have forged the way with the E22, without whose hard work none of this would have been possible! (thebentern, riddick, rainer_vie, beegee-tokyo, geeksville, caveman99, Der_Bear, PlumRugOfDoom, BigCorvus, and many others.) -
- Please take the conclusions here as a tentative work in progress, representing my current (and fairly limited) understanding of the E22 when paired with this particular MCU. It is my hope that this guide will be helpful to others who are interested in trying a DIY Meshtastic build, and also be subject to revision by folks with more experience and better test equipment. -### Obligatory liability disclaimer +### Obligatory Liability Disclaimer This guide and all associated content is for informational purposes only. The information presented is intended for consumption only by persons having appropriate technical skill and judgement, to be used entirely at their own discretion and risk. The authors of this guide in no way provide any warranty, express or implied, toward the content herein, nor its correctness, safety, or suitability to any particular purpose. By following the instructions in this guide in part or in full, you assume all responsibility for all potential risks, including but not limited to fire, property damage, bodily injury, and death. -### Note +## 1. Wire the board -These instructions assume you are running macOS or Linux, but it should be relatively easy to translate each command for Windows. (In this case, in step 2 below, each line of `xiao_ble.sh` would also need to be converted to the equivalent Windows CLI command and run individually.) +Connecting the E22 to the XIAO nrf52840 is straightforward, but there are a few gotchas to be mindful of. -## 1. Update Bootloader +### On the XIAO nrf52840 -The first thing you will need to do is update the Xiao BLE's bootloader. The stock bootloader is functionally very similar to the Adafruit nRF52 UF2 bootloader, but apparently not quite enough so to work with Meshtastic out of the box. +- Pins D4 and D5 are currently mapped to `PIN_WIRE_SDA` and `PIN_WIRE_SCL`, respectively. If you are not using I²C and would like to free up pins D4 and D5 for use as GPIO, `PIN_WIRE_SDA` and `PIN_WIRE_SCL` can be reassigned to any two other unused pin numbers. +- Pins D6 and D7 were originally mapped to the TX and RX pins for serial interface 1 (`PIN_SERIAL1_RX` and `PIN_SERIAL1_TX`) but are currently set to -1 in `variant.h`. If you need to expose a serial interface, you can restore these pins and move e.g. `SX126X_RXEN` to pin 4 or 5 (the opposite should work too). -1. Connect the Xiao BLE to your computer via USB-C. +### On the E22 -2. Install `adafruit-nrfutil` by following the instructions here. +- There are two options for the E22's `TXEN` pin: + 1. It can be connected to the MCU on the pin defined as `SX126X_TXEN` in `variant.h`. In this configuration, the MCU will control Tx/Rx switching "manually". As long as `SX126X_TXEN` and `SX126X_RXEN` are both defined in `variant.h` (and neither is set to `RADIOLIB_NC`), `SX126xInterface.cpp` will initialize the E22 correctly for this mode. + 2. Alternately, it can be connected to the E22's `DIO2` pin only, with neither `TXEN` nor `DIO2` being connected to the MCU. In this configuration, the E22 will control Tx/Rx switching automatically. In `variant.h`, as long as `SX126X_TXEN` is defined as `RADIOLIB_NC`, and `SX126X_RXEN` is defined and connected to the E22's `RXEN` pin, and `E22_TXEN_CONNECTED_TO_DIO2` is defined, `SX126xInterface.cpp` will initialize the E22 correctly for this mode. This configuration frees up a GPIO, and presents no drawbacks that I have found. +- Note that any combination other than the two described above will likely result in unexpected behavior. In my testing, some of these other configurations appeared to "work" at first glance, but every one I tried had at least one of the following flaws: weak Tx power, extremely poor Rx sensitivity, or the E22 overheating because TXEN was never pulled low, causing its PA to stay on indefinitely. +- Along the same lines, it is a good idea to check the E22's temperature frequently by lightly touching the shield. If you feel the shield getting hot (i.e. approaching uncomfortable to touch) near pins 1, 2, and 3, something is probably misconfigured; disconnect both the XIAO nrf52840 and E22 from power and double check wiring and pin mapping. +- Whether you opt to let the E22 control Rx and Tx or handle this manually, **the E22's `RXEN` pin must always be connected to the MCU** on the pin defined as `SX126X_RXEN` in `variant.h`. -3. Open a terminal window and navigate to `firmware/variants/xiao_ble` (where `firmware` is the directory into which you have cloned the Meshtastic firmware repo). +#### Note -4. Run the following command, replacing `/dev/cu.usbmodem2101` with the serial port your Xiao BLE is connected to: +The default pin mapping in `variant.h` uses "Automatic Tx/Rx switching" mode. - ```bash - adafruit-nrfutil --verbose dfu serial --package xiao_nrf52840_ble_bootloader-0.7.0-22-g277a0c8_s140_7.3.0.zip --port /dev/cu.usbmodem2101 -b 115200 --singlebank --touch 1200 - ``` +If you wire your board for Manual Tx/Rx Switching Mode, `SX126X_TXEN` must be defined (`#define #define SX126X_TXEN D6`) in `variants/seeed_xiao_nrf52840_kit/variant.h` in the code block following: -5. If all goes well, the Xiao BLE's red LED should start to pulse slowly, and you should see a new USB storage device called `XIAO-BOOT` appear under `Locations` in Finder. +```c +#ifdef XIAO_BLE_LEGACY_PINOUT +// Legacy xiao_ble variant pinout for third-party SX126x modules e.g. EBYTE E22 +``` -  +### Example Wiring for Automatic Tx/Rx Switching Mode -## 2. PlatformIO Environment Preparation +#### MCU -> E22 Connections -Before building Meshtastic for the Xiao BLE + E22, it is necessary to pull in SoftDevice 7.3.0 and its associated linker script (nrf52840_s140_v7.ld) from Seeed Studio's Arduino core. The `xiao_ble.sh` script does this. +| XIAO nrf52840 pin | variant.h definition | E22 pin | Notes | +| :---------------- | :------------------- | :-------- | :------------------------------------------------------------------------------------------------------------------- | +| D0 | SX126X_CS | 19 (NSS) | | +| D1 | SX126X_DIO1 | 13 (DIO1) | | +| D2 | SX126X_BUSY | 14 (BUSY) | | +| D3 | SX126X_RESET | 15 (NRST) | | +| D7 | SX126X_RXEN | 6 (RXEN) | These pins must still be connected, and `SX126X_RXEN` defined in `variant.h`, otherwise Rx sensitivity will be poor. | +| D8 | PIN_SPI_SCK | 18 (SCK) | | +| D9 | PIN_SPI_MISO | 16 (MISO) | | +| D10 | PIN_SPI_MOSI | 17 (MOSI) | | -1. In your terminal window, run the following command: - - ```bash - sudo ./xiao_ble.sh - ``` - -  - -## 3. Build Meshtastic - -At this point, you should be able to build the firmware successfully. - -1. In VS Code, press `Command Shift P` to bring up the command palette. - -2. Search for and run the `Developer: Reload Window` command. - -3. Bring up the command palette again with `Command Shift P`. Search for and run the `PlatformIO: Pick Project Environment` command. - -4. In the list of environments, select `env:xiao_ble`. PlatformIO may update itself for a minute or two, and should let you know once done. - -5. Return to the command palette once again (`Command Shift P`). Search for and run the `PlatformIO: Build` command. - -6. PlatformIO will build the project. After a few minutes you should see a green `SUCCESS` message. - -  - -## 4. Wire the board - -Connecting the E22 to the Xiao BLE is straightforward, but there are a few gotchas to be mindful of. - -- On the Xiao BLE: - - - Pins D4 and D5 are currently mapped to `PIN_WIRE_SDA` and `PIN_WIRE_SCL`, respectively. If you are not using I²C and would like to free up pins D4 and D5 for use as GPIO, `PIN_WIRE_SDA` and `PIN_WIRE_SCL` can be reassigned to any two other unused pin numbers. - - - Pins D6 and D7 were originally mapped to the TX and RX pins for serial interface 1 (`PIN_SERIAL1_RX` and `PIN_SERIAL1_TX`) but are currently set to -1 in `variant.h`. If you need to expose a serial interface, you can restore these pins and move e.g. `SX126X_RXEN` to pin 4 or 5 (the opposite should work too). - -- On the E22: - - - There are two options for the E22's `TXEN` pin: - - 1. It can be connected to the MCU on the pin defined as `SX126X_TXEN` in `variant.h`. In this configuration, the MCU will control Tx/Rx switching "manually". As long as `SX126X_TXEN` and `SX126X_RXEN` are both defined in `variant.h` (and neither is set to `RADIOLIB_NC`), `SX126xInterface.cpp` will initialize the E22 correctly for this mode. - - 2. Alternately, it can be connected to the E22's `DIO2` pin only, with neither `TXEN` nor `DIO2` being connected to the MCU. In this configuration, the E22 will control Tx/Rx switching automatically. In `variant.h`, as long as `SX126X_TXEN` is defined as `RADIOLIB_NC`, and `SX126X_RXEN` is defined and connected to the E22's `RXEN` pin, and `E22_TXEN_CONNECTED_TO_DIO2` is defined, `SX126xInterface.cpp` will initialize the E22 correctly for this mode. This configuration frees up a GPIO, and presents no drawbacks that I have found. - - - Note that any combination other than the two described above will likely result in unexpected behavior. In my testing, some of these other configurations appeared to "work" at first glance, but every one I tried had at least one of the following flaws: weak Tx power, extremely poor Rx sensitivity, or the E22 overheating because TXEN was never pulled low, causing its PA to stay on indefinitely. - - - Along the same lines, it is a good idea to check the E22's temperature frequently by lightly touching the shield. If you feel the shield getting hot (i.e. approaching uncomfortable to touch) near pins 1, 2, and 3, something is probably misconfigured; disconnect both the Xiao BLE and E22 from power and double check wiring and pin mapping. - - - Whether you opt to let the E22 control Rx and Tx or handle this manually, the E22's `RXEN` pin must always be connected to the MCU on the pin defined as `SX126X_RXEN` in `variant.h`. - -

Note

- -The default pin mapping in `variant.h` uses 'automatic Tx/Rx switching' mode. If you wire your board for manual Rx/Tx switching, make sure to update `variant.h` accordingly by commenting/uncommenting the necessary lines in the 'E22 Tx/Rx control options' section. - -  - ---- - -  - -

Example wiring for "E22 automatic Tx/Rx switching" mode:

-  - -MCU -> E22 connections - -| Xiao BLE pin | variant.h definition | E22 pin | Notes | -| :----------- | :------------------- | :-------- | :------------------------------------------------------------------------------------------------------------------- | -| D0 | SX126X_CS | 19 (NSS) | | -| D1 | SX126X_DIO1 | 13 (DIO1) | | -| D2 | SX126X_BUSY | 14 (BUSY) | | -| D3 | SX126X_RESET | 15 (NRST) | | -| D7 | SX126X_RXEN | 6 (RXEN) | These pins must still be connected, and `SX126X_RXEN` defined in `variant.h`, otherwise Rx sensitivity will be poor. | -| D8 | PIN_SPI_SCK | 18 (SCK) | | -| D9 | PIN_SPI_MISO | 16 (MISO) | | -| D10 | PIN_SPI_MOSI | 17 (MOSI) | | - -  -  - -E22 -> E22 connections: +#### E22 -> E22 Connections | E22 pin | E22 pin | Notes | | :------ | :------ | :------------------------------------------------------------------------ | | TXEN | DIO2 | These must be physically connected for automatic Tx/Rx switching to work. | -

Note

+#### Note The schematic (`xiao-ble-e22-schematic.png`) in the `eagle-project` directory uses this wiring. -  +### Example Wiring for Manual Tx/Rx Switching Mode ---- +#### MCU -> E22 Connections -  +| XIAO nrf52840 pin | variant.h definition | E22 pin | Notes | +| :---------------- | :------------------- | :-------- | :---- | +| D0 | SX126X_CS | 19 (NSS) | | +| D1 | SX126X_DIO1 | 13 (DIO1) | | +| D2 | SX126X_BUSY | 14 (BUSY) | | +| D3 | SX126X_RESET | 15 (NRST) | | +| D6 | SX126X_TXEN | 7 (TXEN) | | +| D7 | SX126X_RXEN | 6 (RXEN) | | +| D8 | PIN_SPI_SCK | 18 (SCK) | | +| D9 | PIN_SPI_MISO | 16 (MISO) | | +| D10 | PIN_SPI_MOSI | 17 (MOSI) | | -

Example wiring for "Manual Tx/Rx switching" mode:

+#### E22 -> E22 connections -MCU -> E22 connections +_(none)_ -| Xiao BLE pin | variant.h definition | E22 pin | Notes | -| :----------- | :------------------- | :-------- | :---- | -| D0 | SX126X_CS | 19 (NSS) | | -| D1 | SX126X_DIO1 | 13 (DIO1) | | -| D2 | SX126X_BUSY | 14 (BUSY) | | -| D3 | SX126X_RESET | 15 (NRST) | | -| D6 | SX126X_TXEN | 7 (TXEN) | | -| D7 | SX126X_RXEN | 6 (RXEN) | | -| D8 | PIN_SPI_SCK | 18 (SCK) | | -| D9 | PIN_SPI_MISO | 16 (MISO) | | -| D10 | PIN_SPI_MOSI | 17 (MOSI) | | +## 2. Build Meshtastic -E22 -> E22 connections: (none) +1. Follow the [Building Meshtastic Firmware](https://meshtastic.org/docs/development/firmware/build/) documentation, stop after **Build** → **Step 2** +2. For **Build** → **Step 3**, select `xiao_ble` as your target +3. Adjust source code if you: + - Wired your board for Manual Tx/Rx Switching Mode: see [Wire the Board](#1-wire-the-board) + - Used an E22-900M33S module + (this step is important to avoid **damaging the power amplifier** in the M33S module and **transmitting power above legal limits**!): + 1. Open `variants/diy/platformio.ini` + 2. Search for `[env:xiao_ble]` + 3. In the line starting with `build_flags` within this section, change `-DEBYTE_E22_900M30S` to `-DEBYTE_E22_900M33S` +4. Follow **Build** → **Step 4** to build the firmware +5. Stop here, because the **PlatformIO: Upload** step does not work for factory-fresh XIAO nrf52840 (the automatic reset to bootloader only works if Meshtastic firmware is already running) +6. The built `firmware.uf2` binary can be found in the folder `.pio/build/xiao_ble/firmware.uf2` (relative to where you cloned the Git repository to), we will need it for [flashing the firmware](#3-flash-the-firmware-to-the-xiao-nrf52840) (manually) -  +## 3. Flash the Firmware to the XIAO nrf52840 -## 5. Flash the firmware to the Xiao BLE +1. Double press the XIAO nrf52840's `reset` button to put it in bootloader mode, and a USB volume named `XIAO SENSE` will appear +2. Copy the `firmware.uf2` file to the `XIAO SENSE` volume (refer to the last step of [Build Meshtastic](#2-build-meshtastic)) +3. The XIAO nrf52840's red LED will flash for several seconds as the firmware is copied +4. Once Meshtastic firmware succesfully boots, the: + 1. Green LED will turn on + 2. Red LED will flash several times to indicate flash memory writes during initial settings file creation + 3. Green LED will blink every second once the firmware is running normally +5. If you do not see the above LED patters, proceed to [Troubleshooting](#4-troubleshooting) -1. Double press the Xiao's `reset` button to put it in bootloader mode. -2. In a terminal window, navigate to the Meshtastic firmware repo's root directory, and from there to `.pio/build/xiao_ble`. -3. Convert the generated `.hex` file into a `.uf2` file: - - ```bash - ../../../bin/uf2conv.py firmware.hex -c -o firmware.uf2 -f 0xADA52840 - ``` - -4. Copy the new `.uf2` file to the Xiao's mass storage volume: - - ```bash - cp firmware.uf2 /Volumes/XIAO-BOOT - ``` - -5. The Xiao's red LED will flash for several seconds as the firmware is copied. -6. Once the firmware is copied, to verify it is running, run the following command: - - ```bash - meshtastic --noproto - ``` - -7. Then, press the Xiao's `reset` button again. You should see a lot of debug output logged in the terminal window. - -  - -## 6. Troubleshooting - -- If after flashing Meshtastic, the Xiao is bootlooped, look at the serial output (you can see this by running `meshtastic --noproto` with the device connected to your computer via USB). +## 4. Troubleshooting +- If after flashing Meshtastic, the XIAO is bootlooped, look at the serial output (you can see this by running `meshtastic --noproto` with the device connected to your computer via USB). - If you see that the SX1262 init result was -2, this likely indicates a wiring problem; double check your wiring and pin mapping in `variant.h`. - - - If you see an error mentioning tinyFS, this may mean you need to reformat the Xiao's storage: - - 1. Double press the `reset` button to put the Xiao in bootloader mode. - - 2. In a terminal window, navigate to the Meshtastic firmware repo's root directory, and from there to `variants/xiao_ble`. - - 3. Run the following command:  `cp xiao-ble-internal-format.uf2 /Volumes/XIAO-BOOT` - - 4. The Xiao's red LED will flash briefly as the filesystem format firmware is copied. - - 5. Run the following command:  `meshtastic --noproto` - - 6. In the output of the above command, you should see a message saying "Formatting...done". - - 7. To flash Meshtastic again, repeat the steps in section 5 above. - + - If you see an error mentioning tinyFS, this may mean you need to reformat the XIAO's storage: + 1. Open the [Meshtastic web flasher](https://flasher.meshtastic.org/) + 2. Select the **_Seeed XIAO NRF52840 Kit_** + 3. Click the **_trash can icon_** to the right of **_Flash_** + 4. Follow the instructions on the screen + **Do not flash the Seeed XIAO NRF52840 Kit firmware** if you have wired the LoRa module according to this variant, as the Seeed XIAO NRF52840 Kit uses different wiring for the SX1262 LoRa chip - If you don't see any specific error message, but the boot process is stuck or not proceeding as expected, this might also mean there is a conflict in `variant.h`. If you have made any changes to the pin mapping, ensure they do not result in a conflict. If all else fails, try reverting your changes and using the known-good configuration included here. - - The above might also mean something is wired incorrectly. Try reverting to one of the known-good example wirings in section 4. - - If the E22 gets hot to the touch: - - The power amplifier is likely running continually. Disconnect it and the Xiao from power immediately, and double check wiring and pin mapping. In my experimentation this occurred in cases where TXEN was inadvertenly high (usually due to a pin mapping conflict). + - The power amplifier is likely running continually. Disconnect it and the XIAO from power immediately, and double check wiring and pin mapping. In my experimentation this occurred in cases where TXEN was inadvertenly high (usually due to a pin mapping conflict). -  +## 5. Notes -## 7. Notes +- **Transmit Power** + - There is a power amplifier after the SX1262's Tx, so the actual Tx power is just over 7 dB greater than the SX1262's set Tx power (the E22-900M30S actually tops out just over 29dB at 5V according to the datasheet) + - Meshtastic firmware is aware of the gain of the E22-900M30S module, so the Meshtastic clients' Tx power setting reflects the actual output power, i.e. setting 30 dBm in the Meshtastic app programs the E22 module to correctly output 30 dBm, setting 24 dBm will output 24 dBm, etc. +- **Adequate 5V Power Supply to the E22 Module** + - Have a bypass capacitor from its 5V supply to ground; 100 µF works well + - Voltage must be between 5V–5.5V, lower supply voltage results in less output power; for example, with a fully charged LiPo at 4.2V, Tx power appears to max out around 26-27 dBm -- There are several anecdotal recommendations regarding the Tx power the E22's internal SX1262 should be set to in order to achieve the advertised output of 30 dBm, ranging from 4 (per this article in the RadioLib github repo) to 22 (per this conversation from the Meshtastic Discord). When paired with the Xiao BLE in the configurations described above, I observed that the output is at its maximum when Tx power is set to 22. +### Additional Reading -- To achieve its full output, the E22 should have a bypass capacitor from its 5V supply to ground. 100 µF works well. +- [S5NC/CDEBYTE_Modules](https://github.com/S5NC/CDEBYTE_Modules) has additional information about EBYTE E22 modules' internal workings, including photographs +- [RadioLib High power Radio Modules Guide](https://github.com/jgromes/RadioLib/wiki/High-power-Radio-Modules-Guide) -- The E22 will happily run on voltages lower than 5V, but the full output power will not be realized. For example, with a fully charged LiPo at 4.2V, Tx power appears to max out around 26-27 dBm. - -  - -## 8. Testing Methodology +## 6. Testing Methodology During what became a fairly long trial-and-error process, I did a lot of careful testing of Tx power and Rx sensitivity. My methodology in these tests was as follows: - All tests were conducted between two nodes: - - 1. The Xiao BLE + E22 coupled with an Abracon ARRKP4065-S915A ceramic patch antenna - - 2. A RAK 5005/4631 coupled with a Laird MA9-5N antenna via a 4" U.FL to Type N pigtail. - + 1. The XIAO nrf52840 + E22 coupled with an [Abracon ARRKP4065-S915A](https://www.digikey.com/en/products/detail/abracon-llc/ARRKP4065-S915A/8593263") ceramic patch antenna + 2. A RAK 5005/4631 coupled with a [Laird MA9-5N](https://www.streakwave.com/laird-technologies-ma9-5n-55dbi-900mhz-mobile-omni-select-mount) antenna via a 4" U.FL to Type N pigtail. - No other nodes were powered up onsite or nearby. - -
- - Each node and its antenna was kept in exactly the same position and orientation throughout testing. - - Other environmental factors (e.g. the location and resting position of my body in the room while testing) were controlled as carefully as possible. - - Each test comprised at least five (and often ten) runs, after which the results were averaged. - - All testing was done by sending single-character messages between nodes and observing the received RSSI reported in the message acknowledgement. Messages were sent one by one, waiting for each to be acknowledged or time out before sending the next. - -- The E22's Tx power was observed by sending messages from the RAK to the Xiao BLE + E22 and recording the received RSSI. - -- The opposite was done to observe the E22's Rx sensitivity: messages were sent from the Xiao BLE + E22 to the RAK, and the received RSSI was recorded. - -While this cannot match the level of accuracy achievable with actual test equipment in a lab setting, it was nonetheless sufficient to demonstrate the (sometimes very large) differences in Tx power and Rx sensitivity between various configurations. +- The E22's Tx power was observed by sending messages from the RAK to the XIAO nrf52840 + E22 and recording the received RSSI. +- The opposite was done to observe the E22's Rx sensitivity: messages were sent from the XIAO nrf52840 + E22 to the RAK, and the received RSSI was recorded. + While this cannot match the level of accuracy achievable with actual test equipment in a lab setting, it was nonetheless sufficient to demonstrate the (sometimes very large) differences in Tx power and Rx sensitivity between various configurations. From 622023de8b5f74db623db96a030874e72317f014 Mon Sep 17 00:00:00 2001 From: Neil Hanlon Date: Sun, 13 Jul 2025 07:19:58 -0400 Subject: [PATCH 60/72] fix(device-update.sh): safely filter args without breaking parsing (#7305) The previous method of removing `--change-mode` from the argument list used a string (`NEW_ARGS`) and `eval set -- $NEW_ARGS` to reconstruct the positional parameters. This was both unsafe and incorrect. Because `NEW_ARGS` was built using quoted literal `$arg` strings instead of the actual values, it resulted in all filtered arguments being set to the same last value of `$arg`. This caused `getopts` to receive incorrect input and silently fail to parse options like `-p` and `-f`, leading to broken behavior and unset variables (e.g., `ESPTOOL_CMD` never got a port). This patch rewrites the logic to use an array (`NEW_ARGS+=("$arg")`), and resets positional parameters via `set -- "${NEW_ARGS[@]}"`. This preserves argument integrity and avoids the unsafe use of `eval`. Example of the broken behavior before this fix: ./device-update.sh -p /dev/ttyACM0 -f firmware.bin Resulted in: set -- firmware.bin firmware.bin firmware.bin firmware.bin Now: set -- -p /dev/ttyACM0 -f firmware.bin as expected. Signed-off-by: Neil Hanlon --- bin/device-update.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bin/device-update.sh b/bin/device-update.sh index 2a39cdef7..ce0b5e434 100755 --- a/bin/device-update.sh +++ b/bin/device-update.sh @@ -31,17 +31,16 @@ EOF } # Check for --change-mode and remove it from arguments -NEW_ARGS="" +NEW_ARGS=() for arg in "$@"; do if [ "$arg" = "--change-mode" ]; then CHANGE_MODE=true else - NEW_ARGS="$NEW_ARGS \"\$arg\"" + NEW_ARGS+=("$arg") fi done -# Reset positional parameters to filtered list -eval set -- $NEW_ARGS +set -- "${NEW_ARGS[@]}" while getopts ":hp:P:f:" opt; do case "${opt}" in From 5e28ee6d1e0e46f3ffa32364d5f5f92927e28acb Mon Sep 17 00:00:00 2001 From: Styne13 <6253936+Styne13@users.noreply.github.com> Date: Sun, 13 Jul 2025 13:26:35 +0200 Subject: [PATCH 61/72] NodeDB.cpp: Fix iOS bluetooth crash by ensuring UINT32_MAX is not used (#7312) Signed-off-by: Marcel <6253936+Styne13@users.noreply.github.com> Co-authored-by: Ben Meadors --- src/mesh/NodeDB.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 270db6b2c..185ea0744 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -344,6 +344,22 @@ NodeDB::NodeDB() config.device.node_info_broadcast_secs = MAX_INTERVAL; if (config.position.position_broadcast_secs > MAX_INTERVAL) config.position.position_broadcast_secs = MAX_INTERVAL; + if (config.position.gps_update_interval > MAX_INTERVAL) + config.position.gps_update_interval = MAX_INTERVAL; + if (config.position.gps_attempt_time > MAX_INTERVAL) + config.position.gps_attempt_time = MAX_INTERVAL; + if (config.position.position_flags > MAX_INTERVAL) + config.position.position_flags = MAX_INTERVAL; + if (config.position.rx_gpio > MAX_INTERVAL) + config.position.rx_gpio = MAX_INTERVAL; + if (config.position.tx_gpio > MAX_INTERVAL) + config.position.tx_gpio = MAX_INTERVAL; + if (config.position.broadcast_smart_minimum_distance > MAX_INTERVAL) + config.position.broadcast_smart_minimum_distance = MAX_INTERVAL; + if (config.position.broadcast_smart_minimum_interval_secs > MAX_INTERVAL) + config.position.broadcast_smart_minimum_interval_secs = MAX_INTERVAL; + if (config.position.gps_en_gpio > MAX_INTERVAL) + config.position.gps_en_gpio = MAX_INTERVAL; if (moduleConfig.neighbor_info.update_interval > MAX_INTERVAL) moduleConfig.neighbor_info.update_interval = MAX_INTERVAL; if (moduleConfig.telemetry.device_update_interval > MAX_INTERVAL) From 2ecbf704d0caff3ba0b02cd516299165075ce163 Mon Sep 17 00:00:00 2001 From: TSAO Date: Sun, 13 Jul 2025 21:28:05 +0800 Subject: [PATCH 62/72] Improve OLED UI Responsiveness and Force Redraws for Canned message module (#7324) * No delay between UI frame rendering for OLED * force redraw the display --------- Co-authored-by: Ben Meadors Co-authored-by: Jason P --- src/graphics/Screen.cpp | 7 ++++++- src/modules/CannedMessageModule.cpp | 10 +++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 57ea64fa9..1f2e7e4d9 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -640,6 +640,11 @@ void Screen::forceDisplay(bool forceUiUpdate) // Tell EInk class to update the display static_cast(dispdev)->forceDisplay(); +#else + // No delay between UI frame rendering + if (forceUiUpdate) { + setFastFramerate(); + } #endif } @@ -1447,4 +1452,4 @@ bool shouldWakeOnReceivedMessage() return false; } return true; -} \ No newline at end of file +} diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index 06a4993a7..a1b89e0f8 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -454,7 +454,7 @@ int CannedMessageModule::handleDestinationSelectionInput(const InputEvent *event else if ((destIndex / columns) >= (scrollIndex + visibleRows)) scrollIndex = (destIndex / columns) - visibleRows + 1; - screen->forceDisplay(); + screen->forceDisplay(true); return 1; } @@ -469,7 +469,7 @@ int CannedMessageModule::handleDestinationSelectionInput(const InputEvent *event if ((destIndex / columns) >= (scrollIndex + visibleRows)) scrollIndex = (destIndex / columns) - visibleRows + 1; - screen->forceDisplay(); + screen->forceDisplay(true); return 1; } @@ -491,7 +491,7 @@ int CannedMessageModule::handleDestinationSelectionInput(const InputEvent *event runState = returnToCannedList ? CANNED_MESSAGE_RUN_STATE_ACTIVE : CANNED_MESSAGE_RUN_STATE_FREETEXT; returnToCannedList = false; - screen->forceDisplay(); + screen->forceDisplay(true); return 1; } @@ -504,7 +504,7 @@ int CannedMessageModule::handleDestinationSelectionInput(const InputEvent *event // UIFrameEvent e; // e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // notifyObservers(&e); - screen->forceDisplay(); + screen->forceDisplay(true); return 1; } @@ -2077,4 +2077,4 @@ String CannedMessageModule::drawWithCursor(String text, int cursor) return result; } -#endif \ No newline at end of file +#endif From 16d265023626a3820deb5fa6835065c89f7eb8db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Sun, 13 Jul 2025 19:16:14 +0200 Subject: [PATCH 63/72] add pioenv to version string in debug log (#7328) --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 773145951..395291766 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -286,7 +286,7 @@ void lateInitVariant() {} */ void printInfo() { - LOG_INFO("S:B:%d,%s", HW_VENDOR, optstr(APP_VERSION)); + LOG_INFO("S:B:%d,%s,%s", HW_VENDOR, optstr(APP_VERSION), optstr(APP_ENV)); } #ifndef PIO_UNIT_TESTING void setup() From 45e428eb25acbd297dd570a32c57eca5f07c65d7 Mon Sep 17 00:00:00 2001 From: Austin Date: Sun, 13 Jul 2025 16:22:42 -0400 Subject: [PATCH 64/72] PPA: Add Ubuntu Questing (25.10) to daily builds (#7329) --- .github/workflows/daily_packaging.yml | 6 +++++- .github/workflows/release_channels.yml | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/daily_packaging.yml b/.github/workflows/daily_packaging.yml index 63d24687b..eb61554f2 100644 --- a/.github/workflows/daily_packaging.yml +++ b/.github/workflows/daily_packaging.yml @@ -30,7 +30,11 @@ jobs: strategy: fail-fast: false matrix: - series: [plucky, noble, jammy] + series: + - jammy # 22.04 + - noble # 24.04 + - plucky # 25.04 + - questing # 25.10 uses: ./.github/workflows/package_ppa.yml with: ppa_repo: ppa:meshtastic/daily diff --git a/.github/workflows/release_channels.yml b/.github/workflows/release_channels.yml index ed2de1717..ef03be9dc 100644 --- a/.github/workflows/release_channels.yml +++ b/.github/workflows/release_channels.yml @@ -20,7 +20,11 @@ jobs: strategy: fail-fast: false matrix: - series: [plucky, noble, jammy] + series: + - jammy # 22.04 + - noble # 24.04 + - plucky # 25.04 + # - questing # 25.10 uses: ./.github/workflows/package_ppa.yml with: ppa_repo: |- From f3ff80963afd5cafa840849c70023817de330863 Mon Sep 17 00:00:00 2001 From: Austin Date: Sun, 13 Jul 2025 20:48:17 -0400 Subject: [PATCH 65/72] Actions: Move all Linux packaging into subdir (#7332) --- .github/workflows/daily_packaging.yml | 18 +++++++++--------- .../copr.yml} | 0 .../debian_src.yml} | 0 .../{ => linux_packaging}/docker_build.yml | 1 + .../{ => linux_packaging}/docker_manifest.yml | 12 ++++++------ .../obs.yml} | 2 +- .../ppa.yml} | 2 +- .github/workflows/main_matrix.yml | 14 +++++++------- .github/workflows/release_channels.yml | 8 ++++---- .../{nightly.yml => trunk_nightly.yml} | 2 +- bin/build-firmware.sh | 4 ++-- 11 files changed, 32 insertions(+), 31 deletions(-) rename .github/workflows/{hook_copr.yml => linux_packaging/copr.yml} (100%) rename .github/workflows/{build_debian_src.yml => linux_packaging/debian_src.yml} (100%) rename .github/workflows/{ => linux_packaging}/docker_build.yml (98%) rename .github/workflows/{ => linux_packaging}/docker_manifest.yml (93%) rename .github/workflows/{package_obs.yml => linux_packaging/obs.yml} (98%) rename .github/workflows/{package_ppa.yml => linux_packaging/ppa.yml} (96%) rename .github/workflows/{nightly.yml => trunk_nightly.yml} (97%) diff --git a/.github/workflows/daily_packaging.yml b/.github/workflows/daily_packaging.yml index eb61554f2..b9b52c5e8 100644 --- a/.github/workflows/daily_packaging.yml +++ b/.github/workflows/daily_packaging.yml @@ -9,11 +9,11 @@ on: paths: - debian/** - "*.rpkg" - - .github/workflows/nightly_packaging.yml - - .github/workflows/build_debian_src.yml - - .github/workflows/package_ppa.yml - - .github/workflows/package_obs.yml - - .github/workflows/hook_copr.yml + - .github/workflows/daily_packaging.yml + - .github/workflows/linux_packaging/debian_src.yml + - .github/workflows/linux_packaging/ppa.yml + - .github/workflows/linux_packaging/obs.yml + - .github/workflows/linux_packaging/copr.yml permissions: contents: write @@ -21,7 +21,7 @@ permissions: jobs: docker-multiarch: - uses: ./.github/workflows/docker_manifest.yml + uses: ./.github/workflows/linux_packaging/docker_manifest.yml with: release_channel: daily secrets: inherit @@ -35,21 +35,21 @@ jobs: - noble # 24.04 - plucky # 25.04 - questing # 25.10 - uses: ./.github/workflows/package_ppa.yml + uses: ./.github/workflows/linux_packaging/ppa.yml with: ppa_repo: ppa:meshtastic/daily series: ${{ matrix.series }} secrets: inherit package-obs: - uses: ./.github/workflows/package_obs.yml + uses: ./.github/workflows/linux_packaging/obs.yml with: obs_project: network:Meshtastic:daily series: unstable secrets: inherit hook-copr: - uses: ./.github/workflows/hook_copr.yml + uses: ./.github/workflows/linux_packaging/copr.yml with: copr_project: daily secrets: inherit diff --git a/.github/workflows/hook_copr.yml b/.github/workflows/linux_packaging/copr.yml similarity index 100% rename from .github/workflows/hook_copr.yml rename to .github/workflows/linux_packaging/copr.yml diff --git a/.github/workflows/build_debian_src.yml b/.github/workflows/linux_packaging/debian_src.yml similarity index 100% rename from .github/workflows/build_debian_src.yml rename to .github/workflows/linux_packaging/debian_src.yml diff --git a/.github/workflows/docker_build.yml b/.github/workflows/linux_packaging/docker_build.yml similarity index 98% rename from .github/workflows/docker_build.yml rename to .github/workflows/linux_packaging/docker_build.yml index cde7fd274..a6173eac5 100644 --- a/.github/workflows/docker_build.yml +++ b/.github/workflows/linux_packaging/docker_build.yml @@ -42,6 +42,7 @@ permissions: jobs: docker-build: + name: docker-${{ inputs.distro }} outputs: digest: ${{ steps.docker_variant.outputs.digest }} runs-on: ${{ inputs.runs-on }} diff --git a/.github/workflows/docker_manifest.yml b/.github/workflows/linux_packaging/docker_manifest.yml similarity index 93% rename from .github/workflows/docker_manifest.yml rename to .github/workflows/linux_packaging/docker_manifest.yml index d1d1a5634..6585212a4 100644 --- a/.github/workflows/docker_manifest.yml +++ b/.github/workflows/linux_packaging/docker_manifest.yml @@ -17,7 +17,7 @@ permissions: jobs: docker-debian-amd64: - uses: ./.github/workflows/docker_build.yml + uses: ./.github/workflows/linux_packaging/docker_build.yml with: distro: debian platform: linux/amd64 @@ -26,7 +26,7 @@ jobs: secrets: inherit docker-debian-arm64: - uses: ./.github/workflows/docker_build.yml + uses: ./.github/workflows/linux_packaging/docker_build.yml with: distro: debian platform: linux/arm64 @@ -35,7 +35,7 @@ jobs: secrets: inherit docker-debian-armv7: - uses: ./.github/workflows/docker_build.yml + uses: ./.github/workflows/linux_packaging/docker_build.yml with: distro: debian platform: linux/arm/v7 @@ -44,7 +44,7 @@ jobs: secrets: inherit docker-alpine-amd64: - uses: ./.github/workflows/docker_build.yml + uses: ./.github/workflows/linux_packaging/docker_build.yml with: distro: alpine platform: linux/amd64 @@ -53,7 +53,7 @@ jobs: secrets: inherit docker-alpine-arm64: - uses: ./.github/workflows/docker_build.yml + uses: ./.github/workflows/linux_packaging/docker_build.yml with: distro: alpine platform: linux/arm64 @@ -62,7 +62,7 @@ jobs: secrets: inherit docker-alpine-armv7: - uses: ./.github/workflows/docker_build.yml + uses: ./.github/workflows/linux_packaging/docker_build.yml with: distro: alpine platform: linux/arm/v7 diff --git a/.github/workflows/package_obs.yml b/.github/workflows/linux_packaging/obs.yml similarity index 98% rename from .github/workflows/package_obs.yml rename to .github/workflows/linux_packaging/obs.yml index 275ffce0e..5156dee2e 100644 --- a/.github/workflows/package_obs.yml +++ b/.github/workflows/linux_packaging/obs.yml @@ -23,7 +23,7 @@ permissions: jobs: build-debian-src: - uses: ./.github/workflows/build_debian_src.yml + uses: ./.github/workflows/linux_packaging/debian_src.yml secrets: inherit with: series: ${{ inputs.series }} diff --git a/.github/workflows/package_ppa.yml b/.github/workflows/linux_packaging/ppa.yml similarity index 96% rename from .github/workflows/package_ppa.yml rename to .github/workflows/linux_packaging/ppa.yml index a54b0bd36..ef089eefb 100644 --- a/.github/workflows/package_ppa.yml +++ b/.github/workflows/linux_packaging/ppa.yml @@ -21,7 +21,7 @@ permissions: jobs: build-debian-src: - uses: ./.github/workflows/build_debian_src.yml + uses: ./.github/workflows/linux_packaging/debian_src.yml secrets: inherit with: series: ${{ inputs.series }} diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index a676efa1e..3aafced62 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -136,7 +136,7 @@ jobs: build-debian-src: if: github.repository == 'meshtastic/firmware' - uses: ./.github/workflows/build_debian_src.yml + uses: ./.github/workflows/linux_packaging/debian_src.yml with: series: UNRELEASED build_location: local @@ -154,7 +154,7 @@ jobs: uses: ./.github/workflows/test_native.yml docker-deb-amd64: - uses: ./.github/workflows/docker_build.yml + uses: ./.github/workflows/linux_packaging/docker_build.yml with: distro: debian platform: linux/amd64 @@ -162,7 +162,7 @@ jobs: push: false docker-deb-amd64-tft: - uses: ./.github/workflows/docker_build.yml + uses: ./.github/workflows/linux_packaging/docker_build.yml with: distro: debian platform: linux/amd64 @@ -171,7 +171,7 @@ jobs: pio_env: native-tft docker-alp-amd64: - uses: ./.github/workflows/docker_build.yml + uses: ./.github/workflows/linux_packaging/docker_build.yml with: distro: alpine platform: linux/amd64 @@ -179,7 +179,7 @@ jobs: push: false docker-alp-amd64-tft: - uses: ./.github/workflows/docker_build.yml + uses: ./.github/workflows/linux_packaging/docker_build.yml with: distro: alpine platform: linux/amd64 @@ -188,7 +188,7 @@ jobs: pio_env: native-tft docker-deb-arm64: - uses: ./.github/workflows/docker_build.yml + uses: ./.github/workflows/linux_packaging/docker_build.yml with: distro: debian platform: linux/arm64 @@ -196,7 +196,7 @@ jobs: push: false docker-deb-armv7: - uses: ./.github/workflows/docker_build.yml + uses: ./.github/workflows/linux_packaging/docker_build.yml with: distro: debian platform: linux/arm/v7 diff --git a/.github/workflows/release_channels.yml b/.github/workflows/release_channels.yml index ef03be9dc..50bd1fba1 100644 --- a/.github/workflows/release_channels.yml +++ b/.github/workflows/release_channels.yml @@ -10,7 +10,7 @@ permissions: jobs: build-docker: - uses: ./.github/workflows/docker_manifest.yml + uses: ./.github/workflows/linux_packaging/docker_manifest.yml with: release_channel: |- ${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} @@ -25,7 +25,7 @@ jobs: - noble # 24.04 - plucky # 25.04 # - questing # 25.10 - uses: ./.github/workflows/package_ppa.yml + uses: ./.github/workflows/linux_packaging/ppa.yml with: ppa_repo: |- ppa:meshtastic/${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} @@ -33,7 +33,7 @@ jobs: secrets: inherit package-obs: - uses: ./.github/workflows/package_obs.yml + uses: ./.github/workflows/linux_packaging/obs.yml with: obs_project: |- network:Meshtastic:${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} @@ -42,7 +42,7 @@ jobs: secrets: inherit hook-copr: - uses: ./.github/workflows/hook_copr.yml + uses: ./.github/workflows/linux_packaging/copr.yml with: copr_project: |- ${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} diff --git a/.github/workflows/nightly.yml b/.github/workflows/trunk_nightly.yml similarity index 97% rename from .github/workflows/nightly.yml rename to .github/workflows/trunk_nightly.yml index 309772b12..4f1e9b426 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/trunk_nightly.yml @@ -1,4 +1,4 @@ -name: Nightly +name: Trunk Nightly on: schedule: - cron: 0 8 * * 1-5 diff --git a/bin/build-firmware.sh b/bin/build-firmware.sh index c53f1b660..933c9bfe6 100644 --- a/bin/build-firmware.sh +++ b/bin/build-firmware.sh @@ -10,8 +10,8 @@ elif (echo $2 | grep -q "nrf52"); then bin/build-nrf52.sh $1 elif (echo $2 | grep -q "stm32"); then bin/build-stm32.sh $1 -elif (echo $2 | grep -q "rpi2040"); then - bin/build-rpi2040.sh $1 +elif (echo $2 | grep -q "rp2xx0"); then + bin/build-rp2xx0.sh $1 else echo "Unknown target $2" exit 1 From 29cca4d62113f4627bcd8b9f33cc553a22f16fba Mon Sep 17 00:00:00 2001 From: Austin Date: Sun, 13 Jul 2025 20:54:52 -0400 Subject: [PATCH 66/72] Revert "Actions: Move all Linux packaging into subdir (#7332)" (#7334) This reverts commit f3ff80963afd5cafa840849c70023817de330863. --- .../debian_src.yml => build_debian_src.yml} | 0 .github/workflows/daily_packaging.yml | 18 +++++++++--------- .../{linux_packaging => }/docker_build.yml | 1 - .../{linux_packaging => }/docker_manifest.yml | 12 ++++++------ .../copr.yml => hook_copr.yml} | 0 .github/workflows/main_matrix.yml | 14 +++++++------- .../{trunk_nightly.yml => nightly.yml} | 2 +- .../obs.yml => package_obs.yml} | 2 +- .../ppa.yml => package_ppa.yml} | 2 +- .github/workflows/release_channels.yml | 8 ++++---- bin/build-firmware.sh | 4 ++-- 11 files changed, 31 insertions(+), 32 deletions(-) rename .github/workflows/{linux_packaging/debian_src.yml => build_debian_src.yml} (100%) rename .github/workflows/{linux_packaging => }/docker_build.yml (98%) rename .github/workflows/{linux_packaging => }/docker_manifest.yml (93%) rename .github/workflows/{linux_packaging/copr.yml => hook_copr.yml} (100%) rename .github/workflows/{trunk_nightly.yml => nightly.yml} (97%) rename .github/workflows/{linux_packaging/obs.yml => package_obs.yml} (98%) rename .github/workflows/{linux_packaging/ppa.yml => package_ppa.yml} (96%) diff --git a/.github/workflows/linux_packaging/debian_src.yml b/.github/workflows/build_debian_src.yml similarity index 100% rename from .github/workflows/linux_packaging/debian_src.yml rename to .github/workflows/build_debian_src.yml diff --git a/.github/workflows/daily_packaging.yml b/.github/workflows/daily_packaging.yml index b9b52c5e8..eb61554f2 100644 --- a/.github/workflows/daily_packaging.yml +++ b/.github/workflows/daily_packaging.yml @@ -9,11 +9,11 @@ on: paths: - debian/** - "*.rpkg" - - .github/workflows/daily_packaging.yml - - .github/workflows/linux_packaging/debian_src.yml - - .github/workflows/linux_packaging/ppa.yml - - .github/workflows/linux_packaging/obs.yml - - .github/workflows/linux_packaging/copr.yml + - .github/workflows/nightly_packaging.yml + - .github/workflows/build_debian_src.yml + - .github/workflows/package_ppa.yml + - .github/workflows/package_obs.yml + - .github/workflows/hook_copr.yml permissions: contents: write @@ -21,7 +21,7 @@ permissions: jobs: docker-multiarch: - uses: ./.github/workflows/linux_packaging/docker_manifest.yml + uses: ./.github/workflows/docker_manifest.yml with: release_channel: daily secrets: inherit @@ -35,21 +35,21 @@ jobs: - noble # 24.04 - plucky # 25.04 - questing # 25.10 - uses: ./.github/workflows/linux_packaging/ppa.yml + uses: ./.github/workflows/package_ppa.yml with: ppa_repo: ppa:meshtastic/daily series: ${{ matrix.series }} secrets: inherit package-obs: - uses: ./.github/workflows/linux_packaging/obs.yml + uses: ./.github/workflows/package_obs.yml with: obs_project: network:Meshtastic:daily series: unstable secrets: inherit hook-copr: - uses: ./.github/workflows/linux_packaging/copr.yml + uses: ./.github/workflows/hook_copr.yml with: copr_project: daily secrets: inherit diff --git a/.github/workflows/linux_packaging/docker_build.yml b/.github/workflows/docker_build.yml similarity index 98% rename from .github/workflows/linux_packaging/docker_build.yml rename to .github/workflows/docker_build.yml index a6173eac5..cde7fd274 100644 --- a/.github/workflows/linux_packaging/docker_build.yml +++ b/.github/workflows/docker_build.yml @@ -42,7 +42,6 @@ permissions: jobs: docker-build: - name: docker-${{ inputs.distro }} outputs: digest: ${{ steps.docker_variant.outputs.digest }} runs-on: ${{ inputs.runs-on }} diff --git a/.github/workflows/linux_packaging/docker_manifest.yml b/.github/workflows/docker_manifest.yml similarity index 93% rename from .github/workflows/linux_packaging/docker_manifest.yml rename to .github/workflows/docker_manifest.yml index 6585212a4..d1d1a5634 100644 --- a/.github/workflows/linux_packaging/docker_manifest.yml +++ b/.github/workflows/docker_manifest.yml @@ -17,7 +17,7 @@ permissions: jobs: docker-debian-amd64: - uses: ./.github/workflows/linux_packaging/docker_build.yml + uses: ./.github/workflows/docker_build.yml with: distro: debian platform: linux/amd64 @@ -26,7 +26,7 @@ jobs: secrets: inherit docker-debian-arm64: - uses: ./.github/workflows/linux_packaging/docker_build.yml + uses: ./.github/workflows/docker_build.yml with: distro: debian platform: linux/arm64 @@ -35,7 +35,7 @@ jobs: secrets: inherit docker-debian-armv7: - uses: ./.github/workflows/linux_packaging/docker_build.yml + uses: ./.github/workflows/docker_build.yml with: distro: debian platform: linux/arm/v7 @@ -44,7 +44,7 @@ jobs: secrets: inherit docker-alpine-amd64: - uses: ./.github/workflows/linux_packaging/docker_build.yml + uses: ./.github/workflows/docker_build.yml with: distro: alpine platform: linux/amd64 @@ -53,7 +53,7 @@ jobs: secrets: inherit docker-alpine-arm64: - uses: ./.github/workflows/linux_packaging/docker_build.yml + uses: ./.github/workflows/docker_build.yml with: distro: alpine platform: linux/arm64 @@ -62,7 +62,7 @@ jobs: secrets: inherit docker-alpine-armv7: - uses: ./.github/workflows/linux_packaging/docker_build.yml + uses: ./.github/workflows/docker_build.yml with: distro: alpine platform: linux/arm/v7 diff --git a/.github/workflows/linux_packaging/copr.yml b/.github/workflows/hook_copr.yml similarity index 100% rename from .github/workflows/linux_packaging/copr.yml rename to .github/workflows/hook_copr.yml diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index 3aafced62..a676efa1e 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -136,7 +136,7 @@ jobs: build-debian-src: if: github.repository == 'meshtastic/firmware' - uses: ./.github/workflows/linux_packaging/debian_src.yml + uses: ./.github/workflows/build_debian_src.yml with: series: UNRELEASED build_location: local @@ -154,7 +154,7 @@ jobs: uses: ./.github/workflows/test_native.yml docker-deb-amd64: - uses: ./.github/workflows/linux_packaging/docker_build.yml + uses: ./.github/workflows/docker_build.yml with: distro: debian platform: linux/amd64 @@ -162,7 +162,7 @@ jobs: push: false docker-deb-amd64-tft: - uses: ./.github/workflows/linux_packaging/docker_build.yml + uses: ./.github/workflows/docker_build.yml with: distro: debian platform: linux/amd64 @@ -171,7 +171,7 @@ jobs: pio_env: native-tft docker-alp-amd64: - uses: ./.github/workflows/linux_packaging/docker_build.yml + uses: ./.github/workflows/docker_build.yml with: distro: alpine platform: linux/amd64 @@ -179,7 +179,7 @@ jobs: push: false docker-alp-amd64-tft: - uses: ./.github/workflows/linux_packaging/docker_build.yml + uses: ./.github/workflows/docker_build.yml with: distro: alpine platform: linux/amd64 @@ -188,7 +188,7 @@ jobs: pio_env: native-tft docker-deb-arm64: - uses: ./.github/workflows/linux_packaging/docker_build.yml + uses: ./.github/workflows/docker_build.yml with: distro: debian platform: linux/arm64 @@ -196,7 +196,7 @@ jobs: push: false docker-deb-armv7: - uses: ./.github/workflows/linux_packaging/docker_build.yml + uses: ./.github/workflows/docker_build.yml with: distro: debian platform: linux/arm/v7 diff --git a/.github/workflows/trunk_nightly.yml b/.github/workflows/nightly.yml similarity index 97% rename from .github/workflows/trunk_nightly.yml rename to .github/workflows/nightly.yml index 4f1e9b426..309772b12 100644 --- a/.github/workflows/trunk_nightly.yml +++ b/.github/workflows/nightly.yml @@ -1,4 +1,4 @@ -name: Trunk Nightly +name: Nightly on: schedule: - cron: 0 8 * * 1-5 diff --git a/.github/workflows/linux_packaging/obs.yml b/.github/workflows/package_obs.yml similarity index 98% rename from .github/workflows/linux_packaging/obs.yml rename to .github/workflows/package_obs.yml index 5156dee2e..275ffce0e 100644 --- a/.github/workflows/linux_packaging/obs.yml +++ b/.github/workflows/package_obs.yml @@ -23,7 +23,7 @@ permissions: jobs: build-debian-src: - uses: ./.github/workflows/linux_packaging/debian_src.yml + uses: ./.github/workflows/build_debian_src.yml secrets: inherit with: series: ${{ inputs.series }} diff --git a/.github/workflows/linux_packaging/ppa.yml b/.github/workflows/package_ppa.yml similarity index 96% rename from .github/workflows/linux_packaging/ppa.yml rename to .github/workflows/package_ppa.yml index ef089eefb..a54b0bd36 100644 --- a/.github/workflows/linux_packaging/ppa.yml +++ b/.github/workflows/package_ppa.yml @@ -21,7 +21,7 @@ permissions: jobs: build-debian-src: - uses: ./.github/workflows/linux_packaging/debian_src.yml + uses: ./.github/workflows/build_debian_src.yml secrets: inherit with: series: ${{ inputs.series }} diff --git a/.github/workflows/release_channels.yml b/.github/workflows/release_channels.yml index 50bd1fba1..ef03be9dc 100644 --- a/.github/workflows/release_channels.yml +++ b/.github/workflows/release_channels.yml @@ -10,7 +10,7 @@ permissions: jobs: build-docker: - uses: ./.github/workflows/linux_packaging/docker_manifest.yml + uses: ./.github/workflows/docker_manifest.yml with: release_channel: |- ${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} @@ -25,7 +25,7 @@ jobs: - noble # 24.04 - plucky # 25.04 # - questing # 25.10 - uses: ./.github/workflows/linux_packaging/ppa.yml + uses: ./.github/workflows/package_ppa.yml with: ppa_repo: |- ppa:meshtastic/${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} @@ -33,7 +33,7 @@ jobs: secrets: inherit package-obs: - uses: ./.github/workflows/linux_packaging/obs.yml + uses: ./.github/workflows/package_obs.yml with: obs_project: |- network:Meshtastic:${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} @@ -42,7 +42,7 @@ jobs: secrets: inherit hook-copr: - uses: ./.github/workflows/linux_packaging/copr.yml + uses: ./.github/workflows/hook_copr.yml with: copr_project: |- ${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }} diff --git a/bin/build-firmware.sh b/bin/build-firmware.sh index 933c9bfe6..c53f1b660 100644 --- a/bin/build-firmware.sh +++ b/bin/build-firmware.sh @@ -10,8 +10,8 @@ elif (echo $2 | grep -q "nrf52"); then bin/build-nrf52.sh $1 elif (echo $2 | grep -q "stm32"); then bin/build-stm32.sh $1 -elif (echo $2 | grep -q "rp2xx0"); then - bin/build-rp2xx0.sh $1 +elif (echo $2 | grep -q "rpi2040"); then + bin/build-rpi2040.sh $1 else echo "Unknown target $2" exit 1 From ac3e5684d6887704eb0e317223e0c7bc8394ac76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Mon, 14 Jul 2025 12:11:26 +0200 Subject: [PATCH 67/72] get git url part from local repo (#7331) --- bin/platformio-custom.py | 13 +++++++++++++ src/main.cpp | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/bin/platformio-custom.py b/bin/platformio-custom.py index be2a9ab71..fc1b4bc2e 100644 --- a/bin/platformio-custom.py +++ b/bin/platformio-custom.py @@ -3,6 +3,7 @@ # trunk-ignore-all(flake8/F821): For SConstruct imports import sys from os.path import join +import subprocess import json import re @@ -92,6 +93,17 @@ prefsLoc = projenv["PROJECT_DIR"] + "/version.properties" verObj = readProps(prefsLoc) print("Using meshtastic platformio-custom.py, firmware version " + verObj["long"] + " on " + env.get("PIOENV")) +# get repository owner if git is installed +try: + r_owner = ( + subprocess.check_output(["git", "config", "--get", "remote.origin.url"]) + .decode("utf-8") + .strip().split("/") + ) + repo_owner = r_owner[-2] + "/" + r_owner[-1].replace(".git", "") +except subprocess.CalledProcessError: + repo_owner = "unknown" + jsonLoc = env["PROJECT_DIR"] + "/userPrefs.jsonc" with open(jsonLoc) as f: jsonStr = re.sub("//.*","", f.read(), flags=re.MULTILINE) @@ -117,6 +129,7 @@ flags = [ "-DAPP_VERSION=" + verObj["long"], "-DAPP_VERSION_SHORT=" + verObj["short"], "-DAPP_ENV=" + env.get("PIOENV"), + "-DAPP_REPO=" + repo_owner, ] + pref_flags print ("Using flags:") diff --git a/src/main.cpp b/src/main.cpp index 395291766..640f0b1fe 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -286,7 +286,7 @@ void lateInitVariant() {} */ void printInfo() { - LOG_INFO("S:B:%d,%s,%s", HW_VENDOR, optstr(APP_VERSION), optstr(APP_ENV)); + LOG_INFO("S:B:%d,%s,%s,%s", HW_VENDOR, optstr(APP_VERSION), optstr(APP_ENV), optstr(APP_REPO)); } #ifndef PIO_UNIT_TESTING void setup() From 1be4fc5ae95dc22e63688f78e9050981d50d88d8 Mon Sep 17 00:00:00 2001 From: Chloe Bethel Date: Mon, 14 Jul 2025 11:12:26 +0100 Subject: [PATCH 68/72] GPS for STM32WL (#7297) * Enable GPS for Wio-E5 variant on Serial2 * Add ability to override GPS serial port using GPS_SERIAL_PORT, and make RAK2560 use it. * Don't try to send ATAK packets if ATAK is disabled, +4k flash --- arch/stm32/stm32.ini | 4 ++-- src/gps/GPS.cpp | 6 +++--- src/modules/PositionModule.cpp | 2 ++ variants/rak2560/variant.h | 1 + variants/wio-e5/platformio.ini | 7 ++++++- variants/wio-e5/variant.h | 2 ++ 6 files changed, 16 insertions(+), 6 deletions(-) diff --git a/arch/stm32/stm32.ini b/arch/stm32/stm32.ini index be1ed662f..03e0bf392 100644 --- a/arch/stm32/stm32.ini +++ b/arch/stm32/stm32.ini @@ -23,11 +23,11 @@ build_flags = -DMESHTASTIC_EXCLUDE_SCREEN=1 -DMESHTASTIC_EXCLUDE_MQTT=1 -DMESHTASTIC_EXCLUDE_BLUETOOTH=1 - -DMESHTASTIC_EXCLUDE_GPS=1 -DMESHTASTIC_EXCLUDE_WIFI=1 -DMESHTASTIC_EXCLUDE_TZ=1 ; Exclude TZ to save some flash space. + -DSERIAL_RX_BUFFER_SIZE=256 ; For GPS - the default of 64 is too small. -DPIO_FRAMEWORK_ARDUINO_NANOLIB_FLOAT_PRINTF ; This is REQUIRED for at least traceroute debug prints - without it the length ends up uninitialized. - ;-DDEBUG_MUTE + -DDEBUG_MUTE ; You can #undef DEBUG_MUTE in certain source files if you need the logs. -fmerge-all-constants -ffunction-sections -fdata-sections diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 345c738d6..3a6b19f64 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -39,9 +39,9 @@ template std::size_t array_count(const T (&)[N]) return N; } -#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) -#if defined(RAK2560) -HardwareSerial *GPS::_serial_gps = &Serial2; +#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL) +#if defined(GPS_SERIAL_PORT) +HardwareSerial *GPS::_serial_gps = &GPS_SERIAL_PORT; #else HardwareSerial *GPS::_serial_gps = &Serial1; #endif diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index 93c65ecc1..8b6a9f19c 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -266,9 +266,11 @@ meshtastic_MeshPacket *PositionModule::allocPositionPacket() LOG_INFO("Position packet: time=%i lat=%i lon=%i", p.time, p.latitude_i, p.longitude_i); +#ifndef MESHTASTIC_EXCLUDE_ATAK // TAK Tracker devices should send their position in a TAK packet over the ATAK port if (config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER) return allocAtakPli(); +#endif return allocDataProtobuf(p); } diff --git a/variants/rak2560/variant.h b/variants/rak2560/variant.h index a03fc3933..f922e8a61 100644 --- a/variants/rak2560/variant.h +++ b/variants/rak2560/variant.h @@ -222,6 +222,7 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG // #define PIN_GPS_EN PIN_3V3_EN #define PIN_GPS_PPS (17) // Pulse per second input from the GPS +#define GPS_SERIAL_PORT Serial2 // On RAK2560 the GPS is be on a different UART // #define GPS_RX_PIN PIN_SERIAL2_RX // #define GPS_TX_PIN PIN_SERIAL2_TX diff --git a/variants/wio-e5/platformio.ini b/variants/wio-e5/platformio.ini index 1ef7abd78..90251c7aa 100644 --- a/variants/wio-e5/platformio.ini +++ b/variants/wio-e5/platformio.ini @@ -15,7 +15,12 @@ build_flags = -DRADIOLIB_EXCLUDE_SX128X=1 -DRADIOLIB_EXCLUDE_SX127X=1 -DRADIOLIB_EXCLUDE_LR11X0=1 - -DHAS_SENSOR + -DHAS_SENSOR=1 + -DENABLE_HWSERIAL2 + -DPIN_SERIAL2_TX=PA2 + -DPIN_SERIAL2_RX=PA3 + -DHAS_GPS=1 + -DGPS_SERIAL_PORT=Serial2 upload_port = stlink diff --git a/variants/wio-e5/variant.h b/variants/wio-e5/variant.h index 5421eaeb9..6098b4ce6 100644 --- a/variants/wio-e5/variant.h +++ b/variants/wio-e5/variant.h @@ -17,6 +17,8 @@ Do not expect a working Meshtastic device with this target. #define LED_PIN PB5 #define LED_STATE_ON 1 +#define WIO_E5 + #if (defined(LED_BUILTIN) && LED_BUILTIN == PNUM_NOT_DEFINED) #undef LED_BUILTIN #define LED_BUILTIN (LED_PIN) From 3599ca6845c3b9788226e875ef7c11a4a6be7da1 Mon Sep 17 00:00:00 2001 From: Chloe Bethel Date: Mon, 14 Jul 2025 11:12:38 +0100 Subject: [PATCH 69/72] Add heap info via standard mallinfo() function for STM32 (#7327) Co-authored-by: Ben Meadors --- src/memGet.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/memGet.cpp b/src/memGet.cpp index ef1102f1e..e8cd177dd 100644 --- a/src/memGet.cpp +++ b/src/memGet.cpp @@ -10,6 +10,10 @@ #include "memGet.h" #include "configuration.h" +#ifdef ARCH_STM32WL +#include +#endif + MemGet memGet; /** @@ -24,6 +28,9 @@ uint32_t MemGet::getFreeHeap() return dbgHeapFree(); #elif defined(ARCH_RP2040) return rp2040.getFreeHeap(); +#elif defined(ARCH_STM32WL) + struct mallinfo m = mallinfo(); + return m.fordblks; // Total free space (bytes) #else // this platform does not have heap management function implemented return UINT32_MAX; @@ -42,6 +49,9 @@ uint32_t MemGet::getHeapSize() return dbgHeapTotal(); #elif defined(ARCH_RP2040) return rp2040.getTotalHeap(); +#elif defined(ARCH_STM32WL) + struct mallinfo m = mallinfo(); + return m.arena; // Non-mmapped space allocated (bytes) #else // this platform does not have heap management function implemented return UINT32_MAX; From f197f0e5ec791c6944e73ec52ded417884ad3f08 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 05:12:52 -0500 Subject: [PATCH 70/72] Upgrade trunk (#7336) 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 f0271c856..7d27efe58 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -9,7 +9,7 @@ plugins: lint: enabled: - checkov@3.2.450 - - renovate@41.29.1 + - renovate@41.30.5 - prettier@3.6.2 - trufflehog@3.89.2 - yamllint@1.37.1 From daa1d582cbdf1a8ae20175d1353feb2e6524526f Mon Sep 17 00:00:00 2001 From: Quency-D <55523105+Quency-D@users.noreply.github.com> Date: Mon, 14 Jul 2025 18:43:25 +0800 Subject: [PATCH 71/72] The screen display of the heltec wireless tracker is abnormal. (#7337) The screen of the heltec wireless tracker uses the same power source as the GPS. If the GPS turns off the power during the screen shutdown period and then turns on the power, the screen will not function properly. So initialize the screen every time it starts. --- src/graphics/Screen.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 1f2e7e4d9..e46f7dffe 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -386,9 +386,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) #ifdef T_WATCH_S3 PMU->enablePowerOutput(XPOWERS_ALDO2); #endif -#ifdef HELTEC_TRACKER_V1_X - uint8_t tft_vext_enabled = digitalRead(VEXT_ENABLE); -#endif + #if !ARCH_PORTDUINO dispdev->displayOn(); #endif @@ -400,10 +398,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) dispdev->displayOn(); #ifdef HELTEC_TRACKER_V1_X - // If the TFT VEXT power is not enabled, initialize the UI. - if (!tft_vext_enabled) { ui->init(); - } #endif #ifdef USE_ST7789 pinMode(VTFT_CTRL, OUTPUT); From 86af5f5252f408fce0fc1509e2430c98395c7d49 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 14 Jul 2025 05:44:29 -0500 Subject: [PATCH 72/72] Trunk --- src/graphics/Screen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index e46f7dffe..e1b4101c4 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -398,7 +398,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) dispdev->displayOn(); #ifdef HELTEC_TRACKER_V1_X - ui->init(); + ui->init(); #endif #ifdef USE_ST7789 pinMode(VTFT_CTRL, OUTPUT);