#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();
}