Protocol
System Design establishes the Station–Pod link as a request–response protocol with the Station as sole initiator, and names three conceptual message categories: commands, status queries, and events. This page defines the wire-level realization of that model: the frame layout, how a request is correlated with its response, and the concrete communication patterns built on top of that correlation. The request–response model itself and the choice to generate the protocol code with commsdsl are decisions made elsewhere and are not repeated here. See ADR-003 and ADR-006.
The protocol is defined as a single schema shared by the Station and every Pod, so both sides agree on the same frame layout, message set, and field encoding by construction. Schema: cocktail-maker-protocol.
Framing
Every exchange between the Station and a Pod is carried in a single frame:
| Field | Type | Purpose |
|---|---|---|
Sync |
|
Fixed marker that lets a receiver find the start of the next frame on the raw USB CDC byte stream. |
Size |
|
Length of the frame from this point onward, used to know when a full frame has been received. |
MsgId |
|
Identifies which message the payload contains (e.g. |
TransactionId |
|
Correlates this frame with the request it belongs to. See Transaction Correlation. |
Payload |
message-specific |
The fields of the message identified by |
Checksum |
|
Computed over Size through Payload. Protects the frame’s integrity on the wire. |
There is no byte-stuffing or escaping layer. Framing relies on Sync, Size, and the CRC alone.
Transaction Correlation
TransactionId is not a field of any individual message. It is a transport-level field carried by
every frame, regardless of which message it holds.
The Station generates a TransactionId for each request it sends and reuses it for every frame that
belongs to the same exchange. This includes an immediate Ack/Nak, a dedicated response, and any
event that concludes a longer-running action later on.
A Pod must echo the TransactionId of the request it is reacting to on every frame it sends back for
that exchange.
Because a TransactionId is only required to be unique among requests that are currently pending, the
Station is free to reuse a value once its exchange has concluded.
This is the only correlation mechanism the protocol has. There is no separate request or session
identifier.
TransactionId scopes exactly one request/response exchange. It does not identify any higher-level
operation a caller of the protocol may be carrying out, and such an operation may itself require
several such exchanges.
Communication Patterns
Every message exchange follows one of three patterns, distinguished by how many frames a Pod sends back for a single request and how far apart in time they arrive.
Request / Response
The simplest pattern: a request is answered by exactly one dedicated response message, carrying the
same TransactionId.
Used by Ping/Pong (keep-alive) and DeviceInfoRequest/DeviceInfoResponse.
Request / Acknowledgement
A request that only needs to confirm it was accepted is answered by the generic Ack, or Nak if it
could not be executed.
Nak carries an ErrorCode explaining the reason, rather than each command defining its own failure
message.
Used by EmergencyStop, LoadCellCalibrateWithRefWeight, LoadCellTare, and HighlightDispenser.
| Value | Meaning |
|---|---|
|
A field in the request had an invalid value. |
|
The referenced |
|
The Pod cannot perform this action in its current state. |
|
An underlying actuator or sensor reported a fault. |
|
The Pod is already executing another action. |
|
The action requires a calibration that has not been performed. |
|
An unspecified internal error occurred. |
|
The dispenser has no ingredient left to act on. |
Request / Acknowledgement / Event
Some requests trigger a physical action whose duration cannot be bounded tightly enough to hold a
single response open. Dispensing a liquid and running a pump calibration routine are both examples
of this.
These are acknowledged immediately with Ack/Nak, and once the underlying action completes, the Pod
sends a dedicated event carrying the same TransactionId as the original request.
This is the mechanism that lets the Station attribute an event arriving seconds later back to the
exact request that caused it, even though other exchanges may have started and finished with
different transaction ids in the meantime.
Used by Dispense (followed by DispenseFinished) and PumpStartCalibration (followed by
PumpFinishedCalibrationResponse). The completion event must reuse the TransactionId of the
request that triggered it.
A Nak can still arrive in place of the event if the action fails after having been accepted, so a
caller of this pattern must be prepared to receive either outcome under the same TransactionId.
Messages
| Message | Direction | Category | Purpose |
|---|---|---|---|
|
Station → Pod |
Request / Response |
Keep-alive request. |
|
Pod → Station |
Request / Response |
Keep-alive response. |
|
Pod → Station |
Request / Acknowledgement |
Generic positive acknowledgement of a command, identified by |
|
Pod → Station |
Request / Acknowledgement |
Generic negative acknowledgement, carrying an |
|
Station → Pod |
Request / Response |
Requests the Pod’s device information. |
|
Pod → Station |
Request / Response |
Hardware/firmware revision, pump/valve counts, and device name. |
|
Station → Pod |
Request / Acknowledgement |
Requests the Pod to bring itself into a safe state. |
|
Station → Pod |
Request / Acknowledgement |
Calibrates a dispenser’s load cell against a known reference weight. |
|
Station → Pod |
Request / Acknowledgement |
Zeroes a dispenser’s load cell at its currently applied weight. |
|
Station → Pod |
Request / Acknowledgement / Event |
Starts calibration of a pump over a given number of steps. |
|
Pod → Station |
Request / Acknowledgement / Event |
Reports steps-per-millilitre once a pump calibration has finished. |
|
Station → Pod |
Request / Acknowledgement |
Drives a dispenser’s LED effect for a given duration. |
|
Station → Pod |
Request / Acknowledgement / Event |
Requests a given amount of liquid to be dispensed. |
|
Pod → Station |
Request / Acknowledgement / Event |
Reports the amount actually dispensed once the action has finished. |