From 6ac33f459df317bf3f4858d8e9ada5ad82cf07b0 Mon Sep 17 00:00:00 2001 From: MaPaLo76 <72209721+MaPaLo76@users.noreply.github.com> Date: Sun, 22 Feb 2026 13:20:52 +0100 Subject: [PATCH] feat(phase1): complete hardware setup, display and button test sketches - platformio.ini: add all 8 libraries via Git URLs, add test-display and test-button environments, reduce SPI clock to 1 MHz for stability - include/config.h: add pin definitions, DISPLAY_HW_TYPE=GENERIC_HW, MQTT topics, NVS keys and logging macros - src/main.cpp: add minimal startup skeleton with config.h include - test_sketches/test_display.cpp: diagnostic sketch showing 0-based module index; GENERIC_HW verified with 90 deg CCW software rotation - test_sketches/test_button.cpp: GPIO INPUT_PULLUP test; LOW_ACTIVE confirmed - test_sketches/README.md: add wiring tables, flash commands, results table - README.md: update HW type to GENERIC_HW, add power supply note (0.5A / 2.5W measured, external 5V PSU required), add Conventional Commits section - Implementation-Plan.md: mark tasks 1.1-1.5 as complete BREAKING CHANGE: none --- Implementation-Plan.md | 17 ++- README.md | 121 ++++++++++++++--- include/config.h | 104 +++++++++++++++ platformio.ini | 57 +++++++- src/main.cpp | 29 +---- test_sketches/README.md | 121 +++++++++++++++++ test_sketches/test_button.cpp | 139 ++++++++++++++++++++ test_sketches/test_display.cpp | 231 +++++++++++++++++++++++++++++++++ 8 files changed, 775 insertions(+), 44 deletions(-) create mode 100644 include/config.h create mode 100644 test_sketches/README.md create mode 100644 test_sketches/test_button.cpp create mode 100644 test_sketches/test_display.cpp diff --git a/Implementation-Plan.md b/Implementation-Plan.md index 696a778..4a9e61e 100644 --- a/Implementation-Plan.md +++ b/Implementation-Plan.md @@ -7,7 +7,7 @@ ## Phase 1 – Projekt-Setup & Konfiguration -- [ ] **1.1** `platformio.ini` mit allen benötigten Bibliotheken erweitern +- [x] **1.1** `platformio.ini` mit allen benötigten Bibliotheken erweitern - `MD_Parola`, `MD_MAX72XX` - `PubSubClient` - `WiFiManager` (tzapu/WiFiManager) @@ -16,13 +16,24 @@ - `ElegantOTA` - `Preferences` (ESP32 built-in, kein Extra-Eintrag nötig) -- [ ] **1.2** `include/config.h` erstellen – zentrale Pin-Definitionen und Konstanten +- [x] **1.2** `include/config.h` erstellen – zentrale Pin-Definitionen und Konstanten - GPIO-Pins (MAX7219 SPI, Laser-Signal) - Anzahl der Module (8), Zonen-Aufteilung - Standard-Werte (Gratiszeit 20s, MQTT Port 1883, etc.) - MQTT-Topic-Konstanten -- [ ] **1.3** Build prüfen (leeres Projekt kompiliert fehlerfrei) +- [x] **1.3** Build prüfen (leeres Projekt kompiliert fehlerfrei) + +- [x] **1.4** Test Verdrahtung Dot-Matrix-Display + - GYMAX7219-Module gemäß Pinbelegung angeschlossen (MOSI GPIO 23, CLK GPIO 18, CS GPIO 5) + - Hardware-Typ `FC16_HW` (nicht GENERIC_HW) verifiziert + - Modul 1 oben-links, Modul 4 oben-rechts, Modul 5 unten-links, Modul 8 unten-rechts + - Zone 0 (0-idx 0–3) = obere Reihe, Zone 1 (0-idx 4–7) = untere Reihe + +- [x] **1.5** Test Verdrahtung Potentialfreier Schalter (Phase 1: Push Button) + - Push Button an GPIO 4 und GND angeschlossen (INPUT_PULLUP, kein externer Widerstand nötig) + - Pegel verifiziert: Button offen = HIGH, Button gedrückt = LOW → Polarität: LOW_ACTIVE + - Debounce 50 ms funktioniert --- diff --git a/README.md b/README.md index 4b6288b..ccb7ee2 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ Dieses Projekt implementiert einen ESP32-basierten MQTT-Client mit Dot-Matrix-Di 11. [Fehlerverhalten](#fehlerverhalten) 12. [Bibliotheken](#bibliotheken) 13. [Build & Flash](#build--flash) +14. [Beitragen / Commits](#beitragen--commits) --- @@ -27,41 +28,90 @@ Dieses Projekt implementiert einen ESP32-basierten MQTT-Client mit Dot-Matrix-Di | Komponente | Modell / Beschreibung | |-------------------------|----------------------------------------------------| | Mikrocontroller | AZ-Delivery ESP32 DevKit V4 | -| Dot-Matrix-Display | 8× GYMAX7219 Module (kompatibel zu MAX7219), 8×8 LEDs je Modul | +| Dot-Matrix-Display | 8× GYMAX7219 Module (kompatibel zu MAX7219), 8×8 LEDs je Modul, Typ: `GENERIC_HW` | | Display-Anordnung | 4 Module nebeneinander × 2 Reihen = 32×16 LEDs | | Display-Kaskadierung | Alle 8 Module in einer einzigen SPI-Kette | +| Stromversorgung Display | **Externes 5 V-Netzteil erforderlich** (gemessen: ~0,5 A / ~2,5 W bei 8 Modulen, Vollast bis ~2 A möglich) – ESP32 USB-Port reicht nicht aus | | Laser-Aktivsignal | Potentialfreier Ausgang des Laser Cutters (Optokoppler empfohlen) | -| Optional | Shelly PM Mini G3 als externer Leistungsmesser (separater MQTT-Service) | +| Optional | Shelly PM Mini G3 als externer Leistungszähler (separater MQTT-Service) | --- ## Pinbelegung -| Signal | ESP32 GPIO | Beschreibung | -|---------------|-----------|------------------------------------------------| -| MAX7219 MOSI | GPIO 23 | SPI Data (VSPI) | -| MAX7219 CLK | GPIO 18 | SPI Clock (VSPI) | -| MAX7219 CS | GPIO 5 | SPI Chip Select | -| Laser-Signal | GPIO 4 | Potentialfreier Eingang (INPUT_PULLUP, konfigurierbar invertierbar) | +| Signal | ESP32 GPIO | Beschreibung | +|---------------|-----------|-----------------------------------------------------------| +| MAX7219 MOSI | GPIO 23 | SPI Data (VSPI) → DIN Modul 1 | +| MAX7219 CLK | GPIO 18 | SPI Clock (VSPI) | +| MAX7219 CS | GPIO 5 | SPI Chip Select (alle Module) | +| Laser-Signal | GPIO 4 | Potentialfreier Eingang (INPUT_PULLUP, konfigurierbar) | -> Die Polarität des Laser-Signals (HIGH = aktiv oder LOW = aktiv) ist über das Webinterface konfigurierbar. +> Die Polarität des Laser-Signals (`LOW_ACTIVE` oder `HIGH_ACTIVE`) ist über das Webinterface konfigurierbar. + +### SPI-Ketten-Schaltbild + +``` + ESP32 OBERE REIHE UNTERE REIHE + + GPIO 23 (MOSI) ─── DIN ┌──────┐ DOUT─DIN ┌──────┐ DOUT─DIN ┌──────┐ DOUT─DIN ┌──────┐ + │ M 1 │ │ M 2 │ │ M 3 │ │ M 4 │ + │oben │ │ │ │ │ │oben │ + │links │ │ │ │ │ │rechts│ + GPIO 18 (CLK) ─────────┤ CLK ├──────────┤ CLK ├──────────┤ CLK ├──────────┤ CLK ├── + GPIO 5 (CS) ─────────┤ CS ├──────────┤ CS ├──────────┤ CS ├──────────┤ CS ├── + └──────┘ └──────┘ └──────┘ └──┬───┘ + │ DOUT + ┌──────┐ DOUT─DIN ┌──────┐ DOUT─DIN ┌──────┐ DOUT─DIN ┌──┴───┐ + │ M 8 │ │ M 7 │ │ M 6 │ │ M 5 │ + │unten │ │ │ │ │ │unten │ + │rechts│ │ │ │ │ │links │ + GPIO 18 (CLK) ─────────┤ CLK ├──────────┤ CLK ├──────────┤ CLK ├──────────┤ CLK │ + GPIO 5 (CS) ─────────┤ CS ├──────────┤ CS ├──────────┤ CS ├──────────┤ CS │ + └──────┘ └──────┘ └──────┘ └──────┘ +``` + +> **Hinweis:** Die untere Reihe läuft im Schaltbild von rechts nach links, weil DOUT von Modul 4 +> direkt zu DIN von Modul 5 geführt wird. MD_Parola berücksichtigt das automatisch. --- ## Display -Das Display besteht aus 8 GYMAX7219-Modulen in einer 4×2-Anordnung (32×16 LEDs gesamt). -Es ist in **zwei Zonen** aufgeteilt, die unabhängig voneinander beschrieben werden: +Das Display besteht aus 8 GYMAX7219-Modulen in einer **4×2-Anordnung** (32×16 LEDs gesamt). +Es ist in **zwei Zonen** aufgeteilt, die unabhängig voneinander beschrieben werden. -| Zone | Module (Kette) | Anzeige-Inhalt | -|------|----------------|----------------------------------------| -| Oben | Module 0–3 | Akkumulierte aktive Laserzeit in **Minuten** (z.B. `42.5 min`) | -| Unten | Module 4–7 | Laufender **Countdown** in Sekunden der Gratiszeit, danach `---` | +### Physisches Layout + +``` + DIN ← ESP32 GPIO 23 + ↓ + ┌─────────┬─────────┬─────────┬─────────┐ + │ Modul 1 │ Modul 2 │ Modul 3 │ Modul 4 │ ← Zone 0 (oben) 0-idx: 0–3 + │ oben │ │ │ oben │ Anzeige: Laserzeit (Minuten) + │ links │ │ │ rechts │ + ├─────────┼─────────┼─────────┼─────────┤ + │ Modul 5 │ Modul 6 │ Modul 7 │ Modul 8 │ ← Zone 1 (unten) 0-idx: 4–7 + │ unten │ │ │ unten │ Anzeige: Countdown / Status + │ links │ │ │ rechts │ + └─────────┴─────────┴─────────┴─────────┘ + SPI-Kette: ESP32 → M1 → M2 → M3 → M4 → M5 → M6 → M7 → M8 +``` + +**Hardware-Typ:** `GENERIC_HW` (verifiziert durch Hardware-Test; physische Ausrichtung erfordert 90° CCW Software-Rotation) + +> **Stromversorgung:** Die 8 Module müssen über ein **externes 5 V-Netzteil** versorgt werden. GND des Netzteils mit ESP32-GND verbinden. Gemessene Leistungsaufnahme im Betrieb: ca. **0,5 A / 2,5 W**; bei allen LEDs EIN (Testmuster) bis ca. 2 A möglich. + +### Zonen-Belegung + +| Zone | 0-Index-Bereich | Physisch | Anzeige-Inhalt | +|------|-----------------|----------------|---------------------------------------------------------| +| 0 | Modul 0–3 | Obere Reihe | Akkumulierte aktive Laserzeit in **Minuten** (z.B. `42.5`) | +| 1 | Modul 4–7 | Untere Reihe | **Countdown** Gratiszeit in Sekunden, danach `---` | - Die Anzeige aktualisiert sich sekündlich, solange der Laser aktiv ist. - Bei inaktivem Laser bleibt die letzte gemessene Zeit dauerhaft sichtbar. -- Fehlerzustände (WLAN, MQTT) werden in der **unteren Zeile, erstes Modul** angezeigt. -- Die Bibliotheken `MD_Parola` + `MD_MAX72XX` werden für die Ansteuerung verwendet. +- Fehlerzustände (WLAN, MQTT) werden in Zone 1 (untere Reihe) angezeigt. +- Bibliotheken: `MD_Parola` + `MD_MAX72XX` --- @@ -228,3 +278,40 @@ MQTT-Display-LaserCutter/ ├── platformio.ini └── README.md ``` +--- + +## Beitragen / Commits + +Dieses Projekt verwendet **[Conventional Commits](https://www.conventionalcommits.org/)** für alle Git-Commit-Nachrichten. + +### Format + +``` +(): + +[optionaler Body] + +[optionaler Footer] +``` + +### Wichtigste Typen + +| Typ | Verwendung | +|---|---| +| `feat` | Neues Feature | +| `fix` | Bugfix | +| `docs` | Nur Dokumentation | +| `refactor` | Code-Umstrukturierung ohne Feature/Fix | +| `test` | Test-Sketches / Test-Code | +| `chore` | Build-System, Abhängigkeiten, Konfiguration | + +### Beispiele + +``` +feat(display): add GENERIC_HW rotation compensation +fix(nvs): prevent crash on empty broker string +docs(readme): add power supply requirements +chore(platformio): reduce SPI clock to 1 MHz for stability +``` + +Breaking Changes werden mit `!` nach dem Typ markiert oder im Footer mit `BREAKING CHANGE:` beschrieben. \ No newline at end of file diff --git a/include/config.h b/include/config.h new file mode 100644 index 0000000..33318a0 --- /dev/null +++ b/include/config.h @@ -0,0 +1,104 @@ +#pragma once + +// ============================================================================= +// config.h – Zentrale Pin-Definitionen und Projekt-Konstanten +// Projekt: MQTT-Display LaserCutter +// Board: AZ-Delivery ESP32 DevKit V4 +// ============================================================================= + +// MD_MAX72XX wird hier für den Hardware-Typ-Enum benötigt +#include + +// ----------------------------------------------------------------------------- +// MAX7219 / GYMAX7219 SPI-Pinbelegung (VSPI) +// ----------------------------------------------------------------------------- +#define DISPLAY_MOSI_PIN 23 // SPI Data +#define DISPLAY_CLK_PIN 18 // SPI Clock +#define DISPLAY_CS_PIN 5 // SPI Chip Select + +// Anzahl der MAX7219-Module in der Kette (4 Spalten × 2 Reihen) +#define DISPLAY_MODULE_COUNT 8 + +// Hardware-Typ: GENERIC_HW für GYMAX7219 Module (verifiziert durch Hardware-Test) +// FC16_HW und PAROLA_HW erzeugen kopfstehende Ziffern +#define DISPLAY_HW_TYPE MD_MAX72XX::GENERIC_HW + +// Zonenaufteilung (MD_Parola zones, Zone 0 = obere Reihe, Zone 1 = untere Reihe) +#define DISPLAY_ZONE_TOP 0 // Module 0–3: Laserzeit in Minuten +#define DISPLAY_ZONE_BOTTOM 1 // Module 4–7: Countdown / Status +#define DISPLAY_ZONE_COUNT 2 + +// Module je Zone +#define DISPLAY_MODULES_PER_ZONE (DISPLAY_MODULE_COUNT / DISPLAY_ZONE_COUNT) // 4 + +// Helligkeit (0–15) +#define DISPLAY_BRIGHTNESS 7 + +// ----------------------------------------------------------------------------- +// Laser-Signal GPIO +// ----------------------------------------------------------------------------- +#define LASER_SIGNAL_PIN 4 // Digitaler Eingang, potentialfreier Schalter + +// Debounce-Zeit in Millisekunden +#define LASER_DEBOUNCE_MS 50 + +// ----------------------------------------------------------------------------- +// Gratiszeit (Default, überschreibbar aus NVS) +// ----------------------------------------------------------------------------- +#define DEFAULT_GRATIS_SECONDS 20 // Sekunden +#define MIN_GRATIS_SECONDS 0 +#define MAX_GRATIS_SECONDS 120 + +// ----------------------------------------------------------------------------- +// MQTT – Default-Konfiguration (überschreibbar aus NVS) +// ----------------------------------------------------------------------------- +#define DEFAULT_MQTT_BROKER "192.168.1.1" +#define DEFAULT_MQTT_PORT 1883 +#define DEFAULT_MQTT_USER "" +#define DEFAULT_MQTT_PASSWORD "" +#define MQTT_CLIENT_ID "lasercutter-display" +#define MQTT_RECONNECT_MS 10000 // Reconnect-Intervall in ms +#define MQTT_HEARTBEAT_MS 60000 // Heartbeat-Intervall in ms + +// MQTT Topics +#define MQTT_TOPIC_SESSION "lasercutter/session" // Publish beim Session-Ende +#define MQTT_TOPIC_STATUS "lasercutter/status" // Publish Heartbeat +#define MQTT_TOPIC_RESET "lasercutter/reset" // Subscribe Reset-Befehl + +// ----------------------------------------------------------------------------- +// WiFiManager +// ----------------------------------------------------------------------------- +#define WIFI_AP_NAME "LaserCutter-Setup" +#define WIFI_AP_TIMEOUT_S 120 // Sekunden bis AP-Timeout und Neustart + +// ----------------------------------------------------------------------------- +// NVS Namespace +// ----------------------------------------------------------------------------- +#define NVS_NAMESPACE "lasercutter" + +// NVS Keys +#define NVS_KEY_MQTT_BROKER "mqtt_broker" +#define NVS_KEY_MQTT_PORT "mqtt_port" +#define NVS_KEY_MQTT_USER "mqtt_user" +#define NVS_KEY_MQTT_PASSWORD "mqtt_pass" +#define NVS_KEY_GRATIS_SEC "gratis_sec" +#define NVS_KEY_SIGNAL_POL "signal_pol" // 0 = LOW_ACTIVE, 1 = HIGH_ACTIVE +#define NVS_KEY_TOTAL_MINUTES "total_min" + +// Signal-Polarität Werte +#define SIGNAL_POL_LOW_ACTIVE 0 // LOW = Laser aktiv (INPUT_PULLUP Default) +#define SIGNAL_POL_HIGH_ACTIVE 1 // HIGH = Laser aktiv + +// ----------------------------------------------------------------------------- +// Serielles Debugging +// ----------------------------------------------------------------------------- +#define SERIAL_BAUD_RATE 115200 + +// Log-Makros (nur wenn CORE_DEBUG_LEVEL >= 1 via build_flags) +#define LOG_I(tag, fmt, ...) Serial.printf("[I][%s] " fmt "\n", tag, ##__VA_ARGS__) +#define LOG_E(tag, fmt, ...) Serial.printf("[E][%s] " fmt "\n", tag, ##__VA_ARGS__) + +// ----------------------------------------------------------------------------- +// Watchdog +// ----------------------------------------------------------------------------- +#define WDT_TIMEOUT_S 30 // Watchdog-Timeout in Sekunden diff --git a/platformio.ini b/platformio.ini index cf3c1bd..3b39a56 100644 --- a/platformio.ini +++ b/platformio.ini @@ -12,6 +12,61 @@ platform = espressif32 board = az-delivery-devkit-v4 framework = arduino + +; Upload & Monitor upload_port = COM3 -monitor_speed = 9600 +monitor_speed = 115200 monitor_echo = yes +monitor_filters = esp32_exception_decoder, default + +; Partitionsschema: min_spiffs gibt mehr Platz für OTA (2x ~1.8 MB App) +board_build.partitions = min_spiffs.csv + +; Build-Flags +build_flags = + -DCORE_DEBUG_LEVEL=1 ; 0=keine, 1=Fehler, 3=Info, 5=Verbose + -DARDUINO_LOOP_STACK_SIZE=8192 + +; Bibliotheken +lib_deps = + majicDesigns/MD_Parola @ ^3.7.3 + majicDesigns/MD_MAX72XX @ ^3.5.1 + knolleary/PubSubClient @ ^2.8 + tzapu/WiFiManager @ ^2.0.17 + https://github.com/me-no-dev/AsyncTCP.git + https://github.com/me-no-dev/ESPAsyncWebServer.git + bblanchon/ArduinoJson @ ^7.3.0 + https://github.com/ayushsharma82/ElegantOTA.git + +; ============================================================================= +; TEST ENVIRONMENT 1.4 – Dot-Matrix-Display Verdrahtungstest +; Flash: pio run -e test-display --target upload +; ============================================================================= +[env:test-display] +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.cpp> +; SPI auf 1 MHz reduzieren: robuster bei 8 verketteten Modulen / langen Leitungen +build_flags = + -DMAX_SPI_CLOCK_SPEED=1000000L + -Wno-cpp ; unterdrückt "INFO: ARDUINO SPI interface selected"-Warning aus MD_MAX72XX +lib_deps = + majicDesigns/MD_Parola @ ^3.7.3 + majicDesigns/MD_MAX72XX @ ^3.5.1 + +; ============================================================================= +; TEST ENVIRONMENT 1.5 – Push Button / Potentialfreier Schalter Verdrahtungstest +; Flash: pio run -e test-button --target upload +; ============================================================================= +[env:test-button] +platform = espressif32 +board = az-delivery-devkit-v4 +framework = arduino +upload_port = COM3 +monitor_speed = 115200 +monitor_echo = yes +build_src_filter = -<*> +<../test_sketches/test_button.cpp> diff --git a/src/main.cpp b/src/main.cpp index 7beda21..26c4001 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,30 +1,13 @@ #include - -// Pin-Definition für die LED -const int LED_PIN = 2; // GPIO Pin 2 für die LED +#include "config.h" void setup() { - // Serial Monitor für Debugging initialisieren - Serial.begin(9600); - - // LED Pin als Ausgang konfigurieren - pinMode(LED_PIN, OUTPUT); - - Serial.println("Einfaches LED-Programm gestartet"); - Serial.println("LED Pin: " + String(LED_PIN)); - Serial.println("LED leuchtet dauerhaft"); + Serial.begin(SERIAL_BAUD_RATE); + LOG_I("MAIN", "LaserCutter Display gestartet"); + LOG_I("MAIN", "Laser GPIO: %d, Display CS: %d", LASER_SIGNAL_PIN, DISPLAY_CS_PIN); } void loop() { - // LED einschalten (HIGH = 3.3V) - digitalWrite(LED_PIN, HIGH); - Serial.println("LED ist AN"); - - delay(100); // 1 Sekunde warten - - // LED ausschalten (LOW = 0V) - digitalWrite(LED_PIN, LOW); - Serial.println("LED ist AUS"); - - delay(100); // 1 Sekunde warten + // Platzhalter – wird in den folgenden Phasen implementiert + delay(1000); } diff --git a/test_sketches/README.md b/test_sketches/README.md new file mode 100644 index 0000000..f70afe1 --- /dev/null +++ b/test_sketches/README.md @@ -0,0 +1,121 @@ +# Test Sketches – Hardware-Verdrahtungstests + +Diese Verzeichnis enthält eigenständige Test-Sketches zur Verifikation der Hardware-Verdrahtung. +Sie laufen als separate PlatformIO-Environments und berühren `main.cpp` nicht. + +--- + +## Physische Modul-Anordnung (GYMAX7219, 4×2) + +``` +DIN ← ESP32 GPIO 23 + ↓ +┌─────────┬─────────┬─────────┬─────────┐ +│ Modul 1 │ Modul 2 │ Modul 3 │ Modul 4 │ ← Obere Reihe (Zone 0, 0-idx: 0–3) +│ links │ │ │ rechts │ +├─────────┼─────────┼─────────┼─────────┤ +│ Modul 5 │ Modul 6 │ Modul 7 │ Modul 8 │ ← Untere Reihe (Zone 1, 0-idx: 4–7) +│ links │ │ │ rechts │ +└─────────┴─────────┴─────────┴─────────┘ + DOUT Modul 4 → DIN Modul 5 (Reihen verbinden) +``` + +SPI-Datenfluss: ESP32 → Modul 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 + +**Hardware-Typ:** `GENERIC_HW` (verifiziert; 90° CCW Software-Rotation aktiv) + +--- + +## Test 1.4 – Dot-Matrix-Display (`test_display.cpp`) + +### Zweck +- Alle 8 Module in korrekter Reihenfolge und Ausrichtung prüfen +- Zonentrennung (Zone 0 = oben, Zone 1 = unten) verifizieren +- Realistische Anzeige (Minuten / Countdown) demonstrieren + +### Verdrahtung + +| ESP32 GPIO | Modul-Pin | Beschreibung | +|-----------|-----------|---------------------| +| GPIO 23 | DIN | SPI Data (VSPI MOSI)| +| GPIO 18 | CLK | SPI Clock | +| GPIO 5 | CS / LOAD | SPI Chip Select | +| GND | GND | Masse (gemeinsam mit ESP32 GND) | +| **5V extern** | VCC | **Externes Netzteil ≥ 1 A** (USB-Port des ESP32 reicht nicht – gemessen 0,5 A / 2,5 W) | + +> Alle 8 Module teilen CLK und CS. DIN eines Moduls wird mit DOUT des Vorgängers verbunden. + +### Flash & Monitor + +```bash +pio run -e test-display --target upload +pio device monitor -e test-display +``` + +### Ablauf +1. **Alle LEDs EIN** (2 s) → prüfen ob alle 8 Module reagieren +2. **ZONE 0** auf oberer Reihe, `------` auf unterer Reihe +3. **ZONE 1** auf unterer Reihe, `------` auf oberer Reihe +4. **Realistische Anzeige**: `42.5` oben, `20` unten +5. **Laufende Zähler** im Loop: Minuten und Countdown-Sekunden + +### Erwartetes Ergebnis +- Obere Reihe (Modul 1–4): zeigt `ZONE 0` bei Schritt 2 +- Untere Reihe (Modul 5–8): zeigt `ZONE 1` bei Schritt 3 +- Text erscheint lesbar von links nach rechts (nicht gespiegelt) + +--- + +## Test 1.5 – Potentialfreier Schalter / Push Button (`test_button.cpp`) + +### Zweck +- GPIO-Eingang mit INPUT_PULLUP testen +- Pegel bei offenem und geschlossenen Schalter dokumentieren +- Debounce-Verhalten prüfen (50 ms) +- Betätigungsdauer (Basis für Gratiszeit-Logik) messen + +### Verdrahtung – Phase 1: Push Button + +``` +ESP32 GPIO 4 ────┤ Button ├──── GND + (NO-Kontakt) +``` + +Kein externer Widerstand nötig – der interne Pull-Up (INPUT_PULLUP) ist aktiv. + +### Verdrahtung – Phase 2: Optokoppler (Laser-Signal) + +``` +ESP32 GPIO 4 ──── Kollektor + Optokoppler +GND ──── Emitter +``` + +Gleiche Logik wie Button – Laser aktiv = Optokoppler zieht GPIO auf GND. + +### Flash & Monitor + +```bash +pio run -e test-button --target upload +pio device monitor -e test-button +``` + +### Erwartetes Ergebnis + +| Zustand | GPIO-Pegel | Serial-Ausgabe | +|-------------------|-----------|------------------------| +| Button offen | HIGH | `HIGH (inaktiv)` | +| Button gedrückt | LOW | `LOW → LASER AKTIV` | + +→ Polarität: `LOW_ACTIVE` (Standardwert in `config.h` / NVS) + +Alle 5 Sekunden erscheint ein Heartbeat mit aktuellem Pegel und Betätigungszähler. + +--- + +## Testergebnisse + +| Task | Status | Ergebnis | +|------|--------|----------| +| 1.4 Display | ✅ | GYMAX7219 mit `GENERIC_HW` + 90° CCW Software-Rotation; Modul 1 oben-links, Modul 8 unten-rechts; externes 5 V-Netzteil notwendig | +| 1.5 Button | ✅ | `LOW_ACTIVE`; Debounce 50 ms funktioniert | diff --git a/test_sketches/test_button.cpp b/test_sketches/test_button.cpp new file mode 100644 index 0000000..4e2e7e3 --- /dev/null +++ b/test_sketches/test_button.cpp @@ -0,0 +1,139 @@ +/** + * TEST SKETCH 1.5 – Potentialfreier Schalter / Push Button Verdrahtungstest + * + * Zweck: + * - Prüft ob der Button/Schalter korrekt am GPIO angeschlossen ist + * - Zeigt HIGH/LOW-Pegel im Seriellen Monitor an + * - Dokumentiert Pegel bei gedrücktem und losgelassenem Zustand + * - Simuliert Debounce wie im späteren LaserTracker + * + * Verdrahtung: + * Schritt 1 (Push Button): + * GPIO 4 ──── Button-Pin 1 + * GND ──── Button-Pin 2 + * (kein externer Widerstand nötig – INPUT_PULLUP ist aktiv) + * + * Erwartetes Verhalten mit INPUT_PULLUP: + * Button OFFEN → GPIO = HIGH (3.3V über Pull-Up) + * Button GEDRÜCKT → GPIO = LOW (direkt auf GND) + * → Polarität: LOW_ACTIVE + * + * Schritt 2 (Optokoppler / potentialfreier Ausgang): + * Gleiche Verdrahtung – Kollektor des Optokopplers an GPIO 4, Emitter an GND + * + * Serielle Ausgabe: 115200 Baud + * + * Flash: pio run -e test-button --target upload + * Monitor: pio device monitor -e test-button + */ + +#include + +// ---- Pin-Definition (aus config.h übernommen) ---- +#define LASER_SIGNAL_PIN 4 +#define DEBOUNCE_MS 50 // Software-Entprellzeit in Millisekunden + +// ---- Zustandsvariablen ---- +int rawState = HIGH; // aktuell gelesener Rohwert +int stableState = HIGH; // entprellter Zustand +int lastRaw = HIGH; // letzter Rohwert (für Flanken-Erkennung) +uint32_t debounceTimer = 0; // Zeitstempel letzter Flanke + +uint32_t pressStart = 0; // Zeitstempel Tastendruck-Beginn +uint32_t pressCount = 0; // Anzahl erkannter Betätigungen + +void printState(int s, bool stable) { + if (s == LOW) { + Serial.printf("[%8lu ms] GPIO %-3s = LOW (0V) %s → LASER AKTIV\n", + millis(), stable ? "ENT" : "RAW", stable ? "✓ STABIL" : ""); + } else { + Serial.printf("[%8lu ms] GPIO %-3s = HIGH (3.3V) %s → LASER INAKTIV\n", + millis(), stable ? "ENT" : "RAW", stable ? "✓ STABIL" : ""); + } +} + +void setup() { + Serial.begin(115200); + delay(500); + + Serial.println("================================================"); + Serial.println(" TEST 1.5 – Potentialfreier Schalter / Button"); + Serial.println("================================================"); + Serial.printf(" GPIO Pin: %d\n", LASER_SIGNAL_PIN); + Serial.printf(" Modus: INPUT_PULLUP\n"); + Serial.printf(" Debounce: %d ms\n", DEBOUNCE_MS); + Serial.println("------------------------------------------------"); + Serial.println(" Verdrahtung (Push Button):"); + Serial.printf(" GPIO %d ──── Button-Pin 1\n", LASER_SIGNAL_PIN); + Serial.println(" GND ──── Button-Pin 2"); + Serial.println("------------------------------------------------"); + Serial.println(" Erwartetes Verhalten:"); + Serial.println(" Button OFFEN → HIGH (kein Strom, Pull-Up aktiv)"); + Serial.println(" Button GEDRÜCKT → LOW (GPIO direkt auf GND)"); + Serial.println(" → Polarität: LOW_ACTIVE (Standard in config.h)"); + Serial.println("================================================"); + Serial.println(" Drücke den Button und beobachte die Ausgabe..."); + Serial.println(); + + pinMode(LASER_SIGNAL_PIN, INPUT_PULLUP); + + stableState = digitalRead(LASER_SIGNAL_PIN); + lastRaw = stableState; + + Serial.printf("[INIT] Startzustand: GPIO = %s\n", + stableState == LOW ? "LOW (Button gedrückt?)" : "HIGH (offen – OK)"); + Serial.println(); +} + +void loop() { + rawState = digitalRead(LASER_SIGNAL_PIN); + + // Flanke erkannt → Debounce-Timer (re)starten + if (rawState != lastRaw) { + debounceTimer = millis(); + lastRaw = rawState; + } + + // Nach Debounce-Zeit: stabilen Zustand aktualisieren + if ((millis() - debounceTimer) >= DEBOUNCE_MS) { + if (rawState != stableState) { + stableState = rawState; + + if (stableState == LOW) { + // Steigende Flanke (Laser aktiv) + pressCount++; + pressStart = millis(); + Serial.println("┌─────────────────────────────────────────┐"); + Serial.printf( "│ BETÄTIGUNG #%3lu – BEGINN │\n", pressCount); + printState(stableState, true); + Serial.println("└─────────────────────────────────────────┘"); + + } else { + // Fallende Flanke (Laser inaktiv) + uint32_t duration = millis() - pressStart; + Serial.println("┌─────────────────────────────────────────┐"); + Serial.printf( "│ BETÄTIGUNG #%3lu – ENDE │\n", pressCount); + printState(stableState, true); + Serial.printf( "│ Dauer: %lu ms (%lu s) │\n", + duration, duration / 1000); + Serial.println("└─────────────────────────────────────────┘"); + Serial.println(); + + // Hinweis für kurze Betätigungen (< Gratiszeit) + if (duration < 20000) { + Serial.printf(" ℹ Dauer < 20 s → würde als GRATISZEIT gelten (kein Kostenanteil)\n\n"); + } + } + } + } + + // Alle 5 Sekunden Heartbeat ausgeben wenn kein Event + static uint32_t lastHeartbeat = 0; + if (millis() - lastHeartbeat >= 5000) { + lastHeartbeat = millis(); + Serial.printf("[%8lu ms] Heartbeat | GPIO = %s | Betätigungen: %lu\n", + millis(), + stableState == LOW ? "LOW (AKTIV) " : "HIGH (inaktiv)", + pressCount); + } +} diff --git a/test_sketches/test_display.cpp b/test_sketches/test_display.cpp new file mode 100644 index 0000000..f35cd8d --- /dev/null +++ b/test_sketches/test_display.cpp @@ -0,0 +1,231 @@ +/** + * TEST SKETCH 1.4 – GYMAX7219 Modulnummern-Diagnose + * + * Jedes Modul zeigt seine eigene 0-basierte Index-Nummer (0..7). + * Direkte MD_MAX72XX API – kein Parola, keine Zonen. + * + * Ablauf: + * 1. Alle Module zeigen dauerhaft ihre Indexnummer 0–7 + * 2. Alle 3 s wird abwechselnd getestet: + * a) Alle LEDs EIN (volle Helligkeit → Stromtest) + * b) Alle LEDs AUS + * c) Jedes Modul zeigt seine Nummer wieder + * + * Serial Monitor zeigt welcher hw-Typ gerade aktiv ist. + * → Nummern richtig/lesbar + Reihenfolge notieren, dann in config.h eintragen. + * + * Flash: pio run -e test-display --target upload + * Monitor: pio device monitor -e test-display + */ + +#include +#include +#include + +// ---- Pins ---- +#define CS_PIN 5 +#define NUM_MOD 8 + +// ---- Zu testende Hardware-Typen ---- +// Kommentiere einen aus und flashe erneut, bis die Zahlen AUFRECHT stehen. +// Dann diesen Typ in config.h als DISPLAY_HW_TYPE eintragen. +#define HW_TYPE MD_MAX72XX::GENERIC_HW +//#define HW_TYPE MD_MAX72XX::FC16_HW +//#define HW_TYPE MD_MAX72XX::PAROLA_HW + +// Lesbare Bezeichnung für Serial (muss zur obigen Wahl passen) +#define HW_TYPE_NAME "GENERIC_HW" +//#define HW_TYPE_NAME "FC16_HW" +//#define HW_TYPE_NAME "PAROLA_HW" + +MD_MAX72XX mx = MD_MAX72XX(HW_TYPE, CS_PIN, NUM_MOD); + +// 5-spaltige 7-Segment-ähnliche Ziffern 0–8 (8 Zeilen, Spalten links→rechts) +// Jeder Eintrag: 8 Bytes = 8 Pixelzeilen von oben nach unten +static const uint8_t DIGIT[9][8] = { + // 0 + { 0b00111100, + 0b01000010, + 0b01000010, + 0b01000010, + 0b01000010, + 0b01000010, + 0b00111100, + 0b00000000 }, + // 1 + { 0b00010000, + 0b00110000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00111000, + 0b00000000 }, + // 2 + { 0b00111100, + 0b01000010, + 0b00000010, + 0b00001100, + 0b00110000, + 0b01000000, + 0b01111110, + 0b00000000 }, + // 3 + { 0b00111100, + 0b01000010, + 0b00000010, + 0b00011100, + 0b00000010, + 0b01000010, + 0b00111100, + 0b00000000 }, + // 4 + { 0b00001000, + 0b00011000, + 0b00101000, + 0b01001000, + 0b01111110, + 0b00001000, + 0b00001000, + 0b00000000 }, + // 5 + { 0b01111110, + 0b01000000, + 0b01000000, + 0b01111100, + 0b00000010, + 0b01000010, + 0b00111100, + 0b00000000 }, + // 6 + { 0b00111100, + 0b01000000, + 0b01000000, + 0b01111100, + 0b01000010, + 0b01000010, + 0b00111100, + 0b00000000 }, + // 7 + { 0b01111110, + 0b00000010, + 0b00000100, + 0b00001000, + 0b00010000, + 0b00010000, + 0b00010000, + 0b00000000 }, + // 8 + { 0b00111100, + 0b01000010, + 0b01000010, + 0b00111100, + 0b01000010, + 0b01000010, + 0b00111100, + 0b00000000 }, +}; + +// Dreht eine 8x8 Bitmap 90° gegen den Uhrzeigersinn +// Kompensiert die 90° CW physische Ausrichtung der Module +// Formel: new[r][c] = old[c][7-r] +// → new_row[r] |= (1<<(7-c)) wenn old_row[c] Bit r gesetzt ist +void 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)); + } + } + } +} + +// Schreibt Ziffer d (0-basiert) in Modul moduleIdx – mit 90° CCW Korrektur +void showDigit(uint8_t moduleIdx, uint8_t d) { + if (d > 8) d = 8; + uint8_t rotated[8]; + rotateCCW(DIGIT[d], rotated); + for (uint8_t row = 0; row < 8; row++) { + mx.setRow(moduleIdx, row, rotated[row]); + } +} + +void allLedsOn() { for (uint8_t m=0;m= 4000) { + last = millis(); + allLedsOff(); + delay(100); + showAllNumbers(); + Serial.println("[LIVE] Module 0–7 angezeigt"); + } +}