-
Notifications
You must be signed in to change notification settings - Fork 3
RICRESTProtocol
RICREST is Raft's transport-agnostic message envelope that carries REST-style requests, responses and binary stream blocks over non-HTTP links (BLE, serial, sockets). On the wire it is normally wrapped by one of the framing protocols RICSerial, RICFrame or RICJSON; the RICREST payload itself is the same in all three.
For higher-level flow see Communications Stack Overview and ProtocolExchange.
┌────────────────────────────────────────────────────────────────┐
│ Outer framing : RICSerial (HDLC) | RICFrame (len-prefixed) | │
│ RICJSON (raw JSON) │
└────────────────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────┐
│ CommsChannelMsg header │
│ - msg number (1 byte for RICFrame; managed by RICSerial HDLC)│
│ - protocol code (RICREST, BridgeRICREST, RawCmdFrame, ...) │
│ - type code (Command, Response, Publish, Report) │
└────────────────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────┐
│ RICREST element (only when protocol == RICREST) │
│ - 1 byte element code │
│ - element-specific payload │
└────────────────────────────────────────────────────────────────┘
ProtocolRICSerial uses MiniHDLC to deframe a byte stream:
-
0x7Eis the FLAG byte that delimits frames. -
0x7Dis the ESCAPE byte; the byte that follows is XOR'd with0x20. - Each frame ends with a CRC.
- The bytes inside the frame are the
CommsChannelMsgheader followed by its payload (see below).
This is the framing used over UART and BLE GATT characteristics, where the byte stream has no inherent message boundary.
ProtocolRICFrame is used where the transport already provides framing (TCP socket, WebSocket binary frame). It does not add HDLC byte-stuffing or a CRC. Each frame is just:
byte 0 : msgNumber (0..255)
byte 1 : (msgTypeCode << 6) | (msgProtocolCode & 0x3f)
bytes 2+ : payload (= RICREST element when protocol == RICREST)
msgTypeCode is 0=COMMAND, 1=RESPONSE, 2=PUBLISH, 3=REPORT.
msgProtocolCode is the CommsMsgProtocol enum (2=RICREST, 3=BridgeRICREST, 0x3e=RawCmdFrame, 0x3f=None, 0=ROSSerial).
ProtocolRICJSON carries pure JSON with no envelope. The JSON document itself encodes whatever metadata it needs and is delivered to the application as a CommsChannelMsg with the JSON in the payload.
When carried inside a RICSerial HDLC frame, the first two bytes are the same as RICFrame's header:
byte 0 : msgNumber
byte 1 : (msgTypeCode << 6) | (msgProtocolCode & 0x3f)
bytes 2+ : payload
So at the API level RICSerial and RICFrame produce identical CommsChannelMsg objects — they differ only in how the bytes are framed for the underlying transport.
When msgProtocolCode == MSG_PROTOCOL_RICREST (2), the payload begins with a one-byte element code:
| Code | Name | Inbound role | Outbound role |
|---|---|---|---|
0 |
URL |
Request as URL string (endpoint/path?query=...). |
— |
1 |
CMDRESPJSON |
Request as JSON { "reqStr": "...", … }. |
Response wrapping the endpoint's JSON output. |
2 |
BODY |
Body chunk for a prior URL request (POST-style data). | — |
3 |
COMMAND_FRAME |
JSON { "cmdName": "...", … } flattened into a query string. |
— |
4 |
FILEBLOCK |
A binary chunk of a file/stream session. | A binary chunk of a file/stream session. |
byte 0 : 0x00
bytes 1..n : ASCII URL, e.g. "subscription?action=update&topic=devjson&rateHz=10"
The whole tail of the message is the URL as a UTF-8 string (max length capped by RICRESTMsg::getMaxRestBodySize() — 5000 without PSRAM, 200000 with).
byte 0 : 0x01
bytes 1..n : ASCII JSON object
Inbound: the JSON contains a reqStr field (and optionally other fields). The framework treats reqStr as if it were a URL element.
Outbound: the JSON is the endpoint's response document, e.g.
{"req":"subscription","rslt":"ok"}The outbound message is marked as a response (msg type code 1) and reuses the original message's msgNumber so the requester can match request to response.
byte 0 : 0x02
bytes 1..4 : bufferPos (uint32, big-endian) ← offset of this chunk in the overall body
bytes 5..8 : totalBytes (uint32, big-endian) ← total body length
bytes 9..n : binary payload
Used to deliver a multi-chunk request body to a previously-registered URL endpoint.
byte 0 : 0x03
bytes 1..n : ASCII JSON object with at least a "cmdName" field
The JSON is converted into a request string of the form cmdName?<flattened-fields> and dispatched via the same path as a URL element. This is convenient for senders that prefer to encode all parameters as JSON instead of a URL.
byte 0 : 0x04
byte 1 : streamID (uint8, non-zero session ID)
bytes 2..4 : filePos / offset (24-bit unsigned integer, big-endian)
bytes 5..n : binary payload
Used by the file/stream session machinery (see File Download Protocol, Real-Time Streams, and the upload/firmware-update flows owned by ProtocolExchange).
The streamID byte identifies the active file/stream session allocated by ufStart or dfStart. It is an 8-bit field, so non-zero session IDs are representable in the range 1..255. streamID value 0 is reserved internally as FILE_STREAM_ID_ANY ("unspecified / match by other criteria") and must not be used for normal FILEBLOCK data belonging to an allocated session.
The stream ID and position are stored as one big-endian 32-bit word in code:
(streamID << 24) | (filePos & 0x00ffffff)
This limits the per-block file/stream position field to 24 bits. Protocol implementations that need larger transfers must handle that at the session/protocol layer rather than placing position bits in the top byte.
Note — the precise byte offsets above match the constants in
RICRESTMsg.h(RICREST_BODY_*,RICREST_FILEBLOCK_*). For exact, copy-pasteable values see that header. If you are implementing a non-Raft client, refer directly to thedecode()andencode()functions inRICRESTMsg.cpprather than transcribing the table.
| Transport | Recommended framing |
|---|---|
| BLE GATT characteristic | RICSerial (HDLC) |
| UART / RS-232 | RICSerial (HDLC) |
| TCP socket | RICFrame |
| WebSocket (binary) | RICFrame |
| WebSocket (text-only) | RICJSON |
| HTTP REST | none — use the URL directly via RestAPIEndpointManager
|
For request/response correlation:
- The sender allocates a
msgNumberin 0..255 (rolling). - The framework's response path calls
endpointMsg.setAsResponse(cmdMsg)which copies thechannelIDandmsgNumberfrom the original request. - Receivers match responses to requests on
(channelID, msgNumber, msgTypeCode == RESPONSE).
Asynchronous messages (publishes, reports) use MSG_TYPE_PUBLISH / MSG_TYPE_REPORT and a different msgNumber stream.
Getting Started
- Quick Start
- Architecture at a Glance
- Writing Your First SysMod
- Adding a Comms Channel
- Adding an I2C Device Type
- PlatformIO / Arduino
Scaffolding & Building
- Raft CLI
- SysTypes
- Top-Level SysType
- Build Process
- WebUI Build Pipeline
- File System
- Partitions & Flash
- Local Dev Libraries
- Library Developer Guide
Architecture
Built-in SysMods
- NetworkManager
- 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
Helpers
Reference