Guides · 15 min de lecture

Feu tricolore Arduino : circuit, code, bouton piéton et carrefour

Réaliser un feu tricolore Arduino : circuit 3 LED et 220 Ω, code avec delay et millis, bouton piéton, carrefour à deux voies, Tinkercad, ESP32 et Pico.

Le feu tricolore Arduino est le grand classique des débutants en électronique : trois LED, rouge, jaune et verte, s'allument et s'éteignent dans l'ordre comme un vrai feu de signalisation. Il te suffit d'une Arduino Uno, de trois LED, de trois résistances de 220 Ω et de quelques fils de connexion, et la première version fonctionne en une demi-heure. Ce petit projet t'apprend pourtant beaucoup : les sorties numériques, le calcul de la résistance d'une LED, la temporisation, la lecture d'un bouton poussoir et la machine à états, la méthode la plus propre pour organiser ton code.

Dans ce guide, tu verras d'abord dans quel ordre et pendant combien de temps un vrai feu change de couleur, puis tu monteras le circuit et écriras le code le plus simple avec delay(). Ensuite, tu ajouteras un bouton d'appel piéton, tu réécriras la même logique avec millis() et une machine à états pour qu'elle ne bloque jamais, et tu piloteras un carrefour à deux voies. Les dernières sections montrent comment tester le projet dans Tinkercad sans rien installer, les versions ESP32 et Raspberry Pi Pico (MicroPython) et des conseils pour rédiger le compte rendu de ton projet scolaire.

LED rouges, jaunes et vertes de 5 mm pour un feu tricolore Arduino
LED de 5 mm : une rouge, une jaune et une verte suffisent pour le feu ; garde-en quelques-unes de plus pour le feu piéton et le carrefour.

Comment fonctionne un vrai feu tricolore ?

Un feu tricolore est une minuterie qui donne la priorité à chaque flux de circulation à tour de rôle. En Allemagne, au Royaume-Uni et en Turquie, le cycle est rouge → rouge + orange → vert → orange → rouge. Le rouge et l'orange allumés ensemble signifient "le vert arrive, prépare-toi, mais ne démarre pas encore", tandis que l'orange seul signifie "le feu va passer au rouge, arrête-toi". En France, comme aux États-Unis, en Italie et en Espagne, cette phase rouge + orange n'existe pas : le feu passe directement du rouge au vert. La durée de l'orange n'est pas choisie au hasard non plus : en Allemagne, la règle prévoit 3 secondes d'orange sur une route limitée à 50 km/h, 4 secondes à 60 km/h et 5 secondes à 70 km/h, car un conducteur trop proche pour s'arrêter au moment où l'orange s'allume doit pouvoir dégager le carrefour en sécurité.

PhaseFeu alluméSignificationDurée dans ce guide
1RougeArrêt5 s
2Rouge + orangeLe vert arrive, prépare-toi1 s
3VertPasse5 s
4OrangeLe rouge arrive, arrête-toi3 s
Au carrefourToutes les voies au rougeMarge de sécurité pour dégager le carrefour1 s

Matériel et calcul de la résistance de 220 Ω

ComposantQtéRemarque
Arduino Uno (ou Nano)1Une ESP32 ou une Raspberry Pi Pico conviennent aussi
LED 5 mm : rouge, jaune, verte1 de chaque+1 rouge et +1 verte pour le feu piéton, +1 jeu pour le carrefour
Résistance 220 Ω3Une par LED ; 8 pour toutes les versions
Bouton poussoir (6×6 mm)1Uniquement pour la version piéton
Breadboard (platine d'essai)1Une demi-taille (400 points) suffit
Fils de connexion (jumpers)10–20Mâle-mâle
Câble USB1Pour téléverser le code et alimenter la carte

Une LED ne limite pas elle-même son courant : branchée directement sur 5 V, elle consomme plus que ce qu'elle et la broche peuvent supporter. C'est pourquoi chaque LED reçoit une résistance en série. Sa valeur se calcule avec la loi d'Ohm : R = (tension d'alimentation − tension directe de la LED) / courant. Pour une LED rouge, (5 V − 2,0 V) / 0,015 A ≈ 200 Ω ; avec la valeur normalisée la plus proche, 220 Ω, le courant est d'environ 13,6 mA. La LED éclaire suffisamment et on reste sous les 20 mA recommandés pour une broche Arduino. Une résistance de 220 Ω porte les couleurs rouge-rouge-marron-or (quatre anneaux) ou rouge-rouge-noir-noir-marron (cinq anneaux) ; pour vérifier, utilise l'outil code couleur des résistances, et pour d'autres couleurs ou tensions le calculateur de résistance pour LED.

Couleur de la LEDTension directe (env.)Courant sous 5 V (220 Ω)Courant sous 3,3 V (220 Ω)
Rouge1,8–2,2 V≈ 13,6 mA≈ 5,9 mA
Jaune1,9–2,2 V≈ 13,2 mA≈ 5,5 mA
Verte (vert jaunâtre)2,0–2,4 V≈ 13,2 mA≈ 5,5 mA
Verte (vert pur, très lumineuse)2,8–3,4 V≈ 9,1 mA≈ 1,4 mA (très faible)

Câblage du circuit et tableau des broches

La patte longue de chaque LED (anode, +) va vers une broche numérique de l'Arduino à travers une résistance, et la patte courte (cathode, −) va au GND. Peu importe que la résistance soit côté anode ou côté cathode, du moment qu'elle est en série. Sur la breadboard, le plus propre est de relier la broche GND de l'Arduino à la ligne bleue (−) et d'y ramener les pattes courtes des trois LED. Le tableau ci-dessous donne les broches utilisées par tous les programmes de ce guide ; le premier n'utilise que les trois premières lignes. Tu peux voir le brochage de ta carte dans l'outil Pinout.

