Unser Projekt auf der Ideenexpo 2024

Der Naturwissenschaften-Forderkurs hat im vergangenen Schuljahr mit Ky eine intelligente Bewässerungsanlage geplant, gebaut, programmiert und dann schließlich auch auf der IdeenExpo für insgesamt 430.000 Besucher ausgestellt. In diesem Artikel erzähle ich euch, womit wir unseren Server programmiert haben und gebe einen kleinen Einblick in die Funktionsweise.

Das Projekt im Allgemeinen

Insgesamt besteht das Projekt aus vier Komponenten. Einer Pumpe für die Bewässerung, ein Feuchtigkeitssensor, der die Feuchtigkeit im Boden bestimmt, einen Server, der die Pumpe und den Sensor steuert und eine Website, die es dem Nutzer ermöglicht das System zu konfigurieren und die Messwerte zu beobachten. Wenn ihr mehr zur Website erfahren wollt, könnt ihr euch gerne den Artikel von Florian anschauen.

Das Ziel war es, das Bewässern von Pflanzen so einfach wie nur möglich zu machen. Dafür haben wir drei verschiedene Modi implementiert, zwischen denen der Nutzer entscheiden kann. Der simpelste Modus ist der Manuelle, dieser gibt dem Nutzer die Möglichkeit einfach die Länge für die Bewässerung einzustellen und dann muss er die Bewässerung Manuell starten. Ein kleines bisschen komplexer ist der Intervall Modus, bei dem man neben der bewässerungslänge auch eintragen musste, wie viele Stunden/Tage zwischen den Bewässerungen liegen sollen. Das System hat dann automatisch in diesem Intervall die Pumpe angesteuert. Der Komplexeste Modus aber auch der angenehmste Modus für die Nutzer ist der Intelligente, bei diesem Modus kann der Nutzer sagen, wie hoch die Feuchtigkeit im Kasten minimal sein darf. Mit diesem Wert hat dann das System die Daten des Sensors ausgewertet und dann bei Bedarf schlau bewässert, bis die Feuchtigkeit wieder über dem minimalen Wert lag. Unser System war jedoch am Anfang natürlich bei weitem noch nicht so intelligent wie ich es euch eben beschrieben habe.

Die Wahl des „Tech Stack“

Zu aller Anfang haben wir die Idee gehabt unser System auf einem ESP-32 laufen zu lassen, da die Schule diese Microcomputer bereits besitzt und sie sich sehr gut mit dem Sensor verbinden lassen. Nachdem wir angefangen hatten die ersten Funktionen zu implementieren sind wir auf zwei Probleme gestoßen.

  • Der ESP-32 ist einfach nicht schnell genug, um eine Website und den Server zu hosten. Mit seinen knapp 4MB muss sowohl das Backend optimiert werden als auch das Frontend, wobei dies noch machbar wäre. Das nächste Problem wäre dann, dass der RAM zu klein und die CPU zu schwach ist, um eine Website für mehrere Menschen zu hosten.
  • Für das Backend ist es deutlich angenehmer die „Services“, die die einzelnen Schnittstellen managen in Nebenläufigkeiten auszulagern. Dadurch kann das Programm präziser auf eingehende Befehle reagieren. Der ESP-32, jedoch wird in einer Art C/C++ Programmiert, welches für Micro-Controller angepasst wurde, die nicht für solche Nebenläufigkeiten ausgelegt wurde.

Deswegen haben wir uns dazu entschieden ein Raspberry-Pi im weiteren Verlauf zu benutzen, da dieser viel schneller ist als ein ESP-32 und angenehmer in der Handhabung, weil man ein ganzes Betriebssystem drauf laufen lassen kann. Durch den Wechsel zu einem Raspberry-Pi war ich auch nun viel freier in der Wahl der Sprache fürs Backend. Letzten Endes habe ich mich für Kotlin als Sprache entschieden und dies aus mehreren Gründen:

  • Kotlin ist zu einem großen Teil eine OOP-Sprache, was bedeutet, dass man bestimmte Funktionalitäten in Klassen „verpacken“ kann und mehrere Primitive Daten einfach in Objekten zusammenfassen kann. Dies bietet eine sehr angenehme Struktur und erleichtert die Entwicklung ungemein.
  • Kotlin hat eine sehr einfache Koroutinen Implementation, was die „Services“ sehr einfach „parallel“ laufen lässt.
  • Es gibt eine recht gut Dokumentierte Library um auf die Pins des Raspberry-Pi via Kotlin / Java zuzugreifen, dadurch können wir dann auch alle Komponenten direkt über den Raspberry-Pi ansteuern.

