From f2d3f548242c80dd460924d3da64dee425f759df Mon Sep 17 00:00:00 2001 From: Austin Date: Thu, 3 Jul 2025 16:10:35 -0400 Subject: [PATCH 01/20] No routers allowed! (#7220) --- src/modules/AdminModule.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 12a586cd7..0ba0e1164 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -630,6 +630,7 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c) #if USERPREFS_EVENT_MODE // If we're in event mode, nobody is a Router or Repeater if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER || + config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_LATE || config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) { config.device.role = meshtastic_Config_DeviceConfig_Role_CLIENT; } From 2254d551f4afb8ba1196eed125ec98ff5e3a64b6 Mon Sep 17 00:00:00 2001 From: todd-herbert Date: Fri, 4 Jul 2025 08:40:43 +1200 Subject: [PATCH 02/20] Honor custom userPrefs boot-screens in InkHUD (#7217) * Honor custom boot screen from userPrefs.jsonc * Meshtastic logo when powered off, userPrefs logo at boot --------- Co-authored-by: Ben Meadors --- .../InkHUD/Applets/System/Logo/LogoApplet.cpp | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/graphics/niche/InkHUD/Applets/System/Logo/LogoApplet.cpp b/src/graphics/niche/InkHUD/Applets/System/Logo/LogoApplet.cpp index 858b1e132..ecaa7cea3 100644 --- a/src/graphics/niche/InkHUD/Applets/System/Logo/LogoApplet.cpp +++ b/src/graphics/niche/InkHUD/Applets/System/Logo/LogoApplet.cpp @@ -52,6 +52,40 @@ void InkHUD::LogoApplet::onRender() setTextColor(WHITE); } +#ifdef USERPREFS_OEM_IMAGE_DATA // Custom boot screen, if defined in userPrefs.jsonc + + // Only show the custom screen at startup + // This allows us to draw the usual Meshtastic logo at shutdown + // The effect is similar to the two-stage userPrefs boot screen used by BaseUI + if (millis() < 10 * 1000UL) { + + // Draw the custom logo + const uint8_t logo[] = USERPREFS_OEM_IMAGE_DATA; + drawXBitmap(logoCX - (USERPREFS_OEM_IMAGE_WIDTH / 2), // Left + logoCY - (USERPREFS_OEM_IMAGE_HEIGHT / 2), // Top + logo, // XBM data + USERPREFS_OEM_IMAGE_WIDTH, // Width + USERPREFS_OEM_IMAGE_HEIGHT, // Height + inverted ? WHITE : BLACK // Color + ); + + // Select the largest font which will still comfortably fit the custom text + setFont(fontLarge); + if (getTextWidth(USERPREFS_OEM_TEXT) > 0.8 * width()) + setFont(fontMedium); + if (getTextWidth(USERPREFS_OEM_TEXT) > 0.8 * width()) + setFont(fontSmall); + + // Draw custom text below logo + int16_t logoB = logoCY + (USERPREFS_OEM_IMAGE_HEIGHT / 2); // Bottom of the logo + printAt(X(0.5), logoB + Y(0.1), USERPREFS_OEM_TEXT, CENTER, TOP); + + // Don't draw the normal boot screen, we've already drawn our custom version + return; + } + +#endif + drawLogo(logoCX, logoCY, logoW, logoH, inverted ? WHITE : BLACK); if (!textLeft.empty()) { From 93132fad284464adfc3129eb912aaa7827ad348a Mon Sep 17 00:00:00 2001 From: Jason P Date: Thu, 3 Jul 2025 17:20:51 -0500 Subject: [PATCH 03/20] Battery Layout Updates and Icons Changes (#7221) * Testing battery states with some info lines in the drawCommonHeader * Update logic of USB connected, update images for states * Tweak battery layout for isHighResolution * Hide the magic 101% * Adjust padding for SENSECAP_INDICATOR * Excessive logs are unnecessary as troubleshooting is done. * Reduce excess code - simplify readability * Restore Lightning Bolt for Charging and related alignment issues --------- Co-authored-by: Jonathan Bennett --- src/graphics/SharedUIDisplay.cpp | 94 ++++++++++++++++++----------- src/graphics/draw/ClockRenderer.cpp | 3 + src/graphics/images.h | 5 +- 3 files changed, 65 insertions(+), 37 deletions(-) diff --git a/src/graphics/SharedUIDisplay.cpp b/src/graphics/SharedUIDisplay.cpp index 9f2422748..7cd876ac5 100644 --- a/src/graphics/SharedUIDisplay.cpp +++ b/src/graphics/SharedUIDisplay.cpp @@ -99,10 +99,17 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti // === Battery State === int chargePercent = powerStatus->getBatteryChargePercent(); - bool isCharging = powerStatus->getIsCharging() == meshtastic::OptionalBool::OptTrue; - if (chargePercent == 100) { + bool isCharging = powerStatus->getIsCharging(); + bool usbPowered = powerStatus->getHasUSB(); + + if (chargePercent >= 100) { isCharging = false; } + if (chargePercent == 101) { + usbPowered = true; // Forcing this flag on for the express purpose that some devices have no concept of having a USB cable + // plugged in + } + uint32_t now = millis(); #ifndef USE_EINK @@ -115,48 +122,63 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti bool useHorizontalBattery = (isHighResolution && screenW >= screenH); const int textY = y + (highlightHeight - FONT_HEIGHT_SMALL) / 2; + int batteryX = 1; + int batteryY = HEADER_OFFSET_Y + 1; + // === Battery Icons === - if (useHorizontalBattery) { - int batteryX = 2; - int batteryY = HEADER_OFFSET_Y + 3; - display->drawXbm(batteryX, batteryY, 9, 13, batteryBitmap_h_bottom); - display->drawXbm(batteryX + 9, batteryY, 9, 13, batteryBitmap_h_top); - if (isCharging && isBoltVisibleShared) - display->drawXbm(batteryX + 4, batteryY, 9, 13, lightning_bolt_h); - else { - display->drawLine(batteryX + 5, batteryY, batteryX + 10, batteryY); - display->drawLine(batteryX + 5, batteryY + 12, batteryX + 10, batteryY + 12); - int fillWidth = 14 * chargePercent / 100; - display->fillRect(batteryX + 1, batteryY + 1, fillWidth, 11); + if (usbPowered && !isCharging) { // This is a basic check to determine USB Powered is flagged but not charging + batteryX += 1; + batteryY += 2; + if (isHighResolution) { + display->drawXbm(batteryX, batteryY, 19, 12, imgUSB_HighResolution); + batteryX += 20; // Icon + 1 pixel + } else { + display->drawXbm(batteryX, batteryY, 10, 8, imgUSB); + batteryX += 11; // Icon + 1 pixel } } else { - int batteryX = 1; - int batteryY = HEADER_OFFSET_Y + 1; + if (useHorizontalBattery) { + batteryX += 1; + batteryY += 2; + display->drawXbm(batteryX, batteryY, 9, 13, batteryBitmap_h_bottom); + display->drawXbm(batteryX + 9, batteryY, 9, 13, batteryBitmap_h_top); + if (isCharging && isBoltVisibleShared) + display->drawXbm(batteryX + 4, batteryY, 9, 13, lightning_bolt_h); + else { + display->drawLine(batteryX + 5, batteryY, batteryX + 10, batteryY); + display->drawLine(batteryX + 5, batteryY + 12, batteryX + 10, batteryY + 12); + int fillWidth = 14 * chargePercent / 100; + display->fillRect(batteryX + 1, batteryY + 1, fillWidth, 11); + } + batteryX += 18; // Icon + 2 pixels + } else { #ifdef USE_EINK - batteryY += 2; + batteryY += 2; #endif - display->drawXbm(batteryX, batteryY, 7, 11, batteryBitmap_v); - if (isCharging && isBoltVisibleShared) - display->drawXbm(batteryX + 1, batteryY + 3, 5, 5, lightning_bolt_v); - else { - display->drawXbm(batteryX - 1, batteryY + 4, 8, 3, batteryBitmap_sidegaps_v); - int fillHeight = 8 * chargePercent / 100; - int fillY = batteryY - fillHeight; - display->fillRect(batteryX + 1, fillY + 10, 5, fillHeight); + display->drawXbm(batteryX, batteryY, 7, 11, batteryBitmap_v); + if (isCharging && isBoltVisibleShared) + display->drawXbm(batteryX + 1, batteryY + 3, 5, 5, lightning_bolt_v); + else { + display->drawXbm(batteryX - 1, batteryY + 4, 8, 3, batteryBitmap_sidegaps_v); + int fillHeight = 8 * chargePercent / 100; + int fillY = batteryY - fillHeight; + display->fillRect(batteryX + 1, fillY + 10, 5, fillHeight); + } + batteryX += 9; // Icon + 2 pixels } } - // === Battery % Display === - char chargeStr[4]; - snprintf(chargeStr, sizeof(chargeStr), "%d", chargePercent); - int chargeNumWidth = display->getStringWidth(chargeStr); - const int batteryOffset = useHorizontalBattery ? 19 : 9; - const int percentX = x + batteryOffset; - display->drawString(percentX, textY, chargeStr); - display->drawString(percentX + chargeNumWidth - 1, textY, "%"); - if (isBold) { - display->drawString(percentX + 1, textY, chargeStr); - display->drawString(percentX + chargeNumWidth, textY, "%"); + if (chargePercent != 101) { + // === Battery % Display === + char chargeStr[4]; + snprintf(chargeStr, sizeof(chargeStr), "%d", chargePercent); + int chargeNumWidth = display->getStringWidth(chargeStr); + display->drawString(batteryX, textY, chargeStr); + display->drawString(batteryX + chargeNumWidth - 1, textY, "%"); + if (isBold) { + display->drawString(batteryX + 1, textY, chargeStr); + display->drawString(batteryX + chargeNumWidth, textY, "%"); + } } // === Time and Right-aligned Icons === diff --git a/src/graphics/draw/ClockRenderer.cpp b/src/graphics/draw/ClockRenderer.cpp index 7ccb1c03c..8d7e91000 100644 --- a/src/graphics/draw/ClockRenderer.cpp +++ b/src/graphics/draw/ClockRenderer.cpp @@ -283,6 +283,9 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1 xOffset += (isHighResolution) ? 32 : 18; } int yOffset = (isHighResolution) ? 3 : 1; +#ifdef SENSECAP_INDICATOR + yOffset -= 3; +#endif if (config.display.use_12h_clock) { display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - yOffset - 2, isPM ? "pm" : "am"); diff --git a/src/graphics/images.h b/src/graphics/images.h index c5865878a..beef3a1b2 100644 --- a/src/graphics/images.h +++ b/src/graphics/images.h @@ -12,7 +12,10 @@ const uint8_t imgSatellite[] PROGMEM = { 0b00000000, 0b00000000, 0b00000000, 0b00011000, 0b11011011, 0b11111111, 0b11011011, 0b00011000, }; -const uint8_t imgUSB[] PROGMEM = {0x60, 0x60, 0x30, 0x18, 0x18, 0x18, 0x24, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x24, 0x24, 0x24, 0x3C}; +const uint8_t imgUSB[] PROGMEM = {0x00, 0xfc, 0xf0, 0xfc, 0x88, 0xff, 0x86, 0xfe, 0x85, 0xfe, 0x89, 0xff, 0xf1, 0xfc, 0x00, 0xfc}; +const uint8_t imgUSB_HighResolution[] PROGMEM = {0x00, 0x3e, 0xf8, 0x80, 0x43, 0xf8, 0xc0, 0xc2, 0xff, 0x60, 0x42, 0xfc, + 0x3c, 0xc2, 0xff, 0x22, 0x42, 0xf8, 0x3d, 0x42, 0xf8, 0x22, 0xc2, 0xff, + 0x61, 0x42, 0xfc, 0xc0, 0xc2, 0xff, 0x80, 0x43, 0xf8, 0x00, 0x3e, 0xf8}; const uint8_t imgPower[] PROGMEM = {0x40, 0x40, 0x40, 0x58, 0x48, 0x08, 0x08, 0x08, 0x1C, 0x22, 0x22, 0x41, 0x7F, 0x22, 0x22, 0x22}; const uint8_t imgUser[] PROGMEM = {0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99, 0x42, 0x3C}; From c1431f4f9ad090cd670edaf56b3600403aa4408d Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Thu, 3 Jul 2025 18:34:04 -0500 Subject: [PATCH 04/20] Disable low brightness, as this soft-bricks at least the L1 (#7223) --- src/graphics/draw/MenuHandler.cpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index 92954bf2e..6dbba853e 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -602,32 +602,28 @@ void menuHandler::BuzzerModeMenu() void menuHandler::BrightnessPickerMenu() { - static const char *optionsArray[] = {"Back", "Low", "Medium", "High", "Very High"}; + static const char *optionsArray[] = {"Back", "Low", "Medium", "High"}; // Get current brightness level to set initial selection - int currentSelection = 1; // Default to Low + int currentSelection = 1; // Default to Medium if (uiconfig.screen_brightness >= 255) { - currentSelection = 4; // Very High + currentSelection = 3; // Very High } else if (uiconfig.screen_brightness >= 128) { - currentSelection = 3; // High - } else if (uiconfig.screen_brightness >= 64) { - currentSelection = 2; // Medium + currentSelection = 2; // High } else { - currentSelection = 1; // Low + currentSelection = 1; // Medium } BannerOverlayOptions bannerOptions; bannerOptions.message = "Brightness"; bannerOptions.optionsArrayPtr = optionsArray; - bannerOptions.optionsCount = 5; + bannerOptions.optionsCount = 4; bannerOptions.bannerCallback = [](int selected) -> void { - if (selected == 1) { // Low - uiconfig.screen_brightness = 1; - } else if (selected == 2) { // Medium + if (selected == 1) { // Medium uiconfig.screen_brightness = 64; - } else if (selected == 3) { // High + } else if (selected == 2) { // High uiconfig.screen_brightness = 128; - } else if (selected == 4) { // Very High + } else if (selected == 3) { // Very High uiconfig.screen_brightness = 255; } From f13dc5b903067b2d10d85217524b8690946ea68c Mon Sep 17 00:00:00 2001 From: Tom Fifield Date: Fri, 4 Jul 2025 07:34:46 +0800 Subject: [PATCH 05/20] Add Kazakhstan frequencies (#7209) As reported by @KZ1R , Kazakhstan has frequencies in use for Lora devices that are not covered by our existing band selections. This adds * KZ_433 433.075 - 434.775 MHz <10 mW EIRP, Low Powered Devices (LPD) * KZ_863 863 - 868 MHz <25 mW EIRP, 500kHz channels allowed, must not be used at airfields Legal ref provided in https://github.com/meshtastic/firmware/issues/7204 and verified. https://www.gov.kz/memleket/entities/mdai/press/article/details/6128 Order of the Ministry of Investments and Development of the Republic of Kazakhstan No. 34 dated January 21, 2015. Published on 01 July 2024 19:03 Updated on 01 July 2024 Fixes https://github.com/meshtastic/firmware/issues/7204 --- src/mesh/RadioInterface.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 4db05b4d4..3632378a5 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -161,6 +161,14 @@ const RegionInfo regions[] = { RDEF(PH_433, 433.0f, 434.7f, 100, 0, 10, true, false, false), RDEF(PH_868, 868.0f, 869.4f, 100, 0, 14, true, false, false), RDEF(PH_915, 915.0f, 918.0f, 100, 0, 24, true, false, false), + /* + Kazakhstan + 433.075 - 434.775 MHz <10 mW EIRP, Low Powered Devices (LPD) + 863 - 868 MHz <25 mW EIRP, 500kHz channels allowed, must not be used at airfields + https://github.com/meshtastic/firmware/issues/7204 + */ + RDEF(KZ_433, 433.075f, 434.775f, 100, 0, 10, true, false, false), RDEF(KZ_863, 863.0f, 868.0f, 100, 0, 30, true, false, true), + /* 2.4 GHZ WLAN Band equivalent. Only for SX128x chips. */ @@ -681,4 +689,4 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p) sendingPacket = p; return p->encrypted.size + sizeof(PacketHeader); -} \ No newline at end of file +} From ff4eed08bcb5c0f99fbb8fbcd443d3896e08547a Mon Sep 17 00:00:00 2001 From: Matt Smith Date: Thu, 3 Jul 2025 20:41:17 -0400 Subject: [PATCH 06/20] Fixed --change-mode option since it was broken (#7144) getopts can't parse double-dash options so it had to be done separately. Also fixed where CHANGE_MODE was checked since it wasn't working either. --- bin/device-update.sh | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/bin/device-update.sh b/bin/device-update.sh index 6adfe4e0e..2a39cdef7 100755 --- a/bin/device-update.sh +++ b/bin/device-update.sh @@ -30,6 +30,18 @@ Flash image file to device, leave existing system intact." EOF } +# Check for --change-mode and remove it from arguments +NEW_ARGS="" +for arg in "$@"; do + if [ "$arg" = "--change-mode" ]; then + CHANGE_MODE=true + else + NEW_ARGS="$NEW_ARGS \"\$arg\"" + fi +done + +# Reset positional parameters to filtered list +eval set -- $NEW_ARGS while getopts ":hp:P:f:" opt; do case "${opt}" in @@ -43,9 +55,6 @@ while getopts ":hp:P:f:" opt; do ;; f) FILENAME=${OPTARG} ;; - --change-mode) - CHANGE_MODE=true - ;; *) echo "Invalid flag." show_help >&2 @@ -55,7 +64,7 @@ while getopts ":hp:P:f:" opt; do done shift "$((OPTIND-1))" -if [[ $CHANGE_MODE == true ]]; then +if [ "$CHANGE_MODE" = true ]; then $ESPTOOL_CMD --baud 1200 --after no_reset read_flash_status exit 0 fi From dfb07e8bd2d8a1b8fe4bb03bba460fa3503a7f7b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 3 Jul 2025 20:16:53 -0500 Subject: [PATCH 07/20] chore(deps): update meshtastic-esp32_https_server digest to 3223704 (#7225) --- arch/esp32/esp32.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini index cba84181b..faeca342f 100644 --- a/arch/esp32/esp32.ini +++ b/arch/esp32/esp32.ini @@ -49,7 +49,7 @@ lib_deps = ${environmental_extra.lib_deps} ${radiolib_base.lib_deps} # renovate: datasource=git-refs depName=meshtastic-esp32_https_server packageName=https://github.com/meshtastic/esp32_https_server gitBranch=master - https://github.com/meshtastic/esp32_https_server/archive/896f1771ceb5979987a0b41028bf1b4e7aad419b.zip + https://github.com/meshtastic/esp32_https_server/archive/3223704846752e6d545139204837bdb2a55459ca.zip # renovate: datasource=custom.pio depName=NimBLE-Arduino packageName=h2zero/library/NimBLE-Arduino h2zero/NimBLE-Arduino@^1.4.3 # renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master From 0f96bd7a26e5a435e90f03492d934121346cf5d2 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Thu, 3 Jul 2025 21:31:09 -0500 Subject: [PATCH 08/20] Add Kazakhstan to the BaseUI LoRa chooser (#7224) --- src/graphics/draw/MenuHandler.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index 6dbba853e..7e5063fef 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -48,12 +48,14 @@ void menuHandler::LoraRegionPicker(uint32_t duration) "PH_433", "PH_868", "PH_915", - "ANZ_433"}; + "ANZ_433", + "KZ_433", + "KZ_863"}; BannerOverlayOptions bannerOptions; bannerOptions.message = "Set the LoRa region"; bannerOptions.durationMs = duration; bannerOptions.optionsArrayPtr = optionsArray; - bannerOptions.optionsCount = 23; + bannerOptions.optionsCount = 25; bannerOptions.InitialSelected = 0; bannerOptions.bannerCallback = [](int selected) -> void { if (selected != 0 && config.lora.region != _meshtastic_Config_LoRaConfig_RegionCode(selected)) { From abbeb4874d6dcf983f298554c4a40f0874f2d634 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 4 Jul 2025 13:12:24 +0800 Subject: [PATCH 09/20] chore(deps): update xpowerslib to v0.3.0 (#7210) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- arch/esp32/esp32.ini | 2 +- arch/esp32/esp32c6.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini index faeca342f..6b9ebcb24 100644 --- a/arch/esp32/esp32.ini +++ b/arch/esp32/esp32.ini @@ -55,7 +55,7 @@ lib_deps = # renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip # renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib - lewisxhe/XPowersLib@^0.2.7 + lewisxhe/XPowersLib@0.3.0 # renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip # renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto diff --git a/arch/esp32/esp32c6.ini b/arch/esp32/esp32c6.ini index 26b5c0f5b..1afb9b547 100644 --- a/arch/esp32/esp32c6.ini +++ b/arch/esp32/esp32c6.ini @@ -28,7 +28,7 @@ lib_deps = ${environmental_extra.lib_deps} ${radiolib_base.lib_deps} # renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib - lewisxhe/XPowersLib@^0.2.7 + lewisxhe/XPowersLib@0.3.0 # renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip # renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto From f35ca812a32f26750aff1008428128f2ca8388f3 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 4 Jul 2025 05:30:56 -0500 Subject: [PATCH 10/20] Add a WiFi menu that can toggle back to Bluetooth (#7226) * Add Kazakhstan to the BaseUI LoRa chooser * Add a WiFi menu that can toggle back to Bluetooth --- src/graphics/Screen.cpp | 2 ++ src/graphics/draw/MenuHandler.cpp | 41 +++++++++++++++++++++++++++++++ src/graphics/draw/MenuHandler.h | 7 +++--- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 067e4418f..ed8119738 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -1373,6 +1373,8 @@ int Screen::handleInputEvent(const InputEvent *event) this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_hopsignal || this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_bearings) { menuHandler::nodeListMenu(); + } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.wifi) { + menuHandler::wifiBaseMenu(); } } else if (event->inputEvent == INPUT_BROKER_BACK) { showPrevFrame(); diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index 7e5063fef..43c226896 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -831,6 +831,44 @@ void menuHandler::numberTest() [](int number_picked) -> void { LOG_WARN("Nodenum: %u", number_picked); }); } +void menuHandler::wifiBaseMenu() +{ + enum optionsNumbers { Back, Wifi_toggle }; + + static const char *optionsArray[] = {"Back", "WiFi Toggle"}; + BannerOverlayOptions bannerOptions; + bannerOptions.message = "WiFi Menu"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = 2; + bannerOptions.bannerCallback = [](int selected) -> void { + if (selected == Wifi_toggle) { + menuQueue = wifi_toggle_menu; + screen->runNow(); + } + }; + screen->showOverlayBanner(bannerOptions); +} + +void menuHandler::wifiToggleMenu() +{ + enum optionsNumbers { Back, Wifi_toggle }; + + static const char *optionsArray[] = {"Back", "Disable"}; + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Disable Wifi and\nEnable Bluetooth?"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = 2; + bannerOptions.bannerCallback = [](int selected) -> void { + if (selected == Wifi_toggle) { + config.network.wifi_enabled = false; + config.bluetooth.enabled = true; + service->reloadConfig(SEGMENT_CONFIG); + rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000); + } + }; + screen->showOverlayBanner(bannerOptions); +} + void menuHandler::handleMenuSwitch(OLEDDisplay *display) { if (menuQueue != menu_none) @@ -894,6 +932,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display) case number_test: numberTest(); break; + case wifi_toggle_menu: + wifiToggleMenu(); + break; } menuQueue = menu_none; } diff --git a/src/graphics/draw/MenuHandler.h b/src/graphics/draw/MenuHandler.h index 09279b041..8824e38ed 100644 --- a/src/graphics/draw/MenuHandler.h +++ b/src/graphics/draw/MenuHandler.h @@ -13,9 +13,7 @@ class menuHandler clock_face_picker, clock_menu, position_base_menu, -#if !MESHTASTIC_EXCLUDE_GPS gps_toggle_menu, -#endif compass_point_north_menu, reset_node_db_menu, buzzermodemenupicker, @@ -26,7 +24,8 @@ class menuHandler add_favorite, remove_favorite, test_menu, - number_test + number_test, + wifi_toggle_menu }; static screenMenus menuQueue; @@ -54,6 +53,8 @@ class menuHandler static void removeFavoriteMenu(); static void testMenu(); static void numberTest(); + static void wifiBaseMenu(); + static void wifiToggleMenu(); }; } // namespace graphics \ No newline at end of file From 1994bb3cd193c2b3a107ad986bb0a9918841df52 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 4 Jul 2025 08:10:23 -0500 Subject: [PATCH 11/20] automated bumps (#7227) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> --- bin/org.meshtastic.meshtasticd.metainfo.xml | 3 +++ debian/changelog | 7 +++++-- version.properties | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/bin/org.meshtastic.meshtasticd.metainfo.xml b/bin/org.meshtastic.meshtasticd.metainfo.xml index ed57386a3..47082718a 100644 --- a/bin/org.meshtastic.meshtasticd.metainfo.xml +++ b/bin/org.meshtastic.meshtasticd.metainfo.xml @@ -87,6 +87,9 @@ + + https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.2 + https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.1 diff --git a/debian/changelog b/debian/changelog index 70a01bab4..42488692b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -meshtasticd (2.7.1.0) UNRELEASED; urgency=medium +meshtasticd (2.7.2.0) UNRELEASED; urgency=medium [ Austin Lane ] * Initial packaging @@ -25,4 +25,7 @@ meshtasticd (2.7.1.0) UNRELEASED; urgency=medium [ ] * GitHub Actions Automatic version bump - -- Fri, 27 Jun 2025 20:12:21 +0000 + [ ] + * GitHub Actions Automatic version bump + + -- Fri, 04 Jul 2025 11:58:01 +0000 diff --git a/version.properties b/version.properties index 3fe1aa385..69f2d6af5 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 7 -build = 1 +build = 2 From 29893e0c281c7266eeeffe956a5b1c367dfc9417 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Fri, 4 Jul 2025 14:22:59 -0500 Subject: [PATCH 12/20] Don't run ble getFromRadio() unless the phone has requested a packet (#7231) --- src/nimble/NimbleBluetooth.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/nimble/NimbleBluetooth.cpp b/src/nimble/NimbleBluetooth.cpp index 8f53c9229..834184292 100644 --- a/src/nimble/NimbleBluetooth.cpp +++ b/src/nimble/NimbleBluetooth.cpp @@ -29,6 +29,7 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread uint8_t fromRadioBytes[meshtastic_FromRadio_size] = {0}; size_t numBytes = 0; bool hasChecked = false; + bool phoneWants = false; protected: virtual int32_t runOnce() override @@ -38,10 +39,10 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread for (uint8_t i = 0; i < queue_size; i++) { handleToRadio(nimble_queue.at(i).data(), nimble_queue.at(i).length()); } - LOG_WARN("Queue_size %u", queue_size); + LOG_DEBUG("Queue_size %u", queue_size); queue_size = 0; } - if (hasChecked == false) { + if (hasChecked == false && phoneWants == true) { numBytes = getFromRadio(fromRadioBytes); hasChecked = true; } @@ -98,9 +99,12 @@ class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks { virtual void onRead(NimBLECharacteristic *pCharacteristic) { - while (!bluetoothPhoneAPI->hasChecked) { + int tries = 0; + bluetoothPhoneAPI->phoneWants = true; + while (!bluetoothPhoneAPI->hasChecked && tries < 100) { bluetoothPhoneAPI->setIntervalFromNow(0); delay(20); + tries++; } std::lock_guard guard(bluetoothPhoneAPI->nimble_mutex); std::string fromRadioByteString(bluetoothPhoneAPI->fromRadioBytes, @@ -111,6 +115,7 @@ class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks bluetoothPhoneAPI->setIntervalFromNow(0); bluetoothPhoneAPI->numBytes = 0; bluetoothPhoneAPI->hasChecked = false; + bluetoothPhoneAPI->phoneWants = false; } }; @@ -186,7 +191,12 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks new meshtastic::BluetoothStatus(meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED)); if (bluetoothPhoneAPI) { + std::lock_guard guard(bluetoothPhoneAPI->nimble_mutex); bluetoothPhoneAPI->close(); + bluetoothPhoneAPI->hasChecked = false; + bluetoothPhoneAPI->phoneWants = false; + bluetoothPhoneAPI->numBytes = 0; + bluetoothPhoneAPI->queue_size = 0; } } }; From 798b1f4d861e756ecd2324083726c098cfb7325e Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Sat, 5 Jul 2025 07:35:20 -0500 Subject: [PATCH 13/20] Add HWIDs for T1000-E in DFU mode (#7235) --- boards/tracker-t1000-e.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/boards/tracker-t1000-e.json b/boards/tracker-t1000-e.json index 2be716e22..9e8870041 100644 --- a/boards/tracker-t1000-e.json +++ b/boards/tracker-t1000-e.json @@ -11,7 +11,8 @@ ["0x239A", "0x8029"], ["0x239A", "0x0029"], ["0x239A", "0x002A"], - ["0x239A", "0x802A"] + ["0x239A", "0x802A"], + ["0x2886", "0x0057"] ], "usb_product": "T1000-E-BOOT", "mcu": "nrf52840", From 98d010761e5a8c0f5651974268adcb09ef2f6639 Mon Sep 17 00:00:00 2001 From: Aiden Fox Ivey Date: Fri, 20 Jun 2025 16:07:23 -0400 Subject: [PATCH 14/20] Add constant time compare to AES-CCM Signed-off-by: Aiden Fox Ivey --- src/mesh/aes-ccm.cpp | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/mesh/aes-ccm.cpp b/src/mesh/aes-ccm.cpp index a650ba2fc..a73c9473e 100644 --- a/src/mesh/aes-ccm.cpp +++ b/src/mesh/aes-ccm.cpp @@ -10,6 +10,31 @@ #include "aes-ccm.h" #if !MESHTASTIC_EXCLUDE_PKI +/** + * Constant-time comparison of two byte arrays + * + * @param a First byte array to compare + * @param b Second byte array to compare + * @param len Number of bytes to compare + * @return 0 if arrays are equal, 1 if different or if inputs are invalid + */ +static int constant_time_compare(const void *a_, const void *b_, size_t len) +{ + // Cast to volatile to prevent the compiler from optimizing out their comparison. + const volatile uint8_t *volatile a = (const volatile uint8_t *volatile) a_; + const volatile uint8_t *volatile b = (const volatile uint8_t *volatile) b_; + if (len == 0) + return 0; + if (a == NULL || b == NULL) + return 1; + size_t i; + volatile uint8_t d = 0U; + for (i = 0U; i < len; i++) { + d |= (a[i] ^ b[i]); + } + return (1 & ((d - 1) >> 8)) - 1; +} + static void WPA_PUT_BE16(uint8_t *a, uint16_t val) { a[0] = val >> 8; @@ -146,7 +171,7 @@ bool aes_ccm_ad(const uint8_t *key, size_t key_len, const uint8_t *nonce, size_t aes_ccm_encr(L, crypt, crypt_len, plain, a); aes_ccm_auth_start(M, L, nonce, aad, aad_len, crypt_len, x); aes_ccm_auth(plain, crypt_len, x); - if (memcmp(x, t, M) != 0) { // FIXME make const comp + if (constant_time_compare(x, t, M) != 0) { return false; } return true; From 13786572066c570d053a9669a59c1804c2ca7aa8 Mon Sep 17 00:00:00 2001 From: Aiden Fox Ivey Date: Sat, 21 Jun 2025 00:12:09 -0400 Subject: [PATCH 15/20] Fix documentation comments. --- src/mesh/aes-ccm.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/mesh/aes-ccm.cpp b/src/mesh/aes-ccm.cpp index a73c9473e..e1c65c7aa 100644 --- a/src/mesh/aes-ccm.cpp +++ b/src/mesh/aes-ccm.cpp @@ -16,22 +16,23 @@ * @param a First byte array to compare * @param b Second byte array to compare * @param len Number of bytes to compare - * @return 0 if arrays are equal, 1 if different or if inputs are invalid + * @return 0 if arrays are equal, -1 if different or if inputs are invalid */ static int constant_time_compare(const void *a_, const void *b_, size_t len) { - // Cast to volatile to prevent the compiler from optimizing out their comparison. + /* Cast to volatile to prevent the compiler from optimizing out their comparison. */ const volatile uint8_t *volatile a = (const volatile uint8_t *volatile) a_; const volatile uint8_t *volatile b = (const volatile uint8_t *volatile) b_; if (len == 0) return 0; if (a == NULL || b == NULL) - return 1; + return -1; size_t i; volatile uint8_t d = 0U; for (i = 0U; i < len; i++) { d |= (a[i] ^ b[i]); } + /* Constant time bit arithmetic to convert d > 0 to -1 and d = 0 to 0. */ return (1 & ((d - 1) >> 8)) - 1; } From 2a2620988928ceaf78f61b025529cf6d6eb13fea Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sat, 5 Jul 2025 12:56:29 -0500 Subject: [PATCH 16/20] Trunk --- src/mesh/aes-ccm.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh/aes-ccm.cpp b/src/mesh/aes-ccm.cpp index e1c65c7aa..420d80e9a 100644 --- a/src/mesh/aes-ccm.cpp +++ b/src/mesh/aes-ccm.cpp @@ -21,8 +21,8 @@ static int constant_time_compare(const void *a_, const void *b_, size_t len) { /* Cast to volatile to prevent the compiler from optimizing out their comparison. */ - const volatile uint8_t *volatile a = (const volatile uint8_t *volatile) a_; - const volatile uint8_t *volatile b = (const volatile uint8_t *volatile) b_; + const volatile uint8_t *volatile a = (const volatile uint8_t *volatile)a_; + const volatile uint8_t *volatile b = (const volatile uint8_t *volatile)b_; if (len == 0) return 0; if (a == NULL || b == NULL) From 708978911ba64c97af25751e1b4927a771a5d84d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 5 Jul 2025 19:26:20 -0500 Subject: [PATCH 17/20] chore(deps): update meshtastic/device-ui digest to 8c7092c (#7238) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 795f86eb9..5ba5e63e0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -109,7 +109,7 @@ lib_deps = [device-ui_base] lib_deps = # renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master - https://github.com/meshtastic/device-ui/archive/4b7bf369adfa5a7bd419fa8293d21206576d52d0.zip + https://github.com/meshtastic/device-ui/archive/8c7092c73425adfda1aac8c6960df06cd85f6d92.zip ; Common libs for environmental measurements in telemetry module [environmental_base] From 40c586ca97dfd10aeaeb73fc9b5d7ebf54fab60e Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Sun, 6 Jul 2025 16:36:22 -0500 Subject: [PATCH 18/20] Automatically bail user out of displaymode_color when not HAS_TFT (#7248) --- src/mesh/NodeDB.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 5630a4ea3..a20acfda0 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -369,6 +369,14 @@ NodeDB::NodeDB() config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY; } +#if !HAS_TFT + if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_COLOR) { + // On a device without MUI, this display mode makes no sense, and will break logic. + config.display.displaymode = meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT; + config.bluetooth.enabled = true; + } +#endif + if (devicestateCRC != crc32Buffer(&devicestate, sizeof(devicestate))) saveWhat |= SEGMENT_DEVICESTATE; if (nodeDatabaseCRC != crc32Buffer(&nodeDatabase, sizeof(nodeDatabase))) From 09d4ee1ea7ab6bc77b59ad943008ac831f8afdfc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 7 Jul 2025 06:37:15 -0500 Subject: [PATCH 19/20] Upgrade trunk (#7254) Co-authored-by: sachaw <11172820+sachaw@users.noreply.github.com> --- .trunk/trunk.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 2ddebdf1d..1dfff137a 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -9,14 +9,14 @@ plugins: lint: enabled: - checkov@3.2.447 - - renovate@41.17.2 + - renovate@41.19.0 - prettier@3.6.2 - trufflehog@3.89.2 - yamllint@1.37.1 - bandit@1.8.5 - - trivy@0.64.0 + - trivy@0.64.1 - taplo@0.9.3 - - ruff@0.12.1 + - ruff@0.12.2 - isort@6.0.1 - markdownlint@0.45.0 - oxipng@9.1.5 From f95c77b8bd8babd071e7cc2b36f0e3952bf4ed92 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 7 Jul 2025 15:50:13 +0300 Subject: [PATCH 20/20] Fast fix, remove saving tx power inside limitPower() (#7255) --- src/mesh/RadioInterface.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 3632378a5..faa67a1c2 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -645,10 +645,6 @@ void RadioInterface::limitPower(int8_t loraMaxPower) if (power > loraMaxPower) // Clamp power to maximum defined level power = loraMaxPower; - if (TX_GAIN_LORA == 0) { // Setting power in config with defined TX_GAIN_LORA will cause decreasing power on each reboot - config.lora.tx_power = power; // Set limited power in config - } - LOG_INFO("Final Tx power: %d dBm", power); }