Bouton poussoir

Bonjour

Je cherche à réaliser un compteur de point à l'aide d'un bouton poussoir. J'affiche une réponse sur mon écran LCD et si l'utilisateur a la bonne réponse, il doit appuyer sur le bouton poussoir pour que le compteur s'incrémente de 1. Cependant l’utilisateur appuis et relâche directement le bouton. Or dans le cas là, mon bouton ne s'incrémente pas, il ne s'incrémente que si l'utilisateur reste longtemps appuyé et appuis avant même que la réponse s'affiche. Pouvez vous m'aider svp ?
Voici une partie de mon code

lcd.print(departement[indice]);
lcd.setCursor(0, 0);
      lcd.print("Reponse: ");
      lcd.setCursor(0, 1);


      strcpy_P(temp_string2, (char *)pgm_read_word(&(chaines2[departement[indice] - 1]))); // Copie le texte à partir du PROGMEM dans temp_string
      if (digitalRead(BUTTON_ROUGE) == HIGH) {
        count_red++;
        delay(300);
        Serial.println(count_red);
        Serial.println("boucle");
      }
      Serial.println("marche");
      lcd.print(temp_string2);

      

      delay(2000);
      lcd.clear();

      delay(500);

postez TOUT le code - merci


avec ces delay() c'est clair que ça ne doit pas aller très vite

Et pour simplifier votre gestion des boutons, éventuellement utilisez la bibliothèque Button dans easyRun de @bricoleau ou OneButton de Matthias Hertel ou encore Toggle de @dlloyd.

Rémi / @pandaroux007 a fait un petit tuto sur OneButton en français si ça peut vous aider

#include <LiquidCrystal.h>
#include <String.h>
#include <Wire.h>
#include <HT16K33.h>
#include <avr/pgmspace.h>

#define LED 2
#define BUTTON A1
#define LED_ROUGE 4
#define LED_BLEUE 3
#define BUTTON_ROUGE A2
#define BUTTON_BLEU A3


HT16K33 seg(0x70);              //Crée un objet
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);  //Pins où on va connecter l'écran (RS, E, D4, D5, D6, D7)

char temp_string[185];
char temp_string2[15];
const char uwu_0[] PROGMEM = {"   Jeu pour apprendre les departements francais. CONSIGNE: Vous avez 3 secondes pour deviner le nom du departement qui s'affiche. Appuyer sur le bouton de la LED verte pour commencer."};
const char *const chaines[] PROGMEM = {uwu_0};