FonctionArduino UnoESP32Raspberry Pi Pico
Rouge véhicules (voie A)D10GPIO 5GP13
Orange véhicules (voie A)D9GPIO 18GP12
Vert véhicules (voie A)D8GPIO 19GP11
Rouge piétonsD7GPIO 21GP15
Vert piétonsD6GPIO 22GP14
Bouton piéton (autre patte au GND)D2GPIO 23GP4
Voie B rouge / orange / vertD5 / D4 / D3GPIO 25 / 26 / 27GP8 / GP7 / GP6
Cathodes des LED et boutonGNDGNDGND
Une LED montée à l'envers reste simplement éteinte sans être abîmée, mais sans résistance la LED et la broche sont surchargées et la LED peut vite perdre en luminosité. Sur l'Arduino Uno, vise 20 mA maximum par broche (maximum absolu 40 mA) et garde le total de toutes les broches sous 200 mA. Si aucune LED ne s'allume, vérifie d'abord le GND : si la ligne bleue de la breadboard n'est pas reliée à une broche GND de l'Arduino, le circuit n'est pas fermé.

Étape 1 : le code de feu tricolore le plus simple avec delay()

Arduino (C++)
1// Arduino trafik lambası: delay() ile en basit sürüm / simplest version with delay()
2const int KIRMIZI = 10;  // kırmızı LED / red LED
3const int SARI = 9;      // sarı LED / yellow LED
4const int YESIL = 8;     // yeşil LED / green LED
5
6// Üç LED'i tek satırda ayarla / set all three LEDs in one line
7void lambalar(int k, int s, int y) {
8  digitalWrite(KIRMIZI, k);
9  digitalWrite(SARI, s);
10  digitalWrite(YESIL, y);
11}
12
13void setup() {
14  pinMode(KIRMIZI, OUTPUT);
15  pinMode(SARI, OUTPUT);
16  pinMode(YESIL, OUTPUT);
17}
18
19void loop() {
20  lambalar(HIGH, LOW, LOW);   // DUR: kırmızı / STOP: red
21  delay(5000);
22  lambalar(HIGH, HIGH, LOW);  // HAZIRLAN: kırmızı + sarı / GET READY: red + yellow
23  delay(1000);
24  lambalar(LOW, LOW, HIGH);   // GEÇ: yeşil / GO: green
25  delay(5000);
26  lambalar(LOW, HIGH, LOW);   // YAVAŞLA: sarı / SLOW DOWN: yellow
27  delay(3000);
28}

Le programme configure les trois broches en sortie dans setup() et enchaîne les quatre phases dans loop(). Au lieu d'écrire sans cesse trois lignes digitalWrite, on utilise une petite fonction appelée lambalar() ("feux") : chaque phase tient sur une ligne et une erreur saute aux yeux. Pour téléverser, choisis la carte Arduino Uno et le bon port dans le menu Outils, puis clique sur Téléverser. En cas d'échec, suis la liste de vérification de Arduino : le téléversement ne fonctionne pas. Pour changer les durées, modifie simplement les millisecondes dans delay() : 5000 correspond à 5 secondes.

Étape 2 : feu tricolore avec bouton d'appel piéton

Les feux à bouton d'appel que tu croises en ville laissent le vert aux voitures et ne donnent la main aux piétons que lorsque quelqu'un appuie sur le bouton. Dans cette version, on ajoute au feu des véhicules un feu piéton à deux LED (rouge et verte) et un bouton sur D2. Une patte du bouton va sur D2, la patte opposée en diagonale au GND ; comme le code utilise INPUT_PULLUP, aucune résistance externe n'est nécessaire et la broche lit LOW quand on appuie. Les connexions internes d'un bouton à quatre pattes et l'anti-rebond sont détaillés dans Brancher un bouton poussoir sur Arduino.

Bouton poussoir 6×6 mm à quatre pattes
Bouton poussoir 6×6 mm : les quatre pattes sont reliées deux à deux à l'intérieur ; en utilisant deux pattes opposées en diagonale, le câblage est toujours bon.
Arduino (C++)
1// Yaya butonlu trafik lambası (delay ile) / pedestrian push-button light (with delay)
2const int KIRMIZI = 10, SARI = 9, YESIL = 8;  // araç lambası / vehicle light
3const int YAYA_KIRMIZI = 7, YAYA_YESIL = 6;   // yaya lambası / pedestrian light
4const int BUTON = 2;                          // diğer ucu GND / other leg to GND
5
6void setup() {
7  pinMode(KIRMIZI, OUTPUT);
8  pinMode(SARI, OUTPUT);
9  pinMode(YESIL, OUTPUT);
10  pinMode(YAYA_KIRMIZI, OUTPUT);
11  pinMode(YAYA_YESIL, OUTPUT);
12  pinMode(BUTON, INPUT_PULLUP);               // basılınca LOW okunur / reads LOW when pressed
13  digitalWrite(YESIL, HIGH);                  // araçlar geçer / cars go
14  digitalWrite(YAYA_KIRMIZI, HIGH);           // yayalar bekler / pedestrians wait
15}
16
17void loop() {
18  if (digitalRead(BUTON) == LOW) {            // yaya butona bastı / pedestrian pressed
19    delay(2000);                              // araçlara kısa süre tanı / short grace time for cars
20    digitalWrite(YESIL, LOW);
21    digitalWrite(SARI, HIGH);                 // sarı / yellow
22    delay(3000);
23    digitalWrite(SARI, LOW);
24    digitalWrite(KIRMIZI, HIGH);              // araçlar durur / cars stop
25    delay(1000);                              // tüm kırmızı güvenlik payı / all-red safety gap
26    digitalWrite(YAYA_KIRMIZI, LOW);
27    digitalWrite(YAYA_YESIL, HIGH);           // yayalar geçer / pedestrians cross
28    delay(6000);
29    for (int i = 0; i < 6; i++) {             // yaya yeşili 3 kez yanıp söner / green man flashes 3 times
30      digitalWrite(YAYA_YESIL, i % 2 == 0 ? LOW : HIGH);
31      delay(500);
32    }
33    digitalWrite(YAYA_YESIL, LOW);
34    digitalWrite(YAYA_KIRMIZI, HIGH);
35    delay(1000);                              // tüm kırmızı / all red
36    digitalWrite(SARI, HIGH);                 // kırmızı + sarı / red + yellow
37    delay(1000);
38    digitalWrite(KIRMIZI, LOW);
39    digitalWrite(SARI, LOW);
40    digitalWrite(YESIL, HIGH);                // araçlar yeniden geçer / cars go again
41    delay(8000);                              // en kısa yeşil süresi / minimum green time
42  }
43}

