mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-10-15 14:50:09 +00:00
peripherals & power: GPS fixes, poweroff/pwrmgt, upstream SensorManager telemetry
- gps: servers honour gps_enabled at boot (was lost on reboot; zc.gps_set upgrades older prefs to on once); sleep lock only while acquiring; gps off works on boards without a power line (UART sleep); UI toggles and fixes handled on the main thread; configure only after a good init; observer parks its GPS; one interval clamp (clampGpsInterval); one nav-mode knob (room servers on Air530Z now stationary); GNSS UARTE from DT - gps: split into ZephyrGPSManager / gps_power / gps_module_cfg (+gps_internal.h); dead driver branches, gps_get_diag, sats_* removed - clock: ZephyrRTCClock writes the hardware RTC itself (clkreboot now resets it); MESH_EVENT_RTC_SAVE gone - power: one power-off path (zephcore_power_off, ZephyrBoard::powerOff), poweroff/shutdown CLI, get pwrmgt.*, crash record kept across reboot, battery reading cached 10 s, fuel gauge on the current API - telemetry: upstream SensorManager + CayenneLPP (rounded to nearest), one builder for all roles; per-sensor channels, MCU temp always, guest gating and inverse mask on servers; sensor list/get/set work; BME280/BMP280 also probed at 0x77 - CLI: gps in upstream's form, get gps detail, gps sync
This commit is contained in:
@@ -66,6 +66,7 @@ LOG_MODULE_REGISTER(zephcore_main, CONFIG_ZEPHCORE_MAIN_LOG_LEVEL);
|
||||
#include <helpers/StatsFormatHelper.h>
|
||||
#include <app/CompanionCLI.h>
|
||||
#include <helpers/battery_curve.h>
|
||||
#include <zephyr_poweroff.h>
|
||||
#endif
|
||||
|
||||
/* Without this a BLE controller assert freezes the CPU at top IRQ priority
|
||||
@@ -92,16 +93,10 @@ static void save_prefs_to_flash(void);
|
||||
static void vcontact_battery_alert_check(void);
|
||||
#endif
|
||||
|
||||
/* Epoch for a deferred hardware-RTC write. gps_fix_callback runs on the GNSS
|
||||
* modem_chat thread, where several ms of blocking I2C would stall NMEA ingest,
|
||||
* so the main thread does the write (MESH_EVENT_RTC_SAVE); posts coalesce to
|
||||
* the latest time. */
|
||||
static atomic_t pending_rtc_epoch = ATOMIC_INIT(0);
|
||||
|
||||
#define MESH_EVENT_BASE (MESH_EVENT_LORA_RX | MESH_EVENT_LORA_TX_DONE | \
|
||||
MESH_EVENT_BLE_RX | MESH_EVENT_HOUSEKEEPING | MESH_EVENT_UI_ACTION | \
|
||||
MESH_EVENT_GPS_ACTION | MESH_EVENT_TX_DRAIN | MESH_EVENT_PREFS_DIRTY | \
|
||||
MESH_EVENT_RTC_SAVE | MESH_EVENT_CONTACT_ITER | MESH_EVENT_LINK)
|
||||
MESH_EVENT_CONTACT_ITER | MESH_EVENT_LINK)
|
||||
#if IS_ENABLED(CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK)
|
||||
#define MESH_EVENT_ALL (MESH_EVENT_BASE | MESH_EVENT_JOYSTICK_LOOP)
|
||||
#else
|
||||
@@ -112,13 +107,6 @@ static atomic_t pending_rtc_epoch = ATOMIC_INIT(0);
|
||||
|
||||
static struct k_event mesh_events;
|
||||
|
||||
/* See pending_rtc_epoch. */
|
||||
static void request_rtc_save(uint32_t epoch)
|
||||
{
|
||||
atomic_set(&pending_rtc_epoch, (atomic_val_t)epoch);
|
||||
k_event_post(&mesh_events, MESH_EVENT_RTC_SAVE);
|
||||
}
|
||||
|
||||
static void process_companion_rx(void); /* runs on MAIN thread (see link_on_rx) */
|
||||
static void run_contact_iteration(void); /* runs on MAIN thread (see MESH_EVENT_CONTACT_ITER) */
|
||||
static void housekeeping_timer_fn(struct k_timer *timer);
|
||||
@@ -434,19 +422,6 @@ static void mesh_event_loop(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* See pending_rtc_epoch. */
|
||||
if (events & MESH_EVENT_RTC_SAVE) {
|
||||
zephcore_rtc_save((uint32_t)atomic_get(&pending_rtc_epoch));
|
||||
#ifdef ZEPHCORE_LORA
|
||||
/* GPS set the clock: arm the time-sync drift envelope and
|
||||
* activate a deferred v-contact. */
|
||||
if (companion_mesh_ptr) {
|
||||
companion_mesh_ptr->noteGPSTimeSync();
|
||||
companion_mesh_ptr->vcontactClockSynced();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK)
|
||||
if (events & MESH_EVENT_JOYSTICK_LOOP) {
|
||||
joystick_ui_task.loop();
|
||||
@@ -562,7 +537,7 @@ static bool companion_transport_tx_idle(void)
|
||||
static CompanionCLICallbacks companion_cli_cbs(data_store, companion_mesh, lora_radio, zephyr_board,
|
||||
ms_clock, packet_mgr, companion_transport_tx_idle);
|
||||
/* No region map and no client ACL: a companion has neither (nullptr both). */
|
||||
static CommonCLI companion_cli(zephyr_board, rtc_clock, nullptr, nullptr,
|
||||
static CommonCLI companion_cli(zephyr_board, rtc_clock, sensors, nullptr, nullptr,
|
||||
&companion_mesh.prefs, &companion_cli_cbs);
|
||||
|
||||
/* CMD_SET_RADIO_PARAMS / CMD_SET_RADIO_TX_POWER (needs companion_cli, hence
|
||||
@@ -754,7 +729,7 @@ static bool companion_shutdown_hook(int reason)
|
||||
return true; /* deliver live — ask the UI for the grace delay */
|
||||
}
|
||||
|
||||
data_store.saveShutdownReason((uint8_t)reason);
|
||||
zephcore_shutdown_reason_save(ZC_SHUTDOWN_LOW_VOLTAGE);
|
||||
return false; /* nobody listening — flash marker, power off now */
|
||||
}
|
||||
|
||||
@@ -885,16 +860,24 @@ static void gps_event_callback(void)
|
||||
k_event_post(&mesh_events, MESH_EVENT_GPS_ACTION);
|
||||
}
|
||||
|
||||
/* A valid fix (GNSS modem_chat thread): set the clock and the node position. */
|
||||
/* A validated fix (main thread, from gps_process_event()): set the clock and
|
||||
* the node position. */
|
||||
static void gps_fix_callback(double lat, double lon, int64_t utc_time)
|
||||
{
|
||||
if (utc_time > 0) {
|
||||
LOG_INF("GPS fix: RTC sync time=%lld", utc_time);
|
||||
rtc_clock.setCurrentTime((uint32_t)utc_time);
|
||||
time_sync_report(TIME_SYNC_GPS);
|
||||
request_rtc_save((uint32_t)utc_time);
|
||||
#ifdef ZEPHCORE_LORA
|
||||
/* Arm the time-sync drift envelope; activate a deferred v-contact. */
|
||||
companion_mesh.noteGPSTimeSync();
|
||||
companion_mesh.vcontactClockSynced();
|
||||
#endif
|
||||
}
|
||||
|
||||
sensors.node_lat = lat;
|
||||
sensors.node_lon = lon;
|
||||
|
||||
#ifdef ZEPHCORE_LORA
|
||||
/* RAM only, as upstream: GPS jitter would otherwise rewrite prefs on
|
||||
* nearly every fix. It reaches flash with the next real settings save. */
|
||||
@@ -985,8 +968,8 @@ int main(void)
|
||||
/* A low-battery shutdown with no app connected left its reason in flash
|
||||
* (see companion_shutdown_hook); the hardware cause cannot tell. */
|
||||
{
|
||||
uint8_t sdr = data_store.takeShutdownReason();
|
||||
if (sdr == UI_SHUTDOWN_LOW_BATTERY) {
|
||||
uint8_t sdr = zephcore_shutdown_reason();
|
||||
if (sdr == ZC_SHUTDOWN_LOW_VOLTAGE) {
|
||||
size_t l = strlen(boot_cause_msg);
|
||||
snprintf(boot_cause_msg + l, sizeof(boot_cause_msg) - l,
|
||||
"%sLast shutdown: low battery",
|
||||
@@ -997,13 +980,16 @@ int main(void)
|
||||
/* Before bt_enable(): a first boot may have to erase the BLE bond store. */
|
||||
data_store.adoptVolume();
|
||||
|
||||
sensor_manager_init();
|
||||
sensors.begin();
|
||||
#ifdef ZEPHCORE_LORA
|
||||
zephyr_board.captureBootVoltage();
|
||||
#endif
|
||||
|
||||
/* Wall-clock time from a battery-backed RTC, if one answers on I2C. */
|
||||
{
|
||||
uint32_t rtc_epoch;
|
||||
if (zephcore_rtc_restore(&rtc_epoch)) {
|
||||
rtc_clock.setCurrentTime(rtc_epoch);
|
||||
rtc_clock.seedCurrentTime(rtc_epoch);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1036,6 +1022,9 @@ int main(void)
|
||||
companion_mesh.prefs.gps_interval = CONFIG_ZEPHCORE_GPS_POLL_INTERVAL_SEC; /* 0 = always on */
|
||||
|
||||
data_store.loadPrefs(companion_mesh.prefs);
|
||||
/* Until the first fix, the position is the configured one. */
|
||||
sensors.node_lat = companion_mesh.prefs.node_lat;
|
||||
sensors.node_lon = companion_mesh.prefs.node_lon;
|
||||
|
||||
/* Saved BLE PIN (0 = Kconfig default) */
|
||||
#if IS_ENABLED(CONFIG_BT)
|
||||
@@ -1080,6 +1069,7 @@ int main(void)
|
||||
}
|
||||
|
||||
companion_mesh.setBatteryCallback(get_battery_mv);
|
||||
companion_mesh.setBoard(&zephyr_board);
|
||||
ui_set_battery_provider(get_battery_mv);
|
||||
ui_set_power_source_provider([]() { return zephyr_board.isExternalPowered(); });
|
||||
ui_set_auto_shutdown_mv(companion_mesh.prefs.auto_shutdown_mv);
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
#define MESH_EVENT_HOUSEKEEPING BIT(3) /* companion: periodic housekeeping */
|
||||
#define MESH_EVENT_MAINTENANCE BIT(3) /* servers: a maintenance deadline came due */
|
||||
|
||||
#define MESH_EVENT_GPS_ACTION BIT(4) /* GPS state change (must run on the main thread) */
|
||||
#define MESH_EVENT_GPS_ACTION BIT(4) /* GPS state change or fix (must run on the main thread) */
|
||||
#define MESH_EVENT_TX_DRAIN BIT(5) /* outbound packet delay expired, run checkSend */
|
||||
#define MESH_EVENT_RTC_SAVE BIT(6) /* hardware-RTC write requested off-main */
|
||||
/* BIT(6) free: was MESH_EVENT_RTC_SAVE, until ZephyrRTCClock wrote the hardware RTC itself */
|
||||
|
||||
#define MESH_EVENT_ROLE_BASE 7 /* first role-only bit */
|
||||
|
||||
@@ -57,6 +57,7 @@ extern "C" void bt_ctlr_assert_handle(char *file, uint32_t line)
|
||||
#include <Serial.h>
|
||||
#include <adapters/clock/ZephyrRTCDiscover.h>
|
||||
#include <ZephyrSensorManager.h>
|
||||
#include <zephyr_poweroff.h>
|
||||
|
||||
/* UI subsystem (display, buttons, buzzer) */
|
||||
#include "ui_task.h"
|
||||
@@ -98,7 +99,7 @@ static const struct gpio_dt_spec led1 = GPIO_DT_SPEC_GET(LED1_NODE, gpios);
|
||||
/* Event bits: the shared ones are in mesh_events.h. */
|
||||
#define MESH_EVENT_INIT_ADVERT BIT(MESH_EVENT_ROLE_BASE) /* Deferred boot advert — send on main thread */
|
||||
#define MESH_EVENT_WAKE BIT(MESH_EVENT_ROLE_BASE + 1) /* Off-main state set; run loop() promptly */
|
||||
#define MESH_EVENT_ALL (MESH_EVENT_LORA_RX | MESH_EVENT_LORA_TX_DONE | MESH_EVENT_CLI_RX | MESH_EVENT_MAINTENANCE | MESH_EVENT_GPS_ACTION | MESH_EVENT_TX_DRAIN | MESH_EVENT_RTC_SAVE | MESH_EVENT_INIT_ADVERT | MESH_EVENT_WAKE)
|
||||
#define MESH_EVENT_ALL (MESH_EVENT_LORA_RX | MESH_EVENT_LORA_TX_DONE | MESH_EVENT_CLI_RX | MESH_EVENT_MAINTENANCE | MESH_EVENT_GPS_ACTION | MESH_EVENT_TX_DRAIN | MESH_EVENT_INIT_ADVERT | MESH_EVENT_WAKE)
|
||||
|
||||
/* Maintenance is deadline-driven, not periodic: after every pass the loop asks
|
||||
* the mesh when its soonest pending deadline is (msUntilNextMaintenance) and
|
||||
@@ -117,18 +118,6 @@ static const struct gpio_dt_spec led1 = GPIO_DT_SPEC_GET(LED1_NODE, gpios);
|
||||
/* Event object for mesh loop */
|
||||
static struct k_event mesh_events;
|
||||
|
||||
/* Pending epoch for a deferred zephcore_rtc_save() — gps_fix_callback runs on
|
||||
* the GNSS modem_chat worker thread, where the RTC's blocking I2C transactions
|
||||
* would stall NMEA ingest. Stash the latest epoch and let the main thread
|
||||
* perform the write; concurrent posts coalesce into one save. */
|
||||
static atomic_t pending_rtc_epoch = ATOMIC_INIT(0);
|
||||
|
||||
static void request_rtc_save(uint32_t epoch)
|
||||
{
|
||||
atomic_set(&pending_rtc_epoch, (atomic_val_t)epoch);
|
||||
k_event_post(&mesh_events, MESH_EVENT_RTC_SAVE);
|
||||
}
|
||||
|
||||
/* USB CDC state */
|
||||
static const struct device *usb_dev;
|
||||
static uint8_t usb_ring_buf_data[USB_RING_BUF_SIZE];
|
||||
@@ -411,7 +400,8 @@ static void gps_event_callback(void)
|
||||
|
||||
static RepeaterDataStore data_store;
|
||||
|
||||
/* GPS fix callback - syncs RTC from GPS time.
|
||||
/* GPS fix callback (main thread, from gps_process_event()) - syncs RTC from
|
||||
* GPS time.
|
||||
* Servers do NOT update prefs lat/lon from GPS — prefs coordinates are the
|
||||
* user's manually-set position used for adverts. Precise GPS position is
|
||||
* served only via telemetry requests (gps_get_last_known_position). */
|
||||
@@ -420,19 +410,25 @@ static void gps_fix_callback(double lat, double lon, int64_t utc_time)
|
||||
if (utc_time > 0) {
|
||||
LOG_INF("GPS fix: RTC sync time=%lld", utc_time);
|
||||
rtc_clock.setCurrentTime((uint32_t)utc_time);
|
||||
/* Defer the hardware-RTC write to the main thread — blocking I2C
|
||||
* here would stall NMEA ingest (gps_fix_callback runs in the
|
||||
* GNSS modem_chat worker context). */
|
||||
request_rtc_save((uint32_t)utc_time);
|
||||
#ifdef ZEPHCORE_LORA
|
||||
/* Arm the mesh time-sync drift envelope. */
|
||||
if (server_role) {
|
||||
server_role->note_gps_time_sync();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* The live position: `gps setloc` and `gps advert share` read it. */
|
||||
sensors.node_lat = lat;
|
||||
sensors.node_lon = lon;
|
||||
|
||||
int lat_deg = (int)lat;
|
||||
int lon_deg = (int)lon;
|
||||
int lat_frac = (int)((lat - lat_deg) * 1000000);
|
||||
int lon_frac = (int)((lon - lon_deg) * 1000000);
|
||||
if (lat_frac < 0) lat_frac = -lat_frac;
|
||||
if (lon_frac < 0) lon_frac = -lon_frac;
|
||||
LOG_INF("GPS fix: lat=%d.%06d lon=%d.%06d (telemetry only)",
|
||||
LOG_INF("GPS fix: lat=%d.%06d lon=%d.%06d",
|
||||
lat_deg, lat_frac, lon_deg, lon_frac);
|
||||
}
|
||||
|
||||
@@ -596,19 +592,6 @@ static void server_event_loop(void)
|
||||
#endif /* ZEPHCORE_HAS_UI */
|
||||
}
|
||||
|
||||
/* Off-main RTC write request (gps_fix_callback runs in modem_chat
|
||||
* context — see request_rtc_save()). Perform the blocking I2C
|
||||
* write here on the main thread instead. */
|
||||
if (events & MESH_EVENT_RTC_SAVE) {
|
||||
zephcore_rtc_save((uint32_t)atomic_get(&pending_rtc_epoch));
|
||||
#ifdef ZEPHCORE_LORA
|
||||
/* GPS just set the clock — arm the mesh time-sync drift envelope. */
|
||||
if (server_role) {
|
||||
server_role->note_gps_time_sync();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Re-arm for the soonest deadline this pass left behind. Done for
|
||||
* every wake, not just maintenance ones: a CLI command, an inbound
|
||||
* packet or a GPS fix can all create or clear a deadline. */
|
||||
@@ -678,14 +661,25 @@ int server_main(const ServerRole &role)
|
||||
}
|
||||
|
||||
/* Initialize sensor manager */
|
||||
sensor_manager_init();
|
||||
sensors.begin();
|
||||
#ifdef ZEPHCORE_LORA
|
||||
zephyr_board.captureBootVoltage();
|
||||
#endif
|
||||
/* Take the shutdown marker now, so it describes this boot only. */
|
||||
{
|
||||
uint8_t sdr = zephcore_shutdown_reason();
|
||||
|
||||
if (sdr != ZC_SHUTDOWN_NONE) {
|
||||
LOG_INF("Last shutdown: %s", zephcore_shutdown_reason_str(sdr));
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore wall-clock time from a battery-backed hardware RTC if present
|
||||
* (shown tagged "L" until the next GPS/CLI sync; no-op if no RTC). */
|
||||
{
|
||||
uint32_t rtc_epoch;
|
||||
if (zephcore_rtc_restore(&rtc_epoch)) {
|
||||
rtc_clock.setCurrentTime(rtc_epoch);
|
||||
rtc_clock.seedCurrentTime(rtc_epoch);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -752,6 +746,9 @@ int server_main(const ServerRole &role)
|
||||
/* LED gate and GPS duty from prefs. After loadPrefs() and ui_init(),
|
||||
* before begin() -- see boot_prefs.h. */
|
||||
apply_boot_prefs(role.prefs, true);
|
||||
/* Until the first fix, the position is the configured one. */
|
||||
sensors.node_lat = role.prefs->node_lat;
|
||||
sensors.node_lon = role.prefs->node_lon;
|
||||
|
||||
/* Start mesh with data store - loads ACL, regions */
|
||||
role.begin(&data_store);
|
||||
|
||||
Reference in New Issue
Block a user