Programmer un menu avec un écran LCD

Arduino & écran LCD

Retrouvez le tutoriel en vidéo sur Youtube

1/ Introduction

Le but de ce chapitre, sera de comprendre et d'appliquer en programmation, la structure d'un menu qui interagit avec des boutons poussoir. Nous verrons la possibilité d'activer ou désactiver plusieurs programmes en parallèles.
Les programmes ci-dessous reste relativement complexe pour quelqu'un qui n'a jamais programmé un écran. Je vous conseils pour débuter, de commencer par le tutoriel "Ecran LCD" sur le site d'IHM-3D.

2/ Avant d'aller plus loin, voici le fonctionnement rechcerché

3/ Le matériel

- Une carte de type UNO

- Deux LED

- Une plaque d'essai

- Des fils de connexions

- 3 résistances de 100Ω au minimum

- 5 boutons poussoir

- Un potentiomètre

- Un module relais


4/ Le schéma

- Le potentiomètre permettra de régler le contraste de l'écran.
- Le rétro éclairage de l'écran se connecte comme pour une LED classique : Une résistance de 100Ω au minimum devra relié au +5V et le (-) à la masse.
- Pour le boutons, nous utiliserons la résistance de PULLUP située dans la carte. De ce fait, il suffira de connecter une extrémité de ceux-ci à la masse, et l'autre aux bornes A0, A1, A2, A3, A4.
- Les LEDS se raccorderont classiquement, avec une résistance à minima de 100Ω chacune.
- Le module relais aura besoin d'une alimentation en +5V et d'une connexion su une borne de la carte qui lui est attribué.

Pour plus d'informations et de détails sur le câblage de l'écran (correspondance des bornes), je vous invites à aller visualiser le premier tutoriel sur le site d'IHM-3D.

Je vous conseils d'utiliser une grande, ou deux plaques d'essais car il y'aura beaucoup de fils.

5/ Le programme


/* Bienvenue sur les tutoriels d'IHM 3D*/
#include <LiquidCrystal.h>
LiquidCrystal lcd(11, 10, 5, 4, 3, 2);// déclaration des broches de l'écran à connecter sur la carte
/*déclaration des variable en sortie*/
int led1 = 6;
int led2 = 7;
int relais = 8;
/*Les variables du MENU*/
int unsigned Menu = 0; /* Cette variable est incrémenté de 10 en 10 et permet
  d'afficher  les Menus sans sous Menu possibilité ici de 10 */
int unsigned SousMenu = 0 ; /* Cette variable permet d'afficher les sous menus
  associés  à  chaque menu (ici de 1 à  9 donc neuf sous menu par menu */
/* VAriable gestion du temps pour la LED 1*/
double DelaiON; /* Temps durant lequel la LED reste Allumée */
double DelaiOFF; /* Temps durant lequel la LED reste éteinte */
double Tempo = millis(); /* Delai entre deux lectures des boutons */
/*Variable gestion du temps pour la LED 2 */
double DelaiON2; /* Temps durant lequel la LED reste Allumée */
double DelaiOFF2; /* Temps durant lequel la LED reste éteinte */
double Tempo2 = millis(); /* Delai entre deux lectures des boutons */

int Clignote1 = 0; /* Cette variable permet d'activer ou  désactiver le sous programme
  de  clignotement de la LED1. */
int Clignote2 = 0; /* Cette variable permet d'activer ou  désactiver le sous programme
  de  clignotement de la LED1. */
int Lerelais = 0;/*Cette variable permet d'activer ou de désactiver le sous programme qui permet la commande
  du relais ON/OFF
  /*Déclaration des boutons de navigation*/
int SELECT = A0;
int UP = A1;
int DOWN = A2;
int LEFT = A3;
int RIGHT = A4;
typedef struct {
  /* Une structure permet de faire une liste de variables qui pourront être
    appelées avec un index numérique. */
  String Menu;
}
/* Définition du ou des champs de la structure */
TypeMenu; /*Nom de la Structure */
TypeMenu NumMenu[100]; /* Limitation du nombre d'enregistrements de la base de
  donnée NumMenu  ayant comme champs les mêmes que la structure TypeMenu */
