// ====================================================================================================
// Projet: Contrôleur de Granules Ver. 6.39
// Auteur: [Votre Nom]
// Description: Correction finale du mappage des boutons tactiles après rotation d'écran.
// ====================================================================================================
// --- Inclusion des bibliothèques ---
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <SPI.h>
#include <Wire.h>
#include <RTClib.h>
#include <EEPROM.h>
// --- Type de base pour les entrées de menu ---
struct MenuOption { const char* nom; int id; };
#include <Adafruit_FT6206.h> // Tactile capacitif FT6206 (émulation joystick)
// ========= Barre de 5 boutons tactiles (bas de l'écran MENU) =========
// [UP] [DN] [LEFT] [RIGHT] [SEL]
// Auto-détection d'orientation FT6206 -> écran rotation(1) 320x240
Adafruit_FT6206 gc_ctp;
static bool gc_ctp_ok = false;
// États latched (impulsions) pour mimer le joystick
static int8_t gc_lastY = -1; // -1: none, 0:UP, 1:DN
static int8_t gc_lastX = -1; // -1: none, 0:LEFT, 1:RIGHT
static bool gc_lastB = false;
static unsigned long gc_msY = 0, gc_msX = 0, gc_msB = 0;
static bool gc_touchDown = false;
// Profil auto-détecté (-1 = inconnu, 0/1/2 = profiles)
static int8_t gc_autoProfile = -1;
// Dessin de la barre fine en bas (28 px)
extern Adafruit_ILI9341 Lcd_Menu;
static void drawTouchButtons() {
const int W = 320, H = 240;
const int h = 28;
const int yTop = H - h;
const int w = W / 5;
Lcd_Menu.fillRect(0, yTop, W, h, ILI9341_DARKGREY);
Lcd_Menu.drawFastHLine(0, yTop, W, ILI9341_BLACK);
for (int i=1;i<5;i++) Lcd_Menu.drawFastVLine(i*w, yTop, h, ILI9341_BLACK);
const char* labels[5] = {"UP","DN","LEFT","RIGHT","SEL"};
for (int i=0;i<5;i++) {
int x = i*w;
int bw = (i==4)? (W - x) : w;
Lcd_Menu.setTextSize(1);
Lcd_Menu.setTextColor(ILI9341_WHITE, ILI9341_DARKGREY);
int tx = x + (bw/2) - 9;
int ty = yTop + (h/2) - 4;
if (tx < x+1) tx = x+1;
if (ty < yTop+1) ty = yTop+1;
Lcd_Menu.setCursor(tx, ty);
Lcd_Menu.print(labels[i]);
}
}
static void highlightButton(int idx, uint16_t color = ILI9341_YELLOW) {
const int W=320, H=240, h=28, yTop=H-h, w=W/5;
int x = idx*w;
int bw = (idx==4)? (W-x) : w;
Lcd_Menu.drawRect(x, yTop, bw-2, h, color);
}
// Convertit TS_Point brut -> coords écran 320x240 selon un profil
// REMOVED: La logique de mappage est maintenant intégrée directement dans detectButtonIdx pour plus de précision.
// Détecte le bouton pressé en se basant sur les dimensions précises des zones de boutons.
static bool detectButtonIdx(const TS_Point &p, int &idx_out) {
const int W = 320, H = 240, h = 28, yTop = H - h;
const int w = W / 5;
// Correction : p.x et p.y sont les coordonnées brutes.
// En rotation 1, l'axe x de l'écran est l'axe y de la touche, et l'axe y de l'écran est l'axe x inversé de la touche.
int xs = p.y;
int ys = H - p.x; // L'axe y est inversé par rapport à l'axe x de la touche
if (ys >= yTop) {
// Le point de contact est dans la barre de boutons.
// Détermine le bouton en se basant sur les dimensions exactes.
for (int i=0; i<5; i++) {
int xMin = i * w;
int xMax = (i == 4) ? W : (i + 1) * w;
if (xs >= xMin && xs < xMax) {
idx_out = i;
return true;
}
}
}
return false;
}
// Poll tactile limité à la barre, avec highlight visuel
static void gc_pollButtons() {
if (!gc_ctp_ok) return;
bool down = gc_ctp.touched();
if (!down) { gc_touchDown = false; return; }
if (gc_touchDown) return; // un appui à la fois
gc_touchDown = true;
TS_Point pr = gc_ctp.getPoint();
int idx;
if (!detectButtonIdx(pr, idx)) return;
// Feedback visuel
highlightButton(idx);
unsigned long now = millis();
gc_lastY = -1; gc_lastX = -1; gc_lastB = false;
switch (idx) {
case 0: gc_lastY = 0; gc_msY = now; break; // UP
case 1: gc_lastY = 1; gc_msY = now; break; // DN
case 2: gc_lastX = 0; gc_msX = now; break; // LEFT
case 3: gc_lastX = 1; gc_msX = now; break; // RIGHT
case 4: gc_lastB = true; gc_msB = now; break; // SEL
}
}
// Fenêtres de latch (augmentées pour une meilleure réactivité)
const unsigned long GC_LATCH_MS_Y = 500;
const unsigned long GC_LATCH_MS_X = 500;
const unsigned long GC_LATCH_MS_B = 500;
// Wrappers compatibles avec le code existant
static int readJoyY() {
gc_pollButtons();
unsigned long now = millis();
if (gc_lastY == 0 && (now - gc_msY) < GC_LATCH_MS_Y) return 0;
if (gc_lastY == 1 && (now - gc_msY) < GC_LATCH_MS_Y) return 1023;
return 512;
}
static int readJoyX() {
gc_pollButtons();
unsigned long now = millis();
if (gc_lastX == 0 && (now - gc_msX) < GC_LATCH_MS_X) return 0;
if (gc_lastX == 1 && (now - gc_msX) < GC_LATCH_MS_X) return 1023;
return 512;
}
static int readJoyBtn() {
gc_pollButtons();
unsigned long now = millis();
if (gc_lastB && (now - gc_msB) < GC_LATCH_MS_B) return LOW;
return HIGH;
}
// --- Déclaration des broches ---
const int pinJoystickX = A0;
const int pinJoystickY = A1;
const int pinJoystickBtn = 2;
#define TFT_MENU_CS 8
#define TFT_MENU_DC 10
#define TFT_MENU_RST 9
#define TFT_INFOS_CS 13
#define TFT_INFOS_DC 11
#define TFT_INFOS_RST 12
#define PIN_RELAIS_POMPE 7
// --- Définitions de constantes pour les adresses EEPROM ---
#define EEPROM_ADDR_MAGIC_NUMBER 0 // Nouvelle adresse pour le numéro magique
#define EEPROM_ADDR_TEMPS_ON_MIN 1
#define EEPROM_ADDR_TEMPS_ON_SEC 2
#define EEPROM_ADDR_TEMPS_OFF_MIN 3
#define EEPROM_ADDR_TEMPS_OFF_SEC 4
#define EEPROM_ADDR_GRADE_GRANULES 5
#define EEPROM_ADDR_UNITE_CONSO 6
#define EEPROM_ADDR_GRANULES_PAR_MINUTE 7 // Nouvelle adresse pour le float (4 octets)
// --- Définitions de constantes pour les états ---
#define MENU_PRINCIPAL 0
#define SOUS_MENU_PARAMETRES 1
#define SOUS_MENU_DONNEES 2
#define MODE_MANUEL_EN_COURS 3
#define MODE_CYCLE_AUTOMATIQUE 4
#define REGLAGE_HEURE_EN_COURS 5
#define DEMARRER_ARRETER_CYCLE_AUTO 7
#define SOUS_MENU_TYPE_GRANULES 8
#define SOUS_MENU_UNITE_CONSO 9
#define REGLAGE_ETALONNAGE 10 // Nouvel état pour l'étalonnage
// --- Structures de données ---
// --- Tableaux des options de menu ---
MenuOption menuPrincipal[] = {
{"", DEMARRER_ARRETER_CYCLE_AUTO},
{"Cycle Auto", MODE_CYCLE_AUTOMATIQUE},
{"Parametres", SOUS_MENU_PARAMETRES},
{"Donnees", SOUS_MENU_DONNEES},
{"Mode Manuel", MODE_MANUEL_EN_COURS}
};
const int tailleMenuPrincipal = sizeof(menuPrincipal) / sizeof(MenuOption);
MenuOption sousMenuParametres[] = {
{"Reglage heure", REGLAGE_HEURE_EN_COURS},
{"Type de granules", SOUS_MENU_TYPE_GRANULES},
{"Consommation", SOUS_MENU_UNITE_CONSO},
{"Etalonnage", REGLAGE_ETALONNAGE},
{"Retour", MENU_PRINCIPAL}
};
const int tailleSousMenuParametres = sizeof(sousMenuParametres) / sizeof(MenuOption);
MenuOption sousMenuDonnees[] = {
{"Historique", -1},
{"Statistiques", -1},
{"Retour", MENU_PRINCIPAL}
};
const int tailleSousMenuDonnees = sizeof(sousMenuDonnees) / sizeof(MenuOption);
MenuOption sousMenuTypeGranules[] = {
{"Premium", 0},
{"Moyen", 1},
{"Economique", 2},
{"Retour", SOUS_MENU_PARAMETRES}
};
const int tailleSousMenuTypeGranules = sizeof(sousMenuTypeGranules) / sizeof(MenuOption);
MenuOption sousMenuUniteConso[] = {
{"Minute", 0},
{"Heure", 1},
{"Jour", 2},
{"Semaine", 3},
{"Mois", 4},
{"Annee", 5},
{"Retour", SOUS_MENU_PARAMETRES}
};
const int tailleSousMenuUniteConso = sizeof(sousMenuUniteConso) / sizeof(MenuOption);
// --- Variables globales pour l'état du menu ---
int menuActuel = MENU_PRINCIPAL;
int selectionActuelle = 0;
int selectionPrecedente = 0;
int champReglageHeure = 0;
int champPrecedentReglageHeure = 0;
int heureTemporaire;
int minuteTemporaire;
int jourTemporaire;
int moisTemporaire;
int anneeTemporaire;
bool pompeActive = false;
// Variables pour le mode de cycle automatique
int champCycleAuto = 0;
int champPrecedentCycleAuto = 0;
int tempsON_min_temp;
int tempsON_sec_temp;
int tempsOFF_min_temp;
int tempsOFF_sec_temp;
int tempsON_min_sauvegarde;
int tempsON_sec_sauvegarde;
int tempsOFF_min_sauvegarde;
int tempsOFF_sec_sauvegarde;
// Variables pour le cycle automatique en cours
bool cycleAutoEnCours = false;
unsigned long timerDepart;
int etatCycleAuto; // 0 = OFF, 1 = ON
// Variables pour le sous-menu "Type de granules"
int selectionGrade = 0; // 0=Premium, 1=Moyen, 2=Economique
int selectionGradeSauvegarde;
// Variables pour le sous-menu "Unite Consommation"
int selectionUniteConso = 0; // 0=Minute, 1=Heure, etc.
int selectionUniteConsoSauvegarde;
// Variables pour la gestion du joystick et le debounce
int etatBoutonJoystick = 0;
int dernierEtatBouton = HIGH;
long dernierDebounceTime = 0;
long delaiDebounce = 50;
unsigned long tempsDerniereNavigationJoystick = 0;
// Nouvelles variables pour l'étalonnage
float granulesParMinute = 45.0; // Valeur par défaut en grammes par minute
float granulesParMinuteSauvegarde;
int champEtalonnage = 0;
int granulesTemporaire; // pour la partie entière
int decimalesTemporaire; // pour la partie décimale
int champPrecedentEtalonnage = 0;
// Constantes pour l'énergie par grade (valeurs par défaut)
#define ENERGIE_KWH_PREMIUM 4.8 // kWh par kg
#define ENERGIE_KWH_MOYEN 4.5 // kWh par kg
#define ENERGIE_KWH_ECONOMIQUE 4.2 // kWh par kg
#define KWH_TO_BTU 3412.14 // Facteur de conversion
// Ajout des variables manquantes pour l'écran d'informations
bool premierAffichageInfos = true;
unsigned long derniereMiseAJourInfos = 0;
const unsigned long intervalleMiseAJour = 1000;
// Création des objets pour les écrans et le RTC
Adafruit_ILI9341 Lcd_Menu = Adafruit_ILI9341(TFT_MENU_CS, TFT_MENU_DC, TFT_MENU_RST);
Adafruit_ILI9341 Lcd_Infos = Adafruit_ILI9341(TFT_INFOS_CS, TFT_INFOS_DC, TFT_INFOS_RST);
RTC_DS1307 rtc;
// --- Fonctions utilitaires pour la date ---
bool isLeapYear(int year) {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
int daysInMonth(int month, int year) {
if (month == 2) {
return isLeapYear(year) ? 29 : 28;
} else if (month == 4 || month == 6 || month == 9 || month == 11) {
return 30;
} else {
return 31;
}
}
// Fonction utilitaire pour sauvegarder et lire un float en EEPROM
void EEPROM_writeFloat(int address, float value) {
byte* p = (byte*)(void*)&value;
for (int i = 0; i < sizeof(value); i++) {
EEPROM.write(address + i, *p++);
}
}
float EEPROM_readFloat(int address) {
float value;
byte* p = (byte*)(void*)&value;
for (int i = 0; i < sizeof(value); i++) {
*p++ = EEPROM.read(address + i);
}
return value;
}
// --- Fonctions modulaires ---
struct MenuOption;
void dessinerOption(int index, bool estSelectionnee, MenuOption* menu, int taille) {
int yPos = 55 + (index * 25);
Lcd_Menu.setTextSize(2);
if (estSelectionnee) {
Lcd_Menu.setTextColor(ILI9341_BLACK);
Lcd_Menu.fillRect(10, yPos - 3, Lcd_Menu.width() - 20, 22, ILI9341_YELLOW);
} else {
Lcd_Menu.setTextColor(ILI9341_WHITE);
Lcd_Menu.fillRect(10, yPos - 3, Lcd_Menu.width() - 20, 22, ILI9341_BLACK);
}
Lcd_Menu.setCursor(20, yPos);
if (menuActuel == MENU_PRINCIPAL && index == 0) {
if (cycleAutoEnCours) {
Lcd_Menu.print("Arreter Cycle Auto");
} else {
Lcd_Menu.print("Demarrer Cycle Auto");
}
} else {
Lcd_Menu.print(menu[index].nom);
}
}
void dessinerMenuComplet() {
// Efface et prépare l'écran de menu
Lcd_Menu.fillScreen(ILI9341_BLACK);
Lcd_Menu.setCursor(10, 10);
MenuOption* menu = nullptr;
int taille = 0;
if (menuActuel == MENU_PRINCIPAL) {
Lcd_Menu.setTextSize(3);
Lcd_Menu.setTextColor(ILI9341_CYAN);
Lcd_Menu.println("Menu");
Lcd_Menu.drawFastHLine(0, 40, Lcd_Menu.width(), ILI9341_CYAN);
menu = menuPrincipal;
taille = tailleMenuPrincipal;
} else if (menuActuel == SOUS_MENU_PARAMETRES) {
Lcd_Menu.setTextSize(3);
Lcd_Menu.setTextColor(ILI9341_CYAN);
Lcd_Menu.println("Parametres");
Lcd_Menu.drawFastHLine(0, 40, Lcd_Menu.width(), ILI9341_CYAN);
menu = sousMenuParametres;
taille = tailleSousMenuParametres;
} else if (menuActuel == SOUS_MENU_DONNEES) {
Lcd_Menu.setTextSize(3);
Lcd_Menu.setTextColor(ILI9341_CYAN);
Lcd_Menu.println("Donnees");
Lcd_Menu.drawFastHLine(0, 40, Lcd_Menu.width(), ILI9341_CYAN);
menu = sousMenuDonnees;
taille = tailleSousMenuDonnees;
} else if (menuActuel == SOUS_MENU_TYPE_GRANULES) {
Lcd_Menu.setTextSize(3);
Lcd_Menu.setTextColor(ILI9341_CYAN);
Lcd_Menu.println("Type de granules");
Lcd_Menu.drawFastHLine(0, 40, Lcd_Menu.width(), ILI9341_CYAN);
menu = sousMenuTypeGranules;
taille = tailleSousMenuTypeGranules;
} else if (menuActuel == SOUS_MENU_UNITE_CONSO) {
Lcd_Menu.setTextSize(2);
Lcd_Menu.setTextColor(ILI9341_CYAN);
Lcd_Menu.println("Unite Consommation");
Lcd_Menu.drawFastHLine(0, 40, Lcd_Menu.width(), ILI9341_CYAN);
menu = sousMenuUniteConso;
taille = tailleSousMenuUniteConso;
}
if (menu != nullptr) {
for (int i = 0; i < taille; i++) {
dessinerOption(i, i == selectionActuelle, menu, taille);
}
}
// Redessine la barre tactile à la fin pour qu'elle reste visible
drawTouchButtons();
}
void sauvegarderHeureRTC() {
DateTime nouvelleHeure(anneeTemporaire, moisTemporaire, jourTemporaire, heureTemporaire, minuteTemporaire, 0);
rtc.adjust(nouvelleHeure);
menuActuel = SOUS_MENU_PARAMETRES;
selectionActuelle = 0;
dessinerMenuComplet();
}
void dessinerUnChampDeHeure(int champ, bool estSelectionne) {
Lcd_Menu.setTextSize(2);
if (estSelectionne) {
Lcd_Menu.setTextColor(ILI9341_BLACK, ILI9341_YELLOW);
} else {
Lcd_Menu.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
}
if (champ == 0) {
Lcd_Menu.setCursor(100, 60);
Lcd_Menu.fillRect(100, 60, 40, 16, estSelectionne ? ILI9341_YELLOW : ILI9341_BLACK);
Lcd_Menu.setCursor(100, 60);
if (heureTemporaire < 10) Lcd_Menu.print('0');
Lcd_Menu.print(heureTemporaire, DEC);
} else if (champ == 1) {
Lcd_Menu.setCursor(150, 60);
Lcd_Menu.fillRect(150, 60, 40, 16, estSelectionne ? ILI9341_YELLOW : ILI9341_BLACK);
Lcd_Menu.setCursor(150, 60);
if (minuteTemporaire < 10) Lcd_Menu.print('0');
Lcd_Menu.print(minuteTemporaire, DEC);
} else if (champ == 2) {
Lcd_Menu.setCursor(100, 90);
Lcd_Menu.fillRect(100, 90, 40, 16, estSelectionne ? ILI9341_YELLOW : ILI9341_BLACK);
Lcd_Menu.setCursor(100, 90);
if (jourTemporaire < 10) Lcd_Menu.print('0');
Lcd_Menu.print(jourTemporaire, DEC);
} else if (champ == 3) {
Lcd_Menu.setCursor(150, 90);
Lcd_Menu.fillRect(150, 90, 40, 16, estSelectionne ? ILI9341_YELLOW : ILI9341_BLACK);
Lcd_Menu.setCursor(150, 90);
if (moisTemporaire < 10) Lcd_Menu.print('0');
Lcd_Menu.print(moisTemporaire, DEC);
} else if (champ == 4) {
Lcd_Menu.setCursor(200, 90);
Lcd_Menu.fillRect(200, 90, 60, 16, estSelectionne ? ILI9341_YELLOW : ILI9341_BLACK);
Lcd_Menu.setCursor(200, 90);
Lcd_Menu.print(anneeTemporaire, DEC);
} else if (champ == 5) {
Lcd_Menu.setCursor(10, 150);
Lcd_Menu.fillRect(10, 147, Lcd_Menu.width() - 20, 22, estSelectionne ? ILI9341_YELLOW : ILI9341_BLACK);
Lcd_Menu.setCursor(20, 150);
Lcd_Menu.print("Sauvegarder");
} else if (champ == 6) {
Lcd_Menu.setCursor(10, 180);
Lcd_Menu.fillRect(10, 177, Lcd_Menu.width() - 20, 22, estSelectionne ? ILI9341_YELLOW : ILI9341_BLACK);
Lcd_Menu.setCursor(20, 180);
Lcd_Menu.print("Annuler");
}
}
void dessinerReglageHeureComplet() {
Lcd_Menu.fillScreen(ILI9341_BLACK);
Lcd_Menu.setCursor(10, 10);
Lcd_Menu.setTextSize(3);
Lcd_Menu.setTextColor(ILI9341_CYAN);
Lcd_Menu.println("Regler l'heure");
Lcd_Menu.drawFastHLine(0, 40, Lcd_Menu.width(), ILI9341_CYAN);
Lcd_Menu.setTextSize(2);
Lcd_Menu.setTextColor(ILI9341_WHITE);
Lcd_Menu.setCursor(10, 60);
Lcd_Menu.print("Heure: ");
Lcd_Menu.setCursor(140, 60);
Lcd_Menu.print(":");
Lcd_Menu.setCursor(10, 90);
Lcd_Menu.print("Date: ");
Lcd_Menu.setCursor(140, 90);
Lcd_Menu.print("/");
Lcd_Menu.setCursor(190, 90);
Lcd_Menu.print("/");
for (int i = 0; i <= 6; i++) {
dessinerUnChampDeHeure(i, i == champReglageHeure);
}
}
void afficherModeManuel() {
Lcd_Menu.fillScreen(ILI9341_BLACK);
Lcd_Menu.setCursor(10, 10);
Lcd_Menu.setTextSize(3);
Lcd_Menu.setTextColor(ILI9341_CYAN);
Lcd_Menu.println("Mode Manuel");
Lcd_Menu.drawFastHLine(0, 40, Lcd_Menu.width(), ILI9341_CYAN);
Lcd_Menu.setTextSize(2);
Lcd_Menu.setCursor(20, 60);
Lcd_Menu.setTextColor(ILI9341_WHITE);
Lcd_Menu.print("Pompe a granules: ");
Lcd_Menu.setCursor(20, 120);
Lcd_Menu.setTextColor(ILI9341_YELLOW);
Lcd_Menu.println("Pressez le bouton pour basculer.");
}
void mettreAJourEtatPompe() {
Lcd_Menu.setTextSize(2);
Lcd_Menu.setCursor(20, 90);
Lcd_Menu.fillRect(20, 90, 200, 20, ILI9341_BLACK);
if (pompeActive) {
Lcd_Menu.setTextColor(ILI9341_GREEN);
Lcd_Menu.print("ACTIVE");
digitalWrite(PIN_RELAIS_POMPE, HIGH);
} else {
Lcd_Menu.setTextColor(ILI9341_RED);
Lcd_Menu.print("INACTIVE");
digitalWrite(PIN_RELAIS_POMPE, LOW);
}
}
void dessinerUnChampDeCycle(int champ, int val) {
Lcd_Menu.setTextSize(2);
Lcd_Menu.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
int xPos = (champ == 0 || champ == 2) ? 150 : 200;
int yPos = (champ == 0 || champ == 1) ? 60 : 90;
Lcd_Menu.fillRect(xPos, yPos, 40, 16, ILI9341_BLACK);
Lcd_Menu.setCursor(xPos, yPos);
if (val < 10) Lcd_Menu.print('0');
Lcd_Menu.print(val, DEC);
}
void dessinerSelection(int champ, bool estSelectionne) {
int xPos = 0;
int yPos = 0;
int width = 0;
int height = 0;
if (champ <= 3) {
xPos = (champ == 0 || champ == 2) ? 148 : 198;
yPos = (champ == 0 || champ == 1) ? 58 : 88;
width = 44;
height = 20;
} else if (champ == 4) { // Sauvegarder
xPos = 8;
yPos = 145;
width = Lcd_Menu.width() - 16;
height = 26;
} else if (champ == 5) { // Annuler
xPos = 8;
yPos = 175;
width = Lcd_Menu.width() - 16;
height = 26;
}
if (estSelectionne) {
Lcd_Menu.drawRect(xPos, yPos, width, height, ILI9341_YELLOW);
} else {
Lcd_Menu.drawRect(xPos, yPos, width, height, ILI9341_BLACK);
}
}
void dessinerProgrammationComplet() {
Lcd_Menu.fillScreen(ILI9341_BLACK);
Lcd_Menu.setCursor(10, 10);
Lcd_Menu.setTextSize(3);
Lcd_Menu.setTextColor(ILI9341_CYAN);
Lcd_Menu.println("Cycle Auto");
Lcd_Menu.drawFastHLine(0, 40, Lcd_Menu.width(), ILI9341_CYAN);
Lcd_Menu.setTextSize(2);
Lcd_Menu.setTextColor(ILI9341_WHITE);
Lcd_Menu.setCursor(20, 60);
Lcd_Menu.print("Temps ON : ");
Lcd_Menu.setCursor(190, 60);
Lcd_Menu.print(":"); // Séparateur fixe
Lcd_Menu.setCursor(20, 90);
Lcd_Menu.print("Temps OFF:");
Lcd_Menu.setCursor(190, 90);
Lcd_Menu.print(":"); // Séparateur fixe
dessinerUnChampDeCycle(0, tempsON_min_temp);
dessinerUnChampDeCycle(1, tempsON_sec_temp);
dessinerUnChampDeCycle(2, tempsOFF_min_temp);
dessinerUnChampDeCycle(3, tempsOFF_sec_temp);
Lcd_Menu.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
Lcd_Menu.setCursor(20, 150);
Lcd_Menu.print("Sauvegarder");
Lcd_Menu.setCursor(20, 180);
Lcd_Menu.print("Annuler");
dessinerSelection(champCycleAuto, true);
}
void dessinerSousMenuTypeGranules() {
Lcd_Menu.fillScreen(ILI9341_BLACK);
Lcd_Menu.setCursor(10, 10);
Lcd_Menu.setTextSize(3);
Lcd_Menu.setTextColor(ILI9341_CYAN);
Lcd_Menu.println("Type de granules");
Lcd_Menu.drawFastHLine(0, 40, Lcd_Menu.width(), ILI9341_CYAN);
MenuOption* menu = sousMenuTypeGranules;
int taille = tailleSousMenuTypeGranules;
for (int i = 0; i < taille; i++) {
dessinerOption(i, i == selectionActuelle, menu, taille);
}
}
void dessinerSousMenuUniteConso() {
Lcd_Menu.fillScreen(ILI9341_BLACK);
Lcd_Menu.setCursor(10, 10);
Lcd_Menu.setTextSize(2);
Lcd_Menu.setTextColor(ILI9341_CYAN);
Lcd_Menu.println("Unite Consommation");
Lcd_Menu.drawFastHLine(0, 40, Lcd_Menu.width(), ILI9341_CYAN);
MenuOption* menu = sousMenuUniteConso;
int taille = tailleSousMenuUniteConso;
for (int i = 0; i < taille; i++) {
dessinerOption(i, i == selectionActuelle, menu, taille);
}
}
void dessinerUnChampEtalonnage(int champ, bool estSelectionne) {
Lcd_Menu.setTextSize(2);
if (estSelectionne) {
Lcd_Menu.setTextColor(ILI9341_BLACK, ILI9341_YELLOW);
} else {
Lcd_Menu.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
}
if (champ == 0) {
Lcd_Menu.setCursor(110, 60);
Lcd_Menu.fillRect(110, 60, 40, 16, estSelectionne ? ILI9341_YELLOW : ILI9341_BLACK);
Lcd_Menu.setCursor(110, 60);
Lcd_Menu.print(granulesTemporaire, DEC);
} else if (champ == 1) {
Lcd_Menu.setCursor(160, 60);
Lcd_Menu.fillRect(160, 60, 40, 16, estSelectionne ? ILI9341_YELLOW : ILI9341_BLACK);
Lcd_Menu.setCursor(160, 60);
Lcd_Menu.print(decimalesTemporaire, DEC);
} else if (champ == 2) {
Lcd_Menu.setCursor(10, 150);
Lcd_Menu.fillRect(10, 147, Lcd_Menu.width() - 20, 22, estSelectionne ? ILI9341_YELLOW : ILI9341_BLACK);
Lcd_Menu.setCursor(20, 150);
Lcd_Menu.print("Sauvegarder");
} else if (champ == 3) {
Lcd_Menu.setCursor(10, 180);
Lcd_Menu.fillRect(10, 177, Lcd_Menu.width() - 20, 22, estSelectionne ? ILI9341_YELLOW : ILI9341_BLACK);
Lcd_Menu.setCursor(20, 180);
Lcd_Menu.print("Annuler");
}
}
void dessinerReglageEtalonnageComplet() {
Lcd_Menu.fillScreen(ILI9341_BLACK);
Lcd_Menu.setCursor(10, 10);
Lcd_Menu.setTextSize(3);
Lcd_Menu.setTextColor(ILI9341_CYAN);
Lcd_Menu.println("Etalonnage");
Lcd_Menu.drawFastHLine(0, 40, Lcd_Menu.width(), ILI9341_CYAN);
Lcd_Menu.setTextSize(2);
Lcd_Menu.setTextColor(ILI9341_WHITE);
Lcd_Menu.setCursor(10, 60);
Lcd_Menu.print("g/min: ");
Lcd_Menu.setCursor(150, 60);
Lcd_Menu.print(".");
Lcd_Menu.setCursor(200, 60);
Lcd_Menu.print("g/min");
dessinerUnChampEtalonnage(0, champEtalonnage == 0);
dessinerUnChampEtalonnage(1, champEtalonnage == 1);
dessinerUnChampEtalonnage(2, champEtalonnage == 2);
dessinerUnChampEtalonnage(3, champEtalonnage == 3);
}
float calculerGranulesParUnite(int unite) {
float facteurConversion = 1.0;
if (unite == 1) facteurConversion = 60.0;
else if (unite == 2) facteurConversion = 60.0 * 24.0;
else if (unite == 3) facteurConversion = 60.0 * 24.0 * 7.0;
else if (unite == 4) facteurConversion = 60.0 * 24.0 * 30.4375; // Moyenne mensuelle
else if (unite == 5) facteurConversion = 60.0 * 24.0 * 365.25; // Moyenne annuelle
return granulesParMinuteSauvegarde * facteurConversion;
}
void calculerPuissanceEnergie(float* btu, float* kwh) {
float tempsON_sec = (tempsON_min_sauvegarde * 60) + tempsON_sec_sauvegarde;
float tempsOFF_sec = (tempsOFF_min_sauvegarde * 60) + tempsOFF_sec_sauvegarde;
float dureeCycle = tempsON_sec + tempsOFF_sec;
float ratioON = (dureeCycle > 0) ? tempsON_sec / dureeCycle : 0.0;
float granulesParHeure = granulesParMinuteSauvegarde * 60.0 * ratioON;
float energie_kwh_par_kg = 0;
if (selectionGradeSauvegarde == 0) {
energie_kwh_par_kg = ENERGIE_KWH_PREMIUM;
} else if (selectionGradeSauvegarde == 1) {
energie_kwh_par_kg = ENERGIE_KWH_MOYEN;
} else if (selectionGradeSauvegarde == 2) {
energie_kwh_par_kg = ENERGIE_KWH_ECONOMIQUE;
}
*kwh = (granulesParHeure / 1000.0) * energie_kwh_par_kg;
*btu = (*kwh) * KWH_TO_BTU;
}
void afficherCompteAReboursInfos() {
static unsigned long previousRemainingTime = 0;
static int previousStatus = -1;
static bool previousCycleState = false;
unsigned long tempsEcoule = millis() - timerDepart;
unsigned long tempsRestant;
Lcd_Infos.setTextSize(2);
Lcd_Infos.setTextColor(ILI9341_WHITE, ILI9341_BLUE);
if (cycleAutoEnCours != previousCycleState) {
Lcd_Infos.fillRect(100, 135, 120, 16, ILI9341_BLUE);
Lcd_Infos.setCursor(100, 135);
if (cycleAutoEnCours) {
Lcd_Infos.setTextColor(ILI9341_GREEN);
Lcd_Infos.print("EN COURS");
} else {
Lcd_Infos.setTextColor(ILI9341_RED);
Lcd_Infos.print("ARRETE");
}
previousCycleState = cycleAutoEnCours;
previousStatus = -1;
return;
}
if (cycleAutoEnCours) {
if (etatCycleAuto != previousStatus) {
Lcd_Infos.fillRect(200, 85, 40, 16, ILI9341_BLUE);
Lcd_Infos.fillRect(200, 105, 40, 16, ILI9341_BLUE);
Lcd_Infos.setTextColor(ILI9341_YELLOW, ILI9341_BLUE);
if (etatCycleAuto == 1) {
unsigned long dureeTotaleON = (tempsON_min_sauvegarde * 60 + tempsON_sec_sauvegarde) * 1000UL;
tempsRestant = (dureeTotaleON > tempsEcoule) ? dureeTotaleON - tempsEcoule : 0;
int secondesRestantes = tempsRestant / 1000;
int minutesRestantes = secondesRestantes / 60;
secondesRestantes %= 60;
Lcd_Infos.setCursor(200, 85);
if (minutesRestantes < 10) Lcd_Infos.print('0');
Lcd_Infos.print(minutesRestantes);
Lcd_Infos.print(":");
if (secondesRestantes < 10) Lcd_Infos.print('0');
Lcd_Infos.print(secondesRestantes);
} else {
unsigned long dureeTotaleOFF = (tempsOFF_min_sauvegarde * 60 + tempsOFF_sec_sauvegarde) * 1000UL;
tempsRestant = (dureeTotaleOFF > tempsEcoule) ? dureeTotaleOFF - tempsEcoule : 0;
int secondesRestantes = tempsRestant / 1000;
int minutesRestantes = secondesRestantes / 60;
secondesRestantes %= 60;
Lcd_Infos.setCursor(200, 105);
if (minutesRestantes < 10) Lcd_Infos.print('0');
Lcd_Infos.print(minutesRestantes);
Lcd_Infos.print(":");
if (secondesRestantes < 10) Lcd_Infos.print('0');
Lcd_Infos.print(secondesRestantes);
}
previousStatus = etatCycleAuto;
previousRemainingTime = tempsRestant / 1000;
return;
}
unsigned long currentRemainingTime = (etatCycleAuto == 1) ? ((tempsON_min_sauvegarde * 60 + tempsON_sec_sauvegarde) * 1000UL > tempsEcoule ? ((tempsON_min_sauvegarde * 60 + tempsON_sec_sauvegarde) * 1000UL - tempsEcoule) / 1000 : 0) : ((tempsOFF_min_sauvegarde * 60 + tempsOFF_sec_sauvegarde) * 1000UL > tempsEcoule ? ((tempsOFF_min_sauvegarde * 60 + tempsOFF_sec_sauvegarde) * 1000UL - tempsEcoule) / 1000 : 0);
if (currentRemainingTime != previousRemainingTime) {
int secondesRestantes = currentRemainingTime;
int minutesRestantes = secondesRestantes / 60;
secondesRestantes %= 60;
Lcd_Infos.setTextColor(ILI9341_YELLOW, ILI9341_BLUE);
if (etatCycleAuto == 1) {
Lcd_Infos.setCursor(200, 85);
Lcd_Infos.fillRect(200, 85, 40, 16, ILI9341_BLUE);
if (minutesRestantes < 10) Lcd_Infos.print('0');
Lcd_Infos.print(minutesRestantes);
Lcd_Infos.print(":");
if (secondesRestantes < 10) Lcd_Infos.print('0');
Lcd_Infos.print(secondesRestantes);
} else {
Lcd_Infos.setCursor(200, 105);
Lcd_Infos.fillRect(200, 105, 40, 16, ILI9341_BLUE);
if (minutesRestantes < 10) Lcd_Infos.print('0');
Lcd_Infos.print(minutesRestantes);
Lcd_Infos.print(":");
if (secondesRestantes < 10) Lcd_Infos.print('0');
Lcd_Infos.print(secondesRestantes);
}
previousRemainingTime = currentRemainingTime;
}
}
}
void afficherInfos() {
static char previousTime[9] = "";
static char previousDate[11] = "";
static int previousONMin = -1;
static int previousONSec = -1;
static int previousOFFMin = -1;
static int previousOFFSec = -1;
static float previousConsoEstimee = -1.0;
static float previousPuissanceEstimee = -1.0;
static float previousBtusEstimee = -1.0;
DateTime now = rtc.now();
char bufferTemps[9];
char bufferDate[11];
sprintf(bufferTemps, "%02d:%02d:%02d", now.hour(), now.minute(), now.second());
sprintf(bufferDate, "%02d/%02d/%04d", now.day(), now.month(), now.year());
if (premierAffichageInfos) {
Lcd_Infos.fillScreen(ILI9341_BLUE);
Lcd_Infos.setTextColor(ILI9341_WHITE);
Lcd_Infos.setTextSize(2);
Lcd_Infos.setCursor(10, 10);
Lcd_Infos.print("Heure : ");
Lcd_Infos.setCursor(10, 30);
Lcd_Infos.print("Date : ");
Lcd_Infos.setCursor(10, 65);
Lcd_Infos.print("Cycle Auto");
Lcd_Infos.setCursor(10, 85);
Lcd_Infos.print("Temps ON : ");
Lcd_Infos.setCursor(10, 105);
Lcd_Infos.print("Temps OFF:");
Lcd_Infos.setCursor(10, 135);
Lcd_Infos.print("Etat : ");
Lcd_Infos.setCursor(10, 160);
Lcd_Infos.print("Type: ");
Lcd_Infos.setCursor(10, 180);
Lcd_Infos.print("Granules : ");
Lcd_Infos.setCursor(10, 200);
Lcd_Infos.print("Puissance: ");
Lcd_Infos.setCursor(10, 220);
Lcd_Infos.print("BTU/hr : ");
premierAffichageInfos = false;
}
// Affiche l'heure et la date seulement si elles ont changé
if (strcmp(bufferTemps, previousTime) != 0) {
Lcd_Infos.setCursor(100, 10);
Lcd_Infos.fillRect(100, 10, 100, 16, ILI9341_BLUE);
Lcd_Infos.print(bufferTemps);
strcpy(previousTime, bufferTemps);
}
if (strcmp(bufferDate, previousDate) != 0) {
Lcd_Infos.setCursor(100, 30);
Lcd_Infos.fillRect(100, 30, 100, 16, ILI9341_BLUE);
Lcd_Infos.print(bufferDate);
strcpy(previousDate, bufferDate);
}
if (tempsON_min_sauvegarde != previousONMin || tempsON_sec_sauvegarde != previousONSec) {
Lcd_Infos.setCursor(150, 85);
Lcd_Infos.fillRect(150, 85, 40, 16, ILI9341_BLUE);
if (tempsON_min_sauvegarde < 10) Lcd_Infos.print('0');
Lcd_Infos.print(tempsON_min_sauvegarde);
Lcd_Infos.print(":");
if (tempsON_sec_sauvegarde < 10) Lcd_Infos.print('0');
Lcd_Infos.print(tempsON_sec_sauvegarde);
previousONMin = tempsON_min_sauvegarde;
previousONSec = tempsON_sec_sauvegarde;
}
if (tempsOFF_min_sauvegarde != previousOFFMin || tempsOFF_sec_sauvegarde != previousOFFSec) {
Lcd_Infos.setCursor(150, 105);
Lcd_Infos.fillRect(150, 105, 40, 16, ILI9341_BLUE);
if (tempsOFF_min_sauvegarde < 10) Lcd_Infos.print('0');
Lcd_Infos.print(tempsOFF_min_sauvegarde);
Lcd_Infos.print(":");
if (tempsOFF_sec_sauvegarde < 10) Lcd_Infos.print('0');
Lcd_Infos.print(tempsOFF_sec_sauvegarde);
previousOFFMin = tempsOFF_min_sauvegarde;
previousOFFSec = tempsOFF_sec_sauvegarde;
}
// Affiche l'état du cycle
afficherCompteAReboursInfos();
// Affiche le type de granules
Lcd_Infos.setCursor(100, 160);
Lcd_Infos.fillRect(100, 160, 100, 16, ILI9341_BLUE);
if (selectionGradeSauvegarde == 0) Lcd_Infos.print("Premium");
else if (selectionGradeSauvegarde == 1) Lcd_Infos.print("Moyen");
else Lcd_Infos.print("Economique");
// Affiche la consommation de granules
float consoEstimee = calculerGranulesParUnite(selectionUniteConsoSauvegarde);
if (consoEstimee != previousConsoEstimee) {
Lcd_Infos.setCursor(100, 180);
Lcd_Infos.fillRect(100, 180, 120, 16, ILI9341_BLUE);
Lcd_Infos.print(consoEstimee, 1);
Lcd_Infos.print(" g/");
if (selectionUniteConsoSauvegarde == 0) Lcd_Infos.print("min");
else if (selectionUniteConsoSauvegarde == 1) Lcd_Infos.print("hr");
else if (selectionUniteConsoSauvegarde == 2) Lcd_Infos.print("jour");
else if (selectionUniteConsoSauvegarde == 3) Lcd_Infos.print("sem");
else if (selectionUniteConsoSauvegarde == 4) Lcd_Infos.print("mois");
else if (selectionUniteConsoSauvegarde == 5) Lcd_Infos.print("an");
previousConsoEstimee = consoEstimee;
}
// Affiche la puissance et l'énergie estimées
float btu_val, kwh_val;
calculerPuissanceEnergie(&btu_val, &kwh_val);
if (kwh_val != previousPuissanceEstimee) {
Lcd_Infos.setCursor(100, 200);
Lcd_Infos.fillRect(100, 200, 120, 16, ILI9341_BLUE);
Lcd_Infos.print(kwh_val, 2);
Lcd_Infos.print(" kW");
previousPuissanceEstimee = kwh_val;
}
if (btu_val != previousBtusEstimee) {
Lcd_Infos.setCursor(100, 220);
Lcd_Infos.fillRect(100, 220, 120, 16, ILI9341_BLUE);
Lcd_Infos.print(btu_val, 0);
Lcd_Infos.print(" BTU/hr");
previousBtusEstimee = btu_val;
}
}
// ====================================================================================================
// Fonction setup()
// ====================================================================================================
void setup() {
// Initialisation des écrans
Lcd_Menu.begin();
Lcd_Infos.begin();
Lcd_Menu.setRotation(1);
Lcd_Infos.setRotation(1);
Lcd_Infos.fillScreen(ILI9341_BLUE);
Lcd_Menu.fillScreen(ILI9341_BLACK);
pinMode(PIN_RELAIS_POMPE, OUTPUT);
digitalWrite(PIN_RELAIS_POMPE, LOW);
// Initialisation du capteur tactile FT6206
gc_ctp_ok = gc_ctp.begin();
Lcd_Menu.setTextSize(2);
Lcd_Menu.setCursor(0, 0);
Lcd_Menu.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
if (!gc_ctp_ok) {
Lcd_Menu.println("FT6206 not found!");
}
// Initialisation du RTC
if (!rtc.begin()) {
Lcd_Menu.println("RTC not found!");
//rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // Pour régler l'heure automatiquement au upload
} else {
if (!rtc.isrunning()) {
Lcd_Menu.println("RTC lost power!");
//rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
}
// Lecture des valeurs EEPROM au démarrage
tempsON_min_sauvegarde = EEPROM.read(EEPROM_ADDR_TEMPS_ON_MIN);
tempsON_sec_sauvegarde = EEPROM.read(EEPROM_ADDR_TEMPS_ON_SEC);
tempsOFF_min_sauvegarde = EEPROM.read(EEPROM_ADDR_TEMPS_OFF_MIN);
tempsOFF_sec_sauvegarde = EEPROM.read(EEPROM_ADDR_TEMPS_OFF_SEC);
selectionGradeSauvegarde = EEPROM.read(EEPROM_ADDR_GRADE_GRANULES);
selectionUniteConsoSauvegarde = EEPROM.read(EEPROM_ADDR_UNITE_CONSO);
granulesParMinuteSauvegarde = EEPROM_readFloat(EEPROM_ADDR_GRANULES_PAR_MINUTE);
// Vérification si les valeurs EEPROM sont valides
if (EEPROM.read(EEPROM_ADDR_MAGIC_NUMBER) != 0xDE) {
// Si non, on initialise l'EEPROM
tempsON_min_sauvegarde = 15;
tempsON_sec_sauvegarde = 0;
tempsOFF_min_sauvegarde = 30;
tempsOFF_sec_sauvegarde = 0;
selectionGradeSauvegarde = 0;
selectionUniteConsoSauvegarde = 1;
granulesParMinuteSauvegarde = 45.0;
EEPROM.write(EEPROM_ADDR_MAGIC_NUMBER, 0xDE);
EEPROM.write(EEPROM_ADDR_TEMPS_ON_MIN, tempsON_min_sauvegarde);
EEPROM.write(EEPROM_ADDR_TEMPS_ON_SEC, tempsON_sec_sauvegarde);
EEPROM.write(EEPROM_ADDR_TEMPS_OFF_MIN, tempsOFF_min_sauvegarde);
EEPROM.write(EEPROM_ADDR_TEMPS_OFF_SEC, tempsOFF_sec_sauvegarde);
EEPROM.write(EEPROM_ADDR_GRADE_GRANULES, selectionGradeSauvegarde);
EEPROM.write(EEPROM_ADDR_UNITE_CONSO, selectionUniteConsoSauvegarde);
EEPROM_writeFloat(EEPROM_ADDR_GRANULES_PAR_MINUTE, granulesParMinuteSauvegarde);
}
// Initialisation des variables temporaires pour la modification
tempsON_min_temp = tempsON_min_sauvegarde;
tempsON_sec_temp = tempsON_sec_sauvegarde;
tempsOFF_min_temp = tempsOFF_min_sauvegarde;
tempsOFF_sec_temp = tempsOFF_sec_sauvegarde;
granulesTemporaire = (int)granulesParMinuteSauvegarde;
decimalesTemporaire = (int)((granulesParMinuteSauvegarde - granulesTemporaire) * 10);
dessinerMenuComplet();
}
// ====================================================================================================
// Fonction loop()
// ====================================================================================================
void loop() {
unsigned long tempsActuel = millis();
if (tempsActuel - derniereMiseAJourInfos >= intervalleMiseAJour) {
afficherInfos();
derniereMiseAJourInfos = tempsActuel;
}
if (cycleAutoEnCours) {
unsigned long dureeTotaleON = (tempsON_min_sauvegarde * 60 + tempsON_sec_sauvegarde) * 1000UL;
unsigned long dureeTotaleOFF = (tempsOFF_min_sauvegarde * 60 + tempsOFF_sec_sauvegarde) * 1000UL;
if (etatCycleAuto == 0) { // ON
if (tempsActuel - timerDepart >= dureeTotaleON) {
digitalWrite(PIN_RELAIS_POMPE, LOW);
timerDepart = tempsActuel;
etatCycleAuto = 1; // Passage à l'état OFF
}
} else { // OFF
if (tempsActuel - timerDepart >= dureeTotaleOFF) {
digitalWrite(PIN_RELAIS_POMPE, HIGH);
timerDepart = tempsActuel;
etatCycleAuto = 0; // Passage à l'état ON
}
}
}
// Joystick (ou boutons tactiles)
int valY = readJoyY();
int valX = readJoyX();
if (menuActuel == MENU_PRINCIPAL) {
if (valY < 200) {
selectionPrecedente = selectionActuelle;
selectionActuelle = (selectionActuelle - 1 + tailleMenuPrincipal) % tailleMenuPrincipal;
dessinerOption(selectionPrecedente, false, menuPrincipal, tailleMenuPrincipal);
dessinerOption(selectionActuelle, true, menuPrincipal, tailleMenuPrincipal);
} else if (valY > 800) {
selectionPrecedente = selectionActuelle;
selectionActuelle = (selectionActuelle + 1) % tailleMenuPrincipal;
dessinerOption(selectionPrecedente, false, menuPrincipal, tailleMenuPrincipal);
dessinerOption(selectionActuelle, true, menuPrincipal, tailleMenuPrincipal);
}
} else if (menuActuel == SOUS_MENU_PARAMETRES) {
if (valY < 200) {
selectionPrecedente = selectionActuelle;
selectionActuelle = (selectionActuelle - 1 + tailleSousMenuParametres) % tailleSousMenuParametres;
dessinerOption(selectionPrecedente, false, sousMenuParametres, tailleSousMenuParametres);
dessinerOption(selectionActuelle, true, sousMenuParametres, tailleSousMenuParametres);
} else if (valY > 800) {
selectionPrecedente = selectionActuelle;
selectionActuelle = (selectionActuelle + 1) % tailleSousMenuParametres;
dessinerOption(selectionPrecedente, false, sousMenuParametres, tailleSousMenuParametres);
dessinerOption(selectionActuelle, true, sousMenuParametres, tailleSousMenuParametres);
}
} else if (menuActuel == SOUS_MENU_DONNEES) {
if (valY < 200) {
selectionPrecedente = selectionActuelle;
selectionActuelle = (selectionActuelle - 1 + tailleSousMenuDonnees) % tailleSousMenuDonnees;
dessinerOption(selectionPrecedente, false, sousMenuDonnees, tailleSousMenuDonnees);
dessinerOption(selectionActuelle, true, sousMenuDonnees, tailleSousMenuDonnees);
} else if (valY > 800) {
selectionPrecedente = selectionActuelle;
selectionActuelle = (selectionActuelle + 1) % tailleSousMenuDonnees;
dessinerOption(selectionPrecedente, false, sousMenuDonnees, tailleSousMenuDonnees);
dessinerOption(selectionActuelle, true, sousMenuDonnees, tailleSousMenuDonnees);
}
} else if (menuActuel == SOUS_MENU_TYPE_GRANULES) {
if (valY < 200) {
selectionPrecedente = selectionActuelle;
selectionActuelle = (selectionActuelle - 1 + tailleSousMenuTypeGranules) % tailleSousMenuTypeGranules;
dessinerOption(selectionPrecedente, false, sousMenuTypeGranules, tailleSousMenuTypeGranules);
dessinerOption(selectionActuelle, true, sousMenuTypeGranules, tailleSousMenuTypeGranules);
} else if (valY > 800) {
selectionPrecedente = selectionActuelle;
selectionActuelle = (selectionActuelle + 1) % tailleSousMenuTypeGranules;
dessinerOption(selectionPrecedente, false, sousMenuTypeGranules, tailleSousMenuTypeGranules);
dessinerOption(selectionActuelle, true, sousMenuTypeGranules, tailleSousMenuTypeGranules);
}
} else if (menuActuel == SOUS_MENU_UNITE_CONSO) {
if (valY < 200) {
selectionPrecedente = selectionActuelle;
selectionActuelle = (selectionActuelle - 1 + tailleSousMenuUniteConso) % tailleSousMenuUniteConso;
dessinerOption(selectionPrecedente, false, sousMenuUniteConso, tailleSousMenuUniteConso);
dessinerOption(selectionActuelle, true, sousMenuUniteConso, tailleSousMenuUniteConso);
} else if (valY > 800) {
selectionPrecedente = selectionActuelle;
selectionActuelle = (selectionActuelle + 1) % tailleSousMenuUniteConso;
dessinerOption(selectionPrecedente, false, sousMenuUniteConso, tailleSousMenuUniteConso);
dessinerOption(selectionActuelle, true, sousMenuUniteConso, tailleSousMenuUniteConso);
}
} else if (menuActuel == REGLAGE_HEURE_EN_COURS) {
if (valY < 200) {
if (champReglageHeure < 5) {
if (champReglageHeure == 0) { // Heures
heureTemporaire = (heureTemporaire + 1) % 24;
} else if (champReglageHeure == 1) { // Minutes
minuteTemporaire = (minuteTemporaire + 1) % 60;
} else if (champReglageHeure == 2) { // Jours
int maxDays = daysInMonth(moisTemporaire, anneeTemporaire);
jourTemporaire = (jourTemporaire >= maxDays) ? 1 : jourTemporaire + 1;
} else if (champReglageHeure == 3) { // Mois
moisTemporaire = (moisTemporaire >= 12) ? 1 : moisTemporaire + 1;
} else if (champReglageHeure == 4) { // Années
anneeTemporaire++;
}
dessinerUnChampDeHeure(champReglageHeure, true);
} else {
champPrecedentReglageHeure = champReglageHeure;
champReglageHeure = (champReglageHeure - 1 < 5) ? 6 : champReglageHeure - 1;
dessinerUnChampDeHeure(champPrecedentReglageHeure, false);
dessinerUnChampDeHeure(champReglageHeure, true);
}
} else if (valY > 800) {
if (champReglageHeure < 5) {
if (champReglageHeure == 0) { // Heures
heureTemporaire = (heureTemporaire - 1 + 24) % 24;
} else if (champReglageHeure == 1) { // Minutes
minuteTemporaire = (minuteTemporaire - 1 + 60) % 60;
} else if (champReglageHeure == 2) { // Jours
int maxDays = daysInMonth(moisTemporaire, anneeTemporaire);
jourTemporaire = (jourTemporaire <= 1) ? maxDays : jourTemporaire - 1;
} else if (champReglageHeure == 3) { // Mois
moisTemporaire = (moisTemporaire <= 1) ? 12 : moisTemporaire - 1;
} else if (champReglageHeure == 4) { // Années
anneeTemporaire--;
}
dessinerUnChampDeHeure(champReglageHeure, true);
} else {
champPrecedentReglageHeure = champReglageHeure;
champReglageHeure = (champReglageHeure + 1 > 6) ? 5 : champReglageHeure + 1;
dessinerUnChampDeHeure(champPrecedentReglageHeure, false);
dessinerUnChampDeHeure(champReglageHeure, true);
}
} else if (valX < 200 && champReglageHeure < 5) {
champPrecedentReglageHeure = champReglageHeure;
champReglageHeure = (champReglageHeure - 1 < 0) ? 6 : champReglageHeure - 1;
dessinerUnChampDeHeure(champPrecedentReglageHeure, false);
dessinerUnChampDeHeure(champReglageHeure, true);
} else if (valX > 800 && champReglageHeure < 5) {
champPrecedentReglageHeure = champReglageHeure;
champReglageHeure = (champReglageHeure + 1 > 4) ? 5 : champReglageHeure + 1;
dessinerUnChampDeHeure(champPrecedentReglageHeure, false);
dessinerUnChampDeHeure(champReglageHeure, true);
}
} else if (menuActuel == MODE_CYCLE_AUTOMATIQUE) {
if (valY < 200) {
if (champCycleAuto == 0) tempsON_min_temp++;
else if (champCycleAuto == 1) tempsON_sec_temp = (tempsON_sec_temp + 1) % 60;
else if (champCycleAuto == 2) tempsOFF_min_temp++;
else if (champCycleAuto == 3) tempsOFF_sec_temp = (tempsOFF_sec_temp + 1) % 60;
dessinerUnChampDeCycle(champCycleAuto, (champCycleAuto == 0 || champCycleAuto == 2) ? tempsON_min_temp : (champCycleAuto == 1 ? tempsON_sec_temp : tempsOFF_sec_temp));
} else if (valY > 800) {
if (champCycleAuto == 0) tempsON_min_temp = (tempsON_min_temp > 0) ? tempsON_min_temp - 1 : 0;
else if (champCycleAuto == 1) tempsON_sec_temp = (tempsON_sec_temp > 0) ? tempsON_sec_temp - 1 : 59;
else if (champCycleAuto == 2) tempsOFF_min_temp = (tempsOFF_min_temp > 0) ? tempsOFF_min_temp - 1 : 0;
else if (champCycleAuto == 3) tempsOFF_sec_temp = (tempsOFF_sec_temp > 0) ? tempsOFF_sec_temp - 1 : 59;
dessinerUnChampDeCycle(champCycleAuto, (champCycleAuto == 0 || champCycleAuto == 2) ? tempsON_min_temp : (champCycleAuto == 1 ? tempsON_sec_temp : tempsOFF_sec_temp));
} else if (valX < 200) {
dessinerSelection(champCycleAuto, false);
champPrecedentCycleAuto = champCycleAuto;
champCycleAuto = (champCycleAuto - 1 < 0) ? 5 : champCycleAuto - 1;
dessinerSelection(champCycleAuto, true);
} else if (valX > 800) {
dessinerSelection(champCycleAuto, false);
champPrecedentCycleAuto = champCycleAuto;
champCycleAuto = (champCycleAuto + 1 > 5) ? 0 : champCycleAuto + 1;
dessinerSelection(champCycleAuto, true);
}
} else if (menuActuel == REGLAGE_ETALONNAGE) {
if (valY < 200) {
if (champEtalonnage == 0) { // Partie entière
granulesTemporaire++;
} else if (champEtalonnage == 1) { // Décimales
decimalesTemporaire = (decimalesTemporaire + 1) % 10;
}
dessinerUnChampEtalonnage(champEtalonnage, true);
} else if (valY > 800) {
if (champEtalonnage == 0) { // Partie entière
granulesTemporaire = (granulesTemporaire > 0) ? granulesTemporaire - 1 : 0;
} else if (champEtalonnage == 1) { // Décimales
decimalesTemporaire = (decimalesTemporaire > 0) ? decimalesTemporaire - 1 : 9;
}
dessinerUnChampEtalonnage(champEtalonnage, true);
} else if (valX < 200) {
champPrecedentEtalonnage = champEtalonnage;
champEtalonnage = (champEtalonnage - 1 < 0) ? 3 : champEtalonnage - 1;
dessinerUnChampEtalonnage(champPrecedentEtalonnage, false);
dessinerUnChampEtalonnage(champEtalonnage, true);
} else if (valX > 800) {
champPrecedentEtalonnage = champEtalonnage;
champEtalonnage = (champEtalonnage + 1 > 3) ? 0 : champEtalonnage + 1;
dessinerUnChampEtalonnage(champPrecedentEtalonnage, false);
dessinerUnChampEtalonnage(champEtalonnage, true);
}
}
int lectureBouton = readJoyBtn();
if (lectureBouton != dernierEtatBouton) {
dernierDebounceTime = millis();
}
if ((millis() - dernierDebounceTime) > delaiDebounce) {
if (lectureBouton != etatBoutonJoystick) {
etatBoutonJoystick = lectureBouton;
if (etatBoutonJoystick == LOW) {
if (menuActuel == MENU_PRINCIPAL) {
int id = menuPrincipal[selectionActuelle].id;
if (id == DEMARRER_ARRETER_CYCLE_AUTO) {
cycleAutoEnCours = !cycleAutoEnCours;
if (cycleAutoEnCours) {
timerDepart = millis();
etatCycleAuto = 0; // Démarre en état ON
digitalWrite(PIN_RELAIS_POMPE, HIGH);
} else {
digitalWrite(PIN_RELAIS_POMPE, LOW);
}
dessinerMenuComplet();
} else if (id == MODE_MANUEL_EN_COURS) {
menuActuel = id;
afficherModeManuel();
mettreAJourEtatPompe();
} else {
menuActuel = id;
selectionActuelle = 0;
dessinerMenuComplet();
}
} else if (menuActuel == SOUS_MENU_PARAMETRES) {
int id = sousMenuParametres[selectionActuelle].id;
if (id == REGLAGE_HEURE_EN_COURS) {
DateTime now = rtc.now();
heureTemporaire = now.hour();
minuteTemporaire = now.minute();
jourTemporaire = now.day();
moisTemporaire = now.month();
anneeTemporaire = now.year();
champReglageHeure = 0;
menuActuel = id;
dessinerReglageHeureComplet();
} else if (id == REGLAGE_ETALONNAGE) {
granulesTemporaire = (int)granulesParMinuteSauvegarde;
decimalesTemporaire = (int)((granulesParMinuteSauvegarde - granulesTemporaire) * 10);
champEtalonnage = 0;
menuActuel = id;
dessinerReglageEtalonnageComplet();
} else {
menuActuel = id;
selectionActuelle = 0;
if (menuActuel == SOUS_MENU_UNITE_CONSO) {
selectionActuelle = selectionUniteConsoSauvegarde;
} else if (menuActuel == SOUS_MENU_TYPE_GRANULES) {
selectionActuelle = selectionGradeSauvegarde;
}
dessinerMenuComplet();
}
} else if (menuActuel == SOUS_MENU_DONNEES) {
int id = sousMenuDonnees[selectionActuelle].id;
if (id == MENU_PRINCIPAL) {
menuActuel = id;
selectionActuelle = 0;
dessinerMenuComplet();
}
} else if (menuActuel == SOUS_MENU_TYPE_GRANULES) {
int id = sousMenuTypeGranules[selectionActuelle].id;
if (id == SOUS_MENU_PARAMETRES) {
menuActuel = id;
selectionActuelle = 0;
dessinerMenuComplet();
} else {
selectionGradeSauvegarde = id;
EEPROM.write(EEPROM_ADDR_GRADE_GRANULES, selectionGradeSauvegarde);
menuActuel = SOUS_MENU_PARAMETRES;
selectionActuelle = 1;
dessinerMenuComplet();
}
} else if (menuActuel == SOUS_MENU_UNITE_CONSO) {
int id = sousMenuUniteConso[selectionActuelle].id;
if (id == SOUS_MENU_PARAMETRES) {
menuActuel = id;
selectionActuelle = 2;
dessinerMenuComplet();
} else {
selectionUniteConsoSauvegarde = id;
EEPROM.write(EEPROM_ADDR_UNITE_CONSO, selectionUniteConsoSauvegarde);
menuActuel = SOUS_MENU_PARAMETRES;
selectionActuelle = 2;
dessinerMenuComplet();
}
} else if (menuActuel == MODE_MANUEL_EN_COURS) {
pompeActive = !pompeActive;
mettreAJourEtatPompe();
} else if (menuActuel == REGLAGE_HEURE_EN_COURS) {
if (champReglageHeure == 5) {
sauvegarderHeureRTC();
} else if (champReglageHeure == 6) {
menuActuel = SOUS_MENU_PARAMETRES;
selectionActuelle = 0;
dessinerMenuComplet();
}
} else if (menuActuel == MODE_CYCLE_AUTOMATIQUE) {
if (champCycleAuto == 4) { // Sauvegarder
tempsON_min_sauvegarde = tempsON_min_temp;
tempsON_sec_sauvegarde = tempsON_sec_temp;
tempsOFF_min_sauvegarde = tempsOFF_min_temp;
tempsOFF_sec_sauvegarde = tempsOFF_sec_temp;
EEPROM.write(EEPROM_ADDR_TEMPS_ON_MIN, tempsON_min_sauvegarde);
EEPROM.write(EEPROM_ADDR_TEMPS_ON_SEC, tempsON_sec_sauvegarde);
EEPROM.write(EEPROM_ADDR_TEMPS_OFF_MIN, tempsOFF_min_sauvegarde);
EEPROM.write(EEPROM_ADDR_TEMPS_OFF_SEC, tempsOFF_sec_sauvegarde);
menuActuel = MENU_PRINCIPAL;
selectionActuelle = 1;
dessinerMenuComplet();
} else if (champCycleAuto == 5) { // Annuler
tempsON_min_temp = tempsON_min_sauvegarde;
tempsON_sec_temp = tempsON_sec_sauvegarde;
tempsOFF_min_temp = tempsOFF_min_sauvegarde;
tempsOFF_sec_temp = tempsOFF_sec_sauvegarde;
menuActuel = MENU_PRINCIPAL;
selectionActuelle = 1;
dessinerMenuComplet();
}
} else if (menuActuel == REGLAGE_ETALONNAGE) {
if (champEtalonnage == 2) {
granulesParMinute = granulesTemporaire + (float)decimalesTemporaire / 10.0;
EEPROM_writeFloat(EEPROM_ADDR_GRANULES_PAR_MINUTE, granulesParMinute);
granulesParMinuteSauvegarde = granulesParMinute;
menuActuel = SOUS_MENU_PARAMETRES;
selectionActuelle = 3;
dessinerMenuComplet();
} else if (champEtalonnage == 3) {
menuActuel = SOUS_MENU_PARAMETRES;
selectionActuelle = 3;
dessinerMenuComplet();
}
}
}
}
dernierEtatBouton = lectureBouton;
}
}