[help] création de menu LCD

Alors voici mon but : créer un simple menu sur un LCD 20*4 permettant de me déplacer en utilisant un bouton select pour la sélection (matérialisée par une flèche) et un bouton OK pour la validation.
Après avoir cherché sur les différents forum je n’ai trouvé aucune solution simple à ce problème.
J’ai donc décidé d’en faire un à l’aide de boucle, et même si dans ma logique :~ ça devrait fonctionner mais dans la pratique ça ne marche pas =(.
Si quelqu’un à une idée j’achète :wink:

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

const int btn_ok = 7;
const int btn_sel = 13;	

int etat_btn_ok; 
int etat_btn_sel;
int position = 0; 


void setup () 
{

  pinMode(btn_ok, INPUT);
  pinMode(btn_sel,INPUT);
  lcd.begin(20,4);
  lcd.clear();

}




void loop ()
{

  lcd.setCursor(5,2);
  lcd.print("MENU V0.3");

  etat_btn_ok = digitalRead(btn_ok);
  if (etat_btn_ok == LOW)	
  {
    menu();
  }
  
}


void menu()
{
  while(1){                   //création boucle infinie pour eviter de revenir à loop
    delay(1000);              //en cas d'appui un peu long lors de l'étape précédente
    
    lcd.clear();	
    lcd.setCursor(3,0);
    lcd.print("menu1");
    lcd.setCursor(3,1);
    lcd.print("menu2");
    lcd.setCursor(3,2);
    lcd.print("menu3");
    lcd.setCursor(3,3);
    lcd.print("retour");
    etat_btn_ok = digitalRead(btn_ok);
    etat_btn_sel = digitalRead(btn_sel);

    if(etat_btn_sel == LOW)	
    {
      position++;             //en cas d'appui sur le bouton select on incremente la position
    }


    if(position > 3)       //retour à zero car il n'y a que 4 positions (0,1,2 et 3)
    {
      position = 0;
    }

    if(position = 0)      //si la position est 0 la flèche s'affiche en face de menu1
    {
      lcd.setCursor(2,1);
      lcd.print(" ");
      lcd.setCursor(2,0);
      lcd.print((char)126);
    }

    if(position = 1)     //si la position est 0 la flèche s'affiche en face de menu2
    {
      lcd.setCursor(2,0);
      lcd.print(" ");
      lcd.setCursor(2,2);
      lcd.print(" ");
      lcd.setCursor(2,1);
      lcd.print((char)126);
    }

    if(position = 2)              //si la position est 0 la flèche s'affiche en face de menu3
    {
      lcd.setCursor(2,3);
      lcd.print(" ");
      lcd.setCursor(2,1);
      lcd.print(" ");
      lcd.setCursor(2,2);
      lcd.print((char)126);
    }

    if(position = 3)         //si la position est 0 la flèche s'affiche en face de retour
    {
      lcd.setCursor(2,2);
      lcd.print(" ");
      lcd.setCursor(2,3);
      lcd.print((char)126);
    }

    if((position = 0) && (etat_btn_ok == LOW))  //si position = 0 et appui sur ok on va sur menu1
    {
      menu1();
    }

    if((position = 1) && (etat_btn_ok == LOW))   //si position = 0 et appui sur ok on va sur menu2
    {
      menu2();
    }

    if((position = 2) && (etat_btn_ok == LOW))   //si position = 0 et appui sur ok on va sur menu3
    {
      menu3();
    }  

    if((position = 3) && (etat_btn_ok == LOW))   //si position = 0 et appui sur ok on retourne à la page précédente
    {
      loop();
    }

  }
}

void menu1()
{
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("menu1");
  delay(10000);
}

void menu2()
{
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("menu2");
  delay(10000);
}


void menu3()
{
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("menu3");
  delay(10000);
}

bonjour,

mais dans la pratique ça ne marche pas

a paumé ma boule de cristale ce soir :) ququimarchepas? :D

hé bien mon menu s'affiche, avec la flèche sur la position 4(retour) et je ne peut ni changer la sélection :fearful: ni valider celle sur lequel il est bloqué :fearful:

