diff --git a/neode-ui/src/components/mesh/MeshDeviceSetupModal.vue b/neode-ui/src/components/mesh/MeshDeviceSetupModal.vue index 895e910a..29ad023c 100644 --- a/neode-ui/src/components/mesh/MeshDeviceSetupModal.vue +++ b/neode-ui/src/components/mesh/MeshDeviceSetupModal.vue @@ -256,8 +256,11 @@
MeshCore RF params for {{ selectedRegion?.code }} are applied to the radio automatically on connect.
-- RNode radio parameters (frequency / bandwidth / SF / CR) are managed by the Reticulum daemon's interface config on this node. +
+ RNode plan for {{ form.region }}: {{ (RNODE_REGION_PLANS[form.region].frequency / 1e6).toFixed(4) }} MHz, {{ RNODE_REGION_PLANS[form.region].bandwidth / 1000 }} kHz, SF{{ RNODE_REGION_PLANS[form.region].spreading_factor }}, CR4/{{ RNODE_REGION_PLANS[form.region].coding_rate }}, {{ RNODE_REGION_PLANS[form.region].txpower }} dBm — applied on connect, and the radio confirms it. +
++ Pick a region to apply its recommended RNode RF plan on connect — editable any time in Mesh → Device settings.
@@ -302,7 +305,7 @@ import { useRouter } from 'vue-router' import BaseModal from '@/components/BaseModal.vue' import { useMeshStore, type MeshDeviceProbe, type MeshConfigureParams, type FlashFirmwareFamily, type FlashBoard, type FlashJobStatus } from '@/stores/mesh' import { useAppStore } from '@/stores/app' -import { LORA_REGIONS, regionByCode, suggestRegionFromLatLon, meshcorePlanFor } from '@/utils/loraRegions' +import { LORA_REGIONS, regionByCode, suggestRegionFromLatLon, meshcorePlanFor, RNODE_REGION_PLANS } from '@/utils/loraRegions' import { resolveMeshDeviceImage } from '@/utils/meshDeviceImages' const mesh = useMeshStore() @@ -501,6 +504,18 @@ async function applySetup() { } } await mesh.configure(params) + // RNode radios: the region's recommended RF plan is applied through the + // Reticulum daemon's persisted settings (mesh.rnode-config-apply), the + // same round-trip the Device panel uses — the radio confirms the values + // itself after the daemon restarts with them. Best-effort here: a + // failure must not abort the connect the user just asked for. + if (effectiveKind.value === 'reticulum' && form.value.region) { + const plan = RNODE_REGION_PLANS[form.value.region] + if (plan) { + mesh.suppressDeviceDetect() + void mesh.applyRnodeConfig({ enabled: true, port: null, ...plan }).catch(() => {}) + } + } mesh.dismissDetectedDevice(path) void router.push('/dashboard/mesh') } catch (e) { diff --git a/neode-ui/src/utils/loraRegions.ts b/neode-ui/src/utils/loraRegions.ts index f9415388..1ad20d7e 100644 --- a/neode-ui/src/utils/loraRegions.ts +++ b/neode-ui/src/utils/loraRegions.ts @@ -126,3 +126,27 @@ export const MESHCORE_RF_PRESETS: MeshcoreRfPreset[] = [ { id: 'us_anz_915', label: 'US / Canada / ANZ — 915.0 MHz, 250 kHz, SF10, CR4/5', freqMhz: 915.0, bwKhz: 250, sf: 10, cr: 5 }, { id: 'eu_433', label: 'Europe (433 MHz) — 433.65 MHz, 250 kHz, SF11, CR4/5', freqMhz: 433.65, bwKhz: 250, sf: 11, cr: 5 }, ] + +/** Recommended Reticulum RNode RF plan per region. Applied via + * mesh.rnode-config-apply (the daemon restarts the radio with these and the + * radio confirms them back). EU868 is the operator-validated Portugal plan: + * 869.4625 MHz sits in the 10%-duty 869.4–869.65 sub-band clear of the + * default community channel, with the EU airtime locks written in. Others + * follow RNS community conventions with the region's legal power cap. */ +export interface RnodeRegionPlan { + frequency: number + bandwidth: number + spreading_factor: number + coding_rate: number + txpower: number + airtime_limit_short: number | null + airtime_limit_long: number | null +} +export const RNODE_REGION_PLANS: Record