Menu pour réglages des paramètres, besoin d'aide

Bonjour, à tous.
Je me suis lancé dans la programmation Arduino, il y a 2 jours.
C'est passionnant et j’espère bien réaliser mon projet.
J'ai créé un code très simple me permettant de faire fonctionner un moteur hydraulique ainsi que son électrovanne.
J'ai ajouté un keypad LCD qui indique à l'utilisateur l'état de la commande monter ou retour automatique, ainsi que si la chaine de sécurités est ouverte.
Tout cela fut simple et fonctionne parfaitement.

Cependant j'aimerais ajouter une option grâce au LCD
En appuyant sur select, je souhaite entrer dans un menu et régler la durée du retour automatique.
Ce qui donnerait :
ECRAN VIERGE (select) TPS RETOUR (select)

et serait lisible

TPS RETOUR
8Secondes

les boutons HAUT BAS me permettant de monter ou descendre le temps
les boutons SELECT LEFT me permettant de valider ou annuler la modification

Voila j'ai regardé des tutos et farfouillé le forum, mais généralement les menus en tuto sont directement sur le LCD alors que je souhaite uniquement le faire apparaitre lorsque l'utilisateur aura besoin de modifier des réglages.

Donc si vous avez des pistes ou bout de code sur lesquels je pourrais m'inspirer je suis preneur.

Je vous copie mon code actuel qui permet le fonctionnement moteur via relais et qui reconnais les touches du keypad me cela s’arrête la.

#include "LiquidCrystal.h"
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
int commande_monter = 0;
int commande_retour = 1;
int securite = 2;
int contact_moteur = 3;
enum {
  BUTTON_NONE, 
  BUTTON_UP,    
  BUTTON_DOWN,  
  BUTTON_LEFT,  
  BUTTON_RIGHT, 
  BUTTON_SELECT 
};

void setup() {
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.write("Digital Dock");
  delay (1000);
  lcd.setCursor(0, 1);
  lcd.write("v1.10");
  delay(5000);
  pinMode(commande_monter, INPUT);
  pinMode(commande_retour, INPUT);
  pinMode(securite, INPUT);
  pinMode(contact_moteur, OUTPUT);
  lcd.clear();
 
}

void loop()
{ 
while ((digitalRead(securite) == HIGH)&&(digitalRead(commande_monter) == HIGH))
{
commande_monter_levre();
}
while ((digitalRead(commande_retour) == HIGH)&&(digitalRead(securite) == HIGH))
{ 
commande_retour_auto();
}
if (digitalRead(securite) == LOW)
{ 
affichage_securites();
}
if ((digitalRead(securite) == HIGH)&&(digitalRead(commande_monter) == LOW)&&(digitalRead(commande_retour) == LOW))
{ 
commande_relache();
}

  switch (getPressedButton()) {
  case BUTTON_NONE:
    lcd.print(F("      "));
    break;

  case BUTTON_UP:
    lcd.print(F("UP    "));
    break;

  case BUTTON_DOWN:
    lcd.print(F("DOWN  "));
    break;

  case BUTTON_LEFT:
    lcd.print(F("LEFT  "));
    break;

  case BUTTON_RIGHT:
    lcd.print(F("RIGHT "));
    break;

  case BUTTON_SELECT:
    lcd.print(F("SELECT"));
    break;
  }
  delay(500);
}


byte getPressedButton() {
int value = analogRead(A0);
  if (value < 50)
    return BUTTON_RIGHT;
  else if (value < 250)
    return BUTTON_UP;
  else if (value < 410)
    return BUTTON_DOWN;
  else if (value < 570)
    return BUTTON_LEFT;
  else if (value < 850)
    return BUTTON_SELECT;
  else
    return BUTTON_NONE;
}

void commande_monter_levre()
{ 
lcd.setCursor(0, 0);
lcd.print("MONTER          ");
lcd.setCursor(0, 1);
lcd.print("                ");
digitalWrite(contact_moteur,HIGH);
}

void commande_retour_auto()
{ 
lcd.setCursor(0, 0);
lcd.print("RETOUR          ");
lcd.setCursor(0, 1);
lcd.print("AUTOMATIQUE     ");
digitalWrite(contact_moteur,HIGH);
delay (8000);
}

void commande_relache()
{ 
lcd.clear();
digitalWrite(contact_moteur,LOW); 
}

void affichage_securites()
{ 
lcd.setCursor(0, 0);
lcd.print("SECURITES       ");
lcd.setCursor(0, 1);
lcd.print("P1              ");
digitalWrite(contact_moteur,LOW);
}

Bonjour nemesis47

Est-ce-que c'est un écran comme celui-ci?
image

Pour ton problème, il faut définir 2 états ds ton programme, prgEtatFonctionnement et prgEtatMenu. Cet état serait commuté par pression sur le bouton select.

//------------------------------------- Etat du programme
enum {prgEtatFonctionnement, prgEtatMenu};
int programmeEtat = prgEtatFonctionnement;

Ainsi dans ta chaîne de test switch (getPressedButton())
la fonction de chaque case est dépendante de programmeEtat.

		case BUTTON_UP:
			switch (programmeEtat) {
				case prgEtatFonctionnement:
					lcd.print(F("UP    "));
					break;
				case prgEtatMenu:
					lcd.print(F("Var ++    "));
					break;
		}