Ce programme fonctionne, mais il a un gros défaut : après un appui sur le bouton, l'Arduino reste bloqué environ 25 secondes dans des lignes delay(). Un nouvel appui pendant ce temps passe inaperçu, rien ne peut être lu sur le port série et aucun écran ne peut être mis à jour. Les appuis pendant l'attente de 8 secondes à la fin sont complètement perdus : le piéton appuie, relâche, rien ne se passe et il doit appuyer de nouveau. Ajouter un son ou un compte à rebours devient encore plus compliqué avec cette structure. La solution : arrêter d'attendre et mesurer le temps.

Étape 3 : sans blocage avec millis() et une machine à états

Une machine à états est une conception dans laquelle le système se trouve toujours dans un seul état et passe au suivant quand certaines conditions sont remplies. Un feu tricolore en est l'exemple parfait : "vert pour les voitures", "orange pour les voitures" et "les piétons traversent" sont chacun un état. Le code n'attend plus avec delay() ; à chaque passage, loop() se pose seulement deux questions : dans quel état suis-je, et depuis combien de temps ? Le temps écoulé se mesure avec millis() - durumBaslangic (l'instant où l'état a commencé) et, quand la durée est atteinte, on passe à l'état suivant. Si millis() est nouveau pour toi, lis d'abord Arduino millis() au lieu de delay(). Mettre les états dans un tableau avant de coder simplifie beaucoup le travail :

ÉtatFeu véhiculesFeu piétonsDuréeÉtat suivant
ARAC_YESILVertRougeAu moins 8 s, jusqu'à une demandeARAC_SARI
ARAC_SARIOrangeRouge3 sYAYA_BEKLE
YAYA_BEKLERougeRouge1 sYAYA_GECER
YAYA_GECERRougeVert6 sYAYA_FLAS
YAYA_FLASRougeVert clignotant3 sYAYA_BITTI
YAYA_BITTIRougeRouge1 sKIRMIZI_SARI
KIRMIZI_SARIRouge + orangeRouge1 sARAC_YESIL
Arduino (C++)
1// Yaya butonlu trafik lambası: millis() + durum makinesi / pedestrian light: millis() + state machine
2const int KIRMIZI = 10, SARI = 9, YESIL = 8;  // araç lambası / vehicle light
3const int YAYA_KIRMIZI = 7, YAYA_YESIL = 6;   // yaya lambası / pedestrian light
4const int BUTON = 2;                          // diğer ucu GND / other leg to GND
5
6enum Durum { ARAC_YESIL, ARAC_SARI, YAYA_BEKLE, YAYA_GECER, YAYA_FLAS, YAYA_BITTI, KIRMIZI_SARI };
7const char* const ISIM[] = { "ARAC_YESIL", "ARAC_SARI", "YAYA_BEKLE", "YAYA_GECER",
8                             "YAYA_FLAS", "YAYA_BITTI", "KIRMIZI_SARI" };
9
10// Süreler (ms) / durations (ms)
11const unsigned long MIN_YESIL = 8000;   // araçlara en az yeşil / minimum green for cars
12const unsigned long SARI_SURE = 3000;   // sarı / yellow
13const unsigned long GUVENLIK = 1000;    // tüm kırmızı / all red
14const unsigned long YAYA_SURE = 6000;   // yaya yeşili / pedestrian green
15const unsigned long FLAS_SURE = 3000;   // yanıp sönen yaya yeşili / flashing green man
16const unsigned long KS_SURE = 1000;     // kırmızı + sarı / red + yellow
17
18Durum durum = ARAC_YESIL;
19unsigned long durumBaslangic = 0;       // durumun başladığı an / when the state started
20bool yayaTalebi = false;                // butona basıldı mı? / was the button pressed?
21
22void aracLamba(bool k, bool s, bool y) {
23  digitalWrite(KIRMIZI, k);
24  digitalWrite(SARI, s);
25  digitalWrite(YESIL, y);
26}
27
28void yayaLamba(bool k, bool y) {
29  digitalWrite(YAYA_KIRMIZI, k);
30  digitalWrite(YAYA_YESIL, y);
31}
32
33// Yeni duruma geç: lambaları ayarla, saati başlat / enter a state: set the lamps, restart the clock
34void gec(Durum yeni) {
35  durum = yeni;
36  durumBaslangic = millis();
37  switch (yeni) {
38    case ARAC_YESIL:   aracLamba(0, 0, 1); yayaLamba(1, 0); break;
39    case ARAC_SARI:    aracLamba(0, 1, 0); yayaLamba(1, 0); break;
40    case YAYA_BEKLE:   aracLamba(1, 0, 0); yayaLamba(1, 0); break;
41    case YAYA_GECER:   aracLamba(1, 0, 0); yayaLamba(0, 1); yayaTalebi = false; break;
42    case YAYA_FLAS:    aracLamba(1, 0, 0); yayaLamba(0, 1); break;
43    case YAYA_BITTI:   aracLamba(1, 0, 0); yayaLamba(1, 0); break;
44    case KIRMIZI_SARI: aracLamba(1, 1, 0); yayaLamba(1, 0); break;
45  }
46  Serial.println(ISIM[yeni]);           // durum günlüğü / state log
47}
48
49void setup() {
50  Serial.begin(9600);
51  pinMode(KIRMIZI, OUTPUT);
52  pinMode(SARI, OUTPUT);
53  pinMode(YESIL, OUTPUT);
54  pinMode(YAYA_KIRMIZI, OUTPUT);
55  pinMode(YAYA_YESIL, OUTPUT);
56  pinMode(BUTON, INPUT_PULLUP);
57  gec(ARAC_YESIL);
58}
59
60void loop() {
61  unsigned long gecen = millis() - durumBaslangic;  // bu durumda geçen süre / time spent in this state
62
63  // yaya yeşili yanarken basış yok sayılır / presses during the green man are ignored
64  if (digitalRead(BUTON) == LOW && durum != YAYA_GECER) {
65    yayaTalebi = true;                  // talebi hatırla / remember the request
66  }
67
68  switch (durum) {
69    case ARAC_YESIL:
70      if (yayaTalebi && gecen >= MIN_YESIL) gec(ARAC_SARI);
71      break;
72    case ARAC_SARI:
73      if (gecen >= SARI_SURE) gec(YAYA_BEKLE);
74      break;
75    case YAYA_BEKLE:
76      if (gecen >= GUVENLIK) gec(YAYA_GECER);
77      break;
78    case YAYA_GECER:
79      if (gecen >= YAYA_SURE) gec(YAYA_FLAS);
80      break;
81    case YAYA_FLAS:
82      digitalWrite(YAYA_YESIL, (gecen / 250) % 2 == 0);  // 250 ms'de bir yanıp söner / toggles every 250 ms
83      if (gecen >= FLAS_SURE) gec(YAYA_BITTI);
84      break;
85    case YAYA_BITTI:
86      if (gecen >= GUVENLIK) gec(KIRMIZI_SARI);
87      break;
88    case KIRMIZI_SARI:
89      if (gecen >= KS_SURE) gec(ARAC_YESIL);
90      break;
91  }
92}

