Skip to content

ConsolePortSharing

Rob Dobson edited this page Oct 12, 2026 · 1 revision

Console Port Sharing (RaftConsolePort)

Lets one owner take the console's port for itself — for example a binary protocol on the USB-Serial-JTAG port that the console normally uses — and give it back later. While the port is claimed nothing else writes text to it.

Source: RaftCore/components/core/Logger/RaftConsolePort.h

Why

On most ESP32-S3/C3/C6 boards the console and the only USB connection are the same USB-Serial-JTAG port. Something that needs that port as a clean byte pipe — the RaftROS serial link is the first user — cannot tolerate a single stray log line. A Raft image writes to the console from several places:

Writer / reader Route Silenced by
Raft LOG_x LoggerCore (direct USB-Serial-JTAG writes once the driver is installed, printf before) RaftConsolePort::claim()
ESP-IDF ESP_LOGx (WiFi, MQTT, LogManager, WebServer, ...) the esp_log_set_vprintf hook (SerialConsole installs one) claim()
printf / stdout / stderr VFS to the console claim()
ROM printf (esp_rom_printf, ESP_EARLY_LOG, ESP_DRAM_LOG, often from ISRs) straight to the hardware claim()
Console replies and the RICSerial channel SerialConsole::putStr / sendMsg SerialConsole releasePort
Console input SerialConsole::getChar SerialConsole releasePort

Readers are SysMods, so the owner tells them separately (see SerialConsole); everything else is handled by RaftConsolePort.

API

#include "RaftConsolePort.h"

bool RaftConsolePort::claim(const char* owner, RaftConsoleTextSink sink = nullptr);
bool RaftConsolePort::release(const char* owner);
bool RaftConsolePort::isClaimed();
void RaftConsolePort::setSink(RaftConsoleTextSink sink);
bool RaftConsolePort::divert(const char* text, uint32_t len);   // for console writers
Call Effect
claim(owner, sink) Stops LoggerCore's console write; replaces the ESP-IDF log hook (saving the previous one); fflushes stdout and freopens it to /dev/null (one FILE is shared by every task, so this covers all of them); points ROM printf channel 1 at a no-op. Text that would have gone to the console is given to sink if one is set. Returns false if another owner holds the port.
release(owner) Restores each of the above: ROM output as ESP-IDF's startup sets it for the configured console, stdout back to /dev/console, the saved log hook. Only the claiming owner can release.
isClaimed() Any task.
divert(text, len) For code that writes to the console itself: returns true (and hands the text to the sink) if the port is claimed, in which case the caller must not write it. LoggerCore uses this.

RaftConsoleTextSink is void (*)(const char* text, uint32_t len). It is called from whichever task logged, so it must be short and must not block or log.

claim() and release() are meant to be called from the main loop. On Linux builds they only track the flag.

Other loggers registered with LoggerCore (the LogManager network destinations, for example) still receive every line while the port is claimed — only the console write is diverted.

Typical use

// Take the port
getSysManager()->sendCmdJSON("SerialConsole", R"({"cmd":"releasePort"})");
RaftConsolePort::claim("MyLink");
// ... the port is yours: use the usb_serial_jtag driver directly ...

// Give it back
RaftConsolePort::release("MyLink");
getSysManager()->sendCmdJSON("SerialConsole", R"({"cmd":"reclaimPort"})");

Log a last line before claiming that says how to get the console back; after the claim nothing more reaches it.

Limits

  • Panic output and the boot ROM's messages are written at reset, outside any claim.
  • Code that writes to the USB-Serial-JTAG hardware or driver directly (rather than through the routes above) is not covered. None of the Raft libraries does, apart from LoggerCore and SerialConsole.

Related

Clone this wiki locally