mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-10-15 13:21:54 +00:00
leds.brightness (PWM-capable boards) was RAM-only since it was added: it always reset to ZEPHCORE_LED_DEFAULT_BRIGHTNESS_PCT (10%) on every boot, regardless of what a user had last set it to. New field led_brightness on NodePrefs, following the exact same append-only pattern as leds_radio_mode/leds_hb_mode added just before it: offset 311 in RepeaterDataStore.cpp (repeater/room-server/observer, shared file), offset 174 in ZephyrDataStore.cpp (companion). Both are backward compatible by construction: a prefs file written before this field existed simply leaves it at the initNodePrefs() default (10%), which is exactly the behaviour every already-deployed node has today -- no special-casing needed to keep "10% on a new/not-yet-upgraded node" as the effective default. CommonCLI.cpp's "set leds.brightness" handler now writes the prefs field and calls savePrefs(), same as the leds.radio/leds.hb handlers right above it; "get leds.brightness" now reads from prefs instead of the RAM getter directly, for the same reason those two already do (always in sync, prefs is the source of truth). The four boot call sites (main_companion.cpp, main_repeater.cpp, main_room_server.cpp, main_observer.cpp) each gained a zephcore_led_set_brightness_pct() call seeding the RAM cache from the persisted value, mirroring the existing leds_radio_mode/leds_hb_mode lines right next to them. Verified building for heltec_t096/nrf52840 (companion) and rak3401_1watt (repeater), the two roles/datastores touched by this change.
386 lines
15 KiB
C++
386 lines
15 KiB
C++
/*
|
|
* SPDX-License-Identifier: MIT
|
|
* PrefsCodec - byte codecs for the two binary prefs files. See PrefsCodec.h.
|
|
*/
|
|
|
|
#include "PrefsCodec.h"
|
|
#include <string.h>
|
|
|
|
namespace {
|
|
|
|
/* Sequential writer / reader over a byte buffer. Rd::get() mirrors fs_read()
|
|
* on a short file, as the server store used to read it: a field that straddles
|
|
* the end gets the bytes that exist. Rd::take() reads whole fields only. Past
|
|
* the end, both leave the field as it was. */
|
|
struct Wr {
|
|
uint8_t *b;
|
|
size_t off;
|
|
void put(const void *src, size_t n) { memcpy(b + off, src, n); off += n; }
|
|
void zeros(size_t n) { memset(b + off, 0, n); off += n; }
|
|
};
|
|
|
|
struct Rd {
|
|
const uint8_t *b;
|
|
size_t len;
|
|
size_t off;
|
|
void get(void *dst, size_t n) {
|
|
size_t k = off < len ? (len - off < n ? len - off : n) : 0;
|
|
memcpy(dst, b + off, k);
|
|
off += n;
|
|
}
|
|
void skip(size_t n) { off += n; }
|
|
/* Whole fields only: one that does not fit keeps its value. */
|
|
void take(void *dst, size_t n) {
|
|
if (off + n <= len) {
|
|
memcpy(dst, b + off, n);
|
|
}
|
|
off += n;
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
/* ── Companion: /lfs/new_prefs ─────────────────────────────────────── */
|
|
|
|
size_t companionPrefsEncode(const NodePrefs &p, uint8_t *buf, size_t cap)
|
|
{
|
|
if (cap < COMPANION_PREFS_SIZE) {
|
|
return 0;
|
|
}
|
|
Wr w = { buf, 0 };
|
|
|
|
/* 0-91: the layout upstream's new_prefs had */
|
|
w.put(&p.airtime_factor, 4);
|
|
w.put(p.node_name, 32);
|
|
w.zeros(4);
|
|
w.put(&p.node_lat, 8);
|
|
w.put(&p.node_lon, 8);
|
|
w.put(&p.freq, 4);
|
|
w.put(&p.sf, 1);
|
|
w.put(&p.cr, 1);
|
|
w.put(&p.client_repeat, 1);
|
|
w.put(&p.manual_add_contacts, 1);
|
|
w.put(&p.bw, 4);
|
|
w.put(&p.tx_power_dbm, 1);
|
|
w.put(&p.telemetry_mode_base, 1);
|
|
w.put(&p.telemetry_mode_loc, 1);
|
|
w.put(&p.telemetry_mode_env, 1);
|
|
w.put(&p.rx_delay_base, 4);
|
|
w.put(&p.advert_loc_policy, 1);
|
|
w.put(&p.multi_acks, 1);
|
|
w.put(&p.path_hash_mode, 1);
|
|
w.zeros(1);
|
|
w.put(&p.ble_pin, 4);
|
|
w.put(&p.buzzer_quiet, 1);
|
|
w.put(&p.gps_enabled, 1);
|
|
w.put(&p.gps_interval, 4);
|
|
w.put(&p.autoadd_config, 1);
|
|
w.put(&p.autoadd_max_hops, 1);
|
|
/* 92-: ZephCore. Upstream put default_scope_name at 93. */
|
|
w.put(&p.rx_boost, 1);
|
|
w.put(&p.leds_disabled, 1);
|
|
w.put(&p._reserved_apc_enabled, 1); /* 94-95: APC, removed in 1.16.6 */
|
|
w.put(&p._reserved_apc_margin, 1);
|
|
w.put(p.default_scope_name, 31);
|
|
w.put(p.default_scope_key, 16);
|
|
w.put(&p.ble_disabled, 1);
|
|
w.put(&p.display_brightness, 1);
|
|
w.put(&p.wake_on_msg, 1);
|
|
w.put(&p.screen_off_secs, 2);
|
|
w.put(&p.auto_shutdown_mv, 2);
|
|
w.put(&p.rx_duty_cycle, 1);
|
|
w.put(&p.meshtimesync, 1);
|
|
w.put(&p.v_contact_enabled, 1);
|
|
w.put(&p.v_battery_alert_mv, 2);
|
|
w.put(&p.cad_auto, 1);
|
|
w.put(&p.cad_offset, 1);
|
|
w.put(&p.probe_interval, 1);
|
|
w.put(&p.cad_busycap, 1);
|
|
w.put(&p.adc_multiplier, 4);
|
|
w.put(p.extra_sf, EXTRA_SF_MAX);
|
|
w.put(&p.v_contact_flags, 1);
|
|
w.put(&p.fem_rxgain, 1);
|
|
w.put(&p.display_rotate, 1);
|
|
w.put(&p.input_rotate, 1);
|
|
w.put(&p.cad_base, 1);
|
|
w.put(&p.tz_offset, 1);
|
|
w.put(&p.leds_radio_mode, 1);
|
|
w.put(&p.leds_hb_mode, 1);
|
|
w.put(&p.wifi_enabled, 1);
|
|
w.put(p.wifi_ssid, sizeof(p.wifi_ssid));
|
|
w.put(p.wifi_pwd, sizeof(p.wifi_pwd));
|
|
return w.off;
|
|
}
|
|
|
|
bool companionPrefsDecode(NodePrefs &prefs, const uint8_t *buf, size_t len)
|
|
{
|
|
/* gps_interval ends at 90: anything shorter is not a prefs file */
|
|
if (len < 90) {
|
|
return false;
|
|
}
|
|
/* Committed only if the radio preset is plausible: a file in some other
|
|
* layout must not become our radio settings. */
|
|
NodePrefs p = prefs;
|
|
Rd r = { buf, len, 0 };
|
|
|
|
r.take(&p.airtime_factor, 4);
|
|
r.take(p.node_name, 32);
|
|
r.skip(4);
|
|
r.take(&p.node_lat, 8);
|
|
r.take(&p.node_lon, 8);
|
|
r.take(&p.freq, 4);
|
|
r.take(&p.sf, 1);
|
|
r.take(&p.cr, 1);
|
|
r.take(&p.client_repeat, 1);
|
|
r.take(&p.manual_add_contacts, 1);
|
|
r.take(&p.bw, 4);
|
|
if (p.freq < ZC_RADIO_FREQ_MIN_MHZ || p.freq > ZC_RADIO_FREQ_MAX_MHZ ||
|
|
p.sf < 5 || p.sf > 12 ||
|
|
p.bw < ZC_RADIO_BW_MIN_KHZ || p.bw > ZC_RADIO_BW_MAX_KHZ) {
|
|
return false;
|
|
}
|
|
r.take(&p.tx_power_dbm, 1);
|
|
r.take(&p.telemetry_mode_base, 1);
|
|
r.take(&p.telemetry_mode_loc, 1);
|
|
r.take(&p.telemetry_mode_env, 1);
|
|
r.take(&p.rx_delay_base, 4);
|
|
r.take(&p.advert_loc_policy, 1);
|
|
r.take(&p.multi_acks, 1);
|
|
r.take(&p.path_hash_mode, 1);
|
|
r.skip(1);
|
|
r.take(&p.ble_pin, 4);
|
|
r.take(&p.buzzer_quiet, 1);
|
|
r.take(&p.gps_enabled, 1);
|
|
r.take(&p.gps_interval, 4);
|
|
r.take(&p.autoadd_config, 1);
|
|
r.take(&p.autoadd_max_hops, 1);
|
|
/* Each field below is newer than some deployed files: one past the end
|
|
* keeps the caller's initNodePrefs() default. */
|
|
r.take(&p.rx_boost, 1);
|
|
r.take(&p.leds_disabled, 1);
|
|
r.take(&p._reserved_apc_enabled, 1);
|
|
r.take(&p._reserved_apc_margin, 1);
|
|
r.take(p.default_scope_name, 31);
|
|
r.take(p.default_scope_key, 16);
|
|
r.take(&p.ble_disabled, 1);
|
|
r.take(&p.display_brightness, 1);
|
|
r.take(&p.wake_on_msg, 1);
|
|
r.take(&p.screen_off_secs, 2);
|
|
r.take(&p.auto_shutdown_mv, 2);
|
|
r.take(&p.rx_duty_cycle, 1);
|
|
r.take(&p.meshtimesync, 1);
|
|
r.take(&p.v_contact_enabled, 1);
|
|
r.take(&p.v_battery_alert_mv, 2);
|
|
r.take(&p.cad_auto, 1);
|
|
r.take(&p.cad_offset, 1);
|
|
r.take(&p.probe_interval, 1);
|
|
r.take(&p.cad_busycap, 1);
|
|
r.take(&p.adc_multiplier, 4);
|
|
r.take(p.extra_sf, EXTRA_SF_MAX);
|
|
r.take(&p.v_contact_flags, 1);
|
|
r.take(&p.fem_rxgain, 1);
|
|
r.take(&p.display_rotate, 1);
|
|
r.take(&p.input_rotate, 1);
|
|
/* cad_base 0 = "no base recorded": setCadParams() then leaves the stored
|
|
* offset alone. */
|
|
r.take(&p.cad_base, 1);
|
|
r.take(&p.tz_offset, 1);
|
|
r.take(&p.leds_radio_mode, 1);
|
|
r.take(&p.leds_hb_mode, 1);
|
|
/* led_brightness (ZephCore extension, since 1.17.4). Absent in files
|
|
* written before this field existed; the no-op EOF read leaves the
|
|
* initNodePrefs() default ZEPHCORE_LED_DEFAULT_BRIGHTNESS_PCT (10%),
|
|
* same value a fresh node already showed before this was persisted. */
|
|
r.take(&p.led_brightness, 1);
|
|
r.take(&p.wifi_enabled, 1);
|
|
r.take(p.wifi_ssid, sizeof(p.wifi_ssid));
|
|
r.take(p.wifi_pwd, sizeof(p.wifi_pwd));
|
|
|
|
/* The one validator: bounds, NaNs, and the unterminated char fields */
|
|
sanitizeNodePrefs(&p);
|
|
prefs = p;
|
|
return true;
|
|
}
|
|
|
|
/* ── Server roles: /lfs/repeater/prefs ─────────────────────────────── */
|
|
|
|
size_t serverPrefsEncode(const NodePrefs &prefs, uint8_t *buf, size_t cap)
|
|
{
|
|
if (cap < SERVER_PREFS_SIZE) {
|
|
return 0;
|
|
}
|
|
Wr w = { buf, 0 };
|
|
|
|
/* Same order as Arduino CommonCLI up to offset 290 */
|
|
w.put(&prefs.airtime_factor, sizeof(prefs.airtime_factor));
|
|
w.put(&prefs.node_name, sizeof(prefs.node_name));
|
|
w.zeros(4);
|
|
w.put(&prefs.node_lat, sizeof(prefs.node_lat));
|
|
w.put(&prefs.node_lon, sizeof(prefs.node_lon));
|
|
w.put(&prefs.password, sizeof(prefs.password));
|
|
w.put(&prefs.freq, sizeof(prefs.freq));
|
|
w.put(&prefs.tx_power_dbm, sizeof(prefs.tx_power_dbm));
|
|
w.put(&prefs.disable_fwd, sizeof(prefs.disable_fwd));
|
|
w.put(&prefs.advert_interval, sizeof(prefs.advert_interval));
|
|
w.zeros(1);
|
|
w.put(&prefs.rx_delay_base, sizeof(prefs.rx_delay_base));
|
|
w.put(&prefs.tx_delay_factor, sizeof(prefs.tx_delay_factor));
|
|
w.put(&prefs.guest_password, sizeof(prefs.guest_password));
|
|
w.put(&prefs.direct_tx_delay_factor, sizeof(prefs.direct_tx_delay_factor));
|
|
w.put(&prefs.backoff_multiplier, sizeof(prefs.backoff_multiplier));
|
|
w.put(&prefs.sf, sizeof(prefs.sf));
|
|
w.put(&prefs.cr, sizeof(prefs.cr));
|
|
w.put(&prefs.allow_read_only, sizeof(prefs.allow_read_only));
|
|
w.put(&prefs.multi_acks, sizeof(prefs.multi_acks));
|
|
w.put(&prefs.bw, sizeof(prefs.bw));
|
|
/* 120: leds_disabled, magic-encoded (was agc_reset_interval). */
|
|
{
|
|
uint8_t leds_byte = prefs.leds_disabled ? LEDS_PREF_OFF : LEDS_PREF_ON;
|
|
w.put(&leds_byte, sizeof(leds_byte));
|
|
}
|
|
w.put(&prefs.path_hash_mode, sizeof(prefs.path_hash_mode));
|
|
w.put(&prefs.loop_detect, sizeof(prefs.loop_detect));
|
|
w.zeros(1);
|
|
w.put(&prefs.flood_max, sizeof(prefs.flood_max));
|
|
w.put(&prefs.flood_advert_interval, sizeof(prefs.flood_advert_interval));
|
|
w.put(&prefs.interference_threshold, sizeof(prefs.interference_threshold));
|
|
w.zeros(25); // bridge settings
|
|
w.put(&prefs.powersaving_enabled, sizeof(prefs.powersaving_enabled));
|
|
w.zeros(3);
|
|
w.put(&prefs.gps_enabled, sizeof(prefs.gps_enabled));
|
|
w.put(&prefs.gps_interval, sizeof(prefs.gps_interval));
|
|
w.put(&prefs.advert_loc_policy, sizeof(prefs.advert_loc_policy));
|
|
w.put(&prefs.discovery_mod_timestamp, sizeof(prefs.discovery_mod_timestamp));
|
|
w.put(&prefs.adc_multiplier, sizeof(prefs.adc_multiplier));
|
|
w.put(prefs.owner_info, sizeof(prefs.owner_info));
|
|
/* ZephCore extensions */
|
|
w.put(&prefs.rx_boost, sizeof(prefs.rx_boost));
|
|
w.put(&prefs.rx_duty_cycle, sizeof(prefs.rx_duty_cycle));
|
|
/* RESERVED — formerly apc_enabled / apc_margin (removed in 1.16.6).
|
|
* Written back unchanged to hold the layout. */
|
|
w.put(&prefs._reserved_apc_enabled, sizeof(prefs._reserved_apc_enabled));
|
|
w.put(&prefs._reserved_apc_margin, sizeof(prefs._reserved_apc_margin));
|
|
/* Flood hop-ceiling extensions (offsets 294-295) */
|
|
w.put(&prefs.flood_max_unscoped, sizeof(prefs.flood_max_unscoped));
|
|
w.put(&prefs.flood_max_advert, sizeof(prefs.flood_max_advert));
|
|
/* Mesh time sync on/off (offset 296) */
|
|
w.put(&prefs.meshtimesync, sizeof(prefs.meshtimesync));
|
|
/* Adaptive CAD (offsets 297-300) */
|
|
w.put(&prefs.cad_auto, sizeof(prefs.cad_auto));
|
|
w.put(&prefs.cad_offset, sizeof(prefs.cad_offset));
|
|
w.put(&prefs.probe_interval, sizeof(prefs.probe_interval));
|
|
w.put(&prefs.cad_busycap, sizeof(prefs.cad_busycap));
|
|
/* LR2021 side-detector SFs (offsets 301-303) */
|
|
w.put(prefs.extra_sf, sizeof(prefs.extra_sf));
|
|
/* External FEM RX gain (offset 304) */
|
|
w.put(&prefs.fem_rxgain, sizeof(prefs.fem_rxgain));
|
|
/* Mounting orientation (offsets 305-306) */
|
|
w.put(&prefs.display_rotate, sizeof(prefs.display_rotate));
|
|
w.put(&prefs.input_rotate, sizeof(prefs.input_rotate));
|
|
/* Family base detPeak cad_offset was learned against (offset 307) */
|
|
w.put(&prefs.cad_base, sizeof(prefs.cad_base));
|
|
/* Display timezone offset (offset 308) — signed whole hours from UTC,
|
|
* applied only when formatting the on-device clock */
|
|
w.put(&prefs.tz_offset, sizeof(prefs.tz_offset));
|
|
/* LED activity/heartbeat modes (offsets 309-310) */
|
|
w.put(&prefs.leds_radio_mode, sizeof(prefs.leds_radio_mode));
|
|
w.put(&prefs.leds_hb_mode, sizeof(prefs.leds_hb_mode));
|
|
return w.off;
|
|
}
|
|
|
|
void serverPrefsDecode(NodePrefs &prefs, const uint8_t *buf, size_t len)
|
|
{
|
|
Rd r = { buf, len, 0 };
|
|
|
|
/* Same order as Arduino CommonCLI up to offset 290 */
|
|
r.get(&prefs.airtime_factor, sizeof(prefs.airtime_factor));
|
|
r.get(&prefs.node_name, sizeof(prefs.node_name));
|
|
r.skip(4);
|
|
r.get(&prefs.node_lat, sizeof(prefs.node_lat));
|
|
r.get(&prefs.node_lon, sizeof(prefs.node_lon));
|
|
r.get(&prefs.password, sizeof(prefs.password));
|
|
r.get(&prefs.freq, sizeof(prefs.freq));
|
|
r.get(&prefs.tx_power_dbm, sizeof(prefs.tx_power_dbm));
|
|
r.get(&prefs.disable_fwd, sizeof(prefs.disable_fwd));
|
|
r.get(&prefs.advert_interval, sizeof(prefs.advert_interval));
|
|
r.skip(1);
|
|
r.get(&prefs.rx_delay_base, sizeof(prefs.rx_delay_base));
|
|
r.get(&prefs.tx_delay_factor, sizeof(prefs.tx_delay_factor));
|
|
r.get(&prefs.guest_password, sizeof(prefs.guest_password));
|
|
r.get(&prefs.direct_tx_delay_factor, sizeof(prefs.direct_tx_delay_factor));
|
|
r.get(&prefs.backoff_multiplier, sizeof(prefs.backoff_multiplier));
|
|
r.get(&prefs.sf, sizeof(prefs.sf));
|
|
r.get(&prefs.cr, sizeof(prefs.cr));
|
|
r.get(&prefs.allow_read_only, sizeof(prefs.allow_read_only));
|
|
r.get(&prefs.multi_acks, sizeof(prefs.multi_acks));
|
|
r.get(&prefs.bw, sizeof(prefs.bw));
|
|
/* 120: leds_disabled, magic-encoded. Formerly agc_reset_interval — see the
|
|
* LEDS_PREF_* comment in NodePrefs.h for why this is not a bare 0/1.
|
|
* leds_byte stays 0 (→ LEDs on) if the file is short. */
|
|
uint8_t leds_byte = 0;
|
|
r.get(&leds_byte, sizeof(leds_byte));
|
|
r.get(&prefs.path_hash_mode, sizeof(prefs.path_hash_mode));
|
|
r.get(&prefs.loop_detect, sizeof(prefs.loop_detect));
|
|
r.skip(1);
|
|
r.get(&prefs.flood_max, sizeof(prefs.flood_max));
|
|
r.get(&prefs.flood_advert_interval, sizeof(prefs.flood_advert_interval));
|
|
r.get(&prefs.interference_threshold, sizeof(prefs.interference_threshold));
|
|
r.skip(25); // bridge settings
|
|
r.get(&prefs.powersaving_enabled, sizeof(prefs.powersaving_enabled));
|
|
r.skip(3);
|
|
r.get(&prefs.gps_enabled, sizeof(prefs.gps_enabled));
|
|
r.get(&prefs.gps_interval, sizeof(prefs.gps_interval));
|
|
r.get(&prefs.advert_loc_policy, sizeof(prefs.advert_loc_policy));
|
|
r.get(&prefs.discovery_mod_timestamp, sizeof(prefs.discovery_mod_timestamp));
|
|
r.get(&prefs.adc_multiplier, sizeof(prefs.adc_multiplier));
|
|
r.get(prefs.owner_info, sizeof(prefs.owner_info));
|
|
/* ZephCore extensions — absent in old files; a field past the end keeps
|
|
* the caller's initNodePrefs() default. */
|
|
r.get(&prefs.rx_boost, sizeof(prefs.rx_boost));
|
|
r.get(&prefs.rx_duty_cycle, sizeof(prefs.rx_duty_cycle));
|
|
/* RESERVED — formerly apc_enabled / apc_margin (APC, removed in 1.16.6).
|
|
* Still consumed so the fields after them stay at their stored offsets. */
|
|
r.get(&prefs._reserved_apc_enabled, sizeof(prefs._reserved_apc_enabled));
|
|
r.get(&prefs._reserved_apc_margin, sizeof(prefs._reserved_apc_margin));
|
|
r.get(&prefs.flood_max_unscoped, sizeof(prefs.flood_max_unscoped));
|
|
r.get(&prefs.flood_max_advert, sizeof(prefs.flood_max_advert));
|
|
r.get(&prefs.meshtimesync, sizeof(prefs.meshtimesync));
|
|
r.get(&prefs.cad_auto, sizeof(prefs.cad_auto));
|
|
r.get(&prefs.cad_offset, sizeof(prefs.cad_offset));
|
|
r.get(&prefs.probe_interval, sizeof(prefs.probe_interval));
|
|
r.get(&prefs.cad_busycap, sizeof(prefs.cad_busycap));
|
|
r.get(prefs.extra_sf, sizeof(prefs.extra_sf));
|
|
r.get(&prefs.fem_rxgain, sizeof(prefs.fem_rxgain));
|
|
r.get(&prefs.display_rotate, sizeof(prefs.display_rotate));
|
|
r.get(&prefs.input_rotate, sizeof(prefs.input_rotate));
|
|
/* cad_base 0 = "no base recorded": setCadParams() then leaves the stored
|
|
* offset alone, since a node upgrading across a table change cannot know
|
|
* which base its offset came from. */
|
|
r.get(&prefs.cad_base, sizeof(prefs.cad_base));
|
|
r.get(&prefs.tz_offset, sizeof(prefs.tz_offset));
|
|
r.get(&prefs.leds_radio_mode, sizeof(prefs.leds_radio_mode));
|
|
r.get(&prefs.leds_hb_mode, sizeof(prefs.leds_hb_mode));
|
|
/* led_brightness (ZephCore extension, since 1.17.4). Absent in files
|
|
* written before this field existed; the no-op EOF read leaves the
|
|
* initNodePrefs() default ZEPHCORE_LED_DEFAULT_BRIGHTNESS_PCT (10%),
|
|
* same value a fresh node already showed before this was persisted. */
|
|
r.get(&prefs.led_brightness, sizeof(prefs.led_brightness));
|
|
|
|
/* Only the explicit "off" magic disables LEDs; a legacy AGC interval or an
|
|
* unwritten byte both mean "on". */
|
|
prefs.leds_disabled = (leds_byte == LEDS_PREF_OFF) ? 1 : 0;
|
|
|
|
if (prefs.freq < ZC_RADIO_FREQ_MIN_MHZ || prefs.freq > ZC_RADIO_FREQ_MAX_MHZ ||
|
|
prefs.sf < 5 || prefs.sf > 12 ||
|
|
prefs.bw < ZC_RADIO_BW_MIN_KHZ || prefs.bw > ZC_RADIO_BW_MAX_KHZ) {
|
|
prefs.freq = mesh::LoRaConfig::FREQ_MHZ;
|
|
prefs.bw = mesh::LoRaConfig::BANDWIDTH_KHZ;
|
|
prefs.sf = mesh::LoRaConfig::SPREADING_FACTOR;
|
|
prefs.cr = mesh::LoRaConfig::CODING_RATE;
|
|
prefs.tx_power_dbm = mesh::LoRaConfig::TX_POWER_DBM;
|
|
}
|
|
/* Bounds, NaNs, and the char fields, which the file stores without
|
|
* terminators. */
|
|
sanitizeNodePrefs(&prefs);
|
|
}
|