ESP32. Utiliser les interruptions externes avec du code Arduino • Domotique et objets connectés à faire soi-même

L’ESP32 dispose de 26 broches numériques qui peuvent être utilisées pour déclencher l’exécution d’une fonction à l’aide d’une interruption externe. Une interruption est un processus qui est déclenché de manière asynchrone par un évènement extérieur. Les interruptions permettent de détecter un évènement en temps réel tout en laissant le processeur du micro-contrôleur faire d’autres tâches.

L’utilisation des interruptions externes permet de simplifier la programmation des événements.

L’ESP32 dispose également de 4 Timers qui permettent de programmer le déclenchement d’alarmes. Tout est expliquée en détail dans ce tutoriel

A LIRE AUSSI :

ESP32. Utiliser les Timers et alarmes avec du code Arduino

Installer le SDK ESP-IDF pour ESP32 sur IDE Arduino et PlatformIO

Si vous débutez avec les cartes de développement ESP32 vous devez d’abord installer le kit de développement ESP-IDF. Voici deux tutoriels pour débuter en fonction de votre éditeur de code

Suivez les instructions de ce tutoriel pour l’IDE Arduino

Et celui-ci pour PlatformIO (idéalement avec VSCode)

Introduction aux interruptions (externes)

La façon classique de programmer lorsqu’on veut déclencher un événement à l’aide d’une entrée numérique est de tester sa valeur régulièrement dans la boucle loop(). En fonction de sa valeur, on exécute le code correspondant à l’état

void loop() { 
  // Lit la valeur du bouton 
  button_state = digitalRead(PIN_BUTTON); 
 
  // si le bouton est appuye, passe l etat de la sortie a HIGH, sinon LOW 
  if (button_state == HIGH) { 
    // Allume la LED 
    digitalWrite(PIN_LED, HIGH); 
  } else { 
    // Eteint la LED 
    digitalWrite(PIN_LED, LOW); 
  } 
}

Cette façon de programmer convient parfaitement la plupart du temps mais pose des problèmes dans les cas suivants :

  • On souhaite ralentir la boucle loop avec un delay()
  • On souhaite mettre en sommeil l’ESP32 et le réveiller lorsqu’un événement externe se produit. Par exemple une détection de mouvement, l’appui sur un bouton ou une touche tactile…

C’est dans ce cas que les interruptions sont utiles

Comme l’interruption est déclenchée depuis un événement extérieur au processeurs, c’est une interruption externe.

Broches du GPIO de l’ESP32 compatibles avec les interruptions

Les interruptions fonctionnent uniquement avec les entrées numériques. Les entrées numériques pouvant déclencher une interruption sont entourée d’un cercle sur le schéma ci-dessous.

Attention toutefois car certaines broches passent à l’état haut (HIGH) ou émettent des signaux PWM au démarrage ou lors de la réinitialisation. D’autres broches sont utilisées par le système pour accéder à la mémoire flash ou téléverser le programme.

N’utilisez pas les broches colorées en orange ou en rouge. Votre programme pourrait avoir un comportement inattendu en utilisant celles-ci.

Broche du GPIO Entrée numérique (Input) Remarque
0 PULL UP Envoi un signal PWM au démarrage.
1 TX Sortie de débogage au démarrage
2 Connecté à la LED embarquée
3 Prend l’état HIGH au démarrage
4
5 Envoi un signal PWM au démarrage
6 Utilisé pour la mémoire flash SPI
7 Utilisé pour la mémoire flash SPI
8 Utilisé pour la mémoire flash SPI
9 Utilisé pour la mémoire flash SPI
10 Utilisé pour la mémoire flash SPI
11 Utilisé pour la mémoire flash SPI
12 Echec de démarrage si en mode PULLUP
13
14 Envoi un signal PWM au démarrage
15 Envoi un signal PWM au démarrage
16
17
18
19
21
22
23
25
26
27
32
33
34
35
36
39

