Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/DisplayFormatters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaC
bool usePreset)
{

// If use_preset is false, always return "Custom"
// If use_preset is false, always return "Custom" — callers such as RadioInterface and Channels
// rely on this being a stable literal for channel-name hashing and default-channel detection.
if (!usePreset) {
return "Custom";
}
Expand Down
13 changes: 11 additions & 2 deletions src/graphics/draw/DebugRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -408,15 +408,24 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
display->drawString(nameX, getTextPositions(display)[line++], device_role);

// === Third Row: Radio Preset ===
auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false, config.lora.use_preset);
// For custom modem settings show the actual parameters; for presets use the preset name.
char modeStr[16];
if (!config.lora.use_preset) {
snprintf(modeStr, sizeof(modeStr), "BW%u-SF%u-CR%u", static_cast<unsigned>(config.lora.bandwidth),
static_cast<unsigned>(config.lora.spread_factor), static_cast<unsigned>(config.lora.coding_rate));
} else {
strncpy(modeStr, DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false, true),
sizeof(modeStr) - 1);
modeStr[sizeof(modeStr) - 1] = '\0';
}

char regionradiopreset[25];
const char *region = myRegion ? myRegion->name : NULL;
if (region != nullptr) {
if (currentResolution == ScreenResolution::UltraLow) {
snprintf(regionradiopreset, sizeof(regionradiopreset), "%s", region);
} else {
snprintf(regionradiopreset, sizeof(regionradiopreset), "%s/%s", region, mode);
snprintf(regionradiopreset, sizeof(regionradiopreset), "%s/%s", region, modeStr);
}
}
textWidth = display->getStringWidth(regionradiopreset);
Expand Down
12 changes: 9 additions & 3 deletions src/graphics/draw/MenuHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "main.h"
#include "mesh/Default.h"
#include "mesh/MeshTypes.h"
#include "mesh/RadioLibInterface.h"
#include "modules/AdminModule.h"
#include "modules/CannedMessageModule.h"
#include "modules/ExternalNotificationModule.h"
Expand Down Expand Up @@ -160,6 +161,14 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
return;
}

// Guard: without a reboot, reconfigure() applies the region directly.
// Reject LORA_24 on sub-GHz-only hardware — getRadio() used to catch this post-reboot.
if (selectedRegion == meshtastic_Config_LoRaConfig_RegionCode_LORA_24 &&
!(RadioLibInterface::instance && RadioLibInterface::instance->wideLora())) {
LOG_WARN("Radio hardware does not support 2.4 GHz; ignoring LORA_24 selection");
return;
}

config.lora.region = selectedRegion;
auto changes = SEGMENT_CONFIG;

Expand All @@ -181,7 +190,6 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
}

service->reloadConfig(changes);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
});
Comment thread
Elwimen marked this conversation as resolved.
Resolved

bannerOptions.durationMs = duration;
Expand Down Expand Up @@ -300,7 +308,6 @@ void menuHandler::FrequencySlotPicker()

config.lora.channel_num = selected;
service->reloadConfig(SEGMENT_CONFIG);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
};

screen->showOverlayBanner(bannerOptions);
Expand Down Expand Up @@ -339,7 +346,6 @@ void menuHandler::radioPresetPicker()
config.lora.channel_num = 0; // Reset to default channel for the preset
config.lora.override_frequency = 0; // Clear any custom frequency
service->reloadConfig(SEGMENT_CONFIG);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
});

screen->showOverlayBanner(bannerOptions);
Expand Down
5 changes: 5 additions & 0 deletions src/mesh/LR11x0Interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ template <typename T> bool LR11x0Interface<T>::reconfigure()
err = lora.setOutputPower(power);
assert(err == RADIOLIB_ERR_NONE);

// Apply RX gain mode — valid in STDBY, matches resetAGC() pattern
err = lora.setRxBoostedGainMode(config.lora.sx126x_rx_boosted_gain);
if (err != RADIOLIB_ERR_NONE)
LOG_WARN("LR11x0 setRxBoostedGainMode %s%d", radioLibErr, err);

startReceive(); // restart receiving

