System Info, Control & Service

The SystemInfo class is a set of static getters over the corresponding ESP-IDF calls: the chip model, revision, core count and feature flags, the ESP-IDF version, the application description embedded in the image (project name, version, build date and time, ELF SHA-256), the running and boot partitions with the OTA image state, the reset reason, uptime, base MAC, flash and PSRAM sizes, CPU frequency and the free / lowest-free heap. collect() gathers everything into one Snapshot and to_string() renders a boot-banner style summary.

The SystemControl class restarts the device: reboot() (esp_restart), and reboot_to_bootloader() which sets the chip’s force download boot flag in its always-on register and restarts, so the next boot stays in the ROM download mode instead of running the app — what holding the BOOT strap during a reset does, without a button. The device then re-enumerates as the ROM’s own flashing interface (the USB CDC / DFU device on the ESP32-S2 / -S3 native USB port; USB-Serial-JTAG on the ESP32-C3 / -C6 / -H2 / -C5 / -C61 / -H21 / -P4), ready for esptool / idf.py flash. The classic ESP32 has no software path (only the GPIO0 strap): bootloader_reboot_supported() is false there and the call fails with operation_not_supported. The *_after(delay) variants restart from a detached thread so a reply can leave the transport first.

By default the reset tears the USB connection down and the ROM enumerates its device afresh, which works for any application. On the ESP32-S2 / -S3 the ROM can instead keep the USB peripheral’s state across the reset (SystemControl::BootloaderOptions::usb_persist, passed through as SystemService::Config::usb_persist) so the host sees no re-plug. This is opt-in and off by default: the ROM only expects it from an application whose USB device is ROM-CDC/DFU-compatible (ESP-IDF’s ROM USB console driver, which is what performs the same sequence — usb_dc_prepare_persist() then the persist flag — before its own reboot into the bootloader). A TinyUSB composite device such as the espp examples’ vendor + CDC has different descriptors, and persisting it can leave the host with a stale enumeration the bootloader cannot serve; leave the option off there.

The SystemService class serves both over any byte stream as a dispatcher module (espp.system v1, module id 7 by default; Config::module moves an instance and hosts find it through discovery by its protocol id). GET_INFO answers with a list of tagged records ([tag u8][len u8][value]) a host decodes while skipping tags it does not know, so fields can be added without a version bump. Every reply echoes the request frame’s correlation id, so a host that stamps its requests can pair replies with them and drop stale ones. REBOOT and REBOOT_TO_BOOTLOADER reply OK first and restart after the requested delay (clamped to Config::min_restart_delay). Both are guarded: Config::allow_reboot / allow_bootloader switch them off, the optional on_reboot_request callback can veto a specific request (an application with a motor running can refuse or defer), and the bootloader restart is refused on chips without a software path. The INFO capabilities record tells a host up front which of the two it may offer.

The hosted espp System Console web app speaks the protocol over WebUSB (vendor interface) or Web Serial (CDC, where it doubles as a serial monitor): a device-info panel, the two restart buttons (with an in-page confirmation), and — when the device also advertises the monitor component’s MonitorService — heap-region gauges and a live, sortable task table with a stream toggle.

espp System Console: device info, reboot / bootloader controls and heap gauges espp System Console streaming the task table (name, CPU %, stack high-water mark, priority, core)

API Reference

Header File

Classes

class SystemInfo

Static accessors for the identity and status of the running system: the chip, the ESP-IDF version, the application description (project name, version, build date / time, ELF SHA-256), the running / boot partitions and OTA state, the reset reason, uptime, base MAC, flash and PSRAM sizes, CPU frequency and heap figures.

Everything is a thin, allocation-light wrapper over the corresponding ESP-IDF call so it can be used from any task. collect() gathers it all into one Snapshot (what espp::SystemService reports to a host) and to_string() renders a human-readable summary for logs.