ou alors il arrive directement sur l'affichage d'un menu aléatoirement, le 2 puis le trois et retour au mode de sélection

Hello,

Je ne vois pas de détection de changement d’état LOW->High ou High->Low, juste “Low”. Ce qui veut dire que tant que ton bouton est dans cette position, ça va cycler en permanence.

Oui mais c'est un bouton poussoir donc il revient à son état initial après appui

Oui, mais comment sais-tu que le bouton a été appuyé ? Je ne vois nulle part un test de l'état "HIGH" et de cette transition.

Donc, tant que ton bouton n'est pas poussé, tes menus cyclent.

+1

En plus faire appel à loop() alors qu'on y est déjà c'est pas recommandé du tout ... Surtout qu'avec la remarque de de XavierMiller tu vas imbriquer des milliers de loop() dans loop() en quelques secondes et comme disent certains, "y'en a qui ont eu des problèmes"

Il faut que tu reprennes ta reflexion : quand je fais des menu je procède ainsi :

#define MENU 1
#define SELECT 2

void loop() {

GestionBouton()
GestionAffichage();

}

void GestionBouton(){

if(digitalread(MonBoutonMenu)==LOW || digitalread(MonBoutonSelect)==LOW){

BoutonPressed = digitalRead(MonBoutonMenu)==LOW?MENU;SELECT;

GestionMenu();

while(digitalread(MonBoutonMenu)==LOW || digitalread(MonBoutonSelect)==LOW)); //tant que mon bouton est appuyé je boucle

delay(50); //anti-rebond

}

}

void GestionMenu(){

if(ButtonPressed==SELECT){
Position++;
if(Position>3) Position=0;
}

if(ButtonPressed==MENU) MenuIndex=Position;
}

void GestionAffichage() {

if(millis()>Depart+40) { // un rafraichissement pour obtneir 25 images/s

Depart=millis();

AfficherFleche(Position);

switch(MenuIndex) {

case 0:
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("menu1");
break; 

case 1:
...
break; 

case 2:
...
break; 

case 3:
...
break; 
}
}

void AfficherFleche(int pos){

switch(pos) {

case 0:
...
break;

case 1:
...
break;

}

Pas testé evidemmment ;) j'ai pas tout mis non plus et j'ai repris par ci par la des bouts de ton code pour que tu te resitues, il faut donc completer mais le but c'est que tu comprennes ;)

BONJOUR,
Il me semble que typiquement, ce genre de programme consiste à déclencher une action quand on enfonce ou que l’on relâche un bouton poussoir. Ce n’est pas l’état « 0 » ou « 1 » qui devient significatif de l’événement attendu pour faire un traitement quelconque, mais LA TRANSITION.
Il importe donc dans ta boucle de base de surveiller to capteur (B.P.) jusqu’à ce qu’il se produise une transition. C’est uniquement dans ce cas que tu active ta procédure de traitement spécifique.
Vas voir le poste de kobelakers avec son problème de barrière infrarouge. J’y ai placé deux petits programmes élémentaires qui précisément traitent de la détection d’une transition par Arduino.
Amicalement : Nulentout.

Pour des boutons poussoir ce n'est forcement utile : tu détectes l'appuie, le relâchement ne déclenche pas d'action. A moins de vouloir que ton affichage reste dynamique quand tu appuies (pour afficher une température, une position ...) puisque dans le code que je propose il ne peut rien se passer en dehors de la boucle. Mais ça peut se résoudre très simplement en ajoutant GestionAffichage() dedans ;)

Cool merci, je vais tester ça ce soir. Par contre je ne comprend pas cette ligne de ton exemple: BoutonPressed = digitalRead(MonBoutonMenu)==LOW?MENU;SELECT;

Ah c'est du C :

Condition?Resultat si vrai : Resultat si faux;

La même ligne peut s'écrire :

if (digitalRead(MonBoutonMenu)==LOW) { BoutonPressed =MENU; } else { BoutonPressed =Select; }

j’ai bien suivi tes instruction et pense avoir compris l’idée :astonished:
désormais mon code ressemble à ça:

#include <LiquidCrystal.h>                //ajout de la lib LCD

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);    //definition des broches du LCD

