Petit jeu avec des Leds et un bouton

Bonjour à tous,

week end venteux, pluvieux et froid... Donc pas trop envie de sortir.
Historie de m'aérer les méninges, je faisais du ménage et trainait dans la poubelle un petit bout de PCB à pastilles... Le ménage n'étant pas mon point fort :slight_smile: — je me suis demandé ce que je pourrais faire de cette chute...

J'ai demandé à un de mes jeunes neveux et il m'a dit pourquoi tu fais pas "un jeu de réflexe"... Bonne question, pourquoi pas !

J'avais des LEDs 3mm de différentes couleurs et des petites résistances de 200Ω qui trainaient et ce ne sont pas les boutons qui manquent dans les tiroirs...

Première étape: agencement du PCB

  • en regardant la taille de mon PCB, je pouvais mettre une douzaine de LEDs en alternant sur 2 lignes, et en me disant qu'un petit arduino standard va permettre simplement d'utiliser les broches de 2 à 13 pour les LEDs, ça fait 12 aussi --> J'ai choisi de prendre 4 groupes de 3 LEds de couleurs
  • et il restait une petite place sur le côté pour Le bouton (qui sera sur une pin analogique pour laisser les pin 0 et 1 pour le port série)

Deuxième étape: le circuit

en tournant comme il faut le bouton (qui a 4 pins connectées deux à deux), je mets tout à gauche du PCB une broche qui sera GND et dont la pin opposée me servira à connecter les résistances / cathodes des Leds. Chaque Led a sa résistance de limitation de courant

Troisième étape: Le montage

Tout simple, j'ai fait bosser mon neveu... test unitaire des Leds et valeur de résistance, installation des composants sur la plaque et soudure un à un des composants — en vérifiant au fur et à mesure la continuité et qu'il n'y a pas de court circuit (il a fallu jouer de la pompe de temps en temps... :grin: )

ensuite en prenant un header à 90°, et en enlevant 2 broches - on se retrouvera avec tout à gauche la broche pour GND et ensuite une broche pour le bouton et une en face de chaque anode de chaque Led.


(y'a des progrès à faire sur la soudure mais c'est pas si mal pour un début)

Voilà, ensuite une petite nappe pour connecter chaque pin à mon arduino et le tour était joué

