C'est l'automne ! FeuillesAutomne.gif

Super Brest 2025 : L'étrange noël de monsieur Simon

De Les Fabriques du Ponant
Révision datée du 30 novembre 2025 à 11:17 par Emma (discussion | contributions) (Conception des boutons :)
Aller à : navigation, rechercher

200px

résumé du projet

Ce hackathon a pour objectif d'adapter le jeu Simon (mémoire des couleurs/sons) en l'adaptant spécifiquement pour la fête de Noël de l'école.

Le projet consiste à :

Utiliser une cheminée existante pour le jeu.

Programmer l'électronique Arduino pour simuler les lumières et les sons de Simon.

Intégrer une thématique Noël (couleurs/sons festifs) pour créer une animation ludique et éducative qui met en avant la créativité.

membres du projet

Audrey : Ailée créature

Emma : Magique arc en ciel

Ewen: Magic flocon

Syrielle: Espiegle corne

Nadia : Divinité flamboyante

Bibliographie et webographie sur le projet

[[1]] https://fr.wikipedia.org/wiki/Simon_(jeu)

banque d'images

200Pxpx

prototype qu'on souhaite réaliser

Prototype simon.jpg

Schema de cablage

schema de cablage

Programme arduino

Le code à ete crée par l'IA claude , voici le prompt :
Crée un jeu Simon pour ESP32 avec :

- 4 boutons/LEDs (Rouge/GPIO12-25, Jaune/GPIO13-26, Vert/GPIO14-27, Bleu/GPIO15-33)

- DFPlayer Mini sur GPIO16-17 avec 6 sons Noël (0001-0006.mp3)

- Ruban LED WS2812B 144 LEDs sur GPIO32 avec effet feu continu

- Potentiomètre GPIO34 pour 3 difficultés (3/4/6 niveaux)

- Son 0005.mp3 pour victoire totale, 0006.mp3 pour échec immédiat

- Bibliothèques : DFRobotDFPlayerMini + FastLED

  1 /*
  2  * JEU SIMON COMPLET - ESP32 + DFPlayer Mini + Ruban LED Feu
  3  * 
  4  * Câblage:
  5  * ─────────────────────────────────────────────
  6  * DFPlayer Mini:
  7  *   - RX      → GPIO 17 (TX ESP32) + résistance 1kΩ
  8  *   - TX      → GPIO 16 (RX ESP32)
  9  *   - VCC     → 5V
 10  *   - GND     → GND
 11  *   - SPK_1/2 → Haut-parleur 3W (4-8Ω)
 12  * 
 13  * 4 Boutons (avec pull-up interne):
 14  *   - Bouton ROUGE  → GPIO 12 → GND
 15  *   - Bouton VERT   → GPIO 13 → GND
 16  *   - Bouton BLEU   → GPIO 14 → GND
 17  *   - Bouton JAUNE  → GPIO 15 → GND
 18  * 
 19  * 4 LEDs (avec résistances 220Ω):
 20  *   - LED ROUGE  → GPIO 25 → Résistance → GND
 21  *   - LED VERTE  → GPIO 26 → Résistance → GND
 22  *   - LED BLEUE  → GPIO 27 → Résistance → GND
 23  *   - LED JAUNE  → GPIO 33 → Résistance → GND
 24  * 
 25  * Ruban LED (WS2812B - 144 LEDs):
 26  *   - DATA → GPIO 32
 27  *   - VCC  → 5V (alimentation externe recommandée si >30 LEDs)
 28  *   - GND  → GND (commun avec ESP32)
 29  * 
 30  * Potentiomètre (sélection difficulté):
 31  *   - Broche gauche  → GND
 32  *   - Broche centrale → GPIO 34 (ADC)
 33  *   - Broche droite   → 3.3V
 34  * 
 35  * Fichiers MP3 sur carte SD (FAT32):
 36  *   - 0001.mp3 : Son bouton ROUGE
 37  *   - 0002.mp3 : Son bouton JAUNE
 38  *   - 0003.mp3 : Son bouton VERT
 39  *   - 0004.mp3 : Son bouton BLEU
 40  *   - 0005.mp3 : Son VICTOIRE (après 5 niveaux réussis)
 41  *   - 0006.mp3 : Son ÉCHEC (erreur dans la séquence)
 42  * 
 43  * IMPORTANT: Installez la bibliothèque FastLED
 44  * (Croquis → Inclure une bibliothèque → Gérer les bibliothèques → FastLED)
 45  */
 46 
 47 #include <DFRobotDFPlayerMini.h>
 48 #include <FastLED.h>
 49 
 50 // Configuration DFPlayer
 51 HardwareSerial mySoftwareSerial(2);
 52 DFRobotDFPlayerMini myDFPlayer;
 53 
 54 // Configuration Ruban LED
 55 #define LED_STRIP_PIN 32
 56 #define NUM_LEDS 144
 57 #define LED_TYPE WS2812B
 58 #define COLOR_ORDER GRB
 59 CRGB leds[NUM_LEDS];
 60 
 61 // Configuration Potentiomètre (sélection difficulté)
 62 #define POTENTIOMETER_PIN 34  // GPIO 34 (ADC1_CH6)
 63 
 64 // Niveaux de difficulté
 65 enum Difficulty {
 66   EASY = 0,    // 3 niveaux
 67   MEDIUM = 1,  // 4 niveaux
 68   HARD = 2     // 6 niveaux
 69 };
 70 
 71 Difficulty currentDifficulty = MEDIUM;
 72 int maxLevels = 4;  // Par défaut moyen
 73 
 74 // Configuration des pins boutons et LEDs
 75 const int BUTTON_PINS[4] = {12, 13, 14, 15};  // Rouge, Jaune, Vert, Bleu
 76 const int LED_PINS[4] = {25, 26, 27, 33};     // Rouge, Jaune, Vert, Bleu
 77 
 78 // Noms des couleurs pour l'affichage
 79 const char* COLOR_NAMES[4] = {"ROUGE", "JAUNE", "VERT", "BLEU"};
 80 
 81 // Correspondance couleur -> numéro de son
 82 const int COLOR_TO_SOUND[4] = {1, 2, 3, 4};  // Rouge=1, Jaune=2, Vert=3, Bleu=4
 83 
 84 // Variables du jeu
 85 int sequence[100];              // Séquence à mémoriser (max 100 niveaux)
 86 int sequenceLength = 0;         // Longueur actuelle de la séquence
 87 int playerInput[100];           // Saisie du joueur
 88 int playerIndex = 0;            // Position dans la saisie
 89 int level = 1;                  // Niveau actuel
 90 bool gameActive = false;        // Jeu en cours
 91 bool waitingForInput = false;   // En attente de la saisie du joueur
 92 
 93 // Débounce des boutons
 94 unsigned long lastButtonPress[4] = {0, 0, 0, 0};
 95 const unsigned long DEBOUNCE_DELAY = 250;
 96 
 97 // Timings du jeu (en millisecondes)
 98 const int FLASH_DURATION = 500;      // Durée d'allumage d'une LED
 99 const int PAUSE_BETWEEN_FLASH = 200; // Pause entre deux LEDs