const int btn_ok = 7;                    //definition des BP
const int btn_sel = 13;


int selection = 0;                  
int BoutonPressed;              //variable etat du bouton
int MenuIndex;
int Depart;

#define MENU 1
#define SELECT 2

void setup()
{
  pinMode(btn_ok, INPUT);        //les BP sont en entrée et le LCD est un 20*4
  pinMode(btn_sel, INPUT);
  lcd.begin(20,4);
  lcd.clear();
}

void loop() {

  lcd.clear();	                //on affiche le menu
  lcd.setCursor(3,0);
  lcd.print("menu1");
  lcd.setCursor(3,1);
  lcd.print("menu2");
  lcd.setCursor(3,2);
  lcd.print("menu3");
  lcd.setCursor(3,3);
  lcd.print("retour");

  GestionBouton();            //on lance les boucles de gestion
  GestionAffichage();
  GestionMenu();
  AfficherFleche();

}

void GestionBouton()        //boucle qui va detecter et enregistrer l'appui sur un BP
{

  if (digitalRead(btn_ok)==LOW)     //si ok appuyé on enregistre MENU
  { 
    BoutonPressed = MENU;
  } 
  if (digitalRead(btn_sel)==LOW)    //si sel appuye on enresgitre SELECT
  {
    BoutonPressed = SELECT;
  }

  GestionMenu();                //on va à la gestion du menu

    while(digitalRead(btn_ok)==LOW || digitalRead(btn_sel)==LOW); //tant que mon bouton est appuyé je boucle

  delay(50); //anti-rebond


}

void GestionMenu(){

  if(BoutonPressed==SELECT){            //si le bp select est actionné on incremente la valeur de selection
    selection++;
    if(selection>3) selection=0;        //on se limite à 3 car il n'y a que 4 valeurs pour la selection 0,1,2 et 3
  }

  if(BoutonPressed==MENU)               //si le BP ok est actionné on rentre la valeur de selection dans la variable menuindex
  {
    MenuIndex=selection;
  }
}

void GestionAffichage()     
{

  if(millis()>Depart+40)  // un rafraichissement pour obtneir 25 images/s

    Depart=millis();

  AfficherFleche();

  switch(MenuIndex) {    //on gère le passage dans un des menu

  case 0:
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("menu1");
    break; 

  case 1:
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("menu1");
    break; 

  case 2:
    lcd.clear();
    lcd.setCursor(0,1);
    lcd.print("menu2");
    break; 

  case 3:
    lcd.clear();
    lcd.setCursor(0,2);
    lcd.print("menu3");
    break; 
  }
}

void AfficherFleche(){         //on gère l'affiche de la flèche en fonction de la selection

  switch(selection) {

  case 0:
    lcd.setCursor(2,1);
    lcd.print(" ");
    lcd.setCursor(2,0);
    lcd.print((char)126);
    break;

  case 1:
    lcd.setCursor(2,0);
    lcd.print(" ");
    lcd.setCursor(2,2);
    lcd.print(" ");
    lcd.setCursor(2,1);
    lcd.print((char)126);
    break;

  case 2:
    lcd.setCursor(2,3);
    lcd.print(" ");
    lcd.setCursor(2,1);
    lcd.print(" ");
    lcd.setCursor(2,2);
    lcd.print((char)126);
    break;

  case 3:
    lcd.setCursor(2,2);
    lcd.print(" ");
    lcd.setCursor(2,3);
    lcd.print((char)126);
    break;
  }
}

mais le LCD reste sur l’affichage du menu sans flèche et sans changement en cas d’appui sur un BP :~

XavierMiller: Oui, mais comment sais-tu que le bouton a été appuyé ? Je ne vois nulle part un test de l'état "HIGH" et de cette transition.

Donc, tant que ton bouton n'est pas poussé, tes menus cyclent.

oui mais lors d'un appui je n'ai aucune réaction alors que mon bouton est bien à l'état LOW, donc pas besoin de tester un etat HIGH?

Nan pas besoin.

