From b475bde7303ef745d56ee95527c96882612aca6f Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 8 May 2026 10:53:21 -0700 Subject: [PATCH] Unify retry preset settings --- docs/cli_commands.md | 45 ++-- docs/halo_keymind_settings.md | 308 ++++++++++++++++++++++++++++ examples/simple_repeater/MyMesh.cpp | 12 +- src/helpers/CommonCLI.cpp | 188 ++++++++--------- src/helpers/CommonCLI.h | 9 +- 5 files changed, 437 insertions(+), 125 deletions(-) create mode 100644 docs/halo_keymind_settings.md diff --git a/docs/cli_commands.md b/docs/cli_commands.md index 9631a64a..c3cf2bd9 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -570,10 +570,10 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- -#### View or change the direct retry timing preset +#### View or change the retry preset **Usage:** -- `get direct.retry.preset` -- `set direct.retry.preset ` +- `get retry.preset` +- `set retry.preset ` **Parameters:** - `value`: `infra`|`rooftop`|`mobile` or `0`|`1`|`2` @@ -581,11 +581,11 @@ This document provides an overview of CLI commands that can be sent to MeshCore **Default:** `rooftop` (`1`) **Presets:** -- `infra` (`0`): `275 ms` base wait, `4` retries, `150 ms` added per retry, SNR gate is SF floor + `15 dB` -- `rooftop` (`1`): `175 ms` base wait, `15` retries, `100 ms` added per retry, SNR gate is SF floor + `5 dB` -- `mobile` (`2`): `175 ms` base wait, `15` retries, `50 ms` added per retry, SNR gate is the SF floor +- `infra` (`0`): `275 ms` direct base wait, `4` direct retries, `150 ms` added per direct retry, SNR gate is SF floor + `15 dB`; flood retry defaults to `1` retry and path gate `1` +- `rooftop` (`1`): `175 ms` direct base wait, `15` direct retries, `100 ms` added per direct retry, SNR gate is SF floor + `5 dB`; flood retry defaults to `3` retries and path gate `2` +- `mobile` (`2`): `175 ms` direct base wait, `15` direct retries, `50 ms` added per direct retry, SNR gate is the SF floor; flood retry defaults to `3` retries and path gate `1` -**Note:** Selecting a preset copies those values into the direct retry settings and also resets flood retry defaults. You can refine `direct.retry.margin`, `direct.retry.count`, `direct.retry.base`, `direct.retry.step`, `flood.retry.count`, or `flood.retry.path` afterward. Retry delay is `direct.txdelay` jitter + base wait + packet-length airtime wait + per-attempt step. +**Note:** Selecting a preset copies those values into the direct retry settings and resets flood retry defaults. You can refine `direct.retry.margin`, `direct.retry.count`, `direct.retry.base`, `direct.retry.step`, `flood.retry.count`, or `flood.retry.path` afterward. Retry delay is `direct.txdelay` jitter + base wait + packet-length airtime wait + per-attempt step. --- @@ -754,23 +754,6 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- -#### View or change the flood retry preset -**Usage:** -- `get flood.retry.preset` -- `set flood.retry.preset ` - -**Parameters:** -- `value`: `infra`|`rooftop`|`mobile` or `0`|`1`|`2` - -**Presets:** -- `infra` (`0`): `1` retry, path gate `1` -- `rooftop` (`1`): `3` retries, path gate `2` -- `mobile` (`2`): `3` retries, path gate `1` - -**Note:** This applies only the flood retry defaults. `set direct.retry.preset` also resets these flood retry defaults. - ---- - #### View or change the number of flood retry attempts **Usage:** - `get flood.retry.count` @@ -797,6 +780,20 @@ This document provides an overview of CLI commands that can be sent to MeshCore --- +#### View or change whether advert packets are flood-retried +**Usage:** +- `get flood.retry.advert` +- `set flood.retry.advert ` + +**Parameters:** +- `state`: `on` or `off` + +**Default:** `off` + +**Note:** When this is `off`, node advert packets (`PAYLOAD_TYPE_ADVERT`, type `4`) are not queued for flood retry. + +--- + #### View or change flood retry target prefixes **Usage:** - `get flood.retry.prefixes` diff --git a/docs/halo_keymind_settings.md b/docs/halo_keymind_settings.md new file mode 100644 index 00000000..c451707f --- /dev/null +++ b/docs/halo_keymind_settings.md @@ -0,0 +1,308 @@ +# Halo and Keymind Branch Settings + +This file covers only CLI settings added by the Halo or Keymind branches. Use +`docs/cli_commands.md` for the general MeshCore CLI. + +## Quick Start + +Use this baseline when bringing a Halo or Keymind repeater onto the network: + +```text +set retry.preset rooftop +set direct.retry.heard on +set flood.retry.advert off +set flood.retry.bridge off +set flood.retry.prefixes none +set flood.retry.ignore none +``` + +Then verify: + +```text +get retry.preset +get direct.retry.heard +get flood.retry.advert +get flood.retry.prefixes +get flood.retry.ignore +``` + +Use prefixes from debug logs such as `path=7773D0>BEEBB0` or `heard=C7618C`. Prefix lists are comma-separated hex values, for example `71CE82,C7618C`. + +## Prefix Worksheet + +Keep Halo and Keymind prefixes in one place before programming buckets or ignore lists. + +| Network | Prefix | Node or site | Use | Notes | +| --- | --- | --- | --- | --- | +| Halo | `A1B2C3` | example remote relay | bucket/target | Replace with real prefix | +| Keymind | `71CE82` | example observed relay | ignore/target | Replace with real prefix | +| Keymind | `C7618C` | example observed relay | ignore/target | Replace with real prefix | + +## Common Examples + +Disable retrying advert packets: + +```text +set flood.retry.advert off +get flood.retry.advert +``` + +Ignore a relay as a successful flood retry echo: + +```text +set flood.retry.ignore 71CE82,C7618C +get flood.retry.ignore +``` + +Only accept specific downstream relays as flood retry success: + +```text +set flood.retry.prefixes BEEBB0,425E5C +get flood.retry.prefixes +``` + +Bridge two groups of repeaters: + +```text +set flood.retry.bridge on +set flood.retry.bucket 1 71CE82,C7618C +set flood.retry.bucket 2 BEEBB0,425E5C +get flood.retry.bucket.1 +get flood.retry.bucket.2 +``` + +Return to simple non-bridge flood retry: + +```text +set flood.retry.bridge off +set flood.retry.prefixes none +set flood.retry.ignore none +``` + +## Added Settings + +| Setting | What it does | How to use | Example | +| --- | --- | --- | --- | +| `recent.repeater` | Shows or seeds the recent repeater prefix/SNR table used by direct retry and bridge freshness checks. | `get recent.repeater`, `get recent.repeater `, `set recent.repeater ` | `set recent.repeater A1B2C3 -8.5` | +| `radio.fem.rxgain` | Controls the external LoRa FEM receive-path LNA where the board supports it. | `get radio.fem.rxgain`, `set radio.fem.rxgain on/off` | `set radio.fem.rxgain on` | + +## Recent Repeater Table + +Direct retry uses the recent repeater table when `direct.retry.heard` is `on`. +Bridge buckets also use this table: a configured bucket prefix is active only +when it was heard within the last hour. + +Show learned rows: + +```text +get recent.repeater +get recent.repeater 2 +``` + +Seed or correct a prefix: + +```text +set recent.repeater A1B2C3 -8.5 +``` + +Rows are sorted by prefix width, then SNR. A full direct retry failure lowers +the matching row by `0.25 dB`. + +## Direct Retry Settings + +Direct retry applies to direct-routed packets. A queued resend is canceled when the next-hop echo is heard. + +| Setting | What it does | How to use | Example | +| --- | --- | --- | --- | +| `retry.preset` | Applies shared direct and flood retry defaults. Values: `infra`, `rooftop`, `mobile` or `0`, `1`, `2`. | `get retry.preset`, `set retry.preset ` | `set retry.preset rooftop` | +| `direct.retry.heard` | Uses the recent repeater table as the direct retry eligibility gate. | `get direct.retry.heard`, `set direct.retry.heard on/off` | `set direct.retry.heard on` | +| `direct.retry.margin` | SNR margin in dB above the SF-specific receive floor. | `get direct.retry.margin`, `set direct.retry.margin <0-40>` | `set direct.retry.margin 5` | +| `direct.retry.count` | Maximum direct retry attempts after initial TX. | `get direct.retry.count`, `set direct.retry.count <1-15>` | `set direct.retry.count 15` | +| `direct.retry.base` | Base wait in milliseconds before retry. | `get direct.retry.base`, `set direct.retry.base <10-5000>` | `set direct.retry.base 175` | +| `direct.retry.step` | Milliseconds added per retry attempt. | `get direct.retry.step`, `set direct.retry.step <0-5000>` | `set direct.retry.step 100` | + +Preset details: + +| Preset | Base | Count | Step | SNR gate | +| --- | ---: | ---: | ---: | --- | +| `infra` | `275 ms` | `4` | `150 ms` | SF floor + `15 dB` | +| `rooftop` | `175 ms` | `15` | `100 ms` | SF floor + `5 dB` | +| `mobile` | `175 ms` | `15` | `50 ms` | SF floor | + +Example for a quiet fixed repeater: + +```text +set retry.preset rooftop +set direct.retry.heard on +set direct.retry.margin 5 +``` + +Example for a moving or weak-link node: + +```text +set retry.preset mobile +set direct.retry.margin 0 +``` + +## Flood And Advert Settings + +Flood retry applies to flood-routed packets. A queued retry is canceled when a qualifying downstream echo is heard. + +| Setting | What it does | How to use | Example | +| --- | --- | --- | --- | +| `flood.retry.count` | Maximum flood retry attempts after initial TX. `0` disables flood retry. | `get flood.retry.count`, `set flood.retry.count <0-3>` | `set flood.retry.count 3` | +| `flood.retry.path` | Maximum path hash count eligible for flood retry, or `off` to disable the gate. | `get flood.retry.path`, `set flood.retry.path <0-63/off>` | `set flood.retry.path 1` | +| `flood.retry.advert` | Allows or blocks retry for node advert packets (`type=4`). Default is `off`. | `get flood.retry.advert`, `set flood.retry.advert on/off` | `set flood.retry.advert off` | +| `flood.retry.prefixes` | Target prefixes. If set, only matching downstream echoes cancel a retry. | `get flood.retry.prefixes`, `set flood.retry.prefixes ` | `set flood.retry.prefixes BEEBB0,425E5C` | +| `flood.retry.ignore` | Ignored prefixes. In non-bridge retry, ignored last-hop echoes do not cancel retry. | `get flood.retry.ignore`, `set flood.retry.ignore ` | `set flood.retry.ignore 71CE82,C7618C` | +| `flood.retry.bridge` | Enables bucket-based bridge retry logic. | `get flood.retry.bridge`, `set flood.retry.bridge on/off` | `set flood.retry.bridge on` | +| `flood.retry.bucket.` | Shows one bridge bucket. Buckets are numbered `1`-`6`. | `get flood.retry.bucket.` | `get flood.retry.bucket.1` | +| `flood.retry.bucket` | Sets bridge bucket prefixes. | `set flood.retry.bucket <1-6> ` | `set flood.retry.bucket 1 71CE82,C7618C` | + +The shared retry preset sets these flood defaults: + +| Preset | Retry count | Path gate | +| --- | ---: | ---: | +| `infra` | `1` | `1` | +| `rooftop` | `3` | `2` | +| `mobile` | `3` | `1` | + +Example for Keymind path-gated retry: + +```text +set retry.preset rooftop +set flood.retry.path 1 +set flood.retry.advert off +set flood.retry.ignore 71CE82,C7618C +``` + +Example for Halo targeted retry: + +```text +set flood.retry.bridge off +set flood.retry.prefixes A1B2C3,D4E5F6 +set flood.retry.ignore none +``` + +Example for Halo/Keymind bridge retry: + +```text +set flood.retry.bridge on +set flood.retry.bucket 1 A1B2C3,D4E5F6 +set flood.retry.bucket 2 71CE82,C7618C +set flood.retry.advert off +``` + +## North South Buckets + +Buckets describe groups of repeaters on different sides of this relay. Bucket +numbers do not have built-in meanings; this example uses bucket `1` for North +and bucket `2` for South. + +```text + North bucket 1 + +-----------------------+ + | A1B2C3 D4E5F6 | + | North A North B | + +-----------+-----------+ + | + v + +-----------+ + | This node | + +-----------+ + ^ + | + +-----------+-----------+ + | 71CE82 C7618C | + | South A South B | + +-----------------------+ + South bucket 2 +``` + +Configure the buckets: + +```text +set flood.retry.bridge on +set flood.retry.bucket 1 A1B2C3,D4E5F6 +set flood.retry.bucket 2 71CE82,C7618C +set flood.retry.ignore none +``` + +Packet heard from the North: + +```text + heard source + | + v + +--------------+ retry targets + | North bucket | -----> South bucket + | bucket 1 | -----> Other fresh/unbucketed relays + +--------------+ +``` + +Packet heard from the South: + +```text + heard source + | + v + +--------------+ retry targets + | South bucket | -----> North bucket + | bucket 2 | -----> Other fresh/unbucketed relays + +--------------+ +``` + +Packet heard from an unbucketed or pathless source: + +```text + heard source + | + v + +--------------+ retry targets + | Other bucket | -----> North bucket + | implicit | -----> South bucket + +--------------+ +``` + +Bridge retry stays eligible until every target bucket has been heard or +`flood.retry.count` is exhausted. A configured bucket is a target only when at +least one of its prefixes is fresh in `recent.repeater`. Prefixes in +`flood.retry.ignore` never count as bucket hits. + +## Troubleshooting + +If advert packets are still retrying: + +```text +get flood.retry.advert +set flood.retry.advert off +``` + +If ignored prefixes still appear in `flood retry good` logs: + +```text +get flood.retry.ignore +set flood.retry.ignore +``` + +The ignored prefix must match the last hop shown as `heard=`. For example, this log needs `C7618C` in the ignore list: + +```text +flood retry good (... path=7773D0>C7618C, heard=C7618C ...) +``` + +If retries are too aggressive: + +```text +set flood.retry.count 1 +set flood.retry.path 1 +set direct.retry.count 4 +``` + +If retries are too sparse: + +```text +set retry.preset rooftop +set flood.retry.count 3 +set flood.retry.path 2 +``` diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index f6f515ee..42505917 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -847,10 +847,10 @@ bool MyMesh::allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_ho return true; } uint8_t MyMesh::getDirectRetryPreset() const { - if (_prefs.direct_retry_preset <= DIRECT_RETRY_PRESET_MOBILE) { - return _prefs.direct_retry_preset; + if (_prefs.retry_preset <= RETRY_PRESET_MOBILE) { + return _prefs.retry_preset; } - return DIRECT_RETRY_PRESET_ROOFTOP; + return RETRY_PRESET_ROOFTOP; } uint8_t MyMesh::getDirectRetryConfiguredMaxAttempts() const { return constrain(_prefs.direct_retry_attempts, (uint8_t)1, (uint8_t)15); @@ -1099,6 +1099,9 @@ bool MyMesh::allowFloodRetry(const mesh::Packet* packet) const { if (_prefs.disable_fwd || constrain(_prefs.flood_retry_attempts, (uint8_t)0, (uint8_t)3) == 0) { return false; } + if (packet != NULL && packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && !_prefs.flood_retry_advert_enabled) { + return false; + } if (!_prefs.flood_retry_bridge_enabled) { return true; } @@ -1750,10 +1753,11 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.direct_retry_attempts = DIRECT_RETRY_ROOFTOP_COUNT; _prefs.direct_retry_base_ms = DIRECT_RETRY_ROOFTOP_BASE_MS; _prefs.direct_retry_step_ms = DIRECT_RETRY_ROOFTOP_STEP_MS; - _prefs.direct_retry_preset = DIRECT_RETRY_PRESET_ROOFTOP; + _prefs.retry_preset = RETRY_PRESET_ROOFTOP; _prefs.flood_retry_attempts = 3; _prefs.flood_retry_path_gate = 2; _prefs.flood_retry_bridge_enabled = 0; + _prefs.flood_retry_advert_enabled = 0; StrHelper::strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name)); _prefs.node_lat = ADVERT_LAT; _prefs.node_lon = ADVERT_LON; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index ab4513ca..136451cd 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -29,11 +29,12 @@ #define DIRECT_RETRY_STEP_MS_DEFAULT DIRECT_RETRY_ROOFTOP_STEP_MS #define DIRECT_RETRY_STEP_MS_MIN 0 #define DIRECT_RETRY_STEP_MS_MAX 5000 -#define DIRECT_RETRY_PRESET_DEFAULT DIRECT_RETRY_PRESET_ROOFTOP +#define RETRY_PRESET_DEFAULT RETRY_PRESET_ROOFTOP #define FLOOD_RETRY_PREFS_MAGIC_0 0xF4 #define FLOOD_RETRY_PREFS_MAGIC_1 0x52 #define FLOOD_RETRY_COUNT_MIN 0 #define FLOOD_RETRY_COUNT_MAX 3 +#define FLOOD_RETRY_ADVERT_DEFAULT 0 // Believe it or not, this std C function is busted on some platforms! static uint32_t _atoi(const char* sp) { @@ -78,20 +79,20 @@ static float directRetryMarginX4ToDb(uint8_t margin_x4) { return ((float)margin_x4) / 4.0f; } -static uint8_t directRetryPresetOrDefault(uint8_t preset) { - if (preset <= DIRECT_RETRY_PRESET_MOBILE) { +static uint8_t retryPresetOrDefault(uint8_t preset) { + if (preset <= RETRY_PRESET_MOBILE) { return preset; } - return DIRECT_RETRY_PRESET_DEFAULT; + return RETRY_PRESET_DEFAULT; } -static const char* directRetryPresetName(uint8_t preset) { - switch (directRetryPresetOrDefault(preset)) { - case DIRECT_RETRY_PRESET_INFRA: +static const char* retryPresetName(uint8_t preset) { + switch (retryPresetOrDefault(preset)) { + case RETRY_PRESET_INFRA: return "infra"; - case DIRECT_RETRY_PRESET_MOBILE: + case RETRY_PRESET_MOBILE: return "mobile"; - case DIRECT_RETRY_PRESET_ROOFTOP: + case RETRY_PRESET_ROOFTOP: default: return "rooftop"; } @@ -113,36 +114,36 @@ static uint8_t directRetryEffectiveMarginX4(const NodePrefs* prefs) { return constrain(prefs->direct_retry_snr_margin_db, (uint8_t)0, (uint8_t)DIRECT_RETRY_SNR_MARGIN_X4_MAX); } -static uint16_t directRetryPresetStepDefault(uint8_t preset) { - switch (directRetryPresetOrDefault(preset)) { - case DIRECT_RETRY_PRESET_INFRA: +static uint16_t retryPresetStepDefault(uint8_t preset) { + switch (retryPresetOrDefault(preset)) { + case RETRY_PRESET_INFRA: return DIRECT_RETRY_INFRA_STEP_MS; - case DIRECT_RETRY_PRESET_MOBILE: + case RETRY_PRESET_MOBILE: return DIRECT_RETRY_MOBILE_STEP_MS; - case DIRECT_RETRY_PRESET_ROOFTOP: + case RETRY_PRESET_ROOFTOP: default: return DIRECT_RETRY_ROOFTOP_STEP_MS; } } static uint8_t floodRetryPresetCountDefault(uint8_t preset) { - switch (directRetryPresetOrDefault(preset)) { - case DIRECT_RETRY_PRESET_INFRA: + switch (retryPresetOrDefault(preset)) { + case RETRY_PRESET_INFRA: return 1; - case DIRECT_RETRY_PRESET_MOBILE: - case DIRECT_RETRY_PRESET_ROOFTOP: + case RETRY_PRESET_MOBILE: + case RETRY_PRESET_ROOFTOP: default: return 3; } } static uint8_t floodRetryPresetPathDefault(uint8_t preset) { - switch (directRetryPresetOrDefault(preset)) { - case DIRECT_RETRY_PRESET_INFRA: + switch (retryPresetOrDefault(preset)) { + case RETRY_PRESET_INFRA: return 1; - case DIRECT_RETRY_PRESET_MOBILE: + case RETRY_PRESET_MOBILE: return 1; - case DIRECT_RETRY_PRESET_ROOFTOP: + case RETRY_PRESET_ROOFTOP: default: return 2; } @@ -157,33 +158,22 @@ static uint8_t floodRetryEffectiveCount(const NodePrefs* prefs) { return constrain(prefs->flood_retry_attempts, (uint8_t)FLOOD_RETRY_COUNT_MIN, (uint8_t)FLOOD_RETRY_COUNT_MAX); } -static uint8_t floodRetryPresetForPrefs(const NodePrefs* prefs) { - uint8_t count = floodRetryEffectiveCount(prefs); - uint8_t path_gate = prefs->flood_retry_path_gate; - for (uint8_t preset = DIRECT_RETRY_PRESET_INFRA; preset <= DIRECT_RETRY_PRESET_MOBILE; preset++) { - if (count == floodRetryPresetCountDefault(preset) && path_gate == floodRetryPresetPathDefault(preset)) { - return preset; - } - } - return directRetryPresetOrDefault(prefs->direct_retry_preset); -} - -static void applyDirectRetryPreset(NodePrefs* prefs, uint8_t preset) { - prefs->direct_retry_preset = directRetryPresetOrDefault(preset); - switch (prefs->direct_retry_preset) { - case DIRECT_RETRY_PRESET_INFRA: +static void applyRetryPreset(NodePrefs* prefs, uint8_t preset) { + prefs->retry_preset = retryPresetOrDefault(preset); + switch (prefs->retry_preset) { + case RETRY_PRESET_INFRA: prefs->direct_retry_base_ms = DIRECT_RETRY_INFRA_BASE_MS; prefs->direct_retry_attempts = DIRECT_RETRY_INFRA_COUNT; prefs->direct_retry_step_ms = DIRECT_RETRY_INFRA_STEP_MS; prefs->direct_retry_snr_margin_db = DIRECT_RETRY_INFRA_MARGIN_X4; break; - case DIRECT_RETRY_PRESET_MOBILE: + case RETRY_PRESET_MOBILE: prefs->direct_retry_base_ms = DIRECT_RETRY_MOBILE_BASE_MS; prefs->direct_retry_attempts = DIRECT_RETRY_MOBILE_COUNT; prefs->direct_retry_step_ms = DIRECT_RETRY_MOBILE_STEP_MS; prefs->direct_retry_snr_margin_db = DIRECT_RETRY_MOBILE_MARGIN_X4; break; - case DIRECT_RETRY_PRESET_ROOFTOP: + case RETRY_PRESET_ROOFTOP: default: prefs->direct_retry_base_ms = DIRECT_RETRY_ROOFTOP_BASE_MS; prefs->direct_retry_attempts = DIRECT_RETRY_ROOFTOP_COUNT; @@ -191,7 +181,13 @@ static void applyDirectRetryPreset(NodePrefs* prefs, uint8_t preset) { prefs->direct_retry_snr_margin_db = DIRECT_RETRY_ROOFTOP_MARGIN_X4; break; } - applyFloodRetryPreset(prefs, prefs->direct_retry_preset); + applyFloodRetryPreset(prefs, prefs->retry_preset); +} + +static void formatRetryPreset(char* reply, const NodePrefs* prefs) { + sprintf(reply, "> %d,%s", + (uint32_t)retryPresetOrDefault(prefs->retry_preset), + retryPresetName(prefs->retry_preset)); } static bool parseFloodRetryPathGate(const char* value, uint8_t& path_gate) { @@ -316,21 +312,21 @@ static bool parseFloodRetryPrefixList(uint8_t dest[][FLOOD_RETRY_PREFIX_LEN], ui return true; } -static bool parseDirectRetryPreset(const char* value, uint8_t& preset) { +static bool parseRetryPreset(const char* value, uint8_t& preset) { if (value == NULL) { return false; } if (strcmp(value, "0") == 0 || strcmp(value, "infra") == 0 || strcmp(value, "infa") == 0 || strcmp(value, "infrastructure") == 0) { - preset = DIRECT_RETRY_PRESET_INFRA; + preset = RETRY_PRESET_INFRA; return true; } if (strcmp(value, "1") == 0 || strcmp(value, "rooftop") == 0) { - preset = DIRECT_RETRY_PRESET_ROOFTOP; + preset = RETRY_PRESET_ROOFTOP; return true; } if (strcmp(value, "2") == 0 || strcmp(value, "mobile") == 0) { - preset = DIRECT_RETRY_PRESET_MOBILE; + preset = RETRY_PRESET_MOBILE; return true; } return false; @@ -418,7 +414,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->direct_retry_timing_magic[0], sizeof(_prefs->direct_retry_timing_magic)); // 294 size_t radio_fem_rxgain_read = file.read((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 296 - file.read((uint8_t *)&_prefs->direct_retry_preset, sizeof(_prefs->direct_retry_preset)); // 297 + file.read((uint8_t *)&_prefs->retry_preset, sizeof(_prefs->retry_preset)); // 297 size_t retry_step_read = file.read((uint8_t *)&_prefs->direct_retry_step_ms, sizeof(_prefs->direct_retry_step_ms)); // 298 size_t flood_retry_attempts_read = file.read((uint8_t *)&_prefs->flood_retry_attempts, @@ -432,6 +428,9 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->flood_retry_bridge_buckets[0][0][0], sizeof(_prefs->flood_retry_bridge_buckets)); // 329 size_t flood_retry_ignore_read = file.read((uint8_t *)&_prefs->flood_retry_ignore_prefixes[0][0], sizeof(_prefs->flood_retry_ignore_prefixes)); // 635 + _prefs->flood_retry_advert_enabled = FLOOD_RETRY_ADVERT_DEFAULT; + size_t flood_retry_advert_read = file.read((uint8_t *)&_prefs->flood_retry_advert_enabled, + sizeof(_prefs->flood_retry_advert_enabled)); // 659 // PowerSaving-only prefs stored radio_fem_rxgain at 291, before direct retry timing existed. if (radio_fem_rxgain_read != sizeof(_prefs->radio_fem_rxgain) && legacy_retry_attempts_read == sizeof(legacy_retry_attempts_or_radio_fem_rxgain) @@ -487,29 +486,35 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { // sanitise settings _prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean _prefs->radio_fem_rxgain = constrain(_prefs->radio_fem_rxgain, 0, 1); // boolean - _prefs->direct_retry_preset = directRetryPresetOrDefault(_prefs->direct_retry_preset); + _prefs->retry_preset = retryPresetOrDefault(_prefs->retry_preset); if (retry_step_read != sizeof(_prefs->direct_retry_step_ms)) { - _prefs->direct_retry_step_ms = directRetryPresetStepDefault(_prefs->direct_retry_preset); + _prefs->direct_retry_step_ms = retryPresetStepDefault(_prefs->retry_preset); } else { _prefs->direct_retry_step_ms = constrain(_prefs->direct_retry_step_ms, DIRECT_RETRY_STEP_MS_MIN, DIRECT_RETRY_STEP_MS_MAX); } if (flood_retry_attempts_read != sizeof(_prefs->flood_retry_attempts) || _prefs->flood_retry_prefs_magic[0] != FLOOD_RETRY_PREFS_MAGIC_0 || _prefs->flood_retry_prefs_magic[1] != FLOOD_RETRY_PREFS_MAGIC_1) { - applyFloodRetryPreset(_prefs, _prefs->direct_retry_preset); + applyFloodRetryPreset(_prefs, _prefs->retry_preset); memset(_prefs->flood_retry_prefixes, 0, sizeof(_prefs->flood_retry_prefixes)); _prefs->flood_retry_bridge_enabled = 0; memset(_prefs->flood_retry_bridge_buckets, 0, sizeof(_prefs->flood_retry_bridge_buckets)); memset(_prefs->flood_retry_ignore_prefixes, 0, sizeof(_prefs->flood_retry_ignore_prefixes)); + _prefs->flood_retry_advert_enabled = FLOOD_RETRY_ADVERT_DEFAULT; } else { _prefs->flood_retry_attempts = constrain(_prefs->flood_retry_attempts, FLOOD_RETRY_COUNT_MIN, FLOOD_RETRY_COUNT_MAX); if (_prefs->flood_retry_path_gate > 63 && _prefs->flood_retry_path_gate != FLOOD_RETRY_PATH_GATE_DISABLED) { - _prefs->flood_retry_path_gate = floodRetryPresetPathDefault(_prefs->direct_retry_preset); + _prefs->flood_retry_path_gate = floodRetryPresetPathDefault(_prefs->retry_preset); } _prefs->flood_retry_bridge_enabled = constrain(_prefs->flood_retry_bridge_enabled, 0, 1); if (flood_retry_ignore_read != sizeof(_prefs->flood_retry_ignore_prefixes)) { memset(_prefs->flood_retry_ignore_prefixes, 0, sizeof(_prefs->flood_retry_ignore_prefixes)); } + if (flood_retry_advert_read != sizeof(_prefs->flood_retry_advert_enabled)) { + _prefs->flood_retry_advert_enabled = FLOOD_RETRY_ADVERT_DEFAULT; + } else { + _prefs->flood_retry_advert_enabled = constrain(_prefs->flood_retry_advert_enabled, 0, 1); + } } file.close(); @@ -581,7 +586,7 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { uint8_t retry_timing_magic[2] = { DIRECT_RETRY_TIMING_MAGIC_0, DIRECT_RETRY_TIMING_MAGIC_1 }; file.write(retry_timing_magic, sizeof(retry_timing_magic)); // 294 file.write((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 296 - file.write((uint8_t *)&_prefs->direct_retry_preset, sizeof(_prefs->direct_retry_preset)); // 297 + file.write((uint8_t *)&_prefs->retry_preset, sizeof(_prefs->retry_preset)); // 297 file.write((uint8_t *)&_prefs->direct_retry_step_ms, sizeof(_prefs->direct_retry_step_ms)); // 298 file.write((uint8_t *)&_prefs->flood_retry_attempts, sizeof(_prefs->flood_retry_attempts)); // 300 file.write((uint8_t *)&_prefs->flood_retry_path_gate, sizeof(_prefs->flood_retry_path_gate)); // 301 @@ -591,7 +596,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->flood_retry_bridge_enabled, sizeof(_prefs->flood_retry_bridge_enabled)); // 328 file.write((uint8_t *)&_prefs->flood_retry_bridge_buckets[0][0][0], sizeof(_prefs->flood_retry_bridge_buckets)); // 329 file.write((uint8_t *)&_prefs->flood_retry_ignore_prefixes[0][0], sizeof(_prefs->flood_retry_ignore_prefixes)); // 635 - // next: 659 + file.write((uint8_t *)&_prefs->flood_retry_advert_enabled, sizeof(_prefs->flood_retry_advert_enabled)); // 659 + // next: 660 file.close(); } @@ -747,6 +753,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re sprintf(reply, "> %s", StrHelper::ftoa(_prefs->tx_delay_factor)); } else if (memcmp(config, "flood.max", 9) == 0) { sprintf(reply, "> %d", (uint32_t)_prefs->flood_max); + } else if (memcmp(config, "retry.preset", 12) == 0) { + formatRetryPreset(reply, _prefs); } else if (memcmp(config, "flood.retry.count", 17) == 0) { sprintf(reply, "> %d", (uint32_t)floodRetryEffectiveCount(_prefs)); } else if (memcmp(config, "flood.retry.path", 16) == 0) { @@ -759,6 +767,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else if (memcmp(config, "flood.retry.ignore", 18) == 0) { formatFloodRetryPrefixList(tmp, _prefs->flood_retry_ignore_prefixes, FLOOD_RETRY_IGNORE_PREFIXES); sprintf(reply, "> %s", tmp[0] ? tmp : "none"); + } else if (memcmp(config, "flood.retry.advert", 18) == 0) { + sprintf(reply, "> %s", _prefs->flood_retry_advert_enabled ? "on" : "off"); } else if (memcmp(config, "flood.retry.bridge", 18) == 0) { sprintf(reply, "> %s", _prefs->flood_retry_bridge_enabled ? "on" : "off"); } else if (memcmp(config, "flood.retry.bucket.", 19) == 0) { @@ -769,21 +779,12 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else { sprintf(reply, "Error, bucket 1-%d", FLOOD_RETRY_BRIDGE_BUCKETS); } - } else if (memcmp(config, "flood.retry.preset", 18) == 0) { - uint8_t preset = floodRetryPresetForPrefs(_prefs); - sprintf(reply, "> %d,%s", - (uint32_t)preset, - directRetryPresetName(preset)); } else if (memcmp(config, "direct.txdelay", 14) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor)); } else if (memcmp(config, "direct.retry.heard", 18) == 0) { sprintf(reply, "> %s", _prefs->direct_retry_recent_enabled ? "on" : "off"); } else if (memcmp(config, "direct.retry.margin", 19) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(directRetryMarginX4ToDb(directRetryEffectiveMarginX4(_prefs)))); - } else if (memcmp(config, "direct.retry.preset", 19) == 0) { - sprintf(reply, "> %d,%s", - (uint32_t)directRetryPresetOrDefault(_prefs->direct_retry_preset), - directRetryPresetName(_prefs->direct_retry_preset)); } else if (memcmp(config, "direct.retry.count", 18) == 0) { sprintf(reply, "> %d", (uint32_t)directRetryEffectiveCount(_prefs)); } else if (memcmp(config, "direct.retry.base", 17) == 0) { @@ -1030,10 +1031,10 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else { strcpy(reply, "Error, max 64"); } - } else if (memcmp(config, "flood.retry.preset ", 19) == 0) { + } else if (memcmp(config, "retry.preset ", 13) == 0) { uint8_t preset; - if (parseDirectRetryPreset(&config[19], preset)) { - applyFloodRetryPreset(_prefs, preset); + if (parseRetryPreset(&config[13], preset)) { + applyRetryPreset(_prefs, preset); savePrefs(); strcpy(reply, "OK"); } else { @@ -1073,6 +1074,18 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re sprintf(reply, "Error, use up to %u comma-separated 3-byte hex prefixes", (unsigned int)FLOOD_RETRY_IGNORE_PREFIXES); } + } else if (memcmp(config, "flood.retry.advert ", 19) == 0) { + if (memcmp(&config[19], "on", 2) == 0) { + _prefs->flood_retry_advert_enabled = 1; + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(&config[19], "off", 3) == 0) { + _prefs->flood_retry_advert_enabled = 0; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be on or off"); + } } else if (memcmp(config, "flood.retry.bridge ", 19) == 0) { if (memcmp(&config[19], "on", 2) == 0) { _prefs->flood_retry_bridge_enabled = 1; @@ -1129,15 +1142,6 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else { sprintf(reply, "Error, min 0 and max %d", DIRECT_RETRY_SNR_MARGIN_DB_MAX); } - } else if (memcmp(config, "direct.retry.preset ", 20) == 0) { - uint8_t preset; - if (parseDirectRetryPreset(&config[20], preset)) { - applyDirectRetryPreset(_prefs, preset); - savePrefs(); - strcpy(reply, "OK"); - } else { - strcpy(reply, "Error, must be infra, rooftop, mobile, 0, 1, or 2"); - } } else if (memcmp(config, "direct.retry.count ", 19) == 0) { int count = atoi(&config[19]); if (count >= DIRECT_RETRY_COUNT_MIN && count <= DIRECT_RETRY_COUNT_MAX) { @@ -1653,10 +1657,10 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } else { strcpy(reply, "Error, max 64"); } - } else if (memcmp(config, "flood.retry.preset ", 19) == 0) { + } else if (memcmp(config, "retry.preset ", 13) == 0) { uint8_t preset; - if (parseDirectRetryPreset(&config[19], preset)) { - applyFloodRetryPreset(_prefs, preset); + if (parseRetryPreset(&config[13], preset)) { + applyRetryPreset(_prefs, preset); savePrefs(); strcpy(reply, "OK"); } else { @@ -1696,6 +1700,18 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep sprintf(reply, "Error, use up to %u comma-separated 3-byte hex prefixes", (unsigned int)FLOOD_RETRY_IGNORE_PREFIXES); } + } else if (memcmp(config, "flood.retry.advert ", 19) == 0) { + if (memcmp(&config[19], "on", 2) == 0) { + _prefs->flood_retry_advert_enabled = 1; + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(&config[19], "off", 3) == 0) { + _prefs->flood_retry_advert_enabled = 0; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error, must be on or off"); + } } else if (memcmp(config, "flood.retry.bridge ", 19) == 0) { if (memcmp(&config[19], "on", 2) == 0) { _prefs->flood_retry_bridge_enabled = 1; @@ -1752,15 +1768,6 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } else { sprintf(reply, "Error, min 0 and max %d", DIRECT_RETRY_SNR_MARGIN_DB_MAX); } - } else if (memcmp(config, "direct.retry.preset ", 20) == 0) { - uint8_t preset; - if (parseDirectRetryPreset(&config[20], preset)) { - applyDirectRetryPreset(_prefs, preset); - savePrefs(); - strcpy(reply, "OK"); - } else { - strcpy(reply, "Error, must be infra, rooftop, mobile, 0, 1, or 2"); - } } else if (memcmp(config, "direct.retry.count ", 19) == 0) { int count = atoi(&config[19]); if (count >= DIRECT_RETRY_COUNT_MIN && count <= DIRECT_RETRY_COUNT_MAX) { @@ -1961,6 +1968,8 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep sprintf(reply, "> %s", StrHelper::ftoa(_prefs->tx_delay_factor)); } else if (memcmp(config, "flood.max", 9) == 0) { sprintf(reply, "> %d", (uint32_t)_prefs->flood_max); + } else if (memcmp(config, "retry.preset", 12) == 0) { + formatRetryPreset(reply, _prefs); } else if (memcmp(config, "flood.retry.count", 17) == 0) { sprintf(reply, "> %d", (uint32_t)floodRetryEffectiveCount(_prefs)); } else if (memcmp(config, "flood.retry.path", 16) == 0) { @@ -1973,6 +1982,8 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep } else if (memcmp(config, "flood.retry.ignore", 18) == 0) { formatFloodRetryPrefixList(tmp, _prefs->flood_retry_ignore_prefixes, FLOOD_RETRY_IGNORE_PREFIXES); sprintf(reply, "> %s", tmp[0] ? tmp : "none"); + } else if (memcmp(config, "flood.retry.advert", 18) == 0) { + sprintf(reply, "> %s", _prefs->flood_retry_advert_enabled ? "on" : "off"); } else if (memcmp(config, "flood.retry.bridge", 18) == 0) { sprintf(reply, "> %s", _prefs->flood_retry_bridge_enabled ? "on" : "off"); } else if (memcmp(config, "flood.retry.bucket.", 19) == 0) { @@ -1983,21 +1994,12 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep } else { sprintf(reply, "Error, bucket 1-%d", FLOOD_RETRY_BRIDGE_BUCKETS); } - } else if (memcmp(config, "flood.retry.preset", 18) == 0) { - uint8_t preset = floodRetryPresetForPrefs(_prefs); - sprintf(reply, "> %d,%s", - (uint32_t)preset, - directRetryPresetName(preset)); } else if (memcmp(config, "direct.txdelay", 14) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor)); } else if (memcmp(config, "direct.retry.heard", 18) == 0) { sprintf(reply, "> %s", _prefs->direct_retry_recent_enabled ? "on" : "off"); } else if (memcmp(config, "direct.retry.margin", 19) == 0) { sprintf(reply, "> %s", StrHelper::ftoa(directRetryMarginX4ToDb(directRetryEffectiveMarginX4(_prefs)))); - } else if (memcmp(config, "direct.retry.preset", 19) == 0) { - sprintf(reply, "> %d,%s", - (uint32_t)directRetryPresetOrDefault(_prefs->direct_retry_preset), - directRetryPresetName(_prefs->direct_retry_preset)); } else if (memcmp(config, "direct.retry.count", 18) == 0) { sprintf(reply, "> %d", (uint32_t)directRetryEffectiveCount(_prefs)); } else if (memcmp(config, "direct.retry.base", 17) == 0) { diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 40806d49..5c1eb62f 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -19,9 +19,9 @@ #define LOOP_DETECT_MODERATE 2 #define LOOP_DETECT_STRICT 3 -#define DIRECT_RETRY_PRESET_INFRA 0 -#define DIRECT_RETRY_PRESET_ROOFTOP 1 -#define DIRECT_RETRY_PRESET_MOBILE 2 +#define RETRY_PRESET_INFRA 0 +#define RETRY_PRESET_ROOFTOP 1 +#define RETRY_PRESET_MOBILE 2 #define DIRECT_RETRY_INFRA_BASE_MS 275 #define DIRECT_RETRY_INFRA_COUNT 4 @@ -111,7 +111,7 @@ struct NodePrefs { // persisted to file uint8_t direct_retry_attempts; uint16_t direct_retry_base_ms; uint8_t direct_retry_timing_magic[2]; - uint8_t direct_retry_preset; + uint8_t retry_preset; uint16_t direct_retry_step_ms; uint8_t flood_retry_attempts; uint8_t flood_retry_path_gate; @@ -120,6 +120,7 @@ struct NodePrefs { // persisted to file uint8_t flood_retry_bridge_enabled; uint8_t flood_retry_bridge_buckets[FLOOD_RETRY_BRIDGE_BUCKETS][FLOOD_RETRY_BUCKET_PREFIXES][FLOOD_RETRY_PREFIX_LEN]; uint8_t flood_retry_ignore_prefixes[FLOOD_RETRY_IGNORE_PREFIXES][FLOOD_RETRY_PREFIX_LEN]; + uint8_t flood_retry_advert_enabled; }; class CommonCLICallbacks {