Arduino millis() au lieu de delay() : temporiser sans bloquer
Arduino millis() : pourquoi delay() fige ton programme, blink sans delay, plusieurs tâches à la fois, débordement de 49 jours sans bug, micros(), ESP32 et Pico.
La fonction Arduino millis() renvoie le temps écoulé depuis le démarrage de la carte, en millisecondes, et c'est la méthode standard pour temporiser sans delay(). Quand tu écris delay(1000), l'Arduino ne fait rien d'autre pendant une seconde entière : un appui sur un bouton passe inaperçu, les capteurs ne sont pas lus et l'écran n'est pas mis à jour. Avec millis(), tu te demandes plutôt "combien de temps s'est écoulé depuis la dernière tâche ?" ; tu exécutes les tâches qui sont dues, tu sautes les autres et loop() ne s'arrête jamais. Ainsi, un seul Arduino peut faire clignoter des LED, lire des boutons, mesurer des capteurs et envoyer des rapports sur le port série en même temps.
Dans ce guide, tu vas apprendre pourquoi delay() bloque ton programme, le célèbre exemple blink without delay (clignoter sans delay), comment exécuter plusieurs tâches en même temps, comment passer sans risque le débordement (overflow) qui survient au bout de 49,7 jours, la fonction micros() qui compte les microsecondes et des modèles de temporisation réutilisables. Dans les dernières parties, tu verras comment la même logique s'écrit sur l'ESP32 et sur le Raspberry Pi Pico (MicroPython).

