mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-09-02 08:43:43 +00:00
205 lines
9.8 KiB
C
205 lines
9.8 KiB
C
/*
|
||
* SPDX-License-Identifier: MIT
|
||
* NodePrefs - persisted node configuration (unified for all roles)
|
||
*
|
||
* Serialized field-by-field, not raw memcpy; struct layout does
|
||
* not affect on-disk compatibility.
|
||
*/
|
||
|
||
#pragma once
|
||
|
||
#include <stdint.h>
|
||
#include <string.h>
|
||
|
||
#define TELEM_MODE_DENY 0
|
||
#define TELEM_MODE_ALLOW_FLAGS 1
|
||
#define TELEM_MODE_ALLOW_ALL 2
|
||
|
||
#define ADVERT_LOC_NONE 0
|
||
#define ADVERT_LOC_SHARE 1
|
||
#define ADVERT_LOC_PREFS 2
|
||
|
||
#define LOOP_DETECT_OFF 0
|
||
#define LOOP_DETECT_MINIMAL 1
|
||
#define LOOP_DETECT_MODERATE 2
|
||
#define LOOP_DETECT_STRICT 3
|
||
|
||
/* Adaptive-CAD operating detPeak offset range (levels from the family base).
|
||
* Wide on purpose: a dense hilltop can need a much higher detPeak than a quiet
|
||
* valley node. The per-family absolute clamp inside the driver (SX126x 15-40,
|
||
* LR11xx/LR20xx 48-90) is a firmware guardrail, NOT a chip limit — cadDetPeak
|
||
* is a full uint8_t (0-255). It just keeps the staircase from wandering into
|
||
* "CAD never fires" (too high) or "CAD always busy" (too low) territory. This
|
||
* offset range limits how far the staircase / manual offset may roam; MUST
|
||
* match CAD_LEVEL_MIN/MAX in adapters/radio/radio_common.h (they index the
|
||
* per-level stats array). */
|
||
#define CAD_OFFSET_MIN (-8)
|
||
#define CAD_OFFSET_MAX 12
|
||
|
||
/* leds_disabled, as stored in the repeater/room-server/observer prefs layout.
|
||
*
|
||
* It occupies the byte that used to hold agc_reset_interval (offset 120),
|
||
* retired when periodic AGC recalibration was removed. That byte is NOT
|
||
* reusable as a plain 0/1 boolean: the old command stored seconds/4, so a node
|
||
* upgrading from a build that had it configured has an arbitrary small integer
|
||
* sitting there, and a bare non-zero test would silently kill its LEDs. Hence
|
||
* a magic encoding — anything that is not one of these two values is a legacy
|
||
* AGC interval and decodes to the default (LEDs on). The first savePrefs()
|
||
* claims the byte for good.
|
||
*
|
||
* The companion layout is unaffected: it has always stored leds_disabled as a
|
||
* plain 0/1 at its own offset 93. */
|
||
#define LEDS_PREF_ON 0xA0
|
||
#define LEDS_PREF_OFF 0xA1
|
||
|
||
/* Number of LR2021 side-detector slots stored in prefs. Matches the chip's
|
||
* ConfigureSideDetectors limit of 3. */
|
||
#define EXTRA_SF_MAX 3
|
||
|
||
struct NodePrefs {
|
||
/* ---- Common fields (both roles) ---- */
|
||
float airtime_factor;
|
||
char node_name[32];
|
||
double node_lat, node_lon;
|
||
char password[16];
|
||
float freq;
|
||
int8_t tx_power_dbm;
|
||
uint8_t disable_fwd; // repeater: disable forwarding
|
||
uint8_t advert_interval; // stored as minutes / 2
|
||
uint8_t flood_advert_interval; // hours
|
||
float rx_delay_base;
|
||
float tx_delay_factor;
|
||
char guest_password[16];
|
||
float direct_tx_delay_factor;
|
||
float backoff_multiplier; // per-dupe reactive backoff (0.0 = disabled)
|
||
uint32_t guard;
|
||
uint8_t sf;
|
||
uint8_t cr;
|
||
uint8_t allow_read_only;
|
||
uint8_t multi_acks;
|
||
float bw;
|
||
uint8_t flood_max;
|
||
uint8_t flood_max_unscoped; // hop limit for un-scoped (ROUTE_TYPE_FLOOD) floods
|
||
uint8_t flood_max_advert; // hop limit for ADVERT floods (curbs advert churn)
|
||
uint8_t interference_threshold;
|
||
uint8_t leds_disabled; // 1 = all LEDs off (heartbeat, unread, LoRa TX)
|
||
// Power saving
|
||
uint8_t powersaving_enabled;
|
||
// GPS settings
|
||
uint8_t gps_enabled;
|
||
uint32_t gps_interval; // in seconds
|
||
uint8_t advert_loc_policy;
|
||
uint32_t discovery_mod_timestamp;
|
||
float adc_multiplier;
|
||
char owner_info[120];
|
||
uint8_t rx_boost; // 1 = boosted RX gain (+3dB), 0 = power save
|
||
uint8_t rx_duty_cycle; // 1 = RX duty cycle, 0 = continuous RX
|
||
/* RESERVED — formerly apc_enabled / apc_margin (Adaptive Power Control,
|
||
* removed in 1.16.6). These two bytes are still read and written at their
|
||
* original offsets in all three prefs serializers (companion new_prefs 94/95,
|
||
* repeater prefs 292/293, RepeaterDataStore) because every field after them
|
||
* is positional: dropping them would shift the rest of the layout and make
|
||
* every already-deployed node misparse its saved prefs on upgrade.
|
||
* Do not reuse for a new setting — an upgraded node still has the old APC
|
||
* values sitting in these bytes. */
|
||
uint8_t _reserved_apc_enabled;
|
||
uint8_t _reserved_apc_margin;
|
||
uint8_t meshtimesync; // 1 = mesh time-sync clock correction on (default off)
|
||
uint8_t cad_auto; // 1 = adaptive-CAD staircase acts on probe stats (default off = dry-run)
|
||
int8_t cad_offset; // operating detPeak offset from family base (-4..4)
|
||
uint8_t probe_interval; // seconds between periodic radio measurements:
|
||
// one noise-floor sample, and the CAD probe that
|
||
// consumes it (0 = CAD probing off, default 15)
|
||
uint8_t cad_busycap; // airtime-protection: max % of TX attempts deferred before backing off detPeak (0 = off, default 25)
|
||
/* LR2021 side detectors: extra spreading factors received concurrently
|
||
* with `sf`, on the same bandwidth. Zero-terminated list — the first 0
|
||
* ends it, so an all-zero array means the feature is off (which is what
|
||
* every node upgrading from a build without this field has). Every
|
||
* entry must be greater than `sf`, all distinct, spread <= 4; the
|
||
* driver rejects anything else. Ignored on non-LR2021 radios. */
|
||
uint8_t extra_sf[EXTRA_SF_MAX];
|
||
|
||
/* ---- Companion-only fields ---- */
|
||
uint8_t manual_add_contacts;
|
||
uint8_t telemetry_mode_base;
|
||
uint8_t telemetry_mode_loc;
|
||
uint8_t telemetry_mode_env;
|
||
uint32_t ble_pin;
|
||
uint8_t buzzer_quiet;
|
||
uint8_t autoadd_config;
|
||
uint8_t client_repeat; // 1 = offgrid mode (forward packets)
|
||
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}
|
||
char default_scope_name[31]; // companion: default flood scope region name ("" = null)
|
||
uint8_t default_scope_key[16]; // companion: default flood scope TransportKey
|
||
uint8_t ble_disabled; // 1 = BLE advertising off
|
||
uint8_t display_brightness; // 0 = default (100%), else 10–100
|
||
uint8_t wake_on_msg; // 0 = don't wake display on message, 1 = wake (default)
|
||
uint16_t screen_off_secs; // 0 = default (Kconfig), else 5–300
|
||
uint16_t auto_shutdown_mv; // low-batt auto-shutdown threshold; 0 = off, else 2900–4200
|
||
uint8_t v_contact_enabled; // v-contact (loopback admin chat via BLE/USB); 1 = on (default)
|
||
uint16_t v_battery_alert_mv; // 0 = alert off; 0xFFFF = board default (auto_shutdown+200); else mV
|
||
/* App-owned ContactInfo.flags byte for the v-contact. The v-contact never
|
||
* enters the contacts table, so it has no record to hold the flags the app
|
||
* sets via CMD_ADD_UPDATE_CONTACT -- bit 0 is the 'favourite' star, the
|
||
* upper bits are telemetry permissions. Kept here so a toggle survives
|
||
* reconnects and reboots instead of being echoed back as 0. */
|
||
uint8_t v_contact_flags;
|
||
};
|
||
|
||
/* Default prefs -- must match LoRaConfig.h defaults for radio interop. */
|
||
static inline void initNodePrefs(NodePrefs* prefs) {
|
||
memset(prefs, 0, sizeof(NodePrefs));
|
||
prefs->airtime_factor = 9.0f; /* Arduino formula: duty% = 100 / (af + 1) → 10% */
|
||
prefs->node_lat = 0.0;
|
||
prefs->node_lon = 0.0;
|
||
#ifdef CONFIG_ZEPHCORE_ADMIN_PASSWORD
|
||
strncpy(prefs->password, CONFIG_ZEPHCORE_ADMIN_PASSWORD, sizeof(prefs->password) - 1);
|
||
#else
|
||
strcpy(prefs->password, "password");
|
||
#endif
|
||
#ifdef CONFIG_ZEPHCORE_GUEST_PASSWORD
|
||
strncpy(prefs->guest_password, CONFIG_ZEPHCORE_GUEST_PASSWORD, sizeof(prefs->guest_password) - 1);
|
||
#endif
|
||
/* Radio params - MUST match LoRaConfig.h for interop with companion nodes */
|
||
prefs->freq = 869.618f; // LoRaConfig::FREQ_HZ / 1000000.0
|
||
prefs->bw = 62.5f; // LoRaConfig::BANDWIDTH
|
||
prefs->sf = 8; // LoRaConfig::SPREADING_FACTOR
|
||
prefs->cr = 8; // CR 4/8 (MeshCore uses 5-8 for CR 4/5 through 4/8)
|
||
#ifdef CONFIG_ZEPHCORE_DEFAULT_TX_POWER_DBM
|
||
prefs->tx_power_dbm = CONFIG_ZEPHCORE_DEFAULT_TX_POWER_DBM;
|
||
#else
|
||
prefs->tx_power_dbm = 22; // LoRaConfig::TX_POWER_DBM
|
||
#endif
|
||
prefs->disable_fwd = 0;
|
||
prefs->advert_interval = 0; // 0 = periodic local advert off; else minutes = value * 2
|
||
prefs->flood_advert_interval = 47; // hours
|
||
prefs->rx_delay_base = 0.0f;
|
||
prefs->tx_delay_factor = 0.5f;
|
||
prefs->direct_tx_delay_factor = 0.3f;
|
||
prefs->allow_read_only = 0;
|
||
prefs->multi_acks = 0;
|
||
prefs->flood_max = 64; // max hops for flood packets (0 = blocking all!)
|
||
prefs->flood_max_unscoped = 64; // un-scoped flood hop limit (defaults to flood_max)
|
||
prefs->flood_max_advert = 8; // ADVERT flood hop limit (upstream default)
|
||
prefs->interference_threshold = 0;
|
||
prefs->leds_disabled = 0; // LEDs on
|
||
prefs->powersaving_enabled = 0;
|
||
prefs->gps_enabled = 0;
|
||
prefs->gps_interval = 300; // 5 minutes
|
||
prefs->advert_loc_policy = ADVERT_LOC_NONE;
|
||
prefs->adc_multiplier = 0.0f;
|
||
prefs->rx_boost = 1; // Default to boosted RX for better sensitivity
|
||
prefs->rx_duty_cycle = 0; // Default OFF — continuous RX for best reliability
|
||
prefs->_reserved_apc_enabled = 0; // reserved (was APC), see NodePrefs
|
||
prefs->_reserved_apc_margin = 0; // reserved (was APC), see NodePrefs
|
||
prefs->cad_auto = 1; // Default ON — adaptive staircase acts on probe stats
|
||
prefs->cad_offset = 0; // Start at family base detPeak (SF+13 on SX126x)
|
||
prefs->probe_interval = 15; // floor sample + CAD probe; staircase responds in ~1-2 h
|
||
prefs->cad_busycap = 25; // back off detPeak once >25% of TX attempts are deferred
|
||
prefs->wake_on_msg = 1; // Default ON — wake display when message arrives
|
||
prefs->v_contact_enabled = 1; // Default ON — v-contact loopback admin chat (companion)
|
||
prefs->v_battery_alert_mv = 0xFFFF; // Sentinel: derive from board auto-shutdown threshold
|
||
}
|