#include "BG.h"
#include <FastLED.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include "SPI.h"
#include <TFT_eSPI.h>
TFT_eSPI tft = TFT_eSPI();
#define I2C_ADDR 0x27
#define LCD_COLS 20
#define LCD_ROWS 4
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLS, LCD_ROWS);
#define BTN_RESET 4
#define BTN_READY 17
#define LED_PIN 5
#define NUM_LEDS 6
// const uint8_t KEYPAD_ADDRESS = 0x38;
// //initialize an instance of class NewKeypad
// I2CKeyPad keyPad(KEYPAD_ADDRESS);
// char keymap[17] = "123A456B789C*0#D";
const uint8_t ROWS = 4;
const uint8_t COLS = 4;
char keys[ROWS][COLS] = {
{ '1', '2', '3', 'A' },
{ '4', '5', '6', 'B' },
{ '7', '8', '9', 'C' },
{ '*', '0', '#', 'D' }
};
uint8_t colPins[COLS] = { 33, 32, 13, 16 }; // Pins connected to C1, C2, C3, C4
uint8_t rowPins[ROWS] = { 14, 27, 26, 25 }; // Pins connected to R1, R2, R3, R4
Keypad keyPad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
unsigned long lastKeyPressTime = 0; // variable pour enregistrer le temps du dernier appui de touche
unsigned long debounceDelay = 250; // délai de rebond en millisecondes
unsigned long lastButtonPressTime = 0;
unsigned long debounceButton = 350;
int buzzerScores[4] = {0, 0, 0, 0}; // Scores initiaux des buzzers
char selectedBuzzer = '\0'; // Buzzer sélectionné pour modification
char buzzerNames[4][26];
int currentQuestion = 1; // Numéro de la question actuelle
int buzzerScoresTrier[4]; // Scores triés des buzzers
char buzzerNamesTrier[4][26]; // Noms triés des équipes
// Couleurs hexadécimales
uint32_t startColor = 0xd84b39;
uint32_t endColor = 0x5acc70;
// Valeurs par défaut pour validAnswerPoints et invalidAnswerPoints
int defaultValidAnswerPoints = 1;
int defaultInvalidAnswerPoints = 0;
// Valeurs actuelles de validAnswerPoints et invalidAnswerPoints
int validAnswerPoints = defaultValidAnswerPoints;
int invalidAnswerPoints = defaultInvalidAnswerPoints;
bool timeronoff = false;
bool timerEnabled = false; // Indicateur permettant de savoir si le timer est activé ou non
unsigned long defaultTimerDuration = 30; // Durée par défaut du timer secondes)
unsigned long timerDuration = defaultTimerDuration;
unsigned long timerHasStarted = 0; // Temps auquel le timer a démarré
unsigned long timerPausedAt = 0; // Temps auquel le timer a été mis en pause
int lastSecondDigit = -1;
int lastTenSecondDigit = -1;
int lastMinuteDigit = -1;
int lastTenMinuteDigit = -1;
int enteredDigits[4] = {0, 0, 0, 0}; // tableau pour stocker les chiffres entrés (jusqu'à 4 chiffres)
int digitIndex = 0; // Index pour suivre la position actuelle dans le tableau
int answeredCount = 0;
unsigned char lastAnsweredButton = 0;
// Initialiser le bandeau LED
CRGB leds[NUM_LEDS];
CRGB colors[] = {CRGB::White, CRGB::Red, CRGB::Yellow, CRGB::Green, CRGB::Blue};
int ledBrightness = 255;
int led_intensityPourcentage = 100;
// Définir le tableau contenant les indices des LED
unsigned char BTN_LEDS[4];
// LED status type
enum LedStatus : unsigned char { lsOff = 0, lsOn = 1, lsFlashing = 2 };
// Status we want to share with the buttons
LedStatus ledStatus[4] = {lsOff, lsOff, lsOff, lsOff};
bool buttonEnabled[4] = {false, false, false, false};
bool buttonConnected[4] = {false, false, false, false};
bool hasAnswered[4] = {false, false, false, false};
unsigned long lastContact[4] = {0, 0, 0, 0};
// Last loop time
unsigned long lastLoopTime = 0;
// System status
bool isReady = false;
// Is audio playing?
bool isPlaying = false;
bool dfPlayerReady = false;
void setup() {
Serial.begin(115200);
pinMode(BTN_RESET, INPUT);
pinMode(BTN_READY, INPUT);
FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
lcd.init(); // Initialise l'écran LCD
lcd.backlight(); // Allume la rétroéclairage de l'écran LCD
tft.begin();
tft.setSwapBytes (true);
tft.setRotation(1);
// keyPad.loadKeyMap(keymap);
// Initialisation des noms d'équipes à des valeurs par défaut
for (int i = 0; i < 4; i++) {
snprintf(buzzerNames[i], sizeof(buzzerNames[i]), "Team %c", 'A' + i);
}
displayScores();
}
void loop() {
// displayAnimation();
// char key = keyPad.getChar();
char key = keyPad.getKey();
if (key != NO_KEY && (millis() - lastKeyPressTime) > debounceDelay) {
lastKeyPressTime = millis();
Serial.println(key);
if (key == '#' && isReady == false) { // Touche # pour activer/désactiver le timer
static bool timerDurationShowState = false;
if (timeronoff == false) {
displayTimer();
// timerDurationShowState = !timerDurationShowState;
// ESPUI.updateControlValue(timerDurationShow, timerDurationShowState ? "1" : "0");
} else {
disableTimer();
// timerDurationShowState = !timerDurationShowState;
// ESPUI.updateControlValue(timerDurationShow, timerDurationShowState ? "1" : "0");
}
}
if (isdigit(key) && isReady == false && timeronoff == true) {
// Si la touche appuyée est un chiffre, stockez-le dans le tableau des chiffres entrés
enteredDigits[digitIndex] = key - '0';
digitIndex++;
int DigitsMinutes = (enteredDigits[0] * 10) + enteredDigits[1];
int DigitsSeconds = (enteredDigits[2] * 10) + enteredDigits[3];
if (DigitsMinutes > 59) {
DigitsMinutes = 59;
}
if (DigitsSeconds > 59) {
DigitsSeconds = 59;
}
timerDuration = DigitsMinutes * 60 + DigitsSeconds;
if (DigitsMinutes < 10) {
lcd.setCursor(16, 1);
lcd.print(DigitsMinutes);
lcd.print(":");
lcd.print(DigitsSeconds);
} else if (DigitsMinutes >= 10) {
lcd.setCursor(15, 1);
lcd.print(DigitsMinutes);
lcd.print(":");
lcd.print(DigitsSeconds);
}
if (isdigit(key)) {
restoreBackground(230, 30, 65, 16);
}
displayTopRightText(String(DigitsMinutes) + ":" + (DigitsSeconds < 10 ? "0" : "") + String(DigitsSeconds), -30, 30, 2, 1);
// ESPUI.getControl(timerDurationNumberMin)->value = DigitsMinutes;
// ESPUI.getControl(timerDurationNumberSec)->value = DigitsSeconds;
// ESPUI.updateControl(timerDurationNumberMin);
// ESPUI.updateControl(timerDurationNumberSec);
// Affichez le nouveau temps sur l'écran
restoreBackground(230, 2, 65, 20);
displayTimer();
if (digitIndex == 4 && timerDuration == DigitsMinutes * 60 + DigitsSeconds) {
digitIndex = 0;
for(int i = 0; i < 4; ++i) {
enteredDigits[i] = 0;
}
unsigned long previousMillisTimer = millis();
unsigned long intervalTimer = 1000;
while(millis() - previousMillisTimer < intervalTimer) {
// Attendre
}
lcd.setCursor(15, 1);
lcd.print(" ");
// tft.fillRect(230, 30, 65, 16, TFT_BLACK);
restoreBackground(230, 30, 65, 16);
}
}
if (key == '*' && isReady == true) { // Valider la réponse
for (unsigned char button = 0; button < 4; button++) {
if (hasAnswered[button]) {
// Mettre à jour le score du buzzer en fonction de la réponse valide
buzzerScores[button] += validAnswerPoints;
// Incrémenter la variable currentQuestion après une bonne réponse
currentQuestion++;
// sortBuzzers();
// for (int i = 0; i < 4; i++) {
// ESPUI.getControl(buzzerScoresNumber[i])->value = String(buzzerScores[i]);
// ESPUI.updateControl(buzzerScoresNumber[i]);
// ESPUI.updateLabel(buzzer_label[i], String(buzzerNames[i]) + " : " + String(buzzerScores[i]));
// if (buzzerScores[i] == buzzerScoresTrier[0]) {
// ESPUI.setElementStyle(buzzer_label[i], "background-color: #ffde35;");
// ESPUI.print(buzzer_label[i], String(buzzerClassement[0]) + String(buzzerNames[i]) + " : " + String(buzzerScores[i]));
// } else if (buzzerScores[i] == buzzerScoresTrier[1]) {
// ESPUI.setElementStyle(buzzer_label[i], "background-color: #b5b5b5,");
// ESPUI.print(buzzer_label[i], String(buzzerClassement[1]) + String(buzzerNames[i]) + " : " + String(buzzerScores[i]));
// } else if (buzzerScores[i] == buzzerScoresTrier[2]) {
// ESPUI.setElementStyle(buzzer_label[i], "background-color: #ff9b37;");
// ESPUI.print(buzzer_label[i], String(buzzerClassement[2]) + String(buzzerNames[i]) + " : " + String(buzzerScores[i]));
// } else if (buzzerScores[i] == buzzerScoresTrier[3]) {
// ESPUI.setElementStyle(buzzer_label[i], "background-color: #c62c2c;");
// ESPUI.print(buzzer_label[i], String(buzzerClassement[3]) + String(buzzerNames[i]) + " : " + String(buzzerScores[i]));
// }
// }
displayScores();
for (unsigned char button = 0; button < 4; button++) {
ledStatus[button] = lsOff;
buttonEnabled[button] = false;
hasAnswered[button] = false;
// if (isPlaying) {
// player.stop();
// isPlaying = false;
// }
}
if (timeronoff == true){
disableTimer();
}
isReady = false;
// digitalWrite(LED_STATUS, LOW);
leds[0] = CRGB::Black;
FastLED.show();
}
}
}
if (key == 'A' || key == 'B' || key == 'C' || key == 'D') {
selectedBuzzer = key;
modifyScore();
}
}
lastLoopTime = millis();
if (digitalRead(BTN_RESET) == LOW && (millis() - lastButtonPressTime) > debounceButton) { // Reset button pressed
lastButtonPressTime = millis();
Serial.println("Reset");
// Turn all buttons off
for (unsigned char button = 0; button < 4; button++) {
ledStatus[button] = lsOff;
buttonEnabled[button] = false;
hasAnswered[button] = false;
// if (isPlaying) {
// player.stop();
// isPlaying = false;
// }
}
if (timeronoff == true){
disableTimer();
}
isReady = false;
// digitalWrite(LED_STATUS, LOW);
leds[0] = CRGB::Black;
FastLED.show();
} else if (digitalRead(BTN_READY) == LOW && (millis() - lastButtonPressTime) > debounceButton) { // Ready button pressed
lastButtonPressTime = millis();
Serial.println("Ready");
if (digitIndex == 1 || digitIndex == 2 || digitIndex == 3) {
int DigitsMinutes = (enteredDigits[0] * 10) + enteredDigits[1];
int DigitsSeconds = (enteredDigits[2] * 10) + enteredDigits[3];
if (DigitsMinutes > 59) {
DigitsMinutes = 59;
}
if (DigitsSeconds > 59) {
DigitsSeconds = 59;
}
timerDuration = DigitsMinutes * 60 + DigitsSeconds;
lcd.setCursor(15, 1);
lcd.print(" ");
// tft.fillRect(230, 30, 65, 16, TFT_BLACK);
restoreBackground(230, 30, 65, 16);
digitIndex = 0;
for(int i = 0; i < 4; i++) {
enteredDigits[i] = 0;
}
if (timerDuration == 0) {
timerDuration = defaultTimerDuration;
}
}
if (isReady && timeronoff && timerEnabled && timerPausedAt != 0) {
resumeTimer();
}
if (isReady == true && answeredCount == 4) { // Invalider la réponse
// Mettre à jour le score du buzzer en fonction de la réponse invalide
buzzerScores[lastAnsweredButton] -= invalidAnswerPoints;
lastAnsweredButton = 0; // Réinitialiser lastAnsweredButton à 0
}
AnswerCount();
// Make the buttons flash that havent answered yet
for (unsigned char button = 0; button < 4; button++) {
buttonEnabled[button] = !hasAnswered[button];
ledStatus[button] = hasAnswered[button] ? lsOff : lsFlashing;
if (isReady && timeronoff && timerEnabled && timerPausedAt == 0 && hasAnswered[button]) {
pauseTimer();
}
lastAnsweredButton = button;
}
isReady = true;
// if (isPlaying) {
// player.stop();
// isPlaying = false;
// }
// digitalWrite(LED_STATUS, HIGH);
leds[0] = colors[0];
FastLED.show();
}
// Update our LEDs and monitor for ones that are out of contact
for (unsigned char button = 0; button < 4; button++) {
// If the button is connected
if (buttonConnected[button]) {
// If its been 1 second since we heard from it
if (lastLoopTime - lastContact[button] > 1000) {
// Disconnect it
buttonConnected[button] = false;
digitalWrite(BTN_LEDS[button], LOW);
leds[button + 1] = CRGB::Black;
FastLED.show();
} else {
// Set the LED to match the state we have it in
digitalWrite(BTN_LEDS[button],(ledStatus[button] == lsOn) || ((ledStatus[button] == lsFlashing) && (lastLoopTime & 255) > 128));
if (ledStatus[button] == lsOn) {
leds[button + 1] = colors[button + 1];
FastLED.show();
} else if ((ledStatus[button] == lsFlashing) && (lastLoopTime & 255) > 128) {
leds[button + 1] = (lastLoopTime & 255) > 128 ? colors[button + 1] : CRGB::Black;
FastLED.show();
} else {
leds[button + 1] = CRGB::Black;
FastLED.show();
}
}
} else {
// For disconnected ones we just give a short 'blip' once per few second
digitalWrite(BTN_LEDS[button], (lastLoopTime & 2047) > 2000);
leds[button + 1] = (lastLoopTime & 2047) > 2000 ? colors[button + 1] : CRGB::Black;
FastLED.show();
}
}
if (isReady == true && timeronoff == true) {
startTimer();
}
if (digitalRead(BTN_RESET) == LOW && digitalRead(BTN_READY) == LOW && (millis() - lastButtonPressTime) > debounceButton) {
Serial.println("Ready & Reset");
lastButtonPressTime = millis();
currentQuestion = 1;
for (int i = 0; i < 4; ++i) {
buzzerScores[i] = 0;
}
displayScores();
for (unsigned char button = 0; button < 4; button++) {
ledStatus[button] = lsOff;
buttonEnabled[button] = false;
hasAnswered[button] = false;
// if (isPlaying) {
// player.stop();
// isPlaying = false;
// }
}
if (timeronoff == true){
disableTimer();
}
isReady = false;
// digitalWrite(LED_STATUS, LOW);
leds[0] = CRGB::Black;
FastLED.show();
}
}
void displayScores() {
Serial.println("displayScores");
lcd.clear();
// Afficher l'image de fond
tft.pushImage(0, 0, tft.width(), tft.height(), (uint16_t*)BG);
for (int i = 0; i < 4; i++) {
if (strlen(buzzerNames[i]) == 0) {
snprintf(buzzerNames[i], sizeof(buzzerNames[i]), "Team %c", 'A' + i);
}
}
char cleanBuzzerNames[4][10]; // Tableau pour stocker les noms de buzzer nettoyés
// Nettoyer les noms de buzzer
for (int i = 0; i < 4; i++) {
strncpy(cleanBuzzerNames[i], buzzerNames[i], sizeof(cleanBuzzerNames[i]) - 1);
cleanBuzzerNames[i][sizeof(cleanBuzzerNames[i]) - 1] = '\0';
lcd.setCursor(0, i);
lcd.print(cleanBuzzerNames[i]);
lcd.print(": ");
lcd.print(buzzerScores[i]);
sortBuzzers();
if (buzzerScores[i] == buzzerScoresTrier[0]) {
tft.setTextColor(0xFF26);
displayTopLeftText("1er", 5, i * 60 + 25, 2, 1);
} else if (buzzerScores[i] == buzzerScoresTrier[1]) {
tft.setTextColor(0xBDF7);
displayTopLeftText("2eme", 5, i * 60 + 25, 2, 1);
} else if (buzzerScores[i] == buzzerScoresTrier[2]) {
tft.setTextColor(0xFD86);
displayTopLeftText("3eme", 5, i * 60 + 25, 2, 1);
} else if (buzzerScores[i] == buzzerScoresTrier[3]) {
tft.setTextColor(0xC518);
displayTopLeftText("4eme", 5, i * 60 + 25, 2, 1);
}
tft.setTextColor(TFT_WHITE);
displayTopLeftText(String(cleanBuzzerNames[i]) + ":" + String(buzzerScores[i]), 5, i * 60 + 5, 2, 1);
}
// Affichage de l'élément en bas à droite
String questionString;
if (currentQuestion < 10) { // Si currentQuestion est à un chiffre
questionString = " Q: " + String(currentQuestion);
} else { // Si currentQuestion est à deux chiffres
questionString = "Q: " + String(currentQuestion);
}
int questionStringLength = questionString.length();
int xPos = 20 - questionStringLength; // Position horizontale adaptée
lcd.setCursor(xPos, 3); // Ligne 4 (indexée à partir de 0)
lcd.print(questionString);
displayBottomRightText(String(questionString), -questionStringLength, 0, 2, 1);
// String MQTTquestion = String(questionString);
// client.publish((stored_mqtt_topic_out + "/Question_Number").c_str(), MQTTquestion.c_str());
// String MQTTnames = "";
// for (int i = 0; i < 4; i++) {
// MQTTnames += String(buzzerNames[i]);
// if (i < 3) {
// MQTTnames += ", ";
// }
// }
// client.publish((stored_mqtt_topic_out + "/Buzzer_Names").c_str(), MQTTnames.c_str());
// String MQTTscores = "";
// for (int i = 0; i < 4; i++) {
// MQTTscores += String(buzzerScores[i]);
// if (i < 3) {
// MQTTscores += ", ";
// }
// }
// client.publish((stored_mqtt_topic_out + "/Buzzer_Scores").c_str(), MQTTscores.c_str());
}
void AnswerCount() {
// Compter le nombre de boutons ayant déjà répondu et le nombre de boutons n'ayant pas encore répondu
for (unsigned char button = 0; button <= 4; button++) {
if (hasAnswered[button]) {
answeredCount++;
}
}
// Si tous les boutons ont répondu, puis si il y a BTN_READY = LOW incrémentez currentQuestion
if (answeredCount == 4) {
if (digitalRead(BTN_READY) == LOW) {
currentQuestion++;
answeredCount = 0;
}
}
}
void modifyScore() {
Serial.print("modifyScore : ");
Serial.println(buzzerNames[selectedBuzzer - 'A']);
lcd.clear();
lcd.print("New score ");
lcd.print(buzzerNames[selectedBuzzer - 'A']);
lcd.setCursor(0, 1);
lcd.print(buzzerScores[selectedBuzzer - 'A']);
lcd.print(" -> ");
// Afficher l'image de fond
tft.pushImage(0, 0, tft.width(), tft.height(), (uint16_t*)BG);
tft.setTextColor(TFT_WHITE);
displayTopLeftText("New score " + String(buzzerNames[selectedBuzzer - 'A']), 5, 10, 2, 1);
displayTopLeftText(String(buzzerScores[selectedBuzzer - 'A']) + "->", 5, 30, 2, 1);
int Score = buzzerScores[selectedBuzzer - 'A'];
int newScore = 0;
int xOffset = 0;
char key;
bool numericKeyPressed = false; // Flag pour vérifier si une touche numérique est pressée
while (true) {
// key = keyPad.getChar();
key = keyPad.getKey();
if (isdigit(key) && (millis() - lastKeyPressTime) > debounceDelay) {
lastKeyPressTime = millis();
newScore = newScore * 10 + (key - '0');
lcd.print(key);
tft.setTextColor(TFT_WHITE);
int textWidth = calculateTextWidth(String(buzzerScores[selectedBuzzer - 'A']) + "->", 2, 1);
xOffset += 12;
displayTopLeftText(String(key), textWidth + xOffset, 30, 2, 1);
numericKeyPressed = true; // Marquer qu'une touche numérique est pressée
} else if (key == selectedBuzzer && (millis() - lastKeyPressTime) > debounceDelay) {
lastKeyPressTime = millis();
// Si aucune touche numérique n'est pressée, mettre à jour newScore avec le score actuel
if (!numericKeyPressed) {
newScore = Score;
}
buzzerScores[selectedBuzzer - 'A'] = newScore;
lcd.clear();
lcd.print("New score saved");
tft.setTextColor(TFT_WHITE);
displayTopLeftText("New score saved", 5, 10, 2, 1);
unsigned long previousMillisScore = millis(); // Variable pour stocker le temps précédent
unsigned long intervalScore = 1000; // Intervalle
// Attendre avant d'afficher à nouveau les scores
while (millis() - previousMillisScore < intervalScore) {
// Attendre
}
// sortBuzzers();
// for (int i = 0; i < 4; i++) {
// ESPUI.getControl(buzzerScoresNumber[i])->value = String(buzzerScores[i]);
// ESPUI.updateControl(buzzerScoresNumber[i]);
// ESPUI.updateLabel(buzzer_label[i], String(buzzerNames[i]) + " : " + String(buzzerScores[i]));
// if (buzzerScores[i] == buzzerScoresTrier[0]) {
// ESPUI.setElementStyle(buzzer_label[i], "background-color: #ffde35;");
// ESPUI.print(buzzer_label[i], String(buzzerClassement[0]) + String(buzzerNames[i]) + " : " + String(buzzerScores[i]));
// } else if (buzzerScores[i] == buzzerScoresTrier[1]) {
// ESPUI.setElementStyle(buzzer_label[i], "background-color: #b5b5b5,");
// ESPUI.print(buzzer_label[i], String(buzzerClassement[1]) + String(buzzerNames[i]) + " : " + String(buzzerScores[i]));
// } else if (buzzerScores[i] == buzzerScoresTrier[2]) {
// ESPUI.setElementStyle(buzzer_label[i], "background-color: #ff9b37;");
// ESPUI.print(buzzer_label[i], String(buzzerClassement[2]) + String(buzzerNames[i]) + " : " + String(buzzerScores[i]));
// } else if (buzzerScores[i] == buzzerScoresTrier[3]) {
// ESPUI.setElementStyle(buzzer_label[i], "background-color: #c62c2c;");
// ESPUI.print(buzzer_label[i], String(buzzerClassement[3]) + String(buzzerNames[i]) + " : " + String(buzzerScores[i]));
// }
// }
displayScores();
break;
}
}
}
// Fonction pour afficher le timer sur l'écran sans le démarrer
void displayTimer() {
timeronoff = true;
unsigned long elapsedTime = 0;
if (timerEnabled) {
if (timerPausedAt != 0) {
elapsedTime = timerPausedAt - timerHasStarted;
} else {
elapsedTime = millis() - timerHasStarted;
}
}
unsigned long remainingTime = timerDuration - (elapsedTime / 1000);
int minutes = remainingTime / 60;
int seconds = remainingTime % 60;
lcd.setCursor(15, 0); // Position du timer en haut à droite de l'écran
if (minutes < 10) {
lcd.print(" ");
}
lcd.print(minutes);
lcd.print(":");
if (seconds < 10) {
lcd.print("0");
}
lcd.print(seconds);
// Convertir la couleur hexadécimale en couleur 16 bits
uint16_t TimerColor16bit = uint32To16BitColor(endColor);
// Définir la couleur du texte
tft.setTextColor(TimerColor16bit);
displayTopRightText(String(minutes) + ":" + (seconds < 10 ? "0" : "") + String(seconds), -30, 5, 2, 1);
TimerGraph(306, 12, 9, 4, TimerColor16bit);
// String timerValue = String(minutes) + ":" + (seconds < 10 ? "0" : "") + String(seconds);
// client.publish((stored_mqtt_topic_out + "/Timer").c_str(), timerValue.c_str());
// ESPUI.updateLabel(timer_label, "Timer : " + timerValue);
// ESPUI.setElementStyle(timer_label, "background-color: #00ff00;");
Serial.println("displayTimer");
}
// Fonction pour démarrer le timer
void startTimer() {
if (!timerEnabled) {
timerEnabled = true;
timerHasStarted = millis(); // Utiliser le temps actuel pour démarrer le timer
}
unsigned long currentTime = millis();
unsigned long elapsedTime = currentTime - timerHasStarted;
unsigned long timeToShow = timerDuration - (elapsedTime / 1000);
int seconds = timeToShow;
int minutes = seconds / 60;
seconds = seconds % 60;
lcd.setCursor(15, 0); // Position du timer sur l'écran LCD
if (minutes < 10) {
lcd.print(" ");
}
lcd.print(String(minutes, DEC));
lcd.print(":");
if (seconds < 10) {
lcd.print("0");
}
lcd.print(String(seconds, DEC));
// Obtenir la couleur hexadécimale
String hexColorTimer = updateColor(elapsedTime);
// Convertir la couleur hexadécimale en couleur 16 bits
uint16_t hexColorTimer16Bit = hexStringTo16BitColor(hexColorTimer);
// Définir la couleur du texte
tft.setTextColor(hexColorTimer16Bit);
displayTopRightText(String(minutes, DEC) + ":" + (seconds < 10 ? "0" : "") + String(seconds, DEC), -30, 5, 2, 1);
TimerGraph(306, 12, 9, 4, hexColorTimer16Bit);
if (seconds % 10 != lastSecondDigit) {
// tft.fillRect(280, 4, 12, 16, TFT_BLACK);
restoreBackground(280, 4, 12, 16);
lastSecondDigit = seconds % 10;
}
if (seconds % 10 == 9 && seconds / 10 != lastTenSecondDigit) {
// tft.fillRect(268, 4, 12, 16, TFT_BLACK);
restoreBackground(268, 4, 12, 16);
lastTenSecondDigit = seconds / 10;
}
if (minutes % 10 != lastMinuteDigit) {
// tft.fillRect(242, 4, 12, 16, TFT_BLACK);
restoreBackground(242, 4, 12, 16);
lastMinuteDigit = minutes % 10;
}
if (minutes % 10 == 9 && minutes / 10 != lastTenMinuteDigit) {
// tft.fillRect(230, 4, 12, 16, TFT_BLACK);
restoreBackground(230, 4, 12, 16);
lastTenMinuteDigit = minutes / 10;
}
if (timeToShow == 0) {
disableTimer(); // Appel à la fonction pour désactiver le timer
}
// Mise à jour de la couleur
// String hexColor = updateColor(elapsedTime);
// ESPUI.setElementStyle(timer_label, "background-color: #" + hexColor + ";");
// String timerValue = String(minutes) + ":" + (seconds < 10 ? "0" : "") + String(seconds);
// client.publish((stored_mqtt_topic_out + "/Timer").c_str(), timerValue.c_str());
// ESPUI.updateLabel(timer_label, "Timer : " + timerValue);
Serial.println("startTimer");
}
// Fonction pour mettre en pause le timer
void pauseTimer() {
if (timerEnabled && timerPausedAt == 0) {
Serial.println("pauseTimer");
timerPausedAt = millis() / 1000; // Enregistrer le moment où le timer est mis en pause
}
}
// Fonction pour reprendre le timer
void resumeTimer() {
if (timerEnabled && timerPausedAt != 0) {
Serial.println("resumeTimer");
timerHasStarted += ((millis() / 1000) - timerPausedAt);
timerPausedAt = 0;
}
}
// Fonction pour désactiver le timer (l'arrêter et le désafficher de l'écran)
void disableTimer() {
Serial.println("disableTimer");
timeronoff = false;
timerEnabled = false;
timerPausedAt = 0;
lcd.setCursor(15, 0); // Position du timer en haut à droite de l'écran
lcd.print(" "); // Effacer le timer de l'écran
lcd.setCursor(15, 1);
lcd.print(" ");
restoreBackground(230, 2, 90, 20);
restoreBackground(230, 30, 65, 16);
// ESPUI.setElementStyle(timer_label, "background-color: #999;");
// String timerValue = "";
// client.publish((stored_mqtt_topic_out + "/Timer").c_str(), timerValue.c_str());
// ESPUI.updateLabel(timer_label, "Timer : off");
// ESPUI.setElementStyle(timer_label, "background-color: #ff0000;");
}
void TimerGraph(int16_t Xpos, int16_t Ypos, int16_t r, int16_t thickness, uint16_t color) {
if (timerEnabled) {
unsigned long currentTime = millis();
unsigned long elapsedTime = (timerPausedAt != 0) ? (timerPausedAt - timerHasStarted) : (currentTime - timerHasStarted);
// Calculer l'angle de progression
float progress = (float)elapsedTime / (timerDuration * 1000);
float angleProgress = 360.0 * progress;
// Dessiner le contour du cercle
int16_t innerRadius = r - thickness;
for (int16_t x = -r; x <= r; x++) {
for (int16_t y = -r; y <= r; y++) {
int16_t distance = sqrt(x*x + y*y);
if (distance <= r && distance > innerRadius) {
float angle = atan2(y, x) * 180.0 / PI;
if (angle < 0) angle += 360.0;
// Effacer la partie du cercle correspondant au temps écoulé
if (angle > angleProgress) {
tft.drawPixel(Xpos + x, Ypos + y, color);
} else {
// Restaurer l'image de fond à cet emplacement
int16_t bgPixelX = Xpos + x;
int16_t bgPixelY = Ypos + y;
// Vérifier que les coordonnées restent dans les limites de l'écran
if (bgPixelX >= 0 && bgPixelX < tft.width() && bgPixelY >= 0 && bgPixelY < tft.height()) {
uint16_t bgColor = ((uint16_t*)BG)[bgPixelY * tft.width() + bgPixelX];
tft.drawPixel(bgPixelX, bgPixelY, bgColor);
}
}
}
}
}
} else {
// Dessiner le contour du cercle complet
int16_t innerRadius = r - thickness;
for (int16_t x = -r; x <= r; x++) {
for (int16_t y = -r; y <= r; y++) {
int16_t distance = sqrt(x*x + y*y);
if (distance <= r && distance > innerRadius) {
tft.drawPixel(Xpos + x, Ypos + y, color);
}
}
}
}
}
int calculateTextWidth(String text, int textSize, int spacing) {
int charWidth = 6 * textSize; // Largeur d'un caractère
int totalWidth = 0;
for (unsigned int i = 0; i < text.length(); i++) {
totalWidth += charWidth;
if (i < text.length() - 1) {
totalWidth += spacing;
}
}
return totalWidth;
}
void displayText(String text, int xOffset, int yOffset, int textSize, int spacing) {
tft.setTextSize(textSize);
int charWidth = 6 * textSize; // Largeur d'un caractère
int charHeight = 8 * textSize; // Hauteur d'un caractère
int x = xOffset;
int y = yOffset;
for (char &c : text) {
tft.setCursor(x, y);
tft.print(c);
x += charWidth + spacing; // Déplace le curseur pour le prochain caractère
if (x + charWidth > tft.width()) { // Vérifie si le prochain caractère dépasse la largeur de l'écran
x = xOffset; // Retourne au début de la ligne
y += charHeight + spacing; // Passe à la ligne suivante
}
}
}
void displayCenteredText(String text, int xOffset, int yOffset, int textSize, int spacing) {
tft.setTextSize(textSize);
int16_t w = tft.textWidth(text);
int16_t h = tft.fontHeight();
int x = (tft.width() - w) / 2 + xOffset;
int y = (tft.height() - h) / 2 + yOffset;
displayText(text, x, y, textSize, spacing);
}
void displayBottomLeftText(String text, int xOffset, int yOffset, int textSize, int spacing) {
tft.setTextSize(textSize);
int16_t w = tft.textWidth(text);
int16_t h = tft.fontHeight();
int x = xOffset;
int y = tft.height() - (8 * textSize) + yOffset;
displayText(text, x, y, textSize, spacing);
}
void displayBottomRightText(String text, int xOffset, int yOffset, int textSize, int spacing) {
tft.setTextSize(textSize);
int16_t w = tft.textWidth(text);
int16_t h = tft.fontHeight();
int x = tft.width() - w + xOffset;
int y = tft.height() - (8 * textSize) + yOffset;
displayText(text, x, y, textSize, spacing);
}
void displayTopLeftText(String text, int xOffset, int yOffset, int textSize, int spacing) {
int16_t w = tft.textWidth(text);
int16_t h = tft.fontHeight();
int x = xOffset;
int y = yOffset;
displayText(text, x, y, textSize, spacing);
}
void displayTopCenteredText(String text, int xOffset, int yOffset, int textSize, int spacing) {
tft.setTextSize(textSize);
int16_t w = tft.textWidth(text);
int16_t h = tft.fontHeight();
int x = (tft.width() - w) / 2 + xOffset;
int y = yOffset;
displayText(text, x, y, textSize, spacing);
}
void displayTopRightText(String text, int xOffset, int yOffset, int textSize, int spacing) {
tft.setTextSize(textSize);
int16_t w = tft.textWidth(text);
int16_t h = tft.fontHeight();
int x = tft.width() - w + xOffset;
int y = yOffset;
displayText(text, x, y, textSize, spacing);
}
void displayCenterLeftText(String text, int xOffset, int yOffset, int textSize, int spacing) {
tft.setTextSize(textSize);
int16_t w = tft.textWidth(text);
int16_t h = tft.fontHeight();
int x = xOffset;
int y = (tft.height() - h) / 2 + yOffset;
displayText(text, x, y, textSize, spacing);
}
void displayCenterRightText(String text, int xOffset, int yOffset, int textSize, int spacing) {
tft.setTextSize(textSize);
int16_t w = tft.textWidth(text);
int16_t h = tft.fontHeight();
int x = tft.width() - w + xOffset;
int y = (tft.height() - h) / 2 + yOffset;
displayText(text, x, y, textSize, spacing);
}
void displayBottomCenterText(String text, int xOffset, int yOffset, int textSize, int spacing) {
tft.setTextSize(textSize);
int16_t w = tft.textWidth(text);
int16_t h = tft.fontHeight();
int x = (tft.width() - w) / 2 + xOffset;
int y = tft.height() - h + yOffset;
displayText(text, x, y, textSize, spacing);
}
// Fonction pour mettre à jour la couleur en fonction du temps écoulé et renvoyer la couleur hexadécimale
String updateColor(unsigned long elapsedTime) {
float progress = (float)(timerDuration - (elapsedTime / 1000)) / timerDuration;
uint32_t color = interpolateColor(startColor, endColor, progress);
return colorToHex(color);
}
// Fonction d'interpolation linéaire entre deux couleurs
uint32_t interpolateColor(uint32_t color1, uint32_t color2, float fraction) {
uint8_t r1 = (color1 >> 16) & 0xFF;
uint8_t g1 = (color1 >> 8) & 0xFF;
uint8_t b1 = color1 & 0xFF;
uint8_t r2 = (color2 >> 16) & 0xFF;
uint8_t g2 = (color2 >> 8) & 0xFF;
uint8_t b2 = color2 & 0xFF;
uint8_t r = r1 + (r2 - r1) * fraction;
uint8_t g = g1 + (g2 - g1) * fraction;
uint8_t b = b1 + (b2 - b1) * fraction;
return (r << 16) | (g << 8) | b;
}
// Fonction pour convertir une couleur RGB en une chaîne hexadécimale
String colorToHex(uint32_t color) {
char hexColor[7];
snprintf(hexColor, sizeof(hexColor), "%06X", color);
return String(hexColor);
}
uint16_t hexStringTo16BitColor(String hexString) {
uint32_t color = strtoul(hexString.c_str(), NULL, 16);
uint8_t r = (color >> 16) & 0xFF;
uint8_t g = (color >> 8) & 0xFF;
uint8_t b = color & 0xFF;
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}
uint16_t uint32To16BitColor(uint32_t color32) {
uint8_t r = (color32 >> 16) & 0xFF;
uint8_t g = (color32 >> 8) & 0xFF;
uint8_t b = color32 & 0xFF;
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}
void sortBuzzers() {
// Copier les scores et les noms pour tri
for (int i = 0; i < 4; i++) {
buzzerScoresTrier[i] = buzzerScores[i];
strcpy(buzzerNamesTrier[i], buzzerNames[i]);
}
// Tri à bulles pour trier les scores et les noms des équipes par score décroissant
for (int i = 0; i < 4 - 1; i++) {
for (int j = 0; j < 4 - i - 1; j++) {
if (buzzerScoresTrier[j] < buzzerScoresTrier[j + 1]) {
// Échange des scores
int tempScore = buzzerScoresTrier[j];
buzzerScoresTrier[j] = buzzerScoresTrier[j + 1];
buzzerScoresTrier[j + 1] = tempScore;
// Échange des noms d'équipes
char tempName[26];
strcpy(tempName, buzzerNamesTrier[j]);
strcpy(buzzerNamesTrier[j], buzzerNamesTrier[j + 1]);
strcpy(buzzerNamesTrier[j + 1], tempName);
}
}
}
}
void restoreBackground(int x, int y, int width, int height) {
for (int16_t i = 0; i < width; i++) {
for (int16_t j = 0; j < height; j++) {
int16_t bgPixelX = x + i;
int16_t bgPixelY = y + j;
// Vérifier que les coordonnées restent dans les limites de l'écran
if (bgPixelX >= 0 && bgPixelX < tft.width() && bgPixelY >= 0 && bgPixelY < tft.height()) {
// Récupérer la couleur du pixel dans l'image de fond
uint16_t bgColor = ((uint16_t*)BG)[bgPixelY * tft.width() + bgPixelX];
// Redessiner le pixel avec la couleur originale
tft.drawPixel(bgPixelX, bgPixelY, bgColor);
}
}
}
}