samedi 15 décembre 2018

Menus de navigation sur écran Nokia 5110 (Arduino)

Dans ce quatrième billet consécutif mettant en vedette un petit écran LCD de type Nokia 5110 contrôlé par un Arduino Uno, je vous présente un sketch qui permet de naviguer dans des menus au moyen de 4 boutons poussoirs.

(Dans un autre article, ce sketch a été adapté pour un écran OLED SH1106 I2C).

Le menu principal comporte un certain nombre de sous-menus. Chaque sous-menu donne accès à une deuxième page comportant une liste d'items pouvant être sélectionnés par l'utilisateur.

Le bouton "flèche vers le bas" permet de sélectionner l'item se situant une ligne plus bas dans la liste. Le bouton "flèche vers le haut" permet de sélectionner l'item se situant une ligne plus haut dans la liste. Le bouton "flèche vers la droite" permet d'exécuter l'item de la liste qui est sélectionné. Le bouton "flèche vers la gauche" permet de revenir à la page précédente.

Le nombre de sous-menus dans le menu principal ainsi que le nombre d'items dans chaque sous-menu peu facilement être modifié à l'intérieur du sketch.

 Je vous présente immédiatement une courte vidéo montrant le résultat:

   

Le circuit  

L'écran Nokia est branché à l'Arduino de la même façon que dans mes 3 billets précédents: un circuit intégré 4050 a été utilisé afin d'abaisser à 3,3 V les tension de sortie de l'Arduino Uno (voir ce précédent billet pour plus de détails concernant les branchements du 4050).
  • La broche SCE de l'afficheur reçoit le signal provenant de la broche 4 de l'Arduino
  • La broche RST de l'afficheur reçoit le signal provenant de la broche 3 de l'Arduino
  • La broche D/C de l'afficheur reçoit le signal de la broche 5 de l'Arduino
  • La broche DN/MOSI de l'afficheur reçoit le signal de la broche 11 de l'Arduino
  • La broche SCLK de l'afficheur reçoit le signal de la broche 13 de l'Arduino
De plus, 4 boutons sont associés à une résistance de tirage de 10 kΩ et branchés à l'Arduino de la façon suivante:
  • bouton "flèche vers le haut": broche 6 de l'Arduino
  • bouton "flèche vers le bas": broche 7 de l'Arduino
  • bouton "flèche vers la gauche": broche 8 de l'Arduino
  • bouton "flèche vers la droite": broche 9 de l'Arduino


Le sketch

Pour utiliser ce sketch, il faut d'abord avoir installé les bibliothèques Adafruit-PCD8544-Nokia-5110-LCD-library et Adafruit-GFX-Library dans votre IDE Arduino.

La fonction loop() consiste essentiellement à surveiller l'état des boutons et, lorsque l'un d'eux a été enfoncé, modifier l'état des variables appropriées (un délai de 50 millisecondes a été prévu pour éviter les rebonds).

La fonction miseAJour() est responsable de dessiner le contenu de l'écran en fonction de l'état des variables ayant été modifiées au moyen des boutons. Par exemple, la variable NumeroMenu prend la valeur "0" lorsqu'il faut afficher le menu principal, mais devient "1" lorsque le sous-menu numéro 1 a été sélectionné. La variable NumeroItem contient le numéro de l'élément de la liste qui est sélectionné (donc écriture blanche sur fond noir).  Puisque l'écran ne peut montrer que 4 éléments de la liste à la fois, mais que la liste peut contenir plus de 4 éléments. la variable numeroTeteDeListe contient le numéro du premier élément à afficher dans le haut de l'écran.

