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 diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini index cba84181b..6b9ebcb24 100644 --- a/arch/esp32/esp32.ini +++ b/arch/esp32/esp32.ini @@ -49,13 +49,13 @@ 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 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 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 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/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", 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/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] 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/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/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index 92954bf2e..43c226896 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)) { @@ -602,32 +604,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; } @@ -833,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) @@ -896,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 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}; 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()) { 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))) diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 4db05b4d4..faa67a1c2 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. */ @@ -637,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); } @@ -681,4 +685,4 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p) sendingPacket = p; return p->encrypted.size + sizeof(PacketHeader); -} \ No newline at end of file +} diff --git a/src/mesh/aes-ccm.cpp b/src/mesh/aes-ccm.cpp index a650ba2fc..420d80e9a 100644 --- a/src/mesh/aes-ccm.cpp +++ b/src/mesh/aes-ccm.cpp @@ -10,6 +10,32 @@ #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]); + } + /* Constant time bit arithmetic to convert d > 0 to -1 and d = 0 to 0. */ + return (1 & ((d - 1) >> 8)) - 1; +} + static void WPA_PUT_BE16(uint8_t *a, uint16_t val) { a[0] = val >> 8; @@ -146,7 +172,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; 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; } 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; } } }; 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