Qu'est-ce que delay() et pourquoi arrête-t-il ton programme ?
delay(ms) fait tourner le processeur dans une boucle jusqu'à ce que le temps indiqué soit écoulé. Pendant ce temps, la ligne suivante de loop() n'est pas atteinte ; on parle d'attente bloquante. Pour faire clignoter une seule LED, aucun problème, mais dès que tu ajoutes un bouton au projet, les ennuis commencent. Le code ci-dessous fait clignoter une LED chaque seconde et vérifie aussi si le bouton est appuyé :
1const int LED_PIN = 13; // dahili LED / built-in LED
2const int BUTON_PIN = 2; // buton, diğer ucu GND / button, other leg to GND
3
4void setup() {
5 pinMode(LED_PIN, OUTPUT);
6 pinMode(BUTON_PIN, INPUT_PULLUP);
7 Serial.begin(9600);
8}
9
10void loop() {
11 digitalWrite(LED_PIN, HIGH);
12 delay(1000); // 1 sn bekle, buton okunmaz / wait 1 s, button is not read
13 digitalWrite(LED_PIN, LOW);
14 delay(1000);
15 if (digitalRead(BUTON_PIN) == LOW) { // 2 saniyede bir bakılır / checked once every 2 s
16 Serial.println(F("Butona basildi / Button pressed"));
17 }
18}Dans ce code, le bouton n'est détecté qu'une fois toutes les deux secondes, et seulement s'il est appuyé pile à ce moment-là ; un appui bref se perd entre les deux appels à delay(). Les interruptions continuent de fonctionner pendant delay() : millis() continue de compter et les octets reçus sur le port série sont stockés dans le tampon de 64 octets. Mais ton code, lui, attend. À mesure que tu ajoutes un écran, des capteurs et un buzzer, chaque delay() retarde toutes les autres tâches et l'appareil paraît lent. La solution : arrêter d'attendre et commencer à mesurer le temps.
Qu'est-ce que millis() et comment ça marche ?
millis() renvoie le nombre de millisecondes écoulées depuis la mise sous tension ou le reset de la carte, sous la forme d'un unsigned long (32 bits, non signé). Sur l'Arduino Uno, ce compteur est mis à jour en arrière-plan par l'interruption de débordement du timer matériel Timer0. À 16 MHz, Timer0 déborde toutes les 1,024 ms ; le cœur Arduino cumule les 0,024 ms en trop et, environ toutes les 43 ms, augmente le compteur de 2 au lieu de 1. millis() saute donc certaines valeurs, c'est pourquoi les tests d'égalité comme millis() == 5000 ne sont pas fiables ; utilise toujours >=. De plus, millis() n'avance pas tant que les interruptions sont désactivées (par exemple dans une routine d'interruption).
| Fonction | Ce qu'elle fait | Résolution (Uno, 16 MHz) | Débordement après | Bloque le programme ? |
|---|---|---|---|---|
| delay(ms) | Attend le nombre de millisecondes indiqué | 1 ms | - | Oui |
| delayMicroseconds(us) | Attente courte (précise de 3 à 16383 µs) | Environ 1 µs | - | Oui |
| millis() | Millisecondes depuis le démarrage | 1 ms (saute parfois de 2) | Environ 49,7 jours | Non |
| micros() | Microsecondes depuis le démarrage | 4 µs | Environ 71,6 minutes | Non |
Blink without delay : faire clignoter une LED avec millis()
- Crée une variable de type unsigned long pour mémoriser le moment où la tâche a été faite pour la dernière fois (oncekiZaman).
- À chaque passage dans loop(), lis le temps actuel avec millis().
- Vérifie si assez de temps s'est écoulé avec la condition simdi - oncekiZaman >= ARALIK.
- Si le délai est écoulé, fais la tâche (inverser la LED) et mets à jour oncekiZaman.
- Sinon, ne fais rien et laisse loop() continuer ; c'est précisément à ce moment que tes autres tâches s'exécutent.
1const int LED_PIN = LED_BUILTIN; // Uno'da 13 / pin 13 on the Uno
2const unsigned long ARALIK = 500; // yanıp sönme aralığı (ms) / blink interval (ms)
3
4unsigned long oncekiZaman = 0; // son değişim anı / time of the last toggle
5bool ledDurumu = false;
6
7void setup() {
8 pinMode(LED_PIN, OUTPUT);
9}
10
11void loop() {
12 unsigned long simdi = millis(); // şu anki zaman / current time
13 if (simdi - oncekiZaman >= ARALIK) { // taşmaya karşı güvenli / overflow-safe
14 oncekiZaman += ARALIK; // kaymayı önler / prevents drift
15 ledDurumu = !ledDurumu;
16 digitalWrite(LED_PIN, ledDurumu);
17 }
18 // burada başka işler beklemeden çalışır / other work runs here without waiting
19}Dans le code, nous avons écrit oncekiZaman += ARALIK au lieu de oncekiZaman = simdi. La différence est petite mais importante : si loop() arrive parfois avec quelques millisecondes de retard, la première méthode cumule ce retard à chaque fois et la LED dérive au fil des heures, alors que la seconde fait avancer l'heure cible par pas fixes, si bien que la période moyenne reste exactement de 500 ms. Utilise += pour les horloges, les métronomes et les mesures régulières. Si ton programme peut rester bloqué longtemps (par exemple en attendant le Wi-Fi), += essaiera de rattraper les pas manqués à la suite ; dans ce cas, = simdi est plus sûr. Si tu veux aussi changer la luminosité de la LED sans bloquer ton code, jette un œil à l'article sur la PWM et analogWrite.
Plusieurs choses à la fois : le multitâche avec millis()
L'Arduino Uno possède un processeur monocœur et ne peut pas vraiment faire deux choses en même temps. Mais comme loop() peut tourner des dizaines de milliers de fois par seconde, accorder quelques microsecondes à chaque tâche quand vient son tour paraît "simultané" vu de l'extérieur. C'est ce qu'on appelle le multitâche coopératif. La règle est simple : chaque tâche a sa propre variable de temps et son propre intervalle, et aucune tâche n'attend longtemps. Dans l'exemple suivant, quatre tâches tournent côte à côte :
1const int KIRMIZI_LED = 8; // 300 ms'de bir yanıp söner / blinks every 300 ms
2const int YESIL_LED = 9; // 1000 ms'de bir yanıp söner / blinks every 1000 ms
3const int SARI_LED = 10; // butonla açılıp kapanır / toggled by the button
4const int BUTON = 2; // diğer ucu GND / other leg to GND
5
6unsigned long kirmiziZaman = 0, yesilZaman = 0, butonZaman = 0, seriZaman = 0;
7bool kirmizi = false, yesil = false, sari = false;
8int sonOkuma = HIGH; // son ham okuma / last raw reading
9int kararliDurum = HIGH; // titreşimsiz durum / debounced state
10
11void setup() {
12 pinMode(KIRMIZI_LED, OUTPUT);
13 pinMode(YESIL_LED, OUTPUT);
14 pinMode(SARI_LED, OUTPUT);
15 pinMode(BUTON, INPUT_PULLUP);
16 Serial.begin(9600);
17}
18
19void loop() {
20 unsigned long simdi = millis();
21
22 // Görev 1: kırmızı LED / Task 1: red LED
23 if (simdi - kirmiziZaman >= 300) {
24 kirmiziZaman = simdi;
25 kirmizi = !kirmizi;
26 digitalWrite(KIRMIZI_LED, kirmizi);
27 }
28
29 // Görev 2: yeşil LED / Task 2: green LED
30 if (simdi - yesilZaman >= 1000) {
31 yesilZaman = simdi;
32 yesil = !yesil;
33 digitalWrite(YESIL_LED, yesil);
34 }
35
36 // Görev 3: 50 ms debounce ile buton / Task 3: button with 50 ms debounce
37 int okuma = digitalRead(BUTON);
38 if (okuma != sonOkuma) { // değişti, sayacı yeniden başlat / changed, restart the timer
39 butonZaman = simdi;
40 sonOkuma = okuma;
41 }
42 if (simdi - butonZaman >= 50 && okuma != kararliDurum) {
43 kararliDurum = okuma;
44 if (kararliDurum == LOW) { // basıldı / pressed
45 sari = !sari;
46 digitalWrite(SARI_LED, sari);
47 }
48 }
49
50 // Görev 4: saniyede bir seri rapor / Task 4: serial report once per second
51 if (simdi - seriZaman >= 1000) {
52 seriZaman = simdi;
53 Serial.print(F("Calisma suresi (s) / Uptime (s): "));
54 Serial.println(simdi / 1000);
55 }
56}| Tâche | Broche | Intervalle | Ce qu'elle fait |
|---|---|---|---|
| LED rouge | 8 | 300 ms | Clignote vite |
| LED verte | 9 | 1000 ms | Clignote lentement |
| Bouton + LED jaune | 2 et 10 | Anti-rebond de 50 ms | Allume ou éteint la LED jaune à chaque appui |
| Rapport série | - | 1000 ms | Affiche la durée de fonctionnement en secondes |
La logique du bouton repose elle aussi sur millis() : à chaque changement de la lecture brute, le chrono redémarre, et si la lecture reste stable pendant 50 ms, l'appui est considéré comme réel. Ainsi, les rebonds mécaniques du bouton ne transforment pas un appui en dizaines d'appuis, et le code n'attend jamais. Pour savoir comment brancher un bouton et utiliser la résistance de pull-up interne, consulte Comment brancher un bouton sur Arduino. Une résistance de 220 Ω suffit pour les LED ; pour d'autres couleurs et courants, utilise le calculateur de résistance pour LED.
Le débordement de 49,7 jours et les comparaisons sûres
Un unsigned long peut contenir au maximum 4 294 967 295. Quand millis() atteint ce nombre, au bout d'environ 49 jours, 17 heures et 2 minutes, il recommence à compter à partir de 0. Si ta carte reste allumée pendant des semaines (gestion d'une serre, aquarium, système d'alarme), ce moment arrivera forcément. Bonne nouvelle : un code écrit avec une soustraction, sous la forme simdi - onceki >= aralik, n'est pas affecté par le débordement. La soustraction non signée se fait modulo 2 puissance 32. Par exemple, si onceki = 4 294 967 000 et qu'après le débordement simdi = 200, l'opération simdi - onceki ne donne pas un nombre négatif mais le bon résultat, 496. Tant que l'intervalle attendu est plus court que 49,7 jours, la comparaison fonctionne donc toujours.
| Écriture | Résultat | Pourquoi ? |
|---|---|---|
| if (millis() - onceki >= aralik) | Correct | La soustraction non signée donne la bonne différence même à travers le débordement |
| if (millis() >= onceki + aralik) | Faux | Quand onceki + aralik déborde, il devient un petit nombre et la condition se déclenche trop tôt |
| if (millis() == hedef) | Faux | millis() saute parfois de 2, la valeur exacte peut ne jamais apparaître |
| int onceki = millis(); | Faux | Sur l'Uno, un int fait 16 bits et contient des valeurs fausses après 32,7 secondes |
| long onceki = millis(); | Risqué | Un type signé devient négatif au bout de 24,8 jours ; utilise unsigned long pour éviter toute confusion |
Mesurer des microsecondes avec micros()
micros() fonctionne de la même façon mais renvoie les microsecondes écoulées depuis le démarrage. Sur les cartes à 16 MHz comme l'Arduino Uno et la Nano, sa résolution est de 4 µs (8 µs sur les cartes à 8 MHz) et il déborde environ toutes les 71,6 minutes ; la règle de la soustraction s'applique ici aussi. On l'utilise pour des tâches précises : mesurer la durée d'un morceau de code, chronométrer des impulsions courtes ou produire du son. L'exemple ci-dessous mesure deux fois par seconde la durée d'un appel à analogRead() ; sur l'Uno, tu verras environ 110 à 120 µs :
1unsigned long raporZaman = 0;
2
3void setup() {
4 Serial.begin(9600);
5}
6
7void loop() {
8 if (millis() - raporZaman >= 500) { // yarım saniyede bir ölç / measure every half second
9 raporZaman = millis();
10 unsigned long basla = micros(); // başlangıç (µs) / start (µs)
11 int deger = analogRead(A0);
12 unsigned long sure = micros() - basla; // geçen süre (µs) / elapsed time (µs)
13 Serial.print(F("A0 = "));
14 Serial.print(deger);
15 Serial.print(F(" sure / time: "));
16 Serial.print(sure);
17 Serial.println(F(" us"));
18 }
19}Pour les intervalles plus longs, préfère millis() à micros() : 71 minutes avant débordement, c'est trop court pour de longues attentes. delayMicroseconds() est réservé aux attentes courtes et inévitables de quelques microsecondes, comme l'envoi de l'impulsion de déclenchement de 10 µs sur la broche TRIG du capteur de distance HC-SR04. Des attentes aussi brèves ne retardent pas sensiblement tes autres tâches.
Modèles de temporisation : périodique, unique et machine à états
- Tâche périodique : un travail qui se répète à intervalle fixe, comme une LED qui clignote, la lecture d'un capteur toutes les 2 secondes ou le rafraîchissement d'un écran chaque seconde. Tu as déjà vu ce modèle plus haut.
- Minuterie à déclenchement unique (timeout) : elle s'exécute une seule fois, un certain temps après un événement. L'éclairage d'escalier en est un bon exemple : allume la lumière quand il y a du mouvement, enregistre l'heure et éteins-la si aucun nouveau mouvement n'arrive pendant 30 secondes. Remettre l'heure à jour à chaque mouvement prolonge la durée.
- Attente avec délai maximal : quand tu attends une réponse ou un signal, tu fixes une limite pour ne jamais rester bloqué indéfiniment ; par exemple, tu affiches une erreur si aucune donnée n'arrive en 2 secondes.
- Machine à états : tu gardes l'étape actuelle de l'appareil dans une variable et tu passes à la suivante quand le temps de chaque étape est écoulé. Les feux tricolores, les programmes d'un lave-linge ou les niveaux d'un jeu s'écrivent ainsi.
- Détection d'appui long : en mesurant avec millis() combien de temps un bouton reste appuyé, tu peux confier des tâches différentes aux appuis courts et longs.
1// Yeniden kullanılabilir zamanlayıcı / Reusable timer
2struct Zamanlayici {
3 unsigned long baslangic = 0;
4 unsigned long sure = 0;
5
6 void kur(unsigned long ms) { // süreyi şimdiden başlat / start the period from now
7 baslangic = millis();
8 sure = ms;
9 }
10 bool doldu() const { // süre doldu mu? / has it expired?
11 return millis() - baslangic >= sure;
12 }
13};
14
15const int KIRMIZI = 11, SARI = 12, YESIL = 13;
16enum Durum { DUR, HAZIRLAN, GEC, YAVASLA }; // trafik lambası adımları / traffic light states
17Durum durum = DUR;
18Zamanlayici isik;
19
20void lambalar(bool k, bool s, bool y) {
21 digitalWrite(KIRMIZI, k);
22 digitalWrite(SARI, s);
23 digitalWrite(YESIL, y);
24}
25
26void setup() {
27 pinMode(KIRMIZI, OUTPUT);
28 pinMode(SARI, OUTPUT);
29 pinMode(YESIL, OUTPUT);
30 lambalar(true, false, false);
31 isik.kur(5000); // kırmızı 5 sn / red for 5 s
32}
33
34void loop() {
35 if (isik.doldu()) {
36 switch (durum) {
37 case DUR: durum = HAZIRLAN; lambalar(true, true, false); isik.kur(1500); break;
38 case HAZIRLAN: durum = GEC; lambalar(false, false, true); isik.kur(5000); break;
39 case GEC: durum = YAVASLA; lambalar(false, true, false); isik.kur(2000); break;
40 case YAVASLA: durum = DUR; lambalar(true, false, false); isik.kur(5000); break;
41 }
42 }
43 // loop serbest: buton, sensör, ekran... / loop stays free: buttons, sensors, display...
44}Dans ce code, une petite structure (struct) appelée Zamanlayici réduit à une seule ligne le démarrage d'une durée et la question de savoir si elle est écoulée. Le feu tricolore est une machine à états à quatre étapes : à chaque étape, les lampes sont réglées et la durée jusqu'à la transition suivante est lancée. Comme loop() n'attend jamais, tu peux facilement ajouter sur la même carte un bouton piéton, un capteur de lumière LDR pour un mode nuit ou un écran affichant le temps restant. Si tu as besoin de nombreuses minuteries, les bibliothèques de temporisation du gestionnaire de bibliothèques reposent sur le même principe.
millis(), delay() et Ticker sur l'ESP32
Dans le cœur Arduino de l'ESP32, millis() et micros() portent les mêmes noms et renvoient le même type unsigned long de 32 bits ; leurs valeurs sont calculées à partir du compteur 64 bits esp_timer_get_time(). Les débordements de 49,7 jours et de 71,6 minutes s'appliquent donc aussi à l'ESP32, et tu utilises la même règle de soustraction. La différence importante concerne delay() : sur l'ESP32, delay() appelle la fonction FreeRTOS vTaskDelay(). Le processeur ne tourne pas à vide ; pendant l'attente, d'autres tâches comme le Wi-Fi et le Bluetooth continuent de tourner. Ta loop() attend quand même, si bien qu'un serveur web, MQTT ou la lecture des boutons prennent du retard. Pour les tâches périodiques, la bibliothèque Ticker fournie avec le cœur ESP32 est elle aussi pratique :
1#include <Ticker.h>
2
3const int LED1 = 2; // çoğu DevKit'te dahili LED / built-in LED on most DevKits
4const int LED2 = 4;
5
6Ticker ticker;
7bool led1Durum = false;
8bool led2Durum = false;
9unsigned long oncekiZaman = 0;
10
11void led1Degistir() { // kısa tut, uzun iş yapma / keep it short, no long work
12 led1Durum = !led1Durum;
13 digitalWrite(LED1, led1Durum);
14}
15
16void setup() {
17 Serial.begin(115200);
18 pinMode(LED1, OUTPUT);
19 pinMode(LED2, OUTPUT);
20 ticker.attach_ms(250, led1Degistir); // her 250 ms'de çağır / call every 250 ms
21}
22
23void loop() {
24 unsigned long simdi = millis();
25 if (simdi - oncekiZaman >= 1000) { // Arduino ile aynı desen / same pattern as on Arduino
26 oncekiZaman = simdi;
27 led2Durum = !led2Durum;
28 digitalWrite(LED2, led2Durum);
29 int64_t us = esp_timer_get_time(); // 64 bit, pratikte taşmaz / 64-bit, practically never overflows
30 Serial.printf("millis=%lu esp_timer=%lld us\n", simdi, us);
31 }
32}La fonction confiée à Ticker est appelée en arrière-plan par une tâche de minuterie ; n'y fais rien de lent (requête HTTP, longue sortie série, delay), contente-toi de lever un indicateur ou de changer une broche. Le compteur 64 bits esp_timer_get_time() ne débordera pas avant des centaines de milliers d'années : tu peux l'utiliser pour des mesures à la microseconde sur un appareil qui reste allumé des mois. La broche de la LED intégrée peut varier selon la carte ; tu peux consulter le brochage de la tienne dans l'outil Pinout. Si tu débutes avec l'ESP32, l'article Qu'est-ce que l'ESP32 ? est un bon point de départ pour l'installation et le choix des broches.
Raspberry Pi Pico (MicroPython) : ticks_ms() et ticks_diff()
En MicroPython, on utilise time.sleep(), time.sleep_ms() et time.sleep_us() à la place de delay(), et time.ticks_ms() à la place de millis(). Attention toutefois : les valeurs de ticks ne repartent pas à zéro à 2 puissance 32 comme sur Arduino, mais à une limite plus petite qui dépend du portage. Sur le Pico, cette limite est de 2 puissance 30 ms, soit environ 12,4 jours (tu peux la vérifier sur ta carte avec time.ticks_add(0, -1)). Ne soustrais donc jamais et ne compare jamais directement deux valeurs de ticks ; calcule la différence avec time.ticks_diff(nouveau, ancien) et utilise time.ticks_add() pour calculer un instant futur. Le code ci-dessous fait clignoter la LED intégrée du Pico sans bloquer pendant qu'un bouton sur GP14 commande une LED sur GP15 :
1from machine import Pin
2import time
3
4led = Pin("LED", Pin.OUT) # dahili LED (Pico ve Pico W) / onboard LED (Pico and Pico W)
5dis_led = Pin(15, Pin.OUT) # GP15'e harici LED / external LED on GP15
6buton = Pin(14, Pin.IN, Pin.PULL_UP) # GP14 buton, diğer ucu GND / GP14 button, other leg to GND
7
8ARALIK = 500 # ms
9onceki = time.ticks_ms()
10son_okuma = 1 # son ham okuma / last raw reading
11kararli = 1 # titreşimsiz durum / debounced state
12degisim = time.ticks_ms() # son değişim anı / time of the last change
13
14while True:
15 simdi = time.ticks_ms()
16 if time.ticks_diff(simdi, onceki) >= ARALIK: # taşmaya karşı güvenli / wrap-safe
17 onceki = time.ticks_add(onceki, ARALIK) # kaymasız / drift-free
18 led.toggle()
19 okuma = buton.value()
20 if okuma != son_okuma: # değişti, sayacı yeniden başlat / changed, restart the timer
21 degisim = simdi
22 son_okuma = okuma
23 if time.ticks_diff(simdi, degisim) >= 50 and okuma != kararli: # 50 ms debounce
24 kararli = okuma
25 if kararli == 0: # basıldı / pressed
26 dis_led.toggle()
27Pour les tâches périodiques, MicroPython propose aussi machine.Timer : la ligne Timer(period=500, mode=Timer.PERIODIC, callback=fonction) appelle ta fonction toutes les 500 ms. Garde ces fonctions de rappel courtes, comme avec Ticker. Si tu programmes le Pico avec l'Arduino IDE (cœur arduino-pico), millis() et micros() fonctionnent exactement comme sur Arduino. Pour installer MicroPython sur le Pico et découvrir ses broches, consulte l'article Qu'est-ce que le Raspberry Pi Pico ?.
Idées de projets avec millis()
- Test de réflexes : allume une LED à un moment aléatoire, mesure avec millis() le temps jusqu'à l'appui sur le bouton et affiche le résultat en millisecondes.
- Éclairage d'escalier : allume la lumière quand un capteur PIR détecte un mouvement et éteins-la 30 secondes après le dernier mouvement, pendant que le code continue ses autres tâches.
- Radar de recul qui accélère : raccourcis l'intervalle des bips du buzzer à mesure que la distance mesurée par le HC-SR04 diminue.
- Mélodie non bloquante : enchaîne les notes d'un buzzer avec millis() au lieu de delay(), pour que les boutons restent actifs pendant la mélodie.
- Capteur et écran ensemble : lis le capteur DHT11 toutes les 2 secondes, mais rafraîchis l'écran toutes les 250 ms.
- Chronomètre : fabrique un chronomètre avec des boutons démarrer, arrêter et remettre à zéro, qui affiche le temps sur un afficheur 7 segments.
Erreurs fréquentes et solutions
| Erreur | Symptôme | Solution |
|---|---|---|
| Variable de temps déclarée en int | Le programme dérape au bout d'environ 33 secondes | Déclare toutes les variables de temps en unsigned long |
| Écrire millis() >= onceki + aralik | La temporisation se dérègle au bout de 49,7 jours | Passe à la forme millis() - onceki >= aralik |
| Tester millis() == hedef | L'événement ne se déclenche parfois jamais | Utilise >= au lieu de l'égalité |
| Oublier de mettre à jour onceki | Une fois le délai écoulé, la tâche se répète à chaque passage | Écris onceki = simdi ou onceki += aralik dans la condition |
| Un delay() oublié dans le code de temporisation | Les autres tâches restent retardées | Découpe les longues attentes en machine à états |
| Attendre avec millis() dans une interruption | La carte se fige | N'attends jamais dans une ISR, lève seulement un indicateur |
| Produit d'int comme 60 * 1000 | L'intervalle devient négatif ou beaucoup trop court | Écris 60000UL ou 60UL * 1000 |
Une fois que tu maîtrises la logique de millis(), tes projets changent complètement : LED, boutons, capteurs et écrans fonctionnent ensemble sans s'attendre les uns les autres. Pour la suite, tu peux choisir un projet qui demande de la temporisation dans la liste d'idées de projets Arduino. Dans l'application BlueGrays, tu peux suivre pas à pas des projets comme le Feu tricolore, le Gyrophare de police, l'Éclairage intelligent d'escalier et de couloir ou le Test de réflexes avec alerte sonore, avec le schéma du circuit et le code prêt à l'emploi, puis les essayer sur ta propre carte.
Questions fréquentes
À quoi sert millis() sur Arduino ?
millis() est une fonction qui renvoie le temps écoulé depuis la mise sous tension ou le reset de l'Arduino, en millisecondes. Sa valeur est de type unsigned long et repart de zéro au bout d'environ 49,7 jours. On l'utilise pour temporiser sans arrêter le programme.
Quelle est la différence entre millis() et delay() ?
delay() met le programme en pause pendant le temps indiqué : rien d'autre ne peut se passer pendant ce temps, comme lire des boutons ou mettre à jour un écran. millis() se contente d'indiquer le temps écoulé et n'attend jamais. En vérifiant le temps écoulé et en exécutant la tâche qui est due, tu peux mener plusieurs tâches de front.
Mon programme plante-t-il quand millis() déborde au bout de 49 jours ?
Non, si tu vérifies le temps avec une soustraction de la forme millis() - onceki >= aralik ; la soustraction non signée donne la bonne différence même à travers le débordement. Les problèmes n'apparaissent qu'avec des comparaisons écrites avec une addition, comme millis() >= onceki + aralik, et avec des variables int.
Peut-on remettre millis() à zéro sur Arduino ?
Il n'existe pas de fonction officielle pour remettre millis() à zéro, et modifier la variable interne du cœur n'est pas recommandé. Ce n'est d'ailleurs pas nécessaire : enregistre dans une variable l'instant de départ voulu et calcule le temps écoulé à partir de là. Un reset de la carte fait de toute façon repartir millis() de 0.
Un Arduino peut-il faire deux choses en même temps ?
Comme l'Uno n'a qu'un seul cœur, il ne peut pas exécuter physiquement deux instructions en même temps, mais si tu découpes chaque tâche en petites étapes avec millis(), loop() tourne des milliers de fois par seconde et donne son tour à chacune. Vu de l'extérieur, la LED, le bouton, le capteur et l'écran semblent fonctionner simultanément. L'ESP32, lui, dispose en plus de deux cœurs et de tâches FreeRTOS.
millis() est-il assez précis pour faire une horloge ?
millis() dépend de l'oscillateur de la carte ; à cause du résonateur céramique de l'Arduino Uno, la dérive peut atteindre quelques minutes par jour. C'est suffisant pour mesurer de courts intervalles, mais pour une horloge qui doit rester juste pendant des jours, un module RTC comme le DS1307 ou le DS3231, ou l'heure récupérée par NTP sur un ESP32, est plus précis.
Projets à réaliser après cet article
Voir tous les projets
Clignotement d'une LED
Projet d'entrée basique qui allume et éteint une seule LED à certains intervalles avec Arduino. Enseigne l'utilisation des fonctions digitalWrite et delay.

Feu de circulation
Projet qui simule un vrai feu de circulation avec des DEL rouges, jaunes et vertes. Enseigne la synchronisation et la logique de contrôle séquentielle.

Gyrophare de police à LED
Projet qui crée un effet de lumière stroboscopique de police en faisant clignoter deux LED à intervalles rapides. Enseigne le contrôle de la boucle et de la synchronisation.

Chenillard à LED à vitesse réglable
Le projet qui permet d'ajuster la vitesse du chenillard à LED en temps réel avec le potentiomètre. Il enseigne le contrôle dynamique du temps de retard en lisant la valeur du potentiomètre avec l'anal

Allumer et éteindre une LED avec un bouton
Un projet de bascule qui change l'état de la LED (on/off) à chaque pression sur un bouton. Enseigne les variables d'état et la logique de rebond.

Éclairage intelligent des escaliers et des couloirs
Un projet d'automatisation élégant avec un détecteur de distance bidirectionnel HC-SR04 qui s'allume et s'éteint alternativement dans le sens du mouvement.

Test de réaction avec signal sonore Arduino
Station de test de réaction Arduino Ce projet est une installation de test numérique développée pour mesurer la vitesse des réflexes d'un utilisateur face à des stimuli visuels et auditifs avec une précision de l'ordre de la milliseconde (ms). Le système se compose d'un contrôleur Arduino, d'un groupe de notification LED à trois niveaux, d'un buzzer pour l'avertissement sonore et d'un écran LCD I2C qui affiche les résultats instantanément. Principe de fonctionnement : Lorsque le test commence,

Horloge numérique avec afficheur 7 segments à 4 chiffres
Projet d'horloge numérique affichant les heures et les minutes avec le module RTC DS1302 et l'afficheur 7 segments à 4 chiffres. Il combine les données de l'horloge en temps réel et l'affichage des se
Autres articles
- Brancher un bouton-poussoir sur Arduino : pull-up, pull-down et anti-rebond
- Qu'est-ce que le PWM ? Guide d'analogWrite, du rapport cyclique et de la fréquence sur Arduino
- Capteur de distance HC-SR04 : mesurer une distance par ultrasons avec Arduino
- Idées de projets Arduino : 30 projets du débutant à l'avancé
Découvre pourquoi un bouton réagit en retard avec delay(), comment gérer LED, boutons et capteurs en même temps avec millis(), comment passer sans souci le débordement de 49,7 jours et quels sont les équivalents sur ESP32 et Pico.

