diff --git a/.github/workflows/pr_tests.yml b/.github/workflows/pr_tests.yml
new file mode 100644
index 000000000..786feeced
--- /dev/null
+++ b/.github/workflows/pr_tests.yml
@@ -0,0 +1,238 @@
+name: Tests
+
+# DISABLED: Changed from automatic PR triggers to manual only
+on:
+ workflow_dispatch:
+ inputs:
+ reason:
+ description: "Reason for manual test run"
+ required: false
+ default: "Manual test execution"
+
+concurrency:
+ group: tests-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+
+permissions:
+ contents: read
+ actions: read
+ checks: write
+ pull-requests: write
+
+jobs:
+ native-tests:
+ name: "π§ͺ Native Tests"
+ if: github.repository == 'meshtastic/firmware'
+ uses: ./.github/workflows/test_native.yml
+ permissions:
+ contents: read
+ actions: read
+ checks: write
+
+ test-summary:
+ name: "π Test Results"
+ runs-on: ubuntu-latest
+ needs: [native-tests]
+ if: always()
+ permissions:
+ contents: read
+ actions: read
+ checks: write
+ pull-requests: write
+ steps:
+ - uses: actions/checkout@v5
+ with:
+ submodules: recursive
+
+ - name: Get release version string
+ run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
+ id: version
+
+ - name: Download test artifacts
+ if: needs.native-tests.result != 'skipped'
+ uses: actions/download-artifact@v5
+ with:
+ name: platformio-test-report-${{ steps.version.outputs.long }}.zip
+ merge-multiple: true
+
+ - name: Parse test results and create detailed summary
+ id: test-results
+ run: |
+ echo "## π§ͺ Test Results Summary" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+
+ # Check overall job status first
+ if [[ "${{ needs.native-tests.result }}" == "success" ]]; then
+ echo "β
**Overall Status**: PASSED" >> $GITHUB_STEP_SUMMARY
+ elif [[ "${{ needs.native-tests.result }}" == "failure" ]]; then
+ echo "β **Overall Status**: FAILED" >> $GITHUB_STEP_SUMMARY
+ elif [[ "${{ needs.native-tests.result }}" == "cancelled" ]]; then
+ echo "βΈοΈ **Overall Status**: CANCELLED" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "Tests were cancelled before completion." >> $GITHUB_STEP_SUMMARY
+ exit 0
+ else
+ echo "β οΈ **Overall Status**: SKIPPED" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "Tests were skipped." >> $GITHUB_STEP_SUMMARY
+ exit 0
+ fi
+
+ echo "" >> $GITHUB_STEP_SUMMARY
+
+ # Parse detailed test results if available
+ if [ -f "testreport.xml" ]; then
+ echo "### π Individual Test Results" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+
+ python3 << 'EOF'
+ import xml.etree.ElementTree as ET
+ import os
+
+ try:
+ tree = ET.parse('testreport.xml')
+ root = tree.getroot()
+
+ total_tests = 0
+ passed_tests = 0
+ failed_tests = 0
+ skipped_tests = 0
+
+ # Parse testsuite elements
+ for testsuite in root.findall('.//testsuite'):
+ suite_name = testsuite.get('name', 'Unknown')
+ suite_tests = int(testsuite.get('tests', '0'))
+ suite_failures = int(testsuite.get('failures', '0'))
+ suite_errors = int(testsuite.get('errors', '0'))
+ suite_skipped = int(testsuite.get('skipped', '0'))
+
+ total_tests += suite_tests
+ failed_tests += suite_failures + suite_errors
+ skipped_tests += suite_skipped
+ passed_tests += suite_tests - suite_failures - suite_errors - suite_skipped
+
+ if suite_tests > 0:
+ status = "β
" if (suite_failures + suite_errors) == 0 else "β"
+ print(f"**{status} Test Suite: {suite_name}**")
+ print(f"- Total: {suite_tests}")
+ print(f"- Passed: β
{suite_tests - suite_failures - suite_errors - suite_skipped}")
+ print(f"- Failed: β {suite_failures + suite_errors}")
+ if suite_skipped > 0:
+ print(f"- Skipped: βοΈ {suite_skipped}")
+ print("")
+
+ # Show individual test results for failed suites
+ if suite_failures + suite_errors > 0:
+ print("**Failed Tests:**")
+ for testcase in testsuite.findall('testcase'):
+ test_name = testcase.get('name', 'Unknown')
+ failure = testcase.find('failure')
+ error = testcase.find('error')
+
+ if failure is not None:
+ msg = failure.get('message', 'Unknown error')[:100]
+ print(f"- β `{test_name}`: {msg}")
+ elif error is not None:
+ msg = error.get('message', 'Unknown error')[:100]
+ print(f"- β `{test_name}`: ERROR - {msg}")
+ print("")
+ else:
+ # Show passed tests for successful suites
+ passed_count = 0
+ for testcase in testsuite.findall('testcase'):
+ if testcase.find('failure') is None and testcase.find('error') is None:
+ if passed_count < 5: # Limit to first 5 to avoid spam
+ test_name = testcase.get('name', 'Unknown')
+ print(f"- β
`{test_name}`: PASSED")
+ passed_count += 1
+ if passed_count > 5:
+ print(f"- ... and {passed_count - 5} more tests passed")
+ print("")
+
+ # Summary statistics
+ print("### π Test Statistics")
+ print(f"- **Total Tests**: {total_tests}")
+ print(f"- **Passed**: β
{passed_tests}")
+ print(f"- **Failed**: β {failed_tests}")
+ if skipped_tests > 0:
+ print(f"- **Skipped**: βοΈ {skipped_tests}")
+
+ if failed_tests > 0:
+ print(f"\nβ **{failed_tests} tests failed out of {total_tests} total**")
+ else:
+ print(f"\nβ
**All {total_tests} tests passed!**")
+
+ except Exception as e:
+ print(f"β Error parsing test results: {e}")
+ EOF
+ else
+ echo "β οΈ **No detailed test report available**" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "Test artifacts may not have been generated properly." >> $GITHUB_STEP_SUMMARY
+ fi
+
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "---" >> $GITHUB_STEP_SUMMARY
+ echo "View detailed logs in the [Actions tab](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> $GITHUB_STEP_SUMMARY
+
+ - name: Comment test results on PR
+ if: github.event_name == 'pull_request' && needs.native-tests.result != 'skipped'
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const fs = require('fs');
+
+ // Read the step summary to use as PR comment
+ let testSummary = "## π§ͺ Test Results Summary\n\n";
+
+ if ("${{ needs.native-tests.result }}" === "success") {
+ testSummary += "β
**All tests passed!**\n\n";
+ } else if ("${{ needs.native-tests.result }}" === "failure") {
+ testSummary += "β **Some tests failed.**\n\n";
+ } else {
+ testSummary += "β οΈ **Tests did not complete normally.**\n\n";
+ }
+
+ testSummary += `View detailed results: [Actions Run](${context.payload.repository.html_url}/actions/runs/${context.runId})\n\n`;
+ testSummary += "---\n";
+ testSummary += "*This comment will be automatically updated when new commits are pushed.*";
+
+ // Find existing comment
+ const comments = await github.rest.issues.listComments({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.issue.number
+ });
+
+ const botComment = comments.data.find(comment =>
+ comment.user.type === 'Bot' &&
+ comment.body.includes('π§ͺ Test Results Summary')
+ );
+
+ if (botComment) {
+ // Update existing comment
+ await github.rest.issues.updateComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ comment_id: botComment.id,
+ body: testSummary
+ });
+ } else {
+ // Create new comment
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: context.issue.number,
+ body: testSummary
+ });
+ }
+
+ - name: Set overall status
+ run: |
+ if [[ "${{ needs.native-tests.result }}" == "success" ]]; then
+ echo "All tests passed! β
"
+ exit 0
+ else
+ echo "Some tests failed! β"
+ exit 1
+ fi
diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml
index f62255aea..de38e3ec0 100644
--- a/.trunk/trunk.yaml
+++ b/.trunk/trunk.yaml
@@ -9,9 +9,9 @@ plugins:
lint:
enabled:
- checkov@3.2.461
- - renovate@41.63.0
+ - renovate@41.74.0
- prettier@3.6.2
- - trufflehog@3.90.3
+ - trufflehog@3.90.5
- yamllint@1.37.1
- bandit@1.8.6
- trivy@0.64.1
diff --git a/Dockerfile b/Dockerfile
index 6a2ddeece..b1e151ac7 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -61,7 +61,7 @@ RUN apt-get update && apt-get --no-install-recommends -y install \
# Fetch compiled binary from the builder
COPY --from=builder /tmp/firmware/release/meshtasticd /usr/bin/
-COPY --from=builder /tmp/web /usr/share/meshtasticd/
+COPY --from=builder /tmp/web /usr/share/meshtasticd/web/
# Copy config templates
COPY ./bin/config.d /etc/meshtasticd/available.d
diff --git a/arch/nrf52/nrf52.ini b/arch/nrf52/nrf52.ini
index 653ec4ffd..d85480453 100644
--- a/arch/nrf52/nrf52.ini
+++ b/arch/nrf52/nrf52.ini
@@ -23,7 +23,7 @@ build_flags =
-DMESHTASTIC_EXCLUDE_PAXCOUNTER=1
build_src_filter =
- ${arduino_base.build_src_filter} - - - - - - - - - -
+ ${arduino_base.build_src_filter} - - - - - - - - - - -
lib_deps=
${arduino_base.lib_deps}
diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini
index 0626f8f0d..17d243791 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/6cb7a455b440dd0738e8ed74a18136ed5cf7ea63.zip
+ https://github.com/meshtastic/platform-native/archive/37d986499ce24511952d7146db72d667c6bdaff7.zip
framework = arduino
build_src_filter =
diff --git a/platformio.ini b/platformio.ini
index 62bbf8a24..ac30eb32e 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -60,9 +60,9 @@ monitor_speed = 115200
monitor_filters = direct
lib_deps =
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master
- https://github.com/meshtastic/esp8266-oled-ssd1306/archive/0119501e9983bd894830b02f545c377ee08d66fe.zip
- # renovate: datasource=custom.pio depName=OneButton packageName=mathertel/library/OneButton
- mathertel/OneButton@2.6.1
+ https://github.com/meshtastic/esp8266-oled-ssd1306/archive/9573abb64dc9c94f3051348f2bf4fc5cedf03c22.zip
+ # renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
+ https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
https://github.com/meshtastic/arduino-fsm/archive/7db3702bf0cfe97b783d6c72595e3f38e0b19159.zip
# renovate: datasource=git-refs depName=meshtastic-TinyGPSPlus packageName=https://github.com/meshtastic/TinyGPSPlus gitBranch=master
@@ -102,6 +102,14 @@ lib_deps =
# renovate: datasource=custom.pio depName=Syslog packageName=arcao/library/Syslog
arcao/Syslog@2.0.0
+; Minimal networking libs for nrf52 (excludes Syslog to save flash)
+[nrf52_networking_base]
+lib_deps =
+ # renovate: datasource=custom.pio depName=TBPubSubClient packageName=thingsboard/library/TBPubSubClient
+ thingsboard/TBPubSubClient@2.12.1
+ # renovate: datasource=custom.pio depName=NTPClient packageName=arduino-libraries/library/NTPClient
+ arduino-libraries/NTPClient@3.2.1
+
[radiolib_base]
lib_deps =
# renovate: datasource=custom.pio depName=RadioLib packageName=jgromes/library/RadioLib
@@ -110,7 +118,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/0cd108ff783539e41ef38258ba2784ab3b1bdc97.zip
+ https://github.com/meshtastic/device-ui/archive/8f5094b248c15ea2f9acf19cedfef6d2248fc1ff.zip
; Common libs for environmental measurements in telemetry module
[environmental_base]
diff --git a/protobufs b/protobufs
index e2c0831aa..be5137698 160000
--- a/protobufs
+++ b/protobufs
@@ -1 +1 @@
-Subproject commit e2c0831aa3d34a58a36c2b9fdcb828e58961cbc5
+Subproject commit be5137698027f9e9fe6e68d5d5d638049f61ba8f
diff --git a/src/BluetoothStatus.h b/src/BluetoothStatus.h
index f6bb43cc2..680aec929 100644
--- a/src/BluetoothStatus.h
+++ b/src/BluetoothStatus.h
@@ -89,14 +89,22 @@ class BluetoothStatus : public Status
case ConnectionState::CONNECTED:
LOG_DEBUG("BluetoothStatus CONNECTED");
#ifdef BLE_LED
+#ifdef BLE_LED_INVERTED
+ digitalWrite(BLE_LED, LOW);
+#else
digitalWrite(BLE_LED, HIGH);
+#endif
#endif
break;
case ConnectionState::DISCONNECTED:
LOG_DEBUG("BluetoothStatus DISCONNECTED");
#ifdef BLE_LED
+#ifdef BLE_LED_INVERTED
+ digitalWrite(BLE_LED, HIGH);
+#else
digitalWrite(BLE_LED, LOW);
+#endif
#endif
break;
}
diff --git a/src/buzz/buzz.cpp b/src/buzz/buzz.cpp
index b09d7a82c..b0d162a44 100644
--- a/src/buzz/buzz.cpp
+++ b/src/buzz/buzz.cpp
@@ -140,6 +140,10 @@ bool playNextLeadUpNote()
playTones(¬e, 1); // Play single note using existing playTones function
leadUpNoteIndex++;
+
+ if (leadUpNoteIndex >= leadUpNotesCount) {
+ return false; // this was the final note
+ }
return true; // Note was played (playTones handles buzzer availability internally)
}
diff --git a/src/graphics/EInkDisplay2.cpp b/src/graphics/EInkDisplay2.cpp
index a627a42cc..1c9f290b6 100644
--- a/src/graphics/EInkDisplay2.cpp
+++ b/src/graphics/EInkDisplay2.cpp
@@ -140,13 +140,13 @@ bool EInkDisplay::connect()
#endif
#endif
-#if defined(TTGO_T_ECHO) || defined(ELECROW_ThinkNode_M1)
+#if defined(TTGO_T_ECHO) || defined(ELECROW_ThinkNode_M1) || defined(T_ECHO_LITE)
{
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1);
adafruitDisplay = new GxEPD2_BW(*lowLevel);
adafruitDisplay->init();
-#ifdef ELECROW_ThinkNode_M1
+#if defined(ELECROW_ThinkNode_M1) || defined(T_ECHO_LITE)
adafruitDisplay->setRotation(4);
#else
adafruitDisplay->setRotation(3);
diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp
index 8d5635f89..fa71e17d8 100644
--- a/src/graphics/Screen.cpp
+++ b/src/graphics/Screen.cpp
@@ -365,9 +365,6 @@ void Screen::doDeepSleep()
{
#ifdef USE_EINK
setOn(false, graphics::UIRenderer::drawDeepSleepFrame);
-#ifdef PIN_EINK_EN
- digitalWrite(PIN_EINK_EN, LOW); // power off backlight
-#endif
#else
// Without E-Ink display:
setOn(false);
@@ -391,8 +388,12 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
dispdev->displayOn();
#endif
-#ifdef ELECROW_ThinkNode_M5
- io.digitalWrite(PCA_PIN_EINK_EN, HIGH);
+#ifdef PIN_EINK_EN
+ if (uiconfig.screen_brightness == 1)
+ digitalWrite(PIN_EINK_EN, HIGH);
+#elif defined(PCA_PIN_EINK_EN)
+ if (uiconfig.screen_brightness == 1)
+ io.digitalWrite(PCA_PIN_EINK_EN, HIGH);
#endif
#if defined(ST7789_CS) && \
@@ -424,13 +425,10 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
// eInkScreensaver parameter is usually NULL (default argument), default frame used instead
setScreensaverFrames(einkScreensaver);
#endif
-#ifdef ELECROW_ThinkNode_M1
- if (digitalRead(PIN_EINK_EN) == HIGH) {
- digitalWrite(PIN_EINK_EN, LOW);
- }
-#endif
-#ifdef ELECROW_ThinkNode_M5
+#ifdef PIN_EINK_EN
+ digitalWrite(PIN_EINK_EN, LOW);
+#elif defined(PCA_PIN_EINK_EN)
io.digitalWrite(PCA_PIN_EINK_EN, LOW);
#endif
@@ -694,7 +692,7 @@ int32_t Screen::runOnce()
#ifndef DISABLE_WELCOME_UNSET
if (!NotificationRenderer::isOverlayBannerShowing() && config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
- menuHandler::LoraRegionPicker(0);
+ menuHandler::OnboardMessage();
}
#endif
if (!NotificationRenderer::isOverlayBannerShowing() && rebootAtMsec != 0) {
diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp
index 3e9bafc6c..24ea6c47a 100644
--- a/src/graphics/TFTDisplay.cpp
+++ b/src/graphics/TFTDisplay.cpp
@@ -849,9 +849,29 @@ static LGFX *tft = nullptr;
#include
#include
+class PanelInit_ST7701 : public lgfx::Panel_ST7701
+{
+ public:
+ const uint8_t *getInitCommands(uint8_t listno) const override
+ {
+ // 180 degree hw rotation: vertical flip, horizontal flip
+ static constexpr const uint8_t list1[] = {0x36, 1, 0x10, // MADCTL for vertical flip
+ 0xFF, 5, 0x77, 0x01, 0x00, 0x00, 0x10, // Command2 BK0 SEL
+ 0xC7, 1, 0x04, // SDIR: X-direction Control (Horizontal Flip)
+ 0xFF, 5, 0x77, 0x01, 0x00, 0x00, 0x00, // Command2 BK0 DIS
+ 0xFF, 0xFF};
+ switch (listno) {
+ case 1:
+ return list1;
+ default:
+ return lgfx::Panel_ST7701::getInitCommands(listno);
+ }
+ }
+};
+
class LGFX : public lgfx::LGFX_Device
{
- lgfx::Panel_ST7701 _panel_instance;
+ PanelInit_ST7701 _panel_instance;
lgfx::Bus_RGB _bus_instance;
lgfx::Light_PWM _light_instance;
lgfx::Touch_FT5x06 _touch_instance;
@@ -1184,9 +1204,9 @@ bool TFTDisplay::connect()
attachInterrupt(digitalPinToInterrupt(SCREEN_TOUCH_INT), rak14014_tpIntHandle, FALLING);
#elif defined(T_DECK) || defined(PICOMPUTER_S3) || defined(CHATTER_2)
tft->setRotation(1); // T-Deck has the TFT in landscape
-#elif defined(T_WATCH_S3) || defined(SENSECAP_INDICATOR)
+#elif defined(T_WATCH_S3)
tft->setRotation(2); // T-Watch S3 left-handed orientation
-#elif ARCH_PORTDUINO
+#elif ARCH_PORTDUINO || defined(SENSECAP_INDICATOR)
tft->setRotation(0); // use config.yaml to set rotation
#else
tft->setRotation(3); // Orient horizontal and wide underneath the silkscreen name label
diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp
index f8e7bce35..d47e10729 100644
--- a/src/graphics/draw/MenuHandler.cpp
+++ b/src/graphics/draw/MenuHandler.cpp
@@ -26,6 +26,27 @@ menuHandler::screenMenus menuHandler::menuQueue = menu_none;
bool test_enabled = false;
uint8_t test_count = 0;
+void menuHandler::OnboardMessage()
+{
+ static const char *optionsArray[] = {"OK", "Got it!"};
+ enum optionsNumbers { OK, got };
+ BannerOverlayOptions bannerOptions;
+#if HAS_TFT
+ bannerOptions.message = "Welcome to Meshtastic!\nSwipe to navigate and\nlong press to select\nor open a menu.";
+#elif defined(BUTTON_PIN)
+ bannerOptions.message = "Welcome to Meshtastic!\nClick to navigate and\nlong press to select\nor open a menu.";
+#else
+ bannerOptions.message = "Welcome to Meshtastic!\nUse the Select button\nto open menus\nand make selections.";
+#endif
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = 2;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ menuHandler::menuQueue = menuHandler::no_timeout_lora_picker;
+ screen->runNow();
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
void menuHandler::LoraRegionPicker(uint32_t duration)
{
static const char *optionsArray[] = {"Back",
@@ -301,7 +322,7 @@ void menuHandler::homeBaseMenu()
static int optionsEnumArray[enumEnd] = {Back};
int options = 1;
-#ifdef PIN_EINK_EN
+#if defined(PIN_EINK_EN) || defined(PCA_PIN_EINK_EN)
optionsArray[options] = "Toggle Backlight";
optionsEnumArray[options++] = Backlight;
#else
@@ -325,12 +346,24 @@ void menuHandler::homeBaseMenu()
bannerOptions.optionsCount = options;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Backlight) {
-#ifdef PIN_EINK_EN
- if (digitalRead(PIN_EINK_EN) == HIGH) {
+#if defined(PIN_EINK_EN)
+ if (uiconfig.screen_brightness == 1) {
+ uiconfig.screen_brightness = 0;
digitalWrite(PIN_EINK_EN, LOW);
} else {
+ uiconfig.screen_brightness = 1;
digitalWrite(PIN_EINK_EN, HIGH);
}
+ saveUIConfig();
+#elif defined(PCA_PIN_EINK_EN)
+ if (uiconfig.screen_brightness == 1) {
+ uiconfig.screen_brightness = 0;
+ io.digitalWrite(PCA_PIN_EINK_EN, LOW);
+ } else {
+ uiconfig.screen_brightness = 1;
+ io.digitalWrite(PCA_PIN_EINK_EN, HIGH);
+ }
+ saveUIConfig();
#endif
} else if (selected == Sleep) {
screen->setOn(false);
@@ -1103,6 +1136,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
case lora_picker:
LoraRegionPicker();
break;
+ case no_timeout_lora_picker:
+ LoraRegionPicker(0);
+ break;
case TZ_picker:
TZPicker();
break;
diff --git a/src/graphics/draw/MenuHandler.h b/src/graphics/draw/MenuHandler.h
index 87a0b055e..b15cf237d 100644
--- a/src/graphics/draw/MenuHandler.h
+++ b/src/graphics/draw/MenuHandler.h
@@ -10,6 +10,7 @@ class menuHandler
enum screenMenus {
menu_none,
lora_picker,
+ no_timeout_lora_picker,
TZ_picker,
twelve_hour_picker,
clock_face_picker,
@@ -41,6 +42,7 @@ class menuHandler
};
static screenMenus menuQueue;
+ static void OnboardMessage();
static void LoraRegionPicker(uint32_t duration = 30000);
static void handleMenuSwitch(OLEDDisplay *display);
static void showConfirmationBanner(const char *message, std::function onConfirm);
diff --git a/src/graphics/draw/NotificationRenderer.cpp b/src/graphics/draw/NotificationRenderer.cpp
index d9cf280ac..3d635e588 100644
--- a/src/graphics/draw/NotificationRenderer.cpp
+++ b/src/graphics/draw/NotificationRenderer.cpp
@@ -383,7 +383,9 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
uint8_t firstOptionToShow = 0;
if (alertBannerOptions > 0) {
- if (curSelected > 1 && alertBannerOptions > visibleTotalLines - lineCount) {
+ if (visibleTotalLines - lineCount == 1) {
+ firstOptionToShow = curSelected;
+ } else if (curSelected > 1 && alertBannerOptions > visibleTotalLines - lineCount) {
if (curSelected > alertBannerOptions - visibleTotalLines + lineCount)
firstOptionToShow = alertBannerOptions - visibleTotalLines + lineCount;
else
@@ -392,6 +394,9 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
firstOptionToShow = 0;
}
}
+ // Useful log line for troubleshooting:
+ /* LOG_WARN("alertBannerOptions: %u, curSelected: %u, visibleTotalLines: %u, lineCount: %u, firstOptionToShow: %u",
+ alertBannerOptions, curSelected, visibleTotalLines, lineCount, firstOptionToShow); */
for (int i = firstOptionToShow; i < alertBannerOptions && linesShown < visibleTotalLines; i++, linesShown++) {
if (i == curSelected) {
diff --git a/src/input/ButtonThread.cpp b/src/input/ButtonThread.cpp
index 233bbefe0..32882f7ae 100644
--- a/src/input/ButtonThread.cpp
+++ b/src/input/ButtonThread.cpp
@@ -92,8 +92,11 @@ bool ButtonThread::initButton(const ButtonConfig &config)
if (config.shortLong != INPUT_BROKER_NONE) {
_shortLong = config.shortLong;
}
-
+#ifdef USE_EINK
+ userButton.setDebounceMs(0);
+#else
userButton.setDebounceMs(1);
+#endif
userButton.setPressMs(_longPressTime);
if (screen) {
@@ -137,8 +140,7 @@ int32_t ButtonThread::runOnce()
}
// Progressive lead-up sound system
- if (buttonCurrentlyPressed && (millis() - buttonPressStartTime) >= BUTTON_LEADUP_MS &&
- (millis() - buttonPressStartTime) < _longLongPressTime) {
+ if (buttonCurrentlyPressed && (millis() - buttonPressStartTime) >= BUTTON_LEADUP_MS) {
// Start the progressive sequence if not already active
if (!leadUpSequenceActive) {
@@ -150,13 +152,14 @@ int32_t ButtonThread::runOnce()
else if ((millis() - lastLeadUpNoteTime) >= 400) { // 400ms interval between notes
if (playNextLeadUpNote()) {
lastLeadUpNoteTime = millis();
+ } else {
+ leadUpPlayed = true;
}
}
}
// Reset when button is released
if (!buttonCurrentlyPressed && buttonWasPressed) {
- leadUpPlayed = false;
leadUpSequenceActive = false;
resetLeadUpSequence();
}
@@ -253,12 +256,13 @@ int32_t ButtonThread::runOnce()
LOG_INFO("LONG PRESS RELEASE AFTER %u MILLIS", millis() - buttonPressStartTime);
if (millis() > 30000 && _longLongPress != INPUT_BROKER_NONE &&
- (millis() - buttonPressStartTime) >= _longLongPressTime) {
+ (millis() - buttonPressStartTime) >= _longLongPressTime && leadUpPlayed) {
evt.inputEvent = _longLongPress;
this->notifyObservers(&evt);
}
// Reset combination tracking
waitingForLongPress = false;
+ leadUpPlayed = false;
break;
}
diff --git a/src/input/ButtonThread.h b/src/input/ButtonThread.h
index bbc8da2a7..c6d6557e2 100644
--- a/src/input/ButtonThread.h
+++ b/src/input/ButtonThread.h
@@ -92,7 +92,7 @@ class ButtonThread : public Observable, public concurrency::
voidFuncPtr _intRoutine = nullptr;
uint16_t _longPressTime = 500;
- uint16_t _longLongPressTime = 5000;
+ uint16_t _longLongPressTime = 3900;
int _pinNum = 0;
bool _activeLow = true;
bool _touchQuirk = false;
diff --git a/src/main.cpp b/src/main.cpp
index 9e46021c9..c53877e37 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -304,7 +304,6 @@ void setup()
Wire.begin(48, 47);
io.pinMode(PCA_PIN_EINK_EN, OUTPUT);
io.pinMode(PCA_PIN_POWER_EN, OUTPUT);
- io.digitalWrite(PCA_PIN_EINK_EN, HIGH);
io.digitalWrite(PCA_PIN_POWER_EN, HIGH);
// io.pinMode(C2_PIN, OUTPUT);
#endif
@@ -326,8 +325,12 @@ void setup()
#ifdef BLE_LED
pinMode(BLE_LED, OUTPUT);
+#ifdef BLE_LED_INVERTED
+ digitalWrite(BLE_LED, HIGH);
+#else
digitalWrite(BLE_LED, LOW);
#endif
+#endif
#if defined(T_DECK)
// GPIO10 manages all peripheral power supplies
diff --git a/src/mesh/LR11x0Interface.cpp b/src/mesh/LR11x0Interface.cpp
index a20db808e..a0d992c42 100644
--- a/src/mesh/LR11x0Interface.cpp
+++ b/src/mesh/LR11x0Interface.cpp
@@ -6,6 +6,10 @@
#include "mesh/NodeDB.h"
#ifdef LR11X0_DIO_AS_RF_SWITCH
#include "rfswitch.h"
+#elif ARCH_PORTDUINO
+#include "PortduinoGlue.h"
+#define rfswitch_dio_pins portduino_config.rfswitch_dio_pins
+#define rfswitch_table portduino_config.rfswitch_table
#else
static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC};
static const Module::RfSwitchMode_t rfswitch_table[] = {
@@ -14,10 +18,6 @@ static const Module::RfSwitchMode_t rfswitch_table[] = {
};
#endif
-#ifdef ARCH_PORTDUINO
-#include "PortduinoGlue.h"
-#endif
-
// Particular boards might define a different max power based on what their hardware can do, default to max power output if not
// specified (may be dangerous if using external PA and LR11x0 power config forgotten)
#if ARCH_PORTDUINO
@@ -117,17 +117,14 @@ template bool LR11x0Interface::init()
#ifdef LR11X0_DIO_AS_RF_SWITCH
bool dioAsRfSwitch = true;
#elif defined(ARCH_PORTDUINO)
- bool dioAsRfSwitch = false;
- if (settingsMap[dio2_as_rf_switch]) {
- dioAsRfSwitch = true;
- }
+ bool dioAsRfSwitch = portduino_config.has_rfswitch_table;
#else
bool dioAsRfSwitch = false;
#endif
if (dioAsRfSwitch) {
lora.setRfSwitchTable(rfswitch_dio_pins, rfswitch_table);
- LOG_DEBUG("Set DIO RF switch", res);
+ LOG_DEBUG("Set DIO RF switch");
}
if (res == RADIOLIB_ERR_NONE) {
diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp
index 836bec086..535b07aec 100644
--- a/src/mesh/NodeDB.cpp
+++ b/src/mesh/NodeDB.cpp
@@ -1616,24 +1616,33 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde
printBytes("Incoming Pubkey: ", p.public_key.bytes, 32);
// Alert the user if a remote node is advertising public key that matches our own
- if (owner.public_key.size == 32 && memcmp(p.public_key.bytes, owner.public_key.bytes, 32) == 0 && !duplicateWarned) {
- duplicateWarned = true;
- char warning[] = "Remote device %s has advertised your public key. This may indicate a compromised key. You may need "
- "to regenerate your public keys.";
- LOG_WARN(warning, p.long_name);
- meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
- cn->level = meshtastic_LogRecord_Level_WARNING;
- cn->time = getValidTime(RTCQualityFromNet);
- sprintf(cn->message, warning, p.long_name);
- service->sendClientNotification(cn);
+ if (owner.public_key.size == 32 && memcmp(p.public_key.bytes, owner.public_key.bytes, 32) == 0) {
+ if (!duplicateWarned) {
+ duplicateWarned = true;
+ char warning[] =
+ "Remote device %s has advertised your public key. This may indicate a compromised key. You may need "
+ "to regenerate your public keys.";
+ LOG_WARN(warning, p.long_name);
+ meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
+ cn->level = meshtastic_LogRecord_Level_WARNING;
+ cn->time = getValidTime(RTCQualityFromNet);
+ sprintf(cn->message, warning, p.long_name);
+ service->sendClientNotification(cn);
+ }
+ return false;
}
}
- if (info->user.public_key.size > 0) { // if we have a key for this user already, don't overwrite with a new one
+ if (info->user.public_key.size == 32) { // if we have a key for this user already, don't overwrite with a new one
+ // if the key doesn't match, don't update nodeDB at all.
+ if (p.public_key.size != 32 || (memcmp(p.public_key.bytes, info->user.public_key.bytes, 32) != 0)) {
+ LOG_WARN("Public Key mismatch, dropping NodeInfo");
+ return false;
+ }
LOG_INFO("Public Key set for node, not updating!");
// we copy the key into the incoming packet, to prevent overwrite
p.public_key.size = 32;
memcpy(p.public_key.bytes, info->user.public_key.bytes, 32);
- } else if (p.public_key.size > 0) {
+ } else if (p.public_key.size == 32) {
LOG_INFO("Update Node Pubkey!");
}
#endif
diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp
index 305689fff..a3a8a2087 100644
--- a/src/mesh/PhoneAPI.cpp
+++ b/src/mesh/PhoneAPI.cpp
@@ -192,12 +192,6 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
size_t PhoneAPI::getFromRadio(uint8_t *buf)
{
- if (!available()) {
- return 0;
- }
- // In case we send a FromRadio packet
- memset(&fromRadioScratch, 0, sizeof(fromRadioScratch));
-
// Respond to heartbeat by sending queue status
if (heartbeatReceived) {
memset(&fromRadioScratch, 0, sizeof(fromRadioScratch));
@@ -209,6 +203,12 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
return numbytes;
}
+ if (!available()) {
+ return 0;
+ }
+ // In case we send a FromRadio packet
+ memset(&fromRadioScratch, 0, sizeof(fromRadioScratch));
+
// Advance states as needed
switch (state) {
case STATE_SEND_NOTHING:
diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp
index 345a410b5..536163e3f 100644
--- a/src/mesh/Router.cpp
+++ b/src/mesh/Router.cpp
@@ -548,8 +548,10 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
// is not in the local nodedb
// First, only PKC encrypt packets we are originating
if (isFromUs(p) &&
- // Don't use PKC with simulator
- radioType != SIM_RADIO &&
+#if ARCH_PORTDUINO
+ // Sim radio via the cli flag skips PKC
+ !portduino_config.force_simradio &&
+#endif
// Don't use PKC with Ham mode
!owner.is_licensed &&
// Don't use PKC if it's not explicitly requested and a non-primary channel is requested
diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h
index 71cf02d09..9cd0b47b6 100644
--- a/src/mesh/generated/meshtastic/mesh.pb.h
+++ b/src/mesh/generated/meshtastic/mesh.pb.h
@@ -270,6 +270,8 @@ typedef enum _meshtastic_HardwareModel {
/* MeshSolar is an integrated power management and communication solution designed for outdoor low-power devices.
https://heltec.org/project/meshsolar/ */
meshtastic_HardwareModel_HELTEC_MESH_SOLAR = 108,
+ /* Lilygo T-Echo Lite */
+ meshtastic_HardwareModel_T_ECHO_LITE = 109,
/* ------------------------------------------------------------------------------------------------------------------------------------------
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/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h
index f758995c2..9af095e78 100644
--- a/src/mesh/generated/meshtastic/telemetry.pb.h
+++ b/src/mesh/generated/meshtastic/telemetry.pb.h
@@ -99,7 +99,9 @@ typedef enum _meshtastic_TelemetrySensorType {
/* Sensirion SFA30 Formaldehyde sensor */
meshtastic_TelemetrySensorType_SFA30 = 42,
/* SEN5X PM SENSORS */
- meshtastic_TelemetrySensorType_SEN5X = 43
+ meshtastic_TelemetrySensorType_SEN5X = 43,
+ /* TSL2561 light sensor */
+ meshtastic_TelemetrySensorType_TSL2561 = 44
} meshtastic_TelemetrySensorType;
/* Struct definitions */
@@ -434,8 +436,8 @@ extern "C" {
/* Helper constants for enums */
#define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET
-#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_SEN5X
-#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_SEN5X+1))
+#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_TSL2561
+#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_TSL2561+1))
diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp
index 39b297965..866497ecc 100644
--- a/src/modules/SerialModule.cpp
+++ b/src/modules/SerialModule.cpp
@@ -60,7 +60,7 @@
SerialModule *serialModule;
SerialModuleRadio *serialModuleRadio;
-#if defined(TTGO_T_ECHO) || defined(CANARYONE) || defined(MESHLINK) || defined(ELECROW_ThinkNode_M1) || \
+#if defined(TTGO_T_ECHO) || defined(T_ECHO_LITE) || defined(CANARYONE) || defined(MESHLINK) || defined(ELECROW_ThinkNode_M1) || \
defined(ELECROW_ThinkNode_M5)
SerialModule::SerialModule() : StreamAPI(&Serial), concurrency::OSThread("Serial") {}
static Print *serialPrint = &Serial;
@@ -179,8 +179,8 @@ int32_t SerialModule::runOnce()
Serial.begin(baud);
Serial.setTimeout(moduleConfig.serial.timeout > 0 ? moduleConfig.serial.timeout : TIMEOUT);
}
-#elif !defined(TTGO_T_ECHO) && !defined(CANARYONE) && !defined(MESHLINK) && !defined(ELECROW_ThinkNode_M1) && \
- !defined(ELECROW_ThinkNode_M5)
+#elif !defined(TTGO_T_ECHO) && !defined(T_ECHO_LITE) && !defined(CANARYONE) && !defined(MESHLINK) && \
+ !defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M5)
if (moduleConfig.serial.rxd && moduleConfig.serial.txd) {
#ifdef ARCH_RP2040
Serial2.setFIFOSize(RX_BUFFER);
@@ -236,8 +236,8 @@ int32_t SerialModule::runOnce()
}
}
-#if !defined(TTGO_T_ECHO) && !defined(CANARYONE) && !defined(MESHLINK) && !defined(ELECROW_ThinkNode_M1) && \
- !defined(ELECROW_ThinkNode_M5)
+#if !defined(TTGO_T_ECHO) && !defined(T_ECHO_LITE) && !defined(CANARYONE) && !defined(MESHLINK) && \
+ !defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M5)
else if ((moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_WS85)) {
processWXSerial();
@@ -496,8 +496,8 @@ ParsedLine parseLine(const char *line)
*/
void SerialModule::processWXSerial()
{
-#if !defined(TTGO_T_ECHO) && !defined(CANARYONE) && !defined(CONFIG_IDF_TARGET_ESP32C6) && !defined(MESHLINK) && \
- !defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M5)
+#if !defined(TTGO_T_ECHO) && !defined(T_ECHO_LITE) && !defined(CANARYONE) && !defined(CONFIG_IDF_TARGET_ESP32C6) && \
+ !defined(MESHLINK) && !defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M5)
static unsigned int lastAveraged = 0;
static unsigned int averageIntervalMillis = 300000; // 5 minutes hard coded.
static double dir_sum_sin = 0;
diff --git a/src/nimble/NimbleBluetooth.cpp b/src/nimble/NimbleBluetooth.cpp
index 834184292..95e191c8e 100644
--- a/src/nimble/NimbleBluetooth.cpp
+++ b/src/nimble/NimbleBluetooth.cpp
@@ -223,9 +223,12 @@ void NimbleBluetooth::deinit()
LOG_INFO("Disable bluetooth until reboot");
#ifdef BLE_LED
+#ifdef BLE_LED_INVERTED
+ digitalWrite(BLE_LED, HIGH);
+#else
digitalWrite(BLE_LED, LOW);
#endif
-
+#endif
NimBLEDevice::deinit();
#endif
}
diff --git a/src/platform/nrf52/architecture.h b/src/platform/nrf52/architecture.h
index ce42bf849..064bd8ef0 100644
--- a/src/platform/nrf52/architecture.h
+++ b/src/platform/nrf52/architecture.h
@@ -60,6 +60,8 @@
#define HW_VENDOR meshtastic_HardwareModel_RAK4631
#elif defined(TTGO_T_ECHO)
#define HW_VENDOR meshtastic_HardwareModel_T_ECHO
+#elif defined(T_ECHO_LITE)
+#define HW_VENDOR meshtastic_HardwareModel_T_ECHO_LITE
#elif defined(ELECROW_ThinkNode_M1)
#define HW_VENDOR meshtastic_HardwareModel_THINKNODE_M1
#elif defined(NANO_G2_ULTRA)
diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp
index 5f99ec2c3..3753c944c 100644
--- a/src/platform/portduino/PortduinoGlue.cpp
+++ b/src/platform/portduino/PortduinoGlue.cpp
@@ -29,11 +29,11 @@
std::map settingsMap;
std::map settingsStrings;
+portduino_config_struct portduino_config;
std::ofstream traceFile;
Ch341Hal *ch341Hal = nullptr;
char *configPath = nullptr;
char *optionMac = nullptr;
-bool forceSimulated = false;
bool verboseEnabled = false;
const char *argp_program_version = optstr(APP_VERSION);
@@ -66,7 +66,7 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
configPath = arg;
break;
case 's':
- forceSimulated = true;
+ portduino_config.force_simradio = true;
break;
case 'h':
optionMac = arg;
@@ -189,7 +189,7 @@ void portduinoSetup()
YAML::Node yamlConfig;
- if (forceSimulated == true) {
+ if (portduino_config.force_simradio == true) {
settingsMap[use_simradio] = true;
} else if (configPath != nullptr) {
if (loadConfig(configPath)) {
@@ -553,6 +553,48 @@ bool loadConfig(const char *configPath)
}
}
}
+ if (yamlConfig["Lora"]["rfswitch_table"]) {
+ portduino_config.has_rfswitch_table = true;
+ portduino_config.rfswitch_table[0].mode = LR11x0::MODE_STBY;
+ portduino_config.rfswitch_table[1].mode = LR11x0::MODE_RX;
+ portduino_config.rfswitch_table[2].mode = LR11x0::MODE_TX;
+ portduino_config.rfswitch_table[3].mode = LR11x0::MODE_TX_HP;
+ portduino_config.rfswitch_table[4].mode = LR11x0::MODE_TX_HF;
+ portduino_config.rfswitch_table[5].mode = LR11x0::MODE_GNSS;
+ portduino_config.rfswitch_table[6].mode = LR11x0::MODE_WIFI;
+ portduino_config.rfswitch_table[7] = END_OF_MODE_TABLE;
+
+ for (int i = 0; i < 5; i++) {
+
+ // set up the pin array first
+ if (yamlConfig["Lora"]["rfswitch_table"]["pins"][i].as("") == "DIO5")
+ portduino_config.rfswitch_dio_pins[i] = RADIOLIB_LR11X0_DIO5;
+ if (yamlConfig["Lora"]["rfswitch_table"]["pins"][i].as("") == "DIO6")
+ portduino_config.rfswitch_dio_pins[i] = RADIOLIB_LR11X0_DIO6;
+ if (yamlConfig["Lora"]["rfswitch_table"]["pins"][i].as("") == "DIO7")
+ portduino_config.rfswitch_dio_pins[i] = RADIOLIB_LR11X0_DIO7;
+ if (yamlConfig["Lora"]["rfswitch_table"]["pins"][i].as("") == "DIO8")
+ portduino_config.rfswitch_dio_pins[i] = RADIOLIB_LR11X0_DIO8;
+ if (yamlConfig["Lora"]["rfswitch_table"]["pins"][i].as("") == "DIO10")
+ portduino_config.rfswitch_dio_pins[i] = RADIOLIB_LR11X0_DIO10;
+
+ // now fill in the table
+ if (yamlConfig["Lora"]["rfswitch_table"]["MODE_STBY"][i].as("") == "HIGH")
+ portduino_config.rfswitch_table[0].values[i] = HIGH;
+ if (yamlConfig["Lora"]["rfswitch_table"]["MODE_RX"][i].as("") == "HIGH")
+ portduino_config.rfswitch_table[1].values[i] = HIGH;
+ if (yamlConfig["Lora"]["rfswitch_table"]["MODE_TX"][i].as("") == "HIGH")
+ portduino_config.rfswitch_table[2].values[i] = HIGH;
+ if (yamlConfig["Lora"]["rfswitch_table"]["MODE_TX_HP"][i].as("") == "HIGH")
+ portduino_config.rfswitch_table[3].values[i] = HIGH;
+ if (yamlConfig["Lora"]["rfswitch_table"]["MODE_TX_HF"][i].as("") == "HIGH")
+ portduino_config.rfswitch_table[4].values[i] = HIGH;
+ if (yamlConfig["Lora"]["rfswitch_table"]["MODE_GNSS"][i].as("") == "HIGH")
+ portduino_config.rfswitch_table[5].values[i] = HIGH;
+ if (yamlConfig["Lora"]["rfswitch_table"]["MODE_WIFI"][i].as("") == "HIGH")
+ portduino_config.rfswitch_table[6].values[i] = HIGH;
+ }
+ }
}
if (yamlConfig["GPIO"]) {
settingsMap[userButtonPin] = yamlConfig["GPIO"]["User"].as(RADIOLIB_NC);
diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h
index 288870eef..6e450c90e 100644
--- a/src/platform/portduino/PortduinoGlue.h
+++ b/src/platform/portduino/PortduinoGlue.h
@@ -3,6 +3,8 @@
#include