void Initialisation() {
  /* Création de la base de donnée, on remarquera que les dizaines
      avec  pour unité "0" correspondent au Menu ! et les autre (11, 12, 13, 21, 22, 23......) aux sous Menus !  */
  NumMenu[10] = (TypeMenu) { //donc ici nous avons un menu
    "1. MENU LED 1"
  }; /*10, 11 ,12 ... correspondent à l'index  */
  NumMenu[11] = (TypeMenu) {//et ici un sous menu
    "1.1Clignote1LED1"
  };
  NumMenu[12] = (TypeMenu) {
    "1.2Clignote2LED1 "
  };
  NumMenu[20] = (TypeMenu) {
    "2.MENU LED 2"
  };
  NumMenu[21] = (TypeMenu) {
    "2.1Clignote1LED2"
  };
  NumMenu[22] = (TypeMenu) {
    "2.2Clignote2LED2"
  };
  NumMenu[30] = (TypeMenu) {
    "3.LE RELAIS"
  };
  NumMenu[31] = (TypeMenu) {//c'est ici que l'on inscris les items désirés
    "3.1Relay ON/OFF"
  };
}
void setup() {
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  pinMode(relais, OUTPUT);
  Serial.begin(9600);
  Initialisation();
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("  * TUTORIEL *  ");
  lcd.setCursor(0, 1);
  lcd.print("   * IHM-3D *   ");
  delay(2000);
  /*Boutons poussoir en entrée en pullup (résistance interne de 20kohm intégrées à l'ARDUINO)*/
  pinMode(SELECT, INPUT_PULLUP);
  pinMode(UP, INPUT_PULLUP);
  pinMode(DOWN, INPUT_PULLUP);
  pinMode(LEFT, INPUT_PULLUP);
  pinMode(RIGHT, INPUT_PULLUP);
}
void loop() {
  /* Lancement de tous les sous programmes qui ne s'executeront que si une
      variable les y autorisent */
  Cmd_relais();/*Sous programme du fonctionnement du relais*/
  LedClignotante1(); /* Sous programme pour la Led1 */
  LedClignotante2(); /* Sous programme pour la Led1 */
  Boutons(); /* Sous programme lecture des Boutons */
//chaque "sous programme" à sa propre "loop"
}
void Boutons() {//les boutons, c'est ici que l'on code les boutons
  if ( millis() > Tempo ) {
    if (digitalRead(LEFT) == LOW) { /* Bouton LEFT  */
      SousMenu++;
      Tempo = millis() + 250;//ici c'est le temps max d'appuie sur le bouton avant de passer au prochain menu.
      AfficheMenu();
    } else if (digitalRead(DOWN) == LOW) { /* Bouton Down  */
      Menu = Menu - 10;
      Tempo = millis() + 250;
      SousMenu = 0 ;
      AfficheMenu();
    } else if (digitalRead(UP) == LOW) { /* Bouton Up */
      Menu = Menu + 10;
      Tempo = millis() + 250;
      SousMenu = 0;
      AfficheMenu();
    } else if (digitalRead(RIGHT) == LOW) { /* Bouton RIGHT  */
      SousMenu--;
      Tempo = millis() + 250;
      AfficheMenu();
    } else if (digitalRead(SELECT) == LOW) { /* Bouton Select */
      Tempo = millis() + 750;
      switch (Menu + SousMenu) {//le bouton select...très important, il faudra programmer les actions de chaque fonction des sous programme.
        case 11 :
          if (!(Clignote1 == 50)) /* Quand cette variable est différente de "0" le programme
  LedClignotante  est activé avec ici la valeur de 50  */
            Clignote1 = 50;
          else
            Clignote1 = 0 ;
          digitalWrite(led1, LOW);
          break;
        case  12 :
          if (!(Clignote1 == 500)) /* Idem qu'un peu plus haut mais avec une valeur ici de
  500  */
            Clignote1 = 500;
          else
            Clignote1 = 0 ; /* Arrêt du programme LedClignotante!  */
          digitalWrite(led1, LOW); /* Forçage de l'extiction de la Led  */
          break;
        case 21 :
          if (!(Clignote2 == 60)) /* Quand cette variable est différente de "0" le programme
  LedClignotante  est activé avec ici la valeur de 60  */
            Clignote2 = 60;
          else
            Clignote2 = 0 ;
          digitalWrite(led2, LOW);
          break;
        case 22 :
          if (!(Clignote2 == 600)) /* Quand cette variable est différente de "0" le programme
  LedClignotante  est activé avec ici la valeur de 600  */
            Clignote2 = 600;
          else
            Clignote2 = 0 ;
          digitalWrite(led2, LOW);
          break;
        case 31 :// par exemple, 31 correspond au menu "3.1" correspondant aux items choisi ci dessus
          if (!(Lerelais == 1))
            Lerelais = 1;
          else
            Lerelais = 0;
          digitalWrite(relais, LOW);
          break;
      }
    }
  }
}
void AfficheMenu() { /* Les Index avec "0" comme unité affichés sur la ligne du haut */
  if (Menu == 0 ) {
    Menu = 30;
  }
  if (Menu == 40 ) {
    Menu = 10;
  }
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(NumMenu[Menu].Menu);
  lcd.setCursor(0, 1);
  if ( SousMenu < 0 ) {
    SousMenu = 0;
  }
  if (NumMenu[Menu + SousMenu].Menu == "") {/* Détection de la fin des sous menu pour un menu Selectionné */
    SousMenu = 0;
  }
  if (Menu + SousMenu > Menu) {
    lcd.print(NumMenu[Menu + SousMenu].Menu);
  }/* Les Index avec autre que "0" comme unité affichés sur la ligne du bas */
}
void LedClignotante1() {// ici j'ai tout les "sous programme" qui peuvent fonctionner en même temps. Et tout ceci aussi, grâce à la fonction millis().
  
  /* L'utilisation de Millis() permet de ne pas interrompre le
    cycle  du "loop()" (Boucle) qui permet de lire en permanence l'état des boutons voir
    d'exécuter d'autres sous programmes simultanément  */
    
  if (Clignote1 > 0) {
    if ((digitalRead(led1) == HIGH & DelaiOFF < millis())) {
      DelaiON = millis() + Clignote1;
      digitalWrite(led1 , LOW);
    }
    if ((digitalRead(led1) == LOW) & ( DelaiON < millis())) {
      DelaiOFF = millis() + Clignote1;
      digitalWrite(led1 , HIGH);
    }
  }
}
void LedClignotante2() {
  if (Clignote2 > 0) {
    if ((digitalRead(led2) == HIGH & DelaiOFF2 < millis())) {
      DelaiON2 = millis() + Clignote2;
      digitalWrite(led2 , LOW);
    }
    if ((digitalRead(led2) == LOW) & ( DelaiON2 < millis())) {
      DelaiOFF2 = millis() + Clignote2;
      digitalWrite(led2 , HIGH);
    }
  }
}
void Cmd_relais() {
  if (Lerelais > 0) {
    if ((digitalRead(Lerelais)) == HIGH) {
      digitalWrite(relais, 1);
    }
    if ((digitalRead(Lerelais)) == LOW) {
      digitalWrite(relais, 0);
    }
  }
}