Le cœur du programme est la fonction gec() ("passer à") : elle enregistre le nouvel état, relance le chronomètre, règle les feux pour cet état et affiche son nom dans le moniteur série. Le switch de loop() vérifie seulement si la durée est écoulée. Un appui sur le bouton ne déclenche rien tout de suite : il lève le drapeau yayaTalebi (demande piéton) et le cycle démarre dès que les voitures ont eu au moins 8 secondes de vert. Plus aucun appui n'est perdu, et comme le bouton ne fait que modifier un drapeau, les rebonds mécaniques ne posent aucun problème. Les appuis pendant que le vert piéton est déjà allumé sont ignorés ; sinon, un nouveau cycle inutile démarrerait juste après la traversée des piétons. Ouvre le moniteur série à 9600 bauds pour voir le nom de chaque transition : cette sortie aide au débogage comme au compte rendu.

Pour ajouter une fonction, ajoute d'abord une ligne au tableau, puis écris un case dans le code. Tu peux par exemple créer un état de nuit avec l'orange clignotant et y passer quand un capteur de lumière LDR détecte l'obscurité. Pour approfondir les machines à états, consulte Machine à états avec Arduino.

Étape 4 : un carrefour à deux voies

Dans un vrai carrefour, les feux des deux rues sont liés : quand la voie A est au vert, la voie B doit être au rouge. Cette version utilise six LED : D10, D9 et D8 pour la voie A, D5, D4 et D3 pour la voie B. Un détail important est le court intervalle de rouge intégral : quand une voie passe au rouge, l'autre ne passe pas tout de suite au vert ; le contrôleur attend 1 seconde pour que les véhicules encore dans le carrefour puissent le dégager. Cette fois, les états sont rangés dans un tableau (array) plutôt que dans un switch : pour chaque étape, les feux allumés sont stockés sous forme de bits dans un seul octet et la durée en millisecondes.

Arduino (C++)
1// İki yönlü kavşak: tablo tabanlı durum makinesi / two-way junction: table-driven state machine
2const byte A_KIRMIZI = 10, A_SARI = 9, A_YESIL = 8;  // A yönü (ana yol) / direction A (main road)
3const byte B_KIRMIZI = 5,  B_SARI = 4, B_YESIL = 3;  // B yönü (ara yol) / direction B (side road)
4const byte PINLER[6] = { A_KIRMIZI, A_SARI, A_YESIL, B_KIRMIZI, B_SARI, B_YESIL };
5
6struct Adim {
7  byte maske;            // bit sırası: A_K A_S A_Y B_K B_S B_Y / bit order: A_R A_Y A_G B_R B_Y B_G
8  unsigned long sure;    // ms
9};
10
11const Adim PLAN[] = {
12  { 0b001100, 8000 },   // A yeşil, B kırmızı / A green, B red
13  { 0b010100, 3000 },   // A sarı, B kırmızı / A yellow, B red
14  { 0b100100, 1000 },   // tüm yönler kırmızı / all red
15  { 0b100110, 1000 },   // A kırmızı, B kırmızı + sarı / A red, B red + yellow
16  { 0b100001, 5000 },   // A kırmızı, B yeşil / A red, B green
17  { 0b100010, 3000 },   // A kırmızı, B sarı / A red, B yellow
18  { 0b100100, 1000 },   // tüm yönler kırmızı / all red
19  { 0b110100, 1000 },   // A kırmızı + sarı, B kırmızı / A red + yellow, B red
20};
21const byte ADIM_SAYISI = sizeof(PLAN) / sizeof(PLAN[0]);
22
23byte adim = 0;
24unsigned long adimBaslangic = 0;
25
26void uygula(byte maske) {
27  if ((maske & 0b001000) && (maske & 0b000001)) {  // iki yeşil aynı anda yanamaz / never two greens
28    maske = 0b100100;                              // güvenli durum: tüm kırmızı / fail-safe: all red
29  }
30  for (byte i = 0; i < 6; i++) {
31    digitalWrite(PINLER[i], (maske >> (5 - i)) & 1);  // soldaki bit A_KIRMIZI / leftmost bit is A_RED
32  }
33}
34
35void setup() {
36  for (byte i = 0; i < 6; i++) pinMode(PINLER[i], OUTPUT);
37  uygula(PLAN[0].maske);
38  adimBaslangic = millis();
39}
40
41void loop() {
42  if (millis() - adimBaslangic >= PLAN[adim].sure) {
43    adimBaslangic += PLAN[adim].sure;   // kaymasız zamanlama / drift-free timing
44    adim = (adim + 1) % ADIM_SAYISI;
45    uygula(PLAN[adim].maske);
46  }
47  // Burada buton, sensör ya da ekran işleri çalışabilir / buttons, sensors or a display can run here
48}

