From f2fb473ecf1a0e0c19b12134c5250f76efdf252d Mon Sep 17 00:00:00 2001 From: Austin Date: Mon, 7 Jul 2025 20:34:25 -0400 Subject: [PATCH 01/68] 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 02/68] 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 03/68] 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 04/68] 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 05/68] 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 06/68] 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 07/68] 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 08/68] 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 09/68] 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 10/68] 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 11/68] 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 12/68] 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 13/68] 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 14/68] 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 15/68] 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 16/68] 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 17/68] 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 18/68] 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 19/68] 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 20/68] 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 21/68] 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 22/68] 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 23/68] 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 24/68] 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 25/68] 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 26/68] 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 27/68] 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 28/68] 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 29/68] 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 30/68] 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 31/68] 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 32/68] 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 33/68] 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 34/68] 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 35/68] 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 36/68] 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 37/68] 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 38/68] 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 39/68] 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 40/68] 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 41/68] 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 42/68] 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 43/68] 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 44/68] 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 45/68] 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 46/68] 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 47/68] 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 48/68] 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 49/68] 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 50/68] 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 51/68] 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 52/68] 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 53/68] 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 54/68] 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 55/68] 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 56/68] 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 57/68] 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 58/68] 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 59/68] 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); From e864fcf9a81d2b7c2a356687231b39edb4c36569 Mon Sep 17 00:00:00 2001 From: "Daniel.Cao" <144674500+DanielCao0@users.noreply.github.com> Date: Mon, 14 Jul 2025 22:29:42 +0800 Subject: [PATCH 60/68] feat: add support for RAK Wismesh Tag hardware platform (#6853) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add new platform rak_wismeshtag * Remove RTC and Ethernet definitions from variant.h * Remove unused EINK and Ethernet definitions from variant.h and platformio.ini * Add WISMESH_TAG hardware model definition in architecture.h and update build flags in platformio.ini * Remove unused build flags and dependencies --------- Co-authored-by: Ben Meadors Co-authored-by: daniel Co-authored-by: Thomas Göttgens --- src/platform/nrf52/architecture.h | 2 + variants/rak_wismeshtag/platformio.ini | 15 ++ variants/rak_wismeshtag/variant.cpp | 45 +++++ variants/rak_wismeshtag/variant.h | 245 +++++++++++++++++++++++++ 4 files changed, 307 insertions(+) create mode 100644 variants/rak_wismeshtag/platformio.ini create mode 100644 variants/rak_wismeshtag/variant.cpp create mode 100644 variants/rak_wismeshtag/variant.h diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h index 684d20e84..ba94f4a2a 100644 --- a/src/platform/nrf52/architecture.h +++ b/src/platform/nrf52/architecture.h @@ -49,6 +49,8 @@ #define HW_VENDOR meshtastic_HardwareModel_RAK2560 #elif defined(WISMESH_TAP) #define HW_VENDOR meshtastic_HardwareModel_WISMESH_TAP +#elif defined(WISMESH_TAG) +#define HW_VENDOR meshtastic_HardwareModel_WISMESH_TAG #elif defined(GAT562_MESH_TRIAL_TRACKER) #define HW_VENDOR meshtastic_HardwareModel_GAT562_MESH_TRIAL_TRACKER #elif defined(RAK4630) diff --git a/variants/rak_wismeshtag/platformio.ini b/variants/rak_wismeshtag/platformio.ini new file mode 100644 index 000000000..a066e5282 --- /dev/null +++ b/variants/rak_wismeshtag/platformio.ini @@ -0,0 +1,15 @@ +; The very slick RAK wireless RAK 4631 / 4630 board - Unified firmware for 5005/19003, with or without OLED RAK 1921 +[env:rak_wismeshtag] +extends = nrf52840_base +board = wiscore_rak4631 +board_check = true +build_flags = ${nrf52840_base.build_flags} -Ivariants/rak_wismeshtag -D WISMESH_TAG -D RAK_4631 + -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" + -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. + -DRADIOLIB_EXCLUDE_SX128X=1 + -DRADIOLIB_EXCLUDE_SX127X=1 + -DRADIOLIB_EXCLUDE_LR11X0=1 + -DMESHTASTIC_EXCLUDE_WIFI=1 +build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak_wismeshtag> +lib_deps = + ${nrf52840_base.lib_deps} \ No newline at end of file diff --git a/variants/rak_wismeshtag/variant.cpp b/variants/rak_wismeshtag/variant.cpp new file mode 100644 index 000000000..e84b60b3b --- /dev/null +++ b/variants/rak_wismeshtag/variant.cpp @@ -0,0 +1,45 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "variant.h" +#include "nrf.h" +#include "wiring_constants.h" +#include "wiring_digital.h" + +const uint32_t g_ADigitalPinMap[] = { + // P0 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + + // P1 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; + +void initVariant() +{ + // LED1 & LED2 + pinMode(PIN_LED1, OUTPUT); + ledOff(PIN_LED1); + + pinMode(PIN_LED2, OUTPUT); + ledOff(PIN_LED2); + + // 3V3 Power Rail + pinMode(PIN_3V3_EN, OUTPUT); + digitalWrite(PIN_3V3_EN, HIGH); +} diff --git a/variants/rak_wismeshtag/variant.h b/variants/rak_wismeshtag/variant.h new file mode 100644 index 000000000..dd82b76a1 --- /dev/null +++ b/variants/rak_wismeshtag/variant.h @@ -0,0 +1,245 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _VARIANT_RAK4630_ +#define _VARIANT_RAK4630_ + +#define RAK4630 + +/** Master clock frequency */ +#define VARIANT_MCK (64000000ul) + +#define USE_LFXO // Board uses 32khz crystal for LF +// define USE_LFRC // Board uses RC for LF + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#include "WVariant.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Number of pins defined in PinDescription array +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) +#define NUM_ANALOG_INPUTS (6) +#define NUM_ANALOG_OUTPUTS (0) + +// LEDs +#define PIN_LED1 (35) +#define PIN_LED2 (36) + +#define LED_BUILTIN PIN_LED1 +#define LED_CONN PIN_LED2 + +#define LED_GREEN PIN_LED1 +#define LED_BLUE PIN_LED2 + +#define LED_STATE_ON 1 // State when LED is litted + +/* + * Buttons + */ + +#define PIN_BUTTON1 9 // Pin for button on E-ink button module or IO expansion +#define BUTTON_NEED_PULLUP +#define PIN_BUTTON2 12 +#define PIN_BUTTON3 24 +#define PIN_BUTTON4 25 + +/* + * Analog pins + */ +#define PIN_A0 (5) +#define PIN_A1 (31) +#define PIN_A2 (28) +#define PIN_A3 (29) +#define PIN_A4 (30) +#define PIN_A5 (31) +#define PIN_A6 (0xff) +#define PIN_A7 (0xff) + +static const uint8_t A0 = PIN_A0; +static const uint8_t A1 = PIN_A1; +static const uint8_t A2 = PIN_A2; +static const uint8_t A3 = PIN_A3; +static const uint8_t A4 = PIN_A4; +static const uint8_t A5 = PIN_A5; +static const uint8_t A6 = PIN_A6; +static const uint8_t A7 = PIN_A7; +#define ADC_RESOLUTION 14 + +// Other pins +#define PIN_AREF (2) +#define PIN_NFC1 (9) +#define PIN_NFC2 (10) + +static const uint8_t AREF = PIN_AREF; + +/* + * Serial interfaces + */ +#define PIN_SERIAL1_RX (15) +#define PIN_SERIAL1_TX (16) + +// Connected to Jlink CDC +#define PIN_SERIAL2_RX (8) +#define PIN_SERIAL2_TX (6) + +/* + * SPI Interfaces + */ +#define SPI_INTERFACES_COUNT 2 + +#define PIN_SPI_MISO (45) +#define PIN_SPI_MOSI (44) +#define PIN_SPI_SCK (43) + +#define PIN_SPI1_MISO (29) // (0 + 29) +#define PIN_SPI1_MOSI (30) // (0 + 30) +#define PIN_SPI1_SCK (3) // (0 + 3) + +static const uint8_t SS = 42; +static const uint8_t MOSI = PIN_SPI_MOSI; +static const uint8_t MISO = PIN_SPI_MISO; +static const uint8_t SCK = PIN_SPI_SCK; + +/* + * eink display pins + */ + +#define PIN_EINK_CS (0 + 26) +#define PIN_EINK_BUSY (0 + 4) +#define PIN_EINK_DC (0 + 17) +#define PIN_EINK_RES (-1) +#define PIN_EINK_SCLK (0 + 3) +#define PIN_EINK_MOSI (0 + 30) // also called SDI + +/* + * Wire Interfaces + */ +#define WIRE_INTERFACES_COUNT 1 + +// RAK WISMESHTAG +#define PIN_WIRE_SDA (25) +#define PIN_WIRE_SCL (24) + +// QSPI Pins +#define PIN_QSPI_SCK 3 +#define PIN_QSPI_CS 26 +#define PIN_QSPI_IO0 30 +#define PIN_QSPI_IO1 29 +#define PIN_QSPI_IO2 28 +#define PIN_QSPI_IO3 2 + + +/* @note RAK5005-O GPIO mapping to RAK4631 GPIO ports + RAK5005-O <-> nRF52840 + IO1 <-> P0.17 (Arduino GPIO number 17) + IO2 <-> P1.02 (Arduino GPIO number 34) + IO3 <-> P0.21 (Arduino GPIO number 21) + IO4 <-> P0.04 (Arduino GPIO number 4) + IO5 <-> P0.09 (Arduino GPIO number 9) + IO6 <-> P0.10 (Arduino GPIO number 10) + IO7 <-> P0.28 (Arduino GPIO number 28) + SW1 <-> P0.01 (Arduino GPIO number 1) + A0 <-> P0.04/AIN2 (Arduino Analog A2 + A1 <-> P0.31/AIN7 (Arduino Analog A7 + SPI_CS <-> P0.26 (Arduino GPIO number 26) + */ + +// RAK4630 LoRa module + +/* Setup of the SX1262 LoRa module ( https://docs.rakwireless.com/Product-Categories/WisBlock/RAK4631/Datasheet/ ) + +P1.10 NSS SPI NSS (Arduino GPIO number 42) +P1.11 SCK SPI CLK (Arduino GPIO number 43) +P1.12 MOSI SPI MOSI (Arduino GPIO number 44) +P1.13 MISO SPI MISO (Arduino GPIO number 45) +P1.14 BUSY BUSY signal (Arduino GPIO number 46) +P1.15 DIO1 DIO1 event interrupt (Arduino GPIO number 47) +P1.06 NRESET NRESET manual reset of the SX1262 (Arduino GPIO number 38) + +Important for successful SX1262 initialization: + +* Setup DIO2 to control the antenna switch +* Setup DIO3 to control the TCXO power supply +* Setup the SX1262 to use it's DCDC regulator and not the LDO +* RAK4630 schematics show GPIO P1.07 connected to the antenna switch, but it should not be initialized, as DIO2 will do the +control of the antenna switch + +SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG + +*/ + +#define DETECTION_SENSOR_EN 4 + +#define USE_SX1262 +#define SX126X_CS (42) +#define SX126X_DIO1 (47) +#define SX126X_BUSY (46) +#define SX126X_RESET (38) +// #define SX126X_TXEN (39) +// #define SX126X_RXEN (37) +#define SX126X_POWER_EN (37) +// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3 +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +// Testing USB detection +#define NRF_APM + +// enables 3.3V periphery like GPS or IO Module +// Do not toggle this for GPS power savings +#define PIN_3V3_EN (34) + +// RAK WISMESHTAG +#define PIN_GPS_EN PIN_3V3_EN +#define PIN_GPS_PPS (17) // Pulse per second input from the GPS + +#define GPS_RX_PIN PIN_SERIAL1_RX +#define GPS_TX_PIN PIN_SERIAL1_TX + +// RAK WISMESHTAG +#define PIN_BUZZER 21 + +// Battery +// The battery sense is hooked to pin A0 (5) +#define BATTERY_PIN PIN_A0 +// and has 12 bit resolution +#define BATTERY_SENSE_RESOLUTION_BITS 12 +#define BATTERY_SENSE_RESOLUTION 4096.0 +#undef AREF_VOLTAGE +#define AREF_VOLTAGE 3.0 +#define VBAT_AR_INTERNAL AR_INTERNAL_3_0 +#define ADC_MULTIPLIER 1.73 + +#define RAK_4631 1 + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ + +#endif \ No newline at end of file From 8f10de5684d189bda6169332b70fd690b5328e9a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 09:46:43 -0500 Subject: [PATCH 61/68] [create-pull-request] automated change (#7338) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- protobufs | 2 +- src/mesh/generated/meshtastic/admin.pb.h | 2 +- src/mesh/generated/meshtastic/config.pb.h | 10 +++++-- src/mesh/generated/meshtastic/deviceonly.pb.h | 4 +-- src/mesh/generated/meshtastic/mesh.pb.h | 28 ++++++++----------- src/mesh/generated/meshtastic/powermon.pb.h | 10 +++---- 6 files changed, 27 insertions(+), 29 deletions(-) diff --git a/protobufs b/protobufs index 584f0a3a3..f6448be77 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 584f0a3a359103acf0bfce506c1b1fc32c639841 +Subproject commit f6448be7770a3521bf52407ff8f5fa5b9b06da7b diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h index 071640b0d..bc0b780b9 100644 --- a/src/mesh/generated/meshtastic/admin.pb.h +++ b/src/mesh/generated/meshtastic/admin.pb.h @@ -7,9 +7,9 @@ #include "meshtastic/channel.pb.h" #include "meshtastic/config.pb.h" #include "meshtastic/connection_status.pb.h" +#include "meshtastic/device_ui.pb.h" #include "meshtastic/mesh.pb.h" #include "meshtastic/module_config.pb.h" -#include "meshtastic/device_ui.pb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h index f28daadbd..20bce5b78 100644 --- a/src/mesh/generated/meshtastic/config.pb.h +++ b/src/mesh/generated/meshtastic/config.pb.h @@ -102,7 +102,11 @@ typedef enum _meshtastic_Config_DeviceConfig_BuzzerMode { meshtastic_Config_DeviceConfig_BuzzerMode_NOTIFICATIONS_ONLY = 2, /* Non-notification system buzzer tones only. Buzzer is enabled only for non-notification tones such as button presses, startup, shutdown, but not for alerts. */ - meshtastic_Config_DeviceConfig_BuzzerMode_SYSTEM_ONLY = 3 + meshtastic_Config_DeviceConfig_BuzzerMode_SYSTEM_ONLY = 3, + /* Direct Message notifications only. + Buzzer is enabled only for direct messages and alerts, but not for button presses. + External notification config determines the specifics of the notification behavior. */ + meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY = 4 } meshtastic_Config_DeviceConfig_BuzzerMode; /* Bit field of boolean configuration options, indicating which optional @@ -645,8 +649,8 @@ extern "C" { #define _meshtastic_Config_DeviceConfig_RebroadcastMode_ARRAYSIZE ((meshtastic_Config_DeviceConfig_RebroadcastMode)(meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY+1)) #define _meshtastic_Config_DeviceConfig_BuzzerMode_MIN meshtastic_Config_DeviceConfig_BuzzerMode_ALL_ENABLED -#define _meshtastic_Config_DeviceConfig_BuzzerMode_MAX meshtastic_Config_DeviceConfig_BuzzerMode_SYSTEM_ONLY -#define _meshtastic_Config_DeviceConfig_BuzzerMode_ARRAYSIZE ((meshtastic_Config_DeviceConfig_BuzzerMode)(meshtastic_Config_DeviceConfig_BuzzerMode_SYSTEM_ONLY+1)) +#define _meshtastic_Config_DeviceConfig_BuzzerMode_MAX meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY +#define _meshtastic_Config_DeviceConfig_BuzzerMode_ARRAYSIZE ((meshtastic_Config_DeviceConfig_BuzzerMode)(meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY+1)) #define _meshtastic_Config_PositionConfig_PositionFlags_MIN meshtastic_Config_PositionConfig_PositionFlags_UNSET #define _meshtastic_Config_PositionConfig_PositionFlags_MAX meshtastic_Config_PositionConfig_PositionFlags_SPEED diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h index f78689cb2..b02b2083d 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.h +++ b/src/mesh/generated/meshtastic/deviceonly.pb.h @@ -6,10 +6,10 @@ #include #include #include "meshtastic/channel.pb.h" -#include "meshtastic/mesh.pb.h" -#include "meshtastic/telemetry.pb.h" #include "meshtastic/config.pb.h" #include "meshtastic/localonly.pb.h" +#include "meshtastic/mesh.pb.h" +#include "meshtastic/telemetry.pb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 9e0415198..584c3d647 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -6,11 +6,11 @@ #include #include "meshtastic/channel.pb.h" #include "meshtastic/config.pb.h" +#include "meshtastic/device_ui.pb.h" #include "meshtastic/module_config.pb.h" #include "meshtastic/portnums.pb.h" #include "meshtastic/telemetry.pb.h" #include "meshtastic/xmodem.pb.h" -#include "meshtastic/device_ui.pb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. @@ -247,32 +247,26 @@ typedef enum _meshtastic_HardwareModel { meshtastic_HardwareModel_NOMADSTAR_METEOR_PRO = 96, /* Elecrow CrowPanel Advance models, ESP32-S3 and TFT with SX1262 radio plugin */ meshtastic_HardwareModel_CROWPANEL = 97, - /* * - Lilygo LINK32 board with sensors */ + /* Lilygo LINK32 board with sensors */ meshtastic_HardwareModel_LINK_32 = 98, - /* * - Seeed Tracker L1 */ + /* Seeed Tracker L1 */ meshtastic_HardwareModel_SEEED_WIO_TRACKER_L1 = 99, - /* * - Seeed Tracker L1 EINK driver */ + /* Seeed Tracker L1 EINK driver */ meshtastic_HardwareModel_SEEED_WIO_TRACKER_L1_EINK = 100, /* Reserved ID for future and past use */ meshtastic_HardwareModel_QWANTZ_TINY_ARMS = 101, - /* * - Lilygo T-Deck Pro */ + /* Lilygo T-Deck Pro */ meshtastic_HardwareModel_T_DECK_PRO = 102, - /* * - Lilygo TLora Pager */ + /* Lilygo TLora Pager */ meshtastic_HardwareModel_T_LORA_PAGER = 103, - /* * - GAT562 Mesh Trial Tracker */ + /* GAT562 Mesh Trial Tracker */ meshtastic_HardwareModel_GAT562_MESH_TRIAL_TRACKER = 104, - /* * - RAKwireless WisMesh Tag */ + /* RAKwireless WisMesh Tag */ meshtastic_HardwareModel_WISMESH_TAG = 105, - /* * - RAKwireless WisBlock Core RAK3312 https://docs.rakwireless.com/product-categories/wisduo/rak3112-module/overview/ */ + /* RAKwireless WisBlock Core RAK3312 https://docs.rakwireless.com/product-categories/wisduo/rak3112-module/overview/ */ meshtastic_HardwareModel_RAK3312 = 106, + /* Elecrow ThinkNode M5 https://www.elecrow.com/wiki/ThinkNode_M5_Meshtastic_LoRa_Signal_Transceiver_ESP32-S3.html */ + meshtastic_HardwareModel_THINKNODE_M5 = 107, /* ------------------------------------------------------------------------------------------------------------------------------------------ Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. ------------------------------------------------------------------------------------------------------------------------------------------ */ diff --git a/src/mesh/generated/meshtastic/powermon.pb.h b/src/mesh/generated/meshtastic/powermon.pb.h index 9d4d94193..3072b8ac5 100644 --- a/src/mesh/generated/meshtastic/powermon.pb.h +++ b/src/mesh/generated/meshtastic/powermon.pb.h @@ -11,7 +11,7 @@ /* Enum definitions */ /* Any significant power changing event in meshtastic should be tagged with a powermon state transition. -If you are making new meshtastic features feel free to add new entries at the end of this definition. */ + If you are making new meshtastic features feel free to add new entries at the end of this definition. */ typedef enum _meshtastic_PowerMon_State { meshtastic_PowerMon_State_None = 0, meshtastic_PowerMon_State_CPU_DeepSleep = 1, @@ -34,13 +34,13 @@ something like "S:PM:C,0x00001234,REASON" where the hex number is the bitmask of meshtastic_PowerMon_State_Screen_Drawing = 512, meshtastic_PowerMon_State_Wifi_On = 1024, /* GPS is actively trying to find our location -See GPSPowerState for more details */ + See GPSPowerState for more details */ meshtastic_PowerMon_State_GPS_Active = 2048 } meshtastic_PowerMon_State; /* What operation would we like the UUT to perform. -note: senders should probably set want_response in their request packets, so that they can know when the state -machine has started processing their request */ + note: senders should probably set want_response in their request packets, so that they can know when the state + machine has started processing their request */ typedef enum _meshtastic_PowerStressMessage_Opcode { /* Unset/unused */ meshtastic_PowerStressMessage_Opcode_UNSET = 0, @@ -67,7 +67,7 @@ typedef enum _meshtastic_PowerStressMessage_Opcode { /* Struct definitions */ /* Note: There are no 'PowerMon' messages normally in use (PowerMons are sent only as structured logs - slogs). -But we wrap our State enum in this message to effectively nest a namespace (without our linter yelling at us) */ + But we wrap our State enum in this message to effectively nest a namespace (without our linter yelling at us) */ typedef struct _meshtastic_PowerMon { char dummy_field; } meshtastic_PowerMon; From 5776385e8cd20c31cc8e67a9f54f3a4c05a64678 Mon Sep 17 00:00:00 2001 From: Austin Date: Mon, 14 Jul 2025 13:52:21 -0400 Subject: [PATCH 62/68] STM32 PlatformIO cleanup (#7339) --- arch/stm32/stm32.ini | 6 ++++++ variants/CDEBYTE_E77-MBL/platformio.ini | 10 ---------- variants/rak3172/platformio.ini | 10 ---------- variants/wio-e5/platformio.ini | 5 ----- 4 files changed, 6 insertions(+), 25 deletions(-) diff --git a/arch/stm32/stm32.ini b/arch/stm32/stm32.ini index 03e0bf392..153ca9f3e 100644 --- a/arch/stm32/stm32.ini +++ b/arch/stm32/stm32.ini @@ -26,11 +26,17 @@ build_flags = -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. + -DHAS_SCREEN=0 ; Always disable screen for STM32, it is not supported. -DPIO_FRAMEWORK_ARDUINO_NANOLIB_FLOAT_PRINTF ; This is REQUIRED for at least traceroute debug prints - without it the length ends up uninitialized. -DDEBUG_MUTE ; You can #undef DEBUG_MUTE in certain source files if you need the logs. -fmerge-all-constants -ffunction-sections -fdata-sections + -DRADIOLIB_EXCLUDE_SX128X=1 + -DRADIOLIB_EXCLUDE_SX127X=1 + -DRADIOLIB_EXCLUDE_LR11X0=1 + -DHAL_DAC_MODULE_ONLY + -DHAL_RNG_MODULE_ENABLED build_src_filter = ${arduino_base.build_src_filter} - - - - - - - - - - - - - - diff --git a/variants/CDEBYTE_E77-MBL/platformio.ini b/variants/CDEBYTE_E77-MBL/platformio.ini index 8a8002086..5c373875c 100644 --- a/variants/CDEBYTE_E77-MBL/platformio.ini +++ b/variants/CDEBYTE_E77-MBL/platformio.ini @@ -9,19 +9,9 @@ build_flags = -DSERIAL_UART_INSTANCE=1 -DPIN_SERIAL_RX=PA3 -DPIN_SERIAL_TX=PA2 - -DHAL_DAC_MODULE_ONLY - -DHAL_RNG_MODULE_ENABLED - -DRADIOLIB_EXCLUDE_SX128X=1 - -DRADIOLIB_EXCLUDE_SX127X=1 - -DRADIOLIB_EXCLUDE_LR11X0=1 -DMESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR=1 -DMESHTASTIC_EXCLUDE_I2C=1 - -DMESHTASTIC_EXCLUDE_WIFI=1 - -DMESHTASTIC_EXCLUDE_BLUETOOTH=1 -DMESHTASTIC_EXCLUDE_GPS=1 - -DMESHTASTIC_EXCLUDE_SCREEN=1 - -DMESHTASTIC_EXCLUDE_MQTT=1 - -DMESHTASTIC_EXCLUDE_POWERMON=1 ;-DPIO_FRAMEWORK_ARDUINO_NANOLIB_FLOAT_PRINTF ;-DCFG_DEBUG diff --git a/variants/rak3172/platformio.ini b/variants/rak3172/platformio.ini index 99610b17c..df2500f31 100644 --- a/variants/rak3172/platformio.ini +++ b/variants/rak3172/platformio.ini @@ -7,18 +7,8 @@ build_flags = -Ivariants/rak3172 -DPIN_WIRE_SDA=PA11 -DPIN_WIRE_SCL=PA12 - -DHAL_DAC_MODULE_ONLY - -DHAL_RNG_MODULE_ENABLED - -DRADIOLIB_EXCLUDE_SX128X=1 - -DRADIOLIB_EXCLUDE_SX127X=1 - -DRADIOLIB_EXCLUDE_LR11X0=1 -DMESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR=1 -DMESHTASTIC_EXCLUDE_I2C=1 - -DMESHTASTIC_EXCLUDE_WIFI=1 - -DMESHTASTIC_EXCLUDE_BLUETOOTH=1 -DMESHTASTIC_EXCLUDE_GPS=1 - -DMESHTASTIC_EXCLUDE_SCREEN=1 - -DMESHTASTIC_EXCLUDE_MQTT=1 - -DMESHTASTIC_EXCLUDE_POWERMON=1 ;-DCFG_DEBUG upload_port = stlink diff --git a/variants/wio-e5/platformio.ini b/variants/wio-e5/platformio.ini index 90251c7aa..5c9f433d4 100644 --- a/variants/wio-e5/platformio.ini +++ b/variants/wio-e5/platformio.ini @@ -10,11 +10,6 @@ build_flags = -DPIN_SERIAL_TX=PB6 -DPIN_WIRE_SDA=PA15 -DPIN_WIRE_SCL=PB15 - -DHAL_DAC_MODULE_ONLY - -DHAL_RNG_MODULE_ENABLED - -DRADIOLIB_EXCLUDE_SX128X=1 - -DRADIOLIB_EXCLUDE_SX127X=1 - -DRADIOLIB_EXCLUDE_LR11X0=1 -DHAS_SENSOR=1 -DENABLE_HWSERIAL2 -DPIN_SERIAL2_TX=PA2 From 31d56c16d57bca9817f80872e2f3010382446bfd Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 14 Jul 2025 20:13:34 -0500 Subject: [PATCH 63/68] Map report should work over devices which do not have network hardware (with client proxy) (#7341) * Map report should work over devices which do not have network hardware (with client proxy) * Fix else --- src/mqtt/MQTT.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index 137c92056..091612827 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -559,10 +559,8 @@ void MQTT::sendSubscriptions() int32_t MQTT::runOnce() { -#if HAS_NETWORKING if (!moduleConfig.mqtt.enabled || !(moduleConfig.mqtt.map_reporting_enabled || channels.anyMqttEnabled())) return disable(); - bool wantConnection = wantsLink(); perhapsReportToMap(); @@ -572,7 +570,7 @@ int32_t MQTT::runOnce() publishQueuedMessages(); return 200; } - +#if HAS_NETWORKING else if (!pubSub.loop()) { if (!wantConnection) return 5000; // If we don't want connection now, check again in 5 secs @@ -596,8 +594,10 @@ int32_t MQTT::runOnce() powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); // Suppress entering light sleep (because that would turn off bluetooth) return 20; } -#endif +#else + // No networking available, return default interval return 30000; +#endif } bool MQTT::isValidConfig(const meshtastic_ModuleConfig_MQTTConfig &config, MQTTClient *client) From 625a529f6c53ba684e81838e753fbfdbb6a918e1 Mon Sep 17 00:00:00 2001 From: Jason P Date: Mon, 14 Jul 2025 20:59:22 -0500 Subject: [PATCH 64/68] Message frame New Message Options and Clock / TDeck / Brightness Refinements (#7344) * Clock updates and some TDeck corrections * TDeck Brightness Works in TFT Builds * Remove HAS_TFT from enabling Brightness, disable Brightness for TDeck * Add default textMessage frame actions and adjust SharedUIDisplay --------- Co-authored-by: Ben Meadors --- src/graphics/Screen.cpp | 9 ++++--- src/graphics/SharedUIDisplay.cpp | 2 +- src/graphics/draw/ClockRenderer.cpp | 7 ++++- src/graphics/draw/MenuHandler.cpp | 42 ++++++++++++++++++++++++++--- src/graphics/draw/MenuHandler.h | 1 + 5 files changed, 52 insertions(+), 9 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index e1b4101c4..dbd0445f7 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -1374,9 +1374,12 @@ int Screen::handleInputEvent(const InputEvent *event) menuHandler::clockMenu(); } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.lora) { menuHandler::LoraRegionPicker(); - } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage && - devicestate.rx_text_message.from) { - menuHandler::messageResponseMenu(); + } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage) { + if (devicestate.rx_text_message.from) { + menuHandler::messageResponseMenu(); + } else { + menuHandler::textMessageBaseMenu(); + } } else if (framesetInfo.positions.firstFavorite != 255 && this->ui->getUiState()->currentFrame >= framesetInfo.positions.firstFavorite && this->ui->getUiState()->currentFrame <= framesetInfo.positions.lastFavorite) { diff --git a/src/graphics/SharedUIDisplay.cpp b/src/graphics/SharedUIDisplay.cpp index 7cd876ac5..b458e54e4 100644 --- a/src/graphics/SharedUIDisplay.cpp +++ b/src/graphics/SharedUIDisplay.cpp @@ -206,7 +206,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti timeX = screenW - xOffset - timeStrWidth + 3; // === Show Mail or Mute Icon to the Left of Time === - int iconRightEdge = timeX - 1; + int iconRightEdge = timeX - 2; bool showMail = false; diff --git a/src/graphics/draw/ClockRenderer.cpp b/src/graphics/draw/ClockRenderer.cpp index 8d7e91000..e3cb5fcb9 100644 --- a/src/graphics/draw/ClockRenderer.cpp +++ b/src/graphics/draw/ClockRenderer.cpp @@ -186,7 +186,7 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1 { display->clear(); display->setTextAlignment(TEXT_ALIGN_LEFT); - int line = 1; + // === Set Title, Blank for Clock const char *titleStr = ""; // === Header === @@ -230,6 +230,8 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1 #ifdef T_WATCH_S3 float scale = 1.5; +#elif defined(CHATTER_2) + float scale = 1.1; #else float scale = 0.75; if (isHighResolution) { @@ -285,6 +287,9 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1 int yOffset = (isHighResolution) ? 3 : 1; #ifdef SENSECAP_INDICATOR yOffset -= 3; +#endif +#ifdef T_DECK + yOffset -= 5; #endif if (config.display.use_12h_clock) { display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - yOffset - 2, diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index f6b250ebc..ded492da6 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -344,6 +344,35 @@ void menuHandler::homeBaseMenu() screen->showOverlayBanner(bannerOptions); } +void menuHandler::textMessageBaseMenu() +{ + enum optionsNumbers { Back, Preset, Freetext, enumEnd }; + + static const char *optionsArray[enumEnd] = {"Back"}; + static int optionsEnumArray[enumEnd] = {Back}; + int options = 1; + optionsArray[options] = "New Preset Msg"; + optionsEnumArray[options++] = Preset; + if (kb_found) { + optionsArray[options] = "New Freetext Msg"; + optionsEnumArray[options++] = Freetext; + } + + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Message Action"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsEnumPtr = optionsEnumArray; + bannerOptions.optionsCount = options; + bannerOptions.bannerCallback = [](int selected) -> void { + if (selected == Preset) { + cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST); + } else if (selected == Freetext) { + cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST); + } + }; + screen->showOverlayBanner(bannerOptions); +} + void menuHandler::systemBaseMenu() { // Check if brightness is supported @@ -729,7 +758,7 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display) screen->runNow(); } -#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT +#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || HAS_TFT if (selected != 0) { display->setColor(BLACK); display->fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); @@ -924,23 +953,28 @@ 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 +#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) hasSupportBrightness = true; #endif +#if defined(T_DECK) + // TDeck Doesn't seem to support brightness at all, at least not reliably + hasSupportBrightness = false; +#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) { + if (hasSupportBrightness) { 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 +#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || HAS_TFT optionsArray[options] = "Screen Color"; optionsEnumArray[options++] = ScreenColor; #endif diff --git a/src/graphics/draw/MenuHandler.h b/src/graphics/draw/MenuHandler.h index 2273dbbed..1f989be79 100644 --- a/src/graphics/draw/MenuHandler.h +++ b/src/graphics/draw/MenuHandler.h @@ -48,6 +48,7 @@ class menuHandler static void ClockFacePicker(); static void messageResponseMenu(); static void homeBaseMenu(); + static void textMessageBaseMenu(); static void systemBaseMenu(); static void favoriteBaseMenu(); static void positionBaseMenu(); From 39716ed1baacbcdabd744dfbb6534a3dbbbbc495 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Mon, 14 Jul 2025 21:14:07 -0500 Subject: [PATCH 65/68] Fix L1 EInk HWModel (#7346) --- src/platform/nrf52/architecture.h | 2 ++ variants/seeed_wio_tracker_L1_eink/platformio.ini | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h index ba94f4a2a..1bbdd77e0 100644 --- a/src/platform/nrf52/architecture.h +++ b/src/platform/nrf52/architecture.h @@ -91,6 +91,8 @@ #define HW_VENDOR meshtastic_HardwareModel_HELTEC_MESH_POCKET #elif defined(NOMADSTAR_METEOR_PRO) #define HW_VENDOR meshtastic_HardwareModel_NOMADSTAR_METEOR_PRO +#elif defined(SEEED_WIO_TRACKER_L1_EINK) +#define HW_VENDOR meshtastic_HardwareModel_SEEED_WIO_TRACKER_L1_EINK #elif defined(SEEED_WIO_TRACKER_L1) #define HW_VENDOR meshtastic_HardwareModel_SEEED_WIO_TRACKER_L1 #else diff --git a/variants/seeed_wio_tracker_L1_eink/platformio.ini b/variants/seeed_wio_tracker_L1_eink/platformio.ini index b84757b9d..b310cce83 100644 --- a/variants/seeed_wio_tracker_L1_eink/platformio.ini +++ b/variants/seeed_wio_tracker_L1_eink/platformio.ini @@ -4,7 +4,8 @@ extends = nrf52840_base, inkhud ;board_level = extra build_flags = ${nrf52840_base.build_flags} ${inkhud.build_flags} -I $PROJECT_DIR/variants/seeed_wio_tracker_L1_eink - -D SEEED_WIO_TRACKER_L1 + -D SEEED_WIO_TRACKER_L1_EINK + -D SEEED_WIO_TRACKER_L1 -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld build_src_filter = ${nrf52_base.build_src_filter} +<../variants/seeed_wio_tracker_L1_eink> ${inkhud.build_src_filter} From c3b2b474c6f9247b01b815bf181fbe104d121a64 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Wed, 16 Jul 2025 16:05:34 -0500 Subject: [PATCH 66/68] Drop NodeInfo packets if the is_licensed bit doesn't match owner (#7361) --- src/modules/NodeInfoModule.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/NodeInfoModule.cpp b/src/modules/NodeInfoModule.cpp index cf9940e25..b6fee7703 100644 --- a/src/modules/NodeInfoModule.cpp +++ b/src/modules/NodeInfoModule.cpp @@ -14,6 +14,11 @@ bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes { auto p = *pptr; + if (p.is_licensed != owner.is_licensed) { + LOG_WARN("Invalid nodeInfo detected, is_licensed mismatch!"); + return true; + } + // Coerce user.id to be derived from the node number snprintf(p.id, sizeof(p.id), "!%08x", getFrom(&mp)); From 55fc4fcd9024df2f711bc797a467cea274e9ab4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Thu, 17 Jul 2025 00:40:29 +0200 Subject: [PATCH 67/68] clean up double i2c init/scan code (#7359) --- src/main.cpp | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 640f0b1fe..c37001307 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -515,25 +515,11 @@ void setup() LOG_INFO("Scan for i2c devices"); #endif -#if defined(I2C_SDA1) && defined(ARCH_RP2040) - Wire1.setSDA(I2C_SDA1); - Wire1.setSCL(I2C_SCL1); - Wire1.begin(); - i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1); -#elif defined(I2C_SDA1) && !defined(ARCH_RP2040) - Wire1.begin(I2C_SDA1, I2C_SCL1); - i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1); -#elif defined(NRF52840_XXAA) && (WIRE_INTERFACES_COUNT == 2) +#if defined(I2C_SDA1) || (defined(NRF52840_XXAA) && (WIRE_INTERFACES_COUNT == 2)) i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1); #endif -#if defined(I2C_SDA) && defined(ARCH_RP2040) - Wire.setSDA(I2C_SDA); - Wire.setSCL(I2C_SCL); - Wire.begin(); - i2cScanner->scanPort(ScanI2C::I2CPort::WIRE); -#elif defined(I2C_SDA) && !defined(ARCH_RP2040) - Wire.begin(I2C_SDA, I2C_SCL); +#if defined(I2C_SDA) i2cScanner->scanPort(ScanI2C::I2CPort::WIRE); #elif defined(ARCH_PORTDUINO) if (settingsStrings[i2cdev] != "") { From 71b6508ad3a248360fe3d409eebdd44d1d3ad7b4 Mon Sep 17 00:00:00 2001 From: Jason P Date: Wed, 16 Jul 2025 19:44:23 -0500 Subject: [PATCH 68/68] BaseUI Updates (#7358) * Calculate the length of the right string and use it * Improve readability of Version Number * Prevent negative message IDs and proactively favorite DM'd nodes * Patch up Remove Favorite functionality * Fix warnings for TFT_MESH_* and hasSupportBrightness * Fix warning around casting variables * Correct Favorite Node Behavior to rebuild favorite nodes when updated. * Resolve bool kb_found issue not working for second discovery keyboards --------- Co-authored-by: Jonathan Bennett --- src/graphics/Screen.cpp | 2 ++ src/graphics/draw/DebugRenderer.cpp | 16 +++++++-- src/graphics/draw/MenuHandler.cpp | 17 ++++----- src/graphics/draw/NotificationRenderer.cpp | 2 +- src/graphics/draw/UIRenderer.cpp | 41 ++++++++++------------ src/graphics/draw/UIRenderer.h | 2 ++ src/input/cardKbI2cImpl.cpp | 1 + src/modules/CannedMessageModule.cpp | 8 ++++- 8 files changed, 52 insertions(+), 37 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index dbd0445f7..b9c9e2fbf 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -864,6 +864,8 @@ void Screen::setFrames(FrameFocus focus) uint8_t previousFrameCount = framesetInfo.frameCount; FramesetInfo fsi; // Location of specific frames, for applying focus parameter + graphics::UIRenderer::rebuildFavoritedNodes(); + LOG_DEBUG("Show standard frames"); showingNormalScreen = true; diff --git a/src/graphics/draw/DebugRenderer.cpp b/src/graphics/draw/DebugRenderer.cpp index b1a901f99..5420d1b4b 100644 --- a/src/graphics/draw/DebugRenderer.cpp +++ b/src/graphics/draw/DebugRenderer.cpp @@ -483,7 +483,7 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, } // **************************** -// * Memory Screen * +// * System Screen * // **************************** void drawMemoryUsage(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { @@ -593,7 +593,19 @@ void drawMemoryUsage(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, } line += 1; char appversionstr[35]; - snprintf(appversionstr, sizeof(appversionstr), "Ver.: %s", optstr(APP_VERSION)); + snprintf(appversionstr, sizeof(appversionstr), "Ver: %s", optstr(APP_VERSION)); + char appversionstr_formatted[40]; + char *lastDot = strrchr(appversionstr, '.'); + if (lastDot) { + size_t prefixLen = lastDot - appversionstr; + strncpy(appversionstr_formatted, appversionstr, prefixLen); + appversionstr_formatted[prefixLen] = '\0'; + strncat(appversionstr_formatted, " (", sizeof(appversionstr_formatted) - strlen(appversionstr_formatted) - 1); + strncat(appversionstr_formatted, lastDot + 1, sizeof(appversionstr_formatted) - strlen(appversionstr_formatted) - 1); + strncat(appversionstr_formatted, ")", sizeof(appversionstr_formatted) - strlen(appversionstr_formatted) - 1); + strncpy(appversionstr, appversionstr_formatted, sizeof(appversionstr) - 1); + appversionstr[sizeof(appversionstr) - 1] = '\0'; + } int textWidth = display->getStringWidth(appversionstr); int nameX = (SCREEN_WIDTH - textWidth) / 2; display->drawString(nameX, getTextPositions(display)[line], appversionstr); diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index ded492da6..7ed9c4ea1 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -375,12 +375,6 @@ void menuHandler::textMessageBaseMenu() 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, Notifications, ScreenOptions, PowerMenu, Test, enumEnd }; static const char *optionsArray[enumEnd] = {"Back"}; static int optionsEnumArray[enumEnd] = {Back}; @@ -450,11 +444,11 @@ void menuHandler::favoriteBaseMenu() bannerOptions.optionsEnumPtr = optionsEnumArray; bannerOptions.optionsCount = options; bannerOptions.bannerCallback = [](int selected) -> void { - if (selected == 1) { + if (selected == Preset) { cannedMessageModule->LaunchWithDestination(graphics::UIRenderer::currentFavoriteNodeNum); - } else if (selected == 2 && kb_found) { + } else if (selected == Freetext) { cannedMessageModule->LaunchFreetextWithDestination(graphics::UIRenderer::currentFavoriteNodeNum); - } else if ((!kb_found && selected == 2) || (selected == 3 && kb_found)) { + } else if (selected == Remove) { menuHandler::menuQueue = menuHandler::remove_favorite; screen->runNow(); } @@ -707,6 +701,7 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display) bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsCount = 10; bannerOptions.bannerCallback = [display](int selected) -> void { +#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || HAS_TFT uint8_t TFT_MESH_r = 0; uint8_t TFT_MESH_g = 0; uint8_t TFT_MESH_b = 0; @@ -758,7 +753,6 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display) screen->runNow(); } -#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || HAS_TFT if (selected != 0) { display->setColor(BLACK); display->fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); @@ -856,8 +850,9 @@ void menuHandler::removeFavoriteMenu() bannerOptions.optionsCount = 2; bannerOptions.bannerCallback = [](int selected) -> void { if (selected == 1) { + LOG_INFO("Removing %x as favorite node", graphics::UIRenderer::currentFavoriteNodeNum); nodeDB->set_favorite(false, graphics::UIRenderer::currentFavoriteNodeNum); - screen->setFrames(graphics::Screen::FOCUS_PRESERVE); + screen->setFrames(graphics::Screen::FOCUS_DEFAULT); } }; screen->showOverlayBanner(bannerOptions); diff --git a/src/graphics/draw/NotificationRenderer.cpp b/src/graphics/draw/NotificationRenderer.cpp index 7350c204f..d9cf280ac 100644 --- a/src/graphics/draw/NotificationRenderer.cpp +++ b/src/graphics/draw/NotificationRenderer.cpp @@ -156,7 +156,7 @@ void NotificationRenderer::drawNumberPicker(OLEDDisplay *display, OLEDDisplayUiS resetBanner(); return; } - if (curSelected == numDigits) { + if (curSelected == static_cast(numDigits)) { alertBannerCallback(currentNumber); resetBanner(); return; diff --git a/src/graphics/draw/UIRenderer.cpp b/src/graphics/draw/UIRenderer.cpp index 9be8b04f4..71d92616f 100644 --- a/src/graphics/draw/UIRenderer.cpp +++ b/src/graphics/draw/UIRenderer.cpp @@ -24,6 +24,23 @@ extern graphics::Screen *screen; namespace graphics { NodeNum UIRenderer::currentFavoriteNodeNum = 0; +std::vector graphics::UIRenderer::favoritedNodes; + +void graphics::UIRenderer::rebuildFavoritedNodes() +{ + favoritedNodes.clear(); + size_t total = nodeDB->getNumMeshNodes(); + for (size_t i = 0; i < total; i++) { + meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i); + if (!n || n->num == nodeDB->getNodeNum()) + continue; + if (n->is_favorite) + favoritedNodes.push_back(n); + } + + std::sort(favoritedNodes.begin(), favoritedNodes.end(), + [](const meshtastic_NodeInfoLite *a, const meshtastic_NodeInfoLite *b) { return a->num < b->num; }); +} #if !MESHTASTIC_EXCLUDE_GPS // GeoCoord object for coordinate conversions @@ -201,27 +218,7 @@ void UIRenderer::drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const mes // ********************** void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *state, int16_t x, int16_t y) { - // --- Cache favorite nodes for the current frame only, to save computation --- - static std::vector favoritedNodes; - static int prevFrame = -1; - // --- Only rebuild favorites list if we're on a new frame --- - if (state->currentFrame != prevFrame) { - prevFrame = state->currentFrame; - favoritedNodes.clear(); - size_t total = nodeDB->getNumMeshNodes(); - for (size_t i = 0; i < total; i++) { - meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i); - // Skip nulls and ourself - if (!n || n->num == nodeDB->getNodeNum()) - continue; - if (n->is_favorite) - favoritedNodes.push_back(n); - } - // Keep a stable, consistent display order - std::sort(favoritedNodes.begin(), favoritedNodes.end(), - [](const meshtastic_NodeInfoLite *a, const meshtastic_NodeInfoLite *b) { return a->num < b->num; }); - } if (favoritedNodes.empty()) return; @@ -657,7 +654,7 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta char combinedName[50]; snprintf(combinedName, sizeof(combinedName), "%s (%s)", longNameStr.empty() ? "" : longNameStr.c_str(), shortnameble); - if (SCREEN_WIDTH - (display->getStringWidth(longName) + display->getStringWidth(shortnameble)) > 10) { + if (SCREEN_WIDTH - (display->getStringWidth(combinedName)) > 10) { size_t len = strlen(combinedName); if (len >= 3 && strcmp(combinedName + len - 3, " ()") == 0) { combinedName[len - 3] = '\0'; // Remove the last three characters @@ -668,7 +665,7 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta nameX, ((rows == 4) ? getTextPositions(display)[line++] : getTextPositions(display)[line++]) + yOffset, combinedName); } else { // === LongName Centered === - textWidth = display->getStringWidth(longName); + textWidth = display->getStringWidth(longNameStr.c_str()); nameX = (SCREEN_WIDTH - textWidth) / 2; display->drawString(nameX, getTextPositions(display)[line++], longNameStr.c_str()); diff --git a/src/graphics/draw/UIRenderer.h b/src/graphics/draw/UIRenderer.h index 9e5e8c4b4..3c8e1dd9d 100644 --- a/src/graphics/draw/UIRenderer.h +++ b/src/graphics/draw/UIRenderer.h @@ -61,6 +61,8 @@ class UIRenderer static void drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); static NodeNum currentFavoriteNodeNum; + static std::vector favoritedNodes; + static void rebuildFavoritedNodes(); // OEM screens #ifdef USERPREFS_OEM_TEXT diff --git a/src/input/cardKbI2cImpl.cpp b/src/input/cardKbI2cImpl.cpp index 21ecf381a..fcbdd0a3f 100644 --- a/src/input/cardKbI2cImpl.cpp +++ b/src/input/cardKbI2cImpl.cpp @@ -67,4 +67,5 @@ void CardKbI2cImpl::init() } #endif inputBroker->registerSource(this); + kb_found = true; } \ No newline at end of file diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index a1b89e0f8..2690c67f0 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -850,7 +850,13 @@ void CannedMessageModule::sendText(NodeNum dest, ChannelIndex channel, const cha this->waitingForAck = true; // Log outgoing message - LOG_INFO("Send message id=%d, dest=%x, msg=%.*s", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes); + LOG_INFO("Send message id=%u, dest=%x, msg=%.*s", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes); + + if (p->to != 0xffffffff) { + LOG_INFO("Proactively adding %x as favorite node", p->to); + nodeDB->set_favorite(true, p->to); + screen->setFrames(graphics::Screen::FOCUS_PRESERVE); + } // Send to mesh and phone (even if no phone connected, to track ACKs) service->sendToMesh(p, RX_SRC_LOCAL, true);