SystemInfo Example

  // Boot banner: everything SystemInfo knows, in one string.
  logger.info("System:\n{}", espp::SystemInfo::to_string());
  logger.info("Reboot into the bootloader is {} on this chip",
              espp::SystemControl::bootloader_reboot_supported() ? "supported" : "NOT supported");

  // The OTA and core-dump engines, shared by the per-transport services below.
  // A panic core-dumps to the `coredump` partition (sdkconfig / partitions.csv)
  // and is reported here on the next boot; the coredump console downloads /
  // erases it. OTA alternates between ota_0 / ota_1 with host-driven rollback
  // confirmation, exactly as the ota example does.
  espp::CoreDump core_dump({.log_level = espp::Logger::Verbosity::INFO});
  const std::string crash_report = core_dump.format_report();
  if (crash_report.empty())
    logger.info("Clean boot history (reset reason: {})",
                espp::CoreDump::reset_reason_name(espp::CoreDump::reset_reason()));
  else
    logger.error("Previous abnormal reset:\n{}", crash_report);
  espp::Ota ota({.reject_same_version = false, .log_level = espp::Logger::Verbosity::INFO});
  if (ota.is_pending_verify())
    logger.warn("This image is PENDING VERIFY (first boot after an OTA update): it rolls back on "
                "the next reset unless the host confirms it (MARK_VALID from the OTA console)");

  // USB composite device: a vendor/WebUSB function and a CDC function, both
  // carrying the same framed protocol.
  espp::UsbDevice::Config usb_cfg;
  usb_cfg.pid = 0x0d37; // distinct from the espp default so the webapp filter is specific
  usb_cfg.manufacturer = "espp";
  usb_cfg.product = "espp System";
  usb_cfg.log_level = espp::Logger::Verbosity::WARN;
  espp::UsbDevice::CdcFunction cdc;
  cdc.interface_name = "espp System (CDC)";
  usb_cfg.cdc = cdc;
  espp::UsbDevice::VendorFunction vendor;
  vendor.interface_name = "espp System (WebUSB)";
  vendor.webusb = true; // advertise BOS / WebUSB / MS OS 2.0 descriptors
  vendor.landing_page_url = "esp-cpp.github.io/espp/apps/system_console.html";
  usb_cfg.vendor = vendor;
  espp::UsbDevice usb(usb_cfg);

  // Replies go back on the stream the request came in on: one send function
  // per transport. Both services share each transport and only serialize
  // their OWN frames (a streamed monitor event and a system reply come from
  // different tasks), so every device->host write on a transport goes through
  // one application-level mutex; write_vendor / write_cdc are all-or-nothing
  // per call, so a frame is never truncated or interleaved.
  std::mutex vendor_tx_mutex, cdc_tx_mutex;
  auto vendor_send = [&](std::span<const uint8_t> frame) {
    std::lock_guard<std::mutex> lock(vendor_tx_mutex);
    usb.write_vendor(frame);
  };
  auto cdc_send = [&](std::span<const uint8_t> frame) {
    std::lock_guard<std::mutex> lock(cdc_tx_mutex);
    usb.write_cdc(frame);
  };

  // The application decides whether a reboot may happen right now: this demo
  // permits every request and logs it. A real application would refuse (or
  // defer) while, say, a motor is running or a file is being written.
  auto reboot_request = [&](espp::SystemService::RebootKind kind) {
    logger.warn("Host requested a {}; allowing it",
                kind == espp::SystemService::RebootKind::Bootloader ? "reboot into the bootloader"
                                                                    : "reboot");
    return true;
  };

  // One service instance per transport (they are cheap; each replies on its
  // own stream). Declared BEFORE the workers that call into them.
  espp::SystemService vendor_system({.send = vendor_send,
                                     .on_reboot_request = reboot_request,
                                     .log_level = espp::Logger::Verbosity::INFO});
  espp::SystemService cdc_system({.send = cdc_send,
                                  .on_reboot_request = reboot_request,
                                  .log_level = espp::Logger::Verbosity::INFO});
  espp::MonitorService vendor_monitor(
      {.send = vendor_send,
       .task_config = {.name = "monitor_v", .stack_size_bytes = 6 * 1024},
       .log_level = espp::Logger::Verbosity::INFO});
  espp::MonitorService cdc_monitor(
      {.send = cdc_send,
       .task_config = {.name = "monitor_c", .stack_size_bytes = 6 * 1024},
       .log_level = espp::Logger::Verbosity::INFO});
  // OTA and core dump: one service per transport over the shared engines (each
  // OtaService only touches the session IT began, so the two cannot interfere).
  espp::OtaService vendor_ota(ota,
                              {.send = vendor_send, .log_level = espp::Logger::Verbosity::INFO});
  espp::OtaService cdc_ota(ota, {.send = cdc_send, .log_level = espp::Logger::Verbosity::INFO});
  espp::CoreDumpService vendor_coredump(
      core_dump, {.send = vendor_send, .log_level = espp::Logger::Verbosity::INFO});
  espp::CoreDumpService cdc_coredump(
      core_dump, {.send = cdc_send, .log_level = espp::Logger::Verbosity::INFO});

  // One DispatcherWorker per byte stream: a bounded receive queue + worker
  // task feeding its Dispatcher, so the services never run on the TinyUSB
  // task. Registering a service routes its module to it and advertises it for
  // discovery; serve_discovery() answers the hub's ListModules query.
  // On an RX overflow the OTA service aborts a transfer it owned and tells the
  // host (an OTA image with dropped bytes is unusable).
  espp::DispatcherWorker vendor_link(
      {.send = vendor_send,
       .on_overflow = [&]() { vendor_ota.on_rx_overflow(); },
       .task_config = {.name = "system_rx_vendor", .stack_size_bytes = 8192}});
  espp::DispatcherWorker cdc_link(
      {.send = cdc_send,
       .on_overflow = [&]() { cdc_ota.on_rx_overflow(); },
       .task_config = {.name = "system_rx_cdc", .stack_size_bytes = 8192}});
  vendor_link.register_module(vendor_system);
  vendor_link.register_module(vendor_monitor);
  vendor_link.register_module(vendor_ota);
  vendor_link.register_module(vendor_coredump);
  cdc_link.register_module(cdc_system);
  cdc_link.register_module(cdc_monitor);
  cdc_link.register_module(cdc_ota);
  cdc_link.register_module(cdc_coredump);
  vendor_link.serve_discovery(usb_cfg.product);
  cdc_link.serve_discovery(usb_cfg.product);

