Add reliable Android BLE ride recorder
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
# Reliable BLE Transport — Version 2
|
||||
|
||||
BLE carries the unchanged, CRC-protected `TRK1` frames defined in
|
||||
`binary-record-v1.md`. The default peripheral name is `TrikkeSensor`. Version 2
|
||||
adds a mandatory, idempotent recording-session handshake; the data envelope and
|
||||
application ACK remain unchanged from version 1.
|
||||
|
||||
## GATT service
|
||||
|
||||
| Purpose | UUID | Properties |
|
||||
| --- | --- | --- |
|
||||
| Service | `7d2ea000-f75b-4a9b-8fbe-3d4c2a1e9c10` | Primary service |
|
||||
| Data | `7d2ea000-f75b-4a9b-8fbe-3d4c2a1e9c11` | Notify |
|
||||
| ACK | `7d2ea000-f75b-4a9b-8fbe-3d4c2a1e9c12` | Write, write without response |
|
||||
| Control | `7d2ea000-f75b-4a9b-8fbe-3d4c2a1e9c13` | Write |
|
||||
|
||||
The firmware prefers a 256-byte ATT MTU, allowing the largest 196-byte `TRK1`
|
||||
frame and its eight-byte BLE envelope to fit in one notification. Smaller MTUs
|
||||
remain protocol-compatible; firmware sends at most eight fragments per bounded
|
||||
poll, although sustained 100 Hz delivery still depends on the negotiated link.
|
||||
|
||||
## Recording-session handshake
|
||||
|
||||
Every new capture generates a random uint64 session token. The reference
|
||||
receivers first write exactly 12 bytes to Control—ASCII `BGN1`, then that token
|
||||
as little-endian uint64—and enable data notifications after that write succeeds.
|
||||
Firmware also accepts the reverse order: it tracks the raw CCCD state separately
|
||||
from session authorization and treats the connection as subscribed as soon as
|
||||
both conditions are true. Notifications never flow before authorization.
|
||||
|
||||
When the token differs from the C3's active token, firmware stores it in
|
||||
RTC-retained memory and performs a controlled software restart. Acquisition and
|
||||
output tasks remain stopped after boot. The receiver reconnects and repeats the
|
||||
same `BGN1` write; firmware recognizes the retained token, authorizes that BLE
|
||||
connection, starts acquisition with empty queues and zeroed volatile counters,
|
||||
and accepts the subsequent notification subscription.
|
||||
|
||||
The Android acceptance capture measured 10.797 seconds from the user's Start
|
||||
action to the first durably persisted frame. That includes the initial control
|
||||
write, controlled restart, advertising and scan latency, GATT reconnection,
|
||||
same-token authorization, notification subscription, and first frame delivery.
|
||||
This startup interval is expected and is not part of the recorded sensor stream.
|
||||
|
||||
The same token must be written on every reconnect during one recording. That
|
||||
write is idempotent: it authorizes the new connection without restarting or
|
||||
discarding the in-flight frame and sample backlog. A different token is an
|
||||
explicit new-session boundary and deliberately discards all prior volatile
|
||||
state through the controlled restart.
|
||||
|
||||
Firmware does not start acquisition after boot and does not honor a notification
|
||||
subscription until a valid control write authorizes the connection. This avoids
|
||||
pre-session queue overflow and prevents an older client from bypassing the
|
||||
session boundary. The token provides idempotence, not authentication or secrecy.
|
||||
|
||||
RTC token recovery is intentionally accepted only after `ESP_RST_SW`. A panic,
|
||||
watchdog, brownout, or power-on reset discards the token and all volatile stream
|
||||
state. When the still-recording receiver reconnects and rewrites its unchanged
|
||||
token, firmware treats it as a new token, performs one additional controlled
|
||||
restart, and authorizes the following same-token reconnect. Packet and sample
|
||||
sequences restart at zero inside the receiver's existing file, where the reset is
|
||||
observable through integrity tracking. This favors a known clean state over
|
||||
silently treating an uncontrolled reset as continuation of the old session.
|
||||
|
||||
## Data notification envelope
|
||||
|
||||
Every notification starts with an eight-byte little-endian envelope:
|
||||
|
||||
| Offset | Size | Field |
|
||||
| ---: | ---: | --- |
|
||||
| 0 | 4 | `TRK1` packet sequence |
|
||||
| 4 | 2 | Byte offset within the complete `TRK1` frame |
|
||||
| 6 | 2 | Complete `TRK1` frame size |
|
||||
| 8 | remaining | Consecutive frame bytes at that offset |
|
||||
|
||||
Offset zero starts or restarts a frame. A receiver appends only consecutive
|
||||
offsets for the same sequence and total size, then validates the complete
|
||||
`TRK1` header and CRC. A malformed or missing fragment is not acknowledged.
|
||||
|
||||
## Application ACK and replay
|
||||
|
||||
After validating and persisting a frame, the receiver writes exactly eight bytes
|
||||
to the ACK characteristic: ASCII `ACK1`, then the acknowledged packet sequence
|
||||
as little-endian uint32. Firmware accepts an ACK only for the frame it currently
|
||||
owns and only from the active subscribed connection.
|
||||
|
||||
`COMPLETE` is not reported to the output task until that ACK arrives. Until then:
|
||||
|
||||
- a one-second ACK timeout replays the frame from offset zero;
|
||||
- disconnect or notification unsubscription preserves the frame;
|
||||
- the next subscription, after the same-token control write, replays it from
|
||||
offset zero;
|
||||
- BLE polling returns `PENDING`, never `RETRY`, after ownership begins.
|
||||
|
||||
The ACK itself can be lost after the receiver persisted the frame. Receivers
|
||||
therefore compare the sequence and raw bytes with their last persisted frame,
|
||||
avoid writing a duplicate, and ACK the replay again. Both reference receivers
|
||||
implement this ordering.
|
||||
|
||||
The rejected-ACK counter is diagnostic, not a pure corruption count. A valid
|
||||
duplicate ACK can arrive after the output task has already completed that frame
|
||||
and begun the next one; firmware then rejects and counts the now-stale write.
|
||||
|
||||
BLE notification success only means the fragment entered the stack. The `ACK1`
|
||||
write is the end-to-end boundary. It deliberately confirms application
|
||||
persistence rather than radio or ATT delivery alone.
|
||||
|
||||
The current firmware reserves a 3072-sample acquisition queue, providing 30.72
|
||||
seconds of transport-outage tolerance at the nominal 100 Hz rate. This interval
|
||||
includes link-loss detection, scanning, GATT reconnection, session
|
||||
reauthorization, notification subscription, and replay—not merely the time a
|
||||
phone's Bluetooth control is visibly off. Longer interruptions remain bounded
|
||||
and detectable through sample-sequence gaps and the queue-overflow counter.
|
||||
|
||||
Version 2 remains an unauthenticated, single-connection prototype service. It
|
||||
does not yet provide pairing, authorization, or confidentiality against a nearby
|
||||
peer; those are separate from the session and replay guarantees above. A nearby
|
||||
peer can also deny availability by connecting and withholding the control write,
|
||||
or by subscribing and never acknowledging. Pairing and connection authorization
|
||||
are required before treating this as a hostile-environment logger.
|
||||
Reference in New Issue
Block a user