Véhicule robot avec menu sur écran LCD 20x4 paramétrable avec encodeur et son bouton !

Bonjour,

Je travaille sur la conception d'un petit véhicule robot autonome avec 3 sonar , mais télécommandable par Capteur infra-rouge VS1838B (TSOP1838) et paramétrable par menu LCD (20x4) avec encodeur et son bouton.

C'est ces derniers points qu'il me reste à maîtriser, paramétrage par l'entremise d'un menu LCD avec encodeur et son bouton directement sur le véhicule robot.

Comme je souhaite quelque chose de très adaptatif dans le temps et malléable pour l'ajout ou le retrait de fonction ou de paramètre programmable par cette interface, et après avoir écumé le forum, je me tourne vers vous pour vos conseils pour la meilleure interface menue sur écran LCD 20x4 pour information à afficher et paramétrage du robot.

Vers quelle bibliothèque ou code d'exemple modèle devrais-je me tourner pour une carte Arduino Mega 2560 et une carte ESP-WROOM-32 à 38 broches ?

En mode véhicule robot autonome activation, l'écran LCD affichera en permanence les données internes de fonctionnement (po), quelque chose comme :

//*****************************************************************************
// FONCTION GererAfficherEcranLCD
//*****************************************************************************
void GererAfficherEcranLCD ()
{
  // Écriture dans l'écran LCD des valeurs lues.
  // NoColonne 00000000001111111111
  // NoColonne 01234567890123456789
  // Ligne 0 = 400 400 400      999
  // Ligne 1 = 1023 1023 1023 1023
  // Ligne 2 = 255 255 255 00
  // Ligne 3 = 255 255 255 00 99999
  LCD_1.clear ();
  LCD_1.setCursor (0, 0);
  LCD_1.print (int (Sonar_1_DistanceActuele));
  LCD_1.setCursor (4, 0);
  LCD_1.print (int (Sonar_2_DistanceActuele));
  LCD_1.setCursor (8, 0);
  LCD_1.print (int (Sonar_3_DistanceActuele));
  LCD_1.setCursor (17, 0);
  LCD_1.print (CodeSituation);


  LCD_1.setCursor (0, 1);
  LCD_1.print (PhotoResistance_1_Valeur);
  LCD_1.setCursor (5, 1);
  LCD_1.print (PhotoResistance_2_Valeur);
  LCD_1.setCursor (10, 1);
  LCD_1.print (PhotoResistance_3_Valeur);
  LCD_1.setCursor (15, 1);
  LCD_1.print (PhotoResistance_4_Valeur);


  LCD_1.setCursor (0, 2);
  LCD_1.print (Moteur_1_VitesseMinimum);
  LCD_1.setCursor (4, 2);
  LCD_1.print (Moteur_1_Vitesse);
  LCD_1.setCursor (8, 2);
  LCD_1.print (Moteur_1_VitesseMaximum);
  LCD_1.setCursor (12, 2);
  LCD_1.print (Encodeur_1_TourParMinute);


  LCD_1.setCursor (0, 3);
  LCD_1.print (Moteur_2_VitesseMinimum);
  LCD_1.setCursor (4, 3);
  LCD_1.print (Moteur_2_Vitesse);
  LCD_1.setCursor (8, 3);
  LCD_1.print (Moteur_2_VitesseMaximum);
  LCD_1.setCursor (12, 3);
  LCD_1.print (Encodeur_2_TourParMinute);
  LCD_1.setCursor (15, 3);
  LCD_1.print (MenuPosition);
}
//*****************************************************************************

Merci pour toute suggestion ou avis sur vos choix, expérience et préférence en la matière !

Cordialement,

il y a de nombreuses bibliothèques qui servent à gérer des menus

par exemple lire :

ou encore

Bonjour J-M-L Jackson,

Merci, je regarde de suite !

Est-ce que vous utilisez dans vos conceptions et programmations l'une de ces bibliothèques ?

Cordialement,

L'ami René

non, je fais mon propre code :slight_smile:

Bonjour J-M-L Jackson,

Pour « lcdmenu », elle ne s'installe pas, problème de dépendance, il me semble.

Pour « MD_Menu », il n'y a pas d'exemple avec LCD et encodeur, je ne l'ai pas mentionné, mais je suis unilingue francophone, alors si ce n'est pas dans le code source que je peux traduire nom par nom au fil de ma lecture du code et compréhension, sinon, ça devient trop laborieux de fouiller dans plusieurs fichiers des sources en différent langage.

Je poursuivrai donc ma recherche, mais merci de me les avoir référencées !

Cordialement,

L'ami René

il y en a d'autres comme GitHub - elmarmut75/Arduino-1602-2004-LCD-menu: Arduino menu library for LCD displays 16x2 and 20x4

