DIY Projects

ESP32. How to use external interrupts with Arduino code

Learn to use the external interrupts of theESP32with Arduino code. Compatible GPIO pins, move function to RAM with IRAM

The ESP32 has 26 digital pins which can be used to trigger the execution of a function using an external interrupt. An interrupt is a process that is triggered asynchronously by an external event. Interrupts make it possible to detect an event in real time while leaving the microcontroller’s processor to do other tasks.

 

The use of external interrupts simplifies the programming of events.

The ESP32 also has 4 Timers which allow you to program the triggering of alarms. Everything is explained in detail in this tutorial

Install the ESP-IDF SDK for ESP32 on IDE Arduino and PlatformIO

If you are new to ESP32 development boards you must first install the ESP-IDF development kit. Here are two tutorials to get started depending on your code editor

Follow the instructions in this tutorial for the Arduino IDE

And this one for PlatformIO (ideally with VSCode)

Introduction to interrupts (external)

The classic way to program when you want to trigger an event using a digital input is to test its value regularly in the loop (). Depending on its value, we execute the code corresponding to the state

void loop() { 
  // Read the value of the switch 
  button_state = digitalRead(PIN_BUTTON); 
 
  // if the button is pressed, changes the state of the output to HIGH, otherwise LOW 
  if (button_state == HIGH) { 
    // Switch on the LED 
    digitalWrite(PIN_LED, HIGH); 
  } else { 
    // Switch off the LED 
    digitalWrite(PIN_LED, LOW); 
  } 
}

This way of programming is suitable most of the time but poses problems in the following cases:

This is where interrupts are useful.

As the interrupt is triggered from an event external to the processors, it is an external interrupt.

ESP32 GPIO pins compatible with interrupts

Interrupts only work with digital inputs. The digital inputs that can trigger an interrupt are circled in the diagram below.

Be careful, however, because some pins go high (HIGH) or emit PWM signals at start-up or during reset. Other pins are used by the system to access flash memory or upload the program.

Do not use pins colored orange or red. Your program might behave unexpectedly while using these.

GPIO pin Digital input Note
0 PULL UP Sends a PWM signal at startup.
1 TX Debug output at startup
2 Connected to the on-board LED
3 Takes the HIGH state at startup
4
5 Sends a PWM signal at startup
6 Used for SPI flash memory
7 Used for SPI flash memory
8 Used for SPI flash memory
9 Used for SPI flash memory
10 Used for SPI flash memory
11 Used for SPI flash memory
12 Fail to start if in PULLUP mode
13
14 Sends a PWM signal at startup
15 Sends a PWM signal at startup
16
17
18
19
21
22
23
25
26
27
32
33
34
35
36
39

Data collected from official documentation.

Configure the digital input of the ESP32

To be able to read on a digital input of the GPIO, you must first configure the pin as an input using the pinMode() function like this

pinMode(pin, mode);

The pinMode method has two arguments

3 modes are available to configure the spindle as a digital input

To read a digital input – like a button – we use the digitalRead() function, which takes the GPIO pin number as argument.

digitalRead(pin);

Use the previous diagram and table to choose the GPIO pins for your project.

Each pull-up or pull-down resistor has a value of 45 kΩ according to the DC Characteristics table (3.3V, 25°C) of technical documentation.

How to add an external interrupt to an ESP32 project?

Then we assign a function that will be executed as soon as an event is detected on the spindle using the attachInterrupt (GPIO, FUNCTION, MODE) method. The method requires 3 arguments

5 trigger modes are possible

Note, it is no longer necessary to declare the pin using the digitalPinToInterrupt method

Declare the function to be executed in the IRAM (IRAM_ATTR)

The execution of a function called by an interrupt is blocking, ie it is necessary to wait for the end of its execution so that the rest of the code can continue.

Usually the code is executed directly on the flash memory of the development board. It is possible to move the function to the internal RAM of the ESP32 which is much faster.

To do this, simply place the IRAM_ATTR attribute just before the name of the function like this

void IRAM_ATTR myfastfunction(){
   ...
}

This is not mandatory and you can also test it yourself with the code offered at the end of the tutorial. However, it is strongly recommended to place in the RAM of the ESP32 all the functions called by the interrupts for a real project.

Example for triggering an interrupt using a switch

Now let’s move on to a concrete example. The number of times the user presses a push button (momentary switch) is counted using an interrupt. As soon as the counter reaches 5 clicks, an LED is lit for 5 seconds using a Timer then the interruption is released.

There are many solutions to trigger an external event:

Whatever equipment is used, we will always receive a LOW / HIGH type signal. So whatever your project, the code will remain perfectly the same

Here we will be using a push button. It is equipped with a return spring which returns the button to the LOW state as soon as it is released.

Circuit

The push button is connected to digital input 4. The LED is connected to output 32.

You can test the operation of the program in the absence of a pull resistor (PULL_UP). You can test using the ESP32’s internal pull-up resistor (PULL_UP) or add a resistor to the circuit (10kΩ for example).