6/ Le résultat

Les différents menus :

Les boutons UP et DOWN (deuxième et troisième) permettent de naviguer dans ses menus. Une fois celui-ci sélectionné, il faudra utiliser le bouton LEFT et RIGHT (troisième et quatrième position) pour naviguer dans les "sous menu".
Le bouton ENTER, première position, permet d'activer et de désactiver un programme. Je rappelle que plusieurs programme peuvent être activer simultanément. (bien étudier le programme ci-dessus)

En reprenant le synoptique du début de ce document, voici la représentation des trois premières "branches".

(les différentes photos ont été prises avec un écran connecté en I2C, voir §6)

Les sous-menus, avec activation de deux programmes simultanément (les deux LEDS) :

7/ Réduction des fils à l'aide d'un écran 16x2 I2C

L'écran possède beaucoup de fils connectés à la carte UNO. Pour posséder un plus grand nombre d'E/S sur la carte, éviter de câbler un potentiomètre, de gagner du fils et d'économiser du temps, il est possible d'utiliser un écran I2C. Il disposera de 2 fils +son alimentation.

Remarques

La borne "A4" de la carte UNO est déjà occupée par la connexion d'un des boutons. Il faudra déplacer celui-ci sur la borne 10 de la carte.
Respecter scrupuleusement le câblage de SDA et SCL.

Décâbler toute la partie "écran" sur votre platine et remplacer par celui-ci. On respire mieux n'est ce pas? Aller, quelques modifications dans le programme et le tour est joué.

7.1/ Modification et ajout de la bibliothèque du programme.

Je vais effectué un copié-collé du programme précédent. Les modifications apportées seront inscrites en rouge. Seul les bibliothèques, quelques variables et le Setup ont besoin d'être modifiés. La loop et le fonctionnement du programme ne sera pas copié.

Attention : Ce type d'écran est capricieux. Il faudra bien choisir la bonne bibliothèque comme ci-dessous :

Commencer par inclure la bibliothèque en cliquant sur Croquis --> Inclure une bibliothèque --> Gérer les bibliothèque.

Puis taper dans la barre de recherche en haut à gauche "LiquidCrystal_I2C. Descendre un peu l'ascenseur, et installer celle qui est encadrée en rouge ci-dessus.

Les modifications du programmes sont en rouge

/* Bienvenue sur les tutoriels d'IHM 3D*/
#include <LiquidCrystal_I2C.h>
#include <Wire.h>

//----- Adressage matériel -----

LiquidCrystal_I2C lcd(0x27, 16, 2);// adressage de votre carte.

/*déclaration des variable en sortie*/

int led1 = 6;
int led2 = 7;
int relais = 8;

/*Les variables du MENU*/

int unsigned Menu = 0; /* Cette variable est incrémenté de 10 en 10 et permet
d'afficher les Menus sans sous Menu possibilité ici de 10 */

