OBC OpenBikeComputer / docs

The companion link

The companion link moves stored objects between OpenBikeComputer and a client. BLE and USB use the same protocol-v4 frames. BLE also supplies pairing, settings, clock, bond removal, and weather-refresh controls. USB supplies object transfer and device information only.

The normative contracts are:

Two planes: control and data

Protocol v4 has a control channel and a stream channel. Control frames select an operation and report its result. Stream frames carry PUT and GET payload bytes. Only one PUT or GET can be active.

BLE maps control frames to the objectControl GATT characteristic. It maps stream frames to one L2CAP connection-oriented channel (CoC). The open protocolVersion characteristic contains u16 value 4. After authentication, the psm characteristic identifies the CoC.

USB binding v5 uses one bulk endpoint pair for each plane. Both transports deliver identical protocol-v4 frame bytes to one transfer engine.

Two planes — protocol v4 control and stream companion app BLE central (iPhone) OBC device BLE peripheral nRF54L Control plane · GATT small, typed state — identity · config · orchestration objectControl · protocolVersion · psm · command · status · config ≤ 512 bytes per attribute — a hard wall Data plane · L2CAP CoC one raw byte pipe · credit-based flow control stream frames, one transfer at a time
BLE uses GATT for control records and L2CAP CoC for stream records. USB uses two bulk endpoint pairs.

Each control frame has a 16-byte header. It contains the OBC4 magic, protocol major, opcode, flags, length, and RequestId. The client selects a nonzero RequestId. The terminal response echoes it. The same value identifies all stream frames for a PUT or GET.

The adapter delivers the control frame before related stream frames. It uses link backpressure if a stream frame arrives first. BLE credits and USB packet completion do not mean that data are durable. Only a successful store commit makes an upload durable.

Protocol-v4 operations

OperationFunction
LISTRead paged catalog entries, StoreId, and commit sequence.
STATUSCheck one known object and revision.
GETDownload one committed object revision.
PUTCreate or replace one object with one commit.
REMOVERemove one object head and any retained revision.
CANCELStop the active PUT or GET.
ARMRequest validation and installation of an uploaded firmware package.
FORMATReplace the card with a new empty flat store.

The protocol, clients, and board adapters implement the ARM request and response. The current nRF54LM20 board policy rejects every ARM request with rejected. Uploading an update package does not install it.

There is no protocol negotiation, wire minor, session, operation ID, or unsolicited status frame. A client sends LIST before other operations. A new StoreId invalidates all cached catalog data. A changed commit sequence tells the client to read the catalog again.

Stored object kinds

ValueKindPayload or function
1RouteOBCR route
2TripOrdered route membership
3RideDevice-produced recording
4Weather bundleOBCW weather data
5MapOne OBCM file with embedded terrain
6RetiredMap-set manifest; producers must not write it
7Update packageOBCU firmware package
8Rollback reserveBootloader rollback space

ObjectId and Revision are unsigned 64-bit values. Object IDs are store-global and are not reused. A create starts at revision 1. A replace increments the revision. A LIST entry also supplies kind, flags, length, CRC-32, and a UTF-8 display name. The display name has a maximum of 48 bytes.

Transfers and commits

PUT

A PUT declares the object identity, expected revision, kind, name, length, and CRC-32. Object ID zero creates an object. A nonzero ID replaces the expected revision.

The client sends stream frames from absolute offset zero. Offsets must be contiguous and increasing. The device writes to an unpublished allocation. After the final byte, it verifies these items:

  • Declared payload length.
  • Whole-payload CRC-32/IEEE.
  • Validator rules for the object kind.
  • Expected revision immediately before commit.

A successful response supplies the object ID, new revision, length, and CRC. An error makes the new bytes unreachable. A cancelled or disconnected transfer releases its allocation. There is no resume or checkpoint operation.

One PUT — request · stream · commit companion app OBC device 1 · PUT control frame — objectControl write RequestId · kind · ObjectId · length · CRC-32 2 · stream frames — L2CAP CoC sink → storage, running CRC no whole-object buffer 3 · final byte → verify whole-object CRC-32 4 · PUT response: committed or error
A PUT is one request. The device commits only after length, CRC, and kind validation succeed.

GET

A GET selects an object ID and an optional revision. The device opens that revision and streams bytes in increasing offset order. The response supplies the served revision, length, and CRC. The client verifies the complete length and CRC.

REMOVE and CANCEL

REMOVE uses the object ID and expected head revision. It removes the head and its retained revision in one commit. It cannot remove an active recording or reserved object.

CANCEL names the active PUT or GET RequestId. It stops the transfer but does not remove an existing committed object. A link loss has the same transfer result.

Reconciliation