Sinon il manque quelque trucs (peut-être qu'ils manquaient dans mon code aussi) :

Accolades pour la condition :

if(millis()>Depart+40) { // un rafraichissement pour obtneir 25 images/s

    Depart=millis();

  AfficherFleche();

  switch(MenuIndex) {    //on gère le passage dans un des menu

  case 0:
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("menu1");
    break; 

  case 1:
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("menu1");
    break; 

  case 2:
    lcd.clear();
    lcd.setCursor(0,1);
    lcd.print("menu2");
    break; 

  case 3:
    lcd.clear();
    lcd.setCursor(0,2);
    lcd.print("menu3");
    break; 
  }
}
}

Sinon dans ta loop tout est mélangé : si tu fait des print alors que tu gère l'affichage ensuite c'est normal que tu ne vois rien ... De plus certaine fonction ne doivent pas être gérées ici :

void loop() {

  GestionBouton();            //on lance les boucles de gestion
  GestionAffichage();

}

Ca ne sert à rien d'appeler GestionFleche() qui est géré par gestion affichage, pareil pour gestionMenu() qui ne sert que lorsqu'on appuie sur un bouton. Avec ce genre de code, c'est non seulement très facile de jongler entre les menus, mais en plus c'est optimisé au niveau de la charge du micro. Il ne bosse que quand il faut, c'est à dire lorsqu'un rafraichissement est necessaire (et encore ici il y en a trop, on pourrait appeler GestionAffichage() uniquement lorsqu'un bouton est appuyé) ou lorsqu'un bouton est appuyé.

Enfin pour la variable Depart, ne pas oublier de l'initialisé à 0 à la fin du setup() et elle doit être de type unsigned long (millis() renvoyant un unsigned long ça va buguer au bout d'un certain temps)

En tout cas merci pour tes réponses précises et rapide!!!
Je vais tester ça 8)

Je risque de passer pour un gros neuneu :roll_eyes: mais même après avoir suivi tes recommandations ça ne fonctionne pas

#include <LiquidCrystal.h>                //ajout de la lib LCD

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);    //definition des broches du LCD

const int btn_ok = 7;                    //definition des BP
const int btn_sel = 13;


int selection = 0;                  
int BoutonPressed;              //variable etat du bouton
int MenuIndex;
unsigned long Depart;

#define MENU 1
#define SELECT 2

void setup()
{
  pinMode(btn_ok, INPUT);        //les BP sont en entrée et le LCD est un 20*4
  pinMode(btn_sel, INPUT);
  lcd.begin(20,4);
  lcd.clear();
  lcd.setCursor(3,0);
  lcd.print("menu1");
  lcd.setCursor(3,1);
  lcd.print("menu2");
  lcd.setCursor(3,2);
  lcd.print("menu3");
  lcd.setCursor(3,3);
  lcd.print("retour");
}

void loop() {

  
  GestionBouton();            //on lance les boucles de gestion
  GestionAffichage();
  
}

void GestionBouton()        //boucle qui va detecter et enregistrer l'appui sur un BP
{

  if (digitalRead(btn_ok)==LOW)     //si ok appuyé on enregistre MENU
  { 
    BoutonPressed = MENU;
  } 
  if (digitalRead(btn_sel)==LOW)    //si sel appuye on enresgitre SELECT
  {
    BoutonPressed = SELECT;
  }

  GestionMenu();                //on va à la gestion du menu

    while(digitalRead(btn_ok)==LOW || digitalRead(btn_sel)==LOW); //tant que mon bouton est appuyé je boucle

  delay(50); //anti-rebond


}

void GestionMenu(){

  if(BoutonPressed==SELECT){            //si le bp select est actionné on incremente la valeur de selection
    selection++;
    if(selection>3) selection=0;        //on se limite à 3 car il n'y a que 4 valeurs pour la selection 0,1,2 et 3
  }

  if(BoutonPressed==MENU)               //si le BP ok est actionné on rentre la valeur de selection dans la variable menuindex
  {
    MenuIndex=selection;
  }
}