Public Static Functions

static inline const char *chip_model_name(esp_chip_model_t model)

Chip model name for an esp_chip_model_t (“ESP32-S3”, …).

static inline const char *reset_reason_name(esp_reset_reason_t reason)

Human-readable name for an esp_reset_reason_t.

static inline const char *ota_state_name(uint8_t state)

Human-readable name for an OTA image state byte (Snapshot::ota_state).

static inline esp_chip_info_t chip_info()

Chip information (model, revision, cores, features).

static inline std::string idf_version()

The ESP-IDF version string the app was built with.

static inline const esp_app_desc_t &app_description()

The application description embedded in the running image.

static inline std::string running_partition()

Label of the partition the running app was loaded from (”” if unknown).

static inline std::string boot_partition()

Label of the partition the bootloader will boot next (”” if unknown).

static inline uint8_t ota_state()

OTA image state of the running partition (esp_ota_img_states_t as a byte; 0xFF when the app runs from a factory partition or the state cannot be read).

static inline esp_reset_reason_t reset_reason()

Why the chip last reset.

static inline uint64_t uptime_ms()

Milliseconds since boot.

static inline std::array<uint8_t, 6> base_mac()

The chip’s base (factory-programmed) MAC address.

static inline uint32_t flash_size()

Size of the main SPI flash in bytes (0 if it cannot be read).

static inline uint32_t psram_size()

Size of the PSRAM in bytes (0 without PSRAM / with CONFIG_SPIRAM off).

static inline uint32_t cpu_mhz()

Current CPU frequency in MHz.

static inline uint32_t free_heap()

Free heap in bytes (default capabilities).

static inline uint32_t min_free_heap()

Lowest free heap since boot, in bytes.

static inline Snapshot collect()

Gather everything into one Snapshot.

static inline std::string to_string(const Snapshot &s)

A multi-line human-readable summary of a Snapshot.

static inline std::string to_string()

Collect and format in one call (for a boot banner).

struct Snapshot

