diff --git a/protobufs b/protobufs index 1cf2783bd..9beb80f1d 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 1cf2783bdb0735590ccf75d9bc825e233e20032a +Subproject commit 9beb80f1d302f70d05f9c4bc9dd543b8f7bc8796 diff --git a/src/DisplayFormatters.cpp b/src/DisplayFormatters.cpp index 246cf0022..d88f9fc9f 100644 --- a/src/DisplayFormatters.cpp +++ b/src/DisplayFormatters.cpp @@ -31,6 +31,9 @@ const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaC case meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST: return useShortName ? "LongF" : "LongFast"; break; + case meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO: + return useShortName ? "LongT" : "LongTurbo"; + break; case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE: return useShortName ? "LongM" : "LongMod"; break; diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index 2a7f479b4..586bdd4a6 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -20,12 +20,41 @@ #include "modules/KeyVerificationModule.h" #include "modules/TraceRouteModule.h" +#include +#include #include +#include extern uint16_t TFT_MESH; namespace graphics { + +namespace +{ + +// Caller must ensure the provided options array outlives the banner callback. +template +BannerOverlayOptions createStaticBannerOptions(const char *message, const MenuOption (&options)[N], + std::array &labels, Callback &&onSelection) +{ + for (size_t i = 0; i < N; ++i) { + labels[i] = options[i].label; + } + + const MenuOption *optionsPtr = options; + auto callback = std::function &, int)>(std::forward(onSelection)); + + BannerOverlayOptions bannerOptions; + bannerOptions.message = message; + bannerOptions.optionsArrayPtr = labels.data(); + bannerOptions.optionsCount = static_cast(N); + bannerOptions.bannerCallback = [optionsPtr, callback](int selected) -> void { callback(optionsPtr[selected], selected); }; + return bannerOptions; +} + +} // namespace + menuHandler::screenMenus menuHandler::menuQueue = menu_none; bool test_enabled = false; uint8_t test_count = 0; @@ -197,48 +226,38 @@ void menuHandler::DeviceRolePicker() void menuHandler::RadioPresetPicker() { - static const char *optionsArray[] = {"Back", "LongSlow", "LongModerate", "LongFast", "MediumSlow", - "MediumFast", "ShortSlow", "ShortFast", "ShortTurbo"}; - enum optionsNumbers { - Back = 0, - radiopreset_LongSlow = 1, - radiopreset_LongModerate = 2, - radiopreset_LongFast = 3, - radiopreset_MediumSlow = 4, - radiopreset_MediumFast = 5, - radiopreset_ShortSlow = 6, - radiopreset_ShortFast = 7, - radiopreset_ShortTurbo = 8 - }; - BannerOverlayOptions bannerOptions; - bannerOptions.message = "Radio Preset"; - bannerOptions.optionsArrayPtr = optionsArray; - bannerOptions.optionsCount = 9; - bannerOptions.bannerCallback = [](int selected) -> void { - if (selected == Back) { - menuHandler::menuQueue = menuHandler::lora_Menu; - screen->runNow(); - return; - } else if (selected == radiopreset_LongSlow) { - config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW; - } else if (selected == radiopreset_LongModerate) { - config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE; - } else if (selected == radiopreset_LongFast) { - config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST; - } else if (selected == radiopreset_MediumSlow) { - config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW; - } else if (selected == radiopreset_MediumFast) { - config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST; - } else if (selected == radiopreset_ShortSlow) { - config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW; - } else if (selected == radiopreset_ShortFast) { - config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST; - } else if (selected == radiopreset_ShortTurbo) { - config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO; - } - service->reloadConfig(SEGMENT_CONFIG); - rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000); + static const RadioPresetOption presetOptions[] = { + {"Back", OptionsAction::Back}, + {"LongTurbo", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO}, + {"LongModerate", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE}, + {"LongFast", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST}, + {"MediumSlow", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW}, + {"MediumFast", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST}, + {"ShortSlow", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW}, + {"ShortFast", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST}, + {"ShortTurbo", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO}, }; + + constexpr size_t presetCount = sizeof(presetOptions) / sizeof(presetOptions[0]); + static std::array presetLabels{}; + + auto bannerOptions = + createStaticBannerOptions("Radio Preset", presetOptions, presetLabels, [](const RadioPresetOption &option, int) -> void { + if (option.action == OptionsAction::Back) { + menuHandler::menuQueue = menuHandler::lora_Menu; + screen->runNow(); + return; + } + + if (!option.hasValue) { + return; + } + + config.lora.modem_preset = option.value; + service->reloadConfig(SEGMENT_CONFIG); + rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000); + }); + screen->showOverlayBanner(bannerOptions); } diff --git a/src/graphics/draw/MenuHandler.h b/src/graphics/draw/MenuHandler.h index a611b7c9d..df7c2739b 100644 --- a/src/graphics/draw/MenuHandler.h +++ b/src/graphics/draw/MenuHandler.h @@ -99,5 +99,24 @@ class menuHandler static void BluetoothToggleMenu(); }; +/* Generic Menu Options designations */ +enum class OptionsAction { Back, Select }; + +template struct MenuOption { + const char *label; + OptionsAction action; + bool hasValue; + T value; + + MenuOption(const char *labelIn, OptionsAction actionIn, T valueIn) + : label(labelIn), action(actionIn), hasValue(true), value(valueIn) + { + } + + MenuOption(const char *labelIn, OptionsAction actionIn) : label(labelIn), action(actionIn), hasValue(false), value() {} +}; + +using RadioPresetOption = MenuOption; + } // namespace graphics #endif \ No newline at end of file diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 3c0da4494..db8677821 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -503,6 +503,11 @@ void RadioInterface::applyModemConfig() cr = 5; sf = 10; break; + case meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO: + bw = (myRegion->wideLora) ? 1625.0 : 500; + cr = 8; + sf = 11; + break; default: // Config_LoRaConfig_ModemPreset_LONG_FAST is default. Gracefully use this is preset is something illegal. bw = (myRegion->wideLora) ? 812.5 : 250; cr = 5; @@ -539,13 +544,26 @@ void RadioInterface::applyModemConfig() } if ((myRegion->freqEnd - myRegion->freqStart) < bw / 1000) { - static const char *err_string = "Regional frequency range is smaller than bandwidth. Fall back to default preset"; - LOG_ERROR(err_string); + const float regionSpanKHz = (myRegion->freqEnd - myRegion->freqStart) * 1000.0f; + const float requestedBwKHz = bw; + const bool isWideRequest = requestedBwKHz >= 499.5f; // treat as 500 kHz preset + const char *presetName = + DisplayFormatters::getModemPresetDisplayName(loraConfig.modem_preset, false, loraConfig.use_preset); + + char err_string[160]; + if (isWideRequest) { + snprintf(err_string, sizeof(err_string), "%s region too narrow for 500kHz preset (%s). Falling back to LongFast.", + myRegion->name, presetName); + } else { + snprintf(err_string, sizeof(err_string), "%s region span %.0fkHz < requested %.0fkHz. Falling back to LongFast.", + myRegion->name, regionSpanKHz, requestedBwKHz); + } + LOG_ERROR("%s", err_string); RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed(); cn->level = meshtastic_LogRecord_Level_ERROR; - sprintf(cn->message, err_string); + snprintf(cn->message, sizeof(cn->message), "%s", err_string); service->sendClientNotification(cn); // Set to default modem preset diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h index 57b855d98..d4ef5bee4 100644 --- a/src/mesh/generated/meshtastic/config.pb.h +++ b/src/mesh/generated/meshtastic/config.pb.h @@ -293,7 +293,8 @@ typedef enum _meshtastic_Config_LoRaConfig_RegionCode { typedef enum _meshtastic_Config_LoRaConfig_ModemPreset { /* Long Range - Fast */ meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST = 0, - /* Long Range - Slow */ + /* Long Range - Slow + Deprecated in 2.7: Unpopular slow preset. */ meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW = 1, /* Very Long Range - Slow Deprecated in 2.5: Works only with txco and is unusably slow */