const char departement_1[] PROGMEM = {"Ain"};
const char departement_2[] PROGMEM = {"Aisne"};
const char departement_3[] PROGMEM = {"Allier"};
const char departement_4[] PROGMEM = {"Alpes-de-Haute-Provence"};
const char departement_5[] PROGMEM = {"Hautes-Alpes"};
const char departement_6[] PROGMEM = {"Alpes-Maritimes"};
const char departement_7[] PROGMEM = {"Ardeche"};
const char departement_8[] PROGMEM = {"Ardennes"};
const char departement_9[] PROGMEM = {"Ariege"};
const char departement_10[] PROGMEM = {"Aube"};
const char departement_11[] PROGMEM = {"Aude"};
const char departement_12[] PROGMEM = {"Aveyron"};
const char departement_13[] PROGMEM = {"Bouches-du-Rhone"};
const char departement_14[] PROGMEM = {"Calvados"};
const char departement_15[] PROGMEM = {"Cantal"};
const char departement_16[] PROGMEM = {"Charente"};
const char departement_17[] PROGMEM = {"Charente-Maritime"};
const char departement_18[] PROGMEM = {"Cher"};
const char departement_19[] PROGMEM = {"Correze"};
const char departement_20[] PROGMEM = {"Corse-du-Sud"};
const char departement_21[] PROGMEM = {"Cote-d'Or"};
const char departement_22[] PROGMEM = {"Cotes-d'Armor"};
const char departement_23[] PROGMEM = {"Creuse"};
const char departement_24[] PROGMEM = {"Dordogne"};
const char departement_25[] PROGMEM = {"Doubs"};
const char departement_26[] PROGMEM = {"Drome"};
const char departement_27[] PROGMEM = {"Eure"};
const char departement_28[] PROGMEM = {"Eure-et-Loir"};
const char departement_29[] PROGMEM = {"Finistere"};
const char departement_30[] PROGMEM = {"Gard"};
const char departement_31[] PROGMEM = {"Haute-Garonne"};
const char departement_32[] PROGMEM = {"Gers"};
const char departement_33[] PROGMEM = {"Gironde"};
const char departement_34[] PROGMEM = {"Herault"};
const char departement_35[] PROGMEM = {"Ille-et-Vilaine"};
const char departement_36[] PROGMEM = {"Indre"};
const char departement_37[] PROGMEM = {"Indre-et-Loire"};
const char departement_38[] PROGMEM = {"Isere"};
const char departement_39[] PROGMEM = {"Jura"};
const char departement_40[] PROGMEM = {"Landes"};
const char departement_41[] PROGMEM = {"Loir-et-Cher"};
const char departement_42[] PROGMEM = {"Loire"};
const char departement_43[] PROGMEM = {"Haute-Loire"};
const char departement_44[] PROGMEM = {"Loire-Atlantique"};
const char departement_45[] PROGMEM = {"Loiret"};
const char departement_46[] PROGMEM = {"Lot"};
const char departement_47[] PROGMEM = {"Lot-et-Garonne"};
const char departement_48[] PROGMEM = {"Lozere"};
const char departement_49[] PROGMEM = {"Maine-et-Loire"};
const char departement_50[] PROGMEM = {"Manche"};
const char departement_51[] PROGMEM = {"Marne"};
const char departement_52[] PROGMEM = {"Haute-Marne"};
const char departement_53[] PROGMEM = {"Mayenne"};
const char departement_54[] PROGMEM = {"Meurthe-et-Moselle"};
const char departement_55[] PROGMEM = {"Meuse"};
const char departement_56[] PROGMEM = {"Morbihan"};
const char departement_57[] PROGMEM = {"Moselle"};
const char departement_58[] PROGMEM = {"Nievre"};
const char departement_59[] PROGMEM = {"Nord"};
const char departement_60[] PROGMEM = {"Oise"};
const char departement_61[] PROGMEM = {"Orne"};
const char departement_62[] PROGMEM = {"Pas-de-Calais"};
const char departement_63[] PROGMEM = {"Puy-de-Dome"};
const char departement_64[] PROGMEM = {"Pyrenees-Atlantiques"};
const char departement_65[] PROGMEM = {"Hautes-Pyrenees"};
const char departement_66[] PROGMEM = {"Pyrenees-Orientales"};
const char departement_67[] PROGMEM = {"Bas-Rhin"};
const char departement_68[] PROGMEM = {"Haut-Rhin"};
const char departement_69[] PROGMEM = {"Rhone"};
const char departement_70[] PROGMEM = {"Haute-Saone"};
const char departement_71[] PROGMEM = {"Saone-et-Loire"};
const char departement_72[] PROGMEM = {"Sarthe"};
const char departement_73[] PROGMEM = {"Savoie"};
const char departement_74[] PROGMEM = {"Haute-Savoie"};
const char departement_75[] PROGMEM = {"Paris"};
const char departement_76[] PROGMEM = {"Seine-Maritime"};
const char departement_77[] PROGMEM = {"Seine-et-Marne"};
const char departement_78[] PROGMEM = {"Yvelines"};
const char departement_79[] PROGMEM = {"Deux-Sevres"};
const char departement_80[] PROGMEM = {"Somme"};
const char departement_81[] PROGMEM = {"Tarn"};
const char departement_82[] PROGMEM = {"Tarn-et-Garonne"};
const char departement_83[] PROGMEM = {"Var"};
const char departement_84[] PROGMEM = {"Vaucluse"};
const char departement_85[] PROGMEM = {"Vendée"};
const char departement_86[] PROGMEM = {"Vienne"};
const char departement_87[] PROGMEM = {"Haute-Vienne"};
const char departement_88[] PROGMEM = {"Vosges"};
const char departement_89[] PROGMEM = {"Yonne"};
const char departement_90[] PROGMEM = {"Territoire de Belfort"};
const char departement_91[] PROGMEM = {"Essonne"};
const char departement_92[] PROGMEM = {"Hauts-de-Seine"};
const char departement_93[] PROGMEM = {"Seine-Saint-Denis"};
const char departement_94[] PROGMEM = {"Val-de-Marne"};
const char departement_95[] PROGMEM = {"Val-d'Oise"};
const char departement_96[] PROGMEM = {"Haute-Corse"};
const char departement_97[] PROGMEM = {"Guadeloupe"};
const char departement_98[] PROGMEM = {"Martinique"};
const char departement_99[] PROGMEM = {"Guyane"};
const char departement_100[] PROGMEM = {"La Réunion"};
const char departement_101[] PROGMEM = {"Mayotte"};

const char *const chaines2[] PROGMEM = {departement_1, departement_2, departement_3, departement_4, departement_5, departement_6, departement_7, departement_8, departement_9, departement_10, departement_11, departement_12, departement_13, departement_14, departement_15, departement_16, departement_17, departement_18, departement_19, departement_20, departement_21, departement_22, departement_23, departement_24, departement_25, departement_26, departement_27, departement_28, departement_29, departement_30, departement_31, departement_32, departement_33, departement_34, departement_35, departement_36, departement_37, departement_38, departement_39, departement_40, departement_41, departement_42, departement_43, departement_44, departement_45, departement_46, departement_47, departement_48, departement_49, departement_50, departement_51, departement_52, departement_53, departement_54, departement_55, departement_56, departement_57, departement_58, departement_59, departement_60, departement_61, departement_62, departement_63, departement_64, departement_65, departement_66, departement_67, departement_68, departement_69, departement_70, departement_71, departement_72, departement_73, departement_74, departement_75, departement_76, departement_77, departement_78, departement_79, departement_80, departement_81, departement_82, departement_83, departement_84, departement_85, departement_86, departement_87, departement_88, departement_89, departement_90, departement_91, departement_92, departement_93, departement_94, departement_95, departement_96, departement_97, departement_98, departement_99, departement_100, departement_101};


