Remote Debug APIs

The RemoteDebug class provides a web-based interface for remote debugging and monitoring of ESP32 devices. It offers:

  • GPIO Control: Configure pins as input/output and control their states remotely

  • ADC Monitoring: Real-time voltage monitoring with live graphs and configurable sampling rates

  • Console Log Viewing: Capture and display stdout output with ANSI color support

  • Multi-Client Support: Efficient batched updates to support multiple simultaneous web clients

The component uses the espp WiFi, ADC, and FileSystem components for functionality, and provides a responsive web interface accessible from any browser on the local network.

API Reference

Header File

Classes

class RemoteDebug : public espp::BaseComponent

Remote Debug Component.

Provides a web-based interface for remote GPIO control and ADC monitoring. Allows real-time control of GPIO pins and plotting of ADC values.

Features:

  • GPIO control (set high/low, read state, configure mode)

  • ADC value reading and real-time plotting

  • Configurable sampling rates

  • Mobile-friendly responsive interface

Remote Debug Example

  // Connect to WiFi using espp WifiSta
  logger.info("Connecting to WiFi SSID: {}", CONFIG_REMOTE_DEBUG_WIFI_SSID);

  espp::WifiSta wifi_sta({.ssid = CONFIG_REMOTE_DEBUG_WIFI_SSID,
                          .password = CONFIG_REMOTE_DEBUG_WIFI_PASSWORD,
                          .num_connect_retries = 5,
                          .on_connected = [&logger]() { logger.info("WiFi connected!"); },
                          .on_disconnected = [&logger]() { logger.warn("WiFi disconnected!"); },
                          .on_got_ip =
                              [&logger](ip_event_got_ip_t *event) {
                                logger.info("got IP: {}.{}.{}.{}", IP2STR(&event->ip_info.ip));
                              }});

  // Wait for connection
  while (!wifi_sta.is_connected()) {
    std::this_thread::sleep_for(100ms);
  }
  logger.info("WiFi connected successfully");

  // Build GPIO list from menuconfig
  std::vector<espp::RemoteDebug::GpioConfig> gpios;
#if CONFIG_REMOTE_DEBUG_NUM_GPIOS >= 1
  gpios.push_back({.pin = static_cast<gpio_num_t>(CONFIG_REMOTE_DEBUG_GPIO_0),
                   .mode = GPIO_MODE_INPUT,
                   .label = CONFIG_REMOTE_DEBUG_GPIO_0_LABEL});
#endif
#if CONFIG_REMOTE_DEBUG_NUM_GPIOS >= 2
  gpios.push_back({.pin = static_cast<gpio_num_t>(CONFIG_REMOTE_DEBUG_GPIO_1),
                   .mode = GPIO_MODE_INPUT,
                   .label = CONFIG_REMOTE_DEBUG_GPIO_1_LABEL});
#endif
#if CONFIG_REMOTE_DEBUG_NUM_GPIOS >= 3
  gpios.push_back({.pin = static_cast<gpio_num_t>(CONFIG_REMOTE_DEBUG_GPIO_2),
                   .mode = GPIO_MODE_INPUT,
                   .label = CONFIG_REMOTE_DEBUG_GPIO_2_LABEL});
#endif
#if CONFIG_REMOTE_DEBUG_NUM_GPIOS >= 4
  gpios.push_back({.pin = static_cast<gpio_num_t>(CONFIG_REMOTE_DEBUG_GPIO_3),
                   .mode = GPIO_MODE_INPUT,
                   .label = CONFIG_REMOTE_DEBUG_GPIO_3_LABEL});
#endif

  // Build ADC list from menuconfig
  size_t task_stack_size = 4096;
#if CONFIG_REMOTE_DEBUG_NUM_ADCS >= 1
  task_stack_size += 2048;
  int adc_sample_rate_hz = CONFIG_REMOTE_DEBUG_ADC_SAMPLE_RATE_HZ;
  size_t adc_buffer_size = CONFIG_REMOTE_DEBUG_ADC_BUFFER_SIZE;
#else
  int adc_sample_rate_hz = 1; // Default to 1 Hz if no ADCs configured
  size_t adc_buffer_size = 1; // Default to buffer size of 1 if no ADCs configured
#endif

  std::vector<espp::RemoteDebug::AdcChannelConfig> adc1_channels;
#if CONFIG_REMOTE_DEBUG_NUM_ADCS >= 1
  adc1_channels.push_back({.channel = static_cast<adc_channel_t>(CONFIG_REMOTE_DEBUG_ADC_0),
                           .label = CONFIG_REMOTE_DEBUG_ADC_0_LABEL});
#endif
#if CONFIG_REMOTE_DEBUG_NUM_ADCS >= 2
  adc1_channels.push_back({.channel = static_cast<adc_channel_t>(CONFIG_REMOTE_DEBUG_ADC_1),
                           .label = CONFIG_REMOTE_DEBUG_ADC_1_LABEL});
#endif
#if CONFIG_REMOTE_DEBUG_NUM_ADCS >= 3
  adc1_channels.push_back({.channel = static_cast<adc_channel_t>(CONFIG_REMOTE_DEBUG_ADC_2),
                           .label = CONFIG_REMOTE_DEBUG_ADC_2_LABEL});
#endif
#if CONFIG_REMOTE_DEBUG_NUM_ADCS >= 4
  adc1_channels.push_back({.channel = static_cast<adc_channel_t>(CONFIG_REMOTE_DEBUG_ADC_3),
                           .label = CONFIG_REMOTE_DEBUG_ADC_3_LABEL});
  task_stack_size += 2048;
