Files
2025-08-15 16:22:58 +02:00

376 lines
12 KiB
C++

/**
* ESP32 P10 RGB Countdown Test
* Test pour vérifier le bon fonctionnement du compte à rebours
*
* Ce test affiche :
* - Un countdown configuré pour 5 minutes dans le futur
* - Différents formats d'affichage selon le temps restant
* - Test d'expiration du countdown
*
* Compatible avec toutes les configurations de cascade définies dans platformio.ini
*/
#define PxMATRIX_SPI_FREQUENCY 10000000
#include <Arduino.h>
#include <PxMatrix.h>
#include <RTClib.h>
// Pins pour la matrice LED
#define P_LAT 5
#define P_A 19
#define P_B 23
#define P_C 18
#define P_OE 4
// Configuration des panneaux - définie par les build flags
#ifndef MATRIX_WIDTH
#define MATRIX_WIDTH 32
#endif
#ifndef MATRIX_HEIGHT
#define MATRIX_HEIGHT 16
#endif
#ifndef MATRIX_PANELS_X
#define MATRIX_PANELS_X 3
#endif
#ifndef MATRIX_PANELS_Y
#define MATRIX_PANELS_Y 1
#endif
// Calcul des dimensions totales
#define TOTAL_WIDTH (MATRIX_WIDTH * MATRIX_PANELS_X)
#define TOTAL_HEIGHT (MATRIX_HEIGHT * MATRIX_PANELS_Y)
// Configuration du timer
hw_timer_t * timer = nullptr;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
// Temps d'affichage ajusté selon le nombre de panneaux
uint8_t display_draw_time = (MATRIX_PANELS_X * MATRIX_PANELS_Y > 4) ? 20 : 30;
// Objet matrice
PxMATRIX display(TOTAL_WIDTH, TOTAL_HEIGHT, P_LAT, P_OE, P_A, P_B, P_C);
// RTC
RTC_DS3231 rtc;
// Couleurs prédéfinies
uint16_t myRED = display.color565(255, 0, 0);
uint16_t myGREEN = display.color565(0, 255, 0);
uint16_t myBLUE = display.color565(0, 0, 255);
uint16_t myYELLOW = display.color565(255, 255, 0);
uint16_t myCYAN = display.color565(0, 255, 255);
uint16_t myMAGENTA = display.color565(255, 0, 255);
uint16_t myWHITE = display.color565(255, 255, 255);
uint16_t myBLACK = display.color565(0, 0, 0);
uint16_t myORANGE = display.color565(255, 165, 0);
// Variables pour le countdown de test
DateTime countdownTarget;
char countdownText[101];
bool countdownExpired = false;
const char* countdownTitle = "TEST COUNTDOWN";
// Variables pour le texte défilant
long scrollX = TOTAL_WIDTH; // Commence depuis la droite de tous les panneaux
unsigned long lastScrollTime = 0;
int scrollSpeed = 50;
// Déclaration anticipée de la fonction getTextWidth
uint16_t getTextWidth(const char* text);
// Gestionnaire d'interruption pour l'affichage
void IRAM_ATTR display_updater() {
portENTER_CRITICAL_ISR(&timerMux);
display.display(display_draw_time);
portEXIT_CRITICAL_ISR(&timerMux);
}
// Activation du timer d'affichage
void display_update_enable() {
timer = timerBegin(0, 80, true);
if (timer != NULL) {
timerAttachInterrupt(timer, &display_updater, true);
timerAlarmWrite(timer, 1500, true);
timerAlarmEnable(timer);
Serial.println("Display timer enabled successfully");
} else {
Serial.println("ERROR: Failed to create display timer");
}
}
// Fonction de calcul et formatage du countdown
void updateCountdown() {
DateTime now = rtc.now();
// Vérifier si le countdown est expiré
if (now >= countdownTarget) {
countdownExpired = true;
strcpy(countdownText, countdownTitle);
strcat(countdownText, " - EXPIRED!");
return;
}
countdownExpired = false;
// Calculer la différence
TimeSpan diff = countdownTarget - now;
long totalSeconds = diff.totalseconds();
int days = totalSeconds / 86400;
int hours = (totalSeconds % 86400) / 3600;
int minutes = (totalSeconds % 3600) / 60;
int seconds = totalSeconds % 60;
// Adapter le format selon la taille de l'écran
char tempText[101];
if (days > 0) {
sprintf(tempText, "%s: %dd %02dh %02dm %02ds", countdownTitle, days, hours, minutes, seconds);
} else if (hours > 0) {
sprintf(tempText, "%s: %02dh %02dm %02ds", countdownTitle, hours, minutes, seconds);
} else {
sprintf(tempText, "%s: %02dm %02ds", countdownTitle, minutes, seconds);
}
// Vérifier si le texte tient sur l'écran
uint16_t textWidth = getTextWidth(tempText);
if (textWidth <= TOTAL_WIDTH) {
// Le texte tient, pas besoin de défilement
strcpy(countdownText, tempText);
} else {
// Si le texte ne tient pas, utiliser un format plus compact si possible
if (days > 0) {
// Format sans titre, avec jours
sprintf(countdownText, "%dd %02dh %02dm %02ds", days, hours, minutes, seconds);
// Vérifier si ce format plus court tient sur l'écran
if (getTextWidth(countdownText) > TOTAL_WIDTH && MATRIX_PANELS_X < 3) {
// Format ultra-compact pour petits affichages
sprintf(countdownText, "%dd%02dh", days, hours);
}
} else if (hours > 0) {
// Format sans titre, avec heures
sprintf(countdownText, "%02dh %02dm %02ds", hours, minutes, seconds);
// Vérifier si ce format plus court tient sur l'écran
if (getTextWidth(countdownText) > TOTAL_WIDTH && MATRIX_PANELS_X < 2) {
// Format ultra-compact pour petits affichages
sprintf(countdownText, "%02dh%02dm", hours, minutes);
}
} else {
// Format sans titre, minutes seulement
sprintf(countdownText, "%02dm %02ds", minutes, seconds);
// Ce format devrait tenir même sur un seul panneau
}
// Si le format compact ne tient toujours pas, on garde le texte original pour défilement
if (getTextWidth(countdownText) > TOTAL_WIDTH) {
strcpy(countdownText, tempText);
}
}
}
// Fonction pour obtenir la largeur du texte
uint16_t getTextWidth(const char* text) {
int16_t x1, y1;
uint16_t w, h;
display.getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
return w;
}
// Fonction de texte défilant
void runScrollingText(const char* text, uint16_t color) {
unsigned long currentTime = millis();
if (currentTime - lastScrollTime >= scrollSpeed) {
lastScrollTime = currentTime;
// Calculer la largeur du texte avec la méthode précise
int textWidth = getTextWidth(text);
// Effacer toute la ligne de texte sur toute la largeur des panneaux
display.fillRect(0, 8, TOTAL_WIDTH, 8, myBLACK);
// Afficher le texte à la position actuelle de défilement
// Le texte doit pouvoir entrer et sortir complètement de l'écran
display.setCursor(scrollX, 8);
display.setTextColor(color);
display.print(text);
// Ajuster la vitesse de défilement en fonction du nombre de panneaux
int scrollStep = 1;
if (MATRIX_PANELS_X >= 4) scrollStep = 2;
if (MATRIX_PANELS_X >= 6) scrollStep = 3;
// Déplacer le texte
scrollX -= scrollStep;
// Réinitialiser la position quand le texte est complètement sorti de l'écran
// Il faut attendre que le texte soit complètement sorti
if (scrollX < -textWidth) {
// Recommencer depuis la droite de tous les panneaux
scrollX = TOTAL_WIDTH;
}
// Pour le débogage, afficher des marqueurs aux extrémités
display.drawPixel(0, 15, myWHITE); // Marqueur à gauche
display.drawPixel(TOTAL_WIDTH-1, 15, myWHITE); // Marqueur à droite
}
}
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("\n=== ESP32 P10 RGB COUNTDOWN TEST ===");
Serial.printf("Configuration: %dx%d panels (%dx%d total resolution)\n",
MATRIX_PANELS_X, MATRIX_PANELS_Y, TOTAL_WIDTH, TOTAL_HEIGHT);
// Initialisation du RTC
Serial.println("Initializing RTC...");
if (!rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1) delay(10);
}
Serial.println("RTC initialized successfully");
// Initialisation de l'affichage avec configuration P10 optimisée
display.begin(4); // 1/8 scan pour P10
display.setScanPattern(ZAGZIG);
display.setMuxPattern(BINARY);
const int muxdelay = 10; // Délai de multiplexage
display.setMuxDelay(muxdelay, muxdelay, muxdelay, muxdelay, muxdelay);
delay(100);
display_update_enable();
display.clearDisplay();
// Luminosité adaptée au nombre de panneaux
int brightness = 150;
if (MATRIX_PANELS_X > 2) brightness = 100;
if (MATRIX_PANELS_X > 4) brightness = 80;
if (MATRIX_PANELS_X > 6) brightness = 60;
display.setBrightness(brightness);
Serial.printf("Brightness set to: %d\n", brightness);
display.setTextWrap(false);
display.setRotation(0);
display.setTextSize(1);
// Configuration du countdown de test : 5 minutes dans le futur
DateTime now = rtc.now();
countdownTarget = now + TimeSpan(0, 0, 5, 0); // +5 minutes
Serial.println("Countdown target set to: ");
Serial.printf("%02d-%02d-%d %02d:%02d:%02d\n",
countdownTarget.day(), countdownTarget.month(), countdownTarget.year(),
countdownTarget.hour(), countdownTarget.minute(), countdownTarget.second());
// Test de la largeur d'affichage - afficher une ligne horizontale pour vérifier l'utilisation de tous les panneaux
display.clearDisplay();
display.drawFastHLine(0, 0, TOTAL_WIDTH, myWHITE); // Ligne horizontale en haut
display.drawFastHLine(0, TOTAL_HEIGHT-1, TOTAL_WIDTH, myWHITE); // Ligne horizontale en bas
display.drawFastVLine(0, 0, TOTAL_HEIGHT, myWHITE); // Ligne verticale à gauche
display.drawFastVLine(TOTAL_WIDTH-1, 0, TOTAL_HEIGHT, myWHITE); // Ligne verticale à droite
// Afficher la résolution totale
char resText[20];
sprintf(resText, "%dx%d", TOTAL_WIDTH, TOTAL_HEIGHT);
int resWidth = getTextWidth(resText);
int resX = (TOTAL_WIDTH - resWidth) / 2;
if (resX < 0) resX = 0;
display.setCursor(resX, 4);
display.setTextColor(myYELLOW);
display.print(resText);
delay(3000);
// Message de démarrage
display.clearDisplay();
display.setTextColor(myWHITE);
String startMsg = "COUNTDOWN TEST";
int msgWidth = startMsg.length() * 6;
int startX = (TOTAL_WIDTH - msgWidth) / 2;
if (startX < 0) startX = 0;
display.setCursor(startX, 0);
display.print(startMsg);
if (TOTAL_HEIGHT > 8) {
String targetMsg = "5 MIN TARGET";
int targetWidth = targetMsg.length() * 6;
int targetX = (TOTAL_WIDTH - targetWidth) / 2;
if (targetX < 0) targetX = 0;
display.setCursor(targetX, 8);
display.print(targetMsg);
}
delay(3000);
display.clearDisplay();
Serial.println("Starting countdown test...");
}
void loop() {
// Mise à jour du countdown chaque seconde
static unsigned long lastUpdate = 0;
unsigned long currentTime = millis();
if (currentTime - lastUpdate >= 1000) {
lastUpdate = currentTime;
updateCountdown();
}
// Vérifier si le texte tient sur l'écran
uint16_t textWidth = getTextWidth(countdownText);
uint16_t textColor = countdownExpired ? myRED : myORANGE;
// Effacer la ligne du texte sur toute la largeur disponible
display.fillRect(0, 8, TOTAL_WIDTH, 8, myBLACK);
// Afficher des marqueurs aux extrémités de l'écran pour visualiser la largeur totale
display.drawPixel(0, 15, myWHITE); // Marqueur à gauche
display.drawPixel(TOTAL_WIDTH-1, 15, myWHITE); // Marqueur à droite
// Affichage du texte (centré ou défilant)
if (textWidth <= TOTAL_WIDTH) {
// Le texte tient sur l'écran, on le centre
int textX = (TOTAL_WIDTH - textWidth) / 2;
if (textX < 0) textX = 0;
// Afficher le texte centré
display.setCursor(textX, 8);
display.setTextColor(textColor);
display.print(countdownText);
} else {
// Le texte est trop long, on utilise le défilement
runScrollingText(countdownText, textColor);
}
// Affichage de l'heure actuelle en haut
static unsigned long lastClockUpdate = 0;
if (currentTime - lastClockUpdate >= 500) {
lastClockUpdate = currentTime;
DateTime now = rtc.now();
char timeText[10];
sprintf(timeText, "%02d:%02d:%02d", now.hour(), now.minute(), now.second());
// Effacer la ligne du haut
display.fillRect(0, 0, TOTAL_WIDTH, 8, myBLACK);
// Centrer l'heure
int timeWidth = strlen(timeText) * 6;
int timeX = (TOTAL_WIDTH - timeWidth) / 2;
if (timeX < 0) timeX = 0;
display.setCursor(timeX, 0);
display.setTextColor(myCYAN);
display.print(timeText);
}
delay(10);
}