Premier programme et remise en question ...

Bonjour à tous , voilà je débute dans votre monde depuis 2 semaines et j'adore ça !

seulement je me lance dans un petit projet et je m'y perd ...

Je dois avoir de mauvaises pratiques (forcement ...) et je viens ici dans un but pédagogique ,
Je souhaite apprendre et recevoir des critiques sur ce qui ne vas pas dans le but d'être autonome et de me faire plaisir :slight_smile:

je vous explique le projet , c'est pour animer des parties de paintball en foret , une bombe factice qui aurait 2 modes ,

Le premier va être ASSAUT , une équipe défend, l'autre doit venir amorcer la bombe factice

Le seconde mode va être Domination, les deux équipes devront se ruer sur la bombe factice et appuyer sur le bouton poussoir correspondant à leur couleur , cela enclenchera un chronos (un pour les bleu , un pour les rouges) et le vainqueur sera celui qui aura le plus de temps ....

j'utilise la fonction millis() pour gérer le temps vu que j'ai appris que la fonction delay() "bloque" le code, ceci dans le but que mes boutons poussoirs soient réactifs aux joueurs !

ces deux modes devront être personnalisable sur la durée entre autres ... je ferai évoluer ses fonctions au fur et a mesure que mes compétences augmenteront (si si je l'espère et je m'accroche !)

voici le code actuel (entier), j'arrive a déterminer en utilisant le port série ou bug le code mais je n'ai pas la logique assez developper pour en trouver la cause ... ma question vient donc se porter sur : pourquoi
dans ma fonction "attente_game" l'affichage n'est même pas présent sur mon LCD ?

dans l'état actuel je fais afficher un "hello !" dans le but de me situer dans le code surtout , puis rien ne se passe ... et si j'appuis sur le bouton (pour l'instant j'en utilise un seul pour simplifier et apprendre) rien ne se passe ... si je maintient le bouton un peu j'ai "temps restant : 00:00:03" soit le loop ...

Pour que les choses soient claire pour vous je recap sur l'idée voulue :

  • Mise sous tension

  • Affichage d'un message bonjour

  • on demande si on veut lancer l'avant game (petit délais laissant les joueurs se mettre en place sur le terrain et qui donnera un bip a la fin pour debut de partie)

  • on appuis sur le bouton pour lancer l'avant game

  • début de partie (exemple 20 min)

  • durant ça, le cœur du programme s’exécute avec assaut ou domination (pour l'instant je code juste assaut, une difficulté à la fois ^^)

  • si l'attaquant amorce la bombe, compte a rebours (qui devra être modulable) durant lequel l'attaquant doit maintenir son effort pour gagner la partie

-fin de partie par chrono ou si une équipe est parvenue a amorcer la bombe

Je remercie d'avance ceux qui prendront le temps de m'aider dans mon apprentissage :slight_smile:

je poste le code en plusieurs messages à la suite ... 9000 caractères obligent !

#include <LiquidCrystal.h>

// Variable de Pin
const int btn = 6 ;
const int buzzer = 8;
const int led = 7;
const int btn_blue = 9;

//Boolean d'états ( 0 = inactif | 1 = Actif )
boolean avant_partie;
boolean fin_de_partie;


// Variable gestion bouton blue
int etat_btn_blue = 0;

//Variable gestion appuis bouton (int btn)

int etat_btn = 0; // Etat du bouton poussoir
int compteur_btn = 0;
int etat_btn_precedant = 0;


// Variable temps de partie
int temps;
int heure;
int minute;
int seconde;

//Variable attente partie

int temps_att;
int heure_att;
int minute_att;
int seconde_att;


unsigned long currentMillis;
unsigned long startMillis;


// Liste des fonctions:
void bip ();
void Display();
void redLedBlink ();
void attente_partie ();

//On initialise la librairie avec les pin connecté de l'écran
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);


// -------------------------------- Début du Setup ------------------------
void setup()
{
  startMillis = millis(); // on démarre le stockage du temps qui passe

  // initialisation de l'écran LCD
  lcd.begin(16, 2);

  // initialisation de la communication avec le port série
  Serial.begin(9600);

  // paramètre des PIN
  pinMode(led, OUTPUT);
  pinMode(buzzer, OUTPUT);
  pinMode(btn, INPUT_PULLUP);
  pinMode(btn_blue, INPUT_PULLUP);

  // test du buzzer
  digitalWrite(buzzer, HIGH);
  delay(100);
  digitalWrite(buzzer, LOW);

  // test de la LED
  digitalWrite(led, HIGH);
  delay(100);
  digitalWrite(led, LOW);

  // valeur temps d'attente
  heure_att = 0;
  minute_att = 0;
  seconde_att = 5;

  // valeur du compte à rebours
  heure = 0;
  minute = 0;
  seconde = 3;


  // Formule pour condenser le temps HH : MM : SS en millisecondes
  temps = ( seconde + (60 * minute) + (3600 * heure));
}
// -------------------------------- Fin du Setup ------------------------


