From 52644a80a4c948f4be7a38cbd1c83954cb2f1f02 Mon Sep 17 00:00:00 2001 From: liquidraver <504870+liquidraver@users.noreply.github.com> Date: Mon, 5 Oct 2026 13:55:55 +0200 Subject: [PATCH] make gps standby command universal --- docs/ARCHITECTURE.md | 10 ++- docs/BUILDING.md | 2 +- docs/Repeater_CLI_commands.md | 6 +- releasenotes/RELEASE_NOTES_1.17.7-zephcore.md | 76 +++++++++++++++++-- zephcore/Kconfig | 18 +++-- zephcore/adapters/gps/ZephyrGPSManager.cpp | 56 +++++++------- zephcore/adapters/gps/ZephyrGPSManager.h | 10 +-- zephcore/adapters/gps/gps_internal.h | 2 +- zephcore/adapters/gps/gps_power.cpp | 23 +++--- zephcore/boards/example_board/board.overlay | 5 ++ zephcore/helpers/CommonCLI.cpp | 11 +-- zephcore/helpers/NodePrefs.h | 4 +- 12 files changed, 150 insertions(+), 73 deletions(-) diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index cd8337f..d42a8f7 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -795,6 +795,10 @@ Loading also range-checks `freq`/`sf`/`bw` and falls back to the compile-time de - Multi-constellation: GPS+GLONASS+Galileo+BeiDou with fallback - T1000-E / MeshTracker X1 (AG3335): multi-GPIO power sequencing; standby is the module's RTC backup sleep (`$PAIR650,0` before `GPS_EN` is cut, VRTC kept, a `GPS_RTC_INT` pulse wakes it) +- Standby depth is decided from the duty interval alone, on every board: up to `prefs.gps_standby_max` + (`set gps standby`, default 3600 s) the module keeps its state (backup sleep, standby pin, VRTC), beyond it the + board's full power-off is used, as it is for `gps off`. `gps_power.cpp` maps the two onto the pins a board + declares; a board with one off state uses it for both - GPS time blocks phone time sync for 2 hours after last fix - Three files: `ZephyrGPSManager.cpp` (states, fix validation, public API), `gps_power.cpp` (power line / PMU rail / UART sleep commands, UARTE PM), `gps_module_cfg.cpp` (GNSS-API or PMTK/PCAS/UBX configuration, `get gps diag`); `gps_internal.h` carries the feature detection - **Main thread only.** The GNSS callbacks (system work queue) only validate and post: a validated fix is snapshotted and handed to the fix callback by `gps_process_event()` on the main thread, so the callback sets the clock and the node position directly. The UI's GPS toggles post an action too. @@ -814,8 +818,10 @@ Loading also range-checks `freq`/`sf`/`bw` and falls back to the compile-time de Two separate timeouts apply to acquisition: -- `CONFIG_ZEPHCORE_GPS_FIRST_FIX_TIMEOUT_SEC` (default 300s): the cold-start window used for the very first acquisition after `gps_enable()`. Longer to allow almanac download. -- `CONFIG_ZEPHCORE_GPS_FIX_TIMEOUT_SEC` (default 120s): the normal per-wake timeout for all subsequent acquisitions (warm start). +- `CONFIG_ZEPHCORE_GPS_FIRST_FIX_TIMEOUT_SEC` (default 300s): the cold-start window, used for every acquisition that follows a full power-off: the first one after `gps_enable()` (boot, `gps on`; `gps off` is a full power-off on every board) and every wake from a standby longer than `prefs.gps_standby_max`. Longer to allow almanac download and poor reception. +- `CONFIG_ZEPHCORE_GPS_FIX_TIMEOUT_SEC` (default 120s): the normal per-wake timeout for all other acquisitions (warm start). + +Like the standby depth, the choice follows from the duty interval alone, never from the board. **Repeater mode** diff --git a/docs/BUILDING.md b/docs/BUILDING.md index d40ecc4..aebe002 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -134,7 +134,7 @@ Set them in a board conf or with `-DCONFIG_...=`. The full list with help texts | `ZEPHCORE_OFFLINE_QUEUE_SIZE` | 256 | Companion offline message queue | | `ZEPHCORE_BLE_PASSKEY` | 123456 | BLE pairing PIN | | `ZEPHCORE_GPS_POLL_INTERVAL_SEC` | 300 | Companion GPS interval between fixes (10 to 86400); the runtime setting is `set gps duty` | -| `ZEPHCORE_GPS_FIRST_FIX_TIMEOUT_SEC` | 300 | Window for the very first fix after a cold start | +| `ZEPHCORE_GPS_FIRST_FIX_TIMEOUT_SEC` | 300 | Window for a fix after a full GPS power-off: boot, `gps on`, and every wake when the GPS interval is above `gps standby` | | `ZEPHCORE_REPEATER_GPS_INTERVAL_SEC` | 172800 | Repeater and room server GPS interval (48 h); 0 = always on | | `ZEPHCORE_PACKET_LOGGING` | n | Upstream-format packet log lines (`packet_logging.conf`) | | `ZEPHCORE_WIFI_OTA` | n | WiFi AP + HTTP firmware update (ESP32 repeaters) | diff --git a/docs/Repeater_CLI_commands.md b/docs/Repeater_CLI_commands.md index 3e9e47f..48bfe58 100644 --- a/docs/Repeater_CLI_commands.md +++ b/docs/Repeater_CLI_commands.md @@ -154,7 +154,7 @@ Regions control which flood packets the repeater forwards. The region tree is hi | `gps diag` | Receiver liveness, in upstream's key form: `req:<0\|1> en:<0\|1> ok: sat: fix:<0\|1> fa: bc: sc:`. `req` is the requested setting, `en` the module powered and searching (0 in duty-cycle standby), `ok` NMEA sentences the driver has parsed since boot (a count that stops rising means the module or its UART went silent, whatever `sat` says), `fa` the age of the last validated fix, `bc`/`sc` module power-on/off counts. Upstream's UART byte and bad-checksum counters are absent: the GNSS driver only passes valid sentences. Module configuration results are `get gps diag` | | `gps sync` | Take a fresh fix now (wake the GPS or extend its window); the fix sets the clock. `gps is off` / `gps provider not found` otherwise | | `gps on` | Enable GPS module | -| `gps off` | Disable GPS module. Persisted: a repeater or room server with GPS off stays off across reboots. Boards whose GPS has no power line (RAK4631, RAK3401 1W, Station G2) put the module to sleep over UART | +| `gps off` | Disable GPS module. Persisted: a repeater or room server with GPS off stays off across reboots. A full power-off on every board (nothing is kept powered for a fast restart, so the fix after `gps on` is a cold start with the long acquire window). Boards whose GPS has no power line (RAK4631, RAK3401 1W, Station G2) put the module to sleep over UART | | `gps setloc` | Update stored latitude/longitude from the last GPS fix (unchanged if there has been none since boot) | | `gps advert` | Show current location advertising policy | | `gps advert none` | Do not include location in advertisements | @@ -162,7 +162,7 @@ Regions control which flood packets the repeater forwards. The region tree is hi | `gps advert prefs` | Include stored lat/lon from prefs in advertisements | | `set gps duty ` | GPS duty interval (standby seconds between fixes). `0` = always-on (continuous; streams fresh fixes, can download a full almanac). Floor 10s, cap 604800 (1 week). Persists to flash, applied live. | | `set gps duty default` | Reset GPS duty to the role default (repeater/room 48h, companion 300s) | -| `set gps standby ` | Only on boards with a GPS standby pin beside a supply switch (SenseCAP Solar). Duty intervals up to `` keep the module powered in standby between fixes (hot start, a fix in seconds); longer intervals cut its supply (cold start, minutes, but no draw in between). `0` = always cut the supply. Default 3600. Cap 604800. Persists to flash; applies at the next standby. `gps off` always cuts the supply. Other boards answer `Error: no GPS standby pin on this board`. | +| `set gps standby ` | Every board. Duty intervals up to `` keep the GPS module's state between fixes (a fix in seconds on wake); longer intervals power it off fully (cold start, minutes, but no draw in between) and give each wake the long acquire window (300 s on a companion instead of 120 s; servers always use 300 s). `0` = always power off fully. Default 3600. Cap 604800. Persists to flash; applies at the next standby. What "keeps its state" means depends on the board: backup sleep with the RTC supply kept (T1000-E, MeshTracker X1), the module's standby pin with the supply kept (SenseCAP Solar). A board with a single way to switch its GPS off (a bare supply switch, a bare standby pin such as the Wio Tracker L1, or the UART sleep command) does the same on both sides of the limit. | | `set gps standby default` | Reset the standby threshold to 3600 s | | `set gps diag <0\|1\|on\|off>` | Arm GPS module-configuration diagnostics (see below). Not persisted — clears on reboot | @@ -303,7 +303,7 @@ Companion builds of boards whose `zephcore.yml` declares `capabilities: wifi: tr | `get input.rotate` | Joystick/D-pad axis swap: `0` or `1` | | `get tz.offset` | Display timezone: whole hours from UTC, `-12`..`14` (`0` = UTC) | | `get gps duty` | Now-effective GPS duty interval in seconds (`always on (0)` when continuous) | -| `get gps standby` | GPS standby threshold in seconds (see `set gps standby`); `n/a` on a board without a GPS standby pin | +| `get gps standby` | GPS standby threshold in seconds (see `set gps standby`) | | `get gps diag` | What the last GPS module-configuration attempt did — which path ran, bytes sent, and tracked satellites per constellation. See **GPS configuration diagnostics** in the GPS section for the field reference | | `get meshtimesync` | Mesh time-sync state + live dry-run: on/off, eligible voter count, votes for/against, consensus skew and radius, would-be verdict (`ok`/`in-band`/`step±N`/`abstain (reason)`/`hold (reason)`; a recent clock set — manual or GPS — shows as `hold (suppressed)`, and a backward step a forward-only role would refuse is annotated `(skipped: forward-only)`), step counters, suppression countdown, and a per-sender evidence table (`prefix hops count skew E`, `E` = counted toward the verdict above). Entries that count print first, so a size-capped reply never hides the ones that explain the summary; if the table doesn't fully fit, a trailing `+N more` shows how many were left out. Sensing runs even while off, so this works as a dry-run before enabling. Over remote admin the reply is truncated to the packet size (summary always fits); the full table needs the USB CLI. | | `get probe.interval` | Seconds between periodic radio measurements (noise-floor sample + CAD probe). 0 = CAD probing off | diff --git a/releasenotes/RELEASE_NOTES_1.17.7-zephcore.md b/releasenotes/RELEASE_NOTES_1.17.7-zephcore.md index e252b1a..2b1aec2 100644 --- a/releasenotes/RELEASE_NOTES_1.17.7-zephcore.md +++ b/releasenotes/RELEASE_NOTES_1.17.7-zephcore.md @@ -1,7 +1,8 @@ # ZephCore 1.17.7-zephcore -Faster GPS fixes on three trackers, a clock that survives a reboot, and a few fixes ported from -upstream MeshCore. +Faster GPS fixes on three trackers, a clock that survives a reboot, a fix for USB companions that +could stop responding right after boot, more reliable detection of clock chips, and a few fixes ported +from upstream MeshCore. > [!NOTE] > A normal upgrade keeps your identity, settings, contacts and phone pairing. @@ -18,22 +19,83 @@ a search from scratch. Three boards now keep that data: - **SenseCAP Solar**: the receiver stays in standby between fixes instead of being switched off. In marginal reception it got a fix on 5 of 6 wakes, against 2 of 6 before. -On the SenseCAP Solar, standby uses a little power all the time, so it is only used when the GPS -interval is one hour or less. Longer intervals (a repeater's default is 48 hours) and `gps off` switch -the receiver off completely, as before. The limit can be changed with `set gps standby ` +Keeping that data uses a little power all the time, so it is only done when the GPS interval is one +hour or less. Longer intervals (a repeater's default is 48 hours) switch the receiver off completely, +and the next fix is a search from scratch. The limit can be changed with `set gps standby ` (`0` = always switch off, `default` = one hour); `get gps standby` shows it. +`gps off` also switches the receiver off completely, on every board. + +A fix that starts from scratch needs more time, most of all in poor reception. So whenever the +receiver was switched off completely (a GPS interval above the limit, `gps off` then `gps on`, a +boot), a companion gives the next fix 5 minutes instead of 2 before it gives up until the next +interval. Repeaters and room servers already allow 5 minutes every time. + +These rules are the same on every board, and a board added later follows them without any extra +work. A board with only one way to switch its GPS off uses that way on both sides of the limit. + +> [!NOTE] +> **T1000-E and MeshTracker X1:** until now the receiver kept its clock supply through every sleep, +> whatever the interval, and through `gps off`. With a GPS interval above one hour, and after +> `gps off`, it is now switched off completely, so the next fix takes longer. +> `set gps standby 604800` keeps the old behaviour for intervals up to a week. + ## The clock survives a reboot A reboot no longer sends the clock back to 1970 on boards without a clock chip. The time is restored at boot and is at most a few seconds behind. A full power loss still resets it until the next GPS fix or phone sync. +## Clock chips are recognised more reliably + +Some boards carry a battery-backed clock chip (DS3231/DS3232/DS1307, PCF8563, RV-3028, RX8130CE). At +boot the firmware checks that the device answering at the chip's address really is that chip, because +other parts can share the address. That check had two faults: + +- **A real clock chip could be skipped.** If another firmware had left it in 12-hour mode, or with a + year out of range, it was treated as "not a clock": no time was read from it and none was ever + written back, so it stayed that way. +- **An erased memory chip at the same address could be taken for a clock.** + +The check now uses the bits each chip's data sheet defines as always zero, and a device is passed over +only when two reads both rule it out. A time is restored only from a read in which every field is +valid. + +Thanks to **ptr727** for finding and fixing this +([PR #99](https://github.com/liquidraver/ZephCore/pull/99)). + +## USB companion: a hang right after boot + +A companion on USB could stop responding when the host sent it a command straight after opening the +port following a boot: nothing more came out of the USB port and the node stopped handling mesh +traffic, while Bluetooth kept advertising. Only a reset recovered it. + +The cause is in the USB serial driver of the Zephyr version 1.17.6 moved to: with its first 64-byte +buffer already full, it reported free space but accepted no data, and the firmware retried forever. +On a Wio Tracker L1 debug build, where the boot log fills that buffer, it happened on every such boot. +It was not reproduced on a release build, but the boot banner left only 3 bytes of margin there. + +The driver is patched. On the Wio Tracker L1, 6 of 6 debug boots and 3 of 3 release boots now respond, +and the companion protocol test gives the same replies as 1.17.6. + +The same patch covers a second way into the same loop: a host computer going to sleep with the port +open. That one is fixed from reading the code and has not been reproduced on hardware. + ## Also in this release - **Malformed encrypted packets are rejected earlier**, before any cryptography runs. - **Large contact lists**: up to 24 contacts can share the same one-byte hash, up from 8. - **T1000-E**: a pin that was wrongly driven as a sensor enable is left alone. Sensor readings are unchanged. -- **New board**: Seeed LR2021 LoRa Plus EVK with a XIAO nRF54LM20A. -- **Zephyr** updated to `ac03a4a9085`. +- **LR2021 with `rxduty` on** (MeshTracker X1, both LR2021 EVK kits): after every noise-floor + measurement the chip rejected the command that puts it back into its duty cycle. The next channel + check recovered it and no packets were lost; the driver now returns the chip to standby first, and + the command is accepted. +- **New board**: Seeed LR2021 LoRa Plus EVK with a XIAO nRF54LM20A. Build it from source; there is no + published firmware for it yet. +- **Zephyr** updated to `ac03a4a9085`. One change in it would have been visible and is handled: + e-paper displays would have drawn white text on a black page. The RAK4631's LEDs are now declared + by ZephCore, since Zephyr's board file no longer describes them; nothing changes on the board. +- **Documentation**: the README was reorganised, build and flashing instructions moved to + `docs/BUILDING.md`, and the example board and porting guide were rewritten for the current board + layout. diff --git a/zephcore/Kconfig b/zephcore/Kconfig index 826c22f..2bd353f 100644 --- a/zephcore/Kconfig +++ b/zephcore/Kconfig @@ -925,19 +925,23 @@ config ZEPHCORE_GPS_FIX_TIMEOUT_SEC GPS will be powered off after this timeout. Default 120s — 30s was too aggressive for Air530Z / MAX-7Q warm starts in marginal sky conditions; the acquire loop would time out - before the 3-consecutive-good-fix gate could promote. Only applies - to wake cycles after the first successful fix since enable. + before the 3-consecutive-good-fix gate could promote. Applies to + wake cycles where the module kept its state; the first acquisition + and every wake after a full power-off use + ZEPHCORE_GPS_FIRST_FIX_TIMEOUT_SEC. config ZEPHCORE_GPS_FIRST_FIX_TIMEOUT_SEC int "GPS first-acquisition timeout in seconds" default 300 range 30 1800 help - Bounded window for the FIRST position fix after GPS is enabled - (cold start). Longer than the normal fix timeout because a cold - start may need to download almanac data. If no fix is acquired in - this window the node drops into the normal duty cycle anyway, so - the GPS can never stay powered indefinitely with no sky view. + Bounded window for a position fix that follows a full power-off + (cold start): the first one after GPS is enabled, and every wake + when the duty interval is above the `gps standby` limit, on every + board. Longer than the normal fix timeout because a cold start may + need to download almanac data. If no fix is acquired in this + window the node drops into the normal duty cycle anyway, so the + GPS can never stay powered indefinitely with no sky view. Default 300s (5 minutes). Companion mode only; repeaters use a fixed 5-minute time-sync window. diff --git a/zephcore/adapters/gps/ZephyrGPSManager.cpp b/zephcore/adapters/gps/ZephyrGPSManager.cpp index 7d584cb..4e6781b 100644 --- a/zephcore/adapters/gps/ZephyrGPSManager.cpp +++ b/zephcore/adapters/gps/ZephyrGPSManager.cpp @@ -13,7 +13,10 @@ * stays powered, see gps_power.cpp) for fast re-acquisition * - GNSS UARTE suspended (device PM) while GPS is off/standby — releases * HFCLK on nRF52840 (~0.5-1 mA), resumed before every wake - * - Full power-off only on user-disable or System OFF + * - Every board: a standby longer than prefs.gps_standby_max is the board's + * full power-off instead of its state-keeping one (see gps_go_to_standby) + * - Full power-off also on user-disable, on boot with the GPS disabled and on + * System OFF; the acquisition after any full power-off gets the long window */ #include "gps_internal.h" @@ -70,6 +73,7 @@ static enum gps_state gps_current_state = GPS_STATE_OFF; static uint8_t consecutive_good_fixes = 0; static bool first_fix_acquired = false; /* True after first 3-good-fix cycle since enable. Cleared on gps_enable(false) and at boot. */ static bool first_acquire_used = false; /* True once the one-time long cold-start window has ended (fix or timeout). Cleared on gps_enable(false) and at boot. */ +static bool standby_powered_off = false; /* The last standby was the full power-off (interval past gps_standby_max_sec), so the wake after it is a cold start. Set on every standby, cleared on gps_enable(false). */ static bool gps_time_synced = false; /* True after GPS syncs RTC. Starts false at boot (RTC reset), * set true after 3 good fixes, cleared when GPS disabled. */ static int64_t last_fix_uptime_ms = 0; /* k_uptime when last validated fix was acquired */ @@ -488,13 +492,17 @@ static void gps_hold_sleep_lock(bool hold) * on forever when there's no sky. Spent once (first_acquire_used set on the * first standby), after which the node uses the normal duty cycle regardless * of whether a fix was obtained. - * - All later windows: the normal (warm) acquire timeout. */ + * - A wake from a standby that was the full power-off: the same long window, + * every time. The module lost its state, so this is a cold start again, and + * in poor reception that does not fit the warm window. Decided from the + * interval like the power-off itself, never from the board. + * - All other windows: the normal (warm) acquire timeout. */ static uint32_t gps_acquire_window_ms(void) { if (gps_repeater_mode) { return GPS_REPEATER_SYNC_TIMEOUT_MS; } - if (!first_fix_acquired && !first_acquire_used) { + if (standby_powered_off || (!first_fix_acquired && !first_acquire_used)) { return gps_first_fix_timeout_ms; } return gps_acquire_timeout_ms; @@ -530,11 +538,16 @@ static void gps_go_to_standby(void) * RTC survive the cut and re-acquisition is a warm/hot start, not cold. * Other non-GPIO boards: software sleep via UART commands (PMTK + UBX). */ gps_reapply_cancel(); - /* A standby-pin board keeps the supply only for a short interval: its - * Standby draws all the time, a cold start only for a few minutes, so - * past gps_standby_max_sec the supply cut is the cheaper one. */ - gps_module_power(false, !HAS_GPS_WAKEUP || - wake_interval / 1000U <= gps_standby_max_sec); + /* The module keeps its state (backup sleep, standby pin, VRTC) only for + * a short interval: that draws all the time, a cold start only for a + * few minutes, so past gps_standby_max_sec the full power-off is the + * cheaper one. Not a board property: every board takes the same + * decision, and gps_power_control() turns it into whatever the board's + * pins can do. Where they give one off state only (a bare supply + * switch, a bare standby pin, the UART sleep commands) both sides of + * the limit are that state. */ + standby_powered_off = wake_interval / 1000U > gps_standby_max_sec; + gps_module_power(false, !standby_powered_off); /* Module is off/asleep — release the UART until the next wake * (nRF: drops the HFCLK request held by the armed RX). */ @@ -884,12 +897,12 @@ void gps_enable(bool enable) k_work_cancel_delayable(&gps_timeout_work); gps_reapply_cancel(); - /* Power off GPS — AG3335 RTC backup sleep where the board has it - * (Arduino stop_gps), full power off otherwise. - * Boards with no power line get the UART sleep commands, as in - * the duty cycle's standby. A standby-pin board cuts the supply - * here: its Standby still draws, and this off has no end. */ - gps_module_power(false, !HAS_GPS_WAKEUP); + /* Full power-off on every board: this off has no end, and any + * state-keeping one (backup sleep, standby pin, VRTC) draws all + * the time. Boards with no power line get the UART sleep + * commands, as in the duty cycle's standby. The next enable is + * a cold start and gets the long window (flags below). */ + gps_module_power(false, false); /* GPS is off until re-enabled — release the UART. */ gps_uart_set_power(false); @@ -913,6 +926,7 @@ void gps_enable(bool enable) * expecting it to try hard for a fix. */ first_fix_acquired = false; first_acquire_used = false; + standby_powered_off = false; /* Clear time sync flag - time will drift, allow phone sync again */ gps_time_synced = false; @@ -947,20 +961,6 @@ uint32_t gps_get_poll_interval_sec(void) #endif } -bool gps_has_standby_pin(void) -{ - return HAS_GPS_WAKEUP; -} - -uint32_t gps_get_standby_max_sec(void) -{ -#if HAS_GNSS - return gps_standby_max_sec; -#else - return 0; -#endif -} - void gps_set_standby_max_sec(uint32_t sec) { #if HAS_GNSS diff --git a/zephcore/adapters/gps/ZephyrGPSManager.h b/zephcore/adapters/gps/ZephyrGPSManager.h index ecc52ef..15cd451 100644 --- a/zephcore/adapters/gps/ZephyrGPSManager.h +++ b/zephcore/adapters/gps/ZephyrGPSManager.h @@ -105,12 +105,10 @@ void gps_ensure_power_state(bool should_be_enabled); void gps_get_position(struct gps_position *pos); uint32_t gps_get_poll_interval_sec(void); void gps_set_poll_interval_sec(uint32_t interval); -/* Boards with a module standby pin beside a supply switch (gps-wakeup alias): - * duty intervals up to this many seconds use the pin (supply kept, hot start - * on wake), longer ones cut the supply. 0 = always cut. Takes effect at the - * next standby. */ -bool gps_has_standby_pin(void); -uint32_t gps_get_standby_max_sec(void); +/* Every board: duty intervals up to this many seconds keep the module's state + * between fixes (backup sleep, standby pin, VRTC: a fast fix on wake), longer + * ones take the board's full power-off. 0 = always the full power-off. Takes + * effect at the next standby. */ void gps_set_standby_max_sec(uint32_t sec); /* Get RTC time from GPS (returns Unix timestamp, 0 if no valid time) */ diff --git a/zephcore/adapters/gps/gps_internal.h b/zephcore/adapters/gps/gps_internal.h index af3f2fe..5d6aa1c 100644 --- a/zephcore/adapters/gps/gps_internal.h +++ b/zephcore/adapters/gps/gps_internal.h @@ -87,7 +87,7 @@ void gps_sat_tally(uint8_t out[5]); #endif /* ---- gps_power.cpp ---- */ -/* keep_vrtc (off only): T1000-E warm standby. */ +/* keep_vrtc (off only): the board's state-keeping off, if it has one. */ void gps_power_control(bool on, bool keep_vrtc = false); /* The power line or rail if the board has one, else the UART sleep commands. */ void gps_module_power(bool on, bool keep_vrtc = true); diff --git a/zephcore/adapters/gps/gps_power.cpp b/zephcore/adapters/gps/gps_power.cpp index de0236e..ac70bc2 100644 --- a/zephcore/adapters/gps/gps_power.cpp +++ b/zephcore/adapters/gps/gps_power.cpp @@ -147,9 +147,10 @@ static const struct gpio_dt_spec gps_resetb_gpio = GPIO_DT_SPEC_GET(DT_ALIAS(gps /* Module standby pin (L76K WAKEUP: asserted = run, de-asserted = Standby, RF * off with the core powered) on a board that ALSO has a supply switch * (gps-enable): standby uses this pin and keeps the supply, so a wake is a - * hot start; a full off (`gps off`, boot with GPS disabled, System OFF) cuts - * the supply, since Standby still draws. A board whose only control is the - * standby pin (Wio Tracker L1) declares it as gps-enable instead. */ + * hot start; a full off (`gps off`, a standby past prefs.gps_standby_max, + * boot with GPS disabled, System OFF) cuts the supply, since Standby still + * draws. A board whose only control is the standby pin (Wio Tracker L1) + * declares it as gps-enable instead. */ #if HAS_GPS_WAKEUP static const struct gpio_dt_spec gps_wakeup_gpio = GPIO_DT_SPEC_GET(DT_ALIAS(gps_wakeup), gpios); #endif @@ -250,10 +251,13 @@ static void gps_drive_off_levels(bool keep_vrtc, bool system_off) /* GPS power control with warm standby support. * @param on true = power on, false = power off - * @param keep_vrtc When powering off: true = keep VRTC alive (warm standby, - * preserves ephemeris/almanac/RTC for fast re-acquisition), - * false = full power-off (cold start on next wake). - * Only relevant on T1000-E (HAS_GPS_VRTC); ignored on other boards. */ + * @param keep_vrtc When powering off: true = the state-keeping off this board + * has (AG3335 backup sleep, the gps-wakeup standby pin, VRTC + * left on: ephemeris/almanac/RTC survive for a fast + * re-acquisition), false = full power-off (cold start on + * next wake). The caller decides from the standby length + * alone (prefs.gps_standby_max), never from the board; a + * board with one off state lands in it either way. */ void gps_power_control(bool on, bool keep_vrtc) { #if HAS_GPS_POWER_REGULATOR @@ -276,8 +280,9 @@ void gps_power_control(bool on, bool keep_vrtc) * (driver PM can hang on modem_pipe_close / modem_chat_run_script). * The GNSS driver's modem pipe stays open. * - * T1000-E / X1 (HAS_GPS_BACKUP_SLEEP): standby and app toggle use the - * AG3335 RTC backup sleep, as Arduino stop_gps(). + * T1000-E / X1 (HAS_GPS_BACKUP_SLEEP): standby up to + * prefs.gps_standby_max uses the AG3335 RTC backup sleep, as Arduino + * stop_gps(); a longer standby and `gps off` cut VRTC too. * Simple boards (Wio etc.): Full power off/on via GPS_EN. */ if (on) { #if HAS_GPS_WAKEUP diff --git a/zephcore/boards/example_board/board.overlay b/zephcore/boards/example_board/board.overlay index 6c78c04..cf482d9 100644 --- a/zephcore/boards/example_board/board.overlay +++ b/zephcore/boards/example_board/board.overlay @@ -755,6 +755,11 @@ zephyr_udc0: &usbd { * gps-vrtc-enable backup (VRTC) supply, kept on for warm starts * gps-sleep-int, gps-rtc-int AG3335 sleep / RTC interrupt pins (t1000_e) * + * Nothing else is needed for `set gps standby`: on every board a standby + * longer than that limit (default one hour) is a full power-off, a shorter + * one keeps the module's state if the pins above give a way to (gps-wakeup, + * or gps-vrtc-enable). With gps-enable alone the two are the same. + * * chosen zephcore,gps-power a regulator instead of a pin: a * regulator-fixed node, or a PMU rail (heltec_t096, * ttgo_tbeam) diff --git a/zephcore/helpers/CommonCLI.cpp b/zephcore/helpers/CommonCLI.cpp index 1dbde6a..1a64fd0 100644 --- a/zephcore/helpers/CommonCLI.cpp +++ b/zephcore/helpers/CommonCLI.cpp @@ -1202,8 +1202,7 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, const char* command, cha reply[0] = '>'; reply[1] = ' '; gps_get_diag_report(reply + 2, cap - 2); } else if (memcmp(config, "gps standby", 11) == 0) { - if (!gps_has_standby_pin()) strcpy(reply, "> n/a (no GPS standby pin on this board)"); - else snprintf(reply, CLI_REPLY_SIZE, "> %u", (unsigned)gps_get_standby_max_sec()); + snprintf(reply, CLI_REPLY_SIZE, "> %u", (unsigned)_prefs->gps_standby_max); } else if (memcmp(config, "gps duty", 8) == 0) { uint32_t s = gps_get_poll_interval_sec(); // now-effective value if (s == 0) strcpy(reply, "> always on (0)"); @@ -1807,16 +1806,14 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, const char* command, cha strcpy(reply, "usage: set gps diag <0|1|on|off>"); } } else if (memcmp(config, "gps standby", 11) == 0) { - // set gps standby | default (0 = always cut the supply) + // set gps standby | default (0 = always the full power-off) const char* arg = config + 11; while (*arg == ' ') arg++; char* end = NULL; unsigned long parsed = strtoul(arg, &end, 10); bool is_default = strcmp(arg, "default") == 0; - if (!gps_has_standby_pin()) { - strcpy(reply, "Error: no GPS standby pin on this board"); - } else if (!is_default && (*arg == '\0' || end == arg || *end != '\0' || parsed > GPS_INTERVAL_MAX_SEC)) { - strcpy(reply, "usage: set gps standby | default (0 = always cut the supply)"); + if (!is_default && (*arg == '\0' || end == arg || *end != '\0' || parsed > GPS_INTERVAL_MAX_SEC)) { + strcpy(reply, "usage: set gps standby | default (0 = always power off)"); } else { uint32_t val = is_default ? GPS_STANDBY_MAX_DEFAULT_SEC : (uint32_t)parsed; _prefs->gps_standby_max = val; diff --git a/zephcore/helpers/NodePrefs.h b/zephcore/helpers/NodePrefs.h index b2e2f86..2667e36 100644 --- a/zephcore/helpers/NodePrefs.h +++ b/zephcore/helpers/NodePrefs.h @@ -142,8 +142,8 @@ struct NodePrefs { * without this marker is upgraded to gps_enabled = 1 once. */ uint8_t gps_enabled_set; uint32_t gps_interval; // in seconds - /* Standby-pin boards: duty intervals up to this (seconds) keep the GPS - * supply and use the standby pin, longer ones cut it. 0 = always cut. */ + /* Every board: duty intervals up to this (seconds) keep the GPS module's + * state between fixes, longer ones power it off fully. 0 = always off. */ uint32_t gps_standby_max; uint8_t advert_loc_policy; uint32_t discovery_mod_timestamp;