int unsigned SousMenu = 0 ; /* Cette variable permet d'afficher les sous menus
associés à chaque menu (ici de 1 à 9 donc neuf sous menu par menu */

/* VAriable gestion du temps pour la LED 1*/

double DelaiON; /* Temps durant lequel la LED reste Allumée */
double DelaiOFF; /* Temps durant lequel la LED reste éteinte */

double Tempo = millis(); /* Delai entre deux lectures des boutons */

/*Variable gestion du temps pour la LED 2 */

double DelaiON2; /* Temps durant lequel la LED reste Allumée */
double DelaiOFF2; /* Temps durant lequel la LED reste éteinte */

double Tempo2 = millis(); /* Delai entre deux lectures des boutons */


int Clignote1 = 0; /* Cette variable permet d'activer ou désactiver le sous programme
de clignotement de la LED1. */
int Clignote2 = 0; /* Cette variable permet d'activer ou désactiver le sous programme
de clignotement de la LED1. */
int Lerelais = 0;/*Cette variable permet d'activer ou de désactiver le sous programme qui permet la commande
du relais ON/OFF

/*Déclaration des boutons de navigation*/

int SELECT = A0;
int UP = A1;
int DOWN = A2;
int LEFT = A3;
int RIGHT = 10;

typedef struct {
/* Une structure permet de faire une liste de variables qui pourront être
appelées avec un index numérique. */
String Menu;
}
/* Définition du ou des champs de la structure */
TypeMenu; /*Nom de la Structure */
TypeMenu NumMenu[100]; /* Limitation du nombre d'enregistrements de la base de
donnée NumMenu ayant comme champs les mêmes que la structure TypeMenu */

void Initialisation() {
/* Création de la base de donnée, on remarquera que les dizaines
avec pour unité "0" correspondent au Menu ! et les autre (11, 12, 13, 21, 22, 23......) aux sous Menus ! */

NumMenu[10] = (TypeMenu) { //donc ici nous avons un menu
"1. MENU LED 1"
}; /*10, 11 ,12 ... correspondent à l'index */
NumMenu[11] = (TypeMenu) {//et ici un sous menu
"1.1Clignote1LED1"
};
NumMenu[12] = (TypeMenu) {
"1.2Clignote2LED1 "
};
NumMenu[20] = (TypeMenu) {
"2.MENU LED 2"
};
NumMenu[21] = (TypeMenu) {
"2.1Clignote1LED2"
};
NumMenu[22] = (TypeMenu) {
"2.2Clignote2LED2"
};
NumMenu[30] = (TypeMenu) {
"3.LE RELAIS"
};
NumMenu[31] = (TypeMenu) {//c'est ici que l'on inscris les items désirés
"3.1Relay ON/OFF"
};
}
void setup() {

pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
pinMode(relais, OUTPUT);
Initialisation();

lcd.init(); // initialisation de l'afficheur
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print(" * TUTORIEL * ");
lcd.setCursor(0, 1);
lcd.print(" * IHM-3D * ");
delay(1000);

/*Boutons poussoir en entrée en pullup (résistance interne de 20kohm intégrées à l'ARDUINO)*/

pinMode(SELECT, INPUT_PULLUP);
pinMode(UP, INPUT_PULLUP);
pinMode(DOWN, INPUT_PULLUP);
pinMode(LEFT, INPUT_PULLUP);
pinMode(RIGHT, INPUT_PULLUP);
}
void loop() { copier-coller, la loop du chapitre précédent.....[]}

7.2/Le résultat

Le résultat sera identique que celui du chapitre précédent.

8/ Conclusion

Ce document tutoriel est maintenant terminé. N'hésitez pas à parcourir le site d'IHM-3D et de découvrir ses activités et de poser vos questions à l'adresse située en pied de page.

Ecran LCD 16x2 I2C

5.41 €
Interagissez, relevez et commandez grâce à cet écran

Plus...

Module relais interface 5Volts

2.55 €
Ce module relais interfacé permet de commuter des tensions plus élevées à partir de la tension de commande du microcontrôleur de 5 Vcc. Pouvoir de coupure: 10 A/ 240 Vac ou 28 Vcc / 10A.

Plus...

Potentiomètre linéaire 10 kohms (lot de 5)

2.72 €
Nos circuits de commandes ne peuvent plus se passer des potentiomètres à résistance variable.

Plus...

Ensemble carte UNO R3 (CH340) + cable USB compatoble ARDUINO

7.64 €
La carte UNO R3 est basée sur un ATmega328 et est compatible ARDUINO. Avec le câble USB, vous pouvez directement vous connecter à cette carte en installant les pilotes CH340. Idéal pour débuter dans l'automatisme et l'électronique.

Plus...