Behoben: Keine automatische Abschaltuk bei t<2 t jetzt limitiert auf 1-120 Dokumentation angelegt
342 lines
18 KiB
Markdown
342 lines
18 KiB
Markdown
# Gartenbewässerung – ESP32 Ventilsteuerung
|
||
|
||
Firmware für eine ESP32-basierte Steuerung motorisierter Gartenventile (Kugelhähne), bedienbar über eine Weboberfläche (WebSocket + REST) und MQTT. Es existieren zwei baugleiche Steuerungen im Einsatz:
|
||
|
||
| Gerät (Hostname) | Standort / Zonen | Belegte Ventilkanäle |
|
||
|------------------------|--------------------------------------------|-----------------------|
|
||
| `Gartenwasser_vorn` | Tröge + Garten vorn | Kanal 1 (Tröge), Kanal 6 (Garten vorn) |
|
||
| `Gartenwasser_hinten` | Hochbeete | Kanal 6 (Hochbeete) |
|
||
|
||
Beide laufen mit derselben Firmware, unterscheiden sich nur in der über den WiFiManager-Captive-Portal gespeicherten Konfiguration (Hostname, MQTT-Topic, `numValves`, …), die in den ESP32-NVS-Preferences abgelegt wird.
|
||
|
||
## Inhaltsverzeichnis
|
||
|
||
- [Systemüberblick](#systemüberblick)
|
||
- [Hardware](#hardware)
|
||
- [Ventil-Zustandsautomat](#ventil-zustandsautomat)
|
||
- [Software-Architektur](#software-architektur)
|
||
- [Kommunikationswege](#kommunikationswege)
|
||
- [Automatik-Modi](#automatik-modi)
|
||
- [Konfiguration & Erstinbetriebnahme](#konfiguration--erstinbetriebnahme)
|
||
- [Boot-Ablauf](#boot-ablauf)
|
||
- [Logging](#logging)
|
||
- [Firmware-Updates (OTA)](#firmware-updates-ota)
|
||
- [Projektstruktur](#projektstruktur)
|
||
- [Bekannte Einschränkungen / TODO](#bekannte-einschränkungen--todo)
|
||
|
||
## Systemüberblick
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
Browser[Web-Browser<br/>index.html] -- HTTP + WebSocket /ws --> ESP
|
||
App[MQTT-Client / Home-Automation] -- MQTT --> Broker[(MQTT-Broker<br/>nas.local)]
|
||
Broker <-- MQTT --> ESP
|
||
NTPsrv[(NTP-Server<br/>nas.local)] --> ESP
|
||
Syslog[(Syslog-Server<br/>NAS, UDP 13377)] <-- UDP --> ESP
|
||
Dev[Entwickler-PC] -- ArduinoOTA / espota --> ESP
|
||
|
||
subgraph ESP["ESP32 Steuerung"]
|
||
FW[Firmware<br/>main.cpp / MQTT.cpp]
|
||
end
|
||
|
||
ESP -- SPI, Latch GPIO4 --> SR[3x 74HC595<br/>Schieberegister, 24 Bit]
|
||
SR -- Transistor + bistabiles Relais<br/>G5RL-K1-E --> Valves[(Ventile Kanal 1-6<br/>12V motorisierte Kugelhähne)]
|
||
ESP -- WS2812, GPIO12 --> Ring[NeoPixel-Ring<br/>12 LEDs, Schaltschrank]
|
||
```
|
||
|
||
Die Firmware übernimmt drei Rollen gleichzeitig:
|
||
|
||
1. **Webserver** (`ESPAsyncWebServer`) liefert die Bedienoberfläche (`data/index.html`, `data/simple.css` aus SPIFFS) und eine REST-Schnittstelle.
|
||
2. **WebSocket-Server** (`/ws`) für Live-Status/Steuerung der Weboberfläche.
|
||
3. **MQTT-Client** (`PubSubClient`) für Steuerung/Status-Publishing aus der Hausautomation.
|
||
|
||
Zusätzlich: `WiFiManager` für WLAN-Konfiguration per Captive Portal, `ArduinoOTA` für Firmware-Updates über das Netzwerk, NTP für die Systemzeit (u. a. für die Bewässerungs-Logs), und ein einfacher UDP-Syslog-Client fürs zentrale Logging.
|
||
|
||
## Hardware
|
||
|
||
### Steuerplatine (`ventilsteuerung2.sch`, 2 Blätter)
|
||
|
||
Der vollständige Schaltplan liegt unter [`docs/hardware/ventilsteuerung2.pdf`](docs/hardware/ventilsteuerung2.pdf) (Rendering), [`docs/hardware/ventilsteuerung2.sch`](docs/hardware/ventilsteuerung2.sch) (Eagle-Schaltplan) und [`docs/hardware/ventilsteuerung2.brd`](docs/hardware/ventilsteuerung2.brd) (Eagle-Platinenlayout). Die folgende Beschreibung basiert auf dem Schaltplan und der Firmware.
|
||
|
||
**Sheet 1 – Steuerlogik:**
|
||
|
||
- **MCU:** ESP32 Dev-Board (im Schaltplan auf einem Arduino-Nano-kompatiblen Sockel). Die Firmware ist für `esp32dev` (Espressif32-Plattform, Arduino-Framework) gebaut.
|
||
- **Ausgangserweiterung:** 2× 74HC595-Schieberegister in Reihe, per Hardware-SPI angesprochen (`SpiShiftRegisterChain`, Latch-Pin GPIO4). Zusammen 24 Bit, wovon 21 genutzt werden.
|
||
- **7 identische Relais-Treiberkanäle** (Kanal 0–6), je bestehend aus:
|
||
- 1× Transistor-Treiberstufe für die **SET-Spule** (oberer Block im Schaltplan)
|
||
- 1× Transistor-Treiberstufe für die **RESET-Spule** (unterer Block im Schaltplan)
|
||
- 1× **bistabiles Relais Omron G5RL-K1-E** (2-Spulen-Stromstoßrelais, 12V) mit Freilaufdiode (Schottky, BAT85) je Spule zum Schutz der Treibertransistoren
|
||
- 1× Status-LED je Kanal (direkt am Schieberegister-Ausgang)
|
||
- 3-poliger Steckverbinder zur Ventilverkabelung
|
||
- Da das Relais bistabil ist, genügt ein kurzer Steuerimpuls, um die Spulenposition dauerhaft (ohne Haltestrom) zu wechseln — daher `KEEP_V(x)` in der Firmware, das beide Spulen wieder stromlos schaltet, nachdem ein Impuls gesetzt wurde.
|
||
|
||
**Sheet 2 – Spannungsversorgung:**
|
||
|
||
- Eingangsklemme für die 12V-Versorgung mit Verpolungs-/Filterschaltung (Drossel + Eingangskondensatoren).
|
||
- Schaltregler erzeugt eine **5V-Schiene** (Logik/Relais-Treiber) und eine **3,3V-Schiene** (ESP32) aus der 12V-Eingangsspannung.
|
||
|
||
### Bit-Belegung des Schieberegisters (`glblData.h`)
|
||
|
||
Kanal-Index `i` läuft von 0–6 (7 Kanäle = 3 Byte = 24 Bit, davon 21 genutzt):
|
||
|
||
| Funktion | Bit-Offset | Bits (Kanal 0–6) |
|
||
|-------------------|-----------:|-------------------|
|
||
| SET-Spule | `i` | 0–6 |
|
||
| RESET-Spule | `i + 8` | 8–14 |
|
||
| Status-LED | `i + 16` | 16–22 |
|
||
|
||
**Kanal 0 ist kein Gartenventil**, sondern schaltet die gemeinsame 12V-Motorspannung für alle Ventile ein/aus (`SET_V(0)` / `RST_V(0)` / `KEEP_V(0)` in `main.cpp`). Kanäle 1–6 sind die eigentlichen Ventile: Ihr SET/RESET-Impuls wählt die Laufrichtung (auf/zu) des jeweiligen Ventilaktors, bevor die gemeinsame Motorspannung (Kanal 0) für die Dauer der Ventillaufzeit angelegt wird.
|
||
|
||
```
|
||
#define RST_V(x) { RESET-Spule(x)=an; SET-Spule(x)=aus; LED(x)=aus; }
|
||
#define SET_V(x) { RESET-Spule(x)=aus; SET-Spule(x)=an; LED(x)=an; }
|
||
#define KEEP_V(x){ SET-Spule(x)=aus; RESET-Spule(x)=aus; } // beide Spulen stromlos
|
||
```
|
||
|
||
`MOTOR_POWER_TIME` = 15 s (als 30 Ticks à 500 ms) ist die Laufzeit, für die die gemeinsame Motorspannung je Öffnungs-/Schließvorgang anliegt.
|
||
|
||
### NeoPixel-Ring
|
||
|
||
12× WS2812-LEDs an GPIO12, montiert am Schaltschrank. Zeigt die verbleibende Bewässerungsrestzeit des länger laufenden der beiden aktiven Timer (`glblData.timers[1]`/`timers[6]`) als umlaufenden Ring an (Minuten als blauer Fortschrittsring, Sekunden als grün/rot abklingender "Kometenschweif", Stunden als rote Segmente).
|
||
|
||
## Ventil-Zustandsautomat
|
||
|
||
Jedes Ventil `i` (1–6) durchläuft beim Öffnen/Schließen denselben zweistufigen Ablauf: zuerst Richtungsauswahl (Kanal `i`), danach gemeinsame Motorspannung (Kanal 0) für 15 s. `valveState()` in `main.cpp` wird alle 500 ms über einen `Ticker` aufgerufen.
|
||
|
||
```mermaid
|
||
stateDiagram-v2
|
||
[*] --> VALVE_CLOSED
|
||
|
||
VALVE_CLOSED --> VALVE_OPENING_COILPWR: setValveState[i]=OPEN\nSET_V(i)
|
||
VALVE_OPENING_COILPWR --> VALVE_OPENING_POWER: KEEP_V(i)
|
||
VALVE_OPENING_POWER --> VALVE_OPENING_POWER_COILPWR: SET_V(0)
|
||
VALVE_OPENING_POWER_COILPWR --> VALVE_OPEN_MOTORIZED: KEEP_V(0)\nv_powered=0
|
||
VALVE_OPEN_MOTORIZED --> VALVE_OPEN_COILPWR: 15s vergangen\nRST_V(0)
|
||
VALVE_OPEN_COILPWR --> VALVE_OPEN: KEEP_V(0)
|
||
|
||
VALVE_OPEN --> VALVE_CLOSING_COILPWR: setValveState[i]=CLOSE\nRST_V(i)
|
||
VALVE_CLOSING_COILPWR --> VALVE_CLOSING_POWER: KEEP_V(i)
|
||
VALVE_CLOSING_POWER --> VALVE_CLOSING_POWER_COILPWR: SET_V(0)
|
||
VALVE_CLOSING_POWER_COILPWR --> VALVE_CLOSED_MOTORIZED: KEEP_V(0)\nv_powered=0
|
||
VALVE_CLOSED_MOTORIZED --> VALVE_CLOSED_COILPWR: 15s vergangen\nRST_V(0)
|
||
VALVE_CLOSED_COILPWR --> VALVE_CLOSED: KEEP_V(0)
|
||
```
|
||
|
||
Nebenbei protokolliert die Firmware pro Ventil die letzten 5 Bewässerungsvorgänge (Startzeit + Dauer in 2er-Schritten, da der Timer-Tick alle 500 ms läuft) als Ringpuffer (`wateringLog`, `wateringLogDuration`, `logIndex`) für die Anzeige im Web-UI.
|
||
|
||
## Software-Architektur
|
||
|
||
| Datei | Verantwortung |
|
||
|--------------------|----------------|
|
||
| `main.cpp` | Setup/Loop, Ventil-Zustandsautomat (`valveState`), Relais-Initialisierung (`init_relays`/`closeValves_tick`), Automatik-Modi (`autoMode_tick`), NeoPixel-Ring (`updateTimerRing`) |
|
||
| `MQTT.h` / `.cpp` | Sammelklasse `MQTT` (erbt von `PubSubClient`, enthält zusätzlich einen `PubSubClient`-Member `psclient`) — bündelt WLAN (`WiFiManager`), Webserver (`ESPAsyncWebServer`), WebSocket, MQTT, OTA, NTP und UDP-Syslog |
|
||
| `glblData.h` / `.cpp` | Globaler Zustand (`dataset_t glblData`): Soll-/Ist-Zustand je Ventil, Timer, Automatikmodus, Watering-Log |
|
||
|
||
```mermaid
|
||
classDiagram
|
||
class MQTT {
|
||
+begin()
|
||
+loop()
|
||
+publish(msg)
|
||
+publish_sub(subtopic, msg)
|
||
-handleMqttMessage(topic, payload, length)
|
||
-handleWebSocket(...)
|
||
-handleWebSocketMessage(...)
|
||
-applyValveJson(doc) bool
|
||
-reconnect()
|
||
-saveConfigToFlash()
|
||
+psclient : PubSubClient
|
||
+wm : WiFiManager
|
||
+prefs : Preferences
|
||
}
|
||
class dataset_t {
|
||
+setValveState[7] : bool
|
||
+valveState[7] : valvestate_t
|
||
+timers[7] : uint32
|
||
+pauseTimers[7] : int32
|
||
+autoMode : automode_t
|
||
+statusChange : bool
|
||
+timerChange : bool
|
||
+wateringLog[7][5]
|
||
}
|
||
MQTT ..> dataset_t : liest/schreibt glblData (extern)
|
||
main ..> MQTT : connections.begin()/loop()
|
||
main ..> dataset_t : valveState(), autoMode_tick()
|
||
```
|
||
|
||
`glblData` ist eine globale, von `main.cpp` und `MQTT.cpp` gemeinsam genutzte Datenstruktur — der eigentliche "Shared State" zwischen dem Ventil-Zustandsautomaten (der die Hardware treibt) und den Kommunikationsschnittstellen (die Sollwerte setzen und Ist-Werte auslesen).
|
||
|
||
## Kommunikationswege
|
||
|
||
### WebSocket (`/ws`)
|
||
|
||
Beim Verbindungsaufbau sendet der Server sofort den aktuellen Status (`makeStatusJSON`) und die Timer (`makeTimerJSON`). Änderungen werden alle 1 s geprüft und bei Bedarf per Broadcast an alle Clients gesendet (`glblData.statusChange` / `glblData.timerChange`).
|
||
|
||
Eingehende Nachrichten (JSON) werden von `handleWebSocketMessage` verarbeitet:
|
||
|
||
```json
|
||
{ "valve": 6, "set": "open", "timer": 30 }
|
||
{ "valve": 6, "set": "close" }
|
||
{ "auto": "Hoch" }
|
||
```
|
||
|
||
Enthält die Nachricht kein gültiges `valve`-Feld, wird stattdessen `auto` ausgewertet (`"Hoch"`/`"Vorn"` → `AUTO_HOCH`, `"Trog"` → `AUTO_TROG`, alles andere → `AUTO_CLOSE`).
|
||
|
||
### Ventilnummern (`valve` / `N`)
|
||
|
||
Der numerische Ventilkanal in WebSocket-, REST- und MQTT-`/set`-Befehlen entspricht der physischen Kanalnummer am Schieberegister (siehe [Bit-Belegung](#hardware)). Aktuell sind pro Gerät nur folgende Kanäle tatsächlich verkabelt:
|
||
|
||
| Kanal-Nr. | Zone | `Gartenwasser_vorn` | `Gartenwasser_hinten` |
|
||
|---|---|---|---|
|
||
| 1 | Tröge | verkabelt | unbenutzt |
|
||
| 6 | Garten vorn / Hochbeete | verkabelt (Garten vorn) | verkabelt (Hochbeete) |
|
||
| 2–5 | — | unbenutzt (Hardware-Reserve) | unbenutzt (Hardware-Reserve) |
|
||
|
||
Kanal 0 ist kein ansteuerbares Ventil, sondern die gemeinsame Motorspannung (siehe [Hardware](#hardware)) und darf nicht als `valve`-Wert verwendet werden.
|
||
|
||
### REST
|
||
|
||
| Endpunkt | Beschreibung |
|
||
|-------------------------|--------------|
|
||
| `GET /` | Liefert `index.html` (mit Platzhaltern `%HOSTNAME%`, `%NAME_VALVE1%`, `%NAME_VALVE2%`) |
|
||
| `GET /simple.css` | Stylesheet |
|
||
| `GET /data` | Aktueller Status als JSON (wie `makeStatusJSON`) |
|
||
| `GET /setValve?valve=N&set=open\|close&timer=M` | Ventil N öffnen/schließen, optional mit Laufzeit in Minuten |
|
||
|
||
### MQTT
|
||
|
||
Basis-Topic ist konfigurierbar (`mqtt_topic`, z. B. `Gartenwasser/vorn`).
|
||
|
||
**Publish (alle 1s bei Änderung, bzw. beim Verbindungsaufbau):**
|
||
|
||
| Topic (relativ zu `mqtt_topic`) | Inhalt |
|
||
|---|---|
|
||
| `/status` | JSON mit Ist-/Soll-Ventilzuständen |
|
||
| `/hostname`, `/IP`, `/numValves`, `/defaultTimerMins`, `/NTP-Server` | Gerätestatus |
|
||
| `/Timers` | JSON mit Restlaufzeiten je Ventil + Automatikmodus |
|
||
|
||
**Subscribe:**
|
||
|
||
| Topic | Payload | Wirkung |
|
||
|---|---|---|
|
||
| `/auto` | `"Hoch"` / `"Vorn"` / `"Trog"` / sonst | setzt `autoMode` (`AUTO_HOCH` / `AUTO_TROG` / `AUTO_CLOSE`), case-insensitive |
|
||
| `/set` | `{"valve":N,"set":"open"\|"close","timer":M}` | steuert ein einzelnes Ventil, analog zur WebSocket-/REST-API |
|
||
|
||
`timer` (Minuten) ist bei `"set":"open"` optional: Fehlt das Feld oder liegt der Wert außerhalb 1–120, greift als Fallback `defaultTimerMins` (Gerätekonfiguration) bzw. wird der Wert auf 1–120 geklemmt — ein Ventil wird also nie ohne Restlaufzeit geöffnet (kein unbegrenztes Offenbleiben). Dieselbe Absicherung gilt auch für den REST-Endpunkt `/setValve`.
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant U as MQTT-Client (Hausautomation)
|
||
participant B as MQTT-Broker (nas.local)
|
||
participant ESP as ESP32 Firmware
|
||
participant HW as Ventil-Hardware
|
||
|
||
U->>B: publish Gartenwasser/vorn/set<br/>{"valve":6,"set":"open","timer":30}
|
||
B->>ESP: handleMqttMessage(topic, payload)
|
||
ESP->>ESP: applyValveJson()<br/>setValveState[6]=OPEN, timers[6]=1800
|
||
loop alle 500ms
|
||
ESP->>HW: Zustandsautomat treibt Relais
|
||
end
|
||
ESP-->>B: publish .../status, .../Timers
|
||
ESP-->>U: WebSocket-Broadcast an Weboberfläche
|
||
```
|
||
|
||
## Automatik-Modi
|
||
|
||
Zwei vordefinierte Bewässerungsprogramme, gesteuert über `glblData.autoMode` und einen minütlichen `Ticker` (`autoMode_tick`):
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant T as autoModeTicker (60s-Takt)
|
||
participant V6 as Ventil 6 (Hoch/Vorn)
|
||
participant V1 as Ventil 1 (Tröge)
|
||
|
||
Note over T,V6: AUTO_HOCH
|
||
T->>V6: t=0 min: OPEN, 30 min
|
||
T->>V6: t=30 min: CLOSE, Modus beendet
|
||
|
||
Note over T,V1: AUTO_TROG
|
||
T->>V1: t=0 min: OPEN, 5 min
|
||
T->>V1: t=5 min: CLOSE, Pause 10 min
|
||
T->>V1: t=15 min: OPEN, 3 min
|
||
T->>V1: t=18 min: CLOSE, Modus beendet
|
||
```
|
||
|
||
`AUTO_TROG` fährt also einen Quell-/Einsickerzyklus: kurz befüllen, ausgiebig ziehen lassen, kurz nachlegen.
|
||
|
||
## Konfiguration & Erstinbetriebnahme
|
||
|
||
Beim ersten Start (bzw. wenn kein WLAN erreichbar ist) öffnet `WiFiManager` einen Access-Point **`AutoConnectAP`** mit Konfigurationsportal. Dort werden neben den WLAN-Zugangsdaten folgende Parameter abgefragt und in den ESP32-NVS-Preferences (Namespace `settings`) gespeichert:
|
||
|
||
| Parameter (Portal) | Preferences-Key | Bedeutung | Default |
|
||
|---|---|---|---|
|
||
| Hostname | `HOST` | Gerätename (WLAN, mDNS, OTA) | `Gartenwasser_vorn` |
|
||
| mqtt server | `MQTT_SERVER` | MQTT-Broker-Adresse | `nas.local` |
|
||
| mqtt port | `MQTT_PORT` | MQTT-Broker-Port | `1883` |
|
||
| topic | `MQTT_TOPIC` | Basis-MQTT-Topic | `Gartenwasser/vorn` |
|
||
| Default ON Time (min) | `DEF_TIME_MIN` | Standard-Bewässerungsdauer | `60` |
|
||
| Available Valves | `NUM_VALVES` | Anzahl im Web-UI angezeigter Zonen (1 oder 2) | `1` |
|
||
| NTP Server | `NTP_SERVER` | Zeitserver | `nas.local` |
|
||
|
||
`numValves` steuert **nur die Web-UI-Beschriftung/Sichtbarkeit** ("Hochbeete" bei 1 Zone, "Garten Vorn"/"Tröge" bei 2 Zonen) — die Hardware unterstützt unabhängig davon bis zu 6 Ventilkanäle.
|
||
|
||
Nach dem Speichern trennt die Firmware die MQTT-Verbindung, um die neuen Einstellungen zu übernehmen (`psclient.disconnect()`); WLAN-Hostname, mDNS und OTA-Hostname werden aktuell nur beim Boot gesetzt — eine Hostname-Änderung wird daher erst nach einem Neustart vollständig wirksam.
|
||
|
||
Solange das Gerät verbunden ist, bleibt das Konfigurationsportal zusätzlich unter Port **8080** erreichbar (die eigentliche Anwendung belegt Port 80).
|
||
|
||
## Boot-Ablauf
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[Start] --> B[Preferences laden<br/>SPIFFS mounten]
|
||
B --> C[WiFiManager: autoConnect]
|
||
C -->|verbunden| D[NTP-Zeit synchronisieren]
|
||
C -->|nicht verbunden, 1. Versuch| E[5s warten, erneut versuchen]
|
||
E -->|verbunden| D
|
||
E -->|weiterhin offline| F[Nur Config-Portal starten<br/>kein Anwendungsbetrieb]
|
||
D --> G[ArduinoOTA einrichten]
|
||
G --> H[Webserver-Routen registrieren<br/>/, /simple.css, /data, /setValve]
|
||
H --> I[mDNS starten: hostname.local]
|
||
I --> J[WebSocket-Handler registrieren]
|
||
J --> K[MQTT verbinden + Topics abonnieren]
|
||
K --> L[Relais initialisieren<br/>closeValves_tick 0..4]
|
||
L --> M[Ventil-Zustandsautomat startet<br/>500ms Ticker]
|
||
```
|
||
|
||
Die Relais-Initialisierung (`init_relays`/`closeValves_tick`) fährt beim Start einmalig alle Ausgänge in einen definierten (stromlosen) Zustand, bevor der reguläre Zustandsautomat übernimmt.
|
||
|
||
## Logging
|
||
|
||
`MQTT::remoteLog*` sendet Log-Nachrichten im BSD-Syslog-ähnlichen Format per UDP an einen zentralen Log-Server im lokalen Netz (aktuell `192.168.179.174:13377`, bereitgestellt vom NAS). Genutzt für Ventilschaltungen, Automatik-Moduswechsel und Verbindungsstatus. Es gibt aktuell keine Bestätigung/Fehlerbehandlung, falls der Log-Server nicht erreichbar ist (Fire-and-forget per UDP).
|
||
|
||
## Firmware-Updates (OTA)
|
||
|
||
`upload_protocol = espota` in `platformio.ini` lädt neue Firmware über WLAN auf das per `upload_port` (`<hostname>.local`) adressierte Gerät hoch — kein serieller Zugriff nötig, sofern das Gerät bereits im WLAN und per mDNS erreichbar ist. Beide Geräte müssen daher beim Flashen über den jeweils passenden Hostnamen adressiert werden (`upload_port` in `platformio.ini` anpassen, aktuell `gartenwasser_hinten.local`).
|
||
|
||
## Projektstruktur
|
||
|
||
```
|
||
src/
|
||
main.cpp Setup/Loop, Ventil-Zustandsautomat, Automatik-Modi, LED-Ring
|
||
MQTT.h / .cpp WLAN/Web/WebSocket/MQTT/OTA-Sammelklasse
|
||
glblData.h / .cpp Globaler Anwendungszustand
|
||
data/
|
||
index.html Weboberfläche (SPIFFS)
|
||
simple.css Stylesheet (SPIFFS)
|
||
lib/
|
||
SpiShiftRegisterChain Ansteuerung der 74HC595-Kette
|
||
PubSubClient MQTT-Client
|
||
WiFiManager WLAN-Provisioning / Captive Portal
|
||
ESPAsyncWebServer, AsyncTCP Webserver + WebSocket
|
||
ArduinoJson JSON (de)serialisierung
|
||
Adafruit_NeoPixel WS2812-Ansteuerung (Statusring)
|
||
platformio.ini Board esp32dev, Framework Arduino, espressif32@6.10.0
|
||
```
|
||
|
||
## Bekannte Einschränkungen / TODO
|
||
|
||
- Hostname-/mDNS-/OTA-Namensänderungen über das Konfigurationsportal werden erst nach einem manuellen Neustart vollständig wirksam (werden nur in `begin()` gesetzt).
|
||
- Laut Code-Kommentar in `MQTT::begin()` ist die Art, wie der Webserver bei Bedarf neu aufgesetzt wird, "not the safest way" — mögliche Abstürze durch bereits registrierte Callbacks auf dem geteilten `AsyncWebServer`-Pointer sind nicht ausgeschlossen.
|
||
- Der Syslog-Ziel-Host (`192.168.179.174:13377`) ist aktuell fest im Code hinterlegt statt konfigurierbar.
|
||
- Die Weboberfläche (`index.html`) geht fest von Ventil 1 ("Tröge") und Ventil 6 ("Garten vorn"/"Hochbeete") aus; eine allgemeinere Zuordnung (z. B. für mehr als 2 sichtbare Zonen) ist nicht vorgesehen.
|