Il faudrait aussi définir un timer pour sortir automatiquement de l'état prgEtatMenu au bout d'un temps défini.
Je n'ai pas pû faire l'essai "en vrai", je n'ai pas ce matériel (ou du moins je crois, il faut que je fouille!)

Cordialement
jpbbricole

Bonjour et merci de votre retour.
Oui c'est le modèle similaire de velleman, le VMA203.

Donc si je comprends bien, je dois mettre les commandes de void loop dans

prgEtatFonctionnement

et le menu dans

prgEtatMenu

qui seront donc ainsi (toujours si comprends bien) :

void prgEtatFonctionnement()
{ 
while ((digitalRead(securite) == HIGH)&&(digitalRead(commande_monter) == HIGH))
{
commande_monter_levre();
}
while ((digitalRead(commande_retour) == HIGH)&&(digitalRead(securite) == HIGH))
{ 
commande_retour_auto();
}
if (digitalRead(securite) == LOW)
{ 
affichage_securites();
}
if ((digitalRead(securite) == HIGH)&&(digitalRead(commande_monter) == LOW)&&(digitalRead(commande_retour) == LOW))
{ 
commande_relache();
}

et un void prgEtatMenu où je dois créer le menu

Ensuite question idiote mais à quoi sert le F dans :

lcd.print(F("UP    "));

Merci d'avance

Bonjour nemesis47

Le mieux est que je te fasse l'exemple complet, ce sera plus simple de comprendre les états.

A+
Cordialement
jpbbricole

Pour info sur la programmation en C++ et être totalement "propre" dans le respect des types:

quand vous déclarez un enum vous déclarez un nouveau type à part entière en C++ et donc le plus simple est de typer votre variable programmeEtat au type de l'énumération.

enum {prgEtatFonctionnement, prgEtatMenu} programmeEtat = prgEtatFonctionnement;

on peut aussi donner un nom au nouveau type dans la déclaration de l'enum, par exemple typeEtat

enum typeEtat {prgEtatFonctionnement, prgEtatMenu};
typeEtat programmeEtat = prgEtatFonctionnement;

et pour économiser de la place mémoire si besoin, on peut même (en C++, pas en C) donner le type numérique sous jacent à l'énumération : par défaut c'est un int donc 2 ou 4 octets suivant la plateforme, mais si vous n'avez que peu d'énumérations et que leur valeur n'est pas importante, on peut par exemple mettre cela sur un seul octet :

enum typeEtat : byte {prgEtatFonctionnement, prgEtatMenu};
typeEtat programmeEtat = prgEtatFonctionnement;

les 2 enumerators (au sein de l'enum) ne sont pas des fonctions, ce sont des constantes nommées


ce que propose @jpbbricole est tout a fait pertinent. Il s'agit de faire une petite machine à état toute simple (cf mon tuto éventuellement sur le sujet où je parle aussi des enum)

Bonjour nemesis47

Pour faire simple, sans le F("ABCDE"), ton texte est stocké en SRAM, avec, ton texte est stocké à la même place que le programme, dans la mémoire flash. Or cette SRAM est précieuse, c'est là que "tourne", il y en a très peu et ton programme plus il en a, plus il sera à l'aise.

J'ai essayé de faire quelque chose pour ton programme, comme je n'ai pas de clavier analogique comme sur ton shield, j'au dû "jongler" avec un potentiomètre, pas facile.
Pour faciliter la lecture du fonctionnement du programme j'ai ajouté des print dans la console à 115200. Tu pourras les supprimer quand tu veux. Il y a un timer, prgrEtatMenuChrono, qui sort de l'état prgEtatMenu après prgrEtatMenuTemps milliSecondes d'inaction des boutons, dans l'état prgEtatMenu

Le programme:

#include "LiquidCrystal.h"
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
int commande_monter = 0;
int commande_retour = 1;
int securite = 2;
int contact_moteur = 3;
enum {
  BUTTON_NONE,
  BUTTON_UP,
  BUTTON_DOWN,
  BUTTON_LEFT,
  BUTTON_RIGHT,
  BUTTON_SELECT
};
//------------------------------------- Etat du programme
enum {prgEtatFonctionnement, prgEtatMenu};
int programmeEtat = prgEtatFonctionnement;

unsigned long prgrEtatMenuChrono = millis();     // Timout du mode prgEtatMenu
unsigned long prgrEtatMenuTemps = 8000;     // Temps du timout du mode prgEtatMenu en milliSecondes

void setup() {
  Serial.begin(115200);

  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.write("Digital Dock");
  delay (1000);
  lcd.setCursor(0, 1);
  lcd.write("v1.10");
  delay(5000);
  pinMode(commande_monter, INPUT);
  pinMode(commande_retour, INPUT);
  pinMode(securite, INPUT);
  pinMode(contact_moteur, OUTPUT);
  lcd.clear();

}

void loop()
{
  while ((digitalRead(securite) == HIGH) && (digitalRead(commande_monter) == HIGH))
  {
    commande_monter_levre();
  }
  while ((digitalRead(commande_retour) == HIGH) && (digitalRead(securite) == HIGH))
  {
    commande_retour_auto();
  }
  if (digitalRead(securite) == LOW)
  {
    affichage_securites();
  }
  if ((digitalRead(securite) == HIGH) && (digitalRead(commande_monter) == LOW) && (digitalRead(commande_retour) == LOW))
  {
    commande_relache();
  }

  if (programmeEtat == prgEtatFonctionnement) {
    switch (getPressedButton()) {
      case BUTTON_NONE:
        lcd.print(F("      "));
        break;

      case BUTTON_UP:
        lcd.print(F("UP    "));
        Serial.println(F("UP"));
        break;

      case BUTTON_DOWN:
        lcd.print(F("DOWN  "));
        Serial.println(F("DOWN"));
        break;

      case BUTTON_LEFT:
        lcd.print(F("LEFT  "));
        Serial.println(F("LEFT"));
        break;

      case BUTTON_RIGHT:
        lcd.print(F("RIGHT "));
        Serial.println(F("RIGHT"));
        break;

      case BUTTON_SELECT:
        lcd.print(F("SELECT"));
        Serial.println(F("SELECT Etat Menu"));
        programmeEtat = prgEtatMenu;     // Changement d'état
        prgrEtatMenuChrono = millis();     // Faire un restart du timer à chaque action en mode prgEtatMenu
        break;
    }
  }

  if (programmeEtat == prgEtatMenu) {
    switch (getPressedButton()) {
      case BUTTON_NONE:
        lcd.print(F("      "));
        break;

      case BUTTON_UP:
        lcd.print(F("Var ++"));
        Serial.println(F("Variable ++"));
        break;

      case BUTTON_DOWN:
        lcd.print(F("Var --  "));
        Serial.println(F("Variable --"));
        break;

      case BUTTON_LEFT:
        lcd.print(F("ANNULER  "));
        Serial.println(F("Annuler"));
        break;

      case BUTTON_RIGHT:
        lcd.print(F("ACCEPTEER "));
        Serial.println(F("Accepter"));
        break;

      case BUTTON_SELECT:
        lcd.print(F("SELECT"));
        Serial.println(F("SELECT Etat Fonctionnement"));
        programmeEtat = prgEtatFonctionnement;     // Changement d'état
        break;
    }
    if (getPressedButton() != BUTTON_NONE)     // Si une touche a été pressée
    {
      Serial.println(F("Redemarrage timer"));
      prgrEtatMenuChrono = millis();     // Faire un restart du timer à chaque action en mode prgEtatMenu
    }
  }
  delay(500);

  //     Si plus d'activité en mode prgEtatMenu
  if ((millis() - prgrEtatMenuChrono >= prgrEtatMenuTemps) && programmeEtat == prgEtatMenu)
  {
    Serial.println(F("Retour automatique etat en fonctionnement"));
    programmeEtat = prgEtatFonctionnement;
  }
}


byte getPressedButton() {
  int value = analogRead(A0);
  if (value < 50)
    return BUTTON_RIGHT;
  else if (value < 250)
    return BUTTON_UP;
  else if (value < 410)
    return BUTTON_DOWN;
  else if (value < 570)
    return BUTTON_LEFT;
  else if (value < 850)
    return BUTTON_SELECT;
  else
    return BUTTON_NONE;
}

void commande_monter_levre()
{
  lcd.setCursor(0, 0);
  lcd.print("MONTER          ");
  lcd.setCursor(0, 1);
  lcd.print("                ");
  digitalWrite(contact_moteur, HIGH);
}

void commande_retour_auto()
{
  lcd.setCursor(0, 0);
  lcd.print("RETOUR          ");
  lcd.setCursor(0, 1);
  lcd.print("AUTOMATIQUE     ");
  digitalWrite(contact_moteur, HIGH);
  delay (8000);
}

void commande_relache()
{
  lcd.clear();
  digitalWrite(contact_moteur, LOW);
}

void affichage_securites()
{
  lcd.setCursor(0, 0);
  lcd.print("SECURITES       ");
  lcd.setCursor(0, 1);
  lcd.print("P1              ");
  digitalWrite(contact_moteur, LOW);
}

A ta disposition pour toute question, je ne suis pas sûre à 100% de mon programme :wink:.

Cordialement
jpbbricole

1 Like

juste un petit point, le delay(500) qui traine dans la loop() risque de nuire à la réactivité des boutons, peut-être qu'il ne sert pas ?

PS: le delay (8000); de commande_retour_auto() veut dire que la commande ne peut être interrompue

1 Like

Bonjour et merci à vous deux pour vos réponses.
Alors j'arrive à entrer dans mon menu et modifier le paramètre de tempo seulement j'ai deux soucis :
Comme il s'agit d'un delay en milliseconds, lorsque je passe de 9 à 10 mille tout se décalle et l'affichage peut indiquer 20000 si je descends en réalité à 2000.
Existe t'il un moyen d'afficher que des secondes au pire avec un zero devant me donnant : 01 02 03...10 11 12 13...etc

Aussi le paramètres modifiés fonctionne cependant il revient a sa valeur initial en cas de redémarrage.

Je vous joint le code ATTENTION il y a du collage d'autres codes pour arriver à mes fins du coup c'est le bazar

String menuItems[] = {"TempoRetour", "Menu 2", "Menu 3", "Menu 4", "Menu 5"};

// Navigation button variables
int readKey;
int tempora = 8000;

int commande_monter = 0;
int commande_retour = 1;
int securite = 2;
int contact_moteur = 3;

// Menu control variables
int menuPage = 0;
int maxMenuPages = round(((sizeof(menuItems) / sizeof(String)) / 2) + .5);
int cursorPosition = 0;

// Creates 3 custom characters for the menu display
byte downArrow[8] = {
  0b00100, //   *
  0b00100, //   *
  0b00100, //   *
  0b00100, //   *
  0b00100, //   *
  0b10101, // * * *
  0b01110, //  ***
  0b00100  //   *
};

byte upArrow[8] = {
  0b00100, //   *
  0b01110, //  ***
  0b10101, // * * *
  0b00100, //   *
  0b00100, //   *
  0b00100, //   *
  0b00100, //   *
  0b00100  //   *
};

byte menuCursor[8] = {
  B01000, //  *
  B00100, //   *
  B00010, //    *
  B00001, //     *
  B00010, //    *
  B00100, //   *
  B01000, //  *
  B00000  //
};
enum {
  BUTTON_NONE, 
  BUTTON_UP,    
  BUTTON_DOWN,  
  BUTTON_LEFT,  
  BUTTON_RIGHT, 
  BUTTON_SELECT 
};

#include <Wire.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

void setup() {

  // Initializes serial communication
  Serial.begin(9600);
  pinMode(commande_monter, INPUT);
  pinMode(commande_retour, INPUT);
  pinMode(securite, INPUT);
  pinMode(contact_moteur, OUTPUT);
  // Initializes and clears the LCD screen
  lcd.begin(16, 2);
  lcd.clear();

  // Creates the byte for the 3 custom characters
  lcd.createChar(0, menuCursor);
  lcd.createChar(1, upArrow);
  lcd.createChar(2, downArrow);
}

void loop()
{ 
while ((digitalRead(securite) == HIGH)&&(digitalRead(commande_monter) == HIGH))
{
commande_monter_levre();
}
while ((digitalRead(commande_retour) == HIGH)&&(digitalRead(securite) == HIGH))
{ 
commande_retour_auto();
}
if (digitalRead(securite) == LOW)
{ 
affichage_securites();
}
if ((digitalRead(securite) == HIGH)&&(digitalRead(commande_monter) == LOW)&&(digitalRead(commande_retour) == LOW))
{ 
commande_relache();
}

  switch (getPressedButton()) {
  case BUTTON_NONE:
    lcd.print(F("      "));
    break;

  case BUTTON_UP:
    lcd.print(F("UP    "));
    break;

  case BUTTON_DOWN:
    lcd.print(F("DOWN  "));
    break;

  case BUTTON_LEFT:
    lcd.print(F("LEFT  "));
    break;

  case BUTTON_RIGHT:
    lcd.print(F("RIGHT "));
    break;

  case BUTTON_SELECT:
          lcd.print(F("SELECT"));
          menus();
          break;
  }
  delay(500);
}


void menus() {
  mainMenuDraw();
  drawCursor();
  operateMainMenu();
}

/*---------LECTURE--DES--BOUTONS--DU--KEYPAD------------*/
byte getPressedButton() {
int value = analogRead(A0);
  if (value < 50)
    return BUTTON_RIGHT;
  else if (value < 250)
    return BUTTON_UP;
  else if (value < 410)
    return BUTTON_DOWN;
  else if (value < 550)
    return BUTTON_LEFT;
  else if (value < 850)
    return BUTTON_SELECT;
  else
    return BUTTON_NONE;
}

// This function will generate the 2 menu items that can fit on the screen. They will change as you scroll through your menu. Up and down arrows will indicate your current menu position.
void mainMenuDraw() {
  Serial.print(menuPage);
  lcd.clear();
  lcd.setCursor(1, 0);
  lcd.print(menuItems[menuPage]);
  lcd.setCursor(1, 1);
  lcd.print(menuItems[menuPage + 1]);
  if (menuPage == 0) {
    lcd.setCursor(15, 1);
    lcd.write(byte(2));
  } else if (menuPage > 0 and menuPage < maxMenuPages) {
    lcd.setCursor(15, 1);
    lcd.write(byte(2));
    lcd.setCursor(15, 0);
    lcd.write(byte(1));
  } else if (menuPage == maxMenuPages) {
    lcd.setCursor(15, 0);
    lcd.write(byte(1));
  }
}

// When called, this function will erase the current cursor and redraw it based on the cursorPosition and menuPage variables.
void drawCursor() {
  for (int x = 0; x < 2; x++) {     // Erases current cursor
    lcd.setCursor(0, x);
    lcd.print(" ");
  }

  // The menu is set up to be progressive (menuPage 0 = Item 1 & Item 2, menuPage 1 = Item 2 & Item 3, menuPage 2 = Item 3 & Item 4), so
  // in order to determine where the cursor should be you need to see if you are at an odd or even menu page and an odd or even cursor position.
  if (menuPage % 2 == 0) {
    if (cursorPosition % 2 == 0) {  // If the menu page is even and the cursor position is even that means the cursor should be on line 1
      lcd.setCursor(0, 0);
      lcd.write(byte(0));
    }
    if (cursorPosition % 2 != 0) {  // If the menu page is even and the cursor position is odd that means the cursor should be on line 2
      lcd.setCursor(0, 1);
      lcd.write(byte(0));
    }
  }
  if (menuPage % 2 != 0) {
    if (cursorPosition % 2 == 0) {  // If the menu page is odd and the cursor position is even that means the cursor should be on line 2
      lcd.setCursor(0, 1);
      lcd.write(byte(0));
    }
    if (cursorPosition % 2 != 0) {  // If the menu page is odd and the cursor position is odd that means the cursor should be on line 1
      lcd.setCursor(0, 0);
      lcd.write(byte(0));
    }
  }
}


void operateMainMenu() {
  int activeButton = 0;
  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 0: // When button returns as 0 there is no action taken
        break;
      case 1:  // This case will execute if the "forward" button is pressed
        button = 0;
        switch (cursorPosition) { // The case that is selected here is dependent on which menu page you are on and where the cursor is.
          case 0:
            menuItem1();
            break;
          case 1:
            menuItem2();
            break;
          case 2:
            menuItem3();
            break;
          case 3:
            menuItem4();
            break;
          case 4:
            menuItem5();
            break;
         
        }
        activeButton = 1;
        mainMenuDraw();
        drawCursor();
        break;
      case 2:
        button = 0;
        if (menuPage == 0) {
          cursorPosition = cursorPosition - 1;
          cursorPosition = constrain(cursorPosition, 0, ((sizeof(menuItems) / sizeof(String)) - 1));
        }
        if (menuPage % 2 == 0 and cursorPosition % 2 == 0) {
          menuPage = menuPage - 1;
          menuPage = constrain(menuPage, 0, maxMenuPages);
        }

        if (menuPage % 2 != 0 and cursorPosition % 2 != 0) {
          menuPage = menuPage - 1;
          menuPage = constrain(menuPage, 0, maxMenuPages);
        }

        cursorPosition = cursorPosition - 1;
        cursorPosition = constrain(cursorPosition, 0, ((sizeof(menuItems) / sizeof(String)) - 1));

        mainMenuDraw();
        drawCursor();
        activeButton = 1;
        break;
      case 3:
        button = 0;
        if (menuPage % 2 == 0 and cursorPosition % 2 != 0) {
          menuPage = menuPage + 1;
          menuPage = constrain(menuPage, 0, maxMenuPages);
        }

        if (menuPage % 2 != 0 and cursorPosition % 2 == 0) {
          menuPage = menuPage + 1;
          menuPage = constrain(menuPage, 0, maxMenuPages);
        }

        cursorPosition = cursorPosition + 1;
        cursorPosition = constrain(cursorPosition, 0, ((sizeof(menuItems) / sizeof(String)) - 1));
        mainMenuDraw();
        drawCursor();
        activeButton = 1;
        break;
    }
  }
}

