Skip to content

RaftHelperClasses

Rob Dobson edited this page Oct 27, 2026 · 5 revisions

Raft Helper Classes

Reference index of utility classes provided by RaftCore/components/core/. These are general-purpose helpers reachable from any SysMod or application code.

For high-level subsystems (file system, network, comms, devices) see the dedicated pages linked from Home.

Concurrency and timing

Class Purpose
RaftThreading Cross-platform task / mutex / semaphore wrappers (FreeRTOS on ESP32, std::thread on Linux unit tests).
ThreadSafeQueue<T> Bounded thread-safe queue (mutex-protected, elements held in PSRAM where available) - see ThreadSafeQueue below.
RingBuffer<T> Lock-free single-producer / single-consumer ring buffer.
Raft::isTimeout() Wrap-safe timeout check on millis()-style timestamps.
SupervisorStats Per-step timing histogram for SysMod loop work; used by SysManager.

I/O and signals

Class Purpose
DebounceButton Edge-debounced GPIO input with hold/click events.
StatusIndicator LED status patterns (steady, blink, breathe, fault codes).
ConfigPinMap Resolve pin names like "GPIO5" or "D7" from JSON config to numeric pins.
MiniHDLC HDLC byte-stuffing framer with CRC; used by ProtocolRICSerial and ESPOTAUpdate. See MiniHDLC below.
RaftConsolePort Take the console's port for a binary protocol and silence every other console writer; give it back later. See Console Port Sharing.

Data and parsing

Class / namespace Purpose
RaftJson / RaftJsonIF JSON parser used everywhere — see JSON / RaftJson.
RaftJsonNVS RaftJson backed by ESP NVS (non-volatile storage).
RaftJsonPrefixed Layered JSON view with a path prefix — used to give each SysMod its own subtree.
ExpressionEval Evaluate arithmetic / boolean expressions with named variables.
NumericalFilters Moving-average / IIR / median filters for sensor data.
NamedValueProvider Generic interface for "fetch a named value as int/float/string".
Raft::libb64 Base64 encode/decode.
SpiramAwareAllocator<T> (Utils/) STL allocator that uses PSRAM when the board has it (falling back to internal RAM), e.g. SpiramAwareUint8Vector. See SpiramAwareAllocator below.

Persistence and identity

Class Purpose
ArPreferences Arduino-Preferences-compatible key/value store over NVS.
DNSResolver Asynchronous DNS lookup with caching. No lookup is made until the device has an IP address; the first is made at once and a failed one is retried after 5 s.
getSystemUniqueString() Stable unique string derived from the chip's MAC — appended to the MQTT client ID, used as an advertising name suffix, etc.

Logging

Class Purpose
LoggerCore Singleton that fan-outs ESP IDF log records to registered remote loggers.
LOG_I/W/E/D/V Standard ESP IDF log macros — work as-is in Raft.

For remote-logging configuration see LogManager SysMod and Remote Logging.

Architecture

Class Purpose
RaftCoreApp Top-level container: creates SysManager, file system, configuration, REST endpoint manager. Your main.cpp instantiates one of these.
SysManager Owns SysMods and drives the lifecycle. See SysManager.
RaftSysMod Base class for every module. See SysMods.
RestAPIEndpointManager Endpoint registry used by HTTP, BLE, serial — see Adding REST API Endpoints.
CommsCoreIF / CommsChannelManager Channel registry — see Comms Channels.
RaftBusSystem (Bus/) Generic bus abstraction; concrete implementations live in RaftI2C.
RaftDevice Per-device base class used by DeviceManager.

LED pixels

LEDPixels/ provides RMT-driven WS2812 / SK6812 / APA102 strips with effects. Configured purely through JSON.

See LEDPixels overview and LED Pixels JSON Config.

ThreadSafeQueue

ThreadSafeQueue<ElemT> (ThreadSafeQueue/ThreadSafeQueue.h) is the queue used between tasks and by the comms channels. The default maximum length is 50 (setMaxLen() changes it).

