-
Notifications
You must be signed in to change notification settings - Fork 3
FileDownloadProtocol
The File Download OKTO Protocol handles downloading files from a Raft device to a connected client (e.g. via BLE or WebSocket). It uses a batch-acknowledged block transfer with flow control and CRC integrity checking.
The client initiates a download by sending a dfStart command:
{
"cmdName": "dfStart",
"reqStr": "getFile",
"fileType": "fs",
"fileName": "data/log.csv",
"batchMsgSize": 5000,
"batchAckSize": 4,
"crcAt": "end"
}| Field | Description |
|---|---|
fileName |
Path to the file on the device filesystem |
fileType |
Source type, typically "fs" for filesystem |
batchMsgSize |
Requested block size in bytes |
batchAckSize |
Number of blocks per batch before requiring an ack |
crcAt |
Optional. Set to "end" to defer CRC computation. Omit for legacy behaviour (CRC at start). |
The server responds with:
{
"req": "dfStart",
"rslt": "ok",
"batchMsgSize": 5000,
"batchAckSize": 4,
"streamID": 1,
"fileLen": 45678,
"crc16": "a1f2"
}| Field | Description |
|---|---|
batchMsgSize |
Actual block size (may be clamped by channel limits) |
batchAckSize |
Actual batch ack size |
streamID |
Non-zero session identifier for this transfer. The FILEBLOCK field can represent 1..255; 0 is reserved internally as FILE_STREAM_ID_ANY and is not a valid data-block session ID. |
fileLen |
Total file size in bytes |
crc16 |
Present only when CRC is computed at start (legacy mode or old firmware). CRC-16/CCITT of the entire file, hex-encoded. |
The server sends file data as binary RICREST file-block frames. Blocks are sent in batches of batchAckSize. Each block contains:
- The allocated
streamIDfromdfStart - File position (24-bit byte offset)
- Block data
The FILEBLOCK header encodes the stream ID and file position as:
[streamID:1 byte][filePos:3 bytes big-endian][payload:N bytes]
Equivalently, the implementation stores the header position word as:
(streamID << 24) | (filePos & 0x00ffffff)
Download FILEBLOCKs for a dfStart session must carry the same non-zero streamID returned in the dfStart response. streamID 0 means "unspecified / any" inside the session lookup code and must not be emitted for normal transfer data.
After receiving a batch of blocks, the client sends an acknowledgement:
{
"cmdName": "dfAck",
"okto": 20000,
"streamID": 1,
"rslt": "ok"
}The okto field tells the server how many bytes have been successfully received. The server uses this to advance its send window. If no ack is received within the timeout, the server retries from the last acknowledged position.
The streamID field must match the active dfStart session being acknowledged.
After receiving all data, the client sends a dfEnd message and waits for the response:
{
"cmdName": "dfEnd",
"reqStr": "getFile",
"fileName": "data/log.csv",
"fileLen": 45678,
"streamID": 1
}The server responds with:
{
"req": "dfEnd",
"rslt": "ok",
"crc16": "a1f2"
}The crc16 field is present when CRC was deferred to the end ("crcAt": "end" in dfStart). The client verifies the file integrity by computing CRC-16/CCITT over the received data and comparing.
Either side can cancel the transfer:
{
"cmdName": "dfCancel",
"reqStr": "getFile",
"streamID": 1
}The server also sends a cancel message if it detects a timeout or error, including a reason field.
The protocol supports two modes for CRC-16/CCITT integrity checking:
When crcAt is omitted or set to "start" in the dfStart request:
- Server reads the entire file to compute CRC before responding to dfStart
- CRC is included in the dfStart response
- Client stores CRC and verifies after all data is received
- dfEnd response does not include CRC
This mode causes a blocking delay at the start of transfer proportional to file size, as the file must be read twice (once for CRC, once for sending).
When crcAt is set to "end" in the dfStart request:
- Server responds to dfStart immediately without reading the file for CRC
- CRC is computed incrementally as each block is sent during the transfer
- CRC is included in the dfEnd response
- Client verifies CRC after receiving the dfEnd response
This mode eliminates the blocking delay at transfer start and avoids reading the file twice.
The crcAt field provides seamless backward compatibility between old and new versions of firmware and client libraries:
| Firmware | Client | Behaviour |
|---|---|---|
| Old | Old | Legacy mode. CRC in dfStart response. |
| Old | New | New client sends crcAt:"end", old firmware ignores unknown field. CRC still in dfStart response. Client detects CRC in dfStart and uses it. |
| New | Old | Old client doesn't send crcAt. New firmware defaults to legacy mode. CRC in dfStart response. |
| New | New | Deferred mode. No CRC in dfStart. Incremental CRC in dfEnd response. Non-blocking start. |
-
Block timing: Minimum
MIN_TIME_BETWEEN_BLOCKS_MS(100ms) between blocks -
Batch acknowledgement: Server sends
batchAckSizeblocks then waits for a dfAck -
Ack timeout: If no ack within
BLOCK_MSGS_TIMEOUT_MS(3s), server retries from last acked position -
Max retries:
MAX_BATCH_BLOCK_ACK_RETRIES(10) before cancelling -
Heap throttling: Blocks are paused when free internal heap drops below
HEAP_LOW_WATER_MARK_BYTES(35KB) -
Overall timeout:
DOWNLOAD_FAIL_TIMEOUT_MS(2 hours) to accommodate slow BLE transfers
Server-side files in RaftCore/components/comms/:
| File | Purpose |
|---|---|
FileStreamProtocols/FileDownloadOKTOProtocol.h/.cpp |
Protocol state machine, block sending, ack handling, CRC modes |
ProtocolExchange/FileStreamSession.h/.cpp |
Session management, file chunker access, CRC callback |
FileStreamProtocols/FileStreamBase.h |
Base class with callback type definitions |
Client-side files in raftjs/src/:
| File | Purpose |
|---|---|
RaftFileHandler.ts |
File download orchestration, CRC verification |
RaftTypes.ts |
Response type definitions, including RaftFileStartResp and RaftFileDownloadResult
|
For upload-style finite and open-ended streams that use ufStart, see Real-Time Streams and ProtocolExchange.
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