// This function is called whenever a button press is evaluated. The LCD shield works by observing a voltage drop across the buttons all hooked up to A0.
int evaluateButton(int x) {
  int result = 0;
  if (x < 50) {
    result = 1; // right
  } else if (x < 195) {
    result = 2; // up
  } else if (x < 380) {
    result = 3; // down
  } else if (x < 790) {
    result = 4; // left
  }
  return result;
}

// If there are common usage instructions on more than 1 of your menu items you can call this function from the sub
// menus to make things a little more simplified. If you don't have common instructions or verbage on multiple menus
// I would just delete this void. You must also delete the drawInstructions()function calls from your sub menu functions.
void drawInstructions() {
  lcd.setCursor(0, 1); // Set cursor to the bottom line
  lcd.print("Use ");
  lcd.write(byte(1)); // Up arrow
  lcd.print("/");
  lcd.write(byte(2)); // Down arrow
  lcd.print(" buttons");
}

void menuItem1() { // Function executes when you select the 1st item from main menu
  int activeButton = 0;
  lcd.clear();
  lcd.setCursor(0, 1);
  drawInstructions();
  lcd.setCursor(0, 0);
  lcd.print("TEMPO:");
  lcd.print(tempora);
  lcd.print(" ms");
  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 2:
        button = 0;
        tempora = tempora + 1000;
        tempora = constrain(tempora,1000,30000);
        lcd.setCursor(7,0);        
        lcd.print(tempora);
        lcd.setCursor(13,0);
        lcd.print(" ms");
        break;
      case 3:
        button = 0;
        tempora = tempora - 1000;
        lcd.setCursor(7,0);        
        lcd.print(tempora);
        lcd.setCursor(13,0);
        lcd.print(" ms");
        break;
      case 4:  // This case will execute if the "back" button is pressed
        button = 0;
        lcd.clear();
        lcd.setCursor(2,0);
        lcd.print("-- REGLAGE --");
        lcd.setCursor(2,1);
        lcd.print("-- VALIDE --");
        delay(1500);
        activeButton = 1;
        break;
    }
  }
}

