Continuous ADC

The ContinuousAdc provides a mechanism for high-frequency, continuous, deterministic sampling of analog voltages for multiple channels (potentially across multiple ADC units, depending on the ESP32 chip used). It does this be enabling the continuous ADC DMA mode and then running its own task which retrieves the data and filters it. When the user calls get_mv(adc_channel_t), it simply returns the most recent filtered value for that channel, if it was configured.

API Reference

Header File

Classes

class ContinuousAdc : public espp::BaseComponent

ContinuousAdc provides a wrapper around the ESP-IDF continuous adc subsystem, enabling high-frequency, filtered measurements of analog values. The get_mv() function will always return the most up to date value, without needing to perform additional reads (therefore it is non-blocking).

Continuous ADC Example

    std::vector<espp::AdcConfig> channels{
        {.unit = ADC_UNIT_1, .channel = ADC_CHANNEL_6, .attenuation = ADC_ATTEN_DB_12},
        {.unit = ADC_UNIT_1, .channel = ADC_CHANNEL_7, .attenuation = ADC_ATTEN_DB_12}};
    // this initailizes the DMA and filter task for the continuous adc
    espp::ContinuousAdc adc(
        {.sample_rate_hz = 20 * 1000,
         .channels = channels,
         .convert_mode =
             ADC_CONV_SINGLE_UNIT_1, // or BOTH_UNIT, ALTER_UNIT, SINGLE_UNIT_1, SINGLE_UNIT_2
         .window_size_bytes = 1024,
         .log_level = espp::Logger::Verbosity::WARN});
    adc.start();
    auto task_fn = [&adc, &channels](std::mutex &m, std::condition_variable &cv) {
      for (auto &conf : channels) {
        auto maybe_mv = adc.get_mv(conf);
        if (maybe_mv.has_value()) {
          fmt::print("{}: {} mV\n", conf, maybe_mv.value());
        } else {
          fmt::print("{}: no value!\n", conf);
        }
        auto maybe_rate = adc.get_rate(conf);
        if (maybe_rate.has_value()) {
          fmt::print("{}: {} Hz\n", conf, maybe_rate.value());
        } else {
          fmt::print("{}: no rate!\n", conf);
        }
      }
      // NOTE: sleeping in this way allows the sleep to exit early when the
      // task is being stopped / destroyed
      {
        std::unique_lock<std::mutex> lk(m);
        cv.wait_for(lk, 500ms);
      }
      // don't want to stop the task
      return false;
    };
    auto task = espp::Task({.callback = task_fn,
                            .task_config = {.name = "Read ADC"},
                            .log_level = espp::Logger::Verbosity::INFO});
    task.start();

    // test stopping and starting the adc
    std::this_thread::sleep_for(3s);
    logger.info("Stopping ADC");
    adc.stop();
    std::this_thread::sleep_for(3s);
    logger.info("Starting ADC");
    adc.start();

Note

The available modes, frequencies, and throughput is dependent on whether you run on ESP32, ESP32s2, or ESP32s3.

Public Functions

inline explicit ContinuousAdc(const Config &config)

Initialize and start the continuous adc reader.

Parameters

configConfig used to initialize the reader.

inline ~ContinuousAdc()

Stop, deinit, and destroy the adc reader.

inline void start()

Start the continuous adc reader.

inline void stop()

Stop the continuous adc reader.

inline std::optional<float> get_mv(const AdcConfig &config)

Get the most up to date filtered voltage (in mV) from the provided channel.

Parameters

config – The config used to initialize the channel (includes unit and channel)

Returns

std::optional<float> voltage in mV for the provided channel (if it was configured and the adc is running).

inline std::optional<float> get_rate(const AdcConfig &config)

Get the most up to date sampling rate (in Hz) from the provided channel.

Parameters

config – The config used to initialize the channel (includes unit and channel)

Returns

std::optional<float> Rate in Hz for the provided channel (if it was configured and the adc is running).

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 Config

Configure the sample rate (globally applied to each channel), select the number of channels, and the conversion mode (for S2 S3)

Public Members

size_t sample_rate_hz

Samples per second to read from each channel.

std::vector<AdcConfig> channels

Channels to read from, with associated attenuations.

adc_digi_convert_mode_t convert_mode

Conversion mode (unit 1, unit 2, alternating, or both). May depend on ESP32 chip.

size_t task_priority = {5}

Priority to run the adc data reading / filtering task.

size_t window_size_bytes = {256}

Amount of bytes to allocate for the DMA buffer when reading results. Larger values lead to more filtering.

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

Verbosity for the adc logger.