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é.

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.
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
- Arduino — hub des tutoriels Arduino
- Configurer les GPIO Arduino — prérequis sur
pinModeetINPUT_PULLUP - Piloter une sortie TOR — la sortie correspondante
- Temporiser —
delay()vsmillis(), indispensable pour l’anti-rebond non bloquant - Interruptions — alternative à la lecture par scrutation pour réagir vite