void menuItem2() { // Function executes when you select the 2nd item from main menu
  int activeButton = 0;

  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Test 2");

  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 4:  // This case will execute if the "back" button is pressed
        button = 0;
        activeButton = 1;
        break;
    }
  }
}

void menuItem3() { // Function executes when you select the 3rd item from main menu
  int activeButton = 0;

  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Test 3");

  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 4:  // This case will execute if the "back" button is pressed
        button = 0;
        activeButton = 1;
        break;
    }
  }
}

void menuItem4() { // Function executes when you select the 4th item from main menu
  int activeButton = 0;

  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Test 4");

  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 4:  // This case will execute if the "back" button is pressed
        button = 0;
        activeButton = 1;
        break;
    }
  }
}

void menuItem5() { // Function executes when you select the 5th item from main menu
  int activeButton = 0;

  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Test 5");

  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 4:  // This case will execute if the "back" button is pressed
        button = 0;
        activeButton = 1;
        break;
    }
  }
}


/*---------COMMANDES------------*/

void commande_monter_levre()
{ 
lcd.setCursor(0, 0);
lcd.print("MONTER          ");
lcd.setCursor(0, 1);
lcd.print("                ");
digitalWrite(contact_moteur,HIGH);
}

void commande_retour_auto()
{ 
lcd.setCursor(0, 0);
lcd.print("RETOUR          ");
lcd.setCursor(0, 1);
lcd.print("AUTOMATIQUE     ");
digitalWrite(contact_moteur,HIGH);
delay (tempora);
}

