Une entrée TOR (Tout Ou Rien) lit un signal binaire qui ne prend que deux états : haut ou bas, appuyé ou relâché, présent ou absent. Bouton-poussoir, interrupteur, fin de course, capteur de présence numérique : tous se lisent par digitalRead(). La difficulté n’est pas dans la lecture elle-même mais dans le traitement du rebond : un contact mécanique génère plusieurs commutations parasites en quelques millisecondes, qu’il faut filtrer pour obtenir un signal propre.

À quoi ça sert ?

Lire une entrée TOR est le geste fondateur de toute interface utilisateur embarquée : démarrer un cycle, valider une consigne, déclencher une mesure, détecter une fin de course mécanique. Sur un projet typique on en a plusieurs (bouton Marche/Arrêt, fin de course de chaque axe, capteur de présence pièce). La bonne pratique est d’apprendre une fois la lecture avec anti-rebond, puis de la dupliquer proprement sur chaque entrée du projet.

Procédure pas à pas

Quatre étapes : câbler avec pull-up, lire, ajouter l’anti-rebond, détecter le front.

1. Câbler le bouton avec INPUT_PULLUP

Un côté du bouton sur la broche choisie (ici D2), l’autre côté sur GND. Aucune résistance externe : la résistance interne de pull-up (configurée par pinMode(BOUTON, INPUT_PULLUP)) tire le potentiel vers +5 V quand le bouton est relâché et le fait tomber à GND quand il est appuyé.

Logique inversée : digitalRead() renvoie HIGH au repos, LOW quand appuyé.

