6.3 KiB
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, neverRETRY, 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.