#endif
#if CONFIG_REMOTE_DEBUG_NUM_ADCS >= 5
  adc1_channels.push_back({.channel = static_cast<adc_channel_t>(CONFIG_REMOTE_DEBUG_ADC_4),
                           .label = CONFIG_REMOTE_DEBUG_ADC_4_LABEL});
#endif
#if CONFIG_REMOTE_DEBUG_NUM_ADCS >= 6
  adc1_channels.push_back({.channel = static_cast<adc_channel_t>(CONFIG_REMOTE_DEBUG_ADC_5),
                           .label = CONFIG_REMOTE_DEBUG_ADC_5_LABEL});
#endif
#if CONFIG_REMOTE_DEBUG_NUM_ADCS >= 7
  adc1_channels.push_back({.channel = static_cast<adc_channel_t>(CONFIG_REMOTE_DEBUG_ADC_6),
                           .label = CONFIG_REMOTE_DEBUG_ADC_6_LABEL});
#endif
#if CONFIG_REMOTE_DEBUG_NUM_ADCS >= 8
  adc1_channels.push_back({.channel = static_cast<adc_channel_t>(CONFIG_REMOTE_DEBUG_ADC_7),
                           .label = CONFIG_REMOTE_DEBUG_ADC_7_LABEL});
  task_stack_size += 2048;
#endif

  // Configure remote debug
  espp::RemoteDebug::Config config {
    .device_name = CONFIG_REMOTE_DEBUG_DEVICE_NAME, .gpios = gpios, .adc1_channels = adc1_channels,
    .adc2_channels = {}, .server_port = static_cast<uint16_t>(CONFIG_REMOTE_DEBUG_SERVER_PORT),
    .adc_sample_rate = std::chrono::milliseconds(1000 / adc_sample_rate_hz),
    .gpio_update_rate = std::chrono::milliseconds(100), .adc_history_size = adc_buffer_size,
    .task_priority = 5, .task_stack_size = task_stack_size,
#if CONFIG_REMOTE_DEBUG_ENABLE_LOGS
    .enable_log_capture = true, .max_log_size = CONFIG_REMOTE_DEBUG_LOG_BUFFER_SIZE,
#else
    .enable_log_capture = false,
#endif
    .log_level = espp::Logger::Verbosity::INFO
  };

  espp::RemoteDebug remote_debug(config);
  remote_debug.start();

  logger.info("Remote Debug Server started on port {}!", CONFIG_REMOTE_DEBUG_SERVER_PORT);
  logger.info("GPIO pins available: {}", gpios.size());
  logger.info("ADC channels available: {}", adc1_channels.size());

  std::this_thread::sleep_for(2s);

  // Create a timer to periodically generate log messages
  int counter = 0;
  auto timer = espp::Timer(espp::Timer::Config{.name = "Log Timer",
                                               .period = 2s,
                                               .callback =
                                                   [&logger, &counter]() {
                                                     logger.info("Timer tick #{}", counter++);
                                                     if (counter % 3 == 0) {
                                                       logger.warn("Warning message every 3 ticks");
                                                     }
                                                     if (counter % 5 == 0) {
                                                       logger.error("Error message every 5 ticks");
                                                     }
                                                     return false; // don't stop
                                                   },
                                               .stack_size_bytes = 6192});

  logger.info("Timer started - generating log messages every 2 seconds");

  // Keep running
  while (true) {
    std::this_thread::sleep_for(1s);
  }

Public Functions

explicit RemoteDebug(const Config &config)

Construct remote debug interface.

Parameters:

config – Configuration structure

~RemoteDebug()

Destructor.

bool start()

Start the debug server.

Returns:

true if started successfully

void stop()

Stop the debug server.

inline bool is_active() const

Check if server is running.

Returns:

true if active

bool set_gpio(gpio_num_t pin, int level)

Set GPIO output level.

Parameters:
  • pin – GPIO pin number

  • level – Level (0=low, 1=high)

Returns:

true if successful

int get_gpio(gpio_num_t pin)

Read GPIO level.

Parameters:

pin – GPIO pin number

Returns:

Level (0 or 1), or -1 on error

bool configure_gpio(gpio_num_t pin, gpio_mode_t mode)

Configure GPIO mode.

Parameters:
  • pin – GPIO pin number

  • mode – GPIO mode (input/output)

Returns:

true if successful

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

struct AdcChannelConfig

ADC configuration.

Public Members

adc_channel_t channel

ADC channel.

adc_atten_t atten = {ADC_ATTEN_DB_12}

Attenuation (affects voltage range)

std::string label = {""}

Optional label for UI.

struct Config

Configuration for remote debug.

Public Members

std::string device_name = {"ESP32 Device"}

Device name shown in UI title.

std::vector<GpioConfig> gpios

GPIO pins to expose.

std::vector<AdcChannelConfig> adc1_channels

ADC1 channels to monitor.

std::vector<AdcChannelConfig> adc2_channels

ADC2 channels to monitor.

uint16_t server_port = {8080}

HTTP server port.

std::chrono::milliseconds adc_sample_rate = {100}

ADC sampling interval.

std::chrono::milliseconds gpio_update_rate = {100}

GPIO state update interval.

size_t adc_history_size = {20}

Number of ADC samples to keep.

size_t task_priority = {5}

Priority for update tasks.

size_t task_stack_size = {4096}

Stack size for update tasks.

bool enable_log_capture = {false}

Enable stdout redirection to file.

std::string log_file_path{"debug.log"}

Path to log file. Will be appended to espp::FileSystem::get_root_path().

size_t max_log_size = {100000}

Maximum log file size in bytes.

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

Log verbosity.

struct GpioConfig

GPIO configuration.

Public Members

gpio_num_t pin

GPIO pin number.

gpio_mode_t mode = {GPIO_MODE_OUTPUT}

Pin mode (input/output)

std::string label = {""}

Optional label for UI.