Everything collect() gathers.

Public Members

std::string chip_model

e.g. “ESP32-S3”

uint16_t chip_revision = {0}

MXX: major * 100 + minor.

uint8_t cores = {0}

CPU core count.

uint32_t chip_features = {0}

CHIP_FEATURE_* bitmask.

std::string idf_version

e.g. “v6.1”

std::string project_name

CMake project name.

std::string app_version

PROJECT_VER.

std::string build_date

compile date

std::string build_time

compile time

std::array<uint8_t, 32> elf_sha256 = {}

SHA-256 of the application ELF.

std::string running_partition

label of the partition the app runs from

std::string boot_partition

label of the partition the bootloader will boot next

uint8_t ota_state = {0xFF}

esp_ota_img_states_t of the running partition; 0xFF = n/a

uint8_t reset_reason = {0}

esp_reset_reason_t

uint64_t uptime_ms = {0}

time since boot

std::array<uint8_t, 6> mac = {}

base MAC address

uint32_t flash_size = {0}

bytes

uint32_t psram_size = {0}

bytes (0 = none / disabled)

uint32_t cpu_mhz = {0}

current CPU frequency

uint32_t free_heap = {0}

bytes

uint32_t min_free_heap = {0}

bytes, lowest since boot

Header File

Classes

class SystemControl

Restart control: a plain reboot, and a reboot into the ROM bootloader’s download (serial flashing) mode.

reboot_to_bootloader() sets the chip’s “force download boot” flag in its always-on register and restarts, so the next boot stays in the ROM download mode instead of running the app &#8212; exactly what holding the BOOT strap during a reset does, without touching a button. The device then re-enumerates as the ROM’s own flashing interface: the USB CDC / DFU device on the ESP32-S2 / -S3 native USB port, or USB-Serial-JTAG on the ESP32-C3 / -C6 / -H2 / -C5 / -C61 / -H21 / -P4 &#8212; so `esptool` / `idf.py flash` can program it. On the classic ESP32 there is no software path (only the GPIO0 strap): bootloader_reboot_supported() is false and reboot_to_bootloader() fails with operation_not_supported.

**USB persistence (S2 / S3, opt-in)**: by default the reset tears the USB connection down and the ROM enumerates its CDC / DFU device afresh, which works for any application. The ROM can instead keep the USB peripheral’s state across the reset (BootloaderOptions::usb_persist), so the host sees no re-plug &#8212; but the ROM only expects that from an application whose USB device is ROM-CDC/DFU-compatible (the same descriptors the ROM exposes, as ESP-IDF’s ROM USB console has); a TinyUSB composite device (the espp examples: vendor + CDC) has different descriptors, and persisting it can leave the host with a stale enumeration the bootloader cannot serve. Leave it off unless the application runs on the ROM USB console driver.

Both functions restart immediately; use the delayed variants (or espp::SystemService, which replies before restarting) when a reply must leave the transport first.

Public Types

using BootloaderOptions = SystemBootloaderOptions

Options for the reboot into download mode (see SystemBootloaderOptions).

Public Static Functions

static inline constexpr bool bootloader_reboot_supported()

Whether reboot_to_bootloader() is implemented for this chip.

static inline void reboot()

Restart the chip (esp_restart()); does not return.

static inline bool reboot_to_bootloader(std::error_code &ec, const BootloaderOptions &options = {})

Restart into the ROM bootloader’s download mode.

Parameters:
  • ec – Set to operation_not_supported on chips without a software path (classic ESP32); then returns false without restarting.

  • options – See BootloaderOptions (USB persistence is opt-in).

Returns:

Does not return on success; false on failure.

static inline void reboot_after(std::chrono::milliseconds delay)

Restart after a delay, from a detached thread, so the caller can finish (e.g. send a reply) first. Returns immediately.

static inline bool reboot_to_bootloader_after(std::chrono::milliseconds delay, std::error_code &ec, const BootloaderOptions &options = {})

Restart into download mode after a delay, from a detached thread. Returns immediately; false (nothing scheduled) if unsupported.

Header File

Classes

class SystemService : public espp::BaseComponent