Chaque ligne du tableau PLAN est une étape. Dans les nombres binaires comme 0b001100, les bits de gauche à droite représentent A rouge, A orange, A vert, B rouge, B orange et B vert ; 1 signifie allumé et 0 éteint. La fonction uygula() ("appliquer") écrit ces six bits sur les broches dans l'ordre. Le test au début de la fonction empêche qu'une faute de frappe allume le vert dans les deux voies en même temps et met tout au rouge à la place. Les vrais contrôleurs de carrefour disposent eux aussi d'un circuit de sécurité séparé qui détecte les verts incompatibles. Pour modifier les durées ou ajouter des étapes, tu n'édites que le tableau, sans toucher au reste du code. Si les broches viennent à manquer avec plus de LED, un registre à décalage 74HC595 fournit huit sorties avec trois broches.

Tester dans Tinkercad : simulation sans aucun composant

  1. Crée un compte gratuit sur tinkercad.com et lance un nouveau circuit dans la section Circuits.
  2. Fais glisser depuis la liste des composants à droite une Arduino Uno R3, une breadboard, trois LED et trois résistances. Clique sur une LED pour choisir sa couleur et sur une résistance pour régler 220 Ω.
  3. Trace les fils : anodes des LED vers D10, D9 et D8 à travers les résistances, cathodes au GND. Choisir la couleur des fils (noir pour le GND, par exemple) rend le schéma lisible.
  4. Clique sur Code, choisis Text (Texte) dans le menu déroulant, supprime le code d'exemple et colle le programme de ce guide.
  5. Clique sur Start Simulation (Démarrer la simulation) ; les LED s'allument dans l'ordre. La sortie série s'affiche dans le Serial Monitor en bas du panneau de code.

Tinkercad est le moyen le plus simple de tester le câblage et le code avant de passer aux vrais composants ; si tu oublies une résistance, le simulateur affiche sur la LED un avertissement indiquant que le courant est trop élevé. Pour tester la version piéton, clique simplement sur le bouton avec la souris pendant la simulation. Sur un ordinateur lent, la simulation peut tourner moins vite que le temps réel, et les durées paraissent alors un peu longues. Pour un devoir, tu peux rendre le lien de partage Tinkercad ou une capture d'écran du circuit. Des instructions détaillées se trouvent dans Simulation Arduino avec Tinkercad, et une alternative qui gère aussi l'ESP32 et la Pico dans Wokwi.

Feu tricolore ESP32 avec mode nuit

Les programmes Arduino de ce guide fonctionnent aussi sur l'ESP32 : choisis la carte ESP32 Dev Module dans l'IDE Arduino et modifie les constantes de broches comme dans le tableau. La version ci-dessous ajoute une fonction : le mode nuit. Aux heures creuses, beaucoup de carrefours passent à l'orange clignotant. Les cartes ESP32 DevKit ont un bouton BOOT relié à GPIO 0, tu n'as donc même pas besoin d'un bouton supplémentaire : une fois la carte démarrée, il se lit comme une entrée normale. Chaque appui bascule entre le mode normal et le mode nuit.

