diff --git a/src/graphics/draw/DebugRenderer.cpp b/src/graphics/draw/DebugRenderer.cpp index 75b65c65f..2dca38d66 100644 --- a/src/graphics/draw/DebugRenderer.cpp +++ b/src/graphics/draw/DebugRenderer.cpp @@ -438,7 +438,7 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, if (currentResolution == ScreenResolution::UltraLow) { snprintf(frequencyslot, sizeof(frequencyslot), "%sMHz (%d)", freqStr, config.lora.channel_num); } else { - snprintf(frequencyslot, sizeof(frequencyslot), "Freq/Ch: %sMHz (%d)", freqStr, config.lora.channel_num); + snprintf(frequencyslot, sizeof(frequencyslot), "Freq: %sMHz (%d)", freqStr, config.lora.channel_num); } } size_t len = strlen(frequencyslot); diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index 5c459d984..c5a4106e7 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -65,12 +65,12 @@ uint8_t test_count = 0; void menuHandler::loraMenu() { - static const char *optionsArray[] = {"Back", "Device Role", "Radio Preset", "LoRa Region"}; - enum optionsNumbers { Back = 0, device_role_picker = 1, radio_preset_picker = 2, lora_picker = 3 }; + static const char *optionsArray[] = {"Back", "Device Role", "Radio Preset", "Frequency Slot", "LoRa Region"}; + enum optionsNumbers { Back = 0, device_role_picker = 1, radio_preset_picker = 2, frequency_slot = 3, lora_picker = 4 }; BannerOverlayOptions bannerOptions; bannerOptions.message = "LoRa Actions"; bannerOptions.optionsArrayPtr = optionsArray; - bannerOptions.optionsCount = 4; + bannerOptions.optionsCount = 5; bannerOptions.bannerCallback = [](int selected) -> void { if (selected == Back) { // No action @@ -78,6 +78,8 @@ void menuHandler::loraMenu() menuHandler::menuQueue = menuHandler::device_role_picker; } else if (selected == radio_preset_picker) { menuHandler::menuQueue = menuHandler::radio_preset_picker; + } else if (selected == frequency_slot) { + menuHandler::menuQueue = menuHandler::frequency_slot; } else if (selected == lora_picker) { menuHandler::menuQueue = menuHandler::lora_picker; } @@ -248,6 +250,113 @@ void menuHandler::DeviceRolePicker() screen->showOverlayBanner(bannerOptions); } +void menuHandler::FrequencySlotPicker() +{ + + enum ReplyOptions : int { Back = -1 }; + constexpr int MAX_CHANNEL_OPTIONS = 202; + static const char *optionsArray[MAX_CHANNEL_OPTIONS]; + static int optionsEnumArray[MAX_CHANNEL_OPTIONS]; + static char channelText[MAX_CHANNEL_OPTIONS - 1][12]; + int options = 0; + optionsArray[options] = "Back"; + optionsEnumArray[options++] = Back; + optionsArray[options] = "Slot 0 (Auto)"; + optionsEnumArray[options++] = 0; + + // Calculate number of channels (copied from RadioInterface::applyModemConfig()) + meshtastic_Config_LoRaConfig &loraConfig = config.lora; + double bw = loraConfig.bandwidth; + if (loraConfig.use_preset) { + switch (loraConfig.modem_preset) { + case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO: + bw = (myRegion->wideLora) ? 1625.0 : 500; + break; + case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST: + bw = (myRegion->wideLora) ? 812.5 : 250; + break; + case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW: + bw = (myRegion->wideLora) ? 812.5 : 250; + break; + case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST: + bw = (myRegion->wideLora) ? 812.5 : 250; + break; + case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW: + bw = (myRegion->wideLora) ? 812.5 : 250; + break; + case meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO: + bw = (myRegion->wideLora) ? 1625.0 : 500; + break; + default: + bw = (myRegion->wideLora) ? 812.5 : 250; + break; + case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE: + bw = (myRegion->wideLora) ? 406.25 : 125; + break; + case meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW: + bw = (myRegion->wideLora) ? 406.25 : 125; + break; + } + } else { + bw = loraConfig.bandwidth; + if (bw == 31) // This parameter is not an integer + bw = 31.25; + if (bw == 62) // Fix for 62.5Khz bandwidth + bw = 62.5; + if (bw == 200) + bw = 203.125; + if (bw == 400) + bw = 406.25; + if (bw == 800) + bw = 812.5; + if (bw == 1600) + bw = 1625.0; + } + + uint32_t numChannels = 0; + if (myRegion) { + numChannels = (uint32_t)floor((myRegion->freqEnd - myRegion->freqStart) / (myRegion->spacing + (bw / 1000.0))); + } else { + LOG_WARN("Region not set, cannot calculate number of channels"); + return; + } + + if (numChannels > (uint32_t)(MAX_CHANNEL_OPTIONS - 2)) + numChannels = (uint32_t)(MAX_CHANNEL_OPTIONS - 2); + + for (uint32_t ch = 1; ch <= numChannels; ch++) { + snprintf(channelText[ch - 1], sizeof(channelText[ch - 1]), "Slot %lu", (unsigned long)ch); + optionsArray[options] = channelText[ch - 1]; + optionsEnumArray[options++] = (int)ch; + } + + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Frequency Slot"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsEnumPtr = optionsEnumArray; + bannerOptions.optionsCount = options; + + // Start highlight on current channel if possible, otherwise on "1" + int initial = (int)config.lora.channel_num + 1; + if (initial < 2 || initial > (int)numChannels + 1) + initial = 1; + bannerOptions.InitialSelected = initial; + + bannerOptions.bannerCallback = [](int selected) -> void { + if (selected == Back) { + menuHandler::menuQueue = menuHandler::lora_Menu; + screen->runNow(); + return; + } + + config.lora.channel_num = selected; + service->reloadConfig(SEGMENT_CONFIG); + rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000); + }; + + screen->showOverlayBanner(bannerOptions); +} + void menuHandler::RadioPresetPicker() { static const RadioPresetOption presetOptions[] = { @@ -278,6 +387,8 @@ void menuHandler::RadioPresetPicker() } config.lora.modem_preset = option.value; + 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); }); @@ -2551,6 +2662,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display) case radio_preset_picker: RadioPresetPicker(); break; + case frequency_slot: + FrequencySlotPicker(); + break; case no_timeout_lora_picker: LoraRegionPicker(0); break; diff --git a/src/graphics/draw/MenuHandler.h b/src/graphics/draw/MenuHandler.h index 121b6dfc9..45fd0bf5f 100644 --- a/src/graphics/draw/MenuHandler.h +++ b/src/graphics/draw/MenuHandler.h @@ -13,6 +13,7 @@ class menuHandler lora_picker, device_role_picker, radio_preset_picker, + frequency_slot, no_timeout_lora_picker, TZ_picker, twelve_hour_picker, @@ -63,6 +64,7 @@ class menuHandler static void loraMenu(); static void DeviceRolePicker(); static void RadioPresetPicker(); + static void FrequencySlotPicker(); static void handleMenuSwitch(OLEDDisplay *display); static void showConfirmationBanner(const char *message, std::function onConfirm); static void clockMenu(); diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp index 7b7ebb595..ea8d6af8e 100644 --- a/src/mesh/http/ContentHandler.cpp +++ b/src/mesh/http/ContentHandler.cpp @@ -173,7 +173,7 @@ void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res) if (req->getMethod() == "OPTIONS") { res->setStatusCode(204); // Success with no content - // res->print(""); @todo remove + res->print(""); return; } @@ -223,7 +223,7 @@ void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res) if (req->getMethod() == "OPTIONS") { res->setStatusCode(204); // Success with no content - // res->print(""); @todo remove + res->print(""); return; } diff --git a/src/modules/ExternalNotificationModule.cpp b/src/modules/ExternalNotificationModule.cpp index 04fcd8e73..8b7ce700a 100644 --- a/src/modules/ExternalNotificationModule.cpp +++ b/src/modules/ExternalNotificationModule.cpp @@ -460,12 +460,15 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP } meshtastic_NodeInfoLite *sender = nodeDB->getMeshNode(mp.from); - bool mutedNode = false; - if (sender) { - mutedNode = (sender->bitfield & NODEINFO_BITFIELD_IS_MUTED_MASK); - } meshtastic_Channel ch = channels.getByIndex(mp.channel ? mp.channel : channels.getPrimaryIndex()); + // If we receive a broadcast message, apply channel mute setting + // If we receive a direct message and the receipent is us, apply DM mute setting + // Else we just handle it as not muted. + const bool directToUs = !isBroadcast(mp.to) && isToUs(&mp); + bool is_muted = directToUs ? (sender && ((sender->bitfield & NODEINFO_BITFIELD_IS_MUTED_MASK) != 0)) + : (ch.settings.has_module_settings && ch.settings.module_settings.is_muted); + if (moduleConfig.external_notification.alert_bell) { if (containsBell) { LOG_INFO("externalNotificationModule - Notification Bell"); @@ -516,8 +519,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP } } - if (moduleConfig.external_notification.alert_message && !mutedNode && - (!ch.settings.has_module_settings || !ch.settings.module_settings.is_muted)) { + if (moduleConfig.external_notification.alert_message && !is_muted) { LOG_INFO("externalNotificationModule - Notification Module"); isNagging = true; setExternalState(0, true); @@ -528,8 +530,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP } } - if (moduleConfig.external_notification.alert_message_vibra && !mutedNode && - (!ch.settings.has_module_settings || !ch.settings.module_settings.is_muted)) { + if (moduleConfig.external_notification.alert_message_vibra && !is_muted) { LOG_INFO("externalNotificationModule - Notification Module (Vibra)"); isNagging = true; setExternalState(1, true); @@ -540,8 +541,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP } } - if (moduleConfig.external_notification.alert_message_buzzer && !mutedNode && - (!ch.settings.has_module_settings || !ch.settings.module_settings.is_muted)) { + if (moduleConfig.external_notification.alert_message_buzzer && !is_muted) { LOG_INFO("externalNotificationModule - Notification Module (Buzzer)"); if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY || (!isBroadcast(mp.to) && isToUs(&mp))) { diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp index 7430c2eae..ec9bbedca 100644 --- a/src/platform/portduino/PortduinoGlue.cpp +++ b/src/platform/portduino/PortduinoGlue.cpp @@ -55,6 +55,7 @@ void cpuDeepSleep(uint32_t msecs) void updateBatteryLevel(uint8_t level) NOT_IMPLEMENTED("updateBatteryLevel"); int TCPPort = SERVER_API_DEFAULT_PORT; +bool checkConfigPort = true; static error_t parse_opt(int key, char *arg, struct argp_state *state) { @@ -63,6 +64,7 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) if (sscanf(arg, "%d", &TCPPort) < 1) return ARGP_ERR_UNKNOWN; else + checkConfigPort = false; printf("Using config file %d\n", TCPPort); break; case 'c': @@ -870,6 +872,14 @@ bool loadConfig(const char *configPath) std::cout << "Cannot set both MACAddress and MACAddressSource!" << std::endl; exit(EXIT_FAILURE); } + if (checkConfigPort) { + portduino_config.api_port = (yamlConfig["General"]["APIPort"]).as(-1); + if (portduino_config.api_port != -1 && + portduino_config.api_port > 1023 && + portduino_config.api_port < 65536) { + TCPPort = (portduino_config.api_port); + } + } portduino_config.mac_address = (yamlConfig["General"]["MACAddress"]).as(""); if (portduino_config.mac_address != "") { portduino_config.mac_address_explicit = true; diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index 8992f5f1a..3a6887421 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -175,6 +175,7 @@ extern struct portduino_config_struct { std::string mac_address = ""; bool mac_address_explicit = false; std::string mac_address_source = ""; + int api_port = -1; std::string config_directory = ""; std::string available_directory = "/etc/meshtasticd/available.d/"; int maxtophone = 100; @@ -508,6 +509,8 @@ extern struct portduino_config_struct { out << YAML::Key << "General" << YAML::Value << YAML::BeginMap; if (config_directory != "") out << YAML::Key << "ConfigDirectory" << YAML::Value << config_directory; + if (api_port != -1) + out << YAML::Key << "TCPPort" << YAML::Value << api_port; if (mac_address_explicit) out << YAML::Key << "MACAddress" << YAML::Value << mac_address; if (mac_address_source != "") @@ -519,4 +522,4 @@ extern struct portduino_config_struct { out << YAML::EndMap; // General return out.c_str(); } -} portduino_config; \ No newline at end of file +} portduino_config;