Serves espp::SystemInfo and espp::SystemControl over any framed byte stream (dispatcher module 7 by default; see Config::module).

GET_INFO answers with a tagged-record snapshot (see detail/system_protocol.hpp; hosts skip tags they do not know). REBOOT and REBOOT_TO_BOOTLOADER reply OK first and restart after the requested delay (at least Config::min_restart_delay, so the reply leaves the transport) from a detached thread &#8212; the same pattern as OtaService’s post-update restart. Both are guarded: Config::allow_reboot / allow_bootloader switch them off (ERROR “not permitted”), the optional Config::on_reboot_request

callback can veto a specific request (an application with a motor running can refuse or defer), and REBOOT_TO_BOOTLOADER is refused with “not

supported” on chips without a software download-mode path (classic ESP32). The INFO capabilities record tells a host up front which of the two it may offer.

**Threading**: an internal mutex covers the parser and request handling; the `send` callback and the veto callback run after it is released. Drive one instance from one context per byte stream (a Dispatcher / DispatcherWorker, or a single task calling feed()).

SystemService Example

  // Boot banner: everything SystemInfo knows, in one string.
  logger.info("System:\n{}", espp::SystemInfo::to_string());
  logger.info("Reboot into the bootloader is {} on this chip",
              espp::SystemControl::bootloader_reboot_supported() ? "supported" : "NOT supported");

  // The OTA and core-dump engines, shared by the per-transport services below.
  // A panic core-dumps to the `coredump` partition (sdkconfig / partitions.csv)
  // and is reported here on the next boot; the coredump console downloads /
  // erases it. OTA alternates between ota_0 / ota_1 with host-driven rollback
  // confirmation, exactly as the ota example does.
  espp::CoreDump core_dump({.log_level = espp::Logger::Verbosity::INFO});
  const std::string crash_report = core_dump.format_report();
  if (crash_report.empty())
    logger.info("Clean boot history (reset reason: {})",
                espp::CoreDump::reset_reason_name(espp::CoreDump::reset_reason()));
  else
    logger.error("Previous abnormal reset:\n{}", crash_report);
  espp::Ota ota({.reject_same_version = false, .log_level = espp::Logger::Verbosity::INFO});
  if (ota.is_pending_verify())
    logger.warn("This image is PENDING VERIFY (first boot after an OTA update): it rolls back on "
                "the next reset unless the host confirms it (MARK_VALID from the OTA console)");

  // USB composite device: a vendor/WebUSB function and a CDC function, both
  // carrying the same framed protocol.
  espp::UsbDevice::Config usb_cfg;
  usb_cfg.pid = 0x0d37; // distinct from the espp default so the webapp filter is specific
  usb_cfg.manufacturer = "espp";
  usb_cfg.product = "espp System";
  usb_cfg.log_level = espp::Logger::Verbosity::WARN;
  espp::UsbDevice::CdcFunction cdc;
  cdc.interface_name = "espp System (CDC)";
  usb_cfg.cdc = cdc;
  espp::UsbDevice::VendorFunction vendor;
  vendor.interface_name = "espp System (WebUSB)";
  vendor.webusb = true; // advertise BOS / WebUSB / MS OS 2.0 descriptors
  vendor.landing_page_url = "esp-cpp.github.io/espp/apps/system_console.html";
  usb_cfg.vendor = vendor;
  espp::UsbDevice usb(usb_cfg);

  // Replies go back on the stream the request came in on: one send function
  // per transport. Both services share each transport and only serialize
  // their OWN frames (a streamed monitor event and a system reply come from
  // different tasks), so every device->host write on a transport goes through
  // one application-level mutex; write_vendor / write_cdc are all-or-nothing
  // per call, so a frame is never truncated or interleaved.
  std::mutex vendor_tx_mutex, cdc_tx_mutex;
  auto vendor_send = [&](std::span<const uint8_t> frame) {
    std::lock_guard<std::mutex> lock(vendor_tx_mutex);
    usb.write_vendor(frame);
  };
  auto cdc_send = [&](std::span<const uint8_t> frame) {
    std::lock_guard<std::mutex> lock(cdc_tx_mutex);
    usb.write_cdc(frame);
  };

  // The application decides whether a reboot may happen right now: this demo
  // permits every request and logs it. A real application would refuse (or
  // defer) while, say, a motor is running or a file is being written.
  auto reboot_request = [&](espp::SystemService::RebootKind kind) {
    logger.warn("Host requested a {}; allowing it",
                kind == espp::SystemService::RebootKind::Bootloader ? "reboot into the bootloader"
                                                                    : "reboot");
    return true;
  };

  // One service instance per transport (they are cheap; each replies on its
  // own stream). Declared BEFORE the workers that call into them.
  espp::SystemService vendor_system({.send = vendor_send,
                                     .on_reboot_request = reboot_request,
                                     .log_level = espp::Logger::Verbosity::INFO});
  espp::SystemService cdc_system({.send = cdc_send,
                                  .on_reboot_request = reboot_request,
                                  .log_level = espp::Logger::Verbosity::INFO});
  espp::MonitorService vendor_monitor(
      {.send = vendor_send,
       .task_config = {.name = "monitor_v", .stack_size_bytes = 6 * 1024},
       .log_level = espp::Logger::Verbosity::INFO});
  espp::MonitorService cdc_monitor(
      {.send = cdc_send,
       .task_config = {.name = "monitor_c", .stack_size_bytes = 6 * 1024},
       .log_level = espp::Logger::Verbosity::INFO});
  // OTA and core dump: one service per transport over the shared engines (each
  // OtaService only touches the session IT began, so the two cannot interfere).
  espp::OtaService vendor_ota(ota,
                              {.send = vendor_send, .log_level = espp::Logger::Verbosity::INFO});
  espp::OtaService cdc_ota(ota, {.send = cdc_send, .log_level = espp::Logger::Verbosity::INFO});
  espp::CoreDumpService vendor_coredump(
      core_dump, {.send = vendor_send, .log_level = espp::Logger::Verbosity::INFO});
  espp::CoreDumpService cdc_coredump(
      core_dump, {.send = cdc_send, .log_level = espp::Logger::Verbosity::INFO});

  // One DispatcherWorker per byte stream: a bounded receive queue + worker
  // task feeding its Dispatcher, so the services never run on the TinyUSB
  // task. Registering a service routes its module to it and advertises it for
  // discovery; serve_discovery() answers the hub's ListModules query.
  // On an RX overflow the OTA service aborts a transfer it owned and tells the
  // host (an OTA image with dropped bytes is unusable).
  espp::DispatcherWorker vendor_link(
      {.send = vendor_send,
       .on_overflow = [&]() { vendor_ota.on_rx_overflow(); },
       .task_config = {.name = "system_rx_vendor", .stack_size_bytes = 8192}});
  espp::DispatcherWorker cdc_link(
      {.send = cdc_send,
       .on_overflow = [&]() { cdc_ota.on_rx_overflow(); },
       .task_config = {.name = "system_rx_cdc", .stack_size_bytes = 8192}});
  vendor_link.register_module(vendor_system);
  vendor_link.register_module(vendor_monitor);
  vendor_link.register_module(vendor_ota);
  vendor_link.register_module(vendor_coredump);
  cdc_link.register_module(cdc_system);
  cdc_link.register_module(cdc_monitor);
  cdc_link.register_module(cdc_ota);
  cdc_link.register_module(cdc_coredump);
  vendor_link.serve_discovery(usb_cfg.product);
  cdc_link.serve_discovery(usb_cfg.product);