Use STATUS after an interrupted replacement. A committed result confirms the requested revision. An absent or superseded result means that replacement did not become the head.

A create has no assigned ID before its commit response. After a lost create response, use LIST. Match kind, payload length, payload CRC, and display name. If multiple entries match, the iOS client keeps the greatest ObjectId. It removes the other matches with their exact revisions. Do not infer state from a notification or operation log.

Reconciliation — LIST identifies the store and catalog on the device store changes upload · ride device-side delete catalog commit StoreId · sequence LIST response on the phone LIST changed → download the list paged LIST GET required objects, on the stream on the next audit Protocol v4 has no ride-acknowledgment mutation phone stores verified ride retired — no command device catalog unchanged
LIST supplies the store identity, commit sequence, and catalog entries. Clients use it to reconcile state.

Rides become downloadable after the RECORDING flag clears. The iOS client lists finished rides, downloads them, and verifies their CRC. Protocol v4 has no ride-possession mutation. The current iOS ackRides compatibility method sends no command. The board does not accept the retired ackRides command.

Pairing and BLE controls

The phone uses LE Secure Connections passkey entry. The device displays a six-digit passkey. The rider enters it in the phone system dialog. This process creates one authenticated bond.

The device stores one phone bond. While this bond exists, it rejects pairing from a different phone. The device action Forget phone clears the bond. The bonded phone can also send forgetBond. The Bluetooth power setting does not remove the bond.

Device Information, Battery, and protocolVersion are open before pairing. psm, objectControl, commands, and configuration require encryption and authentication. The device also refuses an unencrypted CoC.

① First pairing — once device shows 428 913 rider reads it, types it on the phone LESC ECDH MITM-protected bond stored ② Every time after — silent device advertises stable address phone knows this identity re-encrypt · stored LTK resolve RPA · stored IRK connected encrypted no dialog bonded + powered + in range ⇒ connected + encrypted, no interaction ③ Another phone, while bonded — rejected a different phone tries to pair bond exists → no passkey, link dropped phone sees a generic failure only way in: Forget phone
One passkey creates one bond. Later connections use the stored keys. A bonded device rejects a second phone.

BLE keeps a device-local command and configuration surface beside protocol v4. It supports clock setting, bond removal, weather refresh, and settings. These controls are not flat-store objects. They do not exist in USB binding v5.

The phone sets UTC and local offset after encryption. A GPS fix can also establish trusted UTC.

Sensors: the device as BLE central

For the phone, the device is a BLE peripheral. For sensors, the device is a BLE central. Both roles use one radio at the same time.

The sensor manager supports these standard services:

  • Heart Rate Service.
  • Cycling Power Service.
  • Cycling Speed and Cadence Service.
  • Battery Service.

Sensors use their saved address and do not use the phone bond. The device has one saved slot for heart rate, power, and cadence. A power meter can supply cadence when no dedicated cadence sensor is configured.

Two roles, one radio — peripheral to the phone, central to the sensors companion app BLE central (iPhone) OBC device peripheral · to phone central · to sensors one radio · nRF54L the phone link heart-rate strap HRS · 0x180D power meter Cycling Power · 0x1818 cadence sensor CSC · 0x1816 scan · connect · subscribe one radio — MPSL time-slices the peripheral (phone) and central (sensor) roles; no second radio sensors are open GATT servers — connected by stored address, no bond, one saved slot per quantity
The device is a BLE peripheral for the phone and a BLE central for sensors.

The manager scans, connects, discovers, subscribes, decodes, and dispatches measurements. It reconnects after a link failure while Bluetooth is enabled. A value older than 5 seconds becomes unavailable. The ride recorder stores fresh sensor samples and summary statistics. The device does not stream live sensor values to the phone.

BLE and USB binding differences

PropertyBLEUSB binding v5
Protocol framesVersion 4Version 4
Control planeGATT objectControlControl bulk endpoint pair
Stream planeL2CAP CoCStream bulk endpoint pair
AuthorizationAuthenticated bondPhysical cable access
Device-local controlsAvailableNot available
Device informationGATT servicesEP0 GET_DEVICE_INFO

USB advertises bInterfaceProtocol = 5 and bcdDevice = 0x0500. A host checks these values before it exchanges a record. Protocol frames still contain major 4.

Each USB record contains these parts:

  1. A little-endian 32-bit record length.
  2. Exactly that many protocol-frame bytes.
  3. Zero padding to a four-byte boundary.

USB packet boundaries have no record meaning. A record can span multiple packets. The stream-record ceiling is 8,208 bytes, including its 16-byte header. A stream payload is therefore at most 8,192 bytes. The host-to-device control-record ceiling is 256 bytes.

USB has no mass-storage binding. The firmware remains the only owner of the card.

Implementation