From afe7b8a0caf972e6ba36edc87af40ce81e6e1ac5 Mon Sep 17 00:00:00 2001 From: Jakub <106778416+MarekZegare4@users.noreply.github.com> Date: Sat, 20 Jun 2026 01:04:35 +0200 Subject: [PATCH] feat(repeater): default network profile, band-matched to companion freq Enabling the repeater now defaults to a dedicated radio profile rather than relaying on the companion's current frequency: same-network repeating isn't the MeshCore community norm. The default profile is seeded with a frequency in the same legal band as the companion's own network (433/868/915 MHz, via defaultRepeaterFreqForBand()) rather than a flat firmware constant, so it can't land outside what's allowed for the operator's region. Also fixes a gap where a true first boot (no prefs file yet) never hit the DataStore.cpp migration fallback and stayed on repeater_use_profile=0. Co-Authored-By: Claude Sonnet 4.6 --- FEATURES.md | 20 ++++++++-- .../tools_screen/tools_screen.md | 4 +- examples/companion_radio/DataStore.cpp | 17 ++++++--- examples/companion_radio/MyMesh.cpp | 8 ++++ examples/companion_radio/MyMesh.h | 13 +------ examples/companion_radio/NodePrefs.h | 37 ++++++++++++++++++- .../companion_radio/ui-new/RepeaterScreen.h | 8 +++- 7 files changed, 81 insertions(+), 26 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index d7138681..1f4d86c0 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -447,8 +447,10 @@ On-device repeater for the Solo companion, scoped to the SX1262 boards (Wio Tracker L1 OLED/e-ink, GAT562 30S). All on `feature/companion-repeater-presets`. - **Repeater toggle** (`client_repeat`, Tools › Repeater) — the companion relays - flood/direct traffic on its **current** frequency (no dedicated band), still - working as a normal companion. `MyMesh::allowPacketForward` gates it; loop + flood/direct traffic, still working as a normal companion. By default it + switches to a dedicated band on enable (see profile note below) rather than + relaying on whatever network it's chatting on. `MyMesh::allowPacketForward` + gates it; loop detection (`isRepeatLooped`, ported from `simple_repeater`) and an advert flood-depth cap are always applied. Packet pool bumped 16→32 to match the repeater workload (a too-small pool starved channel/DM reception once relaying @@ -464,8 +466,18 @@ Tracker L1 OLED/e-ink, GAT562 30S). All on `feature/companion-repeater-presets`. equal to the companion = "same network", a different one = drop onto a separate repeater network. `MyMesh::applyRepeaterRadio()` is the single decision point, called at boot and on every toggle/edit; `repeaterProfileValid()` gates it. - Schema sentinel `0xC0DE0010`, profile cleared to off if load finds an invalid - config (stray bytes from older files). + Schema sentinel `0xC0DE0010`. **Custom is the default**: a never-configured + device (or one upgrading from a pre-`0x10` file, which has no saved profile) + turns the profile on and seeds `repeater_sf/bw/cr` from `LORA_SF/BW/CR` plus + a band-matched `repeater_freq` — relaying on the same network the operator is + chatting on isn't the MeshCore community norm, so "Current" stays opt-in. + `defaultRepeaterFreqForBand()` (`NodePrefs.h`) buckets the companion's own + `freq` into whichever of the three license-exempt bands MeshCore's app-driven + repeat toggle historically restricted to (433.000 / 869.495 / 918.000 MHz — + see `repeat_freq_ranges` in `MyMesh.cpp`), so the seeded default can't land + outside what's legal for wherever the companion's own network already is. + `LORA_FREQ/BW/SF/CR` fallback `#define`s moved from `MyMesh.h` to + `NodePrefs.h` so `DataStore.cpp`'s migration code can see them too. - **Politeness knobs** (all opt-in, default off, flood-only — a direct route's named next hop is never dropped). Shown only while the repeater is on: - **Skip advert** — don't re-flood adverts (highest-volume flood). diff --git a/docs/solo_features/tools_screen/tools_screen.md b/docs/solo_features/tools_screen/tools_screen.md index 2cbf39bb..1642c52d 100644 --- a/docs/solo_features/tools_screen/tools_screen.md +++ b/docs/solo_features/tools_screen/tools_screen.md @@ -319,14 +319,14 @@ The counters make the repeater behaviour observable: **Forwarded** confirms the ## Repeater -Turns the companion into a packet **repeater** while it keeps working as a normal companion — no separate firmware. By default it relays on its **current** frequency, or you can give it a dedicated radio profile to switch to while relaying (see **Network** below). Loop-detection and an advert flood-depth cap are always applied. This screen keeps the toggle, the network/profile, and its flood-filter options together; live forwarding stats are on **Tools › Diagnostics**. +Turns the companion into a packet **repeater** while it keeps working as a normal companion — no separate firmware. By default, enabling it switches the radio to a dedicated repeater profile rather than relaying on whatever network you're chatting on (see **Network** below) — that matches the MeshCore community norm of repeaters sitting on a standard channel, not a private one. Loop-detection and an advert flood-depth cap are always applied. This screen keeps the toggle, the network/profile, and its flood-filter options together; live forwarding stats are on **Tools › Diagnostics**. Navigate with **UP/DOWN**; change a value with **LEFT/RIGHT** (or **Enter** for toggles). **Cancel/Back** saves and returns to Tools. | Setting | Options | Notes | | -------------- | --------------- | -------------------------------------------------------------------------------------------------------------- | | Repeater | ON / OFF | Master switch. The options below appear only while it is ON. | -| Network | Current / Custom | **Current**: relay on the companion's own frequency (default). **Custom**: enabling the repeater switches the radio to a dedicated profile (below) and disabling restores the companion's settings — so you can drop onto a separate repeater network and come back. Switching to Custom seeds the profile from your current settings. | +| Network | Current / Custom | **Custom** _(default)_: enabling the repeater switches the radio to a dedicated profile (below) and disabling restores the companion's settings — so you can drop onto a separate repeater network and come back. A never-configured device seeds Custom with a frequency in the same band as your own network (433/868/915 MHz region), not a flat one-size-fits-all default — so it can't land outside what's legal for your region. Switching to Custom afterwards (if it was OFF and unconfigured) seeds it from your current settings instead. **Current**: relay on the companion's own frequency — opt-in; not the community norm. | | Rpt preset | named presets | _(Custom only)_ **Enter** picks a community/saved preset for the repeater profile. | | Rpt freq | chip range | _(Custom only)_ **Enter** opens the digit-by-digit editor (chip-validated bounds). | | Rpt SF / BW / CR | 5–12 / 7.8–500 kHz / 5–8 | _(Custom only)_ **LEFT/RIGHT** to adjust the profile's spreading factor, bandwidth, coding rate. | diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index b749ee5c..e78636ca 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -356,16 +356,23 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no rd(&_prefs.repeater_bw, sizeof(_prefs.repeater_bw)); rd(&_prefs.repeater_sf, sizeof(_prefs.repeater_sf)); rd(&_prefs.repeater_cr, sizeof(_prefs.repeater_cr)); - // Pre-0x10 files leave stray sentinel bytes here. If the profile fields aren't a - // valid radio config, force the profile off so the repeater can't switch to junk. + // Pre-0x10 files leave stray sentinel bytes here, same as a never-configured + // device. Either way there's no valid saved profile, so default to a profile + // in the same band as the companion's own network (_prefs.freq, already read + // above) rather than "Current" — a repeater silently following the companion + // onto whatever private network it later joins isn't the MeshCore community + // norm; that stays opt-in. Band-matched rather than a flat frequency so the + // default can't land outside what's legal where the companion is set up. if (_prefs.repeater_use_profile > 1) _prefs.repeater_use_profile = 0; if (!(_prefs.repeater_freq >= 100.0f && _prefs.repeater_freq <= 960.0f && _prefs.repeater_sf >= 5 && _prefs.repeater_sf <= 12 && _prefs.repeater_cr >= 5 && _prefs.repeater_cr <= 8 && _prefs.repeater_bw >= 7.0f && _prefs.repeater_bw <= 510.0f)) { - _prefs.repeater_use_profile = 0; - _prefs.repeater_freq = _prefs.repeater_bw = 0.0f; - _prefs.repeater_sf = _prefs.repeater_cr = 0; + _prefs.repeater_use_profile = 1; + _prefs.repeater_freq = defaultRepeaterFreqForBand(_prefs.freq); + _prefs.repeater_bw = LORA_BW; + _prefs.repeater_sf = LORA_SF; + _prefs.repeater_cr = LORA_CR; } // → 0xC0DE000B: append bot_commands_enabled + quiet-hours. Older files leave // stray bytes here; clamp so upgraders fall back to off / no quiet hours. diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 018d70a0..3aaa07aa 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -1307,6 +1307,14 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe _prefs.bw = LORA_BW; _prefs.cr = LORA_CR; _prefs.tx_power_dbm = LORA_TX_POWER; + // Repeater profile default for a true first boot (no prefs file yet, so + // loadPrefs() below is a no-op) — same band-matched seed as the upgrade + // path in DataStore.cpp, kept in sync rather than left at memset's 0/Current. + _prefs.repeater_use_profile = 1; + _prefs.repeater_freq = defaultRepeaterFreqForBand(_prefs.freq); + _prefs.repeater_bw = LORA_BW; + _prefs.repeater_sf = LORA_SF; + _prefs.repeater_cr = LORA_CR; _prefs.gps_enabled = 0; // GPS disabled by default _prefs.gps_interval = 0; // No automatic GPS updates by default _prefs.display_brightness = 2; // medium brightness by default diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index 3369cca2..13edb0f5 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -41,18 +41,7 @@ class UITask; /* ---------------------------------- CONFIGURATION ------------------------------------- */ -#ifndef LORA_FREQ -#define LORA_FREQ 915.0 -#endif -#ifndef LORA_BW -#define LORA_BW 250 -#endif -#ifndef LORA_SF -#define LORA_SF 10 -#endif -#ifndef LORA_CR -#define LORA_CR 5 -#endif +// LORA_FREQ/BW/SF/CR fallbacks now live in NodePrefs.h (DataStore.cpp needs them too). #ifndef LORA_TX_POWER #define LORA_TX_POWER 20 #endif diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index a69b8ba3..a44e1ef5 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -2,6 +2,37 @@ #include #include +// Firmware's own boot-default radio params — used both as the companion's +// initial freq/sf/bw/cr (MyMesh.cpp) and, here, as the seed for a never- +// configured repeater profile (DataStore.cpp). Kept above any per-board +// target.h include so a board can still override via -D before this file +// is reached. +#ifndef LORA_FREQ +#define LORA_FREQ 915.0 +#endif +#ifndef LORA_BW +#define LORA_BW 250 +#endif +#ifndef LORA_SF +#define LORA_SF 10 +#endif +#ifndef LORA_CR +#define LORA_CR 5 +#endif + +// Bucket a companion frequency into whichever of the three license-exempt +// bands MeshCore's app-driven repeat toggle historically restricted to (see +// repeat_freq_ranges in MyMesh.cpp: 433.000 / 869.495 / 918.000 MHz). Used to +// seed a never-configured repeater profile in the same legal band as the +// companion's own network (MyMesh.cpp begin(), DataStore.cpp) — a flat +// single frequency could land outside the bands allowed where the operator +// actually lives. +static inline float defaultRepeaterFreqForBand(float companion_freq) { + if (companion_freq < 500.0f) return 433.000f; + if (companion_freq < 890.0f) return 869.495f; + return 918.000f; +} + #define TELEM_MODE_DENY 0 #define TELEM_MODE_ALLOW_FLAGS 1 // use contact.flags #define TELEM_MODE_ALLOW_ALL 2 @@ -176,7 +207,11 @@ struct NodePrefs { // persisted to file // Optional dedicated radio profile for repeater mode. When repeater_use_profile // is 1, enabling the repeater switches the radio to repeater_freq/bw/sf/cr and // disabling restores the companion's freq/bw/sf/cr (the fields above). 0 = the - // repeater stays on the current companion frequency (default — no switch). + // repeater stays on the current companion frequency. Default (a never-configured + // device) is 1, seeded via defaultRepeaterFreqForBand(freq) + LORA_SF/BW/CR — + // repeating on whatever private network the companion later joins isn't the + // community norm, and the seed stays in the same legal band as the companion's + // own network rather than a flat frequency. uint8_t repeater_use_profile; float repeater_freq; float repeater_bw; diff --git a/examples/companion_radio/ui-new/RepeaterScreen.h b/examples/companion_radio/ui-new/RepeaterScreen.h index 3e1737f2..518c16a1 100644 --- a/examples/companion_radio/ui-new/RepeaterScreen.h +++ b/examples/companion_radio/ui-new/RepeaterScreen.h @@ -6,10 +6,14 @@ // and keeps the relaying controls next to the numbers that show them working. // // Network modes: -// - Current — repeat on the companion's current frequency (default). +// - Current — repeat on the companion's current frequency. Opt-in only: +// relaying on whatever network the operator is chatting on +// isn't the MeshCore community norm. // - Custom — enabling the repeater switches the radio to a dedicated profile // (preset or manual), and disabling restores the companion params. -// Set the profile equal to the companion's for "same network". +// Default for a never-configured device, seeded with a frequency +// in the same band as the companion's own network (so the default +// can't land outside what's legal for the operator's region). // Included by UITask.cpp. #include