void commande_relache()
{ 
lcd.clear();
digitalWrite(contact_moteur,LOW); 
}

void affichage_securites()
{ 
lcd.setCursor(0, 0);
lcd.print("SECURITES       ");
lcd.setCursor(0, 1);
lcd.print("P1              ");
digitalWrite(contact_moteur,LOW);
}

l'approche classique c'est d'effacer ce qui était écrit avant d'écrire la nouvelle valeur.
un hack c'est de se dire que la plus grosse valeur tient sur 5 digits par exemple et donc avant d'afficher on efface 5 positions (on met un espace) puis on revient au début et on affiche ce que l'on veut

par exemple quand je vois

lcd.setCursor(7,0);        
lcd.print(tempora);
lcd.setCursor(13,0);
lcd.print(" ms");

je me dis que vous avez réservé les caractères de 7 à 12 pour l'affichage du nombre. donc on ferait

lcd.setCursor(7,0);
lcd.print("      "); // efface de 7 à 12        
lcd.setCursor(7,0);
lcd.print(tempora);
lcd.setCursor(13,0);
lcd.print(" ms");

une autre approche c'est de mettre quelques espaces après l'affichage de la valeur, comme cela ça efface ce qui traine (mais c'est possible que si on a la place derrière)

lcd.setCursor(7,0);
lcd.print(tempora);
lcd.print("   "); // efface un peu      
lcd.setCursor(13,0);
lcd.print(" ms");

une autre approche est de fabriquer une chaîne de caractère toujours de la même longueur avec un padding d'espace au début.

par exemple faites tourner ce code et regardez l'affichage dans le moniteur série à 115200 bauds


void setup() {
  Serial.begin(115200); Serial.println();
}

void loop() {
  char message[20];
  int tempora = random(0, 30000);
  snprintf(message, sizeof message, "%5d ms", tempora);
  Serial.print("[");
  Serial.print(message);
  Serial.println("]");
  delay(100);
}

vous devriez voir quelque chose comme cela:

[16807 ms]
[25249 ms]
[10073 ms]
[13658 ms]
[28930 ms]
[21272 ms]
[17544 ms]
[  878 ms]
[27923 ms]
[27709 ms]
[ 4440 ms]
[ 8165 ms]
[24492 ms]
[23042 ms]
...

on voit que le chiffre est bien cadré à droite et que des espace sont insérés avant. Le message a donc toujours la même longueur (mais utiliser sprintf() est coûteux en mémoire programme et ne supporte pas les nombre à virgule sur AVR)

1 Like

Mais oui c'est bien sur.
Je savais que je devais tricher avec les espaces mais le

lcd.setCursor(7,0);
lcd.print("      ");

fait toute la différence.

Merci beaucoup

Par contre pourquoi mon réglage ne reste pas lors d'un redémarrage?

Cordialement

La mémoire est perdue lorsque vous redémarrez le microcontroller Ou si vous coupez et au courant.

Pour sauvegarder une valeur, Vous pouvez l’écrire dans la mémoire EEPROM (et bien sûr il faut la lire dans le setup)

Un bon tuto complet

Bonsoir nemesis47

L'idéal est de sauver tes réglages, chaque fois que tu valides leur changement. Pour ce faire et faciliter la chose, il faut mettre ces réglages dans une structure et sauver cette structure en EEPROM (EEPROM.put), pour la relire, au redémarrage de l'Arduino, avec un EEPROM.get, pour restaurer tes réglages.
Mets ton programme en ligne en indiquant quelles variables tu veux sauver, je te ferais un exemple.

Cordialement
jpbbricole

Le tuto de Henri pointé ci dessus propose déjà du code pour sauver la structure et explique aussi en plus comment s’assurer que l’EEPROM a été initialisée et en bonus il y a la gestion de la perte d’alimentation si besoin de sauvegarde d’urgence.
Ca vaut vraiment la peine de prendre le temps de le lire

Bonjour à vous deux. En me documentant sur vos conseils la modification de temporisation s'enregistre bien en EEPROM.
Voici le code que je essayer d'améliorer mais qui mise à part quelques légers défauts, fonctionne, et cela grâce à vous.
Merci beaucoup.



/*------------MENUS--PRINCIPAUX---------------------------*/
String menuItems[] = {"TempoRetour", "Type", "Menu 3", "Menu 4", "Menu 5"};

/*-----------VARIABLES----------------------*/
int readKey;
int tempora;

int commande_monter = 0;
int commande_retour = 1;
int securite = 2;
int contact_moteur = 3;

int menuPage = 0;
int maxMenuPages = round(((sizeof(menuItems) / sizeof(String)) / 2) + .5);
int cursorPosition = 0;

/*---------------NOUVEAUX--CARACTERES-------------------------*/
byte downArrow[8] = {
  0b00100,
  0b00100,
  0b00100,
  0b00100,
  0b00100,
  0b10101,
  0b01110,
  0b00100
};

byte upArrow[8] = {
  0b00100,
  0b01110,
  0b10101,
  0b00100,
  0b00100,
  0b00100,
  0b00100,
  0b00100
};

byte menuCursor[8] = {
  B01000,
  B00100,
  B00010,
  B00001,
  B00010,
  B00100,
  B01000,
  B00000
};
enum {
  BUTTON_NONE, 
  BUTTON_UP,    
  BUTTON_DOWN,  
  BUTTON_LEFT,  
  BUTTON_RIGHT, 
  BUTTON_SELECT 
};

#include <Wire.h>
#include <EEPROM.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

void setup() {
  Serial.begin(9600);
  EEPROM.get(10,tempora);
  lcd.createChar(0, menuCursor);
  lcd.createChar(1, upArrow);
  lcd.createChar(2, downArrow);
  pinMode(commande_monter, INPUT);
  pinMode(commande_retour, INPUT);
  pinMode(securite, INPUT);
  pinMode(contact_moteur, OUTPUT);
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.write("Digital Dock");
  delay (1000);
  lcd.setCursor(0, 1);
  lcd.write("v1.10");
  delay(5000);
  lcd.clear();
}

void loop()
{ 
while ((digitalRead(securite) == HIGH)&&(digitalRead(commande_monter) == HIGH))
{
commande_monter_levre();
}
while ((digitalRead(commande_retour) == HIGH)&&(digitalRead(securite) == HIGH))
{ 
commande_retour_auto();
}
if (digitalRead(securite) == LOW)
{ 
affichage_securites();
}
if ((digitalRead(securite) == HIGH)&&(digitalRead(commande_monter) == LOW)&&(digitalRead(commande_retour) == LOW))
{ 
commande_relache();
}

  switch (getPressedButton()) {
  case BUTTON_NONE:
    lcd.print(F("      "));
    break;

  case BUTTON_UP:
    lcd.print(F("UP    "));
    break;

  case BUTTON_DOWN:
    lcd.print(F("DOWN  "));
    break;

  case BUTTON_LEFT:
    lcd.print(F("LEFT  "));
    break;

  case BUTTON_RIGHT:
    lcd.print(F("RIGHT "));
    break;

  case BUTTON_SELECT:
          lcd.print(F("SELECT"));
          menus();
          break;
  }
  delay(500);
}


void menus() {
  mainMenuDraw();
  drawCursor();
  operateMainMenu();
}

/*---------LECTURE--DES--BOUTONS--DU--KEYPAD------------*/
byte getPressedButton() {
int value = analogRead(A0);
  if (value < 50)
    return BUTTON_RIGHT;
  else if (value < 250)
    return BUTTON_UP;
  else if (value < 410)
    return BUTTON_DOWN;
  else if (value < 550)
    return BUTTON_LEFT;
  else if (value < 850)
    return BUTTON_SELECT;
  else
    return BUTTON_NONE;
}

void mainMenuDraw() {
  Serial.print(menuPage);
  lcd.clear();
  lcd.setCursor(1, 0);
  lcd.print(menuItems[menuPage]);
  lcd.setCursor(1, 1);
  lcd.print(menuItems[menuPage + 1]);
  if (menuPage == 0) {
    lcd.setCursor(15, 1);
    lcd.write(byte(2));
  } else if (menuPage > 0 and menuPage < maxMenuPages) {
    lcd.setCursor(15, 1);
    lcd.write(byte(2));
    lcd.setCursor(15, 0);
    lcd.write(byte(1));
  } else if (menuPage == maxMenuPages) {
    lcd.setCursor(15, 0);
    lcd.write(byte(1));
  }
}

void drawCursor() {
  for (int x = 0; x < 2; x++) {   
    lcd.setCursor(0, x);
    lcd.print(" ");
  }

  if (menuPage % 2 == 0) {
    if (cursorPosition % 2 == 0) {  // If the menu page is even and the cursor position is even that means the cursor should be on line 1
      lcd.setCursor(0, 0);
      lcd.write(byte(0));
    }
    if (cursorPosition % 2 != 0) {  // If the menu page is even and the cursor position is odd that means the cursor should be on line 2
      lcd.setCursor(0, 1);
      lcd.write(byte(0));
    }
  }
  if (menuPage % 2 != 0) {
    if (cursorPosition % 2 == 0) {  // If the menu page is odd and the cursor position is even that means the cursor should be on line 2
      lcd.setCursor(0, 1);
      lcd.write(byte(0));
    }
    if (cursorPosition % 2 != 0) {  // If the menu page is odd and the cursor position is odd that means the cursor should be on line 1
      lcd.setCursor(0, 0);
      lcd.write(byte(0));
    }
  }
}


void operateMainMenu() {
  int activeButton = 0;
  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 0: // When button returns as 0 there is no action taken
        break;
      case 1:  // This case will execute if the "forward" button is pressed
        button = 0;
        switch (cursorPosition) { // The case that is selected here is dependent on which menu page you are on and where the cursor is.
          case 0:
            menuItem1();
            break;
          case 1:
            menuItem2();
            break;
          case 2:
            menuItem3();
            break;
          case 3:
            menuItem4();
            break;
          case 4:
            menuItem5();
            break;
         
        }
        activeButton = 1;
        mainMenuDraw();
        drawCursor();
        break;
      case 2:
        button = 0;
        if (menuPage == 0) {
          cursorPosition = cursorPosition - 1;
          cursorPosition = constrain(cursorPosition, 0, ((sizeof(menuItems) / sizeof(String)) - 1));
        }
        if (menuPage % 2 == 0 and cursorPosition % 2 == 0) {
          menuPage = menuPage - 1;
          menuPage = constrain(menuPage, 0, maxMenuPages);
        }

        if (menuPage % 2 != 0 and cursorPosition % 2 != 0) {
          menuPage = menuPage - 1;
          menuPage = constrain(menuPage, 0, maxMenuPages);
        }

        cursorPosition = cursorPosition - 1;
        cursorPosition = constrain(cursorPosition, 0, ((sizeof(menuItems) / sizeof(String)) - 1));

        mainMenuDraw();
        drawCursor();
        activeButton = 1;
        break;
      case 3:
        button = 0;
        if (menuPage % 2 == 0 and cursorPosition % 2 != 0) {
          menuPage = menuPage + 1;
          menuPage = constrain(menuPage, 0, maxMenuPages);
        }

        if (menuPage % 2 != 0 and cursorPosition % 2 == 0) {
          menuPage = menuPage + 1;
          menuPage = constrain(menuPage, 0, maxMenuPages);
        }

        cursorPosition = cursorPosition + 1;
        cursorPosition = constrain(cursorPosition, 0, ((sizeof(menuItems) / sizeof(String)) - 1));
        mainMenuDraw();
        drawCursor();
        activeButton = 1;
        break;
    }
  }
}

