Super Brest 2025 : L'étrange noël de monsieur Simon
Sommaire
- 1 résumé du projet
- 2 membres du projet
- 3 Bibliographie et webographie sur le projet
- 4 banque d'images
- 5 prototype qu'on souhaite réaliser
- 6 Schema de cablage
- 7 Programme arduino
- 8 Conception des boutons
- 9 Prototype design bouton
- 10 Galère
- 11 premier diaporama : Alors ? Comment ça se passe ?
- 12 second diaporama : Et voilà !
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
prototype qu'on souhaite réaliser
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
Galère
11h50, jeu de lumiere marche pas, on retourne à l'ancienne version
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 !