Method Behaviour
put(const ElemT&) Copies the element in.
put(ElemT&&) Moves the element in, but only if it is queued: if the put fails (queue full, or the mutex not obtained) the caller's element is left as it was.
get(ElemT&) Moves the front element out and removes it.
peek(ElemT&) Copies the front element without removing it.
peekWith(fn) Calls fn(const ElemT&) with the front element under the queue mutex, without copying it - keep fn short.
pop() Removes the front element without copying it (e.g. after a peekWith() by the only consumer).
count(), canAcceptData() Lock-free; never wait, but the value may be stale by the time it is used.
clear() Empties the queue.
  • put, get, peek, peekWith, pop and clear wait up to maxMsToWait (default 10 ms) for the mutex and return false if they don't get it, so a false return doesn't necessarily mean full or empty. Their results are [[nodiscard]].
  • The elements are held in a std::deque with SpiramAwareAllocator, so they are in PSRAM where the board has it. This keeps internal RAM free (a deque allocates a block as soon as it is created, even when empty). Queues are for tasks, not ISRs.
  • Large elements such as CommsChannelMsg can be moved through a queue rather than copied in and out. The comms channel's outbound loop reads only the type of the next message (peekWith) to decide whether it can be sent - see Comms Channels.

SpiramAwareAllocator

SpiramAwareAllocator<T> (Utils/SpiramAwareAllocator.h) allocates from PSRAM when the board has it and falls back to internal RAM if PSRAM can't satisfy a request (or there is none). SpiramAwareUint8Vector is std::vector<uint8_t, SpiramAwareAllocator<uint8_t>>.

  • construct() forwards its arguments (the standard C++11 allocator form), so elements of a container using it are moved, not copied, when given an rvalue.
  • max_allocatable() returns the largest block that can be allocated now: the largest free PSRAM block where there is PSRAM, otherwise the largest free internal block. Code that is about to make a large allocation (e.g. file upload block and batch sizes, WebSocket receive buffers) checks it first, since a failed allocation crashes the device with exceptions disabled.

MiniHDLC

MiniHDLC (MiniHDLC/MiniHDLC.h) frames and deframes byte streams for RICSerial. The defaults are boundary octet 0xE7, escape octet 0xD7 (an escaped byte is sent as 0xD7 then the byte XOR 0x20) and a CRC-16/CCITT (initial value 0xFFFF) sent high byte first.

  • Decoding: handleBuffer() copies runs of plain bytes (not a boundary or an escape, at least 8 long) into the frame in one go and updates the CRC over the run in one call; boundaries, escapes and short runs go through handleChar() one byte at a time. A 16 KB frame decoded about 4x faster than byte by byte in a Linux test.
  • Frame length limit: a frame longer than the receive maximum (rxMsgMaxLen) is dropped and counted as too long (MiniHDLCStats::_frameTooLongCount). Frame lengths are 32-bit, so the 200000-byte limit ProtocolRICSerial uses with PSRAM isn't truncated. The receive buffer grows only when the memory is there (otherwise the frame is dropped and counted in _rxBufAllocFail), and a buffer grown over 32 KB is released when cleared.
  • Encoding in one pass: appendFrameStart(vec, fcs), appendFramePayload(vec, fcs, pData, len) (call it as many times as needed) and appendFrameEnd(vec, fcs) encode straight into a vector in one pass, updating the CRC inline and copying runs of bytes that need no escaping in one go. Reserve encodedLenEstimate(payloadLen) first (the payload, one escaped byte in 64, and the overhead); the vector grows in the rare case that isn't enough. ProtocolRICSerial encodes every outbound message this way, straight into the outgoing message.
  • The older forms remain: encodeFrame() and encodeFrameStart/AddPayload/End() into a raw buffer (size it with calcEncodedLen() or maxEncodedLen()), and sendFrame().

Where to look in the source

All classes listed above live under RaftCore/components/core/. Each folder contains a header that documents its public API; tests for several of them live under RaftCore/unit_tests/ and RaftCore/linux_unit_tests/.

Clone this wiki locally