void GestionAffichage()     
{
 
  if(millis()>Depart+40)  {// un rafraichissement pour obtneir 25 images/s

    Depart=millis();

  AfficherFleche();

  switch(MenuIndex) {    //on gère le passage dans un des menu

  case 0:
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("menu1");
    break; 

  case 1:
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("menu1");
    break; 

  case 2:
    lcd.clear();
    lcd.setCursor(0,1);
    lcd.print("menu2");
    break; 

  case 3:
    lcd.clear();
    lcd.setCursor(0,2);
    lcd.print("menu3");
    break; 
  }
}
}
void AfficherFleche(){         //on gère l'affiche de la flèche en fonction de la selection

  switch(selection) {

  case 0:
    lcd.setCursor(2,1);
    lcd.print(" ");
    lcd.setCursor(2,0);
    lcd.print((char)126);
    break;

  case 1:
    lcd.setCursor(2,0);
    lcd.print(" ");
    lcd.setCursor(2,2);
    lcd.print(" ");
    lcd.setCursor(2,1);
    lcd.print((char)126);
    break;

  case 2:
    lcd.setCursor(2,3);
    lcd.print(" ");
    lcd.setCursor(2,1);
    lcd.print(" ");
    lcd.setCursor(2,2);
    lcd.print((char)126);
    break;

  case 3:
    lcd.setCursor(2,2);
    lcd.print(" ");
    lcd.setCursor(2,3);
    lcd.print((char)126);
    break;
  }
}

Oui mais "ça fonctionne pas" ça m'aide pas ;)

J'ai noté une autre erreur au passage : dans gestion bouton il faut un if à l'entrée :

void GestionBouton()        //boucle qui va detecter et enregistrer l'appui sur un BP
{

if(digitalRead(btn_ok)==LOW || digitalRead(btn_sel)==LOW){

  if (digitalRead(btn_ok)==LOW)     //si ok appuyé on enregistre MENU
  { 
    BoutonPressed = MENU;
  } 
  if (digitalRead(btn_sel)==LOW)    //si sel appuye on enresgitre SELECT
  {
    BoutonPressed = SELECT;
  }

  GestionMenu();                //on va à la gestion du menu

    while(digitalRead(btn_ok)==LOW || digitalRead(btn_sel)==LOW); //tant que mon bouton est appuyé je boucle

  delay(50); //anti-rebond
}

}

Si ça marche pas encore, utilise le port série pour débuguer : il suffit de placer aux endroits clés des Serial.print() pour savoir où ça bloque ou l'état des variables importantes

Bonjour, Au risque d'insister, je pense que "tu vas tourner en rond". :) (Jeu de mots stupide faisant allusion à la boucle de base) Je crois pouvoir affirmer que ton programme ne fonctionnera pas car tu prends en compte un état et non une transition. (Je précise : "Je crois" car je n'ai pas analysé dans le détail toutes les proposition qui précèdent, sans compter qu'avec la syntaxe du langage C j'ai encore pas mal de tourments)

Plus avant, (Quelques posts avant celui-ci) je t'ai expliqué que c'est la TRANSITION qu'il te faut tester. Je t'ai également donné un lien sur lequel tu trouveras deux programmes Arduino qui chez moi ont fait leurs preuves, tant avec une fourche optique qu'avec un bouton poussoir ou un inverseur.

Testes au moins l'un de mes deux programmes, tu vas certainement gagner du temps.

B@tto: Oui mais "ça fonctionne pas" ça m'aide pas ;)

J'ai noté une autre erreur au passage : dans gestion bouton il faut un if à l'entrée :

void GestionBouton()        //boucle qui va detecter et enregistrer l'appui sur un BP
{

if(digitalRead(btn_ok)==LOW || digitalRead(btn_sel)==LOW){

  if (digitalRead(btn_ok)==LOW)     //si ok appuyé on enregistre MENU   {     BoutonPressed = MENU;   }   if (digitalRead(btn_sel)==LOW)    //si sel appuye on enresgitre SELECT   {     BoutonPressed = SELECT;   }

  GestionMenu();                //on va à la gestion du menu

    while(digitalRead(btn_ok)==LOW || digitalRead(btn_sel)==LOW); //tant que mon bouton est appuyé je boucle

  delay(50); //anti-rebond }

}




Si ça marche pas encore, utilise le port série pour débuguer : il suffit de placer aux endroits clés des Serial.print() pour savoir où ça bloque ou l'état des variables importantes

Ok merci je check ça ce soir