mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-08-02 18:26:11 +00:00
feat(repeater): politeness controls, dedicated radio profile, and diagnostics
Settings > Radio gains a repeater toggle, 16 community-suggested radio presets plus manual Freq/SF/BW/CR tuning (digit-by-digit Freq editor), 4 persisted user preset slots, and the packet pool bumped 16->32 so queued retransmits stop starving incoming-packet allocation while relaying. Four opt-in politeness knobs (skip-advert, max-hops, yield, min-SNR), all flood-only and off by default, plus overhear suppression that cancels a queued retransmit if a peer relays the same packet first. Adaptive Power Control is suppressed while relaying (pins TX power to the ceiling) and duty-cycle RX is forced off, since a repeater needs to hear and relay at consistent power. A dedicated Tools > Repeater screen consolidates the toggle, the politeness knobs, and live forwarding stats, plus an optional "Custom" radio profile — a dedicated frequency/SF/BW/CR for relaying, separate from the companion's own network, band-matched to the companion frequency by default and used everywhere a radio change can happen (boot, on-device toggle, app-driven CMD_SET_RADIO_PARAMS, Settings radio edits) so the device never silently falls back to the wrong params mid-relay. Diagnostics gains the actually-forwarded packet count, real heap-free via mallinfo(), a reset-counters popup, and hardened loop-detect bounds. Also: a frequency-floor fix and a float-equality preset-match fix in the repeater profile logic, deduped BW-table/valCol/profile- seeding helpers shared between Settings and the Repeater screen, and a build.sh fix tolerating control characters in `pio project config`'s JSON dump. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
327e659d51
commit
32c50d1cfe
@@ -2,6 +2,37 @@
|
||||
#include <cstdint>
|
||||
#include <stdio.h>
|
||||
|
||||
// Firmware's own boot-default radio params — used both as the companion's
|
||||
// initial freq/sf/bw/cr (MyMesh.cpp) and, here, as the seed for a never-
|
||||
// configured repeater profile (DataStore.cpp). Kept above any per-board
|
||||
// target.h include so a board can still override via -D before this file
|
||||
// is reached.
|
||||
#ifndef LORA_FREQ
|
||||
#define LORA_FREQ 915.0
|
||||
#endif
|
||||
#ifndef LORA_BW
|
||||
#define LORA_BW 250
|
||||
#endif
|
||||
#ifndef LORA_SF
|
||||
#define LORA_SF 10
|
||||
#endif
|
||||
#ifndef LORA_CR
|
||||
#define LORA_CR 5
|
||||
#endif
|
||||
|
||||
// Bucket a companion frequency into whichever of the three license-exempt
|
||||
// bands MeshCore's app-driven repeat toggle historically restricted to (see
|
||||
// repeat_freq_ranges in MyMesh.cpp: 433.000 / 869.495 / 918.000 MHz). Used to
|
||||
// seed a never-configured repeater profile in the same legal band as the
|
||||
// companion's own network (MyMesh.cpp begin(), DataStore.cpp) — a flat
|
||||
// single frequency could land outside the bands allowed where the operator
|
||||
// actually lives.
|
||||
static inline float defaultRepeaterFreqForBand(float companion_freq) {
|
||||
if (companion_freq < 500.0f) return 433.000f;
|
||||
if (companion_freq < 890.0f) return 869.495f;
|
||||
return 918.000f;
|
||||
}
|
||||
|
||||
#define TELEM_MODE_DENY 0
|
||||
#define TELEM_MODE_ALLOW_FLAGS 1 // use contact.flags
|
||||
#define TELEM_MODE_ALLOW_ALL 2
|
||||
@@ -140,11 +171,58 @@ struct NodePrefs { // persisted to file
|
||||
// shows ✗. 0 = no auto-resend (single attempt). Default 2.
|
||||
uint8_t dm_resend_count;
|
||||
|
||||
// User-saved radio presets (Settings > Radio > Preset > "Save current...").
|
||||
// name[0] == '\0' marks an empty slot.
|
||||
struct UserRadioPreset {
|
||||
char name[16];
|
||||
float freq;
|
||||
float bw;
|
||||
uint8_t sf;
|
||||
uint8_t cr;
|
||||
};
|
||||
static const uint8_t USER_RADIO_PRESET_MAX = 4;
|
||||
UserRadioPreset user_radio_presets[USER_RADIO_PRESET_MAX];
|
||||
|
||||
// Repeater "politeness" — only consulted when client_repeat is on, via
|
||||
// MyMesh::allowPacketForward(). Both default to off (0) so behaviour is
|
||||
// unchanged until the user opts in (Settings > Radio).
|
||||
// repeat_skip_adverts: 1 = don't re-flood ADVERT packets (the highest-volume
|
||||
// flood traffic); messages/acks still relay.
|
||||
// repeat_max_hops: drop a flood packet once it has already travelled this
|
||||
// many hops. 0 = no hop limit.
|
||||
// repeat_delay_boost: extra retransmit-delay multiplier for FORWARDED floods
|
||||
// only (own sends are unaffected) — a mobile companion yields to better-sited
|
||||
// fixed repeaters. Effective delay = base * (1 + repeat_delay_boost). 0 = off.
|
||||
// repeat_min_snr: drop a flood packet received below this SNR (dB), so marginal
|
||||
// fringe traffic isn't re-flooded. REPEAT_SNR_DISABLED (-128) = off.
|
||||
// repeat_suppress_dup: 1 = cancel a queued retransmit when the same flood is
|
||||
// overheard from another node first (less redundant airtime in dense mesh).
|
||||
uint8_t repeat_skip_adverts;
|
||||
uint8_t repeat_max_hops;
|
||||
uint8_t repeat_delay_boost;
|
||||
int8_t repeat_min_snr;
|
||||
static const int8_t REPEAT_SNR_DISABLED = -128;
|
||||
uint8_t repeat_suppress_dup;
|
||||
|
||||
// Optional dedicated radio profile for repeater mode. When repeater_use_profile
|
||||
// is 1, enabling the repeater switches the radio to repeater_freq/bw/sf/cr and
|
||||
// disabling restores the companion's freq/bw/sf/cr (the fields above). 0 = the
|
||||
// repeater stays on the current companion frequency. Default (a never-configured
|
||||
// device) is 1, seeded via defaultRepeaterFreqForBand(freq) + LORA_SF/BW/CR —
|
||||
// repeating on whatever private network the companion later joins isn't the
|
||||
// community norm, and the seed stays in the same legal band as the companion's
|
||||
// own network rather than a flat frequency.
|
||||
uint8_t repeater_use_profile;
|
||||
float repeater_freq;
|
||||
float repeater_bw;
|
||||
uint8_t repeater_sf;
|
||||
uint8_t repeater_cr;
|
||||
|
||||
// Tail sentinel written at the end of /new_prefs. Bump the low byte when
|
||||
// adding/removing/reordering fields in DataStore::savePrefs/loadPrefsInt so
|
||||
// older saves are detected on load and skipped (zero-init defaults kept).
|
||||
// High 24 bits identify the file format; low byte is the schema revision.
|
||||
static const uint32_t SCHEMA_SENTINEL = 0xC0DE000C;
|
||||
static const uint32_t SCHEMA_SENTINEL = 0xC0DE0010;
|
||||
|
||||
// Bit-index for each home page. Used by page_order (entries store bit+1) and
|
||||
// by home_pages_mask. Single source of truth — both HomeScreen::pageBit/bitToPage
|
||||
@@ -213,3 +291,23 @@ struct NodePrefs { // persisted to file
|
||||
if (pos < size) buf[pos] = '\0';
|
||||
}
|
||||
};
|
||||
|
||||
// Bounds for a usable repeater radio profile — must match the radio chip's own
|
||||
// validated range (see CustomSX1262Wrapper::getFreqBounds(), 150-960 MHz), so
|
||||
// a profile that passes here is guaranteed to actually take effect on the radio.
|
||||
static inline bool isValidRepeaterProfile(float freq, float bw, uint8_t sf, uint8_t cr) {
|
||||
return freq >= 150.0f && freq <= 960.0f
|
||||
&& sf >= 5 && sf <= 12
|
||||
&& cr >= 5 && cr <= 8
|
||||
&& bw >= 7.0f && bw <= 510.0f;
|
||||
}
|
||||
|
||||
// Seed a never-configured repeater profile in the same band as the companion's
|
||||
// own network (see defaultRepeaterFreqForBand() above for why).
|
||||
static inline void seedDefaultRepeaterProfile(NodePrefs& prefs) {
|
||||
prefs.repeater_use_profile = 1;
|
||||
prefs.repeater_freq = defaultRepeaterFreqForBand(prefs.freq);
|
||||
prefs.repeater_bw = LORA_BW;
|
||||
prefs.repeater_sf = LORA_SF;
|
||||
prefs.repeater_cr = LORA_CR;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user