montage.png
(il n'a pas été fichu de mettre tous les résistances dans le même sens pour faire joli... ah tout se perd ma bonne dame...:)) )

Quatrième étape: Le code du jeu

Bon, on avait un bouton, des Leds commandées une à une, restait plus qu'à trouver ce qu'on allait en faire comme un jeu de réflexe...

On a retenu un principe assez simple:

  • Le jeu vous montre une configuration de LEDs (allumées / éteintes) à obtenir
  • Chaque LED a un état, au début toutes éteintes - donc on affiche toutes les LEDs éteintes
  • puis les LEDs se mettent à s'allumer à tour de rôle de gauche à droite (led active)
  • Quand on arrive au bout de la rangée, on recommence au début avec un défilement moins rapide
  • Quand vous appuyez sur le bouton, vous basculez l'état mémorisée de la LED active (OFF -> ON ou ON -> OFF)
  • quand vous avez réussi à reproduire la configuration à obtenir, un petite animation vous montre qe vous avez gagné et puis on recommence avec une autre configuration.

Donc voilà je viens de coder cela, ça a l'air de fonctionner, donc je partage si ça peut amuser quelqu'un ou faire passer une après midi de bricolage :slight_smile:

montage.png

Le code (désolé j’ai mis les commentaires en anglais, déformation…)

Pour simplifier la gestion du bouton, j’ai utilisé la librairie de @bricoleau qu’il faudra donc installer.

// ----------------------------------------------------------------------
// SIMPLE LED GAME - © J-M-L for the Arduino Forum
// cf https://forum.arduino.cc/index.php?topic=644261.msg4358456#msg4358456
// Need to install this french library: http://forum.arduino.cc/index.php?topic=375232.0 - give him Karma!
// (thanks @Bricoleau! https://forum.arduino.cc/index.php?action=profile;u=193346)
// LEDs will blink in sequence and when you press the button the current LED state will be remembered
// (turning it off if it was on and turning it on if it was off)
//
// Goal is to turn on the LEDs matching a special challenge pattern
//
// version 1.0: November 3rd, 2019
//
// new features ideas:
//  - add sound or music
//  - generate random patterns, may be starting with 1 LEd, then 2, then 3... to increase complexity (use SIMON like (memory game) appraoch where you only show the next LED to add to the group)
//  - replace button with rotary encoder and use rotary for setting difficulty / game speed
//  - display scores using LEDs smartly
//  - add an optional LCD to provide user feedback
//  - make it multi-user
//
// BSD license, all text above must be included in any redistribution
// ----------------------------------------------------------------------

#include <simpleBouton.h> // http://forum.arduino.cc/index.php?topic=375232.0

// our button is wired on PIN A0
const uint8_t buttonPin = A0;
simpleBouton actionButton(buttonPin);   // wired : buttonPin --- Button --- GND

// These are constant you can play with to match your preferences
const uint32_t resetDuration = 10000UL; // press the button 10 seconds to reset the game
const unsigned long updatePeriodMin = 20; // start pace of the game. the smaller the more difficult
const unsigned int updatePeriodIncrement = 20; // after each lap slow down the game by this amount
const unsigned long blinkPeriod = 250;  // used to show animations

const uint8_t ledPins[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; // order of the pins for each LED

typedef uint16_t lightPattern_t; // could be uint8_t if you have less than 8 LEDs or uint32_t if you have more than 16 and less than 32

// define in this array the sequence of challenges you want to run
// each 1 means a LED to turn ON, each 0 means this LEDs needs to be OFF
const lightPattern_t challenge[] { // max 255 challenges as they will be indexed with a uint8_t
  0b000000000111, // first group of 3 LEDs
  0b000000111000, // second group of 3 LEDs
  0b000111000000, // third group of 3 LEDs
  0b111000000000, // fouth group of 3 LEDs
  0b111000000111, // first and fouth group of 3 LEDs
  0b111000111000, // ... you get the idea
  0b010101010101,
  0b101010101010,
  0b111111111111, // all LEDs
};

// Program Variable, no need to mess around with the below
lightPattern_t ledsOn; // variable to memorize what the user has activated. Each bit set at 1 if a LED ON
const uint8_t nbLeds = sizeof(ledPins) / sizeof(ledPins[0]);
const uint8_t nbChallenges = sizeof(challenge) / sizeof(challenge[0]);
uint8_t currentChallenge = 0xFF; // initialize at max so first one becomes 0
unsigned long updatePeriod = updatePeriodMin;

uint8_t activeLedIndex;
unsigned long nbCyclesToWin;
unsigned long nbClicksToWin;
unsigned long timeToWin;

unsigned long lastChrono;


// ------------ Utilities ------------

void turnLedsOff(uint32_t pause = 0)
{
  for (const auto& aPin : ledPins) {
    digitalWrite(aPin, LOW);
    if (pause) delay(pause);
  }
}

void turnLedsOn(uint32_t pause = 0)
{
  for (const auto& aPin : ledPins) {
    digitalWrite(aPin, HIGH);
    if (pause) delay(pause);
  }
}

void showCurrentChallenge(uint8_t nbBlink = 6)
{
  for (uint8_t count = 0; count < nbBlink; count++) {
    delay(blinkPeriod);
    for (uint8_t ledIndex = 0; ledIndex < nbLeds; ledIndex++) {
      if (bitRead(challenge[currentChallenge], ledIndex)) digitalWrite(ledPins[ledIndex], HIGH);
    }
    delay(blinkPeriod);
    turnLedsOff();
  }
}

void gameSetup()
{
  currentChallenge++; // next challenge
  if (currentChallenge >= nbChallenges) currentChallenge = 0 ; // rollover
  ledsOn = 0; // all leds off
  turnLedsOff();
  updatePeriod = updatePeriodMin;
  showCurrentChallenge();
  activeLedIndex = 0;
  nbCyclesToWin = 0;
  nbClicksToWin = 0;
  digitalWrite(ledPins[activeLedIndex], HIGH);
  timeToWin = lastChrono = millis();
}

void resetGame()
{
  currentChallenge = 0xFF; // this way during the setup we go to next one which will be 0, ie the first challenge in the array
  gameSetup();
}

void showWinLedPettern(uint8_t nbBlink = 6) // by default blink 6 times
{
  turnLedsOff();
  for (uint8_t count = 0; count < nbBlink; count++) {
    turnLedsOn(blinkPeriod / nbLeds);
    turnLedsOff(blinkPeriod / nbLeds);
  }
}

inline bool testWin()
{
  return (ledsOn == challenge[currentChallenge]); // bit comparison, would work up to 32 bits with lightPattern_t being a uint32_t
}


// ------------ State Machine ------------

void testTick()
{
  if (millis() - lastChrono >= updatePeriod) {
    if (bitRead(ledsOn, activeLedIndex) == 0) digitalWrite(ledPins[activeLedIndex], LOW); // turn off previous LED if needed
    activeLedIndex++; // get to next LED (regardless if it's on or not)
    if (activeLedIndex >= nbLeds) {
      activeLedIndex = 0; // rollover - don't use modulo as slower than a test
      nbCyclesToWin++; // one more cycle
      updatePeriod += updatePeriodIncrement; // slows down the game
    }
    digitalWrite(ledPins[activeLedIndex], HIGH); // turn on the next LED
    lastChrono = millis();
  }
}


void testClick()
{
  uint32_t downDuration = actionButton.dureeEnfonce();
  actionButton.actualiser();
  if (actionButton.vientDEtreEnfonce()) { // if we just pressed the button
    nbClicksToWin++;
    if (bitRead(ledsOn, activeLedIndex) == 0) bitSet(ledsOn, activeLedIndex);
    else bitClear(ledsOn, activeLedIndex);
    if (testWin()) {
      Serial.print(F("You won in "));
      Serial.print((millis() - timeToWin) / 1000.0);
      Serial.print(F(" seconds and took "));
      Serial.print(nbCyclesToWin);
      Serial.print(F(" cycle"));      if (nbCyclesToWin > 1) Serial.print(F("s"));
      Serial.print(F(" and required "));
      Serial.print(nbClicksToWin);
      Serial.print(F(" click"));
      if (nbClicksToWin > 1) Serial.print(F("s"));
      Serial.println(F("."));
      showWinLedPettern(); // little animation
      gameSetup(); // get ready for next game
    }
  }
  
  // if we  press the button a long time, we reset the game
  if (downDuration >= resetDuration) resetGame();
}


void setup()
{
  Serial.begin(115200);
  for (const auto& aPin : ledPins) pinMode(aPin, OUTPUT);
  resetGame();
}

void loop()
{
  testTick();
  testClick();
}

On peut ensuite être un peu créatif, rajouter un piezo pour faire un peu de bruit (histoire d’ennuyer les parents), un écran LCD pour afficher les scores, générer des défis aléatoirement (là j’en ai codé que quelques uns à la main dans le tableau challenge[].
Au début je voulais mettre un rotary encoder au lieu du bouton mais c’était trop gros pour mon PCB. On aurait pu utiliser la rotation pour régler la vitesse de défilement par exemple…

bref - c’est une base extensible, n’hésitez pas à poster les jeux que vous pourriez écrire sur cette même base matérielle

amusez vous bien !

J-M-L:
j'ai fait bosser mon neveu... (il a fallu jouer de la pompe de temps en temps... :grin: )

L'OMC condamne le travail des enfants et les châtiments corporels

J-M-L:
(il n'a pas été fichu de mettre tous les résistances dans le même sens pour faire joli... ah tout se perd ma bonne dame...:)) )

D’où les coups de pompe je suppose!

LOL :slight_smile:

(l'assistance aux tâches ménagères est autorisée ainsi que les devoirs de vacances)

J-M-L:
Bonjour à tous,

week end venteux, pluvieux et froid...

:grin:

C'est vraiment béta !

J'avais ... presque tout dans ma campagne ! sauf le "bouton rouge" :wink:

Je rentre donc rapidement ( la nuit aéro tombe/arrive vite en ce moment 8) ) pour regarder si je n'en aurais pas un qui éventuellement trainerait ---> ;D

J-M-L:
il n’a pas été fichu de mettre tous les résistances dans le même sens pour faire joli… ah tout se perd ma bonne dame…:))

C’est comme ça ma p’tite Lucette… j’vous l’dis moi, c’est comme ça !!!

Une petite vidéo de démonstration peut-être ?

Ça pourrait aussi être fait avec un petit ruban de WS2812, pour les fainéants comme moi...

lesept:
Ça pourrait aussi être fait avec un petit ruban de WS2812, pour les fainéants comme moi...

Oui - mais dans ce cas je n'aurais pas pu réutiliser le petit bout de PCB trouvé dans la poubelle :slight_smile:

(et la grosse flemme pour la vidéo :slight_smile: )

Bonjour,
J’aime bien les trucs à LED.
C’est vrai qu’une vidéo serait cool.