/* KY-040 Écriture d'une chaîne de caractères au moyen d'un codeur rotatif
Tourner pour faire défiler les caractères.
Appuyer une fois pour valider le caractère et passant au caractère suivant
Faire un appui long (inférieur à 3 secondes) pour effacer le caractère courant et revenir en arrière
Faire un appui très long (supérieur à 3 secondes) pour effacer toute la chaîne de caractères
Faire un double appui pour valider et mémoriser la chaîne de caractères.
*/
#include "OneButton.h" // Bibliothèque de traitement d'un codeur rotatif
OneButton ENCODER_SW(4, true) ; // Affectation de la broche du poussoir du codeur rotatif
#include <LiquidCrystal_I2C.h> // Bibliothèque de gestion des afficheurs LCD alphanumériques
LiquidCrystal_I2C lcd(0x27, 20, 4) ; // Paramétrage de l'afficheur LCD
#define ENCODER_CLK 2 // Affectation de la broche d'horloge du codeur rotatif
#define ENCODER_DT 3 // Affectation de la broche de donnée du coeur rotatif
//char accueil[80] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZab" ; // Texte d'Accueil
char accueil[80] = " Horloge ESP32 ------------- " ; // Texte d'Accueil
uint8_t start_LCD_backlight[2] = {7,30} ; // Heure de démarrage du rétroéclairage de l'afficheur LCD
uint8_t stop_LCD_backlight[2] = {23,30} ; // Heure d'arrêt du rétroéclairage de l'afficheur LCD
uint8_t start_Carillon[2] = {8,30} ; // Heure de démarrage du Carillon
uint8_t stop_Carillon[2] = {21,15} ; // Heure d'arrêt du Carillon
char ssid[32] = "Wokwi-GUEST" ; // Nom du réseau WiFi
char password[32] = "" ; // Mot de passe du réseau WiFi
char hostname[32] = "Horloge ESP32" ; // Nom de l'hôte sur le réseau WiFi
char ntpServer[32] = "pool.ntp.org" ; // Adresse du serveur NTP
char timeZone[64] = "CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00" ; // Définition du fuseau horaire (voir https://sites.google.com/a/usapiens.com/opnode/time-zones)
uint8_t counterEncoderChar = 32 ; // Compteur de code ASCII du codeur rotatif
uint8_t counterEncoderHeure = 0 ; // Compteur des heures du codeur rotatif
uint8_t counterEncoderMinute = 0 ; // Compteur des minutes du codeur rotatif
uint8_t menu = 0 ; // Numéro du Menu
char Texte[64] ; // Chaîne de caractères à afficher
String Texte_1 ; // Texte de la première ligne
String Texte_2 ; // Texte de la deuxième ligne
String Texte_3 ; // Texte de la troisième ligne
String Texte_4 ; // Texte de la quatrième ligne
char Result[64] = "" ; // Chaîne de caractères validée
uint8_t posChar = 0 ; // Position du caractère à entrer
uint32_t clickTempo = 0 ; // Temporisation pour l'appuis très long
// Programme d'initialisation à n'exécuter qu'une seule fois.
void setup() {
// Mise en service de la communication série vers la console
Serial.begin(115200) ; // Vitesse de communication : 115 200 Bauds
// Initialisation de l'afficheur LCD
lcd.init() ;
lcd.backlight() ;
// Initialisation des broches du codeur rotatif
pinMode(ENCODER_CLK, INPUT) ; // Broche d'horloge
pinMode(ENCODER_DT, INPUT) ; // Broche de donnée
// Initialisation des interruptions pour le codeur rotatif
attachInterrupt(digitalPinToInterrupt(ENCODER_CLK), readEncoder, FALLING) ; // Initialisation des interruptions
ENCODER_SW.attachClick(clickENCODER_SW) ; // Simple appui
ENCODER_SW.attachDoubleClick(doubleclickENCODER_SW) ; // Double appui
ENCODER_SW.attachLongPressStart(longPressStartENCODER_SW) ; // Début d'un appui long
ENCODER_SW.attachLongPressStop(longPressStopENCODER_SW) ; // Appui long en cours
ENCODER_SW.attachDuringLongPress(longPressENCODER_SW) ; // Fin d'un appui long
posChar = String(Texte).length() - 1 ;
counterEncoderChar = Texte[posChar] ;
Serial.println("Horloge ESP32") ;
Serial.println("") ;
}
// Fonction de lecture de l'état du codeur rotatif
void readEncoder() {
int dtValue = digitalRead(ENCODER_DT) ; // Lecture de l'état de la broche de donnée
if (dtValue == HIGH) { // Si la broche est au niveau Haut, alors :
if (counterEncoderChar < 127) counterEncoderChar ++ ; // Le compteur de code ASCII du codeur rotatif est incrémenté
else counterEncoderChar = 32 ; // Dépassement du compteur de code ASCII
if (counterEncoderHeure < 23) counterEncoderHeure ++ ; // Le compteur des heures du codeur rotatif est incrémenté
else counterEncoderHeure = 0 ; // Dépassement du compteur des heures
if (counterEncoderMinute < 59) counterEncoderMinute ++ ; // Le compteur des minutes du codeur rotatif est incrémenté
else counterEncoderMinute = 0 ; // Dépassement du compteur des minutes
}
if (dtValue == LOW) { // Si la broche est au niveau Bas, alors :
if (counterEncoderChar > 32 ) counterEncoderChar -- ; // le compteur de code ASCII du codeur rotatif est décrémenté
else counterEncoderChar = 127 ; // Dépassement du compteur de code ASCII
if (counterEncoderHeure != 0) counterEncoderHeure -- ; // Le compteur des minutes du codeur rotatif est décrémenté
else counterEncoderHeure = 23 ; // Dépassement du compteur des minutes
if (counterEncoderMinute != 0) counterEncoderMinute -- ; // Le compteur des minutes du codeur rotatif est décrémenté
else counterEncoderMinute = 59 ; // Dépassement du compteur des minutes
}
}
// Fonction de lecture du compteur du codeur rotatif
int getCounterEncoderChar() {
int result ; // Défini la variable "result"
noInterrupts(); // Désactive les interrruptions pour s'assurer que readEncoder() ne change pas la valeur pendant qu'on la lit.
result = counterEncoderChar; // Lecture du compteur du codeur rotatif
interrupts(); // Activation des interruptions
return result;
}
int getCounterEncoderHeure() {
int result ; // Défini la variable "result"
noInterrupts(); // Désactive les interrruptions pour s'assurer que readEncoder() ne change pas la valeur pendant qu'on la lit.
result = counterEncoderHeure; // Lecture du compteur du codeur rotatif
interrupts(); // Activation des interruptions
return result;
}
int getCounterEncoderMinute() {
int result ; // Défini la variable "result"
noInterrupts(); // Désactive les interrruptions pour s'assurer que readEncoder() ne change pas la valeur pendant qu'on la lit.
result = counterEncoderMinute; // Lecture du compteur du codeur rotatif
interrupts(); // Activation des interruptions
return result;
}
// Programme principal à exécuter en boucle répétitive
void loop() {
ENCODER_SW.tick() ; // Lecture de l'état du poussoir du codeur rotatif
switch(menu) {
case 0 :
lcd.setCursor(0,0) ; lcd.print(String(accueil).substring(0,20)) ; // Affichage LCD du texte d'Accueil
lcd.setCursor(0,1) ; lcd.print(String(accueil).substring(20,40)) ; // Affichage LCD du texte d'Accueil
lcd.setCursor(0,2) ; lcd.print(String(accueil).substring(40,60)) ; // Affichage LCD du texte d'Accueil
lcd.setCursor(0,3) ; lcd.print(String(accueil).substring(60,80)) ; // Affichage LCD du texte d'Accueil
break ;
case 11 : // Affichage de la plage horaire du rétroéclairage de l'afficheur LCD
case 12 :
case 13 :
case 14 :
LCD_Menu10("Eclairage Afficheur ", start_LCD_backlight, stop_LCD_backlight) ;
break ;
case 15 : // Affichage de la plage horaire du Carillon
case 16 :
case 17 :
case 18 :
LCD_Menu10("Fonction Carillon ", start_Carillon, stop_Carillon) ;
break ;
case 21 : // Affichage du nom du réseau WiFi
LCD_Menu20("SSID WiFi : ") ;
break ;
case 22 : // Affichage du mot de passe du réseau WiFi
LCD_Menu20("Mot de Passe WiFi : ") ;
break ;
case 23 : // Affichage du nom de l'hôte sur le réseau WiFi
LCD_Menu20("Nom d'Hote WiFi : ") ;
break ;
case 24 : // Affichage de l'adresse du serveur NTP
LCD_Menu20("Serveur NTP : ") ;
break ;
case 25 : // Affichage de la définition du fuseau horaire
LCD_Menu20("Fuseau Horaire : ") ;
break ;
}
}
void LCD_Menu10(char* EnTete, uint8_t start[2], uint8_t stop[2]) {
switch(menu) {
case 11 :
case 15 :
start[0] = getCounterEncoderHeure() ;
break ;
case 12 :
case 16 :
start[1] = getCounterEncoderMinute() ;
break ;
case 13 :
case 17 :
stop[0] = getCounterEncoderHeure() ;
break ;
case 14 :
case 18 :
stop[1] = getCounterEncoderMinute() ;
break ;
}
Texte_1 = String(EnTete) ;
if (((menu >= 11) && (menu <= 14)) || ((menu >= 15) && (menu <= 18))) {
Texte_2 = String("de ") ;
if (start[0] < 10) Texte_2 += "0" ;
Texte_2 += start[0] ;
}
if (((menu >= 12) && (menu <= 14)) || ((menu >= 16) && (menu <= 18))) {
Texte_2 += ":" ;
if (start[1] < 10) Texte_2 += "0" ;
Texte_2 += start[1] ;
}
if (((menu >= 13) && (menu <= 14)) || ((menu >= 17) && (menu <= 18))) {
Texte_2 += String(" a ") ;
if (stop[0] < 10) Texte_2 += "0" ;
Texte_2 += stop[0] ;
}
if ((menu == 14) || (menu == 18)) {
Texte_2 += ":" ;
if (stop[1] < 10) Texte_2 += "0" ;
Texte_2 += stop[1] ;
}
Texte_3 = String(" ") ;
Texte_4 = String(" ") ;
lcd.setCursor(0,0) ; lcd.print(Texte_1) ; // Affichage LCD de la fonction
lcd.setCursor(0,1) ; lcd.print(Texte_2) ; // Affichage LCD du texte entrée
}
void LCD_Menu20(char* EnTete) {
Texte[posChar] = getCounterEncoderChar() ; // Lecture du nouveau caractère
Texte_1 = String(EnTete) ;
Texte_2 = String(Texte).substring(0,20) ;
Texte_3 = String(Texte).substring(20,40) ;
Texte_4 = String(Texte).substring(40,80) ;
lcd.setCursor(0,0) ; lcd.print(Texte_1) ; // Affichage LCD de la fonction
lcd.setCursor(0,1) ; lcd.print(Texte_2) ; // Affichage LCD du texte entrée
lcd.setCursor(0,2) ; lcd.print(Texte_3) ; // Affichage LCD du texte entrée
lcd.setCursor(0,3) ; lcd.print(Texte_4) ; // Affichage LCD du texte entrée
}
void saveConfig(char* Config) {
uint8_t size = sizeof(Config) ;
if (Texte[0] == 32) for(uint8_t i = 0 ; i < size ; i ++) Texte[i] = 0 ;
if (String(Texte).length() == 0) {
for(uint8_t i = 0 ; i < size ; i ++) Config[i] = 0 ;
}
else {
for(uint8_t i = 0 ; i < String(Texte).length() ; i ++) {
if ((Texte[i] <= 32) && (Texte[i+1] <= 32)) Config[i] = 0 ;
else Config[i] = Texte[i] ;
}
for(uint8_t i = (String(Texte).length()) ; i < size ; i ++) Config[i] = 0 ;
}
// Affichage sur le terminal de la chaîne de caractères mémorisée
switch(menu) {
case 21 : // Affichage du nom du réseau WiFi
Serial.print("SSID WiFi : ") ;
break ;
case 22 : // Affichage du mot de passe du réseau WiFi
Serial.print("Mot de Passe WiFi : ") ;
break ;
case 23 : // Affichage du nom de l'hôte sur le réseau WiFi
Serial.print("Nom d'Hote WiFi : ") ;
break ;
case 24 : // Affichage de l'adresse du serveur NTP
Serial.print("Serveur NTP : ") ;
break ;
case 25 : // Affichage de la définition du fuseau horaire
Serial.print("Fuseau Horaire : ") ;
break ;
}
Serial.print((char)34) ;
Serial.print(Config) ;
Serial.println((char)34) ;
}
void readConfig(char* Config) {
lcd.clear() ;
if (String(Config).length() == 0) {
posChar = 0 ;
for(uint8_t i = 0 ; i < 64 ; i ++) Texte[i] = 0 ;
counterEncoderChar = 32 ;
}
else {
posChar = String(Config).length() - 1 ;
for(uint8_t i = 0 ; i < (posChar + 1) ; i ++) Texte [i] = Config[i] ;
for(uint8_t i = (posChar + 1) ; i < 64 ; i ++) Texte[i] = 0 ;
counterEncoderChar = Texte[posChar] ;
}
}
// Appel des fonctions du poussoir du codeur rotatif
// Cette fonction sera appelée lorsque le poussoir du codeur rotatif a été appuyé 1 fois.
void clickENCODER_SW() {
switch(menu) {
case 0 :
menu = 11 ;
counterEncoderHeure = start_LCD_backlight[0] ;
lcd.clear() ;
break ;
case 11 :
menu = 12 ;
counterEncoderMinute = start_LCD_backlight[1] ;
break ;
case 12 :
menu = 13 ;
counterEncoderHeure = stop_LCD_backlight[0] ;
break ;
case 13 :
menu = 14 ;
counterEncoderMinute = stop_LCD_backlight[1] ;
break ;
case 15 :
menu = 16 ;
counterEncoderMinute = start_Carillon[1] ;
break ;
case 16 :
menu = 17 ;
counterEncoderHeure = stop_Carillon[0] ;
break ;
case 17 :
menu = 18 ;
counterEncoderMinute = stop_Carillon[1] ;
break ;
case 21 :
case 22 :
case 23 :
case 24 :
case 25 :
posChar ++ ; // Validation du caractère courant et déplacement d'un caractère
break ;
}
}
// Cette fonction sera appelée lorsque le poussoir du codeur rotatif a été appuyé 2 fois dans un court laps de temps.
void doubleclickENCODER_SW() {
switch(menu) {
case 14 :
menu = 15 ;
counterEncoderHeure = start_Carillon[0] ;
lcd.clear() ;
break ;
case 18 :
menu = 0 ;
lcd.clear() ;
break ;
case 21 :
saveConfig(ssid) ;
menu = 22 ;
readConfig(password) ;
break ;
case 22 :
saveConfig(password) ;
menu = 23 ;
readConfig(hostname) ;
break ;
case 23 :
saveConfig(hostname) ;
menu = 24 ;
readConfig(ntpServer) ;
break ;
case 24 :
saveConfig(ntpServer) ;
menu = 25 ;
readConfig(timeZone) ;
break ;
case 25 :
saveConfig(timeZone) ;
Serial.println("") ;
menu = 0 ;
lcd.clear() ;
break ;
}
}
// Cette fonction sera appelée une fois, lorsque le poussoir du codeur rotatif sera appuyé longtemps
void longPressStartENCODER_SW() {
switch(menu) {
case 0 :
menu = 21 ;
readConfig(ssid) ;
break ;
case 12 :
menu = 11 ;
counterEncoderHeure = start_LCD_backlight[0] ;
lcd.setCursor(5,1) ; lcd.print(" ") ;
break ;
case 13 :
menu = 12 ;
counterEncoderMinute = start_LCD_backlight[1] ;
lcd.setCursor(8,1) ; lcd.print(" ") ;
break ;
case 14 :
menu = 13 ;
counterEncoderHeure = stop_LCD_backlight[0] ;
lcd.setCursor(13,1) ; lcd.print(" ") ;
break ;
case 16 :
menu = 15 ;
counterEncoderHeure = start_Carillon[0] ;
lcd.setCursor(5,1) ; lcd.print(" ") ;
break ;
case 17 :
menu = 16 ;
counterEncoderMinute = start_Carillon[1] ;
lcd.setCursor(8,1) ; lcd.print(" ") ;
break ;
case 18 :
menu = 17 ;
counterEncoderHeure = stop_Carillon[0] ;
lcd.setCursor(13,1) ; lcd.print(" ") ;
break ;
case 21 :
case 22 :
case 23 :
case 24 :
case 25 :
if (posChar == 0) { // Premier caractère ?
counterEncoderChar = 32 ; // Réinitialisation du compteur de caractères (affichage du caractère espace)
return ;
}
Texte[posChar] = 32 ; // Effacement du caractère courant
counterEncoderChar = Texte[posChar - 1] ; // Positionnement du compteur de caractères à la valeur du caractère précédent
posChar -- ; // Déplacement d'un caractère à gauche
break ;
}
clickTempo = millis() + 3000 ; // Initialisation de la temporisation pour un appui très long
}
// Cette fonction sera appelée en continu, alors que le poussoir du codeur rotatif est appuyé longtemps
void longPressENCODER_SW() {
if (millis() > clickTempo) { // Est-ce un appuis long ?
switch(menu) {
case 11 :
menu = 21 ;
readConfig(ssid) ;
break ;
case 21 :
case 22 :
case 23 :
case 24 :
case 25 :
for (int8_t i = 0 ; i < 64 ; i ++) Texte[i] = 0 ; // Effacement du texte
lcd.clear() ; // Effacement du LCD
posChar = 0 ; // Positionne sur le premier caractère
counterEncoderChar = 32 ; // Réinitialisation du compteur de caractères (affichage du caractère espace)
break ;
}
clickTempo = millis() + 30000 ; // Initialisation de la temporisation pour un appui très long
}
}
// Cette fonction sera appelée une fois, lorsque le poussoir du codeur rotatif sera relâché après avoir été appuyé longuement
void longPressStopENCODER_SW() {
}