int8_t longueurEcran = 16;


int8_t departement[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101};


int8_t tailleTableau = 10;

int8_t etatButton;
int8_t lastEtatButton;

int8_t etatLED;
int8_t lastEtatLED;

boolean isProgramRunning = false;

int8_t count_red;
int8_t count_blue;


void setup() {
  lcd.clear();
  Serial.begin(9600);         //Initialise moniteur srrie
  seg.begin();                  //Initialise l'affichage
  Wire.setClock(100000);        //Initialise la communication I2C
  seg.displayOff();

  randomSeed(analogRead(A0));// Initialise la génération aléatoire
  lcd.begin(16, 2);   //on initialise l'écran


  pinMode(LED, OUTPUT);
  digitalWrite(LED, HIGH);

  pinMode(BUTTON, INPUT);
  pinMode(BUTTON_ROUGE, INPUT);
  pinMode(BUTTON_BLEU, INPUT);



  pinMode(LED_ROUGE, OUTPUT);
  digitalWrite(LED_ROUGE, HIGH);

  pinMode(LED_BLEUE, OUTPUT);
  digitalWrite(LED_BLEUE, HIGH);

  strcpy_P(temp_string, (char *)pgm_read_word(&(chaines[0])));

  static uint8_t position = 0; // Position actuelle du défilement
  static uint32_t lastScrollTime = 0; // Temps du dernier défilement

  while (position < strlen(temp_string) + longueurEcran  && position != 184) {
    if (millis() - lastScrollTime >= 200) { // Défiler toutes les 200 millisecondes (ajustez la vitesse selon vos besoins)
      lastScrollTime = millis();

      lcd.clear();

      // Déterminer la sous-chaîne à afficher sur une seule ligne
      uint8_t start = position;
      uint8_t end = min(position + longueurEcran, strlen(temp_string)); // Utiliser la longueur de la chaîne comme limite

      // Créer un tableau temporaire pour stocker la sous-chaîne
      char temp_substring[longueurEcran + 1]; // +1 pour le caractère de fin de chaîne '\0'
      strncpy(temp_substring, temp_string + start, end - start);
      temp_substring[end - start] = '\0'; // Ajouter le caractère de fin de chaîne

      // Afficher la sous-chaîne correspondante sur la première ligne de l'écran LCD
      lcd.setCursor(0, 0);
      lcd.print(temp_substring);

      // Augmenter la position
      position++;

      delay(250); // Attendre 200 millisecondes avant le prochain défilement
    }
  }
}



void loop() {
  //  lcd.clear();
  etatButton = digitalRead(BUTTON);

  //Lecture etat bouton
  if (etatButton == HIGH && etatButton != lastEtatButton) {
    etatLED = !etatLED;
    digitalWrite(LED, etatLED);
    isProgramRunning = !isProgramRunning;

  }



  if (isProgramRunning) {
    etatLED = LOW;
    digitalWrite(LED, etatLED);
    Serial.println("ok");
    delay(500);
    while (tailleTableau >= 1) {
      seg.displayOn();              //Active le segment d'affichage
      int indice = random(0, 102);
      lcd.setCursor(0, 0);
      lcd.print("Departement: ");
      lcd.setCursor(7, 1);
      int8_t nombre = departement[indice];
      if (indice == 20) {
        lcd.print("2A");
      }
      else if (departement[indice] == 96) {
        lcd.print("2B");
      }
      else if (departement[indice] == 97) {
        lcd.print("971");
      }
      else if (departement[indice] == 98) {
        lcd.print("972");
      }
      else if (departement[indice] == 99) {
        lcd.print("973");
      }
      else if (departement[indice] == 100) {
        lcd.print("974");
      }
      else if (departement[indice] == 101) {
        lcd.print("975");
      }
      else if (nombre < 10) {
        String texte = String(nombre, DEC);
        texte = "0" + texte;
        lcd.print(texte);
      }
      else {
        lcd.print(departement[indice]); //lcd.print(departement[indice]);
      }


      seg.displayInt(3);
      delay(1300);
      seg.displayInt(2);
      delay(1000);
      seg.displayInt(1);
      delay(2200);
      seg.displayOff();

      lcd.clear();


      lcd.setCursor(0, 0);
      lcd.print("Reponse: ");
      lcd.setCursor(0, 1);

      
      strcpy_P(temp_string2, (char *)pgm_read_word(&(chaines2[departement[indice] - 1]))); // Copie le texte à partir du PROGMEM dans temp_string
      if (digitalRead(BUTTON_ROUGE) == HIGH) {
        count_red++;
        delay(300);
        Serial.println(count_red);
        Serial.println("boucle");
      }
      Serial.println("marche");
      lcd.print(temp_string2);



      delay(2000);
      lcd.clear();

      delay(500);

      // Suppression du département du tableau en décalant les éléments suivants
      for (int i = indice; i < tailleTableau - 1; i++) {
        departement[i] = departement[i + 1];
      }

      tailleTableau--;
      if (tailleTableau == 0) {
        lcd.setCursor(4, 0);
        lcd.print("SCORE");
        delay(2000);
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("BLEU  :");
        lcd.setCursor(8, 0);
        lcd.print(count_blue);
        lcd.setCursor(0, 1);
        lcd.print("ROUGE :");
        lcd.setCursor(8, 1);
        lcd.print(count_red);
        delay(10000);
        lcd.clear();
      }
    }
  } else {
    etatLED = HIGH;
    digitalWrite(LED, etatLED);



  }

}