Arduino (C++)
1// ESP32 trafik lambası + gece modu (BOOT düğmesi) / ESP32 traffic light + night mode (BOOT button)
2const int KIRMIZI = 5;      // GPIO 5
3const int SARI = 18;        // GPIO 18
4const int YESIL = 19;       // GPIO 19
5const int BOOT_BUTON = 0;   // kart üzerindeki BOOT düğmesi / on-board BOOT button
6
7struct Adim { bool k, s, y; unsigned long sure; };
8const Adim PLAN[] = {
9  { true,  false, false, 5000 },   // kırmızı / red
10  { true,  true,  false, 1000 },   // kırmızı + sarı / red + yellow
11  { false, false, true,  5000 },   // yeşil / green
12  { false, true,  false, 3000 },   // sarı / yellow
13};
14const int ADIM_SAYISI = sizeof(PLAN) / sizeof(PLAN[0]);
15
16int adim = 0;
17unsigned long adimBaslangic = 0;
18bool geceModu = false;
19int sonOkuma = HIGH, kararli = HIGH;     // debounce için / for debouncing
20unsigned long degisimZamani = 0;
21
22void lambalar(bool k, bool s, bool y) {
23  digitalWrite(KIRMIZI, k);
24  digitalWrite(SARI, s);
25  digitalWrite(YESIL, y);
26}
27
28void setup() {
29  Serial.begin(115200);
30  pinMode(KIRMIZI, OUTPUT);
31  pinMode(SARI, OUTPUT);
32  pinMode(YESIL, OUTPUT);
33  pinMode(BOOT_BUTON, INPUT_PULLUP);
34  lambalar(PLAN[0].k, PLAN[0].s, PLAN[0].y);
35  adimBaslangic = millis();
36}
37
38void loop() {
39  unsigned long simdi = millis();
40
41  // 50 ms kararlı kalan basışı kabul et / accept a press that stays stable for 50 ms
42  int okuma = digitalRead(BOOT_BUTON);
43  if (okuma != sonOkuma) {
44    sonOkuma = okuma;
45    degisimZamani = simdi;
46  }
47  if (simdi - degisimZamani >= 50 && okuma != kararli) {
48    kararli = okuma;
49    if (kararli == LOW) {                  // yeni basış / new press
50      geceModu = !geceModu;
51      Serial.println(geceModu ? "Gece modu / Night mode" : "Normal mod / Normal mode");
52      adim = 0;                            // normal mod kırmızıdan başlar / normal mode restarts on red
53      adimBaslangic = simdi;
54      lambalar(PLAN[0].k, PLAN[0].s, PLAN[0].y);
55    }
56  }
57
58  if (geceModu) {
59    lambalar(false, (simdi / 500) % 2 == 0, false);  // yanıp sönen sarı / flashing yellow
60    return;
61  }
62
63  if (simdi - adimBaslangic >= PLAN[adim].sure) {
64    adimBaslangic = simdi;
65    adim = (adim + 1) % ADIM_SAYISI;
66    lambalar(PLAN[adim].k, PLAN[adim].s, PLAN[adim].y);
67  }
68}

La lecture du bouton inclut un anti-rebond simple : un appui ne compte que si la valeur lue reste identique pendant 50 ms. Le tableau PLAN qui contient les états des feux est une forme plus lisible de l'idée du programme de carrefour. Comme les broches de l'ESP32 délivrent 3,3 V, les LED consomment moins de courant que sur l'Arduino et brillent un peu moins. Les LED rouge et jaune restent assez lumineuses avec 220 Ω, mais une LED verte très lumineuse avec une tension directe d'environ 3 V peut rester très faible ; dans ce cas, descends la résistance du vert à 100 Ω. Si tu débutes avec l'ESP32, Qu'est-ce que l'ESP32 ? est un bon point de départ pour l'installation et les broches sûres.

Feu piéton avec Raspberry Pi Pico (MicroPython)

Sur la Raspberry Pi Pico, on écrit la même machine à états en MicroPython. Cette fois, les états sont dans une liste : chaque ligne contient le nom de l'état, les valeurs des cinq feux et la durée. Le bouton utilise une interruption : quand GP4 passe à LOW, la fonction buton_kesmesi() ("interruption du bouton") s'exécute et lève seulement le drapeau de demande. Si tu enregistres le code sur la Pico sous le nom main.py avec Thonny, il démarre automatiquement à chaque mise sous tension.

