Files
trikkeSensors/docs/ble-transport-v1.md
T

69 lines
3.2 KiB
Markdown

# Reliable BLE Transport — Version 1
BLE carries the unchanged, CRC-protected `TRK1` frames defined in
`binary-record-v1.md`. The default peripheral name is `TrikkeSensor`.
## 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 |
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.
## 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 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. The reference
`tools/capture_ble.py` implements 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.
Version 1 is an unauthenticated, single-connection prototype service. It does
not yet provide pairing, authorization, or confidentiality against a nearby
peer; those are separate from the loss/replay guarantees above. A nearby peer
can also deny availability by subscribing and never acknowledging: firmware
correctly retains and replays the owned frame, but the RAM queue eventually
fills while the legitimate receiver remains excluded. Pairing and connection
authorization are required before treating this as a hostile-environment
logger.