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 à 12:07 par Emma (discussion | contributions) (Prototype design bouton)
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 lumineux rouge à disposition, le problème est qu'il faut plusieurs couleurs. Alors nous changeons de couleur de la led et voulons remplacer l'opercule rouge par du plexiglass, 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 décider de couper du plexiglass en plusieurs formes : 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. Le fichier prototype a été réalisé avec inkscape et la découpe avec une découpeuse laser.
- Pour finir, nous avons rajouté du vinyl de la même couleur que les boutons pour les rendre plus visible sur la cheminée.

Prototype design bouton



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 !