Skip to content

CommunicationsStackOverview

Rob Dobson edited this page May 4, 2026 · 2 revisions

Communications Stack Overview

The Raft communications stack lets a single REST API endpoint (or stream/file transfer) be reached over any connected transport — HTTP, BLE, serial, sockets, WebSocket, etc. This page describes the layers involved and traces a request end-to-end.

For per-layer detail see:


Layered model

   ┌────────────────────────────────────────────────────────────────────┐
   │  Transports (interfaces): WiFi/HTTP, BLE GATT, UART, sockets, ...  │
   └────────────────────────────────────────────────────────────────────┘
                                     │  raw bytes
                                     ▼
   ┌────────────────────────────────────────────────────────────────────┐
   │  Comms Channels  (CommsChannelManager / CommsCoreIF)               │
   │  ─ each channel has a name, an interface name, and a protocol      │
   │  ─ owns inbound/outbound queues + per-channel settings             │
   └────────────────────────────────────────────────────────────────────┘
                                     │  framed messages
                                     ▼
   ┌────────────────────────────────────────────────────────────────────┐
   │  Protocol Codecs  (ProtocolBase implementations)                   │
   │  ─ RICSerial   (HDLC-framed binary) — used for serial / BLE        │
   │  ─ RICFrame    (length-prefixed framing) — used for sockets        │
   │  ─ RICJSON     (JSON-only)                                         │
   │  ─ ROSSerial   (ROS serial; legacy)                                │
   └────────────────────────────────────────────────────────────────────┘
                                     │  CommsChannelMsg
                                     ▼
   ┌────────────────────────────────────────────────────────────────────┐
   │  ProtocolExchange  (SysMod)                                        │
   │  ─ decodes RICREST messages (URL / Body / CmdRespJSON / ...)       │
   │  ─ owns FileStream sessions for upload / download / FW update      │
   │  ─ routes URL requests to the REST API endpoint manager            │
   └────────────────────────────────────────────────────────────────────┘
                                     │  request string + APISourceInfo
                                     ▼
   ┌────────────────────────────────────────────────────────────────────┐
   │  RestAPIEndpointManager                                            │
   │  ─ matches the request to a registered endpoint                    │
   │  ─ calls the endpoint's handler                                    │
   └────────────────────────────────────────────────────────────────────┘
                                     │
                                     ▼
   ┌────────────────────────────────────────────────────────────────────┐
   │  Your SysMod's API handler                                         │
   └────────────────────────────────────────────────────────────────────┘

The same RestAPIEndpointManager is also used directly by the HTTP server (RaftWebServer) — HTTP requests bypass ProtocolExchange and go straight to the endpoint manager (channel ID CHANNEL_ID_REST_API).

Key terms

Term Meaning
Interface A physical/logical transport (e.g. BLE, Serial1, Sockets). Multiple channels can share one interface.
Channel A bidirectional message pipe through an interface. Identified by a channelID allocated by CommsChannelManager::registerChannel().
Protocol codec A ProtocolBase subclass that knows how to frame/unframe bytes for one wire format (RICSerial, RICFrame, RICJSON).
CommsChannelMsg Decoded message: channel ID + protocol + type (command/response/publish/report) + msg number + payload bytes.
RICREST message A CommsChannelMsg whose protocol is MSG_PROTOCOL_RICREST and whose payload begins with a one-byte RICRESTElemCode (URL, Body, CmdRespJSON, CommandFrame, FileBlock).
Endpoint A REST entry point registered via RestAPIEndpointManager::addEndpoint(). Identified by its first path segment.
Subscription A per-channel publish-rate registration created via the subscription REST endpoint.

Channel registry

When a transport SysMod (BLEManager, CommandSerial, RaftWebServer, etc.) starts up, it registers one or more channels by calling CommsChannelManager::registerChannel(). The call provides:

  • protocolName — which codec to attach (e.g. "RICSerial", "RICFrame", "RICJSON")
  • interfaceName — the underlying interface (e.g. "BLE", "CommandSerial")
  • channelName — a unique channel name within that interface
  • outboundHandleMsgCB — function the channel calls to push bytes out of the device
  • outboundCanAcceptCB — flow-control callback
  • A CommsChannelSettings object controlling block sizes and queue depths

The manager returns a channelID that the transport stores and uses for inbound calls (inboundHandleMsg).

The protocol codecs themselves are added separately (typically by ProtocolExchange and other SysMods in their addCommsChannels() overrides) via CommsCoreIF::addProtocol(). The first time a channel needs its codec, CommsChannelManager looks up the protocol name from the channel and instantiates the matching codec.

See Comms Channels for the full channel API.


Anatomy of a request

This walks one BLE-delivered REST request through the stack to make the layering concrete.

