Construire un minuteur de cuisine Arduino fiable avec affichage LCD
Composants nécessaires
| Composant | Quantité |
|---|---|
| Arduino UNO | 1 |
| Écran LCD 16x2 (blanc sur bleu, compatible HD44780) | 1 |
| Boutons poussoirs 12 mm | 4 |
| Résistances 10 kΩ (pull-down) | 4 |
| Résistances 220 Ω (pour buzzer/LED) | 2 |
| Potentiomètre 100 kΩ (contraste LCD) | 1 |
| Buzzer piézoélectrique | 1 |
| Plaque d'essai et fils de connexion | 1 |
Principe de fonctionnement
Ce minuteur reproduit le comportement d'un minuteur de cuisine commercial, mais avec la satisfaction de l'avoir construit et programmé soi-même. Il fonctionne selon une machine à états à 4 modes :
Les 4 modes de fonctionnement
- IDLE (Attente)
- Mode initial. Affiche le temps programmé. Attend une action utilisateur.
- SETUP (Configuration)
- Accédé par appui long sur le bouton Reset (S4). Permet de régler heures, minutes, secondes via les boutons Up/Down (S1/S2) et de passer d'un champ à l'autre avec Start/Stop (S3).
- RUNNING (Comptage)
- Démarré/arrêté par le bouton Start/Stop (S3). Le temps défile. Retour à IDLE via Start/Stop ou Reset.
- RINGING (Alarme)
- Activé automatiquement à la fin du compte à rebours. Le buzzer sonne. N'importe quel bouton arrête l'alarme et revient à IDLE.
Architecture logicielle
Le code utilise la bibliothèque TimeLib.h (de Paul Stoffregen) pour la gestion du temps et LiquidCrystal.h pour l'affichage.
Gestion des boutons (anti-rebond par détection de front)
// Exemple pour le bouton Start/Stop
startStopButtonPressed = false;
startStopButtonState = digitalRead(startStopButtonPin);
if (startStopButtonState != startStopButtonPrevState) {
startStopButtonPressed = (startStopButtonState == HIGH);
startStopButtonPrevState = startStopButtonState;
}
Chaque bouton utilise une résistance de pull-down 10 kΩ : l'entrée est LOW au repos, HIGH quand on appuie.
Machine à états (switch-case dans loop())
switch (currentMode) {
case MODE_IDLE:
if (resetButtonPressed) Reset();
if (resetButtonLongPressed) currentMode = MODE_SETUP;
if (startStopButtonPressed) {
currentMode = (currentMode == MODE_IDLE) ? MODE_RUNNING : MODE_IDLE;
if (currentMode == MODE_RUNNING) startTime = now();
}
break;
case MODE_SETUP:
// Navigation et modification des valeurs...
break;
case MODE_RUNNING:
// Calcul du temps restant, détection fin de compteur...
break;
case MODE_RINGING:
// Tout appui sur un bouton -> IDLE
break;
}
Gestion du temps et alarme
case MODE_RUNNING:
currentTime = setupTime - (now() - startTime);
if (currentTime <= 0) currentMode = MODE_RINGING;
break;
case MODE_RINGING:
analogWrite(buzzerPin, 20); delay(20);
analogWrite(buzzerPin, 0); delay(40); // Bip périodique
break;
Affichage LCD selon le mode
L'écran est mis à jour à chaque boucle (avec un petit delay(10) pour la stabilité) :
- IDLE : « Timer ready » + temps programmé
- SETUP : « Setup mode: HRS/MINS/SECS » + valeurs en cours d'édition
- RUNNING : « Counting down... » + temps restant formaté HH:MM:SS
- RINGING : « RING-RING! »
Schéma de câblage (résumé)
- LCD : RS=12, EN=11, D4=5, D5=4, D6=3, D7=2, VO=potentiomètre, R/W=GND, VSS=GND, VDD=5V, A=5V via 220Ω, K=GND
- Boutons : S1(Up)=9, S2(Down)=8, S3(Start/Stop)=7, S4(Reset)=6 — chacun avec 10 kΩ vers GND
- Buzzer : Broche 10 (PWM) via 220 Ω
Code complet (ArduinoSketch)
/**************************************************
* Arduino Kitchen Timer v1.0 - 2016/01/27
* Auteur : Angelo Fiorillo (Rome, IT) - GPL3+
**************************************************/
#include <LiquidCrystal.h>
#include <TimeLib.h> // Corrige l'erreur 'now' non déclaré
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
const int buzzerPin = 10;
const int resetButtonPin = 6;
const int startStopButtonPin = 7;
const int downButtonPin = 8;
const int upButtonPin = 9;
// Variables de temps
int setupHours = 0, setupMinutes = 0, setupSeconds = 0;
time_t setupTime = 0;
int currentHours = 0, currentMinutes = 0, currentSeconds = 0;
time_t currentTime = 0, startTime = 0, elapsedTime = 0;
// États des boutons
int resetButtonState = LOW, startStopButtonState = LOW;
int upButtonState = LOW, downButtonState = LOW;
int resetButtonPrevState = LOW, startStopButtonPrevState = LOW;
int upButtonPrevState = LOW, downButtonPrevState = LOW;
bool resetButtonPressed = false, resetButtonLongPressed = false;
bool startStopButtonPressed = false, upButtonPressed = false, downButtonPressed = false;
// Détection appui long (reset)
long resetButtonLongPressCounter = 0;
// Modes
const int MODE_IDLE = 0, MODE_SETUP = 1, MODE_RUNNING = 2, MODE_RINGING = 3;
int currentMode = MODE_IDLE;
int dataSelection = 0; // 0=heures, 1=minutes, 2=secondes
void setup() {
lcd.begin(16, 2);
pinMode(resetButtonPin, INPUT);
pinMode(startStopButtonPin, INPUT);
pinMode(upButtonPin, INPUT);
pinMode(downButtonPin, INPUT);
pinMode(buzzerPin, OUTPUT);
Serial.begin(9600);
}
void loop() {
// --- Lecture boutons (détection fronts) ---
// Reset (avec appui long)
resetButtonPressed = false; resetButtonLongPressed = false;
resetButtonState = digitalRead(resetButtonPin);
if (resetButtonState != resetButtonPrevState) {
resetButtonPressed = (resetButtonState == HIGH);
resetButtonPrevState = resetButtonState;
} else {
if (resetButtonState == HIGH) {
if (++resetButtonLongPressCounter == 100) {
resetButtonPressed = false;
resetButtonLongPressed = true;
resetButtonLongPressCounter = 0;
}
} else {
resetButtonLongPressCounter = 0;
}
}
// Start/Stop
startStopButtonPressed = false;
startStopButtonState = digitalRead(startStopButtonPin);
if (startStopButtonState != startStopButtonPrevState) {
startStopButtonPressed = (startStopButtonState == HIGH);
startStopButtonPrevState = startStopButtonState;
}
// Down
downButtonPressed = false;
downButtonState = digitalRead(downButtonPin);
if (downButtonState != downButtonPrevState) {
downButtonPressed = (downButtonState == HIGH);
downButtonPrevState = downButtonState;
}
// Up
upButtonPressed = false;
upButtonState = digitalRead(upButtonPin);
if (upButtonState != upButtonPrevState) {
upButtonPressed = (upButtonState == HIGH);
upButtonPrevState = upButtonState;
}
// --- Machine à états ---
switch (currentMode) {
case MODE_IDLE:
if (resetButtonPressed) Reset();
if (resetButtonLongPressed) currentMode = MODE_SETUP;
if (startStopButtonPressed) {
currentMode = (currentMode == MODE_IDLE) ? MODE_RUNNING : MODE_IDLE;
if (currentMode == MODE_RUNNING) startTime = now();
}
break;
case MODE_SETUP:
if (resetButtonPressed) {
setupTime = setupSeconds + 60L * setupMinutes + 3600L * setupHours;
currentHours = setupHours; currentMinutes = setupMinutes; currentSeconds = setupSeconds;
dataSelection = 0; currentMode = MODE_IDLE;
}
if (startStopButtonPressed) {
dataSelection = (dataSelection + 1) % 3;
}
if (downButtonPressed) {
switch (dataSelection) {
case 0: if (--setupHours < 0) setupHours = 99; break;
case 1: if (--setupMinutes < 0) setupMinutes = 59; break;
case 2: if (--setupSeconds < 0) setupSeconds = 59; break;
}
}
if (upButtonPressed) {
switch (dataSelection) {
case 0: if (++setupHours == 100) setupHours = 0; break;
case 1: if (++setupMinutes == 60) setupMinutes = 0; break;
case 2: if (++setupSeconds == 60) setupSeconds = 0; break;
}
}
break;
case MODE_RUNNING:
if (startStopButtonPressed) currentMode = MODE_IDLE;
if (resetButtonPressed) { Reset(); currentMode = MODE_IDLE; }
break;
case MODE_RINGING:
if (resetButtonPressed || startStopButtonPressed || downButtonPressed || upButtonPressed)
currentMode = MODE_IDLE;
break;
}
// --- Gestion du temps ---
switch (currentMode) {
case MODE_RUNNING:
currentTime = setupTime - (now() - startTime);
if (currentTime <= 0) currentMode = MODE_RINGING;
break;
case MODE_RINGING:
analogWrite(buzzerPin, 20); delay(20);
analogWrite(buzzerPin, 0); delay(40);
break;
}
// --- Affichage LCD ---
lcd.setCursor(0, 0);
switch (currentMode) {
case MODE_IDLE:
lcd.print("Timer ready ");
lcd.setCursor(0, 1);
lcd.print(currentHours); lcd.print(" ");
lcd.print(currentMinutes); lcd.print(" ");
lcd.print(currentSeconds); lcd.print(" ");
break;
case MODE_SETUP:
lcd.print("Setup mode: ");
lcd.print(dataSelection == 0 ? "HRS " : dataSelection == 1 ? "MINS" : "SECS");
lcd.setCursor(0, 1);
lcd.print(setupHours); lcd.print(" ");
lcd.print(setupMinutes); lcd.print(" ");
lcd.print(setupSeconds); lcd.print(" ");
break;
case MODE_RUNNING:
lcd.print("Counting down...");
lcd.setCursor(0, 1);
if (hour(currentTime) < 10) lcd.print("0");
lcd.print(hour(currentTime)); lcd.print(":");
if (minute(currentTime) < 10) lcd.print("0");
lcd.print(minute(currentTime)); lcd.print(":");
if (second(currentTime) < 10) lcd.print("0");
lcd.print(second(currentTime));
break;
case MODE_RINGING:
lcd.print(" RING-RING! ");
lcd.setCursor(0, 1); lcd.print(" ");
break;
}
delay(10);
}
void Reset() {
currentMode = MODE_IDLE;
currentHours = setupHours;
currentMinutes = setupMinutes;
currentSeconds = setupSeconds;
}
Améliorations possibles
- Ajouter une mise en veille de l'écran après inactivité (économie d'énergie).
- Utiliser un module RTC (DS3231) pour garder l'heure si alimentation coupée.
- Remplacer les
delay()dans l'alarme par une gestion non-bloquante avecmillis(). - Ajouter une mémoire EEPROM pour sauvegarder le dernier temps programmé.
- Boîtier imprimé en 3D pour un fini professionnel.
Processus de fabrication
- Alerte Soif Plante Intelligente – Capteur DIY pour une Hydratation Optimale
- Construire un oscilloscope Arduino 20 kHz sur écran Nokia 5110 – Guide DIY facile
- Stores intelligents : contrôle automatisé de la lumière avec Arduino et énergie solaire
- Horloge à Mots Italienne avec Arduino Nano, NeoPixel, LCD et RTC
- Construire un minuteur à rebours Arduino fiable avec afficheur 7 segments SparkFun
- Compteur kWh Sigfox fiable avec Arduino MKR Fox 1200
- Moniteur de température Bluetooth avec Arduino Nano
- Circuit d'entrée analogique isolée pour Arduino : Protection contre les parasites industriels
- Test Précis de Temps de Réaction avec Arduino UNO