Interrupt APIs

The Interrupt class provides APIs to register interrupt handlers for GPIO and configure the interrupts. Interrupts can be configured to trigger on rising edge, falling edge, high level, low level, or any change in the input signal. A single interrupt object can manage multiple GPIO pins with separate callbacks for each GPIO, or separte Interrupt objects can be created (though that uses more memory and CPU as each Interrupt will has its own queue, ISR handler, and task.

Finally, the Interrupt class is also designed to be subclassed if desired to provide additional functionality, or it can of course be included as a member within another class. The Button class provides a very simple implementation of a subclass of Interrupt.

API Reference

Header File

Classes

class Interrupt : public espp::BaseComponent

A class to handle a GPIO interrupt.

This class uses the ESP-IDF GPIO interrupt handler to detect GPIO interrupts. It then calls the callback function with the event. It can handle multiple GPIO interrupts and call the appropriate callback for each GPIO interrupt.

The class uses a FreeRTOS queue to handle the events. The queue is created in the constructor and deleted in the destructor. The queue is used to wake up the task when an interrupt event occurs. The task then calls the appropriate callback for the interrupt. Since all the GPIO interrupts are handled by the same task, all the callbacks are called from the same task. This means that the callbacks should be fast and not block for long periods of time, otherwise the other interrupts will be delayed.

Regardless of the callback speed, some interrupts could still be missed if they happen too quickly. For this reason, the queue size can be set in the configuration (default is 10). If the queue is full, then the interrupt event will be missed. If you are expecting a lot of interrupts to happen quickly, then you should increase the queue size.

Another way to handle the situation where you have many interrupts and would like to separate out the processing by priority is to have different Interrupt objects for the different priority levels, and assign the interrupt pins/callbacks to the objects according to the priority levels that you want. This will ensure that interrupts at different priority levels / in different objects do not starve the each other, while ensuring that the interupts are still processed in an orderly fashion.

If CONFIG_GPIO_CTRL_FUNC_IN_IRAM is enabled, then the ISR handler will be placed in IRAM. In this condition, the interrupt class’ ISR handler will automatically disable the interrupt associated with that GPIO within the ISR handler.

If CONFIG_GPIO_CTRL_FUNC_IN_IRAM is enabled and the PinConfig has auto_reenable set to false, then the interrupt will not be reenabled automatically. This is because the ISR handler will disable the interrupt, and it will not be reenabled until the user reenables it. This use-case is recommended for ACTIVE_LOW or ACTIVE_HIGH interrupts which may need some other action to clear the interrupt condition and prevent the ISR from being triggered continuously. In this case, simply re-enable the interrupt when you have cleared the interrupt condition if you want to be able to respond to it again.

Interrupt Example

  static auto start = std::chrono::high_resolution_clock::now();

  auto callback = [&](const espp::Interrupt::Event &event) {
    auto now = std::chrono::high_resolution_clock::now();
    auto elapsed = std::chrono::duration<float>(now - start).count();
    logger.info("[Callback][{:.3f}] Interrupt: pin {} changed to active state: {}", elapsed,
                event.gpio_num, event.active);
  };

  espp::Interrupt::PinConfig io0 = {
      .gpio_num = GPIO_NUM_0,
      .callback = callback,
      .active_level = espp::Interrupt::ActiveLevel::LOW,
      .interrupt_type = espp::Interrupt::Type::ANY_EDGE,
      .pullup_enabled = true,
      .pulldown_enabled = false,
      // flexible filter requiring configuration (default is provided as 5us
      // threshold in 10us window), but other configurations can be manually
      // set as below
      .filter_type = espp::Interrupt::FilterType::FLEX_GLITCH_FILTER,
      .filter_config = {.window_width_ns = 10000, .window_threshold_ns = 5000},
  };
  espp::Interrupt::PinConfig io12 = {
      .gpio_num = GPIO_NUM_12,
      .callback = callback,
      .active_level = espp::Interrupt::ActiveLevel::LOW,
      .interrupt_type = espp::Interrupt::Type::ANY_EDGE,
      .pullup_enabled = true,
      .pulldown_enabled = false,
      // pre-configured 2 clock pulse width filter
      .filter_type = espp::Interrupt::FilterType::PIN_GLITCH_FILTER,
  };

  // make an interrupt for a single gpio
  {
    espp::Interrupt interrupt({
        .isr_core_id = 1,
        .interrupts = {io0},
        .task_config =
            {
                .name = "Interrupt task",
                .stack_size_bytes = 6192,
                .priority = 5,
            },
        .log_level = espp::Logger::Verbosity::DEBUG,
    });

    std::this_thread::sleep_for(5s);
  }

  // make multiple interrupts for multiple gpios
  {
    espp::Interrupt interrupt0({
        .interrupts = {io0},
        .task_config =
            {
                .name = "Interrupt task",
                .stack_size_bytes = 6192,
                .priority = 5,
            },
        .log_level = espp::Logger::Verbosity::DEBUG,
    });

    espp::Interrupt interrupt12({
        .interrupts = {io12},
        .task_config =
            {
                .name = "Interrupt 0 task",
                .stack_size_bytes = 6192,
                .priority = 5,
            },
        .log_level = espp::Logger::Verbosity::DEBUG,
    });

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

    // now lets read the instantaneous state of the interrupt pins
    auto is_0_active = interrupt0.is_active(io0);
    auto is_12_active = interrupt12.is_active(io12);
    logger.info("Instantaneous state of pin 0: {}", is_0_active);
    logger.info("Instantaneous state of pin 12: {}", is_12_active);

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

  // make a single interrupt for multiple GPIOs
  // make an interrupt for a single gpio
  {
    // Register for interrupts on a few pins (GPIO_NUM_0, GPIO_NUM_12)
    espp::Interrupt interrupt({
        .interrupts = {io0},
        .task_config =
            {
                .name = "Interrupt task",
                .stack_size_bytes = 6192,
                .priority = 5,
            },
        .log_level = espp::Logger::Verbosity::DEBUG,
    });

    // use the add_interrupt method to add another interrupt
    interrupt.add_interrupt(io12);

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

    // now lets read the instantaneous state of the interrupt pins
    auto active_states = interrupt.get_active_states();
    logger.info("Instantaneous state of pins: {}", active_states);

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

    // print out the minimum number of spaces in the interrupt queue over the
    // last 2 seconds
    auto min_queue_size = interrupt.get_min_queue_size();
    logger.info("Minimum queue size over last 4 seconds: {}", min_queue_size);
  }

#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0)
  esp_intr_dump(stdout);
#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0)

Subclassed by espp::Button

Public Types

enum class ActiveLevel

The active level of the GPIO.

Values:

enumerator LOW

Active low.

enumerator HIGH

Active high.

enum class Type

The type of interrupt to use for the GPIO.

Values:

enumerator ANY_EDGE

Interrupt on any edge.

enumerator RISING_EDGE

Interrupt on rising edge.

enumerator FALLING_EDGE

Interrupt on falling edge.

enumerator LOW_LEVEL

Interrupt on low level.

enumerator HIGH_LEVEL

Interrupt on high level.

typedef std::function<void(const Event&)> event_callback_fn

The callback for the event.

Public Functions

inline explicit Interrupt(const Config &config)

Constructor.

Parameters

config – The configuration for the interrupt

inline ~Interrupt()

Destructor.

inline size_t get_min_queue_size() const

Get the minimum number of free spaces in the queue.

This will return the minimum number of free spaces in the queue Over the lifetime of the object. This can be used to determine if the queue size is too small for the number of interrupts that are being received. It may also help indicate if the interrupt task priority is too low, preventing the queue from being serviced. Finally, it may also help to indicate if additional filtering may be needed on the interrupt line (either using the FilterType or with hardware filtering).

Returns

The minimum number of free spaces in the queue

inline void add_interrupt(const PinConfig &interrupt)

Add an interrupt to the interrupt handler.

Parameters

interrupt – The interrupt to add

inline void remove_all()

Remove all the interrupts from the interrupt handler.

This will remove all the interrupts that are currently registered with the interrupt handler. This will also disable all the interrupts that are currently enabled.

inline void remove_interrupt(const PinConfig &interrupt)

Remove an interrupt from the interrupt handler.

This will find a registered interrupt with the given GPIO number and remove it from the list of interrupts. It will also disable the interrupt for that GPIO number. If no interrupt is found with the given GPIO number, then nothing is done.

Parameters

interrupt – The interrupt to remove

inline void remove_interrupt(int gpio_num)

Remove an interrupt from the interrupt handler.

This will find a registered interrupt with the given GPIO number and remove it from the list of interrupts. It will also disable the interrupt for that GPIO number. If no interrupt is found with the given GPIO number, then nothing is done.

Parameters

gpio_num – The GPIO number of the interrupt to remove

inline void disable_all()

Disable all the interrupts.

This will disable all the interrupts that are currently registered with the interrupt handler. This will not remove the interrupts from the list of interrupts, so they can be reenabled later.

inline void disable_interrupt(const PinConfig &interrupt)

Disable the interrupt for the interrupt PinConfig.

This will disable the interrupt for the GPIO that is specified, regardless of whether the interrupt is in the list of interrupts or not.

Parameters

interrupt – The interrupt to disable

inline void disable_interrupt(int gpio_num)

Disable the interrupt for the GPIO.

This will disable the interrupt for the GPIO that is specified, regardless of whether the interrupt is in the list of interrupts or not.

Parameters

gpio_num – The GPIO number of the interrupt to disable

inline void enable_interrupt(const PinConfig &interrupt)

Enable the interrupt for the GPIO.

This will enable the interrupt for the GPIO that is specified, regardless of whether the interrupt is in the list of interrupts or not.

Parameters

interrupt – The interrupt to enable

inline void enable_interrupt(int gpio_num)

Enable the interrupt for the GPIO.

This will enable the interrupt for the GPIO that is specified, regardless of whether the interrupt is in the list of interrupts or not.

Parameters

gpio_num – The GPIO number of the interrupt to enable

inline bool is_active(const PinConfig &interrupt) const

Get the state of the interrupt.

This will check the raw logic level of the GPIO and return whether the interrupt is active or not according to the active level that was set in the configuration.

Parameters

interrupt – The interrupt to check

Returns

Whether the interrupt is active

inline std::vector<std::pair<int, bool>> get_active_states()

Get the state of all the interrupts.

Returns

A vector of the states of the interrupts as pairs of the GPIO number and whether the interrupt is active

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

The configuration for the interrupt.

Public Members

int isr_core_id = -1

The core to install the ISR service on. If -1, then the ISR service is installed on the core that this constructor is called on. If 0 or 1, then the ISR service is installed on the specified core. If the ISR service is already installed, then this function does nothing. If the core_id is invalid, then an error is logged and the ISR service is not installed.

std::vector<PinConfig> interrupts

The configuration for the interrupts.

size_t event_queue_size = 10

The size of the event queue.

Task::BaseConfig task_config

The configuration for the task.

espp::Logger::Verbosity log_level = espp::Logger::Verbosity::WARN

The log level for the interrupt.

struct Event

The event for the interrupt.

Public Members

uint8_t gpio_num

The GPIO number of the interrupt.

bool active

Whether the interrupt is active or not (based on the active level)

struct FilterConfig

The configuration for the filter.

This is used to configure the GPIO flex glitch filter The filter is used to filter out glitches on the GPIO whose pulses are shorter than window_threshold_ns within the window_width_ns sampling window.

Note

This is only supported on some chips (-C and -S series chips) and is only enabled if CONFIG_SOC_GPIO_FLEX_GLITCH_FILTER_NUM > 0.

Note

This filter config is only supported by the flex_glitch_filter. The pin_glitch_filter is not-configurable.

Public Members

uint32_t window_width_ns{10000}

The width of the sampling window in nanoseconds. Default is 10us.

uint32_t window_threshold_ns = {5000}

The threshold for the sampling window in nanoseconds. If the width of the pulse is less than this, it is filtered out. Default is 5us

struct PinConfig

The configuration for an interrupt on a GPIO.

This is used to configure the GPIO interrupt

Public Members

int gpio_num

GPIO number to for this interrupt.

event_callback_fn callback

Callback for the interrupt event.

bool auto_reenable = true

Whether to auto reenable the interrupt after it is triggered. If false, the interrupt will need to be reenabled in the callback or some other codepath. If true, the interrupt will be reenabled automatically before the callback is called.

ActiveLevel active_level

Active level of the GPIO.

Type interrupt_type = Type::ANY_EDGE

Interrupt type to use for the GPIO.

bool pullup_enabled = false

Whether to enable the pullup resistor.

bool pulldown_enabled = false

Whether to enable the pulldown resistor.

FilterType filter_type = FilterType::NONE

The type of filter to use. If set to FLEX_GLITCH_FILTER, the filter_config should be set.

FilterConfig filter_config = {}

The configuration for the filter. This is only used if filter_type is set to FLEX_GLITCH_FILTER