Public Types

enum class RebootKind : uint8_t

Which restart a host asked for (passed to Config::on_reboot_request).

Values:

enumerator Reboot

plain restart

enumerator Bootloader

restart into the ROM download mode

using send_fn = std::function<void(std::span<const uint8_t> frame)>

Transmits one encoded reply frame to the host.

using reboot_request_fn = std::function<bool(RebootKind kind)>

Asked (outside the lock) before a permitted reboot is acknowledged; return false to veto it (the host gets ERROR “refused by the application”).

Public Functions

inline explicit SystemService(const Config &config)

Construct the service.

inline uint8_t module_id() const

The dispatcher module id this service answers on (Config::module).

inline Dispatcher::ModuleInfo module_info() const

Discovery metadata for registering this service on a Dispatcher.

inline uint32_t capabilities() const

The capabilities bitmask GET_INFO reports (kCapReboot / kCapBootloader).

inline std::vector<uint8_t> build_info() const

Build the INFO payload (a SystemInfo snapshot as tagged records).

inline void handle(const espp::stream_frame::Frame &frame)

Dispatcher entry point: handle one routed frame. Frames for other modules and reply-flagged frames are ignored, so this can be registered directly: `dispatcher.register_module(service)`.

inline void feed(std::span<const uint8_t> data)