Voici le code entier

Je n'ai pas vu dans ton code, le moment ou tu incrémente.
quel est le nom de la variable qui contient ton compteur ?

Sinon la première fois que tu appuis sur bouton et que isProgramRunning passe à True.
tu reviendra pour tester l'état de ton bouton à peu près 20s plus tard.
Cela est-il vraiment ce que tu veux faire?

je simplifierais le stockage des départements sous forme de structure quitte à occuper un peu plus de place en flash (vous avez du rab, même sur UNO). Conserver le N° du département n'est pas forcément nécessaire puisque le tableau est trié donc l'indice + 1 dans le tableau donnera le N° de département mais il me semblait que la corse c'était 2A et 2B non (auquel cas il faudrait une chaîne aussi pour N° si on peut avoir des lettres) ?

struct Departement {
  const uint8_t numero;
  const char nom[30];
};

static const Departement lesDepartements[]  PROGMEM = {
  { 1, "Ain"},
  { 2, "Aisne"},
  { 3, "Allier"},
  { 4, "Alpes-de-Haute-Provence"},
  { 5, "Hautes-Alpes"},
  { 6, "Alpes-Maritimes"},
  { 7, "Ardeche"},
  { 8, "Ardennes"},
  { 9, "Ariege"},
  {10, "Aube"},
  {11, "Aude"},
  {12, "Aveyron"},
  {13, "Bouches-du-Rhone"},
  {14, "Calvados"},
  {15, "Cantal"},
  {16, "Charente"},
  {17, "Charente-Maritime"},
  {18, "Cher"},
  {19, "Correze"},
  {20, "Corse-du-Sud"},
  {21, "Cote-d'Or"},
  {22, "Cotes-d'Armor"},
  {23, "Creuse"},
  {24, "Dordogne"},
  {25, "Doubs"},
  {26, "Drome"},
  {27, "Eure"},
  {28, "Eure-et-Loir"},
  {29, "Finistere"},
  {30, "Gard"},
  {31, "Haute-Garonne"},
  {32, "Gers"},
  {33, "Gironde"},
  {34, "Herault"},
  {35, "Ille-et-Vilaine"},
  {36, "Indre"},
  {37, "Indre-et-Loire"},
  {38, "Isere"},
  {39, "Jura"},
  {40, "Landes"},
  {41, "Loir-et-Cher"},
  {42, "Loire"},
  {43, "Haute-Loire"},
  {44, "Loire-Atlantique"},
  {45, "Loiret"},
  {46, "Lot"},
  {47, "Lot-et-Garonne"},
  {48, "Lozere"},
  {49, "Maine-et-Loire"},
  {50, "Manche"},
  {51, "Marne"},
  {52, "Haute-Marne"},
  {53, "Mayenne"},
  {54, "Meurthe-et-Moselle"},
  {55, "Meuse"},
  {56, "Morbihan"},
  {57, "Moselle"},
  {58, "Nievre"},
  {59, "Nord"},
  {60, "Oise"},
  {61, "Orne"},
  {62, "Pas-de-Calais"},
  {63, "Puy-de-Dome"},
  {64, "Pyrenees-Atlantiques"},
  {65, "Hautes-Pyrenees"},
  {66, "Pyrenees-Orientales"},
  {67, "Bas-Rhin"},
  {68, "Haut-Rhin"},
  {69, "Rhone"},
  {70, "Haute-Saone"},
  {71, "Saone-et-Loire"},
  {72, "Sarthe"},
  {73, "Savoie"},
  {74, "Haute-Savoie"},
  {75, "Paris"},
  {76, "Seine-Maritime"},
  {77, "Seine-et-Marne"},
  {78, "Yvelines"},
  {79, "Deux-Sevres"},
  {80, "Somme"},
  {81, "Tarn"},
  {82, "Tarn-et-Garonne"},
  {83, "Var"},
  {84, "Vaucluse"},
  {85, "Vendée"},
  {86, "Vienne"},
  {87, "Haute-Vienne"},
  {88, "Vosges"},
  {89, "Yonne"},
  {90, "Territoire de Belfort"},
  {91, "Essonne"},
  {92, "Hauts-de-Seine"},
  {93, "Seine-Saint-Denis"},
  {94, "Val-de-Marne"},
  {95, "Val-d'Oise"},
  {96, "Haute-Corse"},
  {97, "Guadeloupe"},
  {98, "Martinique"},
  {99, "Guyane"},
  {100, "La Réunion"},
  {101, "Mayotte"},
};
const size_t nbDepartements = sizeof lesDepartements / sizeof * lesDepartements;