Sinon ce n'est pas très compliqué à coder avec une bibliothèque pour le bouton (Toggle par exemple) et une pour l'encodeur quadratique (encoder par exemple).

j'avais partagé cet exemple pour montrer comment gérer l'encodeur et son bouton

Bonjour J-M-L Jackson,

Merci pour ces nouvelles références !

Pour « Arduino-1602-2004-LCD-menu » il y a un problème avec la bibliothèque « Time » et « TimeLib » pas de source installable.

Merci pour votre code, il fonctionne bien, mais finalement, il ne gère pas un menu sur écran LCD. J'ai déjà un bon fonctionnement pour l'encodeur, avec un code similaire au vôtre !

C'est d'un exemple fonctionnel d'une gestion de menu avec un écran LCD, par un encodeur et son bouton don j'ai besoins. J'ai déjà vu des dizaines de codes d'encodeur, d'écran LCD, de bouton et de menu, mais je n'en trouve pas qui est utilisable dans un même programme, dans un même code.

Cordialement,

L'ami René

Bonjour,

Bon, j'ai peut-être trouvé la bonne bibliothèque et de bons exemples de code, mais j'ai un problème de compilation :

In file included from /home/rene/sda6/Logiciel/Arduino/MesExemples/ArduinoMenu/clickEncoder/clickEncoder.ino:17:0:
/home/rene/sda6/Logiciel/Arduino/libraries/ArduinoMenu_library/src/menuIO/lcdOut.h:12:14: fatal error: LCD.h: No such file or directory
#include <LCD.h>
^~~~~~~
compilation terminated.
/home/rene/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-g++ -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10607 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR -I/home/rene/.arduino15/packages/arduino/hardware/avr/1.8.6/cores/arduino -I/home/rene/.arduino15/packages/arduino/hardware/avr/1.8.6/variants/mega -I/home/rene/.arduino15/packages/arduino/hardware/avr/1.8.6/libraries/Wire/src -I/home/rene/sda6/Logiciel/Arduino/libraries/ArduinoMenu_library/src /tmp/arduino/sketches/440C7A816172C494D189CF8C0A7C5484/sketch/clickEncoder.ino.cpp -o /dev/null
Alternative pour LCD.h: []
ResolveLibrary(LCD.h)
-> candidates: []
exit status 1

Compilation error: exit status 1

Vous auriez une solution ?

Cordialement,

L'ami René

Avez vous essayé d’en coder un?

Bonjour J-M-L Jackson,

Avez vous essayé d’en coder un?

Non, comme je n'y connais pas grand-chose aux menus sur écran LCD avec encodeur, je préférerais profiter du savoir des anciens et des expérimentés pour éviter les solutions boiteuses ou trop laborieuses ou trop gourmandes en ressources.

Cordialement,

L'ami René

Il manque manifestement une librairie LCD.

Bonjour fdufnews,

Il manque manifestement une librairie LCD.

Mais j'ai déjà trois bibliothèques d’installées qui ont un fichier « LCD.h » :

LiquidCrystal
ESP32_Display_Panel
LiquidCrystal_I2C

Et le message parle de la bibliothèque principale « ArduinoMenu », mais elle est installé et fonctionne pour les compilations des exemples qui n'utilise pas d'écran LCD, et dans mon cas, j'utilise soit « LiquidCrystal » ou « LiquidCrystal_I2C » pour les écrans LCD 1602 et 2004 en parallèle ou en I2C, et j'ai le même véhicule robot, mais avec une carte ESP-WROOM-32 38 broche à la place d'une Arduino Mega, de là la bibliothèque « ESP32_Display_Panel » !

Je ne sais pas résoudre ce genre de problème, de là ma demande d'aide pour ce cas !

Cordialement,

L'ami René

Bonjour,

Les exemples de la bibliothèque « ArduinoMenu » que je cherche à comprendre et compiler pour les intégrer à mon code pour mes véhicules robots sont les suivants :

clickEncoder
I2C
LiquidCrystal
plugins

Réf. :
https://github.com/neu-rah/ArduinoMenu

Si vous compilez ces exemples pour une carte Arduino Mega 2506, est-ce que votre compilation se termine correctement ou avez-vous les mêmes messages d'erreurs que moi ?

Cordialement,

L'ami René

voici un exemple avec les bibliothèques que j'ai mentionnées

à tester dans wokwi

click to see the code
#include <Wire.h>
#include <hd44780.h> //  https://github.com/duinoWitchery/hd44780
#include <hd44780ioClass/hd44780_I2Cexp.h> // i2c expander i/o class header

#include <Toggle.h>   // https://github.com/Dlloydev/Toggle
#include <Encoder.h>  // https://www.pjrc.com/teensy/td_libs_Encoder.html