Données collectées depuis la documentation officielle.

Configurer l’entrée numérique de l’ESP32

Avoir de pouvoir utiliser sur une entrée numérique du GPIO, il faut tout d’abord configurer la broche comme une entrée à l’aide de la fonction pinMode() comme ceci

pinMode(broche, mode);

La méthode pinMode a deux arguments

  • Le numéro de la broche
  • Le mode

3 modes sont disponibles pour configurer la broche comme une entrée numérique

  • INPUT En fonction du périphérique d’entré utilisé, il faudra adapter le circuit. Par exemple, lorsqu’on utilise un bouton poussoir de type Momentary Switch, il faudra intégrer une résistance de tirage pull-up ou de rappel pull-down au circuit. Sans résistance de pull-up ou pull-down l’entrée de l’ESP32 est flottante. Le niveau logique est indéfini ce qui peut entraîner un fonctionnement erratique du programme. Par exemple ne pas détecter la pression sur un bouton ou au contraire détecter qu’il est enfoncé alors que ce n’est pas le cas.
  • INPUT_PULLUP On utilise la résistance de tirage (PULL UP) de l’ESP32
  • INPUT_PULLDOWN On utilise la résistance de rappel (PULL DOWN) de l’ESP32

Comment ajouter une interruption externe à un projet ESP32 ?

Ensuite on assigner une fonction qui sera exécutée dès qu’un événement est détecté sur la broche à l’aide de la méthode attachInterrupt(GPIO, FUNCTION, MODE). La méthode nécessite 3 arguments

  • GPIO La broche qui déclenche l’événement. C’est la broche que l’on a configuré précédemment
  • FUNCTION la fonction à exécuter lorsqu’un événement survient
  • MODE mode de déclenchement

5 modes de déclenchement sont possibles

  • LOW pour déclencher l’interruption chaque fois que la broche est à l’état bas (LOW)
  • HIGH pour déclencher l’interruption chaque fois que la broche est à l’état haut (HIGH)
  • CHANGE pour déclencher l’interruption chaque fois que la broche change d’état. Par exemple lorsqu’elle passer de HIGH à LOW ou LOW à HIGH
  • FALLING pour quand la broche passe de HIGH à LOW. C’est la détection du front montant.
  • RISING pour déclencher lorsque la broche passe de LOW à HIGH. C’est la détection du front descendant.

Déclarer la fonction à exécuter dans la IRAM (IRAM_ATTR)

L’execution d’une fonction appelée par une interruption est bloquante, c’est à dire qu’il faut attendre la fin de son exécution pour que le reste du code puisse continuer.

Habituellement, le code est exécuté directement sur la mémoire flash de la carte de développement. Il est possible de déplacer la fonction dans la RAM interne de l’ESP32 qui est beaucoup plus rapide.

Pour cela, il suffit de placer l’attribut IRAM_ATTR juste avant le nom de la fonction comme ceci

void IRAM_ATTR mafonctionrapide(){
   ...
}

Ce n’est pas obligatoire et vous pourrez d’ailleurs le tester vous même avec le code proposé à la fin du tutoriel. Cependant, il est fortement conseillé de placer dans la RAM de l’ESP32 toutes les fonctions appelées par les interruptions pour un projet réel.

Exemple pour déclencher une interruption à l’aide d’un interrupteur

Passons maintenant à un exemple concret. On compte à l’aide d’une interruption le nombre de fois que l’utilisateur appuie sur un bouton poussoir (momentary switch en anglais). Dès que le compteur atteint 5 clics, on allume une LED durant 5 secondes à l’aide d’un Timer puis on détache l’interruption.

Il existe de nombreuses solutions pour déclencher un événement externe :

Quelque soit le matériel utilisé, on recevra toujours un signal de type LOW / HIGH. Donc quelque soit votre projet, le code restera parfaitement identique

