Voici un exemple avec un LCD 2004 en I2C et un clavier 4x4.
/*
Copyright (c) 2020 J-M-L https://forum.arduino.cc/index.php?action=profile;u=438300
Author : J-M-L
Create Time : May 2020
Change Log :
The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
USING LIBRARIES (c) their own authors. See their terms and conditions
Keypad.h https://github.com/Chris--A/Keypad
LiquidCrystal_I2C.h https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library
*/
/* LE CLAVIER */
#include <Keypad.h> // https://github.com/Chris--A/Keypad
const uint8_t KBD_L = 4;
const uint8_t KBD_C = 4;
uint8_t rowPins[] = { 7, 6, 5, 4 };
uint8_t colPins[] = {11, 10, 9, 8};
char keys[KBD_L][KBD_C] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
Keypad clavier = Keypad( makeKeymap(keys), rowPins, colPins, KBD_L, KBD_C);
/* LE LCD */
#include <LiquidCrystal_I2C.h> // https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library
const byte LCD_A = 0x3F; // adresse du LCD en 0x3F
const byte LCD_L = 4; // 4 Lignes
const byte LCD_C = 20; // de 20 caractères
LiquidCrystal_I2C lcd(LCD_A , LCD_C, LCD_L);
/* GESTION SIMPLE D'UN MENU */
/* un menu est composé de plusieurs lignes de commandes */
/* chaque ligne présente un texte, à une touche d'activation et une fonction à appeler */
struct t_LigneMenu {
const char* texte; // le texte à afficher
const char touche; // la touche du keypad à appuyer
void (*action)(char); // la fonction a apppeler en cas d'appui, avec en paramètre la touche de commande
};
struct t_menu {
const uint8_t nbLignes; // le nombre de lignes actives dans le menu
t_LigneMenu menu[LCD_L]; // les lignes, au maximum le nombre de lignes du LCD
};
// on a la notion de menu actif, celui qui s'affiche quand
// on appele la fonction afficheMenu()
// si ce menuActif est NULL c'est qu'on n'a pas de menu à afficher.
t_menu* menuActif = NULL;
// on a deux fonctions utilitaires, une pour afficher le menu en cours
// l'autre pour gérer les interactions avec le Menu (s'il est non NULL)
// retourne true si un menu était à afficher
bool afficheMenu()
{
if (menuActif == NULL) return false; // pas de menu
// sinon on efface l'écran et présente le menu
lcd.clear();
for (uint8_t c = 0; c < menuActif->nbLignes; c++) {
lcd.setCursor(0, c);
lcd.print(menuActif->menu[c].texte);
}
return true;
}
// retourne true si un menu a été déclenché, la fonction aura été appelée
bool gestionMenu()
{
if (menuActif == NULL) return false; // pas de menu
// sinon on regarde si un touche correspondant au menu est appuyée
char t = clavier.getKey();
if (t == NO_KEY) return false; // pas de touche appuyée
// sinon on regarde si elle correspond à une touche autorisée
bool executionMenu = false;
for (uint8_t c = 0; c < menuActif->nbLignes; c++) {
if (t == menuActif->menu[c].touche) { // on a trouvé la bonne touche
menuActif->menu[c].action(t); // on appelle la fonction associée
executionMenu = true; // on dit qu'on a effectué une action
break; // pas la peine d'aller voir plus loin, on sort du for
}
}
return executionMenu;
}
// ***********************************************************
// le menu appelle des fonctions qui sont de signature 'void f(char) {...}'
// on pré-définit ces fonctions pour pouvoir déclarer les menus
void actionsSousMenu(char c);
void actionsA(char c);
void actionsB(char c);
// maintenant on définit les menus
t_menu menuPrincipal = {
2, // 2 entrées actives
{
{"[A] fonction A", 'A', actionsSousMenu},
{"[B] fonction B", 'B', actionsSousMenu},
{NULL, '\0', NULL},
{NULL, '\0', NULL}
}
};
t_menu sousMenuA = {
3, // 3 entrées actives
{
{"[1] fonction A1", '1', actionsA},
{"[2] fonction A2", '2', actionsA},
{"[3] fonction A3", '3', actionsA},
{NULL, '\0', NULL}
}
};
t_menu sousMenuB = {
4, // 4 entrées actives
{
{"[1] fonction B1", '1', actionsB},
{"[2] fonction B2", '2', actionsB},
{"[3] fonction B3", '3', actionsB},
{"[4] fonction B4", '4', actionsB}
}
};
// On définit ce que doivent faire les fonctions.
// ici le menu principal (A/B) affiche un sous menu associé
// qui appelleront soit la fonction actionsA ou actionsB
// avec en paramètre la touche de déclenchement ce qui permet de savoir quoi faire
void actionsSousMenu(char c)
{
Serial.print(F("Appel sous menu, touche ["));
Serial.write(c);
Serial.println(F("]"));
switch (c) {
case 'A':
menuActif = &sousMenuA; // on choisit le prochain menu
break;
case 'B':
menuActif = &sousMenuB; // on choisit le prochain menu
break;
default: // ne devrait pas arriver on conserve le même menu
break;
}
}
void actionsA(char c)
{
Serial.print(F("Actions A — choix ["));
Serial.write(c);
Serial.println(F("]"));
menuActif = &menuPrincipal; // on revient au menu principal
}
void actionsB(char c)
{
Serial.print(F("Actions B — choix ["));
Serial.write(c);
Serial.println(F("]"));
menuActif = &menuPrincipal; // on revient au menu principal
}
// ----------------------
void setup()
{
Serial.begin(115200);
lcd.begin();
lcd.backlight();
lcd.clear();
menuActif = &menuPrincipal; // on dit quel est le menu actif
afficheMenu(); // et on l'affiche
}
void loop()
{
if (gestionMenu()) afficheMenu();// si un déclenchement a été fait, il y a peut-être un nouveau menu
// ici on peut faire autre chose
// ne pas toucher l'affichage si un menu est actif bien sûr (sinon le menu disparait)
}
Vous définissez vos menus dans une structure t_menu
et il y a deux fonctions afficheMenu()
et gestionMenu()
qui font le job pour vous ensuite de superviser ce qui est appuyé sur le clavier et d'appeler la bonne fonction associée.
Il faut initialiser un menu en définissant le menu actif et en l'affichant
menuActif = &menuPrincipal; // on dit quel est le menu actif
afficheMenu(); // et on l'affiche
Lancez le code et regardez ce que dit le LCD et ce qui dit le moniteur Série ouvert à 115200 bauds. Vous verrez vos commandesle moniteur série () 115200 bauds) affichera
[color=purple][nobbc]
Appel sous menu, touche [A]
Actions A — choix [1]
Appel sous menu, touche [A]
Actions A — choix [2]
Appel sous menu, touche [A]
Actions A — choix [3]
Appel sous menu, touche [B]
Actions B — choix [1]
Appel sous menu, touche [B]
Actions B — choix [2]
Appel sous menu, touche [B]
Actions B — choix [3]
Appel sous menu, touche [B]
Actions B — choix [4][/nobbc]
[/color]