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 fichier pour la decoupe de la boite et des boutons
- 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) 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: Espiègle 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.
- Ensuite, nous avons rajouté du vinyl de la même couleur que les boutons pour les rendre plus visible sur la cheminée.
- Pour finir, nous allons les pencher à un angle de 45° pour que ce soit plus visible pour les enfants.
fichier pour la decoupe de la boite et des boutons
Fichier:Prototypes Boutons .pdf
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 !