void setup() {
  Serial.begin(115200); Serial.println();
  Serial.print(F("Nombre de départements : ")); Serial.println(nbDepartements);
  for (size_t i = 0; i < nbDepartements; i++) {
    Serial.print("départements #"); Serial.write('\t'); Serial.print(pgm_read_byte(&lesDepartements[i].numero));
    Serial.write('\t'); Serial.println((const __FlashStringHelper*) lesDepartements[i].nom);
  }
}

void loop() {}

pour le reste je prendrais une bibliothèque pour les boutons et c'est typiquement une définition de programme qui se prête bien à la programmation par machine à états (cf mon tuto éventuellement)

Ma variable s’appelle count_red. J’ai envie musiques bouton, un qui lance le programme et un autre qui sert de compteur.

Je n’ai pas fais de structure car je prends un nombre aléatoire. Ce nombre aléatoire correspond à mon indice. J’affiche le département correspondant à cet indice, ensuite je supprime ce département de ma liste. De cette manière il n’est pas possible d’afficher 2x le même département. Avec les structure je n’arrivais pas à faire en sorte que le département n’apparaissent pas 2 fois.

Du coup, il faut que ton bouton soit enfoncé exactement au bon moment, c'est à dire 5s après le premier appuis qui active isProgramRunning

Si tu utilise des délais, tu force la temporalité des actions que tu fait.
En l'occurrence tu attends le temps cumulé de tous tes délais, puis tu interroge l'état du bouton.

Pour avoir une bonne réactivité sur un bouton, tu dois interroger l'état très régulièrement.
Dans ton cas, soit tu fait comme @J-M-L, ce qui est le meilleur façon de faire de mon point de vue.
Soit tu utilise une fonction qui ferra le bon nombre de délais unitaire cours (200ms maximum) et entre chaque délais pour arriver au temps qui t'intéresse tu demande l'état de ton bouton.
je pourrais te faire un petit exemple, si cela t'intéresse?

vous pouvez décrire le fonctionnement spécifique attendu ?

l'utilisateur ne rentre rien, l'arduino sert juste à afficher les N° de départements de manière aléatoire mais sans répétition et les boutons servent juste comme compteurs de point - c'est ça ?

oui je voudrais bien un exemple :slight_smile:

Le but de ce programme est de crée un programme pour apprendre les départements français. Le jeu consiste à afficher un département sur l’écran LCD puis on a décompte de 3s sur l’afficheur 7-segments. Ensuite la réponse s’affiche et si l’utilisateur a donné parlement la bonne réponse, il appuie sur le bouton poussoir pour compter ces pts.

Lors du lancement du programme, j’affiche les consignes du jeu sur l’écran LCD. On a une LED verte allume qui se trouve prêt du 1er bouton poussoir. Pour commencer le jeu, l’utilisateur doit appuyé sur le 1er bouton poussoir de la LED verte.
Le jeu démarre.
On prends un nombre aléatoire compris entre 0 et la taille de la liste des départements -1. Ce nombre correspondra a l’indice du département qu’on prendra dans la liste. Ensuite on retire cette élément de la liste des numéros de département. De cette manière un même département ne peut être appelé 2 fois.
Une fois le numéros du département affiché et le décompte de 3 seconde. On affiche le nom du département correspondant au numéro. C’est à ce moment qu’intervient les deux autres boutons poussoirs. Qui sert à comptabiliser les points de 2 joueurs
Est ce que c’est plus claire ?

un truc un peu comme cela alors

je vous laisse regarder si ça peut donner des idées

il y a une constante qui dit combien de question on veut dans une partie au début du code

// CHANGER ICI LE NOMBRE DE QUESTIONS DANS UNE PARTIE
constexpr uint8_t nombreDeQuestions = 5;

il faut appuyer sur le bouton jouer pour lancer le jeu et passer à la question suivante