return RADIOLIB_ERR_NONE;
Comment thread
Elwimen marked this conversation as resolved.
Resolved
Expand Down
5 changes: 5 additions & 0 deletions src/mesh/SX126xInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,11 @@ template <typename T> bool SX126xInterface<T>::reconfigure()
LOG_ERROR("SX126X setOutputPower %s%d", radioLibErr, err);
assert(err == RADIOLIB_ERR_NONE);

// Apply RX gain mode — valid in STDBY (datasheet §9.6), matches resetAGC() pattern
err = lora.setRxBoostedGainMode(config.lora.sx126x_rx_boosted_gain);
if (err != RADIOLIB_ERR_NONE)
LOG_WARN("SX126X setRxBoostedGainMode %s%d", radioLibErr, err);

startReceive(); // restart receiving

return RADIOLIB_ERR_NONE;
Comment thread
Elwimen marked this conversation as resolved.
Resolved
Expand Down
44 changes: 31 additions & 13 deletions src/modules/AdminModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "MeshRadio.h"
#include "RadioInterface.h"
#include "TypeConversions.h"
#include "mesh/RadioLibInterface.h"

#if !MESHTASTIC_EXCLUDE_MQTT
#include "mqtt/MQTT.h"
Expand Down Expand Up @@ -198,10 +199,35 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
handleSetOwner(r->set_owner);
break;

case meshtastic_AdminMessage_set_config_tag:
case meshtastic_AdminMessage_set_config_tag: {
LOG_DEBUG("Client set config");
handleSetConfig(r->set_config, fromOthers);

// Non-LoRa configs need no further validation.
if (r->set_config.which_payload_variant != meshtastic_Config_lora_tag) {
LOG_DEBUG("Non-LoRa config, applying directly");
handleSetConfig(r->set_config, fromOthers);
break;
}

// Only LORA_24 requires hardware capability validation.
if (r->set_config.payload_variant.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24) {
Comment thread
Elwimen marked this conversation as resolved.
Resolved
LOG_DEBUG("LoRa config, region is not LORA_24, applying directly");
handleSetConfig(r->set_config, fromOthers);
break;
}

// Hardware supports 2.4 GHz — apply the config.
// Fail closed: null instance is treated as incapable.
if (RadioLibInterface::instance && RadioLibInterface::instance->wideLora()) {
Comment thread
Elwimen marked this conversation as resolved.
Resolved
LOG_DEBUG("LORA_24 requested, radio hardware supports 2.4 GHz, applying");
handleSetConfig(r->set_config, fromOthers);
break;
}

LOG_WARN("Radio hardware does not support 2.4 GHz; rejecting LORA_24 region");
myReply = allocErrorResponse(meshtastic_Routing_Error_BAD_REQUEST, &mp);
break;
}

case meshtastic_AdminMessage_set_module_config_tag:
LOG_DEBUG("Client set module config");
Expand Down Expand Up @@ -816,17 +842,9 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c, bool fromOthers)
// use_preset and bandwidth are coerced into valid values by the check.
}

// If no lora radio parameters change, don't need to reboot
if (oldLoraConfig.use_preset == validatedLora.use_preset && oldLoraConfig.region == validatedLora.region &&
oldLoraConfig.modem_preset == validatedLora.modem_preset && oldLoraConfig.bandwidth == validatedLora.bandwidth &&
oldLoraConfig.spread_factor == validatedLora.spread_factor &&
oldLoraConfig.coding_rate == validatedLora.coding_rate && oldLoraConfig.tx_power == validatedLora.tx_power &&
oldLoraConfig.frequency_offset == validatedLora.frequency_offset &&
oldLoraConfig.override_frequency == validatedLora.override_frequency &&
oldLoraConfig.channel_num == validatedLora.channel_num &&
oldLoraConfig.sx126x_rx_boosted_gain == validatedLora.sx126x_rx_boosted_gain) {
requiresReboot = false;
}
// All LoRa radio changes apply live via configChanged observer → reconfigure().
// reconfigure() puts the radio in standby, reprograms all modem parameters, and restarts receive.
requiresReboot = false;

#if defined(ARCH_PORTDUINO)
// If running on portduino and using SimRadio, do not require reboot
Expand Down
Loading