// This function is called whenever a button press is evaluated. The LCD shield works by observing a voltage drop across the buttons all hooked up to A0.
int evaluateButton(int x) {
  int result = 0;
  if (x < 50) {
    result = 1; // right
  } else if (x < 250) {
    result = 2; // up
  } else if (x < 410) {
    result = 3; // down
  } else if (x < 550) {
    result = 4; // left
  }
  return result;
}

void drawInstructions() {
  lcd.setCursor(0, 1); // Set cursor to the bottom line
  lcd.print("Utilisez ");
  lcd.write(byte(1)); 
  lcd.print("/");
  lcd.write(byte(2)); 
}

void menuItem1() { // Function executes when you select the 1st item from main menu
  int activeButton = 0;
  lcd.clear();
  lcd.setCursor(0, 1);
  drawInstructions();
  lcd.setCursor(0, 0);
  lcd.print("TEMPO: ");
  lcd.setCursor(8,0);
  lcd.print(tempora);
  lcd.setCursor(13, 0);
  lcd.print(" ms");
  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 2:
        button = 0;
        tempora = tempora + 1000;
        tempora = constrain(tempora,1000,30000);
        lcd.setCursor(8,0);
        lcd.print("      ");
        lcd.setCursor(8,0);        
        lcd.print(tempora);
        lcd.setCursor(13,0);
        lcd.print(" ms");
        break;
      case 3:
        button = 0;
        tempora = tempora - 1000;
        tempora = constrain(tempora,1000,30000);
        lcd.setCursor(8,0);
        lcd.print("      ");
        lcd.setCursor(8,0);        
        lcd.print(tempora);
        lcd.setCursor(13,0);
        lcd.print(" ms");
        break;
      case 4:  // This case will execute if the "back" button is pressed
        button = 0;
        EEPROM.put(10,tempora);
        lcd.clear();
        lcd.setCursor(2,0);
        lcd.print("   REGLAGE   ");
        lcd.setCursor(2,1);
        lcd.print("   VALIDE   ");
        delay(1500);
        activeButton = 1;
        break;
    }
  }
}

