Wie es funktioniert

Architektur

Vier Bauweisen standen zur Wahl. Gewählt wurde die, bei der ein Ausfall von Server oder Netzwerk die Pflanzen nicht gefährdet.

Auf dieser Seite

Die Wahl der Bauweise

Variante Bewertung Ergebnis
Raspberry Pi, fest verkabelt Wie beim ersten Beet. SD-Karten sterben, kein Analogeingang, kein Echtzeitverhalten, Linux im feuchten Kasten. verworfen
Reine Netzwerkbrücke, Logik nur auf dem Server Fällt Server oder WLAN aus, bleibt die Heizung an oder die Pflanzen erfrieren. verworfen
Viele einzelne WLAN-Geräte Jedes Gerät braucht Zugangsdaten, IP-Adresse und eigenen Empfang. Unübersichtlich. verworfen
Ein Hub mit Modulbus, Server für Oberfläche und Planung Ein Netzwerkgerät, beliebig viele Module, autonomer Betrieb bei Ausfällen. gewählt

Die erste Variante ist keine Theorie. So war das Beet von 2019 gebaut, und die Nachteile stammen aus dem Betrieb. Mehr dazu unter Geschichte.

Bausteine des Servers

Alle Dienste laufen als Container mit Docker Compose.

Baustein Wahl Grund
TLS-Endpunkt nginx im Container frontend Alles, was über ein Netz geht, ist verschlüsselt: HTTPS, MQTT über TLS, Push. Eigene CA der Installation.
Broker Mosquitto Klein, stabil, ausreichend. Jeder Hub hat eigene Zugangsdaten und darf nur seine eigenen Topics lesen und schreiben.
Backend TypeScript, Node, Fastify Eine Sprache für Backend und Oberfläche.
Datenbank PostgreSQL mit TimescaleDB Stammdaten und Messreihen in einer Datenbank. Alte Daten werden automatisch verdichtet.
Oberfläche React, als Web-App installierbar Live-Daten per WebSocket, auch auf dem Handy.
Benachrichtigung ntfy, selbst gehostet Push aufs Handy ohne Fremddienst.
Zugriff von außen WireGuard Keine Portfreigabe ins Internet.
Browser ─ HTTPS ─┐   (optional: eigener Proxy oder Caddy davor)
Hub ── HTTPS, ───┼── TLS-Endpunkt (nginx) ── Backend (Fastify)
       MQTT/TLS  │   8443, 8883, 8091        │  PostgreSQL + TimescaleDB
ntfy-App ─ HTTPS ┘          │                │  SMTP nur mit TLS, ntfy
                            └─ Mosquitto (nur im Docker-Netz)
                                │
             Hub (ESP32-S3) / Simulator, CA angeheftet
             │ CAN-Bus, 24 V
             Module (ESP32-C3 SuperMini)

Im Betrieb sind nach außen nur drei Ports offen, alle mit TLS: HTTPS (8443), MQTT über TLS (8883) und Push über HTTPS (8091). Oberfläche (8080) und ntfy (8090) ohne TLS hören nur auf 127.0.0.1, für einen Reverse-Proxy auf demselben Rechner. Datenbank, Broker (1883) und Backend sind nur innerhalb von Docker erreichbar. Klartext gibt es sonst nur auf dem CAN-Bus und in den offenen Einrichtungs-WLANs von Hub und Anzeige (WLAN).

Kein Dienst läuft als root

Jeder Container hat einen festen, unprivilegierten Benutzer, auch Datenbank und Broker von Anfang an. nginx nutzt das Image nginx-unprivileged und hört im Container auf Port 8080.

Geheimnisse

Passwörter für Datenbank und Broker und der Sitzungsschlüssel entstehen beim ersten Start von selbst in einem eigenen Volume. Es gibt keine Standardpasswörter. Die eigene CA und das Serverzertifikat legt der Dienst certs an und erneuert das Zertifikat vor dem Ablauf.

Datenmodell

Standort Hub Modul Kanal Sensor oder Aktor Zone z. B. „Kammer A“ Regler verknüpft Sensoren mit Aktoren Profil Sollwerte nach Zeit Ressource geteilt, z. B. der Tank Dazu: Regel · Alarm · Ereignisprotokoll
Datenmodell des Servers. Nichts darin weiß, dass es um Kräuter geht.

Ein Standort hat Hubs, Zonen und Ressourcen. Ein Hub hat Module, ein Modul hat Kanäle. Ein Kanal ist ein Sensor oder ein Aktor. Eine Zone gruppiert Kanäle, etwa „Kammer A“. Eine Ressource wird von mehreren Zonen geteilt, etwa der Tank. Jede Zone hat Regler und ein Profil.

Ein Terrarium ist ein weiterer Standort mit anderen Zonen und Profilen.

Ein gemeinsamer Vertrag

Das Repository ist ein Monorepo. Das Paket packages/protocol enthält den Vertrag: Topics, Nachrichten, Konfiguration, Modulkatalog, Anlagenbeschreibung, Layout und Regelgraph. Backend, Simulator und Oberfläche nutzen dasselbe Paket, damit sie nicht auseinanderlaufen.

Teil Ordner Aufgabe
Protokoll packages/protocol gemeinsamer Vertrag
Backend apps/backend Geräteverwaltung, Übernahme, Messwerte, Regeln, Alarme, Mail, Push, Benutzer, Firmware
Oberfläche apps/frontend React-App mit Live-Modell der Anlage
Simulator apps/simulator ersetzt den Hub samt Modulen
Firmware firmware C++-Kern für Hub, Module und Anzeigen

Aufbau der Firmware

Hub und Module teilen sich eine Codebasis in C++ mit PlatformIO. Der Kern ist von der Hardware getrennt und wird am Rechner getestet. Nur die unterste Schicht spricht echte Bausteine an.

  • Zeit kommt immer als Parameter. Der Kern fragt nie eine Uhr. So lassen sich Abläufe von Minuten in Millisekunden testen.
  • Kein Heap in den Regelschleifen, keine Exceptions, kein RTTI.
  • Konfiguration in zwei Arenen. Eine neue Konfiguration wird in die zweite Arena gelesen, geprüft und aufgebaut. Erst dann wird umgeschaltet. Eine ungültige Konfiguration ändert nichts.
  • Das Hub-eigene Modul läuft als zweiter Modulkern im Hub an der festen Busadresse 1. Für den Hub sieht es aus wie jedes andere Modul.
  • Ein Durchlauf je Sekunde: Regler, Vorrang, Wirkungsüberwachung, Versorgung. Danach gehen die Stellwerte an die Module.

Simulator und Firmware tun dasselbe

Das Verhalten von Hub und Modulen ist in einem Dokument festgelegt. Firmware (C++) und Simulator (TypeScript) setzen beide genau dieses Verhalten um. Weicht eines ab, ist das ein Fehler.

Der Preis dafür: Das Verhalten ist doppelt implementiert und kann auseinanderlaufen. Die Tests beider Seiten prüfen deshalb dieselben Fälle.

    Durchsucht alle Seiten. Die Suche läuft im Browser, nichts wird übertragen.