diff --git a/docs/development/roadmap.md b/docs/development/roadmap.md index b6b3699b..0f927924 100644 --- a/docs/development/roadmap.md +++ b/docs/development/roadmap.md @@ -384,7 +384,7 @@ Radio, both **default OFF**. Under field testing; not yet merged. already noted below (native NRF52 companion power-saving from the v1.16 upstream merge) — safe, but doesn't touch the dominant power draw (the radio in continuous RX), unlike a real duty-cycle. -- **Resolution:** `examples/companion_radio/MyMesh.h` now defines +- **Resolution:** `examples/companion_radio/Features.h` now defines `FEAT_RX_POWERSAVE 0`, gating out the Settings row (`SettingsScreen.h`), the Diagnostics RXPS watchdog row (`DiagnosticsScreen.h`), and every call site that would apply `_prefs.rx_powersave` to the radio or to CAD auto-enable diff --git a/docs/solo_features/settings_screen/settings_screen.md b/docs/solo_features/settings_screen/settings_screen.md index 8d3a0d91..0614a053 100644 --- a/docs/solo_features/settings_screen/settings_screen.md +++ b/docs/solo_features/settings_screen/settings_screen.md @@ -73,7 +73,7 @@ Lists all available home screen pages. For each entry: | CR | 5–8 | LEFT/RIGHT. Coding rate (4/5–4/8). | | Auto pwr | ON / OFF | **Adaptive Power Control.** Lowers TX power on strong links, ramps back to the **TX Pwr** ceiling on weak/lost ones. Link quality from DM ACK SNR, or — for channels (no ACK) — a repeater's rebroadcast. Live power shown on the radio page/name bar. Default OFF. **Suppressed (`--`) while the repeater is on** — restored once it's switched off. | -There is no "Pwr save" row: hardware RX duty-cycle receive was tried and disabled (`FEAT_RX_POWERSAVE 0` in `examples/companion_radio/MyMesh.h`) — the SX126x's duty-cycle preamble detection needs the sender's actual preamble to exactly match what we configure, which a mixed-firmware mesh can't guarantee (silently drops every packet from a mismatched sender, no matter the signal strength). See `docs/development/roadmap.md` for the full writeup. +There is no "Pwr save" row: hardware RX duty-cycle receive was tried and disabled (`FEAT_RX_POWERSAVE 0` in `examples/companion_radio/Features.h`) — the SX126x's duty-cycle preamble detection needs the sender's actual preamble to exactly match what we configure, which a mixed-firmware mesh can't guarantee (silently drops every packet from a mismatched sender, no matter the signal strength). See `docs/development/roadmap.md` for the full writeup. | Scope | list | Shows the list's **default** scope. **Enter** opens the **SCOPE** list: `*` (wildcard = unscoped, always first, can't be renamed or deleted) plus up to 8 named scopes of your own (e.g. `pl`). Typing a name derives a shared key the same way on every device, so any device that types the same name lands on the same scope automatically, no key exchange needed. **Enter** on a row opens **Set as default** / **Rename** / **Delete** (delete confirms first); **+ Add scope** at the bottom opens the keyboard. The **default** scope (marked `[default]`) governs **DMs** and the **repeater's own relay slot**; each **channel** carries its own pick — set from the channel's context menu (see [Message Screen](../message_screen/message_screen.md)) — and a channel left on `*` sends unscoped. Scopes tag flood traffic so repeaters can tell your community's messages apart from others sharing the same frequency; paired with **Tools › Repeater › Scope only** it's also what this device relays for in repeater mode. The default also syncs both ways with the phone app's default-scope setting. Not encryption — message content is unaffected either way. Upgrading from a build with the old single Scope field carries it over as the default entry and seeds every existing channel with it, so nothing changes on the air. | | OLED | E-Ink | diff --git a/examples/companion_radio/Features.h b/examples/companion_radio/Features.h index b71121fd..13475cfa 100644 --- a/examples/companion_radio/Features.h +++ b/examples/companion_radio/Features.h @@ -29,6 +29,23 @@ #define FEAT_FULL_REFRESH_SETTING 0 #endif +// Hardware RX duty-cycle ("Pwr save") is disabled: the SX126x's duty-cycle +// preamble detection needs the *actual transmitted* preamble to exactly match +// what we're configured to expect (confirmed hardware behaviour, see SX1262 +// datasheet 6.1.3 and https://github.com/jgromes/RadioLib/issues/1597) -- +// something we can't guarantee across a mesh with mixed firmware. A mismatch +// doesn't cost a little sensitivity, it silently drops every packet from that +// sender regardless of signal strength (2026-09-22 field report: near-total +// reception loss, unaffected by antenna). The real duty-cycle implementation +// (RadioLibWrapper::armRecv()/CustomSX1262Wrapper::startPowerSaveRecv()) is +// left in place for if a network-wide compatibility mechanism ever lands -- +// flipping this back to 1 requires solving that first, not just re-adding the +// Settings toggle. See docs/development/roadmap.md for the full writeup. +// Lives here (not MyMesh.h) so every `#if FEAT_RX_POWERSAVE` user sees the +// same definition: an undefined macro in `#if` silently reads as 0, which +// would split the build the day this is flipped to 1. +#define FEAT_RX_POWERSAVE 0 + namespace Features { #if defined(EINK_DISPLAY_MODEL) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 395f448d..35178ead 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -288,7 +288,7 @@ bool MyMesh::getCADEnabled() const { // (unlike simple_repeater's CommonCLI `cad` command) — it's wired and // persisted for a future manual override, but always 0 today. // rx_powersave is never actually applied to the radio while FEAT_RX_POWERSAVE - // is 0 (see MyMesh.h) -- don't let a stale persisted byte from before that + // is 0 (see Features.h) -- don't let a stale persisted byte from before that // still auto-enable CAD here for a duty-cycle mode that isn't running. #if FEAT_RX_POWERSAVE return _prefs.cad_enabled || (_prefs.rx_powersave && !_prefs.client_repeat); @@ -2001,7 +2001,7 @@ void MyMesh::begin(bool has_display) { #if FEAT_RX_POWERSAVE radio_driver.setPowerSaving(_prefs.rx_powersave && !_prefs.client_repeat); // duty-cycle RX off while repeating (must hear all traffic) #else - radio_driver.setPowerSaving(false); // see MyMesh.h FEAT_RX_POWERSAVE -- ignore any stale persisted rx_powersave byte + radio_driver.setPowerSaving(false); // see Features.h FEAT_RX_POWERSAVE -- ignore any stale persisted rx_powersave byte #endif board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain); board.setLoRaFemPaGainEnabled(_prefs.radio_fem_txgain); @@ -2496,7 +2496,7 @@ void MyMesh::handleCmdFrame(size_t len) { #if FEAT_RX_POWERSAVE radio_driver.setPowerSaving(_prefs.rx_powersave && !_prefs.client_repeat); #else - radio_driver.setPowerSaving(false); // see MyMesh.h FEAT_RX_POWERSAVE -- ignore any stale persisted rx_powersave byte + radio_driver.setPowerSaving(false); // see Features.h FEAT_RX_POWERSAVE -- ignore any stale persisted rx_powersave byte #endif applyApc(); // pins power to the ceiling; apcActive() keeps it there while repeating MESH_DEBUG_PRINTLN("OK: CMD_SET_RADIO_PARAMS: f=%d, bw=%d, sf=%d, cr=%d", freq, bw, (uint32_t)sf, diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index 050d75b1..83aac909 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -4,6 +4,7 @@ #include #include "AbstractUITask.h" #include +#include "Features.h" // FEAT_RX_POWERSAVE, used by MyMesh.cpp // Forward declaration for UITask class UITask; @@ -11,20 +12,6 @@ class UITask; /*------------ Frame Protocol --------------*/ #define FIRMWARE_VER_CODE 13 -// Hardware RX duty-cycle ("Pwr save") is disabled: the SX126x's duty-cycle -// preamble detection needs the *actual transmitted* preamble to exactly match -// what we're configured to expect (confirmed hardware behaviour, see SX1262 -// datasheet 6.1.3 and https://github.com/jgromes/RadioLib/issues/1597) -- -// something we can't guarantee across a mesh with mixed firmware. A mismatch -// doesn't cost a little sensitivity, it silently drops every packet from that -// sender regardless of signal strength (2026-09-22 field report: near-total -// reception loss, unaffected by antenna). The real duty-cycle implementation -// (RadioLibWrapper::armRecv()/CustomSX1262Wrapper::startPowerSaveRecv()) is -// left in place for if a network-wide compatibility mechanism ever lands -- -// flipping this back to 1 requires solving that first, not just re-adding the -// Settings toggle. See docs/development/roadmap.md for the full writeup. -#define FEAT_RX_POWERSAVE 0 - // Fallback only -- every real build (local or CI) goes through build.sh, which // always injects its own FIRMWARE_BUILD_DATE (today's date at build time). // __DATE__ is the compiler's own "Mmm dd yyyy" build-date macro, so a diff --git a/examples/companion_radio/ui-new/LiveShareScreen.h b/examples/companion_radio/ui-new/LiveShareScreen.h index 47225d5b..702f8ef5 100644 --- a/examples/companion_radio/ui-new/LiveShareScreen.h +++ b/examples/companion_radio/ui-new/LiveShareScreen.h @@ -133,7 +133,7 @@ public: _dirty = true; break; case K_DURATION: _prefs->loc_share_duration_idx = (uint8_t)((_prefs->loc_share_duration_idx + (dir >= 0 ? 1 : NodePrefs::LOC_SHARE_DURATION_COUNT - 1)) % NodePrefs::LOC_SHARE_DURATION_COUNT); - _task->restartLocShareSession(); // a new length starts the session over + _task->restartLocShareClock(); // a new length starts the clock over (no extra [LOC] send) _dirty = true; break; case K_TARGET: if (enter) { _task->pickLocShareTarget(); return; } // full chooser diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 6e3c3bbe..0146ef4c 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -3856,7 +3856,7 @@ void UITask::applyPowerSave() { // kept and restored when the repeater is switched off. radio_driver.setPowerSaving(_node_prefs->rx_powersave && !_node_prefs->client_repeat); #else - radio_driver.setPowerSaving(false); // see MyMesh.h FEAT_RX_POWERSAVE -- ignore any stale persisted rx_powersave byte + radio_driver.setPowerSaving(false); // see Features.h FEAT_RX_POWERSAVE -- ignore any stale persisted rx_powersave byte #endif } diff --git a/examples/companion_radio/ui-new/UITask.h b/examples/companion_radio/ui-new/UITask.h index 7b1c5ca0..52f5b020 100644 --- a/examples/companion_radio/ui-new/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -340,6 +340,10 @@ public: void gotoLiveShareScreen(); // Start the auto-share session clock afresh (next engine tick treats it as a new enable). void restartLocShareSession() { _loc_share_was_enabled = false; } + // Restart only the session's duration clock -- unlike restartLocShareSession(), + // no re-announce. For a changed "Stop after" length, where position hasn't + // changed. No-op before the engine has started a session (it sets the clock then). + void restartLocShareClock() { if (_loc_share_was_enabled) _loc_share_session_ms = millis(); } void gotoLocatorScreen(); // Re-arm the locator state machine so the next evaluation initialises // silently (called by the Locator tool after the target/radius changes, diff --git a/src/helpers/radiolib/CustomSX1262Wrapper.h b/src/helpers/radiolib/CustomSX1262Wrapper.h index 839eaef2..e2d49035 100644 --- a/src/helpers/radiolib/CustomSX1262Wrapper.h +++ b/src/helpers/radiolib/CustomSX1262Wrapper.h @@ -62,7 +62,7 @@ public: // configured preamble is too short for a real duty-cycle (senderPreamble < // 2*minSymbols+1), RadioLib transparently falls back to a continuous receive. // - // NOT CURRENTLY REACHABLE: FEAT_RX_POWERSAVE (MyMesh.h) is 0, so nothing ever + // NOT CURRENTLY REACHABLE: FEAT_RX_POWERSAVE (companion Features.h) is 0, so nothing ever // calls setPowerSaving(true) and this never runs. Left implemented (matching // our own preambleLengthForSF(sf) convention, so it's correct for nodes on // this same convention) rather than removed, because the reason it's disabled @@ -74,7 +74,7 @@ public: // guarantee we can't currently make. A mismatch doesn't cost a little // sensitivity, it silently drops every packet from that sender regardless of // signal strength (2026-09-22 field report: near-total reception loss on a - // stock SF8 preset, unaffected by antenna gain). See MyMesh.h and + // stock SF8 preset, unaffected by antenna gain). See companion Features.h and // docs/development/roadmap.md before ever flipping FEAT_RX_POWERSAVE back on. int16_t startPowerSaveRecv() override { return ((SX126x *)_radio)->startReceiveDutyCycleAuto(preambleLengthForSF(_preamble_sf), 8); diff --git a/src/helpers/radiolib/RadioLibWrappers.h b/src/helpers/radiolib/RadioLibWrappers.h index 33a0dc1b..a7fc6640 100644 --- a/src/helpers/radiolib/RadioLibWrappers.h +++ b/src/helpers/radiolib/RadioLibWrappers.h @@ -46,7 +46,7 @@ protected: // stays in RX to receive the packet (RX_DONE on DIO1) — no MCU state machine, // average RX current cut several-fold. Driven from armRecv()/loop(); falls back // to continuous RX if the modem doesn't support it. - // Companion-side gating: FEAT_RX_POWERSAVE (examples/companion_radio/MyMesh.h) + // Companion-side gating: FEAT_RX_POWERSAVE (examples/companion_radio/Features.h) // is 0, so nothing on that target ever calls setPowerSaving(true) and this // path never actually runs there -- the SX126x duty-cycle's preamble // detection needs the sender's actual preamble to exactly match what we