void menuItem2() { // Function executes when you select the 2nd item from main menu
  int activeButton = 0;

  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Test 2");

  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 4:  // This case will execute if the "back" button is pressed
        button = 0;
        activeButton = 1;
        break;
    }
  }
}

void menuItem3() { // Function executes when you select the 3rd item from main menu
  int activeButton = 0;

  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Test 3");

  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 4:  // This case will execute if the "back" button is pressed
        button = 0;
        activeButton = 1;
        break;
    }
  }
}

void menuItem4() { // Function executes when you select the 4th item from main menu
  int activeButton = 0;

  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Test 4");

  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 4:  // This case will execute if the "back" button is pressed
        button = 0;
        activeButton = 1;
        break;
    }
  }
}

void menuItem5() { // Function executes when you select the 5th item from main menu
  int activeButton = 0;

  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Test 5");

  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 4:  // This case will execute if the "back" button is pressed
        button = 0;
        activeButton = 1;
        break;
    }
  }
}


/*---------COMMANDES------------*/

void commande_monter_levre()
{ 
lcd.setCursor(0, 0);
lcd.print("MONTER          ");
lcd.setCursor(0, 1);
lcd.print("                ");
digitalWrite(contact_moteur,HIGH);
}

void commande_retour_auto()
{ 
lcd.setCursor(0, 0);
lcd.print("RETOUR          ");
lcd.setCursor(0, 1);
lcd.print("AUTOMATIQUE     ");
digitalWrite(contact_moteur,HIGH);
delay (tempora);
}

void commande_relache()
{ 
lcd.clear();
digitalWrite(contact_moteur,LOW); 
}

void affichage_securites()
{ 
lcd.setCursor(0, 0);
lcd.print("SECURITES       ");
lcd.setCursor(0, 1);
lcd.print("P1              ");
digitalWrite(contact_moteur,LOW);
}