Bien entendu, le texte de chaque liste peut être modifié à volonté de façon à produire un programme qui répond à vos besoins (dans son état actuel, chaque titre doit comporter un maximum de 14 caractères, ce qui est sensiblement la taille maximale pouvant être affichée sur une ligne de l'écran).


/*****************************************************
Système de menus de navigation contrôlé par 4 boutons
poussoirs sur écran Nokia 5110 et Arduino.
Pour plus d'informations:
https://electroniqueamateur.blogspot.com/2018/12/menus-de-navigation-sur-ecran-nokia.html
*****************************************************/
// bibliothèques pour l'utilisation de l'écran
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
// broches utilisées par l'écran Nokia
Adafruit_PCD8544 display = Adafruit_PCD8544(5, 4, 3);
// broches utilisées par les boutons poussoirs
#define boutonHAUT 6
#define boutonBAS 7
#define boutonGAUCHE 8
#define boutonDROITE 9
#define nombreDeSousMenus 6 // nombre d'items dans la liste du menu principal
const int nombredItems[nombreDeSousMenus] = {5, 4, 3, 6, 2, 4 }; // nombre d'items dans chacun des sous-menus
int nombreMaxDeSousMenus; // valeur maximale contenue dans le tableau nombredItems (sera calculée dans calculeMax()
int numeroMenu = 0; // numéro du menu affiché à l'écran; on commence par 0, le menu principal
int numeroItem = 1; // numéro de l'item sélectionné; on commence par 1, le premier élément en haut de la liste
int numeroTeteDeListe = 1; // numéro de l'item affiché en haut de la liste visible à l'écran (utile pour les listes plus longues que 4 éléments)
int etatBoutons = 0; // nul quand aucun bouton n'est enfoncé
int etatBoutonsPrecedent = 0; // nul si aucun bouton n'était enfoncé au précédent passage dans la boucle
unsigned long tempsAntiRebond = 0; // dernière fois qu'un changement d'état de bouton a été détecté
const unsigned long delaiAntiRebond = 50; // délai entre le dernier changement d'état de bouton et le début de l'action
// on calcule le nombre maximum d'items qu'il peut y avoir dans un sous-menu
int calculeMax (void) {
int maxim = 0;
for (int i = 0; i < nombreDeSousMenus; i++) {
if (nombredItems[i] > maxim) {
maxim = nombredItems[i];
}
}
nombreMaxDeSousMenus = maxim;
}
void setup() {
pinMode(boutonHAUT, INPUT);
pinMode(boutonBAS, INPUT);
pinMode(boutonGAUCHE, INPUT);
pinMode(boutonDROITE, INPUT);
calculeMax();
display.begin();
display.setContrast(60); // généralement entre 40 et 60: ça dépend de votre écran
display.clearDisplay();
miseAjour();
}
void loop() {
int nombreDeLignes;
int lectureBoutons, lectureBAS, lectureHAUT, lectureGAUCHE, lectureDROITE;
if (numeroMenu == 0) { // menu principal
nombreDeLignes = nombreDeSousMenus;
}
else { // pas le menu principal
nombreDeLignes = nombredItems[numeroMenu - 1];
}
// on vérifie si un des boutons est enfoncé
lectureBAS = digitalRead(boutonBAS);
lectureHAUT = digitalRead(boutonHAUT);
lectureGAUCHE = digitalRead(boutonGAUCHE);
lectureDROITE = digitalRead(boutonDROITE);
// procédure anti-rebonds
lectureBoutons = (lectureBAS || lectureHAUT || lectureGAUCHE || lectureDROITE) ;
if (lectureBoutons != etatBoutonsPrecedent) {
tempsAntiRebond = millis();
}
if ((millis() - tempsAntiRebond) > delaiAntiRebond) {
if (lectureBoutons != etatBoutons) {
// le bouton est nouvellement enfoncé depuis un temps raisonnable
etatBoutons = lectureBoutons;
if ( lectureHAUT ) { // bouton flèche vers le haut (on monte d'une ligne dans la liste)
if (numeroItem > 1) {
numeroItem--;
}
else {
numeroItem = nombreDeLignes;
numeroTeteDeListe = nombreDeLignes - 2;
if (numeroTeteDeListe == 0)
{
numeroTeteDeListe = 1;
}
}
if (numeroTeteDeListe > 1) {
numeroTeteDeListe--;
}
miseAjour();
}
if ( lectureBAS ) { // bouton flèche vers le bas: on descend d'une ligne dans la liste
if (numeroItem < nombreDeLignes) {
numeroItem++;
}
else {
numeroItem = 1;
numeroTeteDeListe = 1;
}
if (numeroItem > 4) {
numeroTeteDeListe++;
}
miseAjour();
}
if ( lectureGAUCHE ) { // bouton flèche vers la gauche: on recule à la page précédente
if ((numeroMenu > 0) && (numeroMenu < 100)) {
numeroItem = numeroMenu;
numeroMenu = 0;
if (numeroItem <= 4) {
numeroTeteDeListe = 1;
}
else {
numeroTeteDeListe = numeroItem - 3;
}
}
else if (numeroMenu > 100) {
numeroItem = numeroMenu % 100;
numeroMenu = numeroMenu / 100;
}
miseAjour();
}
if ( lectureDROITE ) { // bouton flèche vers la droite: on exécute le menu sélectionné
if (numeroMenu == 0) {
numeroMenu = numeroItem;
numeroTeteDeListe = 1;
numeroItem = 1;
}
else {
numeroMenu = numeroMenu * 100 + numeroItem;
}
miseAjour();
}
}
}
etatBoutonsPrecedent = lectureBoutons;
}
void miseAjour(void) // on dessine ce qui doit apparaître à l'écran
{
char* titresSousMenus[] = {"Sous-menu 1", "Sous-menu 2", "Sous-menu 3",
"Sous-menu 4", "Sous-menu 5", "Sous-menu 6"
};
char menuStr[15], itemAStr[15], itemBStr[15], itemCStr[15], itemDStr[15] ;
char titresItems[nombreMaxDeSousMenus][15];
int nombreDeLignes;
display.clearDisplay();
if (numeroMenu < 100) { // nous sommes dans une page de menus
if (numeroMenu == 0) { // menu principal
nombreDeLignes = nombreDeSousMenus;
}
else { // pas le menu principal
nombreDeLignes = nombredItems[numeroMenu - 1];
}
if (numeroMenu == 1) { // sous-menu 1
strcpy(titresItems[0], "Item 1.1");
strcpy(titresItems[1], "Item 1.2");
strcpy(titresItems[2], "Item 1.3");
strcpy(titresItems[3], "Item 1.4");
strcpy(titresItems[4], "Item 1.5");
}
if (numeroMenu == 2) { // sous-menu 2
strcpy(titresItems[0], "Item 2.1");
strcpy(titresItems[1], "Item 2.2");
strcpy(titresItems[2], "Item 2.3");
strcpy(titresItems[3], "Item 2.4");
}
if (numeroMenu == 3) { // sous-menu 1
strcpy(titresItems[0], "Item 3.1");
strcpy(titresItems[1], "Item 3.2");
strcpy(titresItems[2], "Item 3.3");
}
if (numeroMenu == 4) { // sous-menu 1
strcpy(titresItems[0], "Item 4.1");
strcpy(titresItems[1], "Item 4.2");
strcpy(titresItems[2], "Item 4.3");
strcpy(titresItems[3], "Item 4.4");
strcpy(titresItems[4], "Item 4.5");
strcpy(titresItems[5], "Item 4.6");
}
if (numeroMenu == 5) { // sous-menu 1
strcpy(titresItems[0], "Item 5.1");
strcpy(titresItems[1], "Item 5.2");
}
if (numeroMenu == 6) { // sous-menu 2
strcpy(titresItems[0], "Item 6.1");
strcpy(titresItems[1], "Item 6.2");
strcpy(titresItems[2], "Item 6.3");
strcpy(titresItems[3], "Item 6.4");
}
display.setCursor(0, 0);
display.setTextSize(1);
display.setTextColor(BLACK);
// écriture du titre de la page
if (numeroMenu == 0) { // menu principal
strcpy(menuStr, "Menu principal");
}
else {
strcpy(menuStr, titresSousMenus[numeroMenu - 1]);
}
display.println(menuStr);
// rectangle noir à la position de l'item sélectionné
display.fillRect(0, 10 * (numeroItem - numeroTeteDeListe + 1) - 1, 84, 10, BLACK);
// écriture du premier item de la liste
if (numeroMenu == 0) { // menu principal
strcpy(itemAStr, titresSousMenus[numeroTeteDeListe - 1]);
}
else {
strcpy(itemAStr, titresItems[numeroTeteDeListe - 1]);
}
if (numeroItem == 1) {
display.setTextColor(WHITE);
}
else {
display.setTextColor(BLACK);
}
display.setCursor(5, 10);
display.println(itemAStr);
// écriture du 2e item de la liste
if (nombreDeLignes >= 2) {
if (numeroMenu == 0) { // menu principal
strcpy(itemBStr, titresSousMenus[numeroTeteDeListe]);
}
else {
strcpy(itemBStr, titresItems[numeroTeteDeListe]);
}
if (numeroItem == 2) {
display.setTextColor(WHITE);
}
else {
display.setTextColor(BLACK);
}
display.setCursor(5, 20);
display.println(itemBStr);
}
// écriture du 3e item de la liste
if (nombreDeLignes >= 3) {
if (numeroMenu == 0) { // menu principal
strcpy(itemCStr, titresSousMenus[numeroTeteDeListe + 1]);
}
else {
strcpy(itemCStr, titresItems[numeroTeteDeListe + 1]);
}
if (numeroItem == 3) {
display.setTextColor(WHITE);
}
else {
display.setTextColor(BLACK);
}
display.setCursor(5, 30);
display.println(itemCStr);
}
// écriture du 4e item de la liste
if (nombreDeLignes >= 4) {
if (numeroMenu == 0) { // menu principal
strcpy(itemDStr, titresSousMenus[numeroTeteDeListe + 2]);
}
else {
strcpy(itemDStr, titresItems[numeroTeteDeListe + 2]);
}
if (numeroItem >= 4) {
display.setTextColor(WHITE);
}
else {
display.setTextColor(BLACK);
}
display.setCursor(5, 40);
display.println(itemDStr);
}
}
else { // ce n'est pas une page de menu
/* C'est ici que vous pourrez accomplir les actions découlant d'un choix de menu
Par exemple, si la personne a sélectionné l'item 3 du sous-menu 2, la variable
numeroMenu vaut 203 (numéro du sous-menu * 100 + numéro de l'item */
display.setCursor(0, 0);
display.setTextColor(BLACK);
display.println("Vous avez");
display.println("choisi");
display.print("l'item ");
display.println(numeroMenu % 100);
display.print("du sous-menu ");
display.println(numeroMenu / 100);
}
display.display();
}
view raw Nokia_menus.ino hosted with ❤ by GitHub


Yves Pelletier   (TwitterFacebook)

Aucun commentaire:

Enregistrer un commentaire