Neben all diesen Vorteilen gab es aber auch ein paar Nachteile:

  • Kotlin ist eine JVM-Sprache, dies bedeutet es läuft wie Java auf der „Java-Virtual-Machine“. (Kotlin läuft tatsächlich als Java auf der „Java Virtual Machine“, da es auch zu Java-Classes kompiliert.) Die JVM ist nicht unbedingt als Ressourcen schonend bekannt, was uns zunächst etwas verunsichert hatte aber letzten Endes halb so schlimm war. Performanceprobleme gab es nicht.
  • Der Raspberry-Pi ist zwar leistungsstärker als der ESP, jedoch reicht die Leistung immer noch nicht aus um den Code direkt zu Kompilieren. Dank der JVM, die „Compile once, run everywhere“ sich als Motto gesetzt hat (böse Zungen würden sagen „Compile once, debug everywhere“), kann man nun das Programm auf einem externen Gerät schreiben und kompilieren. Die Binärdateien müssen dann nur noch auf dem Raspberry-Pi transferiert werden. Dies funktionierte auch erstaunlich schnell und problemlos, jedoch war an echtes „debugging“ gar nicht zu denken, was hin und wieder echte Kopfschmerzen bereitete.

Letzten Endes war es jedoch eine gute Entscheidung auf Kotlin und einen Raspberry-Pi zu setzen.

Aufbau der Software

Nun da wir unseren „Tech-Stack“ definiert hatten, ging es nun darum die Software rechtzeitig zur Messe fertig zu entwickeln. Im Folgenden möchte ich euch nun einen kleinen überblich geben, wie die Software aufgebaut ist und funktioniert. Damit der Folgende Paragraf einfacher zu verstehen ist habe ich ein kleines Diagramm mit den notwendigsten Beziehungen der einzelnen Bestandteile des Backends erstellt.

Zentral ist hier die main-Funktion wie in fast jeder Programmiersprache. Diese main-Funktion initialisiert dann alle weiteren „Services“ und Objekte.

Die drei „haupt-Services“ managen die Verbindung zum Client, den Sensor und die Wasserpumpe.

Der „WebSocketService“ verwaltet alle Clients und alle Messages. Ein Websocket kann man sich so ähnlich wie einen Gruppenchat vorstellen mit einer kleinen Eigenheit. Der Server kann an alle Clients senden. Die Clients können jedoch nur an den Server senden. Für unsere gesamte Kommunikation haben wir uns auf JSON-Strings geeinigt, die den Befehl, evtl. Daten und teilweise einen Key für Admin-befehle beinhalten. Die Responses folgten in den meisten Fällen auch diesem Schema.

Der „SensorService“ publiziert in einem 30-minütigen Intervall Messwerte, die dann im „DataStore“ zwischengespeichert werden und dem Client mitgeteilt werden, damit dieser den Wasseranteil im Behälter für den Nutzer hübsch aufbereiten kann.

Der „WaterPumpService“ lädt den aktuellen Modus (Manuell, Intervall oder Intelligent) alle 0,5 Sekunden und überprüft dann, ob die Wasserpumpe gestartet werden muss, weil z.B. ein Request eingegangen ist die Pumpe zu starten oder weil der Messwert den Minimalwert unterschritten hat. Sofern eine Notwendigkeit besteht zu bewässern wird der State auf „Watering“ gesetzt dem Client mitgeteilt und dann wird die Wasserpumpe für einen Zeitraum x (der vom Nutzer konfiguriert wurde) gestartet. Dadurch, dass der Client über den Status der Pumpe Bescheid weiß und der „WebSocketService“ „parallel“ läuft, kann er auch bei einem eventuellen Konfigurationsfehler die Bewässerung zwischendurch abbrechen. Nach dem Bewässern wird der State zurückgesetzt.  Zunächst auf einen „Cooldown“ State und dann wieder auf den Standard-State „Measuring“.

Wie funktioniert das aber nun, wenn der Nutzer etwas einstellen will?

Der Nutzer muss zunächst über die Website eine Einstellung ändern. Im Hintergrund wird dann eine Anfrage an den Server gesendet den Wert x zu ändern. Wenn der Nutzer die benötigten Berechtigungen hat, wird eine Nachricht an alle mit dem neuen Wert vom Server gesendet. Liegen die benötigten Berechtigungen nicht vor erhält der Client eine Fehlermeldung und setzt den Wert in seinem ui wieder zurück.

Der Server ist also kein riesiges Hexenwerk, was die Funktionen betrifft. Das Hauptaugenmerk lag letzten Endes auf einer zuverlässigen Kommunikation mit den einzelnen Komponenten und Clients. Aber trotzdem gibt’s auch ein paar Stellen, wo der Server definitiv noch ausbaufähig ist, so wurde bei der Entwicklung kein wert drauf gelegt den Server (was Instanzen und Funktionen angeht) skalierbar zu machen und sog. „Race conditions“ werden auch nicht behandelt. Diese fehlenden Implementationen waren jedoch während der Messe nicht zu spüren, sie wären jedoch essenziel, um dieses Projekt weiterzuführen.

Das könnte dich auch interessieren …