MicroPython
1# Raspberry Pi Pico (MicroPython): yaya butonlu trafik lambası / pedestrian traffic light
2from machine import Pin
3import time
4
5yesil = Pin(11, Pin.OUT)              # araç yeşil / vehicle green
6sari = Pin(12, Pin.OUT)               # araç sarı / vehicle yellow
7kirmizi = Pin(13, Pin.OUT)            # araç kırmızı / vehicle red
8yaya_yesil = Pin(14, Pin.OUT)         # yaya yeşil / pedestrian green
9yaya_kirmizi = Pin(15, Pin.OUT)       # yaya kırmızı / pedestrian red
10buton = Pin(4, Pin.IN, Pin.PULL_UP)   # GP4, diğer ucu GND / other leg to GND
11
12# (durum, kırmızı, sarı, yeşil, yaya kırmızı, yaya yeşil, süre ms)
13# (state, red, yellow, green, ped red, ped green, duration ms)
14DURUMLAR = [
15    ("ARAC_YESIL",   0, 0, 1, 1, 0, 8000),   # en az yeşil süresi / minimum green
16    ("ARAC_SARI",    0, 1, 0, 1, 0, 3000),
17    ("YAYA_BEKLE",   1, 0, 0, 1, 0, 1000),   # tüm kırmızı / all red
18    ("YAYA_GECER",   1, 0, 0, 0, 1, 6000),
19    ("YAYA_FLAS",    1, 0, 0, 0, 1, 3000),
20    ("YAYA_BITTI",   1, 0, 0, 1, 0, 1000),   # tüm kırmızı / all red
21    ("KIRMIZI_SARI", 1, 1, 0, 1, 0, 1000),
22]
23
24talep = False
25adim = 0                              # şu anki durumun sırası / index of the current state
26
27def buton_kesmesi(pin):               # kesmede yalnız bayrak kaldır / only set a flag in the IRQ
28    global talep
29    if DURUMLAR[adim][0] != "YAYA_GECER":   # yaya yeşilinde basış yok sayılır / ignored during the green man
30        talep = True
31
32buton.irq(trigger=Pin.IRQ_FALLING, handler=buton_kesmesi)
33
34def uygula(i):
35    global talep
36    ad, k, s, y, yk, yy, _ = DURUMLAR[i]
37    kirmizi.value(k)
38    sari.value(s)
39    yesil.value(y)
40    yaya_kirmizi.value(yk)
41    yaya_yesil.value(yy)
42    if ad == "YAYA_GECER":
43        talep = False                 # talep karşılandı / request served
44    print("Durum / state:", ad)
45
46uygula(adim)
47baslangic = time.ticks_ms()
48
49while True:
50    gecen = time.ticks_diff(time.ticks_ms(), baslangic)   # taşmaya karşı güvenli / wrap-safe
51    ad = DURUMLAR[adim][0]
52
53    if ad == "YAYA_FLAS":
54        yaya_yesil.value((gecen // 250) % 2 == 0)          # yanıp sönen yaya yeşili / flashing green man
55
56    if gecen >= DURUMLAR[adim][6] and (ad != "ARAC_YESIL" or talep):
57        adim = (adim + 1) % len(DURUMLAR)                   # yeşil, talep gelene kadar sürer / green lasts until a request
58        uygula(adim)
59        baslangic = time.ticks_ms()
60
61    time.sleep_ms(10)

En MicroPython, on utilise time.ticks_ms() à la place de millis() et time.ticks_diff() à la place d'une simple soustraction ; le compteur de ticks repart à zéro après une certaine valeur, donc soustraire directement deux lectures peut donner un résultat faux. Garde la fonction d'interruption courte : pas de print, pas de sleep ni de longs calculs, juste un drapeau à modifier. Les broches de la Pico fonctionnent elles aussi en 3,3 V, la remarque sur la LED verte de la partie ESP32 s'applique donc ici aussi. L'installation de MicroPython et la numérotation des broches sont expliquées dans Qu'est-ce que la Raspberry Pi Pico ?, et les bases du langage dans Les bases de MicroPython.

Erreurs fréquentes et solutions

SymptômeCause probableSolution
Aucune LED ne s'allumeGND non relié, ou ligne d'alimentation de la breadboard coupée au milieuRelie le GND de l'Arduino à la ligne bleue ; sur une longue breadboard, ponte les deux moitiés
Une LED reste éteinteLED à l'envers ou sur la mauvaise brochePatte longue (anode) vers la broche via la résistance, patte courte au GND ; compare le numéro de broche avec le code
La mauvaise couleur s'allumeBroches rouge et verte inversées dans le codeCorrige les fils ou les constantes d'après le tableau des broches
LED très lumineuse qui chauffePas de résistanceAjoute une résistance de 220 Ω en série avec chaque LED
Le bouton ne réagit pas ou semble toujours appuyéBouton branché sur deux pattes déjà reliées à l'intérieur, ou INPUT_PULLUP oubliéUtilise les pattes en diagonale et écris pinMode(BUTON, INPUT_PULLUP)
Pas de réaction immédiate au boutonLe vert minimum des voitures n'est pas encore écouléCe n'est pas un bug ; réduis MIN_YESIL
Le programme se dérègle au bout d'un momentVariable de temps déclarée en intDéclare en unsigned long toutes les variables utilisées avec millis()

Conseils pour le devoir et le compte rendu du projet

Le feu tricolore est l'un des devoirs Arduino les plus fréquents en technologie, en informatique et en robotique. Ton professeur veut généralement voir non seulement un circuit qui marche, mais aussi que tu le comprends et que tu sais l'expliquer. Si tu organises ton compte rendu avec les parties suivantes, il sera clair et tu n'oublieras rien d'important :

  • Objectif : Décris en une ou deux phrases ce que montre le projet, par exemple "temporiser un feu tricolore avec des sorties numériques".
  • Liste du matériel : Indique le nom, la quantité et la valeur (220 Ω) de chaque composant, comme dans le tableau plus haut.
  • Schéma électrique : Ajoute une capture de Tinkercad ou de Fritzing ; si tu le dessines à la main, utilise les bons symboles. Lire un schéma électrique t'aidera.
  • Calcul de la résistance : Montre le calcul R = (5 − 2) / 0,015 ≈ 200 Ω et explique pourquoi tu as choisi 220 Ω.
  • Tableau des états ou organigramme : Ajoute le tableau des états, des durées et des transitions ; c'est la meilleure preuve que tu comprends le code.
  • Code et explications : Mets le code complet et explique les lignes importantes avec tes propres mots.
  • Tests et observations : Chronomètre les phases, mesure le courant d'une LED au multimètre et compare-le à ton calcul.
  • Conclusion et améliorations : Décris les problèmes rencontrés, leurs solutions et les fonctions que tu pourrais ajouter (bouton piéton, son, mode nuit).
Pour te démarquer, compare ta première version avec delay() à la version avec millis() : explique pourquoi des appuis sont perdus dans la première et jamais dans la seconde. Une vidéo de 20 à 30 secondes du circuit en marche et une capture du moniteur série renforcent encore le compte rendu.

Idées pour aller plus loin avec ton feu tricolore

  • Compte à rebours : Affiche sur un afficheur 7 segments les secondes restantes avant la fin du vert.
  • Signal sonore pour les personnes malvoyantes : Pendant le vert piéton, fais biper un buzzer rapidement, et lentement pendant le rouge.
  • Carrefour intelligent : Détecte une voiture qui attend dans la rue secondaire avec un capteur de distance HC-SR04 et ne donne le vert à la voie B que si elle est là.
  • Mode nuit automatique : Utilise une LDR pour passer à l'orange clignotant à la tombée de la nuit.
  • Commande à distance : Crée une page web sur l'ESP32 et change le mode et les durées depuis ton téléphone.
  • Passage à niveau : Avec la même logique, réalise les feux d'un passage à niveau avec deux LED rouges qui clignotent en alternance ; les projets K2000 et chenillard utilisent des idées de temporisation similaires.

Un feu tricolore a l'air d'un petit projet, mais ses idées, machine à états, temporisation sans blocage et transitions sûres, sont aussi la base des robots, alarmes et objets domotiques que tu construiras plus tard. Pour choisir ton prochain montage, parcours les idées de projets Arduino. Dans l'application BlueGrays, tu trouveras des projets de feu tricolore pour Arduino, ESP32 et Raspberry Pi Pico, ainsi que des suites comme le feu à quatre modes pour Pico et les feux d'avertissement de passage à niveau à LED, chacun avec un schéma pas à pas et un code prêt à tester sur ta propre carte.

Questions fréquentes

Comment faire un feu tricolore avec Arduino ?

Relie les pattes longues d'une LED rouge, d'une jaune et d'une verte à des broches numériques de l'Arduino (par exemple D10, D9 et D8) à travers des résistances de 220 Ω, et les pattes courtes au GND. Dans le code, configure ces broches en OUTPUT et allume puis éteins les LED dans l'ordre dans loop(). delay() suffit pour une première version ; pour un bouton et d'autres fonctions, il faut millis().

Quelle résistance pour les LED d'un feu tricolore ?

Sur une Arduino Uno en 5 V, 220 Ω par LED est un choix courant et sûr ; une LED rouge consomme alors environ 13 à 14 mA. Avec 330 Ω, elle éclaire moins mais reste bien visible. Sur l'ESP32 et la Pico en 3,3 V, 220 Ω fonctionne aussi ; descends à 100 Ω seulement si une LED verte très lumineuse reste trop faible.

Pourquoi le rouge et l'orange s'allument-ils ensemble sur certains feux ?

Dans des pays comme l'Allemagne, le Royaume-Uni ou la Turquie, le rouge et l'orange allumés ensemble annoncent que le vert va s'allumer : le conducteur se prépare mais ne démarre qu'au vert. En France, aux États-Unis, en Italie ou en Espagne, cette phase n'existe pas et le feu passe directement du rouge au vert. Dans ton projet Arduino, tu peux l'ajouter ou la supprimer à ta guise.

Faut-il utiliser delay ou millis pour un feu tricolore Arduino ?

Si tu te contentes d'allumer les LED dans l'ordre, delay() est la solution la plus simple et elle suffit. Dès que tu ajoutes un bouton, un capteur, un écran ou un son, delay() met le programme en pause et ces entrées sont manquées. Un code écrit avec millis() et une machine à états n'attend jamais et peut tout surveiller en même temps.

Peut-on réaliser le feu tricolore Arduino dans Tinkercad ?

Oui. Tinkercad Circuits propose l'Arduino Uno, les LED, les résistances et les boutons poussoirs comme composants prêts à l'emploi ; tu dessines le circuit, colles le code dans l'éditeur Texte et lances la simulation. C'est idéal pour tester le circuit et le code sans composants réels et pour obtenir une capture ou un lien de partage pour ton devoir.

Comment fonctionne un feu piéton à bouton d'appel ?

Les voitures ont normalement le vert, et un appui sur le bouton enregistre une demande. Une fois le vert minimum des voitures écoulé, le feu passe à l'orange puis au rouge et, après une courte marge de sécurité, le feu piéton passe au vert. Dans le code de ce guide, le vert piéton clignote avant de s'éteindre, puis le cycle revient aux voitures.

Voir tous les projets
Feu de circulation
#3Débutant

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.

4.8(126)
337
Feu de circulation ESP32
#107Débutant

Feu de circulation ESP32

Simulation d'un vrai feu de circulation avec 3 LED rouge-jaune-vert. Les temps sont proches de la logique du trafic réel : rouge long, jaune court, vert long. Exemple classique de contrôle séquentiel GPIO.

4.4(21)
44
Simulateur de Feu de Circulation Raspberry Pi Pico (3 LEDs séquentielles, GP11/GP12/GP13)
#150Débutant

Simulateur de Feu de Circulation Raspberry Pi Pico (3 LEDs séquentielles, GP11/GP12/GP13)

Nous simulons un vrai feu de circulation avec des LEDs rouge, jaune et verte. Vert 4 sec "passez", jaune 1,5 sec "préparez", rouge 3 sec "arrêtez" et rouge+jaune 1 sec "départ" (motif européen). Apprends la définition de fonction, la logique de machine à états et la synchronisation multi-GPIO. Circuit simple : 3 LEDs sur GP11/GP12/GP13, chacune avec 220Ω.

4.0(1)
12
Deux LED clignotant en alternance
#2Débutant

Deux LED clignotant en alternance

Un projet qui crée un effet de bascule en allumant et éteignant deux DEL de façon séquentielle. Apprend à contrôler plus d'une broche de sortie.

4.6(103)
317
Allumer une LED avec un bouton-poussoir (Push)
#9Débutant

Allumer une LED avec un bouton-poussoir (Push)

Projet de commande instantanée (push) qui allume la LED lorsque le bouton est pressé et l'éteint lorsqu'il est relâché. Enseigne l'utilisation de la lecture de bouton et de l'expression conditionnelle

4.9(112)
190
Gyrophare de police à LED
#4Débutant

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.

4.7(97)
227
Feu de signalisation Raspberry Pi Pico avec sélecteur à 4 modes (machine à états, GP11-13 + GP4)
#158Intermédiaire
Premium

Feu de signalisation Raspberry Pi Pico avec sélecteur à 4 modes (machine à états, GP11-13 + GP4)

4 modes différents pour le feu tricolore avec un seul bouton ! À chaque pression sur le bouton, le mode change : **Cycle normal** (rouge → orange → vert dans cet ordre), **Rouge uniquement** (ordre d'arrêt), **Orange clignotant** (attention/mode nuit), **Éteint** (entretien). Notions : **state machine** (machine à états — la variable `mode` détermine le comportement à adopter), **modulo mod cycling** (`(mode + 1) % 4` → 0→1→2→3→0, boucle infinie), **synchronisation non bloquante** (les phases de

5.0(1)
14
Feux clignotants à LED pour passages à niveau
#146Avancé
Premium

Feux clignotants à LED pour passages à niveau

Feux clignotants à LED pour passages à niveau.

5.0(2)
14