// -------------------------------- Début du Loop ------------------------
void loop() {

  lcd.clear();
  lcd.setCursor(2, 0);
  lcd.print("hello !");
  delay(1000);
  lcd.clear();
  
  avant_partie = 0; // 0 = n'a pas eu lieu , 1 = fait
  fin_de_partie = 0;
  
  if ( avant_partie != 1 ) {
    Serial.print("debug");
    attente_game ();

    avant_partie = 1;
   
  }

  /* etat_btn = digitalRead(btn); // Lecture du Statut du bouton
    lcd.setCursor(3, 0);
    lcd.print("En Attente");
    lcd.setCursor(2, 1);
    lcd.print("D'Activation");*/

  else /*(avant_partie == 1 && fin_de_partie == 0 )*/ {    // Si on appuie sur le bouton

    lcd.clear(); // Efface LCD
    currentMillis = millis(); // on demarre le stockage du temps
    
    while (fin_de_partie == 0 && avant_partie != 1) { // Boucle continue tant que le Temps total n'est pas égal a zero
      if (millis() > (currentMillis + 1000)) { // si une seconde s'est écoulé

        bip(); // on démarre la fonction bip buzzer
        redLedBlink(); // on démarre le clignotement de la led

        seconde = seconde - 1; // Décrémente les secondes
        temps = temps - 1; // Décrémente Temps total secondes
        currentMillis = currentMillis + 1000;  // incrémente la valeur de référence milliseconde
      }
      if (minute > 0) { // si les minutes sont à zero
        if (seconde < 0) { // et si les secondes sont à zero
          minute--; // // On décrémente 1 secondes
          seconde = 59; // et on retourne à 59
        }
      }
      if (heure > 0) { // si les heures sont à zero
        if (minute <= 0) { // et si les minutes sont à zero
          if (temps == 3600 * heure - 1) { // si le temps total est un multiple de 3600 secondes
            heure--; // on décrémente les heures
            minute = 59; //  on retourne les minutes à 59
            seconde = 59; // et on retourne les secondes à 59
          }
        }
      }

      // display affichage
      lcd.setCursor(1, 0);
      lcd.print("Temps Restant:");
      lcd.setCursor(4, 1);
      lcd.print("00:00:00");  // Affiche Template Compte à rebours

      // Imprime les minutes sur la collone 4 si seconde sup?rieur ? 10, et sur la colonne 5 si inf?rieur ? 10:
      if (heure >= 10) {
        lcd.setCursor(4, 1);
      }
      else {
        lcd.setCursor(5, 1);
      }
      lcd.print(heure);

      // Imprime les minutes sur la collone 4 si seconde sup?rieur ? 10, et sur la colonne 5 si inf?rieur ? 10:
      if (minute >= 10) {
        lcd.setCursor(7, 1);
      }
      else {
        lcd.setCursor(8, 1);
      }
      lcd.print(minute);

      // Imprime les seconde sur la collone 7 si seconde sup?rieur ? 10, et sur la colonne 8 si inf?rieur ? 10:
      if (seconde >= 10) {
        lcd.setCursor(10, 1);
      }
      else {
        lcd.setCursor(11, 1);
      }
      lcd.print(seconde);
      if (temps <= 0) { // Si le temps arrive ? 0
        lcd.clear();
        lcd.setCursor(4, 0);
        lcd.print("BOOM"); // Affiche BOOM
      }
      delay(980); // Délais de rafraichisement LCD
    }

    fin_de_partie = 1;
  }
  if (fin_de_partie == 1) {
    display_fin_compte_a_rebours ();
    lcd.clear();
    delay(100);
  }

  lcd.clear();
}





void display_fin_compte_a_rebours () {
  do {
    lcd.clear(); // on reset l'affichage
    lcd.setCursor(4, 0);
    lcd.print("Victoire");
    delay(200);
  } while ( etat_btn == HIGH);

  lcd.clear();
}
// passe cette fonction en commentaire pour test direct dans le loop
/*void display_compte_a_rebours() {
  lcd.setCursor(1, 0);
  lcd.print("Temps Restant:");
  lcd.setCursor(4, 1);
  lcd.print("00:00:00");  // Affiche Template Compte à rebours

  // Imprime les minutes sur la collone 4 si seconde sup?rieur ? 10, et sur la colonne 5 si inf?rieur ? 10:
  if (heure >= 10) {
    lcd.setCursor(4, 1);
  }
  else {
    lcd.setCursor(5, 1);
  }
  lcd.print(heure);

  // Imprime les minutes sur la collone 4 si seconde sup?rieur ? 10, et sur la colonne 5 si inf?rieur ? 10:
  if (minute >= 10) {
    lcd.setCursor(7, 1);
  }
  else {
    lcd.setCursor(8, 1);
  }
  lcd.print(minute);

  // Imprime les seconde sur la collone 7 si seconde sup?rieur ? 10, et sur la colonne 8 si inf?rieur ? 10:
  if (seconde >= 10) {
    lcd.setCursor(10, 1);
  }
  else {
    lcd.setCursor(11, 1);
  }
  lcd.print(seconde);
  if (temps <= 0) { // Si le temps arrive ? 0
    lcd.clear();
    lcd.setCursor(4, 0);
    lcd.print("BOOM"); // Affiche BOOM
  }
  }*/


// -------------------------------- Début de la fonction clignotement de led rouge ------------------------
void redLedBlink () { // initialisation de la fonction

  currentMillis = millis(); // on stock le temps qui passe

  if (currentMillis - startMillis >= 1000) { // si une seconde s'est écoulé
    digitalWrite(led, HIGH); // on allume la led rouge
    delay(100); // pendant 1/10 de seconde
    digitalWrite(led, LOW); // puis on l'éteint
  }
}
// -------------------------------- Fin de la fonction clignotement de led rouge --------------------------