Montage : bouton-poussoir entre D2 et GND et LED + résistance sur D13 (réutilisée dans l'exemple), carte Arduino Uno|600

2. Lecture brute

const int BOUTON = 2;                  // bouton câblé sur la broche D2
 
void setup() {
  pinMode(BOUTON, INPUT_PULLUP);       // entrée + résistance interne : repos = HIGH
  Serial.begin(115200);                // ouvre la liaison série pour observer
}
 
void loop() {
  int etat = digitalRead(BOUTON);      // lit la broche : HIGH (1) ou LOW (0)
  Serial.println(etat);                // affiche 1 ou 0 au moniteur
  delay(10);                           // petite pause : ~100 lectures par seconde
}

Téléversez, ouvrez le moniteur série, appuyez : on voit 1, 1, 1, 0, 0, 0, 1, 1, 1.... Mais si vous regardez de très près, vous verrez parfois apparaître quelques transitions parasites au moment de l’appui (0, 1, 0, 1, 0). C’est le rebond mécanique du contact qui dure quelques millisecondes.

3. Anti-rebond logiciel

La parade la plus simple : ignorer toute commutation qui n’est pas confirmée pendant 20-50 ms.

Chronogramme de l'anti-rebond : à l'appui, la lecture brute oscille quelques millisecondes (rebond) ; après 30 ms sans changement, l'état stable bascule une seule fois.|640

const int BOUTON = 2;                    // bouton câblé sur la broche D2
 
int dernierEtat = HIGH;                  // dernière valeur LUE (tremble pendant le rebond)
int etatStable = HIGH;                   // état CONFIRMÉ du bouton (celui sur lequel on agit)
unsigned long dernierChangement = 0;     // date, en ms, du dernier changement de lecture
const unsigned long DELAI_REBOND = 30;   // calme exigé avant de valider (ms)
 
void setup() {
  pinMode(BOUTON, INPUT_PULLUP);         // entrée + résistance interne : repos = HIGH
  Serial.begin(115200);                  // liaison série pour afficher les appuis
}
 
void loop() {
  int lecture = digitalRead(BOUTON);     // lecture brute à chaque tour de boucle
 
  // La lecture vient-elle de changer ? (à l'appui, elle change plein de fois : rebond)
  if (lecture != dernierEtat) {
    dernierChangement = millis();        // on note QUAND ce changement a eu lieu
    dernierEtat = lecture;               // et on retient la nouvelle valeur lue
  }
 
  // La lecture est-elle restée identique assez longtemps (> 30 ms) ?
  if (millis() - dernierChangement > DELAI_REBOND) {   // oui : le rebond est fini
    if (lecture != etatStable) {         // et l'état confirmé a vraiment changé
      etatStable = lecture;              // on valide le nouvel état
      Serial.print("Bouton : ");
      Serial.println(etatStable == LOW ? "appuye" : "relache");  // LOW = appuyé (pull-up)
    }
  }
}

Comment lire ce code. L’astuce tient en deux variables. dernierEtat suit la valeur brute lue à l’instant (elle tremble pendant le rebond). etatStable ne retient que l’état confirmé, le seul sur lequel on agit. À chaque tour de loop() :

  • si la lecture change, on ne croit pas le bouton tout de suite : on note seulement l’heure du changement (dernierChangement = millis()) ;
  • tant qu’elle rechange (rebond), cette heure est repoussée encore et encore ;
  • dès qu’elle reste identique pendant 30 ms, le rebond est terminé : on valide etatStable.

millis() renvoie le nombre de millisecondes écoulées depuis le démarrage de la carte. millis() - dernierChangement est donc le temps écoulé depuis le dernier tremblement. Le comparer à DELAI_REBOND, c’est demander : « le signal est-il calme depuis assez longtemps pour y croire ? »

Tester : maintenant chaque appui produit exactement une ligne Bouton : appuye puis Bouton : relache, peu importe la qualité mécanique du bouton.

4. Détecter le front (appui vs maintien)

Souvent on veut réagir à l’appui lui-même (toggle, déclenchement) et pas tant que la touche est maintenue. C’est la détection de front descendant :

// Dans la branche d'anti-rebond, remplacer le println par :
if (etatStable == LOW) {
  // FRONT DESCENDANT — bouton vient d'être appuyé
  Serial.println("Appui detecte !");
}

Variante toggle (un appui inverse l’état d’une LED) :

int etatLED = LOW;
// ...
if (etatStable == LOW) {
  etatLED = !etatLED;
  digitalWrite(LED_BUILTIN, etatLED);
}

Exemple — Compter les appuis et basculer une LED

Cas complet qui combine anti-rebond + détection de front + action visible.

const int BOUTON = 2;
const int LED = 13;
 
int dernierEtat = HIGH, etatStable = HIGH;   // mêmes 2 variables qu'à l'étape 3
unsigned long dernierChangement = 0;
const unsigned long DELAI_REBOND = 30;
 
int compteur = 0;                            // nombre d'appuis comptés
bool ledAllumee = false;                     // état courant de la LED
 
void setup() {
  pinMode(BOUTON, INPUT_PULLUP);
  pinMode(LED, OUTPUT);
  Serial.begin(115200);
}
 
void loop() {
  int lecture = digitalRead(BOUTON);
  if (lecture != dernierEtat) {              // --- bloc anti-rebond, identique à l'étape 3 ---
    dernierChangement = millis();
    dernierEtat = lecture;
  }
  if (millis() - dernierChangement > DELAI_REBOND && lecture != etatStable) {
    etatStable = lecture;                    // état confirmé
    if (etatStable == LOW) {                 // front descendant = un nouvel appui
      compteur++;                            // +1 appui
      ledAllumee = !ledAllumee;              // on inverse la LED (toggle)
      digitalWrite(LED, ledAllumee);         // on applique le nouvel état à la LED
      Serial.print("Appui n°");
      Serial.println(compteur);
    }
  }
}

Chaque appui incrémente un compteur visible au moniteur série et inverse l’état de la LED (comportement net, insensible au rebond).

Pièges

Pas d’anti-rebond. Symptôme typique : un appui de bouton compte pour 3 ou 4 incréments. Le rebond mécanique est invisible à l’œil mais visible à la microseconde du processeur.

Câblage INPUT sans résistance externe. Sans pull-up ou pull-down, la broche flotte. La LED bascule au moindre passage de main au-dessus de la carte (effet antenne). Quasi-toujours, INPUT_PULLUP + bouton vers GND est la solution.

Inverser la logique du pull-up. digitalRead() == HIGH signifie relâché, pas appuyé. Une erreur d’inversion donne une LED allumée par défaut qui s’éteint à l’appui, fonctionnel mais contre-intuitif.

Délai d’anti-rebond trop court. En dessous de ~10 ms, certains boutons mécaniques bon marché passent à travers le filtre. 20-50 ms est la plage usuelle. Ne pas descendre sans avoir mesuré le rebond du composant utilisé.

Confondre maintien et appui. Une boucle qui réagit à digitalRead() == LOW agit tant que le bouton est maintenu (ex. compteur qui incrémente 1000 fois par seconde de maintien). Pour un comportement par appui, il faut détecter le front : comparer l’état actuel à l’état précédent.

delay() dans la boucle de lecture. Un delay(500) au milieu du loop() rate les appuis brefs. Le pattern anti-rebond ci-dessus n’utilise volontairement aucun delay : il observe le temps via [[arduino-temporisation|millis()]].

Brancher le bouton entre 5 V et la broche. Mode INPUT_PULLUP + bouton vers 5 V = lecture toujours HIGH. Le bouton court-circuite la pull-up vers son propre niveau. Toujours bouton vers GND.

Cas particulier — Plusieurs boutons

Pour 4-8 boutons on duplique simplement le pattern (un état stable, un dernier changement par bouton). Au-delà, deux pistes :

  • Matrice de boutons (lignes × colonnes scannées) pour 16+ boutons sur peu de broches.
  • Convertisseur analogique (réseau de résistances + analogRead) pour lire N boutons sur une seule broche analogique — économique mais sensible.

Voir bibliothèques Arduino pour Bounce2 (anti-rebond clé en main) ou OneButton (détection clic court, long, double).

Raccrochage projet

  • Étape 2 de la phase de preuve de concept — tout bouton de commande utilisateur (Marche/Arrêt, validation) doit être testé isolément avec anti-rebond avant d’être intégré.
  • Étape 2 de la phase d’intégration et tests — chaque fin de course mécanique (axe en butée, présence pièce) se valide comme une entrée TOR avec anti-rebond.

Le pattern anti-rebond est à coller-modifier d’une fiche à l’autre une fois qu’il est compris une fois. Investir 10 minutes ici évite des heures de débogage où l’on cherche pourquoi le système compte les appuis de travers.

Voir aussi