mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-09-02 00:38:50 +00:00
fix observer role
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
#include <adapters/rng/ZephyrRNG.h> /* generateFirstBootIdentity (hardened keygen) */
|
||||
#include <helpers/MeshcoreJson.h>
|
||||
|
||||
#include <zephyr/fs/fs.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(zephcore_observer, CONFIG_ZEPHCORE_OBSERVER_LOG_LEVEL);
|
||||
|
||||
@@ -57,10 +58,30 @@ void ObserverMesh::begin(RepeaterDataStore *store, struct ObserverCreds *creds)
|
||||
_prefs.tx_power_dbm = 0; /* observer never TXes anyway */
|
||||
/* freq=869.618, bw=62.5, sf=8 already set by initNodePrefs */
|
||||
|
||||
/* First boot has to be detected BEFORE loadPrefs(): the store is shared
|
||||
* with the repeater and its no-file branch re-runs initNodePrefs(), applies
|
||||
* *repeater* defaults over whatever the caller passed in, saves them, and
|
||||
* returns true. So the observer values set above are silently discarded on
|
||||
* a fresh unit and there is no return code that says so. Probing for the
|
||||
* file is the only observer-local way to tell — the alternative, changing
|
||||
* the no-file branch, would alter repeater and room-server behaviour. */
|
||||
char prefs_path[64];
|
||||
struct fs_dirent prefs_ent;
|
||||
snprintf(prefs_path, sizeof(prefs_path), "%s/prefs", _store->getBasePath());
|
||||
const bool first_boot = (fs_stat(prefs_path, &prefs_ent) < 0);
|
||||
|
||||
/* Load persisted prefs (overrides defaults with saved values) */
|
||||
if (!_store->loadPrefs(_prefs)) {
|
||||
/* First boot — save observer defaults */
|
||||
_store->loadPrefs(_prefs);
|
||||
|
||||
if (first_boot) {
|
||||
/* Re-apply the observer defaults the shared no-file branch overwrote,
|
||||
* then persist them so this runs exactly once. Only the prefs file is
|
||||
* rewritten — obs_creds (WiFi/MQTT/IATA/lat/lon) is a separate file and
|
||||
* is never touched here. */
|
||||
_prefs.cr = 5;
|
||||
_prefs.tx_power_dbm = 0;
|
||||
_store->savePrefs(_prefs);
|
||||
LOG_INF("First boot — saved observer prefs defaults");
|
||||
}
|
||||
|
||||
/* Load or generate node identity.
|
||||
@@ -410,14 +431,19 @@ bool ObserverMesh::handleCLI(const char *command, char *reply, int reply_size)
|
||||
float f = (float)atof(val);
|
||||
/* Accept Hz (e.g. 869618000) or MHz (e.g. 869.618) */
|
||||
if (f > 1000000.0f) f /= 1000000.0f;
|
||||
if (f >= 150.0f && f <= 2500.0f) {
|
||||
/* 300..1000 MHz is not the radio's limit — it is what
|
||||
* RepeaterDataStore::loadPrefs() accepts on the way back in.
|
||||
* Anything outside it saves fine and is then silently reset to
|
||||
* defaults on the next boot, taking bw/sf/cr/tx_power with it, so
|
||||
* refuse it here rather than hand back a value that won't survive. */
|
||||
if (f >= 300.0f && f <= 1000.0f) {
|
||||
_prefs.freq = f;
|
||||
_store->savePrefs(_prefs);
|
||||
((LoRaRadioBase *)_radio)->reconfigureWithParams(
|
||||
_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
|
||||
snprintf(reply, reply_size, "freq=%.3f MHz", (double)_prefs.freq);
|
||||
} else {
|
||||
snprintf(reply, reply_size, "ERR freq out of range");
|
||||
snprintf(reply, reply_size, "ERR freq must be 300-1000 MHz");
|
||||
}
|
||||
|
||||
} else if ((val = find_val(rest, "sf")) != nullptr) {
|
||||
@@ -443,14 +469,16 @@ bool ObserverMesh::handleCLI(const char *command, char *reply, int reply_size)
|
||||
} else {
|
||||
bw = (float)atof(val);
|
||||
}
|
||||
if (bw > 0.0f) {
|
||||
/* Lower bound 7 kHz mirrors loadPrefs()'s validator — see the freq
|
||||
* case above for why the CLI must not accept what it will reject. */
|
||||
if (bw >= 7.0f && bw <= 500.0f) {
|
||||
_prefs.bw = bw;
|
||||
_store->savePrefs(_prefs);
|
||||
((LoRaRadioBase *)_radio)->reconfigureWithParams(
|
||||
_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
|
||||
snprintf(reply, reply_size, "bw=%.2f kHz", (double)_prefs.bw);
|
||||
} else {
|
||||
snprintf(reply, reply_size, "ERR invalid bw");
|
||||
snprintf(reply, reply_size, "ERR bw must be 7-500 kHz (or index 0-5)");
|
||||
}
|
||||
|
||||
} else if ((val = find_val(rest, "cr")) != nullptr) {
|
||||
|
||||
@@ -159,7 +159,7 @@ static void print_banner(void)
|
||||
cli_println(line);
|
||||
|
||||
cli_println("");
|
||||
cli_println("--- Configure ---");
|
||||
cli_println("--- Configure: network ---");
|
||||
cli_println("set wifi.ssid <name> WiFi network name");
|
||||
cli_println("set wifi.psk <password> WiFi password (empty = open)");
|
||||
cli_println("set mqtt.host <hostname> MQTT broker hostname");
|
||||
@@ -168,17 +168,36 @@ static void print_banner(void)
|
||||
cli_println("set mqtt.user <username> MQTT username");
|
||||
cli_println("set mqtt.password <pass> MQTT password");
|
||||
cli_println("set mqtt.iata <code> Location code (e.g. BUD BTS VIE SEA)");
|
||||
cli_println("");
|
||||
cli_println("--- Configure: node ---");
|
||||
cli_println("set name <name> Node display name");
|
||||
cli_println("set freq <MHz|Hz> LoRa frequency (e.g. 869.618 or 869618000)");
|
||||
cli_println("set sf <7-12> Spreading factor");
|
||||
cli_println("set bw <idx> Bandwidth: 3=62.5 0=125 1=250 2=500 kHz");
|
||||
cli_println("set cr <5-8> Coding rate");
|
||||
cli_println("set lat <-90..90> Latitude, decimal degrees");
|
||||
cli_println("set lon <-180..180> Longitude, decimal degrees");
|
||||
cli_println(" (lat+lon+non-default name = self-advert)");
|
||||
cli_println("set meshtimesync <on|off> Mesh clock consensus correction");
|
||||
cli_println("");
|
||||
cli_println("--- Configure: radio ---");
|
||||
cli_println("set freq <MHz|Hz> 300-1000 (e.g. 869.618 or 869618000)");
|
||||
cli_println("set sf <7-12> Spreading factor");
|
||||
cli_println("set bw <idx|kHz> 0=125 1=250 2=500 3=62.5 4=41.7 5=31.25");
|
||||
cli_println("set cr <5-8> Coding rate");
|
||||
cli_println("");
|
||||
cli_println("--- Query ---");
|
||||
cli_println("get name Node display name");
|
||||
cli_println("get role Node role (observer)");
|
||||
cli_println("get board Board name");
|
||||
cli_println("get version Firmware version and build date");
|
||||
cli_println("get public.key Node public key (hex)");
|
||||
cli_println("get radio LoRa radio parameters");
|
||||
cli_println("get lat / get lon Configured position");
|
||||
cli_println("get wifi.ssid Configured WiFi network");
|
||||
cli_println("get wifi.status WiFi connection state");
|
||||
cli_println("get mqtt.status MQTT connection state");
|
||||
cli_println("get radio LoRa radio parameters");
|
||||
cli_println("get mqtt.host MQTT broker hostname");
|
||||
cli_println("get mqtt.port MQTT broker port");
|
||||
cli_println("get mqtt.tls MQTT TLS on/off");
|
||||
cli_println("get mqtt.user MQTT username");
|
||||
cli_println("get mqtt.iata Location code");
|
||||
cli_println("get meshtimesync Time-sync consensus state (dry-run)");
|
||||
cli_println("help Show this screen");
|
||||
cli_println("=========================");
|
||||
@@ -248,7 +267,9 @@ static mesh::ZephyrMillisecondClock s_ms_clock;
|
||||
|
||||
static const struct device *const lora_dev = DEVICE_DT_GET(DT_ALIAS(lora0));
|
||||
|
||||
/* Radio prefs — observer-specific defaults set in main() */
|
||||
/* Static-init placeholder only. The radio is rebound to the mesh's own
|
||||
* NodePrefs via setPrefs() in main() before observer_mesh.begin() — this object
|
||||
* is not the live radio configuration and is never loaded from flash. */
|
||||
static NodePrefs s_radio_prefs;
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZEPHCORE_RADIO_LR1110)
|
||||
@@ -300,14 +321,34 @@ int main(void)
|
||||
/* LoRa RX callback — observer never needs TX done */
|
||||
lora_radio.setRxCallback(lora_rx_callback, nullptr);
|
||||
|
||||
/* Bind the radio to the mesh's NodePrefs BEFORE begin().
|
||||
*
|
||||
* The radio reads freq/bw/sf/cr through this pointer, both during
|
||||
* begin() → Dispatcher::begin() → Radio::begin() and on every later
|
||||
* reconfigure() (LoRaRadioBase::reconfigureWithParams() ignores its
|
||||
* arguments and re-reads the pointer). Constructed against
|
||||
* s_radio_prefs, which is never loaded from flash, the radio stayed on
|
||||
* the compiled-in defaults forever: `set freq/sf/bw/cr` wrote flash and
|
||||
* updated the CLI/MQTT readback but never reached the hardware, so the
|
||||
* setting looked accepted and then "reverted" on reboot.
|
||||
*
|
||||
* Binding before begin() is safe and required: begin() calls loadPrefs()
|
||||
* first and Dispatcher::begin() last, so the object is populated by the
|
||||
* time the radio reads through it. Mirrors main_repeater.cpp and
|
||||
* main_room_server.cpp. */
|
||||
lora_radio.setPrefs(observer_mesh.getNodePrefs());
|
||||
|
||||
/* Initialize and start mesh (loads prefs + identity from flash) */
|
||||
s_mesh_ptr = &observer_mesh;
|
||||
observer_mesh.begin(&data_store, &s_creds);
|
||||
|
||||
/* Generate a default node name based on pubkey if still generic */
|
||||
NodePrefs *prefs = observer_mesh.getNodePrefs();
|
||||
/* "Observer" is deliberately NOT in this list: it is a name a user can set,
|
||||
* and regenerating it here made `set name Observer` silently revert on every
|
||||
* reboot. "Repeater" is the name the shared store writes on first boot, so
|
||||
* it still counts as unset. */
|
||||
if (strlen(prefs->node_name) == 0 ||
|
||||
strcmp(prefs->node_name, "Observer") == 0 ||
|
||||
strcmp(prefs->node_name, "Repeater") == 0) {
|
||||
/* Use first 4 bytes of pubkey for uniqueness */
|
||||
const char *hex = observer_mesh.getPubkeyHex();
|
||||
|
||||
@@ -2,11 +2,20 @@
|
||||
* Reroute console + shell off USB Serial/JTAG onto uart0 (physical TX/RX).
|
||||
*
|
||||
* ESP32-S3 USB Serial/JTAG shares the D+/D- pins with USB OTG and only one
|
||||
* controller can own the port at a time. Boards that default their console to
|
||||
* USB Serial/JTAG include this alongside esp32s3_usb_otg.dtsi so that, when
|
||||
* esp32s3_usb.conf disables USB Serial/JTAG and hands the port to USB OTG, the
|
||||
* console still has a home. Boards with a dedicated UART console (e.g. Heltec)
|
||||
* do not need this.
|
||||
* controller can own the port at a time. When esp32s3_usb.conf disables USB
|
||||
* Serial/JTAG and hands the port to USB OTG, a console chosen as usb_serial
|
||||
* would go silent — this gives it a home on uart0 instead.
|
||||
*
|
||||
* Boards that default their console to USB Serial/JTAG must NOT include this
|
||||
* from board.overlay: that applies to every build of the board, including the
|
||||
* ones that never enable USB OTG, and silently strands the console (and the
|
||||
* observer/repeater serial CLI, which falls back to the chosen console) on
|
||||
* GPIO43/44. It is included from the auto-paired esp32s3_usb.overlay instead,
|
||||
* so it follows esp32s3_usb.conf.
|
||||
*
|
||||
* Boards whose console really is a physical uart0 — Heltec V3/V4/V43, and
|
||||
* ThinkNode M9 (USB-C via a CH9102-class bridge into UART0, native USB pads not
|
||||
* bonded) — do include it unconditionally, which is correct for them.
|
||||
*
|
||||
* uart0 is disabled by default in the ESP32-S3 SoC dtsi, so we enable it and
|
||||
* pin it to the chip's ROM-default UART0 pads (GPIO43 TX / GPIO44 RX) — the
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
# boards/common/esp32s3_usb_otg.dtsi). Without the DT node,
|
||||
# DT_HAS_COMPAT_STATUS_OKAY(zephyr_cdc_acm_uart) is false and
|
||||
# zephcore_usbd_init() is compiled out at the #if check.
|
||||
#
|
||||
# The same-named esp32s3_usb.overlay is auto-paired with this conf and reroutes
|
||||
# the console off USB Serial/JTAG onto uart0 — see that file. Keep the pairing
|
||||
# intact: the reroute must never be applied by a build that does not set the
|
||||
# Kconfig below, or non-companion roles lose their console and serial CLI.
|
||||
|
||||
CONFIG_USB_DEVICE_STACK_NEXT=y
|
||||
CONFIG_ZEPHCORE_COMPANION_USB=y
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Device-tree half of esp32s3_usb.conf — auto-paired by the
|
||||
* zephcore_auto_pair_overlay() helper in CMakeLists.txt, so it is applied if
|
||||
* and only if the build actually passes boards/common/esp32s3_usb.conf.
|
||||
*
|
||||
* esp32s3_usb.conf disables USB Serial/JTAG and hands the shared D+/D- pins to
|
||||
* the USB OTG controller (the CDC-ACM companion channel). Any board whose
|
||||
* console rides usb_serial would go silent at that moment, so the console is
|
||||
* rerouted to uart0 here — in the paired overlay rather than in board.overlay,
|
||||
* because board.overlay applies to EVERY build of the board regardless of role.
|
||||
*
|
||||
* That is the bug this file exists to prevent: xiao_esp32s3 and station_g2 used
|
||||
* to include the reroute unconditionally, which silently moved the console (and
|
||||
* with it the observer/repeater serial CLI, which falls back to the chosen
|
||||
* console) onto GPIO43/44 in builds that never enabled USB OTG at all.
|
||||
*
|
||||
* Devicetree is processed before Kconfig, so an overlay cannot test
|
||||
* CONFIG_ZEPHCORE_COMPANION_USB — the conf/overlay pairing is what makes this
|
||||
* conditional.
|
||||
*
|
||||
* Boards whose console is already a physical uart0 (Heltec V3/V4/V43, ThinkNode
|
||||
* M9) are unaffected: the include re-asserts the same uart0 + GPIO43/44 setup
|
||||
* they already have.
|
||||
*/
|
||||
#include "esp32s3_console_uart0.dtsi"
|
||||
@@ -93,7 +93,13 @@
|
||||
/* LittleFS auto-mount — standard /lfs mount point */
|
||||
#include "../../common/filesystem.dtsi"
|
||||
|
||||
/* USB OTG companion transport. station_g2 consoles over USB Serial/JTAG, which
|
||||
* shares D+/D- with USB OTG, so the console is rerouted to uart0. */
|
||||
#include "../../common/esp32s3_console_uart0.dtsi"
|
||||
/* USB OTG companion transport. The node is inert unless the build enables the
|
||||
* USB device stack via boards/common/esp32s3_usb.conf.
|
||||
*
|
||||
* station_g2 consoles over USB Serial/JTAG (board DTS), which shares D+/D- with
|
||||
* USB OTG — so the console reroute to uart0 lives in the auto-paired
|
||||
* boards/common/esp32s3_usb.overlay and applies only when that conf is passed.
|
||||
* Do NOT include esp32s3_console_uart0.dtsi here: it would move the console
|
||||
* (and the observer/repeater serial CLI that falls back to it) onto GPIO43/44
|
||||
* in every build of this board, including the ones still using USB Serial/JTAG. */
|
||||
#include "../../common/esp32s3_usb_otg.dtsi"
|
||||
|
||||
@@ -70,7 +70,13 @@
|
||||
/* LittleFS auto-mount — standard /lfs mount point */
|
||||
#include "../../common/filesystem.dtsi"
|
||||
|
||||
/* USB OTG companion transport. Consoles over USB Serial/JTAG (shares D+/D-
|
||||
* with USB OTG), so the console is rerouted to uart0. */
|
||||
#include "../../common/esp32s3_console_uart0.dtsi"
|
||||
/* USB OTG companion transport. The node is inert unless the build enables the
|
||||
* USB device stack via boards/common/esp32s3_usb.conf.
|
||||
*
|
||||
* The XIAO consoles over USB Serial/JTAG (stock board DTS), which shares D+/D-
|
||||
* with USB OTG — so the console reroute to uart0 lives in the auto-paired
|
||||
* boards/common/esp32s3_usb.overlay and applies only when that conf is passed.
|
||||
* Do NOT include esp32s3_console_uart0.dtsi here: it would move the console
|
||||
* (and the observer/repeater serial CLI that falls back to it) onto GPIO43/44
|
||||
* in every build of this board, including the ones still using USB Serial/JTAG. */
|
||||
#include "../../common/esp32s3_usb_otg.dtsi"
|
||||
|
||||
Reference in New Issue
Block a user