Feed received transport bytes (standalone use, without a Dispatcher).

inline void reset_parser()

Discard any partially-buffered frame bytes (standalone feed() use).

inline bool handle_frame(uint8_t type, std::span<const uint8_t> payload, std::optional<uint16_t> correlation = std::nullopt)

Handle one already-parsed request frame.

Note

The `send` and veto callbacks run after the internal mutex is released.

Parameters:

correlation – The request frame’s correlation id, if it carried one; every reply echoes it so the host can pair them.

Returns:

true if the type belongs to the system protocol (a reply was sent), false if it was ignored.

inline const std::string &get_name() const

Get the name of the component

Note

This is the tag of the logger

Returns:

A const reference to the name of the component

inline void set_log_tag(const std::string_view &tag)

Set the tag for the logger

Parameters:

tag – The tag to use for the logger

inline espp::Logger::Verbosity get_log_level() const

Get the log level for the logger

Returns:

The verbosity level of the logger

inline void set_log_level(espp::Logger::Verbosity level)

Set the log level for the logger

Parameters:

level – The verbosity level to use for the logger

inline void set_log_verbosity(espp::Logger::Verbosity level)

Set the log verbosity for the logger

See also

set_log_level

Note

This is a convenience method that calls set_log_level

Parameters:

level – The verbosity level to use for the logger

inline espp::Logger::Verbosity get_log_verbosity() const

Get the log verbosity for the logger

See also

get_log_level

Note

This is a convenience method that calls get_log_level

Returns:

The verbosity level of the logger

inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)

Set the rate limit for the logger

Note

Only calls to the logger that have _rate_limit suffix will be rate limited

Parameters:

rate_limit – The rate limit to use for the logger

Public Static Attributes

static constexpr uint8_t kModule = espp::detail::system_protocol::kModule

Default dispatcher module id (7). Only a routing key: Config::module serves on any id, and the hosted system console finds it through discovery (by kProtocol).

static constexpr const char *kProtocol = espp::detail::system_protocol::kProtocol

Stable protocol identifier + version advertised through discovery.

struct Config

Configuration for the SystemService.

Public Members

send_fn send = {nullptr}

Transmits an encoded reply frame (required).

uint8_t module = {kModule}

Dispatcher module id this instance answers on (and stamps on its replies). A routing key only: hosts find whichever id is chosen through discovery (by kProtocol), so any id 0x00..0xEF is fine.

bool allow_reboot = {true}

Serve REBOOT (else ERROR “not permitted”).

bool allow_bootloader = {true}

Serve REBOOT_TO_BOOTLOADER (else ERROR “not permitted”).

bool usb_persist = {false}

Keep the USB peripheral’s state across a REBOOT_TO_BOOTLOADER reset (ESP32-S2 / -S3 ROM only; see SystemControl::BootloaderOptions). Off by default: the ROM re-enumerates its CDC / DFU device on its own, which is right for a TinyUSB (vendor / CDC composite) application; enable only when the application’s USB device is ROM-CDC/DFU-compatible.

reboot_request_fn on_reboot_request = {nullptr}

Optional veto for a specific reboot request; called after the allow_* checks, outside the lock. nullptr = every permitted request proceeds.

std::chrono::milliseconds min_restart_delay = {250}

Lower bound on the delay between the OK reply and the restart, so the reply reaches the host even when it asked for 0 ms.

espp::Logger::Verbosity log_level = {espp::Logger::Verbosity::WARN}

Logger verbosity.