// --------------------------------------- Début de la fonction bip ---------------------------------------

void bip () { // initialisation de la fonction bip

  currentMillis = millis(); // on stock le temps qui passe

  if (currentMillis - startMillis >= 1000) { // si une seconde s'est écoulé
    digitalWrite(buzzer, HIGH); // on allume le buzzer
    delay(50); // pendant 1/50 de secondes
    digitalWrite(buzzer, LOW); // puis on l'éteint
  }
}
// --------------------------------------- Fin de la fonction bip ---------------------------------------

void attente_game () {

  Serial.println("   attente game   ");
  lcd.clear();
  etat_btn = digitalRead(btn); // Lecture du Statut du bouton
  lcd.setCursor(2, 0);
  lcd.print("Lancer la");
  lcd.setCursor(0, 1);
  lcd.print("mise en place ?");
  
  if (etat_btn == LOW) {    // Si on appuie sur le bouton

    Serial.println("   Debug 01   ");

    lcd.clear(); // Efface LCD

    boolean attente_termine = 0;
    currentMillis = millis(); // on demarre le stockage du temps
    while (temps > 1 && fin_de_partie == 0 && attente_termine == 0) { // Boucle continue tant que le Temps total n'est pas égal a zero et que l'attente partie n'est pas écoulée
      if (millis() > (currentMillis + 1000)) { // si une seconde s'est écoulé

        seconde_att = seconde_att - 1; // Décrémente les secondes
        temps_att = temps_att - 1; // Décrémente Temps total secondes
        currentMillis = currentMillis + 1000;  // incrémente la valeur de référence milliseconde
      }
      if (minute_att > 0) { // si les minutes sont à zero
        if (seconde_att < 0) { // et si les secondes sont à zero
          minute_att--; // // On décrémente 1 secondes
          seconde_att = 59; // et on retourne à 59
        }
      }
      if (heure_att > 0) { // si les heures sont à zero
        if (minute_att <= 0) { // et si les minutes sont à zero
          if (temps_att == 3600 * heure - 1) { // si le temps total est un multiple de 3600 secondes
            heure_att--; // on décrémente les heures
            minute_att = 59; //  on retourne les minutes à 59
            seconde_att = 59; // et on retourne les secondes à 59
          }
        }
      }
      lcd.setCursor(1, 0);
      lcd.print("Temps Restant:");
      lcd.setCursor(4, 1);
      lcd.print("00:00:00");  // Affiche Template Compte à rebours

      // Imprime les minutes sur la collone 4 si seconde sup?rieur ? 10, et sur la colonne 5 si inf?rieur ? 10:
      if (heure >= 10) {
        lcd.setCursor(4, 1);
      }
      else {
        lcd.setCursor(5, 1);
      }
      lcd.print(heure);

      // Imprime les minutes sur la collone 4 si seconde sup?rieur ? 10, et sur la colonne 5 si inf?rieur ? 10:
      if (minute >= 10) {
        lcd.setCursor(7, 1);
      }
      else {
        lcd.setCursor(8, 1);
      }
      lcd.print(minute);

      // Imprime les seconde sur la collone 7 si seconde sup?rieur ? 10, et sur la colonne 8 si inf?rieur ? 10:
      if (seconde >= 10) {
        lcd.setCursor(10, 1);
      }
      else {
        lcd.setCursor(11, 1);
      }
      lcd.print(seconde);
      if (temps <= 0) { // Si le temps arrive ? 0
        lcd.clear();
        lcd.setCursor(4, 0);
        lcd.print("BOOM"); // Affiche BOOM
      }
      delay(980); // Délais de rafraichisement LCD
    }
  }
}

Je remercie d’avance ceux qui prendront le temps de m’aider dans mon apprentissage :slight_smile: [/code]

bonjour
déjà, merci d'avoir présenté correctement ton projet et posté ton code avec les balises, c'est agréable :slight_smile:

je n'ai pas le temps de lire ton code ce matin, mais il me semble au premier abord que le projet se prête bien à la programmation par machine à états.
je te conseille de lire le très bon cours/tuto de JML sur la question.

Et éventuellement si tu veux te simplifier la vie une fois que tu as saisi le concept, il y a une librairie pour ça.