Scenario: a mobile app calls subscription?action=update&topic=devjson&rateHz=10 over BLE.

  1. Transport (BLEManager / BLEGapServer) receives a GATT write on the RICREST characteristic. The manager has previously registered a channel with protocolName = "RICSerial", interfaceName = "BLE", channelName = "BLE". It calls commsCoreIF.inboundHandleMsg(channelID, pBytes, len) with the raw GATT payload.

  2. CommsChannelManager queues the bytes into the channel's inbound queue and, on its loop, hands them to the channel's protocol codec via addRxData().

  3. ProtocolRICSerial uses MiniHDLC to deframe the bytes. Once a complete HDLC frame is decoded it calls _msgRxCB (registered by ProtocolExchange) with a fully-populated CommsChannelMsg. The message has protocol = MSG_PROTOCOL_RICREST and a payload whose first byte is the RICREST element code.

  4. ProtocolExchange (in processEndpointMsg) sees MSG_PROTOCOL_RICREST, decodes the inner RICRESTMsg, switches on the element code:

    • URLprocessRICRESTURL() — request string is the URL itself, e.g. "subscription?action=update&topic=devjson&rateHz=10".
    • BodyprocessRICRESTBody() — used for chunked POST-style data.
    • CmdRespJSON → response message (other end's reply).
    • CommandFrame → JSON command frame (cmdName + arbitrary fields).
    • FileBlock → a chunk of a file/stream session.
  5. RestAPIEndpointManager::handleApiRequest is called with the request string and an APISourceInfo(channelID). It splits the request on /, looks up the first segment (subscription) in its endpoint table, and invokes the matching RestAPIFunction callback.

  6. The endpoint handler (in StatePublisher for subscription) does its work and writes a JSON response into respStr.

  7. ProtocolExchange wraps the response in a RICRESTMsg with element code CmdRespJSON, sets it as a response to the original message (endpointMsg.setAsResponse(cmdMsg) — which copies channelID and msgNumber so the response goes back to the right place), and calls getCommsCore()->outboundHandleMsg(endpointMsg).

  8. CommsChannelManager routes the outbound message to the channel matching channelID, which encodes it via its protocol codec (encodeTxMsgAndSend()) and calls the channel's outboundHandleMsgCB. In our case this is BLEGapServer::sendBLEMsg, which writes the bytes to the GATT notify characteristic.

The same handler — subscription — would have been reached if the same request arrived as an HTTP GET, a serial RICSerial frame, or a CommandFrame JSON {"cmdName":"subscription", …} over BLE. The channelID differs, so subsequent publish messages are sent only to the channel that originated the subscription.

HTTP path differences

HTTP requests do not flow through ProtocolExchange:

  1. RaftWebServer receives the request and matches it against its internal handlers.
  2. The RaftWebHandlerRestAPI handler calls RestAPIEndpointManager::handleApiRequest() directly, with APISourceInfo(CHANNEL_ID_REST_API).
  3. The response string returned by the endpoint handler is sent back as the HTTP response body.

POST bodies and chunked uploads use additional callbacks on the registered endpoint (callbackBody, callbackChunk); see Adding REST API Endpoints.

Bridges

A bridge connects two channels so that messages from one are forwarded to the other. This is used (for example) to attach a peripheral device on a serial channel to a BLE-connected host. Bridges are registered via CommsCoreIF::bridgeRegister() and use the special MSG_PROTOCOL_BRIDGE_RICREST envelope so the bridged message keeps its original framing intact.

See Comms Channels for details.

Where to look in the source

Class / file Repo Role
CommsCoreIF, CommsChannelSettings, ProtocolCodecFactoryHelper RaftCore/components/comms/CommsCoreIF Public interface to the comms core
CommsChannelManager, CommsChannel, CommsChannelBridge RaftCore/components/comms/CommsChannels Channel registration and queues
CommsChannelMsg, CommsBridgeMsg RaftCore/components/comms/CommsChannelMsg, CommsBridgeMsg Message containers
ProtocolBase, ProtocolRICSerial, ProtocolRICFrame, ProtocolRICJSON, ProtocolRawMsg, ProtocolOverAscii RaftCore/components/comms/Protocol* Wire-format codecs
RICRESTMsg RaftCore/components/comms/RICRESTMsg RICREST message encoder/decoder
ProtocolExchange, FileStreamSession RaftCore/components/comms/ProtocolExchange Dispatch hub and stream sessions
RestAPIEndpointManager, RestAPIEndpoint RaftCore/components/core/RestAPIEndpoints Endpoint registration and dispatch
MiniHDLC RaftCore/components/core/MiniHDLC HDLC framer used by RICSerial

Clone this wiki locally