The LED must be protected by a resistor, the value of which depends on the output voltage and current of the pin (3.3V – 40mA) and the maximum supply voltage of the LED.

You can use this calculator to determine the required resistance value for your circuit.

Series resistance calculator for one or more LEDs
Supply Voltage in Volt
Forward Tension in Volt
Avertissement: la tension directe ne doit pas dépasser la tension d'alimentation.
Current in mA
Resistor calculated in Ω
Estimated Power in W

This calculator is used to determine the resistance required to drive one or more LEDs connected in series from a voltage source at a specified current level.

Note. It is preferable to supply the circuit with a nominal power between 2 and 10 times the calculated value in order to avoid overheating.
Color Wavelength (nm) Voltage (V) for ⌀3mm LED Volatge (V) for ⌀5mm LED
Red 625-630  1,9-2,1 2,1-2,2
Blue 460-470 3,0-3,2 3,2-3,4
Green 520-525 2,0-2,2 2,0-2,2
Yellow 585-595 2,0-2,2 3,0-3,2
White 460-470 3,0-3,2 1,9-2,1

And buy a resistor assortment

See other assortments

Upload the Arduino code of the project

Create a new sketch on the Arduino IDE or a new PlatformIO project.

On the Arduino IDE you can remove the first line #include <Arduino.h> .

#include <Arduino.h>

// Filtre anti-rebond (debouncer)
#define DEBOUNCE_TIME 250
volatile uint32_t DebounceTimer = 0;

// Pin to which the button, PIR motion detector or radar is connected
#define PIN_BUTTON 4
uint32_t button_count = 0;

// LED to indicate the end of the program
#define PIN_LED 32
#define DELAY_LED 2000

// Uncomment to not put the function in the RAM of the ESP32
//void buttonpressed() {
// The function is placed in the RAM of the ESP32. 
void IRAM_ATTR buttonpressed() {
  if ( millis() - DEBOUNCE_TIME  >= DebounceTimer ) {
    DebounceTimer = millis();
    button_count += 1;
    Serial.printf("Button has been pressed %u times\n", button_count);
  } 
}

void setup() {
  Serial.begin(115200);
  pinMode(PIN_BUTTON, INPUT_PULLDOWN);
  attachInterrupt(PIN_BUTTON, buttonpressed, RISING);

  // Configure LED output
  pinMode(PIN_LED, OUTPUT);
}

void loop() {
  //Detach interruption after 5 clicks 
  if ( button_count >= 5) {
    detachInterrupt(PIN_BUTTON);
    Serial.println("Interrupt Detached!");
    // reset click counter to avoid re-enter here
    button_count = 0;
    
    // Lights up the LED to indicate that the interrupt is detached
    digitalWrite(PIN_LED, HIGH);
    delay(DELAY_LED);
    digitalWrite(PIN_LED, LOW);
  }
}

After uploading the program, open the serial monitor and do a RESET of the board to follow the progress

Button has been pressed 1 times
Button has been pressed 2 times
Button has been pressed 3 times
Button has been pressed 4 times
Button has been pressed 5 times
Interrupt Detached!

PlatformIO configuration for a LoLin D32

Here is an example platformio.ini configuration file for a LoLin D32 Pro development board

[env:lolin_d32_pro]
platform = espressif32
board = lolin_d32_pro
framework = arduino
monitor_speed = 115200

Explanation of the code

On initialization (setup function ), we declare that the input is a digital input. The internal pull-down resistor of the ESP32 is used which saves a resistance in the circuit. We attach the button to an interrupt. We execute the buttonpressed function as soon as we press the button by detecting the rising edge (RISING).

pinMode(PIN_BUTTON, INPUT_PULLDOWN);
attachInterrupt(PIN_BUTTON, buttonpressed, RISING);

If we execute the function as is as soon as we press the button, we will have multiple executions which can cause slowdown problems or unnecessary requests on a remote server.

To do this, it suffices to wait a certain time before executing the function, this operation is called Debounce.

There are complex libraries to do this, but here it is unnecessary. All you have to do is initialize a DEBOUNCE_TIME variable which contains the timestamp of the last execution. The time elapsed since the last pass in the buttonpressed function must be at least equal to the bounce time. Here it is set at 250ms at the start of the program. You can adjust the value according to your electronics.

To be sure that the function will be executed in priority and quickly, it is placed in the RAM of the ESP32 using the argument IRAM_ATTR.

The function is rudimentary and just increments a counter.

void IRAM_ATTR buttonpressed() {
  if ( millis() - DEBOUNCE_TIME  >= DebounceTimer ) {
    DebounceTimer = millis();
    button_count += 1;
  } 
}

The interruption is detached after 10 clicks on the button.

if ( button_count >= 10) { 
  detachInterrupt(PIN_BUTTON); 
  // reset click counter to avoid re-enter here 
  button_count = 0; 
}

Updates

5/10/2020 Publication of the article

Click to rate this post!
[Total: 2 Average: 5]
Exit mobile version