Merge USB session safety and preserve GPS preference compatibility

This commit is contained in:
mikecarper committed 2026-10-03 16:12:32 -07:00
commit 24fcbf3247
62 files changed
+2479 -240

No files matched your search

+2
View File
@@ -208,6 +208,7 @@ should use command `0x42`. See [Companion radio binary protocol](companion_proto
| Retry | [`get/set/clear recent.repeater`; `get recent.repeaters`](cli_commands.md#view-seed-or-clear-the-recent-repeater-table) | Repeater | Yes | Yes | Yes |
| GPS | [`get gps`; `set gps on/off`](cli_commands.md#view-or-change-gps-state) | Compiled GPS | Feature | Feature | Feature |
| GPS | [`gps sync`](cli_commands.md#sync-this-nodes-clock-with-gps-time) | Compiled GPS | Feature | Feature | Feature |
| GPS | [`get/set gps.sync.interval`](cli_commands.md#set-the-automatic-gps-clock-sync-interval) | Compiled GPS and provider; 1-336 hours | Feature | Feature | Feature |
| GPS | [`gps setloc`](cli_commands.md#set-this-nodes-location-based-on-the-gps-coordinates) | Compiled GPS | Feature | Feature | Feature |
| GPS | [`gps advert [none/share/prefs]`](cli_commands.md#view-or-change-the-gps-advert-policy) | Compiled GPS | Feature | Feature | Feature |
| Sensors | [`get/set telemetry.access`](cli_commands.md#view-or-change-telemetry-access-mode) | Sensor-capable role | Feature | Feature | Limited |
@@ -382,6 +383,7 @@ should use command `0x42`. See [Companion radio binary protocol](companion_proto
| Retry | [`get/set/clear recent.repeater`; `get recent.repeaters`](cli_commands.md#view-seed-or-clear-the-recent-repeater-table) | Repeater | Yes | Yes | Yes | Yes | Yes |
| GPS | [`get gps`; `set gps on/off`](cli_commands.md#view-or-change-gps-state) | Compiled onboard GPS | Feature | Feature | Feature | Feature | Feature |
| GPS | [`gps sync`](cli_commands.md#sync-this-nodes-clock-with-gps-time) | Compiled onboard GPS | Feature | Feature | Feature | Feature | Feature |
| GPS | [`get/set gps.sync.interval`](cli_commands.md#set-the-automatic-gps-clock-sync-interval) | Compiled GPS and provider; 1-336 hours | Feature | Feature | Feature | Feature | Feature |
| GPS | [`gps setloc`](cli_commands.md#set-this-nodes-location-based-on-the-gps-coordinates) | Compiled onboard GPS | Feature | Feature | Feature | Feature | Feature |
| GPS | [`gps advert [none/share/prefs]`](cli_commands.md#view-or-change-the-gps-advert-policy) | Compiled onboard GPS | Feature | Feature | Feature | Feature | Feature |
| Sensors | [`get/set telemetry.access`](cli_commands.md#view-or-change-telemetry-access-mode) | Sensor-capable full parser | Feature | Feature | Limited | Feature | Feature |
+39 -1
View File
@@ -2422,7 +2422,8 @@ a trace already on air is allowed to finish. Locally initiated traffic and
management replies are not cancelled.
The dynamic configuration field is `repeat.trace_off` (`0` or `1`). It is
appended to `/com_prefs`; all previously saved field offsets stay unchanged.
appended to `/com_prefs`; all field offsets in the published upstream layout
stay unchanged.
---
@@ -4561,6 +4562,43 @@ On repeaters with device power saving enabled, the automatic GPS cycle wakes
for up to 10 minutes and repeats seven days after GPS sleeps. A valid GPS time
can correct the RTC either forward or backward. With device power saving off,
an enabled GPS refreshes the RTC every 30 minutes.
These are the legacy defaults; `gps.sync.interval` below overrides them.
---
#### Set the automatic GPS clock-sync interval
```text
get gps.sync.interval
set gps.sync.interval 1
set gps.sync.interval 24
set gps.sync.interval 336
```
The interval is a whole number of **hours**, from **1 to 336** (two weeks).
Larger numbers are capped, not rejected. For example:
```text
set gps.sync.interval 999
OK - GPS sync interval 336 hours (saved)
```
Zero, negative numbers, fractional hours, and nonnumeric values are rejected.
The setting is saved across reboots and supported by GPS-equipped repeater,
room-server, sensor, and Companion builds. Until it is explicitly configured,
the getter reports `default (board GPS sync policy)` and existing board timings
are retained.
The selected interval controls periodic clock acquisitions both with GPS power
saving and with a continuously powered receiver. Changing it reschedules a
sleeping receiver without immediately powering it on. A GPS fix can take time;
an unsuccessful acquisition retains the existing bounded awake window, then
backs off before trying again. Device GPS/power switches still apply.
This does not change position-update `gps_interval`, location telemetry/cache
timings, or mesh/NTP time synchronization. Location requests can still power GPS
for a fix, but do not bypass the configured clock-sync interval. Explicit
`gps sync` remains an immediate acquisition request where that command exists.
---
+6
View File
@@ -118,6 +118,12 @@ The Companion app's `gps=1` / `gps=0` custom setting controls the same GPS.
Only boards with a compiled GPS provider expose this setting. Sharing location
with contacts is a separate setting.
Use `set gps.sync.interval <hours>` to save an automatic clock-sync interval of
1-336 hours (two weeks); `get gps.sync.interval` reports it. Larger inputs are
capped at 336 and the reply shows the applied value. Existing GPS timings are
unchanged until configured. This controls clock synchronization, not the
position-update `gps_interval` or location-sharing policy.
When the exact Full image includes MQTT, use WebConfig's MQTT cards or the
same CLI settings used by infrastructure, such as `set mqtt1.preset custom`
and `set mqtt1.server broker.example.com`. `set mqtt.enabled on|off` controls
@@ -41,12 +41,14 @@ Run only one PlatformIO process in the checkout at a time.
```sh
python test/test_esp32_tinyusb_nonblocking.py
python test/test_esp32_usb_session_fix.py
python test/test_esp32_tinyusb_role_hygiene.py
python test/test_esp32_tinyusb_cooperative_output.py
python test/test_esp32_usb_serial_hygiene.py
python test/test_nrf52_usb_logging_contract.py
python test/test_nrf52_usb_lifecycle.py
python test/test_common_cli_logging_transaction.py
python test/test_companion_usb_logging_reply.py
python test/test_usb_diagnostic_logging.py
pio test -e native -f test_nrf52_debug_output -f test_serial_packet_log -f test_serial_mode_switch -f test_mesh_tables
```
@@ -84,6 +86,28 @@ ownership; USB is log-only during that session. Disabling logging restores USB
traffic without accepting bytes typed during the log-only interval. A dedicated
nRF52 logging port does not park the separate Companion port.
A framed USB logging command retains its original requester and queues its
acknowledgement before changing ownership. The handoff advances without waiting
for the host: live logging stays unchanged while the complete USB reply is
backpressured. Disconnect/reset cancels that session's pending handoff; an
already-saved preference can still take effect at the next boot. BLE/WiFi replies
are never used as a fallback for a refused USB acknowledgement.
On native ESP32 TinyUSB, build-local framework hooks capture DTR and USB session
boundaries synchronously in the USB owner task, before Arduino's delayed events.
They purge old input at the boundary and reject late input while DTR is low,
without deleting a new host's first query during main-loop cleanup. An armed
old IN packet (or a racing writer) triggers immediate soft-disconnect, a minimum
20 ms detached interval, and quarantine until fresh enumeration. Shared SDK
files are not modified; a changed native-CDC descriptor fails the build closed
until the hook is reviewed. This path is specific to the pinned Arduino-ESP32
2.0.17 native CDC implementation; hardware Serial/JTAG and UART are unchanged.
Disabling diagnostics is not itself a protocol change and does not discard
functional replies from the shared ESP32 text queue. Actual Binary/mOTA changes
use the explicit terminal-output barrier. Touch and invalid-Ethernet-hostname
diagnostics also honor the runtime logging gate and bounded USB stream.
Ethernet and enabled MQTT diagnostics honor the runtime logging gate and use
the bounded USB facade. Infrastructure logging setters commit preferences
before changing live output; failed saves leave previous settings unchanged.
@@ -95,6 +119,10 @@ advance one row per service pass, using their existing output buffer. Reconnect
cancels the old listing. The listing is a bounded live view, not an immutable
snapshot of clients changed during output.
Stored dumps make progress even when HWCDC falls back to 256/512-byte TX rings.
Recent-repeater headers/rows and EOF retain every unwritten suffix; a short
write never causes their accepted prefix to be retried.
nRF52 FIFO cleanup also checks for an already-armed IN packet. If one exists
at a session boundary (including a pending zero-length packet), both USB ports
briefly detach and re-enumerate: a FIFO clear alone cannot recall endpoint RAM.
@@ -103,3 +131,31 @@ without blocking the mesh loop. Dedicated CDC descriptors are added while
detached even if the host has not yet completed enumeration. These recovery
paths have native regression coverage; real host/board timing still requires
hardware qualification.
The nRF52 callback only drops the pull-up and closes producer gates. The
application loop sends the stack's unplug event afterward, so a full owner-task
event queue cannot make its consumer wait on itself. The detach interval starts
when that application-side stack detach actually occurs. A regression models a
full finite event queue as well as the separate armed endpoint packet.
## Local qualification (2026-10-03)
These are the upstream USB-session qualification results before the subsequent
USB watchdog, GPS preference, and repeater trace merge. The byte counts below
describe those earlier binaries, not the current merged source.
- All 112 USB Python regression methods passed, plus nine logging-transaction,
Bluetooth-control, and WiFi-status methods. The native facade includes a
threaded close-versus-writer race and an unmount-with-reset-endpoints check.
- `Xiao_S3_companion_radio_usb` built successfully with its default hardware
Serial/JTAG transport: 880,709 flash bytes and 73,244 static RAM bytes.
- A native-TinyUSB Xiao S3 USB Companion qualification build passed with debug
and packet logging enabled: 928,133 flash bytes and 89,312 static RAM bytes.
Its linked ELF contains the synchronous session hooks and endpoint query.
- A RAK4631 Full Companion qualification build with two CDCs, debug/packet
logging, and USB/BLE mOTA sources passed: 678,092 flash bytes and 154,428
static RAM bytes. The runtime RAM and Bluetooth DFU safety checks also passed.
The two custom qualification builds used the lab profile
`909.5 MHz / 500 kHz / SF5 / CR5`. No board was flashed, and no physical
disconnect/reconnect or hardware endpoint recovery was claimed by these checks.
+21
View File
@@ -163,6 +163,10 @@ as immediate alerts. Reading status does not renew the logging-client lease.
## Verification
The two qualification stages below precede the subsequent upstream USB/GPS
and repeater trace merge. Their firmware byte margins describe those earlier
images, not the current merged source.
Initial watchdog qualification on 2026-10-03, before the latest-event extension,
passed 109 USB regression tests, 143 native test cases, 11 management protocol
tests, and the browser decoder checks.
@@ -196,3 +200,20 @@ can still exceed that tight budget. No features or filesystem space were cut
to fit this extension. This is not an all-board release qualification or a
physical USB recovery test. No hardware was flashed, and verification artifacts
stayed outside `out`.
The subsequent USB/GPS/repeater-trace merge passed all 1,718 native cases,
133 USB regression methods, 25 management/event/watchdog methods plus the
browser decoder, and 10 GPS/trace/persistence methods. Companion preference
tests exercise six platform/layout variants under fatal sanitizers. The
compact final preference writer also matches the previous full image for
256 tail-value sets per variant and preserves committed files through every
partial-tail write/readback boundary and rename failure.
Compile checks for that merge passed standard XIAO S3 Full Companion, Station
G2 Repeater and Room Server, native-USB XIAO S3 USB Companion with all five
session/progress hooks linked, and XIAO nRF52 Repeater. The final compact writer
passed both the bare STM32 CI recipe and the normal `1.17.1.9` recipe; the
latter uses 229,020 of 229,376 app flash bytes (356 bytes of margin), retaining
all features and the 32 KiB filesystem. The bare recipe is tighter still.
These checks do not qualify every board or physical USB recovery. No device
was flashed and existing `out` artifacts were not changed.