Repository navigation
CommunicationsStackOverview
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:
- Comms Channels — channel registration, inbound/outbound message flow, transport settings
- ProtocolExchange — the SysMod that decodes RICREST messages and dispatches them
- RICREST Protocol — wire formats used over non-HTTP transports
- Adding REST API Endpoints — registering endpoints from a SysMod
- File Download Protocol (OKTO) — stream/file-block transfer that runs through this stack
-
Real-Time Streams — open-ended
rtstreamsessions over RICRESTFILEBLOCKframes
┌────────────────────────────────────────────────────────────────────┐
│ 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) — serial / BLE / WebSocket │
│ ─ 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).
| 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. |
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
CommsChannelSettingsobject 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.
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.
-
Transport (BLEManager /
BLEGapServer) receives a GATT write on the RICREST characteristic. The manager has previously registered a channel withprotocolName = "RICSerial",interfaceName = "BLE",channelName = "BLE". It callscommsCoreIF.inboundHandleMsg(channelID, pBytes, len)with the raw GATT payload. -
CommsChannelManager queues the bytes into the channel's inbound queue and, on its loop, hands them to the channel's protocol codec via
addRxData(). -
ProtocolRICSerial uses
MiniHDLCto deframe the bytes (runs of plain bytes are copied in one go - see MiniHDLC). Once a complete HDLC frame is decoded it calls_msgRxCB(registered byProtocolExchange) with a fully-populatedCommsChannelMsg. The message hasprotocol = MSG_PROTOCOL_RICRESTand a payload whose first byte is the RICREST element code. -
ProtocolExchange (in
processEndpointMsg) seesMSG_PROTOCOL_RICREST, decodes the innerRICRESTMsg, switches on the element code:-
URL→processRICRESTURL()— request string is the URL itself, e.g."subscription?action=update&topic=devjson&rateHz=10". -
Body→processRICRESTBody()— 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.
-
-
RestAPIEndpointManager::handleApiRequest is called with the request string and an
APISourceInfo(channelID)(which, for a numbered message, allows the handler to defer its response). It splits the request on/, looks up the first segment (subscription) in its endpoint table, and invokes the matchingRestAPIFunctioncallback. -
The endpoint handler (in
StatePublisherforsubscription) does its work and writes a JSON response intorespStr. -
ProtocolExchange wraps the response in a
RICRESTMsgwith element codeCmdRespJSON, 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 callsgetCommsCore()->outboundHandleMsg(endpointMsg). -
CommsChannelManager queues the response on the channel matching
channelID. On its loop it encodes it via the channel's protocol codec (encodeTxMsgAndSend(), which HDLC-encodes in one pass straight into the outgoing message) and calls the channel'soutboundHandleMsgCB. In our case this isBLEGapServer::sendBLEMsg, which queues the bytes to be sent on the response characteristic as notifications or indications, in pieces sized from the connection's MTU.
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 requests do not flow through ProtocolExchange:
-
RaftWebServerreceives the request and matches it against its internal handlers. - The
RaftWebHandlerRestAPIhandler creates aRaftWebResponderRestAPI, which calls the endpoint's main callback withAPISourceInfo(CHANNEL_ID_REST_API)- for a GET from its loop, for a POST (or other method) once the whole request body has been received. Either way the endpoint may defer its response. - The response string returned by the endpoint handler (or completed later) 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.
Everything above runs on the main task, which also runs every SysMod's loop(), so a request handler that takes long holds up the whole device (polling, publishing, other links). The aim is to keep each main-loop pass short (around 10 ms) and do slow work elsewhere:
- A handler that can't answer at once defers its response: it returns
RAFT_RESP_DEFERRED, does the work from itsloop()a step at a time, in a task, or on the file system worker, and completes the response later on the main task. HTTP, RICREST messages with a non-zero message number (serial, BLE, WebSocket), the serial console's typed commands, command files and the remote logger can all take a deferred response. See Deferred Responses. - File uploads are written, and file downloads and static web files read, on the file system worker rather than in the main loop.
-
SysManagerreports a SysMod whoseloop()takes longer thanslowSysModMs(default 50 ms) - see SysManager.
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.
| 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 |
Getting Started
- Quick Start
- Architecture at a Glance
- Writing Your First SysMod
- Adding a Comms Channel
- Adding an I2C Device Type
Scaffolding & Building
- Raft CLI
- Web Apps (Device Types, Flasher)
- SysTypes
- Top-Level SysType
- Build Process
- WebUI Build Pipeline
- File System
- Partitions & Flash
- Local Dev Libraries
- Library Developer Guide
Architecture
Built-in SysMods
- NetworkManager
- WiFi Troubleshooting
- BLEManager
- WebServer
- MQTTManager
- SerialConsole
- CommandSerial
- CommandSocket
- CommandFile
- FileManager
- LogManager
- ESPOTAUpdate
- StatePublisher
- Remote Logging
- Data Source Registration
Comms & Protocols
- Stack Overview
- Comms Channels
- ProtocolExchange
- RICREST Protocol
- Real-Time Streams
- Adding REST Endpoints
- Built-in REST Endpoints
- File Download (OKTO)
- OTA Update Flow
Devices & Buses
- DeviceManager
- Device Manager REST API
- Device Factory & Classes
- Device Type Records
- Adding an I2C Device Type
- Device Data Publishing
- Data Logger
- I2C Bus
- I2C Device Scanning
- I2C ID & Polling
- MotorControl Overview
- MotorControl Config
- MotorControl Commands
- Camera
ROS 2
Helpers
Reference