1. Vorschlag Implementierung
This commit is contained in:
parent
5a3a4e40bd
commit
d84dfd2119
229
Implementation-Plan.md
Normal file
229
Implementation-Plan.md
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
# Implementation Plan – Laser Cutter MQTT Display
|
||||
|
||||
> Dieses Dokument beschreibt die Entwicklungsreihenfolge des Projekts.
|
||||
> Tasks werden während der Entwicklung mit `[x]` abgehakt.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 – Projekt-Setup & Konfiguration
|
||||
|
||||
- [ ] **1.1** `platformio.ini` mit allen benötigten Bibliotheken erweitern
|
||||
- `MD_Parola`, `MD_MAX72XX`
|
||||
- `PubSubClient`
|
||||
- `WiFiManager` (tzapu/WiFiManager)
|
||||
- `ESPAsyncWebServer` + `AsyncTCP`
|
||||
- `ArduinoJson`
|
||||
- `ElegantOTA`
|
||||
- `Preferences` (ESP32 built-in, kein Extra-Eintrag nötig)
|
||||
|
||||
- [ ] **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)
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 – NVS Persistenz (`Settings`)
|
||||
|
||||
- [ ] **2.1** `include/settings.h` + `src/settings.cpp` erstellen
|
||||
Speichert und lädt alle Konfigurationswerte über ESP32 `Preferences` (NVS):
|
||||
- MQTT Broker IP, Port, User, Passwort
|
||||
- Gratiszeit (0–120 s)
|
||||
- Signal-Polarität (`LOW_ACTIVE` / `HIGH_ACTIVE`)
|
||||
- Akkumulierte Laserzeit (float, Minuten)
|
||||
|
||||
- [ ] **2.2** Unit-Test (Serial-Output): Werte schreiben, ESP32 neu starten, Werte lesen und verifizieren
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 – WiFi & WiFiManager
|
||||
|
||||
- [ ] **3.1** WiFiManager einbinden
|
||||
- AP-Name: `LaserCutter-Setup`
|
||||
- Captive-Portal-Seite für WLAN-Credentials
|
||||
- Timeout: 120 s, danach Neustart
|
||||
|
||||
- [ ] **3.2** Verbindungsstatus-Callback implementieren (für spätere Fehleranzeige)
|
||||
|
||||
- [ ] **3.3** Test: Erstverbindung mit neuem AP, gespeichertes WLAN nach Neustart automatisch verbinden
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 – Dot-Matrix-Display (`DisplayManager`)
|
||||
|
||||
- [ ] **4.1** `include/display_manager.h` + `src/display_manager.cpp` erstellen
|
||||
Wrapper um `MD_Parola` mit zwei Zonen:
|
||||
- Zone 0 (Module 0–3, obere Reihe): Laserzeit in Minuten
|
||||
- Zone 1 (Module 4–7, untere Reihe): Countdown / Statusmeldungen
|
||||
|
||||
- [ ] **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)
|
||||
|
||||
- [ ] **4.3** Test: Statische Zahlen und Scrolltext auf beiden Zonen anzeigen
|
||||
|
||||
---
|
||||
|
||||
## Phase 5 – Laser-Signaldetektion & Zeit-Tracking (`LaserTracker`)
|
||||
|
||||
- [ ] **5.1** `include/laser_tracker.h` + `src/laser_tracker.cpp` erstellen
|
||||
|
||||
- [ ] **5.2** GPIO-Eingang konfigurieren:
|
||||
- `INPUT_PULLUP` auf GPIO 4
|
||||
- Software-Debounce (min. 50 ms Entprellzeit)
|
||||
- Polarität über `Settings` auslesbar
|
||||
|
||||
- [ ] **5.3** Session-Logik implementieren:
|
||||
- `onLaserActive()` → Session-Start, Timer starten
|
||||
- `onLaserInactive()` → Session-Ende, akkumulierte Zeit aktualisieren, NVS speichern
|
||||
|
||||
- [ ] **5.4** Gratiszeit-Logik:
|
||||
- Countdown läuft ab Session-Start
|
||||
- Zeit wird erst nach Ablauf der Gratiszeit zur Gesamtzeit addiert
|
||||
- `getCountdownRemaining()` → Rückgabe verbleibende Sekunden (0 wenn abgelaufen)
|
||||
|
||||
- [ ] **5.5** Öffentliche Getter:
|
||||
- `getTotalMinutes()` → float
|
||||
- `getSessionSeconds()` → int (aktuelle Session ohne Gratiszeit)
|
||||
- `isActive()` → bool
|
||||
- `getLastSessionSeconds()` → int (für MQTT Publish)
|
||||
|
||||
- [ ] **5.6** Test: Laser-Signal simulieren (GPIO auf GND ziehen), Zeitmessung verifizieren
|
||||
|
||||
---
|
||||
|
||||
## Phase 6 – MQTT Client (`MqttClient`)
|
||||
|
||||
- [ ] **6.1** `include/mqtt_client.h` + `src/mqtt_client.cpp` erstellen
|
||||
Wrapper um `PubSubClient`
|
||||
|
||||
- [ ] **6.2** Verbindungsaufbau mit Credentials aus `Settings`
|
||||
|
||||
- [ ] **6.3** Publish-Methode `publishSession(sessionSec, totalMin, gratisSec)`:
|
||||
- Topic: `lasercutter/session`
|
||||
- JSON mit `session_s`, `total_min`, `gratiszeit_s`, `ts`
|
||||
- Wird von `LaserTracker` beim Session-Ende aufgerufen
|
||||
|
||||
- [ ] **6.4** Publish-Methode `publishStatus(totalMin, ipStr, uptimeSec)`:
|
||||
- Topic: `lasercutter/status`
|
||||
- Heartbeat alle 60 Sekunden
|
||||
- QoS 0
|
||||
|
||||
- [ ] **6.5** Subscribe auf `lasercutter/reset`:
|
||||
- Payload `"1"` oder `{"action":"reset"}` → `LaserTracker::resetTotal()`
|
||||
- QoS 1
|
||||
|
||||
- [ ] **6.6** Reconnect-Logik (Non-blocking, max. alle 10 Sekunden versuchen)
|
||||
|
||||
- [ ] **6.7** Test: Session-Daten mit MQTT Explorer empfangen, Reset auslösen und verifizieren
|
||||
|
||||
---
|
||||
|
||||
## Phase 7 – Webinterface (`WebServer`)
|
||||
|
||||
- [ ] **7.1** `include/web_server.h` + `src/web_server.cpp` erstellen
|
||||
Basierend auf `ESPAsyncWebServer`
|
||||
|
||||
- [ ] **7.2** Route `GET /` – Statusseite (HTML):
|
||||
- Aktuelle Laserzeit (Minuten)
|
||||
- Letzter Session-Wert (Sekunden)
|
||||
- WLAN-Status, MQTT-Status
|
||||
- IP-Adresse
|
||||
|
||||
- [ ] **7.3** Route `GET /config` – Konfigurationsformular (HTML):
|
||||
- MQTT Broker IP, Port, User, Passwort
|
||||
- Gratiszeit (Slider 0–120 s)
|
||||
- Signal-Polarität (Radio-Button)
|
||||
|
||||
- [ ] **7.4** Route `POST /config` – Konfiguration speichern (via `Settings`, NVS)
|
||||
|
||||
- [ ] **7.5** Route `POST /reset` – Laserzeit zurücksetzen (Button auf Statusseite)
|
||||
|
||||
- [ ] **7.6** ElegantOTA unter `/update` einbinden
|
||||
|
||||
- [ ] **7.7** Test: Alle Seiten im Browser aufrufen, Konfiguration ändern und nach Neustart prüfen
|
||||
|
||||
---
|
||||
|
||||
## Phase 8 – Integration & Hauptprogramm (`main.cpp`)
|
||||
|
||||
- [ ] **8.1** `main.cpp` aufräumen und alle Module initialisieren:
|
||||
- Reihenfolge: `Settings` → `DisplayManager` → `WiFiManager` → `MqttClient` → `WebServer` → `LaserTracker`
|
||||
|
||||
- [ ] **8.2** `loop()` implementieren:
|
||||
- `displayManager.update()`
|
||||
- `laserTracker.update()` (GPIO-Polling + Session-Logik)
|
||||
- `mqttClient.loop()` (Reconnect + Heartbeat)
|
||||
- `displayManager.showLaserTime(laserTracker.getTotalMinutes())`
|
||||
- `displayManager.showCountdown(...)` oder `showIdle()` oder `showError(...)`
|
||||
|
||||
- [ ] **8.3** Callback von `LaserTracker` → `MqttClient.publishSession(...)` verdrahten
|
||||
|
||||
- [ ] **8.4** WLAN/MQTT-Fehlerzustand auf Display spiegeln
|
||||
|
||||
- [ ] **8.5** Watchdog Timer aktivieren (ESP32 WDT, 30 s)
|
||||
|
||||
---
|
||||
|
||||
## Phase 9 – OTA & Stabilisierung
|
||||
|
||||
- [ ] **9.1** ElegantOTA in `main.cpp` initialisieren und in `loop()` einbinden
|
||||
|
||||
- [ ] **9.2** Serielles Logging vereinheitlichen (Log-Level: INFO / DEBUG per `#define`)
|
||||
|
||||
- [ ] **9.3** Langzeit-Test: 24h Betrieb, Speicherleck-Check via Serial Monitor (`ESP.getFreeHeap()`)
|
||||
|
||||
- [ ] **9.4** Edge-Cases testen:
|
||||
- Laser aktiv, WLAN wird getrennt → Display läuft weiter
|
||||
- MQTT-Broker nicht erreichbar → Reconnect
|
||||
- Reset während aktiver Session
|
||||
- Neustart nach akkumulierter Zeit → Zeit korrekt aus NVS geladen
|
||||
|
||||
---
|
||||
|
||||
## Phase 10 – Dokumentation & Abschluss
|
||||
|
||||
- [ ] **10.1** README.md finalisieren (Screenshots, Schaltplan-Skizze ggf. ergänzen)
|
||||
|
||||
- [ ] **10.2** `platformio.ini` mit finalen Library-Versionen dokumentieren
|
||||
|
||||
- [ ] **10.3** MQTT-Topics in README.md validieren (mit realem Broker testen)
|
||||
|
||||
- [ ] **10.4** Optionaler Schaltplan (Fritzing / ASCII) für Laser-Signal-Anschluss (Optokoppler)
|
||||
|
||||
---
|
||||
|
||||
## Abhängigkeitsdiagramm (Reihenfolge)
|
||||
|
||||
```
|
||||
Phase 1 (Setup)
|
||||
└── Phase 2 (NVS/Settings)
|
||||
├── Phase 3 (WiFi)
|
||||
├── Phase 4 (Display)
|
||||
└── Phase 5 (LaserTracker)
|
||||
└── Phase 6 (MQTT)
|
||||
└── Phase 7 (Webinterface)
|
||||
└── Phase 8 (Integration)
|
||||
└── Phase 9 (OTA/Stabilisierung)
|
||||
└── Phase 10 (Doku)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Verwendete MQTT-Topics (Überblick)
|
||||
|
||||
| Topic | Richtung | QoS | Auslöser |
|
||||
|------------------------|-----------|-----|-------------------------------|
|
||||
| `lasercutter/session` | Publish | 1 | Session-Ende (Laser inaktiv) |
|
||||
| `lasercutter/status` | Publish | 0 | Heartbeat alle 60 s |
|
||||
| `lasercutter/reset` | Subscribe | 1 | Externer Reset-Befehl |
|
||||
|
||||
---
|
||||
|
||||
*Erstellt: 22. Februar 2026*
|
||||
228
README.md
228
README.md
|
|
@ -1,4 +1,230 @@
|
|||
# Laser Cutter Dot Matrix Display und MQTT Client
|
||||
|
||||
Dieses Projekt ist ein Dotmatrix-Display und MQTT-Client für einen Laser Cutter. Es ist darauf ausgelegt, die aktive Lasercutter Zeit auf einem Dotmatrix-Display anzuzeigen und Nachrichten über die aktive Laserzeit an einen MQTT-Broker zu senden und Nachrichten zum Zurücksetzen der Lasercutter-Zeit zu empfangen.
|
||||
Dieses Projekt implementiert einen ESP32-basierten MQTT-Client mit Dot-Matrix-Display für einen Laser Cutter. Es misst und visualisiert die aktive Laserzeit, sendet Session-Daten an einen MQTT-Broker und bietet ein Browser-Webinterface zur Konfiguration und Steuerung.
|
||||
|
||||
---
|
||||
|
||||
## Inhaltsverzeichnis
|
||||
|
||||
1. [Hardware](#hardware)
|
||||
2. [Pinbelegung](#pinbelegung)
|
||||
3. [Display](#display)
|
||||
4. [Laser-Signaldetektion](#laser-signaldetektion)
|
||||
5. [Zeit-Tracking & Gratiszeit](#zeit-tracking--gratiszeit)
|
||||
6. [MQTT Client](#mqtt-client)
|
||||
7. [Webinterface](#webinterface)
|
||||
8. [Konfiguration & Persistenz](#konfiguration--persistenz)
|
||||
9. [WiFi-Setup (WiFiManager)](#wifi-setup-wifimanager)
|
||||
10. [OTA Firmware-Update](#ota-firmware-update)
|
||||
11. [Fehlerverhalten](#fehlerverhalten)
|
||||
12. [Bibliotheken](#bibliotheken)
|
||||
13. [Build & Flash](#build--flash)
|
||||
|
||||
---
|
||||
|
||||
## Hardware
|
||||
|
||||
| Komponente | Modell / Beschreibung |
|
||||
|-------------------------|----------------------------------------------------|
|
||||
| Mikrocontroller | AZ-Delivery ESP32 DevKit V4 |
|
||||
| Dot-Matrix-Display | 8× GYMAX7219 Module (kompatibel zu MAX7219), 8×8 LEDs je Modul |
|
||||
| Display-Anordnung | 4 Module nebeneinander × 2 Reihen = 32×16 LEDs |
|
||||
| Display-Kaskadierung | Alle 8 Module in einer einzigen SPI-Kette |
|
||||
| Laser-Aktivsignal | Potentialfreier Ausgang des Laser Cutters (Optokoppler empfohlen) |
|
||||
| Optional | Shelly PM Mini G3 als externer Leistungsmesser (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) |
|
||||
|
||||
> Die Polarität des Laser-Signals (HIGH = aktiv oder LOW = aktiv) ist über das Webinterface konfigurierbar.
|
||||
|
||||
---
|
||||
|
||||
## 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:
|
||||
|
||||
| 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 `---` |
|
||||
|
||||
- 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.
|
||||
|
||||
---
|
||||
|
||||
## Laser-Signaldetektion
|
||||
|
||||
Der ESP32 überwacht einen **digitalen GPIO-Eingang** mit internem Pull-Up-Widerstand (`INPUT_PULLUP`).
|
||||
Der potentialfreie Ausgang des Laser Cutters (z.B. Schließer-Kontakt über Optokoppler) wird an diesen Pin angeschlossen.
|
||||
|
||||
- **Polarität konfigurierbar**: Im Webinterface einstellbar, ob `LOW` oder `HIGH` den aktiven Zustand bedeutet.
|
||||
- **Debounce**: Software-Entprellung, um Fehlmessungen bei Schaltflanken zu vermeiden.
|
||||
- Eine **Session** beginnt, wenn der Laser aktiv wird, und endet, wenn er inaktiv wird.
|
||||
|
||||
---
|
||||
|
||||
## Zeit-Tracking & Gratiszeit
|
||||
|
||||
- Die Laserzeit wird **akkumulativ** in Minuten gezählt und über Neustarts hinaus im **NVS (Non-Volatile Storage)** des ESP32 gespeichert.
|
||||
- **Gratiszeit**: Eine konfigurierbare Zeitspanne (0–120 Sekunden) am Anfang jeder Session wird **nicht** zur akkumulierten Zeit gezählt. Dies erlaubt kurze Testläufe zum Einstellen des Laser Cutters ohne Kosten.
|
||||
- Standard: 20 Sekunden
|
||||
- Einstellbar über das Webinterface
|
||||
- Die untere Display-Zeile zeigt während der Gratiszeit einen Countdown an.
|
||||
|
||||
---
|
||||
|
||||
## MQTT Client
|
||||
|
||||
Der ESP32 verbindet sich mit einem MQTT-Broker (konfigurierbar über Webinterface).
|
||||
|
||||
### Topics
|
||||
|
||||
| Richtung | Topic | Format | Beschreibung |
|
||||
|------------|---------------------------|---------|-------------------------------------------|
|
||||
| Publish | `lasercutter/session` | JSON | Wird beim **Ende einer Session** gesendet |
|
||||
| Subscribe | `lasercutter/reset` | `"1"` | Setzt die akkumulierte Laserzeit auf 0 |
|
||||
| Publish | `lasercutter/status` | JSON | Heartbeat alle 60 Sekunden (online/offline)|
|
||||
|
||||
### JSON-Format `lasercutter/session`
|
||||
```json
|
||||
{
|
||||
"session_s": 125,
|
||||
"total_min": 42.5,
|
||||
"gratiszeit_s": 20,
|
||||
"ts": "2026-02-22T10:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### JSON-Format `lasercutter/status`
|
||||
```json
|
||||
{
|
||||
"online": true,
|
||||
"total_min": 42.5,
|
||||
"ip": "192.168.1.100",
|
||||
"uptime_s": 3600
|
||||
}
|
||||
```
|
||||
|
||||
### Verhalten
|
||||
- Reconnect im Hintergrund, falls MQTT-Broker nicht erreichbar.
|
||||
- Bei aktivem Laser läuft die Zeitmessung unabhängig vom MQTT-Status weiter.
|
||||
- QoS 1 für Session- und Reset-Topics.
|
||||
|
||||
---
|
||||
|
||||
## Webinterface
|
||||
|
||||
Das Webinterface ist über die IP-Adresse des ESP32 im Browser erreichbar.
|
||||
|
||||
| Seite | URL | Funktion |
|
||||
|------------------|-------------|--------------------------------------------------------------|
|
||||
| Status | `/` | Aktuelle Laserzeit, letzter Session-Wert, Systemstatus |
|
||||
| Konfiguration | `/config` | MQTT-Broker (IP, Port, User, Passwort), Gratiszeit, Polarität |
|
||||
| Reset | `/reset` | Setzt akkumulierte Laserzeit zurück |
|
||||
| OTA Update | `/update` | Firmware-Update über Browser |
|
||||
|
||||
---
|
||||
|
||||
## Konfiguration & Persistenz
|
||||
|
||||
Alle Einstellungen werden im **NVS (Non-Volatile Storage)** des ESP32 gespeichert und sind über das Webinterface änderbar:
|
||||
|
||||
| Einstellung | Standard | Beschreibung |
|
||||
|-----------------------|---------------|-----------------------------------------|
|
||||
| MQTT Broker IP | `192.168.1.1` | IP-Adresse des MQTT-Brokers |
|
||||
| MQTT Port | `1883` | Port des MQTT-Brokers |
|
||||
| MQTT User | *(leer)* | MQTT Benutzername (optional) |
|
||||
| MQTT Passwort | *(leer)* | MQTT Passwort (optional) |
|
||||
| Gratiszeit | `20` | Sekunden, 0–120 |
|
||||
| Signal-Polarität | `LOW_ACTIVE` | `LOW_ACTIVE` oder `HIGH_ACTIVE` |
|
||||
| Akkumulierte Zeit | `0.0` | Gespeicherte Laserzeit in Minuten (NVS) |
|
||||
|
||||
---
|
||||
|
||||
## WiFi-Setup (WiFiManager)
|
||||
|
||||
Beim ersten Start (oder wenn keine gespeicherten WLAN-Daten vorhanden) öffnet das Gerät einen **Access Point** mit Captive Portal:
|
||||
|
||||
- SSID: `LaserCutter-Setup`
|
||||
- Im Browser öffnet sich automatisch die Konfigurationsseite.
|
||||
- WLAN-Credentials werden nach erfolgreicher Verbindung im NVS gespeichert.
|
||||
|
||||
---
|
||||
|
||||
## OTA Firmware-Update
|
||||
|
||||
Firmware-Updates können kabellos über die Weboberfläche unter `/update` eingespielt werden (ElegantOTA).
|
||||
|
||||
---
|
||||
|
||||
## Fehlerverhalten
|
||||
|
||||
| Zustand | Display-Anzeige (untere Zeile, Modul 1) | Verhalten |
|
||||
|----------------------|----------------------------------------|----------------------------------------|
|
||||
| WLAN getrennt | `WiFi ERR` | Zeiterfassung läuft weiter, Reconnect |
|
||||
| MQTT nicht erreichbar| `MQTT ERR` | Zeiterfassung läuft weiter, Reconnect |
|
||||
| WLAN + MQTT OK | Normaler Betrieb (Countdown/`---`) | – |
|
||||
|
||||
---
|
||||
|
||||
## Bibliotheken
|
||||
|
||||
| Bibliothek | Zweck |
|
||||
|-----------------------|--------------------------------------|
|
||||
| `MD_Parola` | Dot-Matrix-Display Textausgabe |
|
||||
| `MD_MAX72XX` | Treiber für MAX7219/GYMAX7219 |
|
||||
| `PubSubClient` | MQTT Client |
|
||||
| `WiFiManager` | WiFi Captive Portal |
|
||||
| `ESPAsyncWebServer` | Asynchroner Webserver |
|
||||
| `AsyncTCP` | TCP-Basis für ESPAsyncWebServer |
|
||||
| `ArduinoJson` | JSON Serialisierung/Deserialisierung |
|
||||
| `Preferences` | NVS-Zugriff (built-in ESP32 Arduino) |
|
||||
| `ElegantOTA` | OTA-Update über Webinterface |
|
||||
|
||||
---
|
||||
|
||||
## Build & Flash
|
||||
|
||||
```bash
|
||||
# PlatformIO CLI
|
||||
pio run --target upload
|
||||
|
||||
# Serieller Monitor
|
||||
pio device monitor
|
||||
```
|
||||
|
||||
Ziel-Board: `az-delivery-devkit-v4` (ESP32), Upload-Port: `COM3`
|
||||
|
||||
---
|
||||
|
||||
## Projektstruktur
|
||||
|
||||
```
|
||||
MQTT-Display-LaserCutter/
|
||||
├── src/
|
||||
│ └── main.cpp # Hauptprogramm
|
||||
├── include/
|
||||
│ ├── config.h # Pin-Definitionen, Konstanten
|
||||
│ ├── display_manager.h # Display-Logik (MD_Parola)
|
||||
│ ├── laser_tracker.h # Signal-Detektion & Zeiterfassung
|
||||
│ ├── mqtt_client.h # MQTT-Wrapper (PubSubClient)
|
||||
│ ├── web_server.h # Webinterface (ESPAsyncWebServer)
|
||||
│ └── settings.h # NVS-Persistenz (Preferences)
|
||||
├── parser/
|
||||
│ └── shelly_parser.py # Separater Python-Parser für Shelly PM G3
|
||||
├── platformio.ini
|
||||
└── README.md
|
||||
```
|
||||
|
|
|
|||
81
parser/shelly_parser.py
Normal file
81
parser/shelly_parser.py
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
"""Shelly PM Mini G3 Parser - extracts power data from MQTT messages."""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
import structlog
|
||||
|
||||
from exceptions import ParserError
|
||||
|
||||
logger = structlog.get_logger()
|
||||
|
||||
|
||||
class ShellyParser:
|
||||
"""Parser for Shelly PM Mini G3 MQTT Messages."""
|
||||
|
||||
def parse_message(self, topic: str, payload: dict) -> dict | None:
|
||||
"""
|
||||
Parse Shelly MQTT message.
|
||||
|
||||
Args:
|
||||
topic: MQTT topic
|
||||
payload: Message payload (already JSON parsed)
|
||||
|
||||
Returns:
|
||||
Dict with parsed data or None
|
||||
"""
|
||||
try:
|
||||
# Only parse status messages
|
||||
if "/status/pm1:0" in topic:
|
||||
return self._parse_status_message(topic, payload)
|
||||
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.debug("shelly_parse_error", topic=topic, error=str(e))
|
||||
return None
|
||||
|
||||
def _parse_status_message(self, topic: str, data: dict) -> dict | None:
|
||||
"""
|
||||
Parse Shelly PM status message.
|
||||
Topic: shaperorigin/status/pm1:0
|
||||
|
||||
Payload format:
|
||||
{
|
||||
"id": 0,
|
||||
"voltage": 230.0,
|
||||
"current": 0.217,
|
||||
"apower": 50.0,
|
||||
"freq": 50.0,
|
||||
"aenergy": {"total": 12345.6},
|
||||
"temperature": {"tC": 35.2}
|
||||
}
|
||||
"""
|
||||
try:
|
||||
device_id = self._extract_device_id(topic)
|
||||
|
||||
result = {
|
||||
"message_type": "status",
|
||||
"device_id": device_id,
|
||||
"timestamp": datetime.utcnow().isoformat() + "Z",
|
||||
"voltage": data.get("voltage"),
|
||||
"current": data.get("current"),
|
||||
"apower": data.get("apower", 0), # Active Power in Watts
|
||||
"frequency": data.get("freq"),
|
||||
"total_energy": data.get("aenergy", {}).get("total"),
|
||||
"temperature": data.get("temperature", {}).get("tC"),
|
||||
}
|
||||
|
||||
logger.debug("shelly_parsed_status", device_id=device_id, apower=result["apower"])
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error("shelly_status_parse_error", error=str(e))
|
||||
return None
|
||||
|
||||
def _extract_device_id(self, topic: str) -> str:
|
||||
"""Extract device ID from topic path."""
|
||||
# Example: shaperorigin/status/pm1:0 -> shaperorigin
|
||||
parts = topic.split("/")
|
||||
if len(parts) > 0:
|
||||
return parts[0]
|
||||
return "unknown"
|
||||
Loading…
Reference in New Issue
Block a user