From 4349b37f055a64032802f61301af460fda025e8d Mon Sep 17 00:00:00 2001 From: MaPaLo76 <72209721+MaPaLo76@users.noreply.github.com> Date: Sun, 22 Feb 2026 14:00:54 +0100 Subject: [PATCH] feat(display): implement DisplayManager with raw MD_MAX72XX - Add include/display_manager.h: DisplayManager class declaration - Two-zone layout: Zone 0 (top, laser time), Zone 1 (bottom, countdown/status) - showLaserTime(), showCountdown(), showIdle(), showStatus(), setBrightness() - rotateCCW() bitmap transformation for 90 deg physical module rotation - charBitmap() for 17-character set (0-9, space, dash, dot, special chars) - Add src/display_manager.cpp: full implementation - Double-init pattern for SPI power stability - showLaserTime() format: <10 -> ' x.x', <100 -> 'xx.x', <1000 -> ' xxx', else 'xxxx' - showCountdown() right-aligned 4-char format - All methods use writeZone() -> writeChar() -> rotateCCW() -> MD_MAX72XX - Add test_sketches/test_display_manager.cpp: 6-step verification test - allLedsOn/Off, showLaserTime (12 boundary values), showCountdown 5->0 - showIdle, showStatus (Err/AP/WiFi/oF), live simulation loop - Update platformio.ini: add test-display-mgr environment - Update src/main.cpp: integrate display.begin/showIdle/update - Update Implementation-Plan.md: mark Phase 4 tasks 4.1-4.3 complete Tested on hardware: all 6 test steps passed --- Implementation-Plan.md | 28 ++- include/display_manager.h | 97 ++++++++ platformio.ini | 19 ++ src/display_manager.cpp | 330 +++++++++++++++++++++++++ src/main.cpp | 6 + test_sketches/test_display_manager.cpp | 131 ++++++++++ 6 files changed, 602 insertions(+), 9 deletions(-) create mode 100644 include/display_manager.h create mode 100644 src/display_manager.cpp create mode 100644 test_sketches/test_display_manager.cpp diff --git a/Implementation-Plan.md b/Implementation-Plan.md index ec4a9c1..6618422 100644 --- a/Implementation-Plan.md +++ b/Implementation-Plan.md @@ -77,19 +77,29 @@ ## Phase 4 – Dot-Matrix-Display (`DisplayManager`) -- [ ] **4.1** `include/display_manager.h` + `src/display_manager.cpp` erstellen - Wrapper um `MD_Parola` mit zwei Zonen: +- [x] **4.1** `include/display_manager.h` + `src/display_manager.cpp` erstellen + Implementierung mit rohem `MD_MAX72XX` (nicht MD_Parola) für vollständige Rotationskontrolle: - Zone 0 (Module 0–3, obere Reihe): Laserzeit in Minuten - Zone 1 (Module 4–7, untere Reihe): Countdown / Statusmeldungen + - `rotateCCW()` kompensiert physische 90°-CW-Verdrehung der Module + - 17 Zeichen-Bitmaps definiert (0–9, Sonderzeichen, Buchstaben) -- [ ] **4.2** Methoden implementieren: - - `showLaserTime(float minutes)` – obere Zeile - - `showCountdown(int seconds)` – untere Zeile (während Gratiszeit) - - `showIdle()` – `---` in unterer Zeile wenn kein Countdown - - `showError(const char* msg)` – Fehlermeldung in unterer Zeile, Modul 1 - - `update()` – in `loop()` aufrufen (MD_Parola benötigt regelmäßigen Aufruf) +- [x] **4.2** Methoden implementiert: + - `showLaserTime(float minutes)` – obere Zeile (4 Formate je Wertebereich) + - `showCountdown(int seconds)` – untere Zeile, rechtsbündig (während Gratiszeit) + - `showIdle()` – ` --` in unterer Zeile wenn kein Countdown aktiv + - `showStatus(const char* msg)` – max. 4-Zeichen-Statusmeldung (untere Zeile) + - `setBrightness()`, `allLedsOn()`, `allLedsOff()`, `clear()`, `printToSerial()` + - `update()` – in `loop()` aufrufen (no-op, Interface für künftige Animationen) + - `main.cpp` integriert: `display.begin()`, `display.showIdle()`, `display.update()` -- [ ] **4.3** Test: Statische Zahlen und Scrolltext auf beiden Zonen anzeigen +- [x] **4.3** Test: `test_sketches/test_display_manager.cpp`, Environment `test-display-mgr` + - Alle LEDs EIN/AUS ✅ + - `showLaserTime()` alle 12 Grenzwerte (0.0 – 9999.0 min) ✅ + - `showCountdown()` 5→0 ✅ + - `showIdle()` → ` --` ✅ + - `showStatus()` mit „Err ", „AP ", „WiFi", „ oF" ✅ + - Realistischer Loop: Laserzeit steigt, Countdown 20→0, dann Idle ✅ --- diff --git a/include/display_manager.h b/include/display_manager.h new file mode 100644 index 0000000..4b6dd0b --- /dev/null +++ b/include/display_manager.h @@ -0,0 +1,97 @@ +#pragma once + +// ============================================================================= +// display_manager.h – Dot-Matrix-Display Ausgabe (8× GYMAX7219, 4×2 Layout) +// Projekt: MQTT-Display LaserCutter +// +// Implementierung mit rohem MD_MAX72XX (kein MD_Parola), damit die physische +// 90°-CW Verdrehung der Module per Software (rotateCCW) zuverlässig kompensiert +// werden kann – ohne dass MD_Parola Zonenlogik interferiert. +// +// Zone-Aufteilung: +// Zone 0 (top): Module 0–3 → akkumulierte Laserzeit (Float, Minuten) +// Zone 1 (bottom): Module 4–7 → Countdown / Status +// +// Verwendung: +// display.begin(); // einmalig in setup() +// display.showLaserTime(42.5f); +// display.showCountdown(18); +// display.showIdle(); +// display.update(); // in loop() aufrufen (Pflicht) +// ============================================================================= + +#include +#include +#include +#include "config.h" + +// --------------------------------------------------------------------------- +// Maximale Textlänge je Zone (= Anzahl Module pro Zone) +// --------------------------------------------------------------------------- +#define DISP_CHARS_PER_ZONE DISPLAY_MODULES_PER_ZONE // 4 + +class DisplayManager { +public: + DisplayManager(); + + // Initialisierung (einmalig in setup()) + void begin(); + + // Helligkeit setzen 0–15 + void setBrightness(uint8_t level); + + // ---- Anzeige-Methoden --------------------------------------------------- + + // Obere Zone: Laserzeit in Minuten + // < 10 → " x.x" z.B. " 9.5" + // < 100 → "xx.x" z.B. "42.5" + // < 1000 → " xxx" z.B. " 123" + // < 10000 → "xxxx" z.B. "1234" + // >= 10000 → "!!!!" (Überlauf) + void showLaserTime(float minutes); + + // Untere Zone: Countdown-Sekunden (rechtsbündig) + // 0–999s werden dargestellt, > 999 → " !!!" + void showCountdown(int seconds); + + // Untere Zone: Leerlauf-Anzeige (" --") + void showIdle(); + + // Untere Zone: Statustext (max. 4 Zeichen, wird abgeschnitten / aufgefüllt) + void showStatus(const char* msg); + + // Beide Zonen löschen + void clear(); + + // Muss in loop() aufgerufen werden (reserviert für zukünftige Scroll-Animation) + void update() {} + + // Alle LEDs ein / aus (Testfunktion) + void allLedsOn(); + void allLedsOff(); + + // Debug-Ausgabe auf Serial + void printToSerial() const; + +private: + MD_MAX72XX _mx; + + // Schreibt genau DISP_CHARS_PER_ZONE Zeichen auf eine Zone + // zone 0 → Module 0..3 (oben) + // zone 1 → Module 4..7 (unten) + // str muss exakt DISP_CHARS_PER_ZONE Zeichen enthalten (kein Null-Terminator nötig) + void writeZone(uint8_t zone, const char* str); + + // Schreibt ein Zeichen auf ein einzelnes Modul + void writeChar(uint8_t moduleIdx, char c); + + // 90° CCW Rotation – kompensiert physische 90° CW Verdrehung der Module + static void rotateCCW(const uint8_t src[8], uint8_t dst[8]); + + // Gibt Zeiger auf 8-Byte-Bitmap für ASCII-Zeichen zurück + // Unterstützt: '0'–'9', ' ', '-', '.', '!', 'E', 'r', 'o', 'n', 'A', 'P' + static const uint8_t* charBitmap(char c); +}; + +// Globale Instanz +extern DisplayManager display; diff --git a/platformio.ini b/platformio.ini index c0aa190..e4239a2 100644 --- a/platformio.ini +++ b/platformio.ini @@ -108,3 +108,22 @@ build_flags = lib_deps = tzapu/WiFiManager @ ^2.0.17 majicDesigns/MD_MAX72XX @ ^3.5.1 + +; ============================================================================= +; TEST ENVIRONMENT 4.3 – DisplayManager Verifikation +; Flash: pio run -e test-display-mgr --target upload +; Monitor: pio device monitor -e test-display-mgr +; ============================================================================= +[env:test-display-mgr] +platform = espressif32 +board = az-delivery-devkit-v4 +framework = arduino +upload_port = COM3 +monitor_speed = 115200 +monitor_echo = yes +build_src_filter = -<*> +<../test_sketches/test_display_manager.cpp> +<../src/display_manager.cpp> +build_flags = + -DCORE_DEBUG_LEVEL=1 + -Wno-cpp +lib_deps = + majicDesigns/MD_MAX72XX @ ^3.5.1 diff --git a/src/display_manager.cpp b/src/display_manager.cpp new file mode 100644 index 0000000..262ec92 --- /dev/null +++ b/src/display_manager.cpp @@ -0,0 +1,330 @@ +// ============================================================================= +// display_manager.cpp – Dot-Matrix-Display Ausgabe +// Verwendet rohen MD_MAX72XX mit manuellem 90° CCW Bitmap-Rotations- +// ausgleich für die physisch 90° CW verbauten GYMAX7219 Module. +// ============================================================================= + +#include "display_manager.h" +#include + +// Globale Instanz +DisplayManager display; + +// --------------------------------------------------------------------------- +// Zeichensatz-Bitmaps +// Darstellung in AUFRECHTER (normaler) Orientierung. rotateCCW() kompensiert +// die physische 90° CW Verdrehung der Module beim Schreiben auf die Hardware. +// Je Eintrag: 8 Bytes = 8 Pixelzeilen von oben (Index 0) nach unten (Index 7), +// Bit 7 = linke Spalte, Bit 0 = rechte Spalte. +// --------------------------------------------------------------------------- +static const uint8_t BMP_0[8] = { + 0b00111100, 0b01000010, 0b01000110, 0b01001010, + 0b01010010, 0b01100010, 0b00111100, 0b00000000 +}; +static const uint8_t BMP_1[8] = { + 0b00010000, 0b00110000, 0b00010000, 0b00010000, + 0b00010000, 0b00010000, 0b00111000, 0b00000000 +}; +static const uint8_t BMP_2[8] = { + 0b00111100, 0b01000010, 0b00000010, 0b00001100, + 0b00010000, 0b00100000, 0b01111110, 0b00000000 +}; +static const uint8_t BMP_3[8] = { + 0b00111100, 0b01000010, 0b00000010, 0b00011100, + 0b00000010, 0b01000010, 0b00111100, 0b00000000 +}; +static const uint8_t BMP_4[8] = { + 0b00001000, 0b00011000, 0b00101000, 0b01001000, + 0b01111110, 0b00001000, 0b00001000, 0b00000000 +}; +static const uint8_t BMP_5[8] = { + 0b01111110, 0b01000000, 0b01111100, 0b00000010, + 0b00000010, 0b01000010, 0b00111100, 0b00000000 +}; +static const uint8_t BMP_6[8] = { + 0b00111100, 0b01000000, 0b01111100, 0b01000010, + 0b01000010, 0b01000010, 0b00111100, 0b00000000 +}; +static const uint8_t BMP_7[8] = { + 0b01111110, 0b00000010, 0b00000100, 0b00001000, + 0b00010000, 0b00100000, 0b00100000, 0b00000000 +}; +static const uint8_t BMP_8[8] = { + 0b00111100, 0b01000010, 0b01000010, 0b00111100, + 0b01000010, 0b01000010, 0b00111100, 0b00000000 +}; +static const uint8_t BMP_9[8] = { + 0b00111100, 0b01000010, 0b01000010, 0b00111110, + 0b00000010, 0b01000010, 0b00111100, 0b00000000 +}; +// Sonderzeichen +static const uint8_t BMP_SPACE[8] = { + 0b00000000, 0b00000000, 0b00000000, 0b00000000, + 0b00000000, 0b00000000, 0b00000000, 0b00000000 +}; +static const uint8_t BMP_MINUS[8] = { + 0b00000000, 0b00000000, 0b00000000, 0b01111110, + 0b00000000, 0b00000000, 0b00000000, 0b00000000 +}; +static const uint8_t BMP_DOT[8] = { // Dezimalpunkt '.' + 0b00000000, 0b00000000, 0b00000000, 0b00000000, + 0b00000000, 0b00000000, 0b00110000, 0b00000000 +}; +static const uint8_t BMP_EXCLAM[8] = { // '!' Überlauf-Anzeige + 0b00010000, 0b00010000, 0b00010000, 0b00010000, + 0b00010000, 0b00000000, 0b00010000, 0b00000000 +}; +static const uint8_t BMP_E[8] = { + 0b01111110, 0b01000000, 0b01000000, 0b01111100, + 0b01000000, 0b01000000, 0b01111110, 0b00000000 +}; +static const uint8_t BMP_r[8] = { // Kleinbuchstabe 'r' + 0b00000000, 0b00000000, 0b01011100, 0b01100000, + 0b01000000, 0b01000000, 0b01000000, 0b00000000 +}; +static const uint8_t BMP_o[8] = { // Kleinbuchstabe 'o' + 0b00000000, 0b00000000, 0b00111100, 0b01000010, + 0b01000010, 0b01000010, 0b00111100, 0b00000000 +}; +static const uint8_t BMP_n[8] = { + 0b00000000, 0b00000000, 0b01111000, 0b01000100, + 0b01000010, 0b01000010, 0b01000010, 0b00000000 +}; +static const uint8_t BMP_A[8] = { + 0b00011000, 0b00100100, 0b01000010, 0b01111110, + 0b01000010, 0b01000010, 0b01000010, 0b00000000 +}; +static const uint8_t BMP_P[8] = { + 0b01111100, 0b01000010, 0b01000010, 0b01111100, + 0b01000000, 0b01000000, 0b01000000, 0b00000000 +}; +static const uint8_t BMP_W[8] = { + 0b01000001, 0b01000001, 0b01000001, 0b01010101, + 0b01010101, 0b01100011, 0b01000001, 0b00000000 +}; +static const uint8_t BMP_i[8] = { + 0b00001000, 0b00000000, 0b00001000, 0b00001000, + 0b00001000, 0b00001000, 0b00001110, 0b00000000 +}; +static const uint8_t BMP_F[8] = { + 0b01111110, 0b01000000, 0b01000000, 0b01111100, + 0b01000000, 0b01000000, 0b01000000, 0b00000000 +}; + +// --------------------------------------------------------------------------- +// Lookup: ASCII-Zeichen → Bitmap-Zeiger +// --------------------------------------------------------------------------- +const uint8_t* DisplayManager::charBitmap(char c) { + switch (c) { + case '0': return BMP_0; + case '1': return BMP_1; + case '2': return BMP_2; + case '3': return BMP_3; + case '4': return BMP_4; + case '5': return BMP_5; + case '6': return BMP_6; + case '7': return BMP_7; + case '8': return BMP_8; + case '9': return BMP_9; + case ' ': return BMP_SPACE; + case '-': return BMP_MINUS; + case '.': return BMP_DOT; + case '!': return BMP_EXCLAM; + case 'E': return BMP_E; + case 'r': return BMP_r; + case 'o': return BMP_o; + case 'n': return BMP_n; + case 'A': return BMP_A; + case 'P': return BMP_P; + case 'W': return BMP_W; + case 'i': return BMP_i; + case 'F': return BMP_F; + default: return BMP_SPACE; + } +} + +// --------------------------------------------------------------------------- +// 90° CCW Rotation – kompensiert physische 90° CW Verdrehung der Module +// --------------------------------------------------------------------------- +void DisplayManager::rotateCCW(const uint8_t src[8], uint8_t dst[8]) { + memset(dst, 0, 8); + for (uint8_t r = 0; r < 8; r++) { + for (uint8_t c = 0; c < 8; c++) { + if (src[c] & (1 << r)) { + dst[r] |= (1 << (7 - c)); + } + } + } +} + +// --------------------------------------------------------------------------- +// Konstruktor +// --------------------------------------------------------------------------- +DisplayManager::DisplayManager() + : _mx(DISPLAY_HW_TYPE, DISPLAY_CS_PIN, DISPLAY_MODULE_COUNT) +{} + +// --------------------------------------------------------------------------- +// Doppelte Initialisierung – holt Module nach die beim ersten begin() noch +// nicht bereit waren (Power-On Anlaufverzögerung bei ext. Netzteil) +// --------------------------------------------------------------------------- +static void initMx(MD_MAX72XX& mx, uint8_t brightness) { + mx.begin(); + mx.control(MD_MAX72XX::INTENSITY, brightness); + mx.control(MD_MAX72XX::TEST, MD_MAX72XX::OFF); + mx.clear(); +} + +void DisplayManager::begin() { + initMx(_mx, DISPLAY_BRIGHTNESS); + delay(200); + initMx(_mx, DISPLAY_BRIGHTNESS); + LOG_I("DISP", "DisplayManager initialisiert (GENERIC_HW, %d Module)", DISPLAY_MODULE_COUNT); +} + +// --------------------------------------------------------------------------- +void DisplayManager::setBrightness(uint8_t level) { + if (level > 15) level = 15; + _mx.control(MD_MAX72XX::INTENSITY, level); +} + +// --------------------------------------------------------------------------- +// Einzelnes Zeichen auf ein Modul schreiben (mit CCW Rotation) +// --------------------------------------------------------------------------- +void DisplayManager::writeChar(uint8_t moduleIdx, char c) { + const uint8_t* bmp = charBitmap(c); + uint8_t rotated[8]; + rotateCCW(bmp, rotated); + for (uint8_t row = 0; row < 8; row++) { + _mx.setRow(moduleIdx, row, rotated[row]); + } +} + +// --------------------------------------------------------------------------- +// 4-Zeichen-String auf eine Zone schreiben +// zone 0 → Module 0–3, zone 1 → Module 4–7 +// str muss genau DISP_CHARS_PER_ZONE Zeichen enthalten +// --------------------------------------------------------------------------- +void DisplayManager::writeZone(uint8_t zone, const char* str) { + uint8_t base = zone * DISPLAY_MODULES_PER_ZONE; + for (uint8_t i = 0; i < DISPLAY_MODULES_PER_ZONE; i++) { + writeChar(base + i, str[i]); + } +} + +// --------------------------------------------------------------------------- +// Laserzeit oben anzeigen (Zone 0) +// --------------------------------------------------------------------------- +void DisplayManager::showLaserTime(float minutes) { + char buf[DISP_CHARS_PER_ZONE + 1]; + + if (minutes < 0.0f) minutes = 0.0f; + + if (minutes < 10.0f) { + // " x.x" → Trennzeichen in Position 2 + int whole = (int)minutes; + int frac = (int)((minutes - whole) * 10.0f + 0.5f); + if (frac >= 10) { whole++; frac = 0; } + snprintf(buf, sizeof(buf), " %d.%d", whole, frac); + } else if (minutes < 100.0f) { + // "xx.x" + int whole = (int)minutes; + int frac = (int)((minutes - whole) * 10.0f + 0.5f); + if (frac >= 10) { whole++; frac = 0; } + snprintf(buf, sizeof(buf), "%2d.%d", whole, frac); + } else if (minutes < 1000.0f) { + // " xxx" + snprintf(buf, sizeof(buf), "%4d", (int)(minutes + 0.5f)); + } else if (minutes < 10000.0f) { + // "xxxx" + snprintf(buf, sizeof(buf), "%4d", (int)(minutes + 0.5f)); + } else { + strlcpy(buf, "!!!!", sizeof(buf)); + } + + // snprintf kann Länge > 4 produzieren wenn Rounding unerwartet → absichern + if (strlen(buf) != DISP_CHARS_PER_ZONE) { + strlcpy(buf, "!!!!", sizeof(buf)); + } + + writeZone(DISPLAY_ZONE_TOP, buf); +} + +// --------------------------------------------------------------------------- +// Countdown-Sekunden unten anzeigen (Zone 1), rechtsbündig +// --------------------------------------------------------------------------- +void DisplayManager::showCountdown(int seconds) { + char buf[DISP_CHARS_PER_ZONE + 1]; + + if (seconds < 0) seconds = 0; + + if (seconds < 10) { + snprintf(buf, sizeof(buf), " %d", seconds); + } else if (seconds < 100) { + snprintf(buf, sizeof(buf), " %d", seconds); + } else if (seconds < 1000) { + snprintf(buf, sizeof(buf), " %d", seconds); + } else { + strlcpy(buf, " !!!", sizeof(buf)); + } + + if (strlen(buf) != DISP_CHARS_PER_ZONE) { + strlcpy(buf, " !!!", sizeof(buf)); + } + + writeZone(DISPLAY_ZONE_BOTTOM, buf); +} + +// --------------------------------------------------------------------------- +// Leerlauf-Anzeige unten (" --") +// --------------------------------------------------------------------------- +void DisplayManager::showIdle() { + writeZone(DISPLAY_ZONE_BOTTOM, " --"); +} + +// --------------------------------------------------------------------------- +// Statustext unten (max. 4 Zeichen) +// --------------------------------------------------------------------------- +void DisplayManager::showStatus(const char* msg) { + char buf[DISP_CHARS_PER_ZONE + 1] = " "; // mit Leerzeichen vorbelegen + size_t len = strlen(msg); + if (len > DISP_CHARS_PER_ZONE) len = DISP_CHARS_PER_ZONE; + memcpy(buf, msg, len); + buf[DISP_CHARS_PER_ZONE] = '\0'; + writeZone(DISPLAY_ZONE_BOTTOM, buf); +} + +// --------------------------------------------------------------------------- +// Beide Zonen löschen +// --------------------------------------------------------------------------- +void DisplayManager::clear() { + _mx.clear(); +} + +// --------------------------------------------------------------------------- +// Alle LEDs ein +// --------------------------------------------------------------------------- +void DisplayManager::allLedsOn() { + for (uint8_t m = 0; m < DISPLAY_MODULE_COUNT; m++) { + for (uint8_t r = 0; r < 8; r++) { + _mx.setRow(m, r, 0xFF); + } + } +} + +// --------------------------------------------------------------------------- +// Alle LEDs aus +// --------------------------------------------------------------------------- +void DisplayManager::allLedsOff() { + _mx.clear(); +} + +// --------------------------------------------------------------------------- +void DisplayManager::printToSerial() const { + LOG_I("DISP", "DisplayManager: %d Module, HW: GENERIC_HW, CS: GPIO%d", + DISPLAY_MODULE_COUNT, DISPLAY_CS_PIN); + LOG_I("DISP", "Zone 0 (oben): Module 0-%d = Laserzeit", + DISPLAY_MODULES_PER_ZONE - 1); + LOG_I("DISP", "Zone 1 (unten): Module %d-%d = Countdown/Status", + DISPLAY_MODULES_PER_ZONE, DISPLAY_MODULE_COUNT - 1); +} diff --git a/src/main.cpp b/src/main.cpp index cf4d06d..8e0d15a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,6 +2,7 @@ #include "config.h" #include "settings.h" #include "wifi_connector.h" +#include "display_manager.h" void setup() { Serial.begin(SERIAL_BAUD_RATE); @@ -10,11 +11,16 @@ void setup() { LOG_I("MAIN", "Laser GPIO: %d, Display CS: %d", LASER_SIGNAL_PIN, DISPLAY_CS_PIN); settings.printToSerial(); + display.begin(); + display.printToSerial(); + display.showIdle(); + wifiConnector.begin(); wifiConnector.printToSerial(); } void loop() { wifiConnector.loop(); + display.update(); delay(100); } diff --git a/test_sketches/test_display_manager.cpp b/test_sketches/test_display_manager.cpp new file mode 100644 index 0000000..79588d9 --- /dev/null +++ b/test_sketches/test_display_manager.cpp @@ -0,0 +1,131 @@ +/** + * TEST SKETCH 4.3 – DisplayManager Verifikation + * + * Testet alle Methoden des DisplayManagers: + * 1. Alle LEDs EIN/AUS (Modul-Check) + * 2. showLaserTime() mit verschiedenen Werten (Grenzwerttest) + * 3. showCountdown() hochzählend + * 4. showIdle() + * 5. showStatus() mit "Err", "AP", "WiFi" + * 6. Realistischer Loop: laufende Laserzeit + Countdown simuliert + * + * Flash: pio run -e test-display-mgr --target upload + * Monitor: pio device monitor -e test-display-mgr + */ + +#include +#include "display_manager.h" + +// Pause zwischen Testschritten +#define STEP_MS 2000 + +static void step(const char* desc) { + Serial.printf("[STEP] %s\n", desc); + delay(STEP_MS); +} + +// --------------------------------------------------------------------------- +void setup() { + Serial.begin(115200); + delay(500); + + Serial.println("\n========================================"); + Serial.println(" TEST 4.3 – DisplayManager"); + Serial.println("========================================"); + + display.begin(); + display.printToSerial(); + + // ------------------------------------------------------------------ + // 1. Alle LEDs EIN → alle 8 Module müssen leuchten + // ------------------------------------------------------------------ + Serial.println("\n[1] Alle LEDs EIN (2s) – alle 8 Module pruefen"); + display.allLedsOn(); + delay(2000); + display.allLedsOff(); + delay(500); + + // ------------------------------------------------------------------ + // 2. showLaserTime() – Grenzwerttests + // ------------------------------------------------------------------ + Serial.println("\n[2] showLaserTime() Grenzwerte:"); + + float testTimes[] = { 0.0f, 0.5f, 1.0f, 9.9f, 10.0f, 42.5f, 99.9f, + 100.0f, 123.0f, 999.0f, 1000.0f, 9999.0f }; + for (float t : testTimes) { + Serial.printf(" %.1f min → oben\n", t); + display.showLaserTime(t); + delay(1500); + } + display.clear(); + delay(500); + + // ------------------------------------------------------------------ + // 3. showCountdown() von 120 → 0 (jede Sekunde) + // ------------------------------------------------------------------ + Serial.println("\n[3] showCountdown() 5..0:"); + for (int s = 5; s >= 0; s--) { + Serial.printf(" %d s\n", s); + display.showCountdown(s); + delay(1000); + } + delay(500); + + // ------------------------------------------------------------------ + // 4. showIdle() + // ------------------------------------------------------------------ + Serial.println("\n[4] showIdle() (2s)"); + display.showIdle(); + step("Erwarte ' --' auf unterer Reihe"); + + // ------------------------------------------------------------------ + // 5. showStatus() mit verschiedenen Strings + // ------------------------------------------------------------------ + Serial.println("\n[5] showStatus() Tests:"); + const char* statMsgs[] = { "Err ", "AP ", "WiFi", " oF" }; + for (const char* m : statMsgs) { + Serial.printf(" '%s'\n", m); + display.showStatus(m); + delay(STEP_MS); + } + display.clear(); + delay(500); + + // ------------------------------------------------------------------ + // 6. Realistischer Betrieb: Laserzeit wächst, Countdown läuft + // ------------------------------------------------------------------ + Serial.println("\n[6] Realistischer Betrieb (30s): Laserzeit + Countdown"); + Serial.println(" Erwarte: oben steigt, unten zaehlt runter, dann '--'"); +} + +// --------------------------------------------------------------------------- +void loop() { + static float totalMin = 42.3f; + static int countdown = 20; + static bool counting = true; + static uint32_t lastSec = 0; + static uint32_t lastTenth = 0; + + uint32_t now = millis(); + + // Jede 100ms: Laserzeit um ~0.001 min erhöhen (0.06 min/min = 1x Normal) + if (now - lastTenth >= 100) { + lastTenth = now; + totalMin += 0.001f; + display.showLaserTime(totalMin); + } + + // Jede Sekunde: Countdown herunterzählen + if (now - lastSec >= 1000) { + lastSec = now; + if (counting && countdown > 0) { + display.showCountdown(countdown); + countdown--; + } else if (counting && countdown == 0) { + counting = false; + display.showIdle(); + Serial.println("[LIVE] Countdown abgelaufen → Idle"); + } + Serial.printf("[LIVE] %.2f min, countdown=%d\n", totalMin, countdown); + } +}