100 const int DELAY_AFTER_SEQUENCE = 500;// Délai avant que le joueur joue
101 
102 // Variables pour l'effet feu
103 byte heat[NUM_LEDS];
104 unsigned long lastFireUpdate = 0;
105 const int FIRE_UPDATE_INTERVAL = 50; // Mise à jour toutes les 50ms
106 
107 void setup() {
108   Serial.begin(115200);
109   delay(1000);
110   
111   Serial.println("\n╔═══════════════════════════════════╗");
112   Serial.println("║      JEU SIMON - ESP32            ║");
113   Serial.println("╚═══════════════════════════════════╝\n");
114   
115   // Configuration des boutons
116   for (int i = 0; i < 4; i++) {
117     pinMode(BUTTON_PINS[i], INPUT_PULLUP);
118   }
119   Serial.println("✓ Boutons configurés");
120   
121   // Configuration des LEDs
122   for (int i = 0; i < 4; i++) {
123     pinMode(LED_PINS[i], OUTPUT);
124     digitalWrite(LED_PINS[i], LOW);
125   }
126   Serial.println("✓ LEDs configurées");
127   
128   // Configuration du ruban LED
129   FastLED.addLeds<LED_TYPE, LED_STRIP_PIN, COLOR_ORDER>(leds, NUM_LEDS);
130   FastLED.setBrightness(100); // Luminosité (0-255)
131   FastLED.clear();
132   FastLED.show();
133   Serial.println("✓ Ruban LED configuré (144 LEDs)");
134   
135   // Configuration du potentiomètre
136   pinMode(POTENTIOMETER_PIN, INPUT);
137   Serial.println("✓ Potentiomètre configuré sur GPIO 34");
138   
139   // Lire la difficulté initiale
140   readDifficulty();
141   
142   // Configuration DFPlayer
143   mySoftwareSerial.begin(9600, SERIAL_8N1, 16, 17);
144   
145   Serial.println("\nInitialisation du DFPlayer...");
146   if (!myDFPlayer.begin(mySoftwareSerial)) {
147     Serial.println("\n✗ ERREUR DFPlayer!");
148     Serial.println("Vérifiez:");
149     Serial.println("  - Carte SD insérée (FAT32)");
150     Serial.println("  - Fichiers 0001.mp3 à 0006.mp3");
151     Serial.println("  - Connexions RX/TX");
152     while(true) { delay(1000); }
153   }
154   
155   Serial.println("✓ DFPlayer OK!");
156   myDFPlayer.volume(25);  // Volume (0-30)
157   delay(100);
158   
159   // Animation de démarrage
160   startupAnimation();
161   
162   // Initialiser le générateur aléatoire
163   randomSeed(analogRead(0));
164   
165   Serial.println("\n═══════════════════════════════════");
166   Serial.println("  DIFFICULTÉ SÉLECTIONNÉE:");
167   printDifficulty();
168   Serial.println("═══════════════════════════════════");
169   Serial.println("   Appuyez sur un bouton pour");
170   Serial.println("        commencer le jeu!");
171   Serial.println("═══════════════════════════════════\n");
172 }
173 
174 void loop() {
175   // Mettre à jour l'effet feu en continu
176   updateFireEffect();
177   
178   if (!gameActive) {
179     // Attendre qu'un bouton soit pressé pour démarrer
180     for (int i = 0; i < 4; i++) {
181       if (digitalRead(BUTTON_PINS[i]) == LOW) {
182         delay(300);
183         startNewGame();
184         break;
185       }
186     }
187   } else if (waitingForInput) {
188     checkPlayerInput();
189   }
190 }
191 
192 void startNewGame() {
193   // Lire la difficulté au début de chaque partie
194   readDifficulty();
195   
196   Serial.println("\n╔═══════════════════════════════════╗");
197   Serial.println("║      NOUVELLE PARTIE              ║");
198   Serial.println("╚═══════════════════════════════════╝");
199   Serial.print("\n  Difficulté: ");
200   printDifficulty();
201   Serial.println();
202   
203   gameActive = true;
204   level = 1;
205   sequenceLength = 0;
206   
207   delay(1000);
208   nextLevel();
209 }
210 
211 void nextLevel() {
212   Serial.println("─────────────────────────────────────");
213   Serial.print("         NIVEAU ");
214   Serial.print(level);
215   Serial.print(" / ");
216   Serial.println(maxLevels);
217   Serial.println("─────────────────────────────────────\n");
218   
219   // Ajouter une couleur aléatoire à la séquence
220   sequence[sequenceLength] = random(0, 4);
221   sequenceLength++;
222   
223   delay(1000);
224   
225   // Afficher la séquence au joueur
226   playSequence();
227   
228   // Attendre la saisie du joueur
229   playerIndex = 0;
230   waitingForInput = true;
231 }
232 
233 void playSequence() {
234   Serial.println("👀 Mémorisez la séquence:\n");
235   
236   for (int i = 0; i < sequenceLength; i++) {
237     int color = sequence[i];
238     
239     Serial.print("   ");
240     Serial.print(i + 1);
241     Serial.print(". ");
242     Serial.println(COLOR_NAMES[color]);
243     
244     // Allumer la LED et jouer le son
245     digitalWrite(LED_PINS[color], HIGH);
246     myDFPlayer.play(COLOR_TO_SOUND[color]);  // Rouge=1, Jaune=2, Vert=3, Bleu=4
247     
248     delay(FLASH_DURATION);
249     
250     // Éteindre la LED
251     digitalWrite(LED_PINS[color], LOW);
252     
253     // Pause entre les flashs
254     if (i < sequenceLength - 1) {
255       delay(PAUSE_BETWEEN_FLASH);
256     }
257   }
258   
259   delay(DELAY_AFTER_SEQUENCE);
260   Serial.println("\n🎮 À vous de jouer!\n");
261 }
262 
263 void checkPlayerInput() {
264   for (int i = 0; i < 4; i++) {
265     if (digitalRead(BUTTON_PINS[i]) == LOW) {
266       unsigned long currentTime = millis();
267       
268       // Débounce
269       if (currentTime - lastButtonPress[i] > DEBOUNCE_DELAY) {
270         lastButtonPress[i] = currentTime;
271         
272         // Feedback visuel et sonore
273         digitalWrite(LED_PINS[i], HIGH);
274         myDFPlayer.play(COLOR_TO_SOUND[i]);  // Jouer le son correspondant
275         
276         Serial.print("   → ");
277         Serial.println(COLOR_NAMES[i]);
278         
279         // Enregistrer la saisie
280         playerInput[playerIndex] = i;
281         
282         delay(300);
283         digitalWrite(LED_PINS[i], LOW);
284         
285         // Vérifier si c'est correct
286         if (playerInput[playerIndex] != sequence[playerIndex]) {
287           gameLost();
288           return;
289         }
290         
291         playerIndex++;
292         
293         // Vérifier si la séquence complète est correcte
294         if (playerIndex >= sequenceLength) {
295           sequenceComplete();
296         }
297       }
298     }
299   }
300 }
301 
302 void sequenceComplete() {
303   Serial.println("\n✅ Séquence correcte!\n");
304   waitingForInput = false;
305   
306   level++;
307   
308   // Vérifier si on a atteint le niveau maximum selon la difficulté
309   if (level > maxLevels) {
310     gameWon();
311   } else {
312     delay(500);
313     
314     // Animation de succès (rapide entre chaque niveau)
315     for (int j = 0; j < 2; j++) {
316       for (int i = 0; i < 4; i++) {
317         digitalWrite(LED_PINS[i], HIGH);
318       }
319       delay(100);
320       for (int i = 0; i < 4; i++) {
321         digitalWrite(LED_PINS[i], LOW);
322       }
323       delay(100);
324     }
325     
326     delay(1000);
327     nextLevel();
328   }
329 }
330 
331 void gameLost() {
332   Serial.println("\n╔═══════════════════════════════════╗");
333   Serial.println("║          ❌ ERREUR!               ║");
334   Serial.println("╚═══════════════════════════════════╝");
335   Serial.print("\n   Vous étiez au niveau ");
336   Serial.println(level);
337   Serial.println();
338   
339   waitingForInput = false;
340   gameActive = false;
341   
342   // Son d'échec (0006.mp3)
343   myDFPlayer.play(6);
344   
345   // Animation d'échec (clignotement rapide)
346   for (int j = 0; j < 5; j++) {
347     for (int i = 0; i < 4; i++) {
348       digitalWrite(LED_PINS[i], HIGH);
349     }
350     delay(150);
351     for (int i = 0; i < 4; i++) {
352       digitalWrite(LED_PINS[i], LOW);
353     }
354     delay(150);
355   }
356   
357   Serial.println("═══════════════════════════════════");
358   Serial.println(" Appuyez sur un bouton pour rejouer");
359   Serial.println("═══════════════════════════════════\n");
360 }
361 
362 void gameWon() {
363   Serial.println("\n╔═══════════════════════════════════╗");
364   Serial.println("║      🎉 VICTOIRE! 🎉              ║");
365   Serial.println("╚═══════════════════════════════════╝");
366   Serial.print("\n  Vous avez réussi les ");
367   Serial.print(maxLevels);
368   Serial.println(" niveaux!");
369   Serial.print("  Difficulté: ");
370   printDifficulty();
371   Serial.println();
372   
373   gameActive = false;
374   
375   // Son de victoire (0005.mp3)
376   myDFPlayer.play(5);
377   
378   // Animation de victoire spectaculaire
379   // Vague de lumière rapide
380   for (int j = 0; j < 8; j++) {
381     for (int i = 0; i < 4; i++) {
382       digitalWrite(LED_PINS[i], HIGH);
383       delay(60);
384       digitalWrite(LED_PINS[i], LOW);
385     }
386   }
387   
388   // Clignotement final toutes ensemble
389   for (int j = 0; j < 4; j++) {
390     for (int i = 0; i < 4; i++) {
391       digitalWrite(LED_PINS[i], HIGH);
392     }
393     delay(200);
394     for (int i = 0; i < 4; i++) {
395       digitalWrite(LED_PINS[i], LOW);
396     }
397     delay(200);
398   }
399   
400   Serial.println("\n═══════════════════════════════════");
401   Serial.println(" Ajustez le potentiomètre pour");
402   Serial.println(" changer de difficulté");
403   Serial.println(" Appuyez sur un bouton pour rejouer");
404   Serial.println("═══════════════════════════════════\n");
405 }
406 
407 void startupAnimation() {
408   Serial.println("\nAnimation de démarrage...\n");
409   
410   // Allumage séquentiel
411   for (int j = 0; j < 2; j++) {
412     for (int i = 0; i < 4; i++) {
413       digitalWrite(LED_PINS[i], HIGH);
414       delay(100);
415     }
416     for (int i = 0; i < 4; i++) {
417       digitalWrite(LED_PINS[i], LOW);
418       delay(100);
419     }
420   }
421   
422   // Flash final
423   for (int i = 0; i < 4; i++) {
424     digitalWrite(LED_PINS[i], HIGH);
425   }
426   delay(300);
427   for (int i = 0; i < 4; i++) {
428     digitalWrite(LED_PINS[i], LOW);
429   }
430   
431   delay(500);
432 }
433 
434 // Effet feu de bois pour le ruban LED
435 void updateFireEffect() {
436   unsigned long currentTime = millis();
437   
438   // Mettre à jour seulement toutes les FIRE_UPDATE_INTERVAL ms
439   if (currentTime - lastFireUpdate < FIRE_UPDATE_INTERVAL) {
440     return;
441   }
442   lastFireUpdate = currentTime;
443   
444   // Refroidissement: chaque cellule perd un peu de chaleur
445   for (int i = 0; i < NUM_LEDS; i++) {
446     int cooldown = random(0, ((55 * 10) / NUM_LEDS) + 2);
447     if (cooldown >= heat[i]) {
448       heat[i] = 0;
449     } else {
450       heat[i] = heat[i] - cooldown;
451     }
452   }
453   
454   // Diffusion de chaleur vers le haut
455   for (int k = NUM_LEDS - 1; k >= 2; k--) {
456     heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;
457   }
458   
459   // Ajout de nouvelles étincelles aléatoires en bas
460   if (random(255) < 120) {
461     int y = random(7);
462     heat[y] = heat[y] + random(160, 255);
463   }
464   
465   // Conversion de la chaleur en couleurs
466   for (int j = 0; j < NUM_LEDS; j++) {
467     // Palette de couleurs feu: noir → rouge → orange → jaune → blanc
468     byte temperature = heat[j];
469     
470     // Noir → Rouge foncé
471     byte t192 = scale8_video(temperature, 191);
472     byte heatramp = t192 & 0x3F; // 0..63
473     heatramp <<= 2; // 0..252
474     
475     if (t192 & 0x80) {
476       // Partie la plus chaude - jaune vers blanc
477       leds[j].r = 255;
478       leds[j].g = 255;
479       leds[j].b = heatramp;
480     } else if (t192 & 0x40) {
481       // Partie chaude - rouge vers jaune
482       leds[j].r = 255;
483       leds[j].g = heatramp;
484       leds[j].b = 0;
485     } else {
486       // Partie froide - noir vers rouge
487       leds[j].r = heatramp;
488       leds[j].g = 0;
489       leds[j].b = 0;
490     }
491   }
492   
493   FastLED.show();
494 }
495 
496 // Lire le potentiomètre et déterminer la difficulté
497 void readDifficulty() {
498   int potValue = analogRead(POTENTIOMETER_PIN);  // 0-4095 sur ESP32
499   
500   // Diviser en 3 zones
501   if (potValue < 1365) {
502     // Zone 1: FACILE (0-1365)
503     currentDifficulty = EASY;
504     maxLevels = 3;
505   } else if (potValue < 2730) {
506     // Zone 2: MOYEN (1365-2730)
507     currentDifficulty = MEDIUM;
508     maxLevels = 4;
509   } else {
510     // Zone 3: DIFFICILE (2730-4095)
511     currentDifficulty = HARD;
512     maxLevels = 6;
513   }
514 }
515 
516 // Afficher la difficulté actuelle
517 void printDifficulty() {
518   switch(currentDifficulty) {
519     case EASY:
520       Serial.print("FACILE (3 niveaux)");
521       break;
522     case MEDIUM:
523       Serial.print("MOYEN (4 niveaux)");
524       break;
525     case HARD:
526       Serial.print("DIFFICILE (6 niveaux)");
527       break;
528   }
529 }

Conception des boutons :

- Au départ : Bouton rouge à disposition, le problème est qu'il faut plusieur couleur. Alors nous changeons de couleur la led et voulons remplacer l'opercule rouge par du plexiglace, cependant cela ne se colle pas au bouton.
- Alors : Nous avons pris des boutons plus petit de plusieurs couleur (rouge, bleu, vert et jaune). Pour les rendre plus grands, nous avons decider de couper du plexiglace en plusieur forme, bonhomme de neige, sapin, renne, et pain d'épice, nous avons laissé un trou (de 27mm de diamètre) dans chacune des formes afin de mettre les boutons dedans.
- Pour finir, nous avons rajouté du vinyl de la même couleur que les boutons pour les rendre plus visible sur la cheminée.

premier diaporama : Alors ? Comment ça se passe ?

Diaporama type Alors ?: Téléchargez et adaptez-le !

second diaporama : Et voilà !

Diaporama type Et Voilà ! : Téléchargez et adaptez-le !