ESP32 GPIO Interrupts with Arduino IDE
Configure and handle interrupts with the ESP32 board to detect and respond to changes on its input GPIOs using Arduino IDE. Interface interrupts with pusbhutton and PIR motion sensor
- Suitable forB.Tech/Diploma IoT/ESP32 students
- Build time1-2 weeks
- LevelBeginner
- Available atAjvika Labs, Kakinada
About this project
Learn how to configure and handle interrupts with the ESP32 board to detect and respond to changes on its input GPIOs. We’ll build a project example using a pushbutton and another one using a PIR Motion Sensor.
Setting an ESP32 Input Pin as an interrupt allows you to quickly detect events (changes in the GPIO state) and react to them by halting the main program and running a callback function (also called an interrupt service routine—ISR). This can be useful to detect the press of a pushbutton, when a sensor passes a certain threshold, when motion is detected, etc.
Table of Contents
In this tutorial, we’ll cover the following subjects:
Introducing Interrupts
What are Interrupts?
Types of Interrupts
Using Interrupts with the ESP32
Example 1: ESP32 – Detecting a Button Press with an Interrupt
Example 2: ESP32 with a PIR Sensor – Detect Motion with an Interrupt
Prerequisites
Before proceeding with this tutorial, you should have the ESP32 boards installed in your Arduino IDE. Follow this next tutorial to install the ESP32 on the Arduino IDE, if you haven’t already.
Installing ESP32 Board in Arduino IDE 2 (Windows, Mac OS X, Linux)
Introducing Interrupts
Interrupts are useful for making things happen automatically in microcontroller programs and can help solve timing problems.
Interrupts provide mechanisms to respond to external events, enabling your ESP32 boards to react quickly to changes without continuously polling (continuously checking the current value of a pin or variable).
What are Interrupts?
Interrupts are signals that pause the normal execution flow of a program to handle a specific event. When an interrupt happens, the processor stops the execution of the main program to execute a task and then gets back to the main program. That task is also referred to as an
interrupt handling routine
(or
interrupt service routine
, ISR), as shown in the figure below.
In summary, with interrupts, you don’t need to constantly check the current value of a pin. With interrupts, when a change is detected, a callback function is triggered.
Types of Interrupts
There are different types of interrupts: external interrupts (hardware-based) and timer interrupts (software interrupts).
External Interrupts
: triggered by external signals and detected on the ESP32 GPIOs, such as a button press or a sensor reading—this is hardware-based and associated with a specific GPIO pin. When the state of a pin changes, it will trigger the interrupt service routine (ISR). We’ll focus on these in this tutorial.
Timer Interrupts (software timers)
: initiated based on time intervals, enabling periodic actions—this is software-based.
You can learn more about software interrupts with the ESP32 in this tutorial
Using Interrupts with the ESP32
To set an interrupt in the Arduino IDE, you use the
attachInterrupt()
function, that accepts as arguments: the GPIO pin, the name of the function to be executed, and the mode:
attachInterrupt(GPIO, callback_function, mode);
This instruction should be added to the
setup()
of your Arduino code.
Let’s take a look at the arguments you should pass to that function.
GPIO Interrupt
The first argument of the
attachInterrupt()
function is the GPIO number where we’ll detect the change. For example, if you want to use GPIO 27 as an interrupt, you can use:
digitalPinToInterrupt(27)
With an ESP32 board, all the pins that can act as inputs can be set as interrupts.
Callback Function
The second argument of the
attachInterrupt()
function is the name of the function that will be called when the interrupt is triggered.
Now, there are a few important rules you should be aware of when defining your ISR (callback function).
The ISR should not return anything.
ISRs should be as short and fast as possible because they halt the normal execution of the code.
They should have the
ARDUINO_ISR_ATTR
attribute, so that they run in the ESP32 Internal RAM and not in Flash. IRAM access is much faster, which is critical for ISRs to run reliably without timing issues or crashes during interrupts.
Variables that are used inside ISRs and throughout the code should preferably be
volatile
. This prevents the compiler from caching values in registers (and skipping memory access), so reads/writes always access the actual memory location and reflect unexpected changes caused by the interrupt.
Here’s an example of an ISR so that you can check its syntax:
void ARDUINO_ISR_ATTR my_callback() {
// Any code you want to run
}
Another important thing about ISRs is that you should keep their code as fast and simple as possible and avoid things like complex operations, writing to the Serial Monitor, or using
delay()
. Instead, you should use a flag or counter to indicate that the interrupt happened, and then handle whatever you need to do in the main code or
loop()
section.
Mode
The third argument is the mode. There are 5 different modes:
LOW
: to trigger the interrupt whenever the pin is LOW;
HIGH
: to trigger the interrupt whenever the pin is HIGH;
CHANGE
: to trigger the interrupt whenever the pin changes value – for example, from HIGH to LOW or LOW to HIGH;
FALLING
: for when the pin goes from HIGH to LOW;
RISING
: to trigger when the pin goes from LOW to HIGH.
The following picture will help you better understand the different trigger modes.
Attach an Interrupt with Arguments
Besides the
attachInterrupt()
function, you can alternatively use the
attachInterruptArg()
function instead. The function
attachInterruptArg()
is used to attach the interrupt to the defined pin using argumentsthis means you can pass arguments to the callback function.
attachInterruptArg(uint8_t pin, void callback_function, void * arg, int mode);
pin
defines the GPIO pin number.
callback_function
set the callback function.
arg
pointer to the interrupt arguments.
mode
set the interrupt mode.
Detaching/Disabling an Interrupt from a GPIO Pin
When you don’t want the ESP32 to no longer monitor the pin, you can call the
detachInterrupt()
function and pass as argument the GPIO pin.
detachInterrupt(digitalPinToInterrupt(interruptPin));
Example 1: ESP32 – Detecting a Button Press with an Interrupt
In this section, we’ll create a simple example to detect a button press using an interrupt. We’re using a pushbutton in this example, but you can use a sensor with a threshold, like a PIR Motion Sensor, for example (we’ll take a look at that later in this guide).
Parts Required
ESP32 Board
(model of your choice)
1x
Pushbutton
Jumper Wires
Breadboard
You can use the preceding links or go directly to
MakerAdvisor.com/tools
to find all the parts for your projects at the best price!
Wiring Diagram
For this example, you need to connect a pushbutton to GPIO 18. We won’t use any resistors for the pushbutton because we’ll use the ESP32 internal pull-up resistors. Wire one lead of the pushbutton to GPIO 18 and the other one to GND, as shown in the diagram below.
Alternatively, you can use any other suitable GPIO as long as you modify the code. Make sure to check our
ESP32 Pinout Guide
Code
Upload the following code to your ESP32. It detects the pushbutton presses and prints the number of presses in the Serial Monitor.
#include <Arduino.h>
// Global variables for the button
const uint8_t buttonPin = 18;
volatile int32_t counter = 0;
volatile bool pressed = false;
// Interrupt Service Routine (ISR)
void ARDUINO_ISR_ATTR buttonISR() {
counter++;
pressed = true;
}
void setup() {
Serial.begin(115200);
pinMode(buttonPin, INPUT_PULLUP);
attachInterrupt(buttonPin, buttonISR, RISING);
Serial.println("Press the button on GPIO 18.");
}
void loop() {
if (pressed) {
Serial.print("Button pressed ");
Serial.print(counter);
Serial.println(" times.");
pressed = false;
}
delay(10);
}
How Does the Code W
Setting an ESP32 Input Pin as an interrupt allows you to quickly detect events (changes in the GPIO state) and react to them by halting the main program and running a callback function (also called an interrupt service routine—ISR). This can be useful to detect the press of a pushbutton, when a sensor passes a certain threshold, when motion is detected, etc.
Table of Contents
In this tutorial, we’ll cover the following subjects:
Introducing Interrupts
What are Interrupts?
Types of Interrupts
Using Interrupts with the ESP32
Example 1: ESP32 – Detecting a Button Press with an Interrupt
Example 2: ESP32 with a PIR Sensor – Detect Motion with an Interrupt
Prerequisites
Before proceeding with this tutorial, you should have the ESP32 boards installed in your Arduino IDE. Follow this next tutorial to install the ESP32 on the Arduino IDE, if you haven’t already.
Installing ESP32 Board in Arduino IDE 2 (Windows, Mac OS X, Linux)
Introducing Interrupts
Interrupts are useful for making things happen automatically in microcontroller programs and can help solve timing problems.
Interrupts provide mechanisms to respond to external events, enabling your ESP32 boards to react quickly to changes without continuously polling (continuously checking the current value of a pin or variable).
What are Interrupts?
Interrupts are signals that pause the normal execution flow of a program to handle a specific event. When an interrupt happens, the processor stops the execution of the main program to execute a task and then gets back to the main program. That task is also referred to as an
interrupt handling routine
(or
interrupt service routine
, ISR), as shown in the figure below.
In summary, with interrupts, you don’t need to constantly check the current value of a pin. With interrupts, when a change is detected, a callback function is triggered.
Types of Interrupts
There are different types of interrupts: external interrupts (hardware-based) and timer interrupts (software interrupts).
External Interrupts
: triggered by external signals and detected on the ESP32 GPIOs, such as a button press or a sensor reading—this is hardware-based and associated with a specific GPIO pin. When the state of a pin changes, it will trigger the interrupt service routine (ISR). We’ll focus on these in this tutorial.
Timer Interrupts (software timers)
: initiated based on time intervals, enabling periodic actions—this is software-based.
You can learn more about software interrupts with the ESP32 in this tutorial
Using Interrupts with the ESP32
To set an interrupt in the Arduino IDE, you use the
attachInterrupt()
function, that accepts as arguments: the GPIO pin, the name of the function to be executed, and the mode:
attachInterrupt(GPIO, callback_function, mode);
This instruction should be added to the
setup()
of your Arduino code.
Let’s take a look at the arguments you should pass to that function.
GPIO Interrupt
The first argument of the
attachInterrupt()
function is the GPIO number where we’ll detect the change. For example, if you want to use GPIO 27 as an interrupt, you can use:
digitalPinToInterrupt(27)
With an ESP32 board, all the pins that can act as inputs can be set as interrupts.
Callback Function
The second argument of the
attachInterrupt()
function is the name of the function that will be called when the interrupt is triggered.
Now, there are a few important rules you should be aware of when defining your ISR (callback function).
The ISR should not return anything.
ISRs should be as short and fast as possible because they halt the normal execution of the code.
They should have the
ARDUINO_ISR_ATTR
attribute, so that they run in the ESP32 Internal RAM and not in Flash. IRAM access is much faster, which is critical for ISRs to run reliably without timing issues or crashes during interrupts.
Variables that are used inside ISRs and throughout the code should preferably be
volatile
. This prevents the compiler from caching values in registers (and skipping memory access), so reads/writes always access the actual memory location and reflect unexpected changes caused by the interrupt.
Here’s an example of an ISR so that you can check its syntax:
void ARDUINO_ISR_ATTR my_callback() {
// Any code you want to run
}
Another important thing about ISRs is that you should keep their code as fast and simple as possible and avoid things like complex operations, writing to the Serial Monitor, or using
delay()
. Instead, you should use a flag or counter to indicate that the interrupt happened, and then handle whatever you need to do in the main code or
loop()
section.
Mode
The third argument is the mode. There are 5 different modes:
LOW
: to trigger the interrupt whenever the pin is LOW;
HIGH
: to trigger the interrupt whenever the pin is HIGH;
CHANGE
: to trigger the interrupt whenever the pin changes value – for example, from HIGH to LOW or LOW to HIGH;
FALLING
: for when the pin goes from HIGH to LOW;
RISING
: to trigger when the pin goes from LOW to HIGH.
The following picture will help you better understand the different trigger modes.
Attach an Interrupt with Arguments
Besides the
attachInterrupt()
function, you can alternatively use the
attachInterruptArg()
function instead. The function
attachInterruptArg()
is used to attach the interrupt to the defined pin using argumentsthis means you can pass arguments to the callback function.
attachInterruptArg(uint8_t pin, void callback_function, void * arg, int mode);
pin
defines the GPIO pin number.
callback_function
set the callback function.
arg
pointer to the interrupt arguments.
mode
set the interrupt mode.
Detaching/Disabling an Interrupt from a GPIO Pin
When you don’t want the ESP32 to no longer monitor the pin, you can call the
detachInterrupt()
function and pass as argument the GPIO pin.
detachInterrupt(digitalPinToInterrupt(interruptPin));
Example 1: ESP32 – Detecting a Button Press with an Interrupt
In this section, we’ll create a simple example to detect a button press using an interrupt. We’re using a pushbutton in this example, but you can use a sensor with a threshold, like a PIR Motion Sensor, for example (we’ll take a look at that later in this guide).
Parts Required
ESP32 Board
(model of your choice)
1x
Pushbutton
Jumper Wires
Breadboard
You can use the preceding links or go directly to
MakerAdvisor.com/tools
to find all the parts for your projects at the best price!
Wiring Diagram
For this example, you need to connect a pushbutton to GPIO 18. We won’t use any resistors for the pushbutton because we’ll use the ESP32 internal pull-up resistors. Wire one lead of the pushbutton to GPIO 18 and the other one to GND, as shown in the diagram below.
Alternatively, you can use any other suitable GPIO as long as you modify the code. Make sure to check our
ESP32 Pinout Guide
Code
Upload the following code to your ESP32. It detects the pushbutton presses and prints the number of presses in the Serial Monitor.
#include <Arduino.h>
// Global variables for the button
const uint8_t buttonPin = 18;
volatile int32_t counter = 0;
volatile bool pressed = false;
// Interrupt Service Routine (ISR)
void ARDUINO_ISR_ATTR buttonISR() {
counter++;
pressed = true;
}
void setup() {
Serial.begin(115200);
pinMode(buttonPin, INPUT_PULLUP);
attachInterrupt(buttonPin, buttonISR, RISING);
Serial.println("Press the button on GPIO 18.");
}
void loop() {
if (pressed) {
Serial.print("Button pressed ");
Serial.print(counter);
Serial.println(" times.");
pressed = false;
}
delay(10);
}
How Does the Code W
Technologies used
Hardware components
- ESP32
- Arduino
- PIR
- IR Sensor
Software & tools
- Arduino IDE
What you get
- Working project / hardware kit, tested before delivery
- Complete source code with explanation
- Project demo and guidance for your viva
- Project report, PPT and IEEE paper support (on request)
Delivery & Payment
Delivery takes a minimum of two weeks from the date your inquiry is confirmed.
Payment is in two parts:
• 50% advance to start the project
• Remaining 50% before delivery
Note:
• Documentation and PPT are not included by default
• If you need documentation/PPT, it can be arranged for extra time and cost
We'll confirm the exact timeline and cost with you after reviewing your requirements.