mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-10-15 20:19:56 +00:00
offgrid overhaul
This commit is contained in:
@@ -86,6 +86,9 @@ All of this is local behaviour. Nothing here changes the wire protocol, so mixed
|
||||
- **LEDs you control.** Turn them all off with `set leds off`, or choose what each one shows: blink on
|
||||
transmit, on receive or both, and a heartbeat that can signal unread messages. The receive blink tells
|
||||
you at a glance whether a repeater is hearing anything.
|
||||
- **Off-grid mode from the device.** A companion can relay for a group of handhelds out of reach of any
|
||||
repeater. Switch it on from the display or the CLI and the node moves to the off-grid frequency of its
|
||||
band and back by itself. ([OFFGRID.md](docs/OFFGRID.md))
|
||||
- **2.4 GHz on LR2021 boards.** Set a 2.4 GHz frequency and the radio switches to its high-band path on
|
||||
its own.
|
||||
- **Runs on Linux too.** The same code runs as a normal process on a Raspberry Pi or Femtofox with a real
|
||||
@@ -169,6 +172,7 @@ ZephCore is a plain west workspace application, and it tries to be a well-behave
|
||||
| [adr/](docs/adr/README.md) | Decisions and why they were made |
|
||||
| [BUILDING.md](docs/BUILDING.md) | Every build variant, platform notes, Kconfig options |
|
||||
| [CLI_commands.md](docs/CLI_commands.md) | Every CLI command |
|
||||
| [OFFGRID.md](docs/OFFGRID.md) | Off-grid mode: a companion that relays, and the frequencies it runs on |
|
||||
| [supported_boards.md](docs/supported_boards.md) | Boards and build strings |
|
||||
| [LINUX_NATIVE.md](docs/LINUX_NATIVE.md) | Running on a Pi or Femtofox |
|
||||
| [releasenotes/](releasenotes/) | What changed in each release |
|
||||
|
||||
@@ -604,6 +604,13 @@ Handles the binary BLE protocol with ~50 command opcodes. Key features:
|
||||
- **Protocol versioning**: V2/V3 frame format negotiation with phone app
|
||||
- **Ed25519 signing**: 3-phase flow (start→data→finish) for signing up to 8KB
|
||||
- **Flood scope**: Transport key filtering for region-scoped sends
|
||||
- **Off-grid mode** (upstream "client repeat", user view in `OFFGRID.md`): the companion forwards, unfiltered, only
|
||||
while `client_repeat` is set **and** `prefs.freq` is one of upstream's off-grid frequencies (433.000 / 869.495 /
|
||||
918.000 MHz). `helpers/OffgridPolicy.h` holds that rule and the prefs transitions; `allowPacketForward()`, the
|
||||
boot-time load, `CMD_DEVICE_QUERY` byte 80 and both device UIs go through it. The app's `CMD_SET_RADIO_PARAMS`
|
||||
sets frequency and flag together, as upstream. The device toggle and the companion's `set repeat` call
|
||||
`CompanionMesh::setOffgridMode()`: on = nearest off-grid frequency, the one left kept in `zc.og_home`; off = back
|
||||
to it. BW, SF, CR and TX power are never touched
|
||||
|
||||
### 6.2.1 V-Contact (Loopback Admin Contact)
|
||||
|
||||
|
||||
@@ -337,7 +337,7 @@ The observer role (ESP32, `boards/common/observer.conf`) has its own CLI and, ap
|
||||
|---------|---------|
|
||||
| `get name` | Node name |
|
||||
| `get role` | Firmware role: `repeater` or `room_server` (companion builds report `companion`) |
|
||||
| `get repeat` | Forwarding enabled: `on` or `off` |
|
||||
| `get repeat` | Forwarding enabled: `on` or `off`. On a companion this is off-grid mode ([OFFGRID.md](OFFGRID.md)): `on` only while repeat is set and the node is on an off-grid frequency |
|
||||
| `get radio` | Radio params as `freq,bw,sf,cr` — the same comma-separated form `set radio` takes, so a reply can be edited and sent straight back |
|
||||
| `get freq` | Frequency in MHz |
|
||||
| `get freqerr` | *(ZephCore only)* Carrier frequency error measured on received packets: `mean N Hz, min A, max B, K pkts`. **LR2021 only** — other radios answer `not available`. Purely diagnostic; nothing acts on it. **The mean only approximates *this* node's reference error once it is averaged over many different peers** — their individual errors cancel, ours does not — so read `K` and the min/max spread before believing it: a tight spread over a handful of packets is one chatty neighbour, not a population. Small values are the expected answer and mean there is nothing to do; LoRa tolerates carrier error up to roughly a quarter of the bandwidth before sensitivity suffers, so at BW 62.5 kHz a few hundred Hz is noise. If it is kHz-scale the correction is board-dependent: XTAL parts have `SetXoscCpTrim`, but **TCXO parts have no chip-side trim at all** (DS §6.11.4: "If a TCXO is configured, this command has no effect"), leaving only a software offset to the programmed frequency. Values beyond ±200 kHz are discarded by the driver and warn once — the field is decoded from three `GetLoraPacketStatus` bytes that DS rev 2.1 does not document, so implausible readings are evidence the field is not real on that firmware rather than a genuine measurement. Reset by `clear stats`. |
|
||||
@@ -421,7 +421,7 @@ four radio parameters together, since they are one interop-critical set.
|
||||
| Command | Constraints | Description |
|
||||
|---------|-------------|-------------|
|
||||
| `set name <name>` | No `[ ] \ : , ? *` | Set node name |
|
||||
| `set repeat <on\|off>` | | Enable or disable packet forwarding |
|
||||
| `set repeat <on\|off>` | | Enable or disable packet forwarding. **Companion:** off-grid mode, the same as the display toggle ([OFFGRID.md](OFFGRID.md)), local only and applied at once. `on` moves the node to the nearest of 433.000 / 869.495 / 918.000 MHz and starts forwarding; `off` returns to the frequency it left. A node already on an off-grid frequency stays there both ways. BW, SF, CR and TX power are not touched; the frequency move resets the learned CAD offset. Replies `OK - repeat is now ON (869.495 MHz)`, or `Error: no off-grid frequency for this band` on a 2.4 GHz preset. `set radio` / `set freq` to any other frequency turns it off |
|
||||
| `set radio <freq>,<bw>,<sf>,<cr>` | freq 150–2500, bw 7–500 (7–1000 on LR2021), sf 5–12, cr 5–8 | **Comma-separated**, not space-separated — spaces parse as a single argument and the command is rejected. Set radio params *(reboot required)*. If freq, bw or sf actually changed, this also performs a full `set cad.reset` and replies `OK - reboot to apply (CAD reset)` — the learned detPeak offset and its probe statistics belong to the old preset. The radio keeps running the old preset, at its learned offset, until the reboot; CAD adaptation is suspended in the meantime so it cannot re-dirty the reset. A cr-only change resets nothing. |
|
||||
| `set freq <mhz>` | 150–2500 *(USB only)* | Set frequency alone *(reboot required)*. Same CAD reset as `set radio` when the frequency actually changes. On LR2021 boards a frequency at or above 1500 MHz also moves the radio to its 2.4 GHz front end — see the note below the table. |
|
||||
| `set tx <dbm>` | −9 to board max (default 30) | Set TX power |
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# Off-grid mode
|
||||
|
||||
Off-grid mode makes a companion forward packets for others, like a small repeater. It is for a group
|
||||
of handhelds that is out of reach of any repeater: each node relays for the rest, so messages get
|
||||
further than one radio hop.
|
||||
|
||||
It is MeshCore's "client repeat". ZephCore nodes and stock MeshCore nodes in off-grid mode work
|
||||
together.
|
||||
|
||||
## It runs on its own frequencies
|
||||
|
||||
A companion forwards only on one of three frequencies set aside for this:
|
||||
|
||||
| Band | Off-grid frequency |
|
||||
|---|---|
|
||||
| 433 MHz | 433.000 MHz |
|
||||
| EU 868 MHz | 869.495 MHz |
|
||||
| US 915 MHz | 918.000 MHz |
|
||||
|
||||
Turning off-grid mode on therefore **moves the node off your normal mesh**. While it is on you reach
|
||||
only other nodes on the same off-grid frequency with the same bandwidth, spreading factor and coding
|
||||
rate. Those three, and the TX power, are left as they were, so a group that switches from the same
|
||||
mesh preset ends up on matching settings.
|
||||
|
||||
A companion never forwards on a normal mesh frequency. It forwards everything it hears, without the
|
||||
hop limits, region filters and loop detection a repeater applies, which is fine for a handful of
|
||||
handhelds and harmful on a busy mesh.
|
||||
|
||||
## Turning it on and off
|
||||
|
||||
**From the app.** Choose one of the off-grid frequencies in the radio settings and enable repeat.
|
||||
When you turn repeat off in the app the node stays on that frequency: select your mesh preset again
|
||||
to return.
|
||||
|
||||
**From the display.** The Offgrid page (button boards) or System > Device > Offgrid (joystick boards)
|
||||
asks for confirmation, then moves to the off-grid frequency nearest to the one you are on and starts
|
||||
forwarding. Turning it off there returns to the frequency you came from. If the node was already on an
|
||||
off-grid frequency when you turned it on, it stays there.
|
||||
|
||||
**From the CLI** (USB or the app's command line, not over the mesh): `set repeat on` and
|
||||
`set repeat off` do the same as the display, and `get repeat` shows the state.
|
||||
|
||||
On a 2.4 GHz preset there is no off-grid frequency, and the display and the CLI refuse.
|
||||
|
||||
## After an update from 1.17.7 or earlier
|
||||
|
||||
Up to 1.17.7 the display toggle turned forwarding on without changing the frequency, so a companion
|
||||
could end up repeating on the live mesh. A node left in that state stops forwarding when it boots
|
||||
1.17.8 or later, and shows off-grid mode as off.
|
||||
@@ -1,6 +1,7 @@
|
||||
# ZephCore 1.17.8-zephcore
|
||||
|
||||
Button fixes on four boards with a display: the Station G2's button now drives the pages, and
|
||||
Off-grid mode from the display no longer turns a companion into a repeater on the live mesh. Button
|
||||
fixes on four boards with a display: the Station G2's button now drives the pages, and
|
||||
Power Off, DFU and Offgrid can be confirmed on the Heltec Mesh Node T1 and both LR2021 LoRa Plus EVK
|
||||
kits.
|
||||
|
||||
@@ -9,6 +10,28 @@ kits.
|
||||
|
||||
---
|
||||
|
||||
## Off-grid mode: only on the off-grid frequencies
|
||||
|
||||
Off-grid mode makes a companion forward packets for others, for a group of handhelds out of reach of
|
||||
any repeater. It belongs on three frequencies set aside for it: 433.000, 869.495 and 918.000 MHz. The
|
||||
app only allows it there, but the Offgrid toggle on the display switched forwarding on without
|
||||
changing the frequency, so the node repeated everything it heard on the live mesh.
|
||||
|
||||
- **The display toggle now moves the node.** Turning Offgrid on goes to the off-grid frequency nearest
|
||||
to the one you are on and starts forwarding; turning it off returns to the frequency you came from.
|
||||
Bandwidth, spreading factor, coding rate and TX power stay as they are.
|
||||
- **A companion never forwards on any other frequency.** If you had switched Offgrid on from the
|
||||
display on an earlier version, the node stops repeating after this update and shows Offgrid as off.
|
||||
Nothing else changes; turn it on again if you want off-grid mode.
|
||||
- **`set repeat on` / `set repeat off` work on a companion** and do the same as the display toggle;
|
||||
`get repeat` shows the real state. Before, they answered but did nothing.
|
||||
- **The display follows the app.** Offgrid switched from the app or the CLI now shows on the display
|
||||
page within a few seconds.
|
||||
|
||||
While off-grid mode is on, the node is off your normal mesh. How it works: [OFFGRID.md](../docs/OFFGRID.md).
|
||||
|
||||
Thanks to N8HOP for the report.
|
||||
|
||||
## Station G2: the user button drives the display
|
||||
|
||||
Until now a press of the user button only woke the display: the pages could not be turned, and
|
||||
|
||||
@@ -352,6 +352,21 @@
|
||||
"name": "Scoped unscoped and advert hop boundaries",
|
||||
"source": "tests/unit/policy_tests.cpp"
|
||||
},
|
||||
{
|
||||
"id": "UNIT-OFFGRID-001",
|
||||
"name": "Off-grid frequency: the three allowed, nearest per band, none above sub-GHz",
|
||||
"source": "tests/unit/policy_tests.cpp"
|
||||
},
|
||||
{
|
||||
"id": "UNIT-OFFGRID-002",
|
||||
"name": "Off-grid toggle: to the off-grid frequency and back, preset otherwise untouched",
|
||||
"source": "tests/unit/policy_tests.cpp"
|
||||
},
|
||||
{
|
||||
"id": "UNIT-OFFGRID-003",
|
||||
"name": "Off-grid: a repeat flag on a mesh frequency is dropped, app and CLI edits drop the home frequency",
|
||||
"source": "tests/unit/policy_tests.cpp"
|
||||
},
|
||||
{
|
||||
"id": "UNIT-POOL-001",
|
||||
"name": "Exhaust reclaim and reuse all pool slots",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "test.h"
|
||||
#include <RoutingPolicy.h>
|
||||
#include <OffgridPolicy.h>
|
||||
#include <RateLimiter.h>
|
||||
#include <mesh/Maintenance.h>
|
||||
using namespace mesh;
|
||||
@@ -47,3 +48,73 @@ TEST(maintenance_wrap, "UNIT-TIME-001", "Maintenance deadlines across uint32 wra
|
||||
CHECK(maintenanceSooner(MAINTENANCE_IDLE, 32) == 32);
|
||||
CHECK(maintenanceSooner(0, MAINTENANCE_IDLE) == 0);
|
||||
}
|
||||
|
||||
namespace {
|
||||
NodePrefs offgridPrefs(float freq, uint8_t repeat = 0, float home = 0.0f) {
|
||||
NodePrefs p{};
|
||||
p.freq = freq; p.bw = 62.5f; p.sf = 7; p.cr = 5; p.tx_power_dbm = 22;
|
||||
p.client_repeat = repeat; p.offgrid_home_freq = home;
|
||||
return p;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(offgrid_freq_pick, "UNIT-OFFGRID-001", "Off-grid frequency: the three allowed, nearest per band, none above sub-GHz") {
|
||||
CHECK(isOffgridFreq(433.0f) && isOffgridFreq(869.495f) && isOffgridFreq(918.0f));
|
||||
CHECK(!isOffgridFreq(869.618f) && !isOffgridFreq(869.494f) && !isOffgridFreq(910.525f));
|
||||
CHECK(isOffgridFreqKhz(869495) && !isOffgridFreqKhz(869496));
|
||||
CHECK(nearestOffgridFreqKhz(869.618f) == 869495);
|
||||
CHECK(nearestOffgridFreqKhz(868.1f) == 869495);
|
||||
CHECK(nearestOffgridFreqKhz(910.525f) == 918000);
|
||||
CHECK(nearestOffgridFreqKhz(927.875f) == 918000);
|
||||
CHECK(nearestOffgridFreqKhz(433.875f) == 433000);
|
||||
CHECK(nearestOffgridFreqKhz(150.0f) == 433000);
|
||||
CHECK(nearestOffgridFreqKhz(869.495f) == 869495);
|
||||
CHECK(nearestOffgridFreqKhz(2445.0f) == 0);
|
||||
CHECK(nearestOffgridFreqKhz(0.0f) == 0);
|
||||
}
|
||||
TEST(offgrid_toggle, "UNIT-OFFGRID-002", "Off-grid toggle: to the off-grid frequency and back, preset otherwise untouched") {
|
||||
NodePrefs p = offgridPrefs(869.618f);
|
||||
CHECK(!offgridActive(p));
|
||||
CHECK(offgridSet(p, true) == OFFGRID_RETUNED);
|
||||
CHECK(offgridActive(p) && p.freq == 869.495f && p.offgrid_home_freq == 869.618f);
|
||||
CHECK(p.bw == 62.5f && p.sf == 7 && p.cr == 5 && p.tx_power_dbm == 22);
|
||||
CHECK(offgridSet(p, true) == OFFGRID_UNCHANGED);
|
||||
CHECK(offgridSet(p, false) == OFFGRID_RETUNED);
|
||||
CHECK(!offgridActive(p) && p.freq == 869.618f && p.offgrid_home_freq == 0.0f && p.client_repeat == 0);
|
||||
CHECK(offgridSet(p, false) == OFFGRID_UNCHANGED);
|
||||
|
||||
// Already on an off-grid frequency (the app left it there): flag only, both ways.
|
||||
p = offgridPrefs(918.0f);
|
||||
CHECK(offgridSet(p, true) == OFFGRID_SET);
|
||||
CHECK(offgridActive(p) && p.freq == 918.0f && p.offgrid_home_freq == 0.0f);
|
||||
CHECK(offgridSet(p, false) == OFFGRID_SET);
|
||||
CHECK(!offgridActive(p) && p.freq == 918.0f);
|
||||
|
||||
// No off-grid frequency on 2.4 GHz: refused, nothing changes.
|
||||
p = offgridPrefs(2445.0f);
|
||||
CHECK(offgridSet(p, true) == OFFGRID_NO_BAND);
|
||||
CHECK(p.client_repeat == 0 && p.freq == 2445.0f && p.offgrid_home_freq == 0.0f);
|
||||
}
|
||||
TEST(offgrid_normalize, "UNIT-OFFGRID-003", "Off-grid: a repeat flag on a mesh frequency is dropped, app and CLI edits drop the home frequency") {
|
||||
// What the pre-1.17.8 device toggle left behind.
|
||||
NodePrefs p = offgridPrefs(869.618f, 1);
|
||||
CHECK(!offgridActive(p));
|
||||
CHECK(offgridNormalize(p) && p.client_repeat == 0 && p.freq == 869.618f);
|
||||
CHECK(!offgridNormalize(p));
|
||||
// Off-grid mode on is left alone, home frequency included.
|
||||
p = offgridPrefs(869.495f, 1, 869.618f);
|
||||
CHECK(!offgridNormalize(p) && offgridActive(p) && p.offgrid_home_freq == 869.618f);
|
||||
// The app turned repeat off and stayed on the off-grid frequency.
|
||||
p = offgridPrefs(869.495f, 0, 869.618f);
|
||||
offgridFreqEdited(p);
|
||||
CHECK(p.offgrid_home_freq == 0.0f && p.freq == 869.495f && p.client_repeat == 0);
|
||||
// The app moved to another off-grid frequency with repeat on: stays on, home gone.
|
||||
p = offgridPrefs(433.0f, 1, 869.618f);
|
||||
offgridFreqEdited(p);
|
||||
CHECK(offgridActive(p) && p.offgrid_home_freq == 0.0f);
|
||||
CHECK(offgridSet(p, false) == OFFGRID_SET && p.freq == 433.0f);
|
||||
// The CLI set a mesh frequency while off-grid mode was on: off, and nothing to return to.
|
||||
p = offgridPrefs(868.0f, 1, 869.618f);
|
||||
offgridFreqEdited(p);
|
||||
CHECK(p.client_repeat == 0 && p.offgrid_home_freq == 0.0f && p.freq == 868.0f);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ NodePrefs sample() {
|
||||
for (int i = 0; i < 16; i++) p.default_scope_key[i] = (uint8_t)(i + 1);
|
||||
p.ble_disabled = 1; p.display_brightness = 50; p.wake_on_msg = 0; p.screen_off_secs = 60;
|
||||
p.auto_shutdown_mv = 3300; p.v_contact_enabled = 0; p.v_battery_alert_mv = 3500;
|
||||
p.v_contact_flags = 0x81; p.wifi_enabled = 0;
|
||||
p.v_contact_flags = 0x81; p.wifi_enabled = 0; p.offgrid_home_freq = 869.618f;
|
||||
strcpy(p.wifi_ssid, "MyNet"); strcpy(p.wifi_pwd, "secret");
|
||||
return p;
|
||||
}
|
||||
@@ -75,6 +75,7 @@ TEST(pjson_companion_roundtrip, "UNIT-PJSON-001", "Companion prefs survive prefs
|
||||
CHECK(companionBytes(q) == companionBytes(p));
|
||||
// prefs.json only: not in the binary layout.
|
||||
CHECK(q.led_brightness == 42);
|
||||
CHECK(q.offgrid_home_freq == 869.618f);
|
||||
}
|
||||
|
||||
TEST(pjson_server_roundtrip, "UNIT-PJSON-002", "Server prefs survive prefs.json field for field") {
|
||||
|
||||
@@ -129,6 +129,18 @@ public:
|
||||
(void)freq; (void)bw; (void)sf; (void)cr; (void)timeout_mins;
|
||||
}
|
||||
|
||||
/* `get repeat` / `set repeat`: off-grid mode, the same call as the device toggle. */
|
||||
bool hasOffgridMode() const override { return true; }
|
||||
bool isOffgridActive() const override { return _mesh.isOffgridActive(); }
|
||||
void setOffgridMode(bool on, char* reply) override {
|
||||
if (_mesh.setOffgridMode(on) == mesh::OFFGRID_NO_BAND) {
|
||||
strcpy(reply, "Error: no off-grid frequency for this band");
|
||||
} else {
|
||||
snprintf(reply, CLI_REPLY_SIZE, "OK - repeat is now %s (%.3f MHz)",
|
||||
_mesh.isOffgridActive() ? "ON" : "OFF", (double)_mesh.prefs.freq);
|
||||
}
|
||||
}
|
||||
|
||||
MeshTimeSync* getMeshTimeSync() override {
|
||||
return _mesh.getMeshTimeSync();
|
||||
}
|
||||
|
||||
@@ -39,27 +39,6 @@ static void notify_contact_msg_ui(
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Allowed client repeat frequency ranges (matches Arduino) */
|
||||
struct FreqRange {
|
||||
uint32_t lower_freq, upper_freq;
|
||||
};
|
||||
|
||||
static const FreqRange repeat_freq_ranges[] = {
|
||||
{ 433000, 433000 }, /* 433 MHz (ISM band) */
|
||||
{ 869495, 869495 }, /* 869.495 MHz (EU 869.4-869.65 band, 10% DC, 500mW ERP) */
|
||||
{ 918000, 918000 }, /* 918 MHz (US ISM band) */
|
||||
};
|
||||
|
||||
static bool isValidClientRepeatFreq(uint32_t f)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(repeat_freq_ranges) / sizeof(repeat_freq_ranges[0]); i++) {
|
||||
if (f >= repeat_freq_ranges[i].lower_freq && f <= repeat_freq_ranges[i].upper_freq) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CompanionMesh::CompanionMesh(mesh::Radio &radio, mesh::MillisecondClock &ms, mesh::RNG &rng,
|
||||
mesh::RTCClock &rtc, mesh::PacketManager &mgr, mesh::MeshTables &tables,
|
||||
ZephyrDataStore &store)
|
||||
@@ -143,7 +122,27 @@ void CompanionMesh::begin()
|
||||
bool CompanionMesh::allowPacketForward(const mesh::Packet *packet)
|
||||
{
|
||||
(void)packet;
|
||||
return prefs.client_repeat != 0;
|
||||
return mesh::offgridActive(prefs);
|
||||
}
|
||||
|
||||
bool CompanionMesh::isOffgridActive() const
|
||||
{
|
||||
return mesh::offgridActive(prefs);
|
||||
}
|
||||
|
||||
mesh::OffgridResult CompanionMesh::setOffgridMode(bool on)
|
||||
{
|
||||
mesh::OffgridResult r = mesh::offgridSet(prefs, on);
|
||||
|
||||
if (r == mesh::OFFGRID_SET || r == mesh::OFFGRID_RETUNED) {
|
||||
_store->savePrefs(prefs);
|
||||
}
|
||||
if (r == mesh::OFFGRID_RETUNED && _radio_reconfig_cb) {
|
||||
_radio_reconfig_cb(true);
|
||||
}
|
||||
LOG_INF("offgrid %s: result=%d freq=%u kHz", on ? "on" : "off", (int)r,
|
||||
(unsigned)mesh::offgridFreqKhz(prefs.freq));
|
||||
return r;
|
||||
}
|
||||
|
||||
void CompanionMesh::sendFloodScoped(const TransportKey &scope, mesh::Packet *pkt, uint32_t delay_millis)
|
||||
@@ -1789,7 +1788,7 @@ bool CompanionMesh::handleCmdDeviceQuery(const uint8_t *data, size_t len)
|
||||
memcpy(&rsp[8], fw_build, 12);
|
||||
memcpy(&rsp[20], model, 40);
|
||||
memcpy(&rsp[60], version, 20);
|
||||
rsp[80] = prefs.client_repeat; // v9+: offgrid mode state
|
||||
rsp[80] = mesh::offgridActive(prefs) ? 1 : 0; // v9+: offgrid mode state
|
||||
rsp[81] = prefs.path_hash_mode; // v10+: path hash mode
|
||||
writeFrame(rsp, sizeof(rsp));
|
||||
return true;
|
||||
@@ -2439,7 +2438,7 @@ bool CompanionMesh::handleCmdSetRadioParams(const uint8_t *data, size_t len)
|
||||
repeat = data[11]; // v9+: client repeat flag
|
||||
}
|
||||
// If repeat requested, validate frequency against allowed bands
|
||||
if (repeat && !isValidClientRepeatFreq(freq)) {
|
||||
if (repeat && !mesh::isOffgridFreqKhz(freq)) {
|
||||
writeErrFrame(ERR_ILLEGAL_ARG);
|
||||
/* freq arrives in kHz, bw in Hz. Same limits as the USB
|
||||
* CLI and the prefs loader — see NodePrefs.h. Accepting
|
||||
@@ -2464,6 +2463,7 @@ bool CompanionMesh::handleCmdSetRadioParams(const uint8_t *data, size_t len)
|
||||
prefs.sf = sf;
|
||||
prefs.cr = cr;
|
||||
prefs.client_repeat = repeat;
|
||||
mesh::offgridFreqEdited(prefs);
|
||||
_store->savePrefs(prefs);
|
||||
if (_radio_reconfig_cb) _radio_reconfig_cb(preset_changed);
|
||||
LOG_INF("SET_RADIO_PARAMS: client_repeat=%d", repeat);
|
||||
@@ -3392,12 +3392,12 @@ bool CompanionMesh::handleCmdGetAutoaddConfig(const uint8_t *data, size_t len)
|
||||
|
||||
bool CompanionMesh::handleCmdGetAllowedRepeatFreq(const uint8_t *data, size_t len)
|
||||
{
|
||||
uint8_t rsp[1 + sizeof(repeat_freq_ranges)];
|
||||
uint8_t rsp[1 + sizeof(mesh::OFFGRID_FREQ_RANGES)];
|
||||
int i = 0;
|
||||
rsp[i++] = PACKET_ALLOWED_REPEAT_FREQ;
|
||||
for (size_t k = 0; k < sizeof(repeat_freq_ranges) / sizeof(repeat_freq_ranges[0]); k++) {
|
||||
put_le32(&rsp[i], repeat_freq_ranges[k].lower_freq); i += 4;
|
||||
put_le32(&rsp[i], repeat_freq_ranges[k].upper_freq); i += 4;
|
||||
for (const mesh::OffgridFreqRange &r : mesh::OFFGRID_FREQ_RANGES) {
|
||||
put_le32(&rsp[i], r.lower_khz); i += 4;
|
||||
put_le32(&rsp[i], r.upper_khz); i += 4;
|
||||
}
|
||||
writeFrame(rsp, i);
|
||||
return true;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <helpers/TransportKeyStore.h>
|
||||
#include <ZephyrDataStore.h>
|
||||
#include <NodePrefs.h>
|
||||
#include <helpers/OffgridPolicy.h>
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
/* Push notification codes */
|
||||
@@ -201,6 +202,13 @@ public:
|
||||
* zephcore_persist_before_off(). */
|
||||
void flushPendingWrites() { flushDirtyContacts(); flushDirtyChannels(); }
|
||||
|
||||
/* Off-grid mode (upstream "client repeat"): forwarding, allowed only on the
|
||||
* off-grid frequencies. setOffgridMode() is the device toggle and the CLI's
|
||||
* `set repeat`: it retunes to the nearest off-grid frequency and back, saves,
|
||||
* and reconfigures the radio. Main thread only. */
|
||||
bool isOffgridActive() const;
|
||||
mesh::OffgridResult setOffgridMode(bool on);
|
||||
|
||||
NodePrefs prefs;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <helpers/ui/display.h>
|
||||
#endif
|
||||
#include <helpers/MeshTimeSync.h>
|
||||
#include <helpers/OffgridPolicy.h>
|
||||
#include <helpers/LoopWakeStats.h>
|
||||
extern "C" void power_diag_line(int n, char *out, size_t cap); /* TEMPORARY: helpers/power_diag.c */
|
||||
#include <helpers/time_sync.h>
|
||||
@@ -855,6 +856,7 @@ bool CommonCLI::handleRadioCmd(uint32_t sender_timestamp, const char* command, c
|
||||
_prefs->bw = bw;
|
||||
_prefs->sf = sf;
|
||||
_prefs->cr = cr;
|
||||
mesh::offgridFreqEdited(*_prefs);
|
||||
_callbacks->savePrefs();
|
||||
_callbacks->freezeRadioParams(old_freq, old_bw, old_sf, old_cr);
|
||||
/* Anything that moves the channel or the detPeak base table
|
||||
@@ -920,6 +922,7 @@ bool CommonCLI::handleRadioCmd(uint32_t sender_timestamp, const char* command, c
|
||||
f >= 150.0f && f <= 2500.0f) {
|
||||
float old_freq = _prefs->freq;
|
||||
_prefs->freq = f;
|
||||
mesh::offgridFreqEdited(*_prefs);
|
||||
savePrefs();
|
||||
/* Keep _prefs->freq = f in RAM so a later savePrefs() (from any
|
||||
* other "set" command before reboot) can't rewrite the old freq
|
||||
@@ -1024,7 +1027,8 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, const char* command, cha
|
||||
} else if (memcmp(config, "name", 4) == 0) {
|
||||
snprintf(reply, CLI_REPLY_SIZE, "> %s", _prefs->node_name);
|
||||
} else if (memcmp(config, "repeat", 6) == 0) {
|
||||
snprintf(reply, CLI_REPLY_SIZE, "> %s", _prefs->disable_fwd ? "off" : "on");
|
||||
bool on = _callbacks->hasOffgridMode() ? _callbacks->isOffgridActive() : !_prefs->disable_fwd;
|
||||
snprintf(reply, CLI_REPLY_SIZE, "> %s", on ? "on" : "off");
|
||||
} else if (memcmp(config, "lat", 3) == 0) {
|
||||
snprintf(reply, CLI_REPLY_SIZE, "> %.6f", _prefs->node_lat);
|
||||
} else if (memcmp(config, "lon", 3) == 0) {
|
||||
@@ -1473,7 +1477,15 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, const char* command, cha
|
||||
strcpy(reply, "Error: name cannot contain [ ] \\ : , ? *");
|
||||
}
|
||||
} else if (memcmp(config, "repeat ", 7) == 0) {
|
||||
if (memcmp(&config[7], "on", 2) == 0) {
|
||||
bool on = memcmp(&config[7], "on", 2) == 0;
|
||||
if (_callbacks->hasOffgridMode() && (on || memcmp(&config[7], "off", 3) == 0)) {
|
||||
/* Local only: it moves the node to another frequency. */
|
||||
if (sender_timestamp == 0) {
|
||||
_callbacks->setOffgridMode(on, reply);
|
||||
} else {
|
||||
strcpy(reply, "Error: local only");
|
||||
}
|
||||
} else if (on) {
|
||||
_prefs->disable_fwd = 0;
|
||||
savePrefs();
|
||||
strcpy(reply, "OK - repeat is now ON");
|
||||
|
||||
@@ -79,6 +79,12 @@ public:
|
||||
* _prefs takes the new ones for the next boot. No-op under tempradio. */
|
||||
virtual void freezeRadioParams(float freq, float bw, uint8_t sf, uint8_t cr) { (void)freq; (void)bw; (void)sf; (void)cr; }
|
||||
|
||||
/* Companion: `get repeat` / `set repeat` are off-grid mode, not the
|
||||
* servers' repeat pref. setOffgridMode() writes the reply. */
|
||||
virtual bool hasOffgridMode() const { return false; }
|
||||
virtual bool isOffgridActive() const { return false; }
|
||||
virtual void setOffgridMode(bool on, char* reply) { (void)on; (void)reply; }
|
||||
|
||||
// Adaptive contention window
|
||||
virtual float getContentionEstimate() const { return -1.0f; }
|
||||
virtual float getFloodDelayFactor() const { return 0.5f; }
|
||||
|
||||
@@ -168,6 +168,9 @@ struct NodePrefs {
|
||||
uint8_t buzzer_quiet;
|
||||
uint8_t autoadd_config;
|
||||
uint8_t client_repeat; // 1 = offgrid mode (forward packets)
|
||||
/* MHz the device toggle left when it turned off-grid mode on, 0 = none.
|
||||
* Only meaningful while off-grid mode is on (helpers/OffgridPolicy.h). */
|
||||
float offgrid_home_freq;
|
||||
uint8_t path_hash_mode; // path mode 0-2
|
||||
uint8_t autoadd_max_hops; // 0 = no limit, N = up to N-1 hops
|
||||
uint8_t loop_detect; // LOOP_DETECT_{OFF,MINIMAL,MODERATE,STRICT}
|
||||
@@ -277,6 +280,8 @@ static inline void sanitizeNodePrefs(NodePrefs* p) {
|
||||
p->airtime_factor = sanePrefFloat(p->airtime_factor, 0.0f, 9.0f, 9.0f);
|
||||
p->rx_delay_base = sanePrefFloat(p->rx_delay_base, 0.0f, 3600.0f, 0.0f);
|
||||
p->adc_multiplier = sanePrefFloat(p->adc_multiplier, 0.0f, 30000.0f, 0.0f);
|
||||
p->offgrid_home_freq = sanePrefFloat(p->offgrid_home_freq, ZC_RADIO_FREQ_MIN_MHZ,
|
||||
ZC_RADIO_FREQ_MAX_MHZ, 0.0f);
|
||||
/* Range matches the CLI (0.0-2.0). 0.0 is a legal value meaning "reactive
|
||||
* backoff off" and must survive: this guard exists for NaN and corruption
|
||||
* only. RepeaterDataStore used to coerce 0.0 -> 0.2 here, which silently
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* ZephCore OffgridPolicy - companion off-grid mode (upstream "client repeat")
|
||||
*
|
||||
* The frequencies a companion may forward on, and the prefs transitions behind
|
||||
* the app command, the device toggle and the CLI. Pure decision logic, no I/O.
|
||||
* Design: devdocs/lld/06-companion.md.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "NodePrefs.h"
|
||||
|
||||
namespace mesh {
|
||||
|
||||
struct OffgridFreqRange {
|
||||
uint32_t lower_khz, upper_khz;
|
||||
};
|
||||
|
||||
/* Upstream's allowed client-repeat frequencies; CMD_GET_ALLOWED_REPEAT_FREQ
|
||||
* reports this table to the app. */
|
||||
static const OffgridFreqRange OFFGRID_FREQ_RANGES[] = {
|
||||
{ 433000, 433000 }, /* 433 MHz ISM */
|
||||
{ 869495, 869495 }, /* EU 869.4-869.65 MHz */
|
||||
{ 918000, 918000 }, /* US 902-928 MHz */
|
||||
};
|
||||
#define OFFGRID_FREQ_RANGE_COUNT (sizeof(mesh::OFFGRID_FREQ_RANGES) / sizeof(mesh::OFFGRID_FREQ_RANGES[0]))
|
||||
|
||||
/* Top of the sub-GHz LoRa range; above it no off-grid frequency applies. */
|
||||
#define OFFGRID_SUBGHZ_MAX_MHZ 960.0f
|
||||
|
||||
inline uint32_t offgridFreqKhz(float mhz)
|
||||
{
|
||||
return mhz > 0.0f ? (uint32_t)(mhz * 1000.0f + 0.5f) : 0;
|
||||
}
|
||||
|
||||
inline bool isOffgridFreqKhz(uint32_t khz)
|
||||
{
|
||||
for (const OffgridFreqRange &r : OFFGRID_FREQ_RANGES) {
|
||||
if (khz >= r.lower_khz && khz <= r.upper_khz) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool isOffgridFreq(float mhz) { return isOffgridFreqKhz(offgridFreqKhz(mhz)); }
|
||||
|
||||
/* The off-grid frequency (kHz) nearest to `mhz`; 0 above the sub-GHz range. */
|
||||
inline uint32_t nearestOffgridFreqKhz(float mhz)
|
||||
{
|
||||
if (!(mhz > 0.0f && mhz <= OFFGRID_SUBGHZ_MAX_MHZ)) return 0;
|
||||
|
||||
uint32_t khz = offgridFreqKhz(mhz);
|
||||
uint32_t best = 0, best_dist = UINT32_MAX;
|
||||
for (const OffgridFreqRange &r : OFFGRID_FREQ_RANGES) {
|
||||
uint32_t c = khz < r.lower_khz ? r.lower_khz : (khz > r.upper_khz ? r.upper_khz : khz);
|
||||
uint32_t dist = c > khz ? c - khz : khz - c;
|
||||
if (dist < best_dist) {
|
||||
best_dist = dist;
|
||||
best = c;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
/* Off-grid mode is on: repeat is set AND the node is on an off-grid frequency. */
|
||||
inline bool offgridActive(const NodePrefs &p)
|
||||
{
|
||||
return p.client_repeat != 0 && isOffgridFreq(p.freq);
|
||||
}
|
||||
|
||||
/* Drops a repeat flag left on any other frequency, and the remembered home
|
||||
* frequency with it (it only means something while off-grid mode is on).
|
||||
* True if prefs changed. */
|
||||
inline bool offgridNormalize(NodePrefs &p)
|
||||
{
|
||||
if (offgridActive(p)) return false;
|
||||
|
||||
bool changed = p.client_repeat != 0 || p.offgrid_home_freq != 0.0f;
|
||||
p.client_repeat = 0;
|
||||
p.offgrid_home_freq = 0.0f;
|
||||
return changed;
|
||||
}
|
||||
|
||||
/* The frequency was set by the app or the CLI: whoever set it owns it, so the
|
||||
* remembered home frequency goes. */
|
||||
inline void offgridFreqEdited(NodePrefs &p)
|
||||
{
|
||||
p.offgrid_home_freq = 0.0f;
|
||||
offgridNormalize(p);
|
||||
}
|
||||
|
||||
enum OffgridResult : uint8_t {
|
||||
OFFGRID_UNCHANGED, /* already in the requested state */
|
||||
OFFGRID_SET, /* repeat flag changed, frequency kept */
|
||||
OFFGRID_RETUNED, /* repeat flag and frequency changed: reconfigure the radio */
|
||||
OFFGRID_NO_BAND, /* refused: no off-grid frequency for the current band */
|
||||
};
|
||||
|
||||
/* The device toggle and `set repeat`. On: move to the nearest off-grid
|
||||
* frequency and remember the one left. Off: return to it. A node already on
|
||||
* an off-grid frequency stays there both ways. BW, SF, CR and TX power are
|
||||
* never touched (as the app). */
|
||||
inline OffgridResult offgridSet(NodePrefs &p, bool on)
|
||||
{
|
||||
if (on) {
|
||||
if (offgridActive(p)) return OFFGRID_UNCHANGED;
|
||||
if (isOffgridFreq(p.freq)) {
|
||||
p.client_repeat = 1;
|
||||
p.offgrid_home_freq = 0.0f;
|
||||
return OFFGRID_SET;
|
||||
}
|
||||
uint32_t khz = nearestOffgridFreqKhz(p.freq);
|
||||
if (khz == 0) return OFFGRID_NO_BAND;
|
||||
p.offgrid_home_freq = p.freq;
|
||||
p.freq = (float)khz / 1000.0f;
|
||||
p.client_repeat = 1;
|
||||
return OFFGRID_RETUNED;
|
||||
}
|
||||
|
||||
bool was_set = p.client_repeat != 0;
|
||||
float home = offgridActive(p) ? p.offgrid_home_freq : 0.0f;
|
||||
p.client_repeat = 0;
|
||||
p.offgrid_home_freq = 0.0f;
|
||||
if (home != 0.0f) {
|
||||
p.freq = home;
|
||||
return OFFGRID_RETUNED;
|
||||
}
|
||||
return was_set ? OFFGRID_SET : OFFGRID_UNCHANGED;
|
||||
}
|
||||
|
||||
} /* namespace mesh */
|
||||
@@ -144,6 +144,7 @@ protected:
|
||||
def("vbat_mv", _p->v_battery_alert_mv);
|
||||
def("adc_mult", _p->adc_multiplier);
|
||||
def("pwr_sav_en", _p->powersaving_enabled);
|
||||
def("og_home", _p->offgrid_home_freq);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "ui_pages.h"
|
||||
#include "ui_task.h"
|
||||
#include "ui_mesh_actions.h"
|
||||
#include "display.h"
|
||||
#include <helpers/buzzer_gate.h>
|
||||
|
||||
@@ -1391,8 +1392,9 @@ static void render_offgrid_mono(void)
|
||||
draw_centered(centered_row(2, 3), "Press to confirm");
|
||||
} else {
|
||||
draw_centered(centered_row(2, 3),
|
||||
state.offgrid_enabled ? "Press to Disable"
|
||||
: "Press to Enable");
|
||||
state.offgrid_enabled ? "Press to Disable" :
|
||||
mesh_offgrid_available() ? "Press to Enable"
|
||||
: "Not on this band");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1417,8 +1419,9 @@ static void render_offgrid_color(void)
|
||||
draw_centered_color(y,
|
||||
state.offgrid_confirm_time != 0 ?
|
||||
"Press to confirm" :
|
||||
(state.offgrid_enabled ? "Press to disable"
|
||||
: "Press to enable"),
|
||||
(state.offgrid_enabled ? "Press to disable" :
|
||||
mesh_offgrid_available() ? "Press to enable"
|
||||
: "Not on this band"),
|
||||
state.offgrid_confirm_time != 0 ? UI_COLOR_WARN
|
||||
: UI_COLOR_VALUE);
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ static void action_page_enter(void)
|
||||
st_og->offgrid_confirm_time = 0;
|
||||
mesh_set_offgrid_mode(new_state);
|
||||
LOG_INF("offgrid mode %s (button)", new_state ? "on" : "off");
|
||||
} else {
|
||||
} else if (st_og->offgrid_enabled || mesh_offgrid_available()) {
|
||||
/* First press — enter confirmation state */
|
||||
st_og->offgrid_confirm_time = now_og;
|
||||
}
|
||||
@@ -1135,8 +1135,12 @@ void ui_set_buzzer_mode(uint8_t mode)
|
||||
void ui_set_offgrid_mode(bool enabled)
|
||||
{
|
||||
struct ui_state *s = get_state();
|
||||
bool changed = s->offgrid_enabled != enabled;
|
||||
|
||||
s->offgrid_enabled = enabled;
|
||||
if (changed) {
|
||||
schedule_render_auto();
|
||||
}
|
||||
}
|
||||
|
||||
/* Schedule a redraw for an automatic (non-interactive) state update, e.g. a
|
||||
|
||||
@@ -1050,7 +1050,7 @@ void JoystickUITask::disableSerial() { _ble_enabled = false; mesh_ble_set_enable
|
||||
/* ===== Offgrid / LEDs / DFU ===== */
|
||||
void JoystickUITask::setOffgridMode(bool enable)
|
||||
{
|
||||
if (_prefs) _prefs->client_repeat = enable ? 1 : 0;
|
||||
/* CompanionMesh::setOffgridMode() does the prefs and the retune. */
|
||||
mesh_set_offgrid_mode(enable);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <helpers/ContactInfo.h>
|
||||
#include <helpers/ChannelDetails.h>
|
||||
#include <helpers/NodePrefs.h>
|
||||
#include <helpers/OffgridPolicy.h>
|
||||
#include <adapters/clock/ZephyrRTCClock.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
@@ -125,7 +126,10 @@ public:
|
||||
bool isSerialEnabled() const { return _ble_enabled; }
|
||||
void disableSerial();
|
||||
void enableSerial();
|
||||
bool isOffgridEnabled() const { return _prefs && _prefs->client_repeat != 0; }
|
||||
bool isOffgridEnabled() const { return _prefs && mesh::offgridActive(*_prefs); }
|
||||
/* kHz that turning it on leads to (the current one if already off-grid);
|
||||
* 0 = no off-grid frequency for this band. */
|
||||
uint32_t offgridTargetKhz() const { return _prefs ? mesh::nearestOffgridFreqKhz(_prefs->freq) : 0; }
|
||||
void setOffgridMode(bool enable);
|
||||
bool isLedsDisabled() const { return _prefs && _prefs->leds_disabled != 0; }
|
||||
void toggleLeds();
|
||||
|
||||
@@ -169,6 +169,8 @@ bool SystemScreen::handleInput(char c)
|
||||
if (_task->isOffgridEnabled()) {
|
||||
_task->setOffgridMode(false);
|
||||
_task->showAlert("Offgrid: OFF", 1000);
|
||||
} else if (_task->offgridTargetKhz() == 0) {
|
||||
_task->showAlert("No offgrid band", 1500);
|
||||
} else {
|
||||
_mode = SYSMODE_OFFGRID_CONFIRM;
|
||||
}
|
||||
@@ -278,8 +280,12 @@ bool SystemScreen::handleInput(char c)
|
||||
}
|
||||
if (_mode == SYSMODE_OFFGRID_CONFIRM) {
|
||||
if (c == KEY_ENTER) {
|
||||
char og_alert[24];
|
||||
uint32_t og_khz = _task->offgridTargetKhz();
|
||||
snprintf(og_alert, sizeof(og_alert), "Offgrid ON %u.%03u",
|
||||
(unsigned)(og_khz / 1000), (unsigned)(og_khz % 1000));
|
||||
_task->setOffgridMode(true);
|
||||
_task->showAlert("Offgrid: ON", 1000);
|
||||
_task->showAlert(og_alert, 1500);
|
||||
_mode = SYSMODE_DEVICE;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -190,9 +190,14 @@ extern "C" void mesh_set_buzzer_mode(uint8_t mode)
|
||||
k_event_post(s_mesh_events, s_mesh_event_ui_action);
|
||||
}
|
||||
|
||||
extern "C" bool mesh_offgrid_available(void)
|
||||
{
|
||||
return s_mesh && mesh::nearestOffgridFreqKhz(s_mesh->prefs.freq) != 0;
|
||||
}
|
||||
|
||||
extern "C" void mesh_set_offgrid_mode(bool enable)
|
||||
{
|
||||
/* Defer the flash write (savePrefs) to mesh thread */
|
||||
/* Defer the retune and the flash write to mesh thread */
|
||||
atomic_set(&pending_offgrid_enabled, enable ? 1 : 0);
|
||||
atomic_or(&pending_ui_actions, UI_ACTION_OFFGRID_TOGGLE);
|
||||
k_event_post(s_mesh_events, s_mesh_event_ui_action);
|
||||
@@ -254,9 +259,10 @@ extern "C" void mesh_handle_ui_actions(void)
|
||||
|
||||
if (actions & UI_ACTION_OFFGRID_TOGGLE) {
|
||||
bool og = atomic_get(&pending_offgrid_enabled) != 0;
|
||||
s_mesh->prefs.client_repeat = og ? 1 : 0;
|
||||
LOG_INF("client_repeat=%d (button)", og);
|
||||
need_save = true;
|
||||
/* Saves and retunes by itself; the page shows what it ended up as. */
|
||||
s_mesh->setOffgridMode(og);
|
||||
ui_set_offgrid_mode(s_mesh->isOffgridActive());
|
||||
ui_push_radio_state(*s_lora_radio);
|
||||
}
|
||||
|
||||
if (actions & UI_ACTION_LEDS_TOGGLE) {
|
||||
@@ -349,6 +355,8 @@ extern "C" void mesh_housekeeping_ui_refresh(void)
|
||||
ui_set_tz(s_mesh->prefs.tz_offset);
|
||||
|
||||
ui_push_radio_state(*s_lora_radio);
|
||||
/* The app and the CLI change it too. */
|
||||
ui_set_offgrid_mode(s_mesh->isOffgridActive());
|
||||
|
||||
/* Update GPS satellite count even without fix. When GPS is disabled, push
|
||||
* a zeroed count — gps_enable(false) already zeros the internal count, but
|
||||
|
||||
@@ -57,6 +57,8 @@ void mesh_gps_set_enabled(bool enable);
|
||||
void mesh_ble_set_enabled(bool enable);
|
||||
void mesh_set_buzzer_mode(uint8_t mode);
|
||||
void mesh_set_offgrid_mode(bool enable);
|
||||
/* False when the current band has no off-grid frequency (2.4 GHz). */
|
||||
bool mesh_offgrid_available(void);
|
||||
void mesh_set_leds_disabled(bool disabled);
|
||||
void mesh_reboot_to_ota_dfu(void);
|
||||
void mesh_save_brightness(uint8_t brightness);
|
||||
|
||||
@@ -19,6 +19,7 @@ __attribute__((weak)) void mesh_gps_set_enabled(bool enable) { ARG_UNUSED(enable
|
||||
__attribute__((weak)) void mesh_ble_set_enabled(bool enable) { ARG_UNUSED(enable); }
|
||||
__attribute__((weak)) void mesh_set_buzzer_mode(uint8_t mode) { ARG_UNUSED(mode); }
|
||||
__attribute__((weak)) void mesh_set_offgrid_mode(bool enable) { ARG_UNUSED(enable); }
|
||||
__attribute__((weak)) bool mesh_offgrid_available(void) { return false; }
|
||||
__attribute__((weak)) void mesh_set_leds_disabled(bool disabled) { ARG_UNUSED(disabled); }
|
||||
__attribute__((weak)) void mesh_reboot_to_ota_dfu(void) {}
|
||||
/* Repeater builds persist orientation through RepeaterDataStore in
|
||||
|
||||
@@ -982,6 +982,11 @@ int main(void)
|
||||
companion_mesh.prefs.gps_interval = CONFIG_ZEPHCORE_GPS_POLL_INTERVAL_SEC; /* 0 = always on */
|
||||
|
||||
data_store.loadPrefs(companion_mesh.prefs);
|
||||
/* A repeat flag stored on a non-off-grid frequency is dropped. */
|
||||
if (mesh::offgridNormalize(companion_mesh.prefs)) {
|
||||
LOG_WRN("offgrid mode dropped: not on an off-grid frequency");
|
||||
data_store.savePrefs(companion_mesh.prefs);
|
||||
}
|
||||
/* ESP32 light sleep: `powersaving off` holds a sleep lock. No-op elsewhere. */
|
||||
zc_pm_set_powersaving(companion_mesh.prefs.powersaving_enabled != 0);
|
||||
/* Until the first fix, the position is the configured one. */
|
||||
@@ -1071,9 +1076,9 @@ int main(void)
|
||||
ui_set_gps_enabled(companion_mesh.prefs.gps_enabled != 0);
|
||||
ui_set_ble_enabled(companion_mesh.prefs.ble_disabled != 1); /* BLE starts advertising at boot */
|
||||
|
||||
ui_set_offgrid_mode(companion_mesh.prefs.client_repeat != 0);
|
||||
ui_set_offgrid_mode(companion_mesh.isOffgridActive());
|
||||
LOG_INF("offgrid mode: %s (from prefs)",
|
||||
companion_mesh.prefs.client_repeat ? "on" : "off");
|
||||
companion_mesh.isOffgridActive() ? "on" : "off");
|
||||
|
||||
/* buzzer_init() starts quiet, so the saved mode is always applied. */
|
||||
#if IS_ENABLED(CONFIG_ZEPHCORE_UI_BUZZER)
|
||||
|
||||
Reference in New Issue
Block a user