boot_prefs shared helper

This commit is contained in:
liquidraver
2026-09-19 20:42:47 +02:00
parent 3055ae9269
commit a5a67e9ea6
5 changed files with 69 additions and 118 deletions
+2 -8
View File
@@ -39,7 +39,7 @@ LOG_MODULE_REGISTER(zephcore_observer_main, CONFIG_ZEPHCORE_MAIN_LOG_LEVEL);
#include <ZephyrWiFiStation.h>
#include <ZephyrMQTTPublisher.h>
#include "observer_creds.h"
#include <helpers/led_gate.h>
#include <helpers/boot_prefs.h>
/* ========== LED (optional) ========== */
@@ -387,13 +387,7 @@ int main(void)
/* Apply the persisted LED master switch. The observer has no CLI of its own
* to change it, but a unit reflashed from a repeater build keeps the setting
* — and it still drives lora-tx-led on TX-capable boards. */
{
bool leds_off = prefs->leds_disabled != 0;
zephcore_leds_set_disabled(leds_off);
zephcore_leds_set_radio_mode(prefs->leds_radio_mode);
zephcore_leds_set_hb_mode(prefs->leds_hb_mode);
LOG_INF("LEDs: %s (from prefs)", leds_off ? "disabled" : "enabled");
}
apply_boot_prefs(prefs, false);
/* Initialize USB serial for CLI */
#if ZEPHCORE_USB_STACK && DT_HAS_COMPAT_STATUS_OKAY(zephyr_cdc_acm_uart)
+45
View File
@@ -0,0 +1,45 @@
/*
* ZephCore - apply persisted prefs to hardware at boot
* Copyright (c) 2025 ZephCore
* SPDX-License-Identifier: MIT
*
* Shared by the repeater, room server and observer mains. Header-only so it
* logs under the including file's log module.
*/
#ifndef ZEPHCORE_BOOT_PREFS_H
#define ZEPHCORE_BOOT_PREFS_H
#include <zephyr/logging/log.h>
#include <helpers/NodePrefs.h>
#include "led_gate.h"
#include "ZephyrGPSManager.h"
/* Push the prefs that drive hardware directly (LED gate, GPS duty) into their
* managers. Ordering is load-bearing:
* - after loadPrefs(): before it the struct still holds the initNodePrefs()
* defaults (leds on, gps_interval=300, the companion value). On
* repeater/room server loadPrefs() is also where the 48 h GPS default is
* set and persisted. Calling this too early is the bug class where CLI
* readback is correct and the hardware disagrees.
* - after ui_init(), so the heartbeat cycle exists to be stopped.
* - after the GPS fix/event callbacks are registered, because
* gps_set_repeater_mode() starts acquisition immediately.
* gps_time_sync: put GPS in repeater (time-sync-only) mode. Only roles that
* own the GPS pass true. */
static inline void apply_boot_prefs(const NodePrefs *p, bool gps_time_sync)
{
bool leds_off = p->leds_disabled != 0;
zephcore_leds_set_disabled(leds_off);
zephcore_leds_set_radio_mode(p->leds_radio_mode);
zephcore_leds_set_hb_mode(p->leds_hb_mode);
LOG_INF("LEDs: %s (from prefs)", leds_off ? "disabled" : "enabled");
if (gps_time_sync && gps_is_available()) {
gps_set_poll_interval_sec(p->gps_interval);
gps_set_repeater_mode(true);
}
}
#endif /* ZEPHCORE_BOOT_PREFS_H */
+6 -45
View File
@@ -22,7 +22,7 @@ LOG_MODULE_REGISTER(zephcore_repeater_main, CONFIG_ZEPHCORE_MAIN_LOG_LEVEL);
#include <zephyr/drivers/hwinfo.h>
#include <zephyr/sys/reboot.h>
#include "oled_power.h"
#include "led_gate.h"
#include "boot_prefs.h"
/* BLE controller assert handler — BT is compiled even for repeater (via zephcore_common.conf) */
#if IS_ENABLED(CONFIG_BT_CTLR_ASSERT_HANDLER)
@@ -700,8 +700,8 @@ int main(void)
}
}
/* GPS callbacks only. The duty interval and the mode switch that starts
* acquisition are applied after loadPrefs() below — see the block there. */
/* GPS callbacks only. Duty interval and repeater mode are applied by
* apply_boot_prefs() after loadPrefs() below. */
if (gps_is_available()) {
gps_set_fix_callback(gps_fix_callback);
gps_set_event_callback(gps_event_callback);
@@ -760,48 +760,9 @@ int main(void)
data_store.loadPrefs(*repeater_mesh.getNodePrefs());
lora_radio.setPrefs(repeater_mesh.getNodePrefs());
/* Set GPS to repeater mode: power off now, wake every duty interval for
* time sync only. This prevents GPS from draining power on boards that
* have it (e.g., Wio Tracker).
*
* MUST come after loadPrefs(), for the same reason as the LED switch
* below -- but note loadPrefs() is not a pure read here. It is also the
* only place this role's GPS default is established: initNodePrefs()
* sets gps_interval=300 (the companion value), and the 48 h default is
* applied and PERSISTED by loadPrefs()'s first-boot path.
*
* This block used to sit ~55 lines earlier, where getNodePrefs() still
* held that 300. So the GPS manager was armed from the companion default
* on every boot and nothing re-applied the real value afterwards:
* - a stock repeater woke its GPS every 5 min instead of every 48 h;
* - any operator value, notably "set gps duty 0" (always-on), was
* stored and read back correctly but never reached the hardware.
* Same failure shape as the radio-frequency and LED ordering bugs in this
* file. main_companion.cpp has always had this order right. */
if (gps_is_available()) {
gps_set_poll_interval_sec(repeater_mesh.getNodePrefs()->gps_interval);
gps_set_repeater_mode(true);
}
/* Apply the persisted LED master switch ("set leds on|off"). MUST come
* after loadPrefs(): this used to sit just after ui_init(), ~45 lines
* earlier, where getNodePrefs() still held the initNodePrefs() default of
* leds_disabled=0. A node with "off" persisted therefore opened the gate on
* every boot and never closed it again -- `get leds` read the (correct) RAM
* prefs and said "off" while the LEDs kept blinking, until the user issued
* `set leds off` a second time to drive the gate directly. The other two
* ordering constraints still hold here: ui_init() has already run, so the
* heartbeat cycle exists to be stopped, and repeater_mesh.begin() ->
* Dispatcher::begin() -> Radio::begin() is still below, so the first
* transmit honours it. */
{
const NodePrefs *lp = repeater_mesh.getNodePrefs();
bool leds_off = lp->leds_disabled != 0;
zephcore_leds_set_disabled(leds_off);
zephcore_leds_set_radio_mode(lp->leds_radio_mode);
zephcore_leds_set_hb_mode(lp->leds_hb_mode);
LOG_INF("LEDs: %s (from prefs)", leds_off ? "disabled" : "enabled");
}
/* LED gate and GPS duty from prefs. After loadPrefs() and ui_init(),
* before begin() -- see boot_prefs.h. */
apply_boot_prefs(repeater_mesh.getNodePrefs(), true);
/* Start mesh with data store - loads ACL, regions */
repeater_mesh.begin(&data_store);
+6 -45
View File
@@ -22,7 +22,7 @@ LOG_MODULE_REGISTER(zephcore_room_main, CONFIG_ZEPHCORE_MAIN_LOG_LEVEL);
#include <zephyr/drivers/hwinfo.h>
#include <zephyr/sys/reboot.h>
#include "oled_power.h"
#include "led_gate.h"
#include "boot_prefs.h"
/* BLE controller assert handler — BT is compiled even for repeater (via zephcore_common.conf) */
#if IS_ENABLED(CONFIG_BT_CTLR_ASSERT_HANDLER)
@@ -586,8 +586,8 @@ int main(void)
}
}
/* GPS callbacks only. The duty interval and the mode switch that starts
* acquisition are applied after loadPrefs() below — see the block there. */
/* GPS callbacks only. Duty interval and repeater mode are applied by
* apply_boot_prefs() after loadPrefs() below. */
if (gps_is_available()) {
gps_set_fix_callback(gps_fix_callback);
gps_set_event_callback(gps_event_callback);
@@ -645,48 +645,9 @@ int main(void)
data_store.loadPrefs(*room_mesh.getNodePrefs());
lora_radio.setPrefs(room_mesh.getNodePrefs());
/* Set GPS to repeater mode: power off now, wake every duty interval for
* time sync only. This prevents GPS from draining power on boards that
* have it (e.g., Wio Tracker).
*
* MUST come after loadPrefs(), for the same reason as the LED switch
* below -- but note loadPrefs() is not a pure read here. It is also the
* only place this role's GPS default is established: initNodePrefs()
* sets gps_interval=300 (the companion value), and the 48 h default is
* applied and PERSISTED by loadPrefs()'s first-boot path.
*
* This block used to sit ~55 lines earlier, where getNodePrefs() still
* held that 300. So the GPS manager was armed from the companion default
* on every boot and nothing re-applied the real value afterwards:
* - a stock repeater woke its GPS every 5 min instead of every 48 h;
* - any operator value, notably "set gps duty 0" (always-on), was
* stored and read back correctly but never reached the hardware.
* Same failure shape as the radio-frequency and LED ordering bugs in this
* file. main_companion.cpp has always had this order right. */
if (gps_is_available()) {
gps_set_poll_interval_sec(room_mesh.getNodePrefs()->gps_interval);
gps_set_repeater_mode(true);
}
/* Apply the persisted LED master switch ("set leds on|off"). MUST come
* after loadPrefs(): this used to sit just after ui_init(), ~45 lines
* earlier, where getNodePrefs() still held the initNodePrefs() default of
* leds_disabled=0. A node with "off" persisted therefore opened the gate on
* every boot and never closed it again -- `get leds` read the (correct) RAM
* prefs and said "off" while the LEDs kept blinking, until the user issued
* `set leds off` a second time to drive the gate directly. The other two
* ordering constraints still hold here: ui_init() has already run, so the
* heartbeat cycle exists to be stopped, and room_mesh.begin() ->
* Dispatcher::begin() -> Radio::begin() is still below, so the first
* transmit honours it. */
{
const NodePrefs *lp = room_mesh.getNodePrefs();
bool leds_off = lp->leds_disabled != 0;
zephcore_leds_set_disabled(leds_off);
zephcore_leds_set_radio_mode(lp->leds_radio_mode);
zephcore_leds_set_hb_mode(lp->leds_hb_mode);
LOG_INF("LEDs: %s (from prefs)", leds_off ? "disabled" : "enabled");
}
/* LED gate and GPS duty from prefs. After loadPrefs() and ui_init(),
* before begin() -- see boot_prefs.h. */
apply_boot_prefs(room_mesh.getNodePrefs(), true);
/* Start mesh with data store - loads ACL, regions */
room_mesh.begin(&data_store);
@@ -1,22 +1,20 @@
/*
* ThinkNode M6 formatter overlay — minimal drivers, single LittleFS instance.
* ThinkNode M6 formatter overlay — minimal drivers.
*
* The M6 main DTS includes both filesystem.dtsi (/lfs) and qspi-ext.dtsi
* (/ext), giving two LittleFS instances. Each needs ~8-10KB RAM for block
* caches; two instances overflow the formatter's RAM budget. Drop /ext here
* — the QSPI storage partition node remains so main.c can still erase it via
* FIXED_PARTITION_EXISTS(qspi_storage_partition).
* The M6 board DTS no longer carries the QSPI flash: no shipped unit has the
* chip fitted, so the board dropped &qspi, its partition, the P0.21 enable
* regulator and the qspi-ext.dtsi (/ext) include together (see the note in
* thinknode_m6.dts). There is therefore no /ext LittleFS instance to drop
* and no QSPI enable to hog here; main.c's FIXED_PARTITION_EXISTS() checks
* simply skip the QSPI erase. If the board ever restores QSPI, restore the
* "/delete-node/ &qspi_lfs;" and the P0.21 hog that used to live here.
*
* Also replace regulator-fixed power-enable nodes with gpio-hogs so
* Replace the regulator-fixed power-enable node with a gpio-hog so
* CONFIG_REGULATOR is not forced on by regulator-boot-on.
*/
/* Remove /ext LittleFS fstab — formatter only needs one LittleFS instance */
/delete-node/ &qspi_lfs;
/* Remove regulator-fixed nodes and replace with gpio-hogs */
/* Remove regulator-fixed node and replace with a gpio-hog */
/delete-node/ &pwr_enable;
/delete-node/ &qspi_flash_enable;
&gpio0 {
/* Board power enable (PIN_PWR_EN = P0.27) */
@@ -26,12 +24,4 @@
output-high;
line-name = "PWR Enable";
};
/* QSPI flash enable (QSPI_FLASH_EN = P0.21) — must be HIGH before flash responds */
qspi-flash-en-hog {
gpio-hog;
gpios = <21 GPIO_ACTIVE_HIGH>;
output-high;
line-name = "QSPI Flash Enable";
};
};