(à améliorer, par exemple pas de scrolling pour le Nom des dept qui sont éventuellement tronqués à l'affichage)

--- le code pour mémoire ---

#include <Wire.h>
#include <hd44780.h>                       // main hd44780 header
#include <hd44780ioClass/hd44780_I2Cexp.h> // i2c expander i/o class header
#include <OneButton.h>

// CHANGER ICI LE NOMBRE DE QUESTIONS DANS UNE PARTIE
constexpr uint8_t nombreDeQuestions = 5;


hd44780_I2Cexp lcd;

const int LCD_COLS = 20;
const int LCD_ROWS = 4;

const byte pinLedVerte = 3;
const byte pinBuzzer = 9;
OneButton boutonJouer(2);
OneButton boutonJoueur1(12);
OneButton boutonJoueur2(11);
unsigned long scoreJ1, scoreJ2;
bool scoreJ1Fait, scoreJ2Fait;

enum : uint8_t {DEBUT, QUESTION, DECOMPTE, ATTENTE, FIN} etat = DEBUT;

struct Departement {
  const char indicatif[4];
  const char nom[30];
};

static const Departement lesDepartements[]  PROGMEM = {
  {"1", "Ain"},
  {"2", "Aisne"},
  {"3", "Allier"},
  {"4", "Alpes-de-Haute-Provence"},
  {"5", "Hautes-Alpes"},
  {"6", "Alpes-Maritimes"},
  {"7", "Ardeche"},
  {"8", "Ardennes"},
  {"9", "Ariege"},
  {"10", "Aube"},
  {"11", "Aude"},
  {"12", "Aveyron"},
  {"13", "Bouches-du-Rhone"},
  {"14", "Calvados"},
  {"15", "Cantal"},
  {"16", "Charente"},
  {"17", "Charente-Maritime"},
  {"18", "Cher"},
  {"19", "Correze"},
  {"2A", "Corse-du-Sud"},
  {"2B", "Haute-Corse"},
  {"21", "Cote-d'Or"},
  {"22", "Cotes-d'Armor"},
  {"23", "Creuse"},
  {"24", "Dordogne"},
  {"25", "Doubs"},
  {"26", "Drome"},
  {"27", "Eure"},
  {"28", "Eure-et-Loir"},
  {"29", "Finistere"},
  {"30", "Gard"},
  {"31", "Haute-Garonne"},
  {"32", "Gers"},
  {"33", "Gironde"},
  {"34", "Herault"},
  {"35", "Ille-et-Vilaine"},
  {"36", "Indre"},
  {"37", "Indre-et-Loire"},
  {"38", "Isere"},
  {"39", "Jura"},
  {"40", "Landes"},
  {"41", "Loir-et-Cher"},
  {"42", "Loire"},
  {"43", "Haute-Loire"},
  {"44", "Loire-Atlantique"},
  {"45", "Loiret"},
  {"46", "Lot"},
  {"47", "Lot-et-Garonne"},
  {"48", "Lozere"},
  {"49", "Maine-et-Loire"},
  {"50", "Manche"},
  {"51", "Marne"},
  {"52", "Haute-Marne"},
  {"53", "Mayenne"},
  {"54", "Meurthe-et-Moselle"},
  {"55", "Meuse"},
  {"56", "Morbihan"},
  {"57", "Moselle"},
  {"58", "Nievre"},
  {"59", "Nord"},
  {"60", "Oise"},
  {"61", "Orne"},
  {"62", "Pas-de-Calais"},
  {"63", "Puy-de-Dome"},
  {"64", "Pyrenees-Atlantiques"},
  {"65", "Hautes-Pyrenees"},
  {"66", "Pyrenees-Orientales"},
  {"67", "Bas-Rhin"},
  {"68", "Haut-Rhin"},
  {"69", "Rhone"},
  {"70", "Haute-Saone"},
  {"71", "Saone-et-Loire"},
  {"72", "Sarthe"},
  {"73", "Savoie"},
  {"74", "Haute-Savoie"},
  {"75", "Paris"},
  {"76", "Seine-Maritime"},
  {"77", "Seine-et-Marne"},
  {"78", "Yvelines"},
  {"79", "Deux-Sevres"},
  {"80", "Somme"},
  {"81", "Tarn"},
  {"82", "Tarn-et-Garonne"},
  {"83", "Var"},
  {"84", "Vaucluse"},
  {"85", "Vendee"},
  {"86", "Vienne"},
  {"87", "Haute-Vienne"},
  {"88", "Vosges"},
  {"89", "Yonne"},
  {"90", "Territoire de Belfort"},
  {"91", "Essonne"},
  {"92", "Hauts-de-Seine"},
  {"93", "Seine-Saint-Denis"},
  {"94", "Val-de-Marne"},
  {"95", "Val-d'Oise"},
  {"971", "Guadeloupe"},
  {"972", "Martinique"},
  {"973", "Guyane"},
  {"974", "La Reunion"},
  {"975", "Mayotte"},
};
constexpr size_t nbDepartements = sizeof lesDepartements / sizeof * lesDepartements;

uint8_t ordreInterrogation[nbDepartements];
size_t indiceInterrogation;
unsigned long chrono, decompte;
const unsigned long dureeAttente = 3000;

void configurationInitiale() {
  etat = DEBUT;
  digitalWrite(pinLedVerte, HIGH);
  lcd.clear();
  lcd.print(F("--- DEPARTEMENTS ---"));
  lcd.setCursor(0, 1); lcd.print(F("appuyer sur le"));
  lcd.setCursor(5, 2);  lcd.print(F("bouton vert"));
  lcd.setCursor(10, 3);  lcd.print(F("pour JOUER"));
}

void preparerJeu() {
  lcd.clear();
  lcd.setCursor(0, 3); lcd.print(F("J1: 000"));
  lcd.setCursor(13, 3); lcd.print(F("J2: 000"));

  // on initialise dans l'ordre
  for (size_t i = 0; i < nbDepartements; i++) ordreInterrogation[i] = i;

  // on fait un mélange des éléments (algo de Fisher-Yates)
  for (size_t i = nbDepartements - 1; i > 0; --i) {
    size_t r = random(0, i + 1);
    uint8_t tmpSwap = ordreInterrogation[i];
    ordreInterrogation[i] = ordreInterrogation[r];
    ordreInterrogation[r] = tmpSwap;
  }
  scoreJ1 = scoreJ2 = 0;
  indiceInterrogation = 0;
  digitalWrite(pinLedVerte, LOW);
  etat = QUESTION;
}

void afficherScore() {
  char buf[10];
  snprintf(buf, sizeof buf, "%03lu", scoreJ1);
  lcd.setCursor(4, 3); lcd.print(buf);
  snprintf(buf, sizeof buf, "%03lu", scoreJ2);
  lcd.setCursor(17, 3); lcd.print(buf);
}

void debut() {
  switch (etat) {
    case DEBUT: preparerJeu(); break;
    case ATTENTE:
      digitalWrite(pinLedVerte, LOW);
      scoreJ1Fait = false;
      scoreJ2Fait = false;
      if (++indiceInterrogation >= nombreDeQuestions)
        etat = FIN;
      else
        etat = QUESTION;
      break;
    default: break;
  }
}

void scoreJoueur1() {
  if (!scoreJ1Fait && etat == ATTENTE) {
    scoreJ1Fait = true;
    scoreJ1++;
    afficherScore();
  }
}

void scoreJoueur2() {
  if (!scoreJ2Fait && etat == ATTENTE) {
    scoreJ2Fait = true;
    scoreJ2++;
    afficherScore();
  }
}

void setup() {
  pinMode(pinLedVerte, OUTPUT);
  pinMode(pinBuzzer, OUTPUT);
  randomSeed(analogRead(A0));
  boutonJouer.attachClick(debut);
  boutonJoueur1.attachClick(scoreJoueur1);
  boutonJoueur2.attachClick(scoreJoueur2);
  Serial.begin(115200); Serial.println();

  int result = lcd.begin(LCD_COLS, LCD_ROWS);
  if (result) {
    Serial.print("LCD initialization failed: ");
    Serial.println(result);
    hd44780::fatalError(result);
  }
  lcd.print(F("--- DEPARTEMENTS ---"));
  configurationInitiale();
}

void loop() {
  boutonJouer.tick();
  boutonJoueur1.tick();
  boutonJoueur2.tick();

  switch (etat) {
    case DEBUT: // on attend l'appui du bouton vert
      break;

    case QUESTION:
      lcd.setCursor(0, 0); for (uint8_t i = 0; i < LCD_COLS; i++) lcd.write(' '); // on efface la ligne de question
      lcd.setCursor(0, 1); for (uint8_t i = 0; i < LCD_COLS; i++) lcd.write(' '); // on efface la ligne de réponse
      lcd.setCursor(0, 2); for (uint8_t i = 0; i < LCD_COLS; i++) lcd.write(' '); // on efface la ligne de réponse

      lcd.setCursor(0, 0); lcd.print(F("Dept "));
      lcd.print((const __FlashStringHelper*) lesDepartements[ordreInterrogation[indiceInterrogation]].indicatif);
      lcd.print(F(" ?"));

      decompte = chrono = millis();
      etat = DECOMPTE;
      break;

    case DECOMPTE:
      if (millis() - decompte >= 100) {
        char buf[10];
        snprintf(buf, sizeof buf, "%4lu", (dureeAttente - (millis() - chrono)));
        lcd.setCursor(16, 0); lcd.print(buf);
        decompte = millis();
        tone(pinBuzzer, (dureeAttente - (millis() - chrono)), 15);
      }
      if (millis() - chrono >= dureeAttente) { // temps écoulé
        tone(pinBuzzer, 2000, 50); delay(60);
        tone(pinBuzzer, 3000, 50);
        char buf[LCD_COLS + 1];
        snprintf(buf, sizeof buf, "(%03u/%03u)", indiceInterrogation + 1, nombreDeQuestions );
        lcd.setCursor(11, 0); lcd.print(buf); // on efface le décompte

        strncpy_P(buf, lesDepartements[ordreInterrogation[indiceInterrogation]].nom, LCD_COLS );
        buf[LCD_COLS] = '\0';
        lcd.setCursor(0, 1);  lcd.print(buf); // affichage tronqué éventuellement...

        digitalWrite(pinLedVerte, HIGH);
        etat = ATTENTE;
      }
      break;

    case ATTENTE:      // on attend l'appui du bouton vert
      break;

    case FIN: // le jeu est fini
      lcd.clear();
      lcd.setCursor(0, 3); lcd.print(F("J1: 000"));
      lcd.setCursor(13, 3); lcd.print(F("J2: 000"));
      if (scoreJ1 > scoreJ2) {
        lcd.print(F("Joueur 1 VAINQUEUR"));
      } else if (scoreJ1 < scoreJ2) {
        lcd.print(F("Joueur 2 VAINQUEUR"));
      } else {
        lcd.print(F("JOUEURS EX AEQUO"));
      }
      afficherScore();

      etat = DEBUT;
      digitalWrite(pinLedVerte, HIGH);
      break;
  }

}

Vous oubliez les départements d'outre mer -la Réunion a pour numéro 974; là le stockage par numéro (qui ne permettait aux Corses que de massacre un préfet à la fois) devient pafaitemet inefficace... Il faut mettre une clef sur 3 caractères, coincidant avec les numéros de déprtement uniquement en France Metropolitaine.

Pour arriver à saisir rapidement une telle clef dans un jeu avec seulement des boutons et un LCD à deux lignes, ça risque d'être héroîque...

Et voila ce qui ne va pas : le delay bloque le micro pendant 2 secondes.
2 secondes où il ne peut rien faire.

La solution : utiliser millis().
Comparer les exemples blink qui utilise delay() et blink_without_delay qui utilise millis()

1 Like

Vous voulez dire comme cela ?

….
Cf le wokwi ci dessus

Si vous avez lu son explication il n’y a pas de saisie

Un peu comme la fonction tirageAvecRemise (pour PC/RPi) cidessous:

#ifdef PC
#include <stdint.h>
#include <stdio.h>
#include <math.h>
/* PC est une option  transmise à la compilation g++ -DPC ...
#endif
constexpr uint8_t TAILLELIBELLE = 40;
constexpr uint8_t TAILLECLE = 4;
struct Departement {
  // const uint8_t numero;

  const char code[TAILLECLE +1 ]; // 3 pour l'OutreMer + 1 pour null terninated
  const char nom[TAILLELIBELLE];
};

#ifdef PC
static const Departement lesDepartements[]  = {
#else
static const Departement lesDepartements[]  PROGMEM = {
#endif
  {" 1", "Ain"},
  {" 2", "Aisne"},
  {" 3", "Allier"},
  {" 4", "Alpes-de-Haute-Provence"},
  {" 5", "Hautes-Alpes"},
  {"2A", "Corse du Nord"},
  {"2B", "Corse du Sud"},
  {"2582", "Iles de la Desolation /Kerguelen"},
  {"974", "La Reunion"}
};
void tirageAvecRemise(const int taille, const int longueurUtile,
                      uint16_t numeros[]) {
  int lg = taille;
  for (uint8_t i = 0; i < longueurUtile; i++) {
#ifdef PC
    uint16_t aEchanger = i + rand() % lg;
#else
    afaire = pour arduino // rand n'et pas le même AFAIK
#endif
    uint16_t aux = numeros[aEchanger];
    numeros[aEchanger] = numeros[i];
    numeros[i] = aux;
#ifdef PC
    printf("%d {%d %d} ",aEchanger, numeros[i], numeros[aEchanger]); 
#endif
    lg--;
  }
}

#ifdef PC
void imprimer(uint16_t numero) {
  printf("%s %s\n", lesDepartements[numero].code,
                     lesDepartements[numero].nom);
}
int main() {
  const int taille = sizeof(lesDepartements) / sizeof(lesDepartements[0]) ; 
  printf("Taille : %d\n", taille);
  uint16_t numeros[taille];
//  uint8_t unusedTest =5; // teste le Makefile
  for (uint8_t i = 0; i < taille; i++) numeros[i] = i;
  tirageAvecRemise(taille, taille, numeros);
#ifdef yyy
  for (uint8_t i = 0; i < taille; i++) {
    uint16_t aEchanger = i + rand() % longueurUtile;
    printf("%d ", aEchanger); 
    uint16_t aux = numeros[aEchanger];
    numeros[aEchanger] = numeros[i];
    numeros[i] = aux;
    printf("{%d %d} ", numeros[i], numeros[aEchanger]); 
    longueurUtile--;
  }
#endif
  printf("\n");
 for (uint8_t i = 0; i < taille; i++) imprimer(numeros[i]);

return(0);
}
#endif

A noter que si vous mettez une temporisation fixe entre l'affichage de deux departements, quelqu'un ayant le sens du rythme peut gagner" en répondant machinalement -un peu comme dans les Temps Modernes- (et il n'apprendra certainement pas les départements). Mettre des départements fantaisistes, avec une structure de données à peine plus compliquée, permet d'éviter de donner un bon score à cette réponse machinale

1 octet de trop dans le code :wink:

dans le wokwi que j'ai posté précédemment j'ai pris une approche différente : je remplis comme vous un tableau dans l'ordre de 0 au nombre de départements moins un et j'applique ensuite un mélange (dit de Fisher-Yates) sur le tableau. Je n'ai ensuite qu'à parcourir ce tableau en l'utilisant comme table d'indirection pour parcourir tous les départements qu'une seule fois, dans un ordre aléatoire. ça évite d'avoir à remettre en ordre le tableau à chaque fois.

1 Like

Si tu regardes dans le code, j’ai mis des conditions pour l’es département d’outre mer pour qu’ils soient bien traités.

ok j’essaierai demain merci