Hardware interrupts, or GPIO interrupts allow the microcontroller to detect and respond to specific changes in the input pins, even while the main program is running.
When an event occurs that generates an interrupt, the main program is temporarily halted and the Callback function is executed. Once the Callback function is complete, the main program continues its execution.
This is especially useful in situations where we need to handle events very quickly, which we might not detect otherwise. Or, to avoid constantly checking the status of a sensor, for example.
However, we should not abuse them either. Keep in mind that during the time it is handling the interrupt, the processor is ignoring everything else. So use them wisely, and always keep the ISR as short as possible.
If you don’t know what digital inputs or outputs are, or need more help, check these entries:
Using GPIO interrupts on the ESP32
Using GPIO interrupts on the ESP32 with the Arduino IDE is very similar to using it with a “conventional” Arduino, but it has some peculiarities.
Defining the callback function (ISR)
First, we define the callback function, which will be executed when the interrupt occurs on the GPIO pin. This function must have a void
data type and should not take any arguments.
void IRAM_ATTR myInterruptFunction() {
// Here you can add the code that will be executed when the interrupt occurs
}
Here comes one of the important differences; we have tagged the ISR with the IRAM_ATTR
attribute. This tells the compiler to place the function’s code in the ESP32’s internal RAM.
Without this attribute, the function would go to Flash, which is much slower. As we said, we want the ISR to be as short and fast as possible. Therefore, we must add the
More information at:
Memory Types - ESP32 - — ESP-IDF Programming Guide latest documentation
Configuring the interrupt
Once we have defined the callback function, we configure the interrupt on the desired GPIO pin using the attachInterrupt()
function.
This function takes three arguments: the GPIO pin number, the callback function, and the interrupt mode.
The interrupt mode determines when the interrupt will be triggered. The possible modes are:
RISING
: The interrupt is triggered when the pin goes from low to high (rising edge).FALLING
: The interrupt is triggered when the pin goes from high to low (falling edge).CHANGE
: The interrupt is triggered on both rising and falling edges.
For example, to configure an interrupt on pin 12 that is triggered on a rising edge:
const int interruptPin = 12; // GPIO pin where the interrupt will be configured
void setup() {
pinMode(interruptPin, INPUT_PULLUP); // Configure the pin as an input with an internal pull-up resistor
attachInterrupt(digitalPinToInterrupt(interruptPin), myInterruptFunction, RISING); // Configure the interrupt
}
Disabling the interrupt (optional)
If at any point you want to disable the interrupt, you can use the detachInterrupt()
function. This function takes the GPIO pin number where the interrupt was configured as an argument.
detachInterrupt(digitalPinToInterrupt(interruptPin)); // Disable the interrupt
Code example
Below is a complete example of how to use an interrupt with a button connected to pin 12 of the ESP32:
const int buttonPin = 12; // GPIO pin where the button is connected
volatile bool has_interrupted = false;
void IRAM_ATTR myInterruptFunction() {
has_interrupted = true;
}
void setup()
{
Serial.begin(115200);
pinMode(buttonPin, INPUT_PULLUP); // Configure the pin as an input with an internal pull-up resistor
attachInterrupt(digitalPinToInterrupt(buttonPin), myInterruptFunction, RISING); // Configure the interrupt
}
void loop()
{
if(has_interrupted)
{
Serial.println("Button pressed!");
has_interrupted = false;
}
}
In this example,
- Pressing the button connected to pin 12 will trigger the interrupt
- The callback function
myInterruptFunction()
will be executed - This will print the message Button pressed! to the serial monitor.