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

3.2 KiB

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.