Notez que if (millis() > (currentMillis + 1000)) { // si une seconde s'est écoulée n’est pas bon à cause du fait que currentMillis + 1000 peut faire un rollover (retourner vers 0)... (bon c’est dans une dans une cinquantaine de jours, pas sur que votre jeu dure si longtemps :slight_smile: ).

Pour la bonne approche, Il faut toujours procéder par soustraction if (millis() - currentMillis >= 1000) { // si une seconde s'est écoulée

Vous l’avez fait correctement dans la fonction bip(), pourquoi ne le faites-vous pas partout ? :slight_smile:

Sinon oui une machine à état permet de bien structurer ce genre de code

Merci pour vos premières infos ! Je répond depuis le tel (en pause au boulot) mais la machine a état a l'air d'être très intéressante

Bonjour

Effectivement voilà une entrée en matière qui donne envie d'aider.

Pour ma part je dirais après un survol rapide :

  1. millis() oui mais attention à ne pas utiliser une seule variable globale currentMillis pour plusieurs gestions temporelles distinctes.
    Dès que tu voudras gérer plusieurs tempo simultanément, il te faudra plusieurs variables.
    Le plus simple est d'avoir une variable par nature de temporisation

  2. il reste des delay()
    Faut tous les exterminer sans pitié jusqu'au dernier :smiling_imp:
    Un delay = un moment pendant lequel l'arduino ne pourra rien faire d'autre, ce qui ne peut que poser problème. Et même si "ça marche comme ça", cela t'embêtera dès que tu voudras ajouter une fonctionnalité.
    Et puis cela t'obligera à structurer ton code de manière plus modulaire.

Voici par exemple comment il est possible de gérer le clignotement de la led rouge :

...
//Variables globales pour gérer la led rouge
const int led = 7;
bool clignoter_led;// mettre à jour pour déclencher/arreter le clignotement.
bool led_allumee;
unsigned long millis_led;
const unsigned long duree_led_allumee = 100;
const unsigned long duree_led_eteinte = 900;

//Fonctions de gestion de la led rouge
void allumer_led() {
  led_allumee = true;
  digitalWrite(led, HIGH);
  millis_led = millis();
}

void eteindre_led() {
  led_allumee = false;
  digitalWrite(led, LOW);
  millis_led = millis();
}

void initialiser_led() {
  pinMode(led, OUTPUT);
  clignoter_led = false;
  eteindre_led();
}

void gerer_led() {
  if (clignoter_led) {
    unsigned long duree = millis() - millis_led;
    if (led_allumee) {
      if (duree >= duree_led_allumee) eteindre_led();
    } else {
      if (duree >= duree_led_eteinte) allumer_led();
    }
  } else {
    if (led_allumee) eteindre_led();
  }
}

void setup() {
  initialiser_led();
  ...
}

void loop() {
  gerer_led();
  ...
}
...

Avec ce code, la gestion de la led est complètement isolée et indépendante du reste du programme.
Il suffit alors à ce dernier de modifier la valeur de la variable clignoter_led à true ou false.

Bien évidemment, pour que cela fonctionne, on doit passer très souvent dans gerer_led()
La condition pour arriver à ce resultat est toujours la même : exterminer tous les delay()

Après, tu peux aussi utiliser une machine à états pour gérer le clignotement de la led, cela n'enlève rien au principe ci-dessus.

mince mon message depuis mon tel n'a pas été posté en entier ! je reprends depuis un vrai clavier ...

pour

bricofoy:
bonjour
déjà, merci d'avoir présenté correctement ton projet et posté ton code avec les balises, c'est agréable :slight_smile:

c'est la moindre des choses si on veut voler un peu de temps a celui qui survole mon topic mais merci à toi (j'ai même regardé si des membres se présentaient ... habitué des forums mais ici je n'ai pas vu le sujet ...)

bricofoy:
je n'ai pas le temps de lire ton code ce matin, mais il me semble au premier abord que le projet se prête bien à la programmation par machine à états.
je te conseille de lire le très bon cours/tuto de JML sur la question.

Merci du tuyau ! je vais de ce pas me pencher là dessus avant que mon code ne soit trop avancé !

J-M-L:
Notez que if (millis() > (currentMillis + 1000)) { // si une seconde s'est écoulée n’est pas bon à cause du fait que currentMillis + 1000 peut faire un rollover (retourner vers 0)... (bon c’est dans une dans une cinquantaine de jours, pas sur que votre jeu dure si longtemps :slight_smile: ).

Pour la bonne approche, Il faut toujours procéder par soustraction if (millis() - currentMillis >= 1000) { // si une seconde s'est écoulée

Vous l’avez fait correctement dans la fonction bip(), pourquoi ne le faites-vous pas partout ? :slight_smile:

Sinon oui une machine à état permet de bien structurer ce genre de code

Oups en effet ... pour être honnête j'ai trouvé un code pour la gestion d'un compte à rebours et je l'ai copié tout en l'écrivant moi même (pas de copier collé) et en essayant de le comprendre et de l'assimiler ... par contre les fonctions bip et redLedBlink c'est bibi et c'est le cours de openclassroom qui m'a appris a le faire ...
je n'ai pas fait attention à cela mais je prends note merci à toi !

bricoleau:
Bonjour

Effectivement voilà une entrée en matière qui donne envie d'aider.

Pour ma part je dirais après un survol rapide :

  1. millis() oui mais attention à ne pas utiliser une seule variable globale currentMillis pour plusieurs gestions temporelles distinctes.
    Dès que tu voudras gérer plusieurs tempo simultanément, il te faudra plusieurs variables.
    Le plus simple est d'avoir une variable par nature de temporisation

  2. il reste des delay()
    Faut tous les exterminer sans pitié jusqu'au dernier :smiling_imp:
    Un delay = un moment pendant lequel l'arduino ne pourra rien faire d'autre, ce qui ne peut que poser problème. Et même si "ça marche comme ça", cela t'embêtera dès que tu voudras ajouter une fonctionnalité.
    Et puis cela t'obligera à structurer ton code de manière plus modulaire.

Voici par exemple comment il est possible de gérer le clignotement de la led rouge :

...

//Variables globales pour gérer la led rouge
const int led = 7;
bool clignoter_led;// mettre à jour pour déclencher/arreter le clignotement.
bool led_allumee;
unsigned long millis_led;
const unsigned long duree_led_allumee = 100;
const unsigned long duree_led_eteinte = 900;

//Fonctions de gestion de la led rouge
void allumer_led() {
  led_allumee = true;
  digitalWrite(led, HIGH);
  millis_led = millis();
}

void eteindre_led() {
  led_allumee = false;
  digitalWrite(led, LOW);
  millis_led = millis();
}

void initialiser_led() {
  pinMode(led, OUTPUT);
  clignoter_led = false;
  eteindre_led();
}

void gerer_led() {
  if (clignoter_led) {
    unsigned long duree = millis() - millis_led;
    if (led_allumee) {
      if (duree >= duree_led_allumee) eteindre_led();
    } else {
      if (duree >= duree_led_eteinte) allumer_led();
    }
  } else {
    if (led_allumee) eteindre_led();
  }
}

void setup() {
  initialiser_led();
  ...
}

void loop() {
  gerer_led();
  ...
}
...




Avec ce code, la gestion de la led est complètement isolée et indépendante du reste du programme.
Il suffit alors à ce dernier de modifier la valeur de la variable clignoter_led à true ou false.

Bien évidemment, pour que cela fonctionne, on doit passer très souvent dans gerer_led()
La condition pour arriver à ce resultat est toujours la même : exterminer tous les delay()

Après, tu peux aussi utiliser une machine à états pour gérer le clignotement de la led, cela n'enlève rien au principe ci-dessus.

Ah oui je fais varier mon currentMillis pendant que je l'utilise en fait ... voilà le soucis ! je vais reprendre tout ça aussi ...

pour les delay ok, je vais les chasser un par un ^^ c'est vraiment une utilisation basique ce delay , on ne va pas le chercher souvent dans des programmes un peu plus complexe que faire clignoter une led simplement ?

pour ton code , je trouve ça bien d'utiliser les fonctions perso, ça rend le code plus lisible mais question, si je cree un onglet pour "gestion led rouge" et que j'y écris tout les codes nécessaires, l'utilisation des fonctions dans l'onglet principal va rester la même si la fonction appelé est dans un onglet particulier ?

merci à tous :slight_smile:

Je ne sais pas pour les onglets, car je ne suis pas fan de la chose.
Il me semble que c'est encore un artifice ajouté par l'IDE.

Perso je préfère tout regrouper dans une classe, que je peux ensuite déplier/replier en un clic dans mon éditeur de code C++ favori (= pas l'IDE Arduino, que je n'utilise que pour y coller le code + compil).

Et dans certains cas, déporter la classe dans une bibliothèque avec un .h et .cpp

Comme je ne savais pas si tu es familiarisé avec la programmation orientée objet, je t'ai mis un exemple avec variables globales et fonctions.
Mais c'est très facilement transposable en une classe.

je ne suis pas familiariser avec grand chose mais je recherche dès que je trouve une nouvelle chose ... donc là si tu me parle de classe tu parle de créer sa "librairie" c'est ça ?

j'ai un peu touché a bracket et au java script ... il est vrai ue le logiciel de base arduino est bcp moins intuitif mais comme je televerse parfois 10 fois dans la minute pour corriger une boulette ... pour l'instant je m’efforce de rester là dessus et de tout écrire tampis ...

Grossièrement :

Quand on développe sous arduino, on a très souvent (voir tout le temps) besoin de faire "plusieurs choses à la fois".

Tout simplement parce qu'il y a toujours plusieurs fils branchés sur l'arduino, et qu'il peut/doit se passer quelque chose sur chacun d'eux n'importe quand. Une led, un bouton, des capteurs, un écran, c'est très classique.

L'arduino ne sait faire qu'une seule chose à la fois, mais il mouline très vite.
On peut donc le programmer de manière à zapper vite et en boucle sur tout ce qui l'entoure, ce qui donne au final l'impression de faire "plusieurs choses à la fois", à notre échelle humaine de perception du temps.

D'où l'évangélisation sur la suppresion des delay()

Une fois ceci posé, cela nous mène où ?
à un code structuré en fonctions, où chaque fonction réalise un petit bout ininterruptible de la logique d'ensemble.

Et pour s'y retrouver, quand on est dans une fonction, il est nécessaire de s'appuyer sur un jeu de variables globales qui permettent de définir l'état du système à un instant t.

On arrive donc à des fonctions "élémentaires" et des variables d'état, tel que dans mon exemple précédent.

La notion de classe ou d'objet, c'est l'étape d'après.
Cela consiste à regrouper ces variables et fonctions élémentaires associées, dans un contenant unique et cohérent.

Cela renforce l'isolation des différentes parties du code (donc aussi la fiabilité, car une fois la classe testée isolément, on peut s'appuyer dessus les yeux fermés sans mauvaise surprise).

C'est aussi pratique pour gérer plusieurs objets similaires. Par exemple ici : gérer deux leds clignotantes à des cadences différentes, basées sur le même code.

NB : ce n'est pas contradictoire avec la machine à états, qui est un outillage permettant de simplifier la mise en place de notions d'état et traitements élémentaires associés.

Ainsi par exemple, mon exemple ci-dessus pourrait devenir un truc du genre

//Paramètres de la led rouge
const int ledRouge_pin = 7;
const unsigned long ledRouge_duree_allumee = 100; 
const unsigned long ledRouge_duree_eteinte = 900; 

//Gestion de led clignotante
class led_clignotante() {

  private : //tout ce qui n'est pas accessible depuis l'exterieur de la classe
  
    int _pin;
    bool _allumee;
    unsigned long _millis;
    unsigned long _duree_allumee;
    unsigned long _duree_eteinte;

    void _allumer() {
      _allumee = true;
      digitalWrite(_pin, HIGH);
      _millis = millis();
    }

    void _eteindre() {
      _allumee = false;
      digitalWrite(_pin, LOW);
      _millis = millis();
    }

  public :

    bool clignoter;
  
    //Constructeur
    led_clignotante(int pin, unsigned long duree_allumee, unsigned long duree_eteinte) {
      _pin = pin;
      _duree_allumee = duree_allumee;
      _duree_eteinte = duree_eteinte;
    }
    
    void initialiser() {
      pinMode(_pin, OUTPUT);
      clignoter = false;
      _eteindre();
    }

    void gerer() {
      if (clignoter) {
        unsigned long duree = millis() - _millis;
        if (_allumee) {
          if (duree >= _duree_allumee) _eteindre();
        } else {
          if (duree >= _duree_eteinte) _allumer();
        }
      } else {
          if (_allumee) _eteindre();
      }
    }
};

//instanciation de l'objet ledRouge
led_clignotante ledRouge(ledRouge_pin, ledRouge_duree_allumee, ledRouge_duree_eteinte);

//et un deuxième qui montre l'intérêt des classes
led_clignotante blink(13, 500, 500);

void setup() {
  ledRouge.initialiser();
  blink.initialiser();
  blink.clignoter = true;
  ...
}

void loop() {
  ledRouge.gerer();
  blink.gerer();
  ...
}
...

Et enfin, la dernière étape est (éventuellement) de déporter la classe dans une bibliothèque, avec un fichier header .h et un fichier source .cpp

Cela permet de pouvoir réutiliser facilement la classe dans un autre programme

Mon exemple ci-dessus deviendrait alors

#include "led_clignotante.h"

//Paramètres de la led rouge
const int ledRouge_pin = 7;
const unsigned long ledRouge_duree_allumee = 100; 
const unsigned long ledRouge_duree_eteinte = 900; 

//instanciation de l'objet ledRouge
led_clignotante ledRouge(ledRouge_pin, ledRouge_duree_allumee, ledRouge_duree_eteinte);

//et un deuxième qui montre l'intérêt des classes
led_clignotante blink(13, 500, 500);

void setup() {
  ledRouge.initialiser();
  blink.initialiser();
  blink.clignoter = true;
  ...
}

void loop() {
  ledRouge.gerer();
  blink.gerer();
  ...
}
...

je comprends pour le principe de fonctionnement , dans l’utilisation il ne faut pas ralentir son loop quoi …

je regarde la librairie YASM là , et j’ai déjà quelque petits soucis …

/*
   BlinkLed.ino example file for yasm library.

   The purpose of this arduino sketch is to blink a led on pin 13.

   It creates a state machine with two states : ledOn, ledOff,
   and illustrate the use of elapsed(delay_in_ms) timing function to trigger
   state change.
*/


#include <yasm.h> //include the yasm library

#define OnDelay 100 //500ms led "On" state delay
#define OffDelay 900 //750ms led "Off" state delay

#define LedPin 7 //pin 13 because most arduino boards provide a led here
#define buzzer 8

YASM led; //declaration of the "led" state machine
YASM buzzer;

void setup()
{
  pinMode(LedPin, OUTPUT); //declare the led pin as output
  led.next(ledOn); //set the initial state of the led state machine
  pinMode(buzzer, OUTPUT);
  buzzer.next(buzzerON);

}

void loop()
{
  led.run();  //update the state machine
  buzzer.run();
}

//////////buzzer state machine/////////////
void buzzerOn()
{
  digitalWrite(buzzer, HIGH); //this is the "On" state so we turn on the led

  if (buzzer.elapsed(OnDelay)) //check if the delay for the "on" state is elapsed
    buzzer.next(buzzerOff); //if the delay is elapsed we switch to the "off" state
}

void buzzerOff()
{
  digitalWrite(buzzerPin, LOW); //this is the "Off" state so we turn off the led

  if (buzzer.elapsed(OffDelay)) //check if the delay for the "off" state is elapsed
    buzzer.next(buzzerOn); //if the delay is elapsed we switch to the "on" state
}
//////////led state machine/////////////

void ledOn()
{
  digitalWrite(LedPin, HIGH); //this is the "On" state so we turn on the led

  if (led.elapsed(OnDelay)) //check if the delay for the "on" state is elapsed
    led.next(ledOff); //if the delay is elapsed we switch to the "off" state
}

void ledOff()
{
  digitalWrite(LedPin, LOW); //this is the "Off" state so we turn off the led

  if (led.elapsed(OffDelay)) //check if the delay for the "off" state is elapsed
    led.next(ledOn); //if the delay is elapsed we switch to the "on" state
}

j’essaie de comprendre les bases mais voilà en voulant faire de même que “LedPin” avec mon buzzer , cela me renvoi une erreur " expected unqualified-id before numeric constant " sur la ligne 18 (celle du #define buzzer) …

j’ai du mal comprendre un truc là …

Normal. Le même nom buzzer est utilisé pour une constante et une variable.

#define buzzer 8
YASM buzzer;

@+

Forcement ... Merci :-[

Salut !

Bon je modifie un peu mon idée de départ pour crée d'abord le mode domination et j'ai une question d'ordre technique ...

je lance un compte à rebours de 20 min que je fait afficher sur un LCD 20x4 (I2C)

si une équipe capture elle va lancer son chrono de domination , et là mon code n'est pas opti du tout car les 2 chronos a l'écran ne sont pas régulier ...

j'ai procédé comme suit :

** Une fonction chrono , ou se trouve la décrémentation et l'affichage des 20min ,

** Une fonction chrono_blue , ou se trouve un compteur de temps si cette équipe domine (active son bouton), même principe calcul et affichage

** Une fonction chrono_Red , idem aussi ...

dans mon loop , je lance ma fonction "chrono" ... si une équipe domine ça va lancer la fonction correspondante mais vu que je dois d'abord passer par la boucle du "chrono" , je pense que c'est ça qui rend les chronos irréguliers ...

Quelle "forme" dois-je utiliser pour arriver a quelque chose de fonctionnel et fluide ? des décompte dans des focntion mais un seul affichage global toutes les secondes réglerai le soucis ?

je vous remercie d'avance :slight_smile:

il faudrait que tu poste ton code actuel pour savoir ce qui se passe...

Bonjour

Pour ma part, voici comment je procède pour des affichages LCD fluides :

En préambule un détail : les LCD I2C sont bien pratiques question connectique, mais la liaison est lente. Il faut environ 100 ms pour transmettre 20x4 = 80 caractères à afficher.

  1. je conserve en RAM arduino les 4 lignes de 20 caractères, en 4 variables globales char[20], en permanence à l'image exacte de ce qui est affiché sur l'écran.

  2. à haute fréquence, c'est-à-dire un fois par loop() dans un programme qui ne contient aucun delay :
    J'appelle une fonction qui compose intégralement les 4 lignes théoriques à afficher à cet instant, dans des variables temporaires.
    Puis je compare les variables temporaires aux variables globales, et s'il y a des différences je n'envoie au LCD que les caractères en écart.

Ainsi, le programme rafraichit très souvent l'affichage, sur le plan logique, mais ne descend vers le LCD qu'en cas de nécessité.

bricofoy:
il faudrait que tu poste ton code actuel pour savoir ce qui se passe…

justement je m’interroge sur la bonne façon de faire tourner plusieurs chronos … quand j’aurais un résultat un peu meilleur je posterai ça :confused:

bricoleau:
Bonjour

Pour ma part, voici comment je procède pour des affichages LCD fluides :

En préambule un détail : les LCD I2C sont bien pratiques question connectique, mais la liaison est lente. Il faut environ 100 ms pour transmettre 20x4 = 80 caractères à afficher.

  1. je conserve en RAM arduino les 4 lignes de 20 caractères, en 4 variables globales char[20], en permanence à l’image exacte de ce qui est affiché sur l’écran.

  2. à haute fréquence, c’est-à-dire un fois par loop() dans un programme qui ne contient aucun delay :
    J’appelle une fonction qui compose intégralement les 4 lignes théoriques à afficher à cet instant, dans des variables temporaires.
    Puis je compare les variables temporaires aux variables globales, et s’il y a des différences je n’envoie au LCD que les caractères en écart.

Ainsi, le programme rafraichit très souvent l’affichage, sur le plan logique, mais ne descend vers le LCD qu’en cas de nécessité.

ok donc tu penses que l’I2C pour l’utilisation choisie est limité ? je rafraîchi le LCD toutes les 980 ms … ça me semble jouable au vu de ce que tu me décris !

je comprend le principe pour l’écran mais pour l’instant je ne sais pas faire tout cela …

Je peux éventuellement utiliser 2 afficheurs 7 segments pour les chronos des 2 équipes et conserver le lcd juste pour le temps global et le statut de la bombe factice …

void affichage_lcd () {
  
  
  if (lcd_refresh) {
    
    //affichage sur port série
    //Time
    Serial.print(minute);
    Serial.print(" : ");
    Serial.print(seconde);
    
    Serial.print("  - Blue >>  ");
    
    //Blue time
    Serial.print(minuteBlue);
    Serial.print(" : ");
    Serial.print(secondeBlue);

    Serial.print("  - Red >>  ");
    
    //Red time
    Serial.print(minuteRed);
    Serial.print(" : ");
    Serial.println(secondeRed);
    
    //Affichage du texte (fixe)
    //Time
    lcd.setCursor(0, 0);
    lcd.print ("Time:");
    lcd.setCursor(8, 0);
    lcd.print(":");
    //Statut Bomb
    lcd.setCursor(0, 1);
    lcd.print ("Stat:");
    lcd.setCursor(6, 1);   
    lcd.print ("Disarmed");// cette ligne est juste là pour tester la présentation ( sera remplacer par la valeur d'une variable )
    //Blue Time
    lcd.setCursor(0, 2);
    lcd.print ("Blue:");
    lcd.setCursor(8, 2);
    lcd.print(":");
    //Red time
    lcd.setCursor(0, 3);
    lcd.print ("Red :");
    lcd.setCursor(8, 3);
    lcd.print(":");
    
    //============================== Bloc du TIME =============================
    if (seconde >= 1) { // S'il reste au moins une seconde


      if (minute > 9) { // si les les minutes sont supérieures à 9
        //time
        lcd.setCursor(6, 0);
        lcd.print(minute); // on affiche les minutes
        //Blue time
        lcd.setCursor(6, 2);
        lcd.print(minuteBlue);
        //Red time
        lcd.setCursor(6, 3);
        lcd.print(minuteRed);


      }

      else if (minute <= 0) { //si les minutes sont à zéro
        //Time
        lcd.setCursor(6, 0);
        lcd.print("00"); // on affiche 00
        //Blue time
        lcd.setCursor(6, 2);
        lcd.print("00");
        //Red time
        lcd.setCursor(6, 3);
        lcd.print("00");
      }
      else {
        //time
        lcd.setCursor(6, 0);
        lcd.print("0");
        lcd.setCursor(7, 0);
        lcd.print(minute);
        lcd.setCursor(8, 0);
        lcd.print(":");
        //Blue time
        lcd.setCursor(6, 2);
        lcd.print("0");
        lcd.setCursor(7, 2);
        lcd.print(minuteBlue);
        //Red time
        lcd.setCursor(6, 3);
        lcd.print("0");
        lcd.setCursor(7, 3);
        lcd.print(minuteRed);
      }
      if (seconde > 9) {
        //Time
        lcd.setCursor(9, 0);
        lcd.print(seconde);
        //Blue time
        lcd.setCursor(9, 2);
        lcd.print(secondeBlue);
        //Red time
        lcd.setCursor(9, 3);
        lcd.print(secondeRed);
      }
      else if (seconde <= 0) {
        //Time
        lcd.setCursor(9, 0);
        lcd.print("00");
        //Blue time
        lcd.setCursor(9, 2);
        lcd.print("00");
        //Red time
        lcd.setCursor(9, 3);
        lcd.print("00");
      }
      else {
        //Time
        lcd.setCursor(9, 0);
        lcd.print("0");
        lcd.setCursor(10, 0);
        lcd.print(seconde);
        lcd.setCursor(11, 0);
        lcd.print(" ");
        //Blue time
        lcd.setCursor(9, 2);
        lcd.print("0");
        lcd.setCursor(10, 2);
        lcd.print(secondeBlue);
        lcd.setCursor(11, 2);
        lcd.print(" ");
        //Red time
        lcd.setCursor(9, 3);
        lcd.print("0");
        lcd.setCursor(10, 3);
        lcd.print(secondeRed);
        lcd.setCursor(11, 3);
        lcd.print(" ");
      }
    }
  }
}
//======================================================================================================================================================

voilà le code actuel pour remonter les infos sur le LCD …

je revois le coté comptage du temps avec l’utilisation de la bibliothèque simple minuteur :slight_smile: (merci bricoleau au passage !)

Mais NON justement il ne faut pas "attendre d'avoir un code un peu meilleur" si le but est que l'on puisse t'aider à améliorer ton code, il faut bien le voir, bon sang...
Parceque pour afficher des chronos en secondes, il n'y a absolument aucun soucis avec le temps de transfert de l'I2C, donc c'est vraiment dans ton code que ça foire, et pour savoir justement ce qui foire, sans le code....

d'autant que si tu utilise ma librairies YASM, les fonctions de chronométrage son intégrées, reste juste à s'en servir...

Bonjour,

Dans un projet similaire, j'ai choisi de ne pas gérer directement le temps en utilisant des variables pour les secondes, minutes et heures. Même si dans mon cas je dispose d'une RTC pour enregistrer les événements au format epoch, je trouve qu'il est plus simple de travailler sur des cumuls de tranche de temps que l'on initialise au moment des événements (début de partie, pression sur un bouton, fin de partie,..).

L'affichage des chronomètres n'a besoin que d'une simple fonction que transforme le cumul des tranches et de la tranche en cours en une chaîne de string directement exploitable par l'afficheur ou son cache. Pour la fin de partie, il te suffit d'utiliser un simple timer qui se déclenche au bout de 20 minutes par exemple.

L'avantage est que tu n'as pas à gérer 3 variables de temps, dont 2 en base 60, par équipes. Il te suffit de faire des soustractions et des additions avec millis(). Je ne tiens pas compte de l'overload de ce paramètre dans la mesure où une partie ne dure que quelques dizaines de minutes et non pas 5 jours.

Edit:

équipe bleu:

Tps_Cum_Bleu = temps des tranches cumulées
Tps_Ref_Bleu = temps de référence pour le début de la tranche en cours

Le temps de possession total à l'instant t est égale à Tps_Cum_Bleu si le drapeau bleu n'est pas hissé, et on ajoute (temps réel - Tps_Ref_Bleu) dans le cas contraire.
Par temps réel, je parle de la valeur de millis() à l'instant t.
lorsque l'équipe bleu hisse son drapeau, on mémorise Tps_Ref_Bleu et on active la mise à jour automatique de l'affichage de son chronomètre.
Lorsqu'elle le perd, on ajoute le temps de la dernière tranche au temps cumulé de l'équipe. On fait une dernière mise à jour du chronomètre de l'équipe après avoir désactivé le mode de rafraichissement automatique du chronomètre.

La même chose pour l'équipe rouge et pour le chronomètre général deux variables Tps_Ref_Gen et Tps_Duree pour la durée de la partie. Tu as juste à détecter

millis()-Tps_Ref_Gen >= Tps_duree

Toutes les variables étant en millisecondes, une division par 1000 te donne le nombre de secondes. quelques divisions et soustractions dans une fonction et tu obtiens heures, minutes et secondes.

L'avantage est que tu travailles à la milliseconde. Non pas que cette résolution de temps soit nécessaire pour départager les équipes, mais elle te permettra d'éviter de perdre litéralement du temps lors des changements de drapeau. Il suffit d'appuyer sur le bouton vers la fin de la seconde pour perdre cette portion de temps qui ne pourra donc pas être cumulée.
La somme de ces pertes pourrait être relativement importante sur une partie longue et active.