Ici, nous allons utiliser un bouton poussoir. Il est équipé d’un ressort de rappel qui fait retourner le bouton à l’état BAS dès qu’on relâche ce dernier.

Circuit

Le bouton poussoir est connecté à l’entrée numérique 4. La LED est connectée à la sortie 32.

Vous pouvez tester le fonctionnement du programme en l’absence de résistance de tirage (PULL_UP). Vous pouvez tester en utilisant la résistance de tirage (PULL_UP) interne de l’ESP32 ou ajouter une résistance au circuit (10 kΩ par exemple).

La LED doit être protégée par une résistance dont la valeur dépend de la tension et l’intensité de sortie de la broche (3,3V – 40mA) et de la tension d’alimentation maximale de la LED.

Vous pouvez utiliser ce calculateur pour déterminer la valeur de la résistance nécessaire pour votre circuit.

Voir d’autres assortiments

Téléverser le code Arduino du projet

Créer un nouveau croquis sur l’IDE Arduino ou un nouveau projet PlatformIO.

Sur l’IDE Arduino vous pouvez retirer la première ligne #include .

#include 

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

// Broche sur laquelle est connectee le bouton, le detecteur de mouvement PIR ou le radar
#define PIN_BUTTON 4
uint32_t button_count = 0;

// LED pour indiquer la fin du programme
#define PIN_LED 32
#define DELAY_LED 2000

// Decommenter pour de pas mettre la fonction dans la RAM de l ESP32
//void buttonpressed() {
// La fonction est placee dans la RAM de l 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 la sortie de la LED
  pinMode(PIN_LED, OUTPUT);
}

void loop() {
  //Detache l interruption apres 5 clics 
  if ( button_count >= 5) {
    detachInterrupt(PIN_BUTTON);
    Serial.println("Interrupt Detached!");
    // reset click counter to avoid re-enter here
    button_count = 0;
    
    // Allume la LED pour indiquer que l interruption est detachee
    digitalWrite(PIN_LED, HIGH);
    delay(DELAY_LED);
    digitalWrite(PIN_LED, LOW);
  }
}

Après avoir téléversé le programme, ouvrez le moniteur série et faites un RESET de la carte pour suivre le déroulement

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!

Configuration PlatformIO pour une LoLin D32

Voici un exemple de fichier de configuration platformio.ini pour une carte de développement LoLin D32 Pro

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

Explication du code

A l’initialisation (setup), on déclare que l’entrée est une entrée numérique. On utilise la résistance interne de pull-down de l’ESP32 ce qui permet d’économiser une résistance dans le circuit. On attache le bouton à une interruption. On exécute la fonction buttonpressed dès qu’on appuie sur le bouton en détectant le front montant (RISING).

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

Si on exécute tel quel la fonction dès qu’on presse le bouton, on va avoir des exécutions multiples ce qui peut poser des problèmes de ralentissement ou des requêtes inutiles sur un serveur distant..

Pour cela il suffit d’attendre un certain temps avant d’exécuter la fonction.

En anglais, cette opération se nomme Debounce.

Il existe des librairies complexes pour faire ça, mais ici c’est inutile. Il suffit d’initialiser une variable DEBOUNCE_TIME qui contient l’horodatage de la dernière exécution. Le temps écoulé depuis le dernier passage dans la fonction buttonpressed doit être au moins égal au temps de rebond. Ici, il est fixé à 250ms au début du programme. Vous pouvez ajuster la valeur en fonction de votre électronique.

Pour être certain que la fonction sera exécutée en priorité et rapidement, on la place dans la RAM de l’ESP32 à l’aide de l’argument IRAM_ATTR.

La fonction est rudimentaire et se contente d’incrémenter un compteur.

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

On détache l’interruption après 10 clics sur le bouton.

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

Mises à jour

30/09/2020 Publication de l’article

Avez-vous aimé cet article ?

[Total: 6 Moyenne: 4.8]