60 lines
2.6 KiB
Markdown
60 lines
2.6 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.
|
|
|
|
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.
|