// LE LCD
hd44780_I2Cexp lcd;
const int nbCols = 20;
const int nbLignes = 4;

byte fleche[] = {
  0b00000,
  0b00000,
  0b00100,
  0b00010,
  0b11111,
  0b00010,
  0b00100,
  0b00000
};

// L'ENCODEUR
const byte encoderCLKPin = 2;
const byte encoderDTPin  = 3;
Encoder encoder(encoderDTPin, encoderCLKPin);
long encoderPosition;


// LE BOUTON DE L'ENCODEUR
const byte encoderSWPin = 4;
Toggle encoderSwitch;


// UN TYPE POUR SIMPLIFIER LA DECLARATION D'UN CALLBACL
typedef void (*FunctionPointer)(byte);


struct ElementMenu {
  const char * texte;
  FunctionPointer callback;
};

struct Menu {
  const char * titre;
  const byte nombreElements;
  ElementMenu * elements;
};

Menu * menuEnCours = nullptr;
byte posDebut = 0; // l'index du premier élément de menu visible à l'écran
byte posChoix = 0; // l'index du menu pointé par la flèche

//------------------

void callback1(byte menupos);
void callback2(byte menupos);
void choixMenu1(byte menupos);
void choixMenu2(byte menupos);

ElementMenu elems1[] = {
  {"fonction1", callback1},
  {"fonction2", callback1},
  {"choixMenu2", choixMenu2},
  {"fonction3", callback1},
  {"fonction4", callback1},
  {"fonction5", callback1},
};

ElementMenu elems2[] = {
  {"m2, fonction1", callback2},
  {"m2, choixMenu1", choixMenu1},
};



Menu menuPrincipal = {"MENU1", sizeof elems1 / sizeof * elems1, elems1};
Menu menuSecondaire = {"MENU2", sizeof elems2 / sizeof * elems2, elems2};


void callback1(byte menupos) {
  Serial.print("callback MENU1 - click sur ");
  Serial.println(menuEnCours->elements[menupos].texte);
}

void callback2(byte menupos) {
  Serial.print("callback MENU2 - click sur ");
  Serial.println(menuEnCours->elements[menupos].texte);
}

void choixMenu1(byte menupos) {
  Serial.println("aller au menu 1");
  menuEnCours = &menuPrincipal;
}

void choixMenu2(byte menupos) {
  Serial.println("aller au menu 2");
  menuEnCours = &menuSecondaire;
}


bool encoderChanged() {
  bool changed = false;
  long newPosition = encoder.read() >> 2;

  if (newPosition != encoderPosition) {
    if (menuEnCours != nullptr) {
      if (newPosition < 0) {
        encoder.write(0);
        changed = true;
      } else if (newPosition >= menuEnCours->nombreElements) {
        encoder.write((menuEnCours->nombreElements - 1) << 2);
      } else {
        encoderPosition = newPosition;
        changed = true;
      }
    }
  }
  return changed;
}

void effacerLigne(int ligne) {
  lcd.setCursor(0, ligne);
  for (int i = 0; i < nbCols; i++) lcd.write(' ');
}

void afficherCentre(const char * texte, int ligne) {
  int col = 0;
  int longueur =  strlen(texte);
  if (longueur < nbCols) col = (nbCols - longueur) / 2;
  lcd.setCursor(col, ligne);
  lcd.print(texte);
}

void afficherElement(const char * texte, int ligne) {
  lcd.setCursor(1, ligne);
  lcd.print(texte);
}

void afficherMenu(byte debut, byte menuPos) {
  static Menu * ancienMenu = nullptr;
  static byte ancienMenuPos = 255;
  static byte ancienDebut = 255;

  if (menuEnCours != ancienMenu) {
    lcd.clear();
    if (menuEnCours != nullptr) {
      afficherCentre(menuEnCours->titre, 0);
      for (byte y = 0; y < nbLignes - 1; y++) {
        if (y < menuEnCours->nombreElements) afficherElement(menuEnCours->elements[y].texte, y + 1);
      }
      lcd.setCursor(0, 1); lcd.write(0); // affiche la flèche sur la première entrée
    }
    encoder.write(0);
    posDebut = 0;
    posChoix = 0;
    ancienMenuPos = 0;
    ancienDebut = 0;
    ancienMenu = menuEnCours;
  } else if (debut != ancienDebut) {
    byte l = 1;
    for (byte y = debut; l < nbLignes; y++, l++) {
      effacerLigne(l);
      if (menuEnCours != nullptr && y < menuEnCours->nombreElements) afficherElement(menuEnCours->elements[y].texte, l);
    }
    ancienDebut = debut;
  }

  if (menuPos != ancienMenuPos) {
    for (byte i = 1; i < nbLignes; i++) {
      lcd.setCursor(0, i); lcd.write(' ');
    }
    lcd.setCursor(0, 1 + menuPos - debut);
    lcd.write(0); // affiche la flèche
    ancienMenuPos = menuPos;
  }

}

void gestionMenu() {
  if (encoderChanged()) {
    posChoix = encoderPosition;
    if (posChoix < posDebut) {
      posDebut = posChoix;
    }
    else if (posChoix >= posDebut + nbLignes - 1) {
      posDebut = posChoix - nbLignes + 2;
    }
  }
  afficherMenu(posDebut, posChoix);

  encoderSwitch.poll();
  if (encoderSwitch.onPress()) {
    if (menuEnCours != nullptr)  menuEnCours->elements[posChoix].callback(posChoix);
  }
}



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

  int result = lcd.begin(nbCols, nbLignes);
  if (result) {
    Serial.print("LCD initialization failed: ");
    Serial.println(result);
    hd44780::fatalError(result);
  }
  lcd.createChar(0, fleche);

  menuEnCours = &menuPrincipal;

}

void loop() {
  gestionMenu();
}

les menus sont décrits dans des structures

Bonjour J-M-L,

Ha oui, ça ressemble beaucoup à ce que je recherche, et c'est déjà tout en français, variable, et fonction, etc., en français c'est comme du velours pour moi qui est unilingue francophone, en anglais, c'est comme avoir la tête dans le brouillard, cela fait vraiment une grande différence !

Vous faites une grande différence !

Je regarde cela dans le détail pour voir comment ça fonctionne et comment je pourrai l'intégrer dans le code des véhicules robots. Et comme c'est déjà en français, vous me facilitez grandement la tâche !

Un très grand merci M. J-M-L !

C'est ce que vous utilisez dans vos programmations, dans vos codes, c'est votre création ?

Cordialement,

L'ami René

j'ai tapé ça à la va vite dans wokwi donc ce n'est pas très propre, mais c'était pour vous donner une idée et vous montrer que ce n'est pas compliqué ➜ j'adapte ce principe aux besoins de mon application

Bonjour,

Hé bien vous travaillez vite et bien, et merci pour votre code !

J'ai commencé a adapter votre code. Par exemple, je n'ai pas de « hd44780 », mais avec «
LiquidCrystal_I2C » ça fonctionne bien. Je n'y perds que le code :

  if (result) {
    Serial.print("LCD initialization failed: ");
    Serial.println(result);
  //  hd44780::fatalError(result);
  }

Et je n'avais pas la bibliothèque pour l'encodeur « Encoder-master » de :

https://github.com/PaulStoffregen/Encoder

Maintenant, votre code fonctionne sur un Arduino Mega avec écran LCD2004 i2c et encodeur avec bouton.

Si j'arrive a adapter votre code pour l'affichage don j'ai besoin, sur la planche d'essais, je pourrai tout intégrer dans le code des véhicules robot, sinon je m'en inspirerai pour une version personnelle. Mais dans tous les cas, quand tout sera fini, je rendrai le tout disponible sur « thingiverse.com », codes, dessins 3D pour imprimante 3D et documentation.

Merci !

Cordialement,

L'ami René

installez cette bibliothèque hd44780, elle est de loin bien meilleure que les LiquidCrystal_I2C....

Bonjour J-M-L,

Pour moi et pour le bénéfice de tous les lecteurs de cette discussion, en quoi « cette bibliothèque hd44780, elle est de loin bien meilleure que les LiquidCrystal_I2C.... », sur quels point (vitesse, options, fonctions, espace mémoire, etc.) et comment peut-ons le vérifier ?

Si vous pouvez le démontrer, je passerai tous les projets que je toucherai à cette bibliothèque avec joie, en commençant par les véhicules robots autonomes.

Cordialement,

Comme dit dans le GitHub


The hd44780 API also provides some addtional extensions and all the API functions provided by hd44780 are common across all i/o subclasses. The most most significant extensions being:

  • ability to enable automatic line wrapping
  • ability to modify the libraries expected command execution times.
  • API functions return a status to indicate whether successful
  • automatic detection and s/w work around for LCD keypad shields with bad backlight circuit
  • self configuration of i2c addr
    pin mappings for i2c backpacks
  • ability to tell if lcd initialization failed
  • ability to read data or status from the LCD (requires r/w control)
  • ability to tell if sending a raw command to the LCD failed

L’implémentation est aussi proche de la spec donc la perf est bonne mais ce ne sent pas forcément dans l’usage de base et l’API comme vous l’avez vu est grandement compatible donc ça ne nécessite pas de grosse modif du code ou d’apprentissage.