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.
-
enumerator LOW
-
enum class Type
The type of interrupt to use for the GPIO.
Values:
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
See also
See also
- Returns
The verbosity level of the logger
-
inline void set_log_level(espp::Logger::Verbosity level)
Set the log level for the logger
See also
See also
- 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
See also
See also
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
See also
See also
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
See also
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.
-
size_t event_queue_size = 10
The size of the event queue.
-
Task::BaseConfig task_config
The configuration for the task.
-
int isr_core_id = -1
-
struct Event
The event for the interrupt.
-
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.
-
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.
-
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
-
int gpio_num
-
enum class ActiveLevel