Der eigene Pool – wird smart…

So einen Pool zu haben ist eine tolle Sache, dennoch investiert man immer wieder Zeit, um das Optimum aus der Sache zu machen. Wann lohnt es sich die Heizung einzuschalten? Wann ist das kontraproduktiv. All das sind Entscheidungen, die man in ganz klare Regeln fassen kann. All das, was man in Regeln fassen kann, kann man auch automatisieren. Ich persönlich halte nicht viel von smarten Lösungen, die auf einer Cloud wie Google oder Amazon, … basieren. Leider gibt es aber kaum Smarthome-Lösungen, die nicht cloudbasiert sind. Sobald man nicht mehr nach Mainstreamprodukten sucht, sondern nach speziellen Lösungen (wasserfeste Temperatursensoren, die Steckdosen in Abhängigkeit zueinander schalten) wird es ohnehin mau, was die inzwischen so beliebten „Einstecken und glücklich sein“-Lösungen angeht. Also muss ich einen ganz anderen Ansatz fahren. Es gibt natürlich bereits fertige Poolsteuerungen. Die sind mir aber viel zu teuer und auch zu unflexibel. Ich möchte keinen 4-stelligen Betrag ausgeben, um dann zwar eine richtig gute Steuerung zu haben, die ich aber nicht so nutzen kann, wie ich es mir vorstelle. Daher habe ich mir eine eigene modulare Lösung auf einem Raspberry Pi basierend gebaut.

Was ist das Ziel?

  1. Ich möchte die Pumpe für die Solarheizung intelligent schalten können. D.h. die Pumpe soll einschalten, wenn der Pool zu kalt ist und das Wasser aus der Solaranlage wärmer ist als im Pool. Sie soll ausschalten, wenn der Pool die gewünschte Temperatur erreicht hat oder das Wasser in der Solaranlage nicht wärmer als der Pool ist.
  2. Werte des Pools anzeigen und überwachen. Für das Überwachen nutze ich ein Monitoring Tool „Check MK“, in das ich die Sensoren einbauen möchte. Für das Anzeigen möchte ich eine Webseite haben, die man aufrufen kann. In der ersten Planung sind folgende Werte vorgesehen:
    – Temperatur des Pools
    – Temperatur der Solarschnecken
    – Temperatur des Unterstands, in dem die Technik (Pumpen, Filter) steht
    – Temperatur der Umgebung (Außentemperatur)
    – PH Wert des Pools
    – TDS Wert des Pools

Wie beschrieben habe ich bereits ein Monitoring System, mit dem ich die Werte überwachen kann. Ich werde daher hier nur beschreiben, wie ich die Checks gebaut habe, die vom Check MK Agenten ausgelesen werden. Es gibt aber auch andere Variablen, die bei mir „speziell“ sind und hier den Rahmen sprengen. Ich versuche mich daher hier auf das Wesentliche zu konzentrieren und einen halbwegs simplen Nachbau zu ermöglichen.

Wer eine einfache Lösung will ohne viel Aufwand, sucht am besten gleich weiter. Meine Lösung ist auch nicht an einem Tag gebaut worden. Dinge die laut anderen Anleitungen gehen sollten, gingen bei mir nicht. Viele der verwendeten Bauteile sind schlecht beschrieben und ich habe damit keine Erfahrungen. Was bei mir funktioniert muss daher nicht zwangsläufig auch woanders funktionieren. Wer damit leben kann ggf. selber ein wenig zu basteln, um eine Lösung zu bekommen wird hier sicher eine gute Basis für ein modulares System finden. Der Vorteil hier: Alle Bauteile sind recht günstig, das System ist beliebig erweiterbar (oder schmälerbar) und lässt sich individuell anpassen. Sogar ein Bildschirm für die Werte am Pool ist denkbar.

Einkaufsliste:

Raspberry Pi Zero WH (Achtung! Das W steht für WiFi und das H für die aufgelötete GPIO-Steckerleiste. Der Pi Zero hat keine RJ45-Ethernetschnittstelle und ohne GPIO-Steckerleiste muss man löten)
Gehäuse für Raspberry Pi Zero (Aluminium Gehäuse kühlen den Raspberry meiner Erfahrung nach am besten)
5V 2,5A Micro USB Netzteil
16 GB Micro SD Karte (8 GB reichen auch, wer will darf auch größer, mit 16 GB hat man aber schon ausreichend Reserven)
Multibreadboard zur besseren Verwaltung der GPIO Pins am Raspberry
Temperatur Sensor DS18B20 Adapter Board Set (1 Sensor ist inkl.)
Temperatur Sensoren DS18B20 (es werden 4 zusätzliche Sensoren benötigt für Pool, Außentemperatur, Solarschnecke und Rücklauftemperatur von der Solarschnecke) Die Anzahl kann beliebig erweitert werden
Analog-Digital Wandler (für PH und TPM Sensor)
PH Sensor
TPM Sensor
ORP Sensor (Bestimmung des Chlorgehaltes)
PH Sensor Modul (zweckentfremded um den ORP Sensor anzuschließen)
Kabelabzweigkasten (zur Unterbringung der Technik)
RJ45-Patchpanel
„Telefonkabel“ für die Verlängerung der Sensorleitungen
RJ45-Stecker
BNC Verlängerungskabel (für PH Sensor)
Isolier Klebeband
Klebeklettband (zur Fixierung des Raspberry)
JST XH 2,54mm Stecker und Buchse (zur Verlängerung des TDS Sensors)
Schrumpfschlauch
Jumper Wire Cable
Bei mir hat es zwar im Haus geklappt, im Bereich der Pumpe wird das Funksignal allerdings so stark gestört, dass ich die Funksteckdosen nicht nutzen kann. Die Variante erfordert kein besonders anspruchsvolles E-Technik-Wissen:
433 MHz Sender und Empfänger (mit Antennen)
Funksteckdosen Set 433 MHz
Meine Alternative dazu ist ein Relais, dass 230V zu einer Steckdose durchschaltet, wenn der Raspberry das Steuersignal gibt. (Achtung! Strom ist lebensgefährlich. Diese Arbeiten sollten nur von einer Fachkraft durchgeführt werden.)
Relais
Feuchtraumabzweigdose
Schuko-Anschlussleitung (alternativ Direktanschluss, direkt an das Zuleitungskabel)
Feuchtraumsteckdose
NYM-Kabel 2,5mm²

Geht das auch mit weniger Material? Bestimmt. Das sieht nach einer Menge aus und ist es auch. Viele Produkte die gebraucht werden sind in Einheitsgrößen verpackt, die für etliche solcher Projekte ausreichen. Man kann eigentlich alle Verbindungen auch mit einem 4 adrigen Kabel und z.B. Lüsterklemmen ersetzen. Vielleicht hat man das ein oder andere auch schon zu Hause. Sicher gibt es auch andere Wege, um die Adapter und Verlängerungen zu umgehen. Ich mag es aber gerne modular. Für mich hat das den besten Komfort. Man muss auch nicht alles verbauen. Wer nur gucken will, braucht kein Relais oder keinen 433MHz Sender + Empfänger. Wen PH, Chlorgehalt und TDS nicht interessiert lässt alles was man dazu braucht einfach weg. Der Vorteil dieser Lösung ist, das eben alles modular ist. Man kann beliebig erweitern oder verschlanken. So wie man es gerade benötigt. Wenn man bei Null anfängt und alles braucht, landet man bei rund 400 €. Auch wenn das erst einmal ein ganz schöner Batzen ist, ist es immer noch günstiger als fertige Steuerungen zu kaufen. Es wird natürlich günstiger, wenn man entweder schon Teile zu Hause hat oder nicht wie ich alles modular und steckbar haben möchte, sondern auch hart verdrahtete Lösungen / Lüsterklemmen toleriert.

Wer sich die Frage stellt: Warum muss es ein Pi Zero sein? Meiner Erfahrung nach hat insbesondere der Pi 4 enormen Bedarf Wärme loszuwerden. Das Problem ist, dass ich den Pi irgendwie zumindest spritzwassergeschützt unterbringen muss. Wo kein Wasser durchkommt, kommt meist auch keine Luft durch und damit kann die Wärme nicht abgeführt werden. Der Pi Zero hat weniger Leistung und auch weniger Abwärme. Trotz der Installation in einem Kabelabzweigkasten war die Maximaltemperatur bislang bei 52 Grad und damit voll im Toleranzbereich.

Installation des Pi

Los geht das Ganze mit dem Zusammenbauen des Pi. Das Gehäuse kommt zerlegt an (2 Teile + Schrauben). Der Pi wird dabei einfach in ein Teil des Gehäuse gelegt und das Gehäuse zugeklappt und verschraubt. Hier kann man nicht viel falsch machen. Bitte darauf achten, dass man selbst geerdet ist (dazu gibt es spezielle Antistatikarmbänder, wenn man keins hat reicht es auch wenn man kurz mal an einen Heizkörper fasst, bevor man loslegt.)

Jetzt muss das Betriebssystem drauf. Mein Computer läuft mit Windows. Es gibt auch Alternativen für MacOS und Linux. Da muss man nicht lang suchen. Auf der Webseite Operating system images – Raspberry Pi kann man sich das Betriebssystem runterladen. Ich empfehle das „Raspberry Pi OS Lite“ ohne Desktop, denn wir haben auf der Kiste nicht so viel Performance und einen Desktop braucht man höchstens zum Einrichten. Ganz einfach geht es indem man den „Raspberry Pi Imager“ von der Seite herunterlädt. Diesen führt man auf dem Rechner aus, gibt an welches OS man installieren möchte und wählt die eingelegte SD Karte als Ziel. Sobald das OS auf der Speicherkarte ist, kann man die SD Karte im Pi einstecken. Hier verbindet man einen Monitor / TV per Mini HDMI (ggf. Adpater nutzen) und eine Tastatur per OTG-Kabel mit dem linken Micro-USB-Anschluss des Pi. Dann verbindet man den rechten Micro-USB-Anschluss des Pi mit dem Netzteil und das OS startet. Im Standard ist der Benutzername „pi“ und das Passwort „raspberry“ (Achtung: es ist noch die engl. Tastaturbelegung eingestellt. Daher ist das Passwort für Deutsche „raspberrz“ 😉 ).

Jetzt richten wir den Pi ein:

sudo raspi-config    # das - ist bei uns ß

Fangen wir erstmal an mit der deutschen Tastatur:

5 Localisation Options ->L1 Locale
de_DE.UTF-8 UTF-8 wählen

5 Localisation Options -> L2 Timezone -> Europa -> Berlin
5 Localisation Options -> L3 Keyboard -> 108 Tasten NODEADKeys
5 Localisation Options -> L4 DE Germany

Jetzt mit OK aus dem Dialog raus und auf der Shell folgendes eingeben:

sudo reboot now

Nach dem Neustart wieder mit „pi“ und „raspberry“ anmelden (diesmal ist y auch y).

sudo raspi-config

1 System Options -> Wireless LAN -> Zugangsdaten eingeben
1 System Options -> Hostname -> Namen des Pi angeben (z.B. Poolmon)
3 Interface Options -> P2 SSH -> Enable: Ja
3 Interface Options -> P5 I2C -> Enable: Ja
3 Interface Options -> P7 1-Wire – Enable: Ja
Dann mit OK wieder zurück auf die Shell.

Mit dem Befehl „ip addr“ bekommt man die aktuelle IP-Adresse unter (inet) angezeigt.

ip addr

Ab jetzt kann man das Kabel zum Monitor und der Tastatur abziehen und bequem vom Computer aus darauf zugreifen. Ich empfehle z.B. PuTTy (kostenlos). Für die Dateiübertragung von Windows lässt sich WinSCP nutzen. Es empfiehlt sich die IP Adresse des Pi zu reservieren. Das geht auf dem DHCP Server, der meist im Router ist. Da jeder Router anders ist, kann ich das leider nicht beschreiben. Sobald die IP Adresse fix ist, ändert sie sich nicht mehr, auch wenn der Pi mal neustartet.

PuTTy starten und IP Adresse des Pi eingeben.

Sobald man auf Open klickt öffnet sich ein Fenster an dem man sich anmelden kann. Klappt das? Prima. Dann erstmal herunterfahren, wir müssen jetzt mit der Verkabelung unserer Bauteile beginnen.

sudo shutdown now

Nach 30 Sekunden kann man das Netzteil aus der Steckdose ziehen.

Jetzt werden die Komponenten zusammengebaut und im Kabelabzweigkasten installiert. Das sieht bei mir nachher so aus.

Installation Temperatursensoren

Aber fangen wir vorne an. Wie die Temperatursensoren installiert werden hatte ich in einem anderen Eintrag schon beschrieben. Daher hier im Schnelldurchlauf:

PIN Belegung des Raspberry PI (Abbildung so wie im Bild darüber)

Die Anschlüsse auf dem mitgelieferten Board des DS18B20 Board Sets werden mit den GPIO Pins des Raspberry verbunden:

DAT -> PIN 7 (GPIO4)
VCC -> PIN 1 (+3,3V)
GND -> PIN 9 (GND)

An die Schraubanschlüsse habe ich das „Telefonkabel“ verbunden. Mir reichen hier 1,5m, da ich die Temperaturanschlüsse direkt daneben verteilen will. Das Kabel sollte vor dem Anschließen durch eine Öffnung des Kabelabzweigkastens geführt werden.

DAT: rot (eigentlich weiß-blau)
VCC: braun
GND: weiß-braun

Auf das andere Ende des Kabels habe ich einen RJ45 gecripmpt. Man braucht dafür eine Crimpzange. Meine Zange hat inzwischen zwei Dekaden auf dem Buckel und geht immer noch.

RJ45 Stecker mit Kontakten nach oben

Im Prinzip ist es egal, wie man die Stecker crimpt, man sollte die Belegung nur einheitlich machen. Ich habe mich (auch wenn es Quatsch ist) an den Netzwerkstandards orientiert. Im Kabel sind alle Kabel verdrillt. Die weißen Kabel sind je mit einem farbigen Kabel in sich nochmals verdrillt. Ist ein z.B. weißes Kabel mit einem gelben verdrillt, nenne ich es weiß-gelb. Die Pin-Reihenfolge ist wie im Bild oben von links nach rechts. Meine Steckerbelegung ist daher so:

PIN 1: weiß-gelb
PIN 2: gelb
PIN 3: weiß-grün
PIN 4: blau
PIN 5: rot (ACHTUNG: Bei diesem Kabel ist das Kabel rot! Es hätte weiß-blau sein müssen.)
PIN 6: grün
PIN 7: weiß-braun
PIN 8: braun

ACHTUNG! Obwohl mir das eigentlich bekannt sein sollte bin ich trotzdem über diesen Fehler gestolpert: Die Adern sind bei einem CAT Stecker nicht 1:1 gleich im eingesteckten Zustand. Es empfiehlt sich daher beim Bau auf beiden Seiten zu messen. Falsch aufgelegte / gecrimpte Anschlüsse fallen so auf und man weiß auch das das Signal da ankommt wo man es erwartet und spart sich so die lästige Fehlersuche im Nachgang. Folgende Umsetzung muss man beachten:

PINFarbe am SteckerFarbe an der anderen Seite
1weiß/gelbbraun
2gelbweiß/braun
3weiß-grüngrün
4blaurot (eigentlich weiß-blau)
5rot (eigentlich weiß-blau)blau
6grünweiß-grün
7weiß-braungelb
8braunweiß-gelb

Das Patchpanel muss natürlich auch angeschlossen werden. Hier habe ich die Kabel mit einem LSA Plus Auflegewerkzeug von links nach rechts durchgelegt und somit alle 8 Anschlüsse zu einem Bus verbunden (gebrückt). Mein LSA Plus Auflegewerkzeug ist genauso alt wie die Crimpzange. Das habe ich bei Amazon sogar gefunden. Wer ohnehin vor hat sich Netzwerkbuchsen im Haus zu verlegen, kommt daran eh nicht vorbei.

Verkabelung im Patchpanel

Die anderen Stecker für die Temperatursensoren werden dann wie der erste oben gezeigte gecrimpt, sofern eine Verlängerung notwendig ist. Am anderen Ende des Kabels werden dann die 3 Kabel miteinander verlötet und mit einem Schrumpfschlauch gegen Feuchtigkeit gesichert. Da mein Sensor für den Unterstand der Pumpe direkt am Verteiler ist, brauche ich hier auch keine Verlängerung und habe daher die einzelnen Adern so gekürzt, dass ich die Kabel des Sensors direkt in den Stecker gecripmpt habe. Belegt werden müssen in diesem Stecker nur die PINs 5,7 und 8. Das ist mitunter etwas fummelig, geht aber. Die Stecker habe ich mit Heißkleber verfüllt. Klar, die Stecker werden nie so gut, wie fertig konfektionierte. Aber zumindest bekommt man den Stecker so etwas dichter als vorher.

PIN 5 -> gelb (DAT)
PIN 7 -> schwarz (GND)
PIN 8 -> rot (VCC)

RJ45 Stecker mit gecrimptem Kabel direkt vom Sensor
RJ45 Stecker mit Heißkleber verfüllt

Die Verbindung zwischen Sensor auf Telefonkabel zur Verlängerung muss daher so sein:

Telefonkabel -> Sensor
rot (weiß-blau) -> gelb (DAT)
weiß-braun -> schwarz (GND)
braun -> rot (VCC)

Steckt man jetzt den Stecker aus dem Kabelabzweigkasten und den ersten Sensor jeweils in einen der Anschlüsse des Patchpanels sollte der Sensor auslesbar sein.

Installation 433 MHz Sender und Empfänger

Die beiden Boards habe ich über ein Multibreadboard verbunden. Das Multibreadboard verbindet dabei ist in der Mitte geteilt und verteilt außer über die Mitte von links nach rechts (oder umgekehrt) die Signale. Mit den Jumper Wire Cables verbinden wir PIN2, PIN6, PIN11 und PIN13 des Raspberry mit je einer Leiterbahn des Multibreadboards. Den Sender und Empfänger verbindet man wie in der Grafik geschildert.

433 MHz Empfänger
433 MHz Sender

Verbinden des Analog Digital Wandlers

Mit dem CQRobot Analog Digital Wandler gibt es auch eine Anleitung. Laut der Anleitung sollen das Modul mit den PINs 3,4,5 und 6 verbunden werden. 6 ist allerdings schon belegt. Daher habe ich die folgende Belegung genutzt:

CQRobot -> Raspberry PI
rot -> Pin 4 (+5V)
schwarz -> Pin 14 GND
grün -> Pin 3 (SDA)
blau -> Pin 5 (SCL)

Verbinden des PH Sensors

Hier kann man dann nicht mehr viel falsch machen. Es gibt auf dem CQRobot Board 4 Kanäle: A0, A1, A2 und A3. Ich habe den PH Sensor auf A0 gesteckt.

schwarz -> G (schwarz)
rot -> V (rot)
gelb -> S (blau)

Den PH Sensor habe ich durch einen Auslass direkt nach Außen geführt und auf der anderen Seite mit einer Mutter gekontert.

Verbinden des TDS Sensors

Der TDS Sensor hat zwar auch schwarz-, rot-, gelb-codierte Kabel, diese haben am Ende allerdings selber einen Pin. Daher habe ich aus dem Jumper Wire Cables Set 3 Kabel mit dem jeweiligen Gegenstück genommen, um das Modul mit dem CQRobot auf Kanal A1 zu verbinden. Wie zuvor ist die Belegung gleich.

Das Kabel des TDS Sensors ist sehr kurz (nicht mal ein Meter). Daher bietet es sich an das zu Verlängern. Ein Kabel an das man ein JST XH Stecker und eine JST XH Buchse konfektioniert sollte dafür ausreichen.

Was die Hardware angeht war es das schon, sofern man die 433MHz Steckdosen nutzen will. Ich habe die Module im Kabelabzweigkasten noch fixiert, das Multibreadboard hat bereits werksseitig einen Klebestreifen unterhalb und kann daher geklebt werden. Den Raspberry habe ich mit einem Streifen Klebeklettband an einer Stelle fixiert, so dass er an kein Modul kommt.

Installation der Temperatur Sensoren

Der Kabelabzweigkasten lässt sich mit ein paar Schrauben irgendwo im Technikbereich unterbringen. Ich habe ihn einfach an ein Brett geschraubt. Ebenso habe ich das Patchpanel festgeschraubt. Ich habe das Patchpanel allerdings vorher mit Gewebeklebeband so abgedichtet, dass kein Spritzwasser in das Gehäuse gelangen kann. An der Rückseite sind Öffnungen, aus denen eigentlich die Netzwerkkabel kommen und die dann mit Kabelbindern an Metallösen befestigt werden. Diese Ösen habe ich genutzt, um mit Scheiben und Schrauben das Panel an das Brett zu schrauben. Zusätzlich habe ich ein Lochband quer über das Panel geschraubt.

Der erste Sensor für den Technikbereich kann man prinzipiell einstecken und einfach irgendwo befestigen, wo er nicht stört. Der Sensor soll anzeigen, ob ggf. der Bereich der Pumpe zu warm wird. V.a. wenn man die Pumpe in einem geschlossenen Bereich aufgestellt hat, könnte dieser Sensor helfen rechtzeitig zu lüften.

Der zweite Sensor für den Pool wird einfach in den Pool geworfen. Er ist wasserdicht und somit dafür geignet. Meiner Erfahrung nach sollte der Sensor ordentlich tief unter Wasser sein. U.U. kann das oben treibende Wasser deutlich wärmer als unten sein. Dann stimmen die Werte nicht, wenn der Sensor an der Oberfläche ist. Also ruhig 50 cm tief (oder mehr) eintauchen. Um den Sensor zu fixieren, habe ich das Kabel mit einem Kabelbinder am Metallrahmen des Pools fixiert.

Der dritte Sensor kommt an die Solarschnecken. Ich habe den Sensor in der Mitte einer Schnecke (ca. halber Radius) angebracht. Erst habe ich den Sensor am Kabel mit einem Kabelbinder fixiert.

Dann habe ich den Sensor mit Gewebeklebeband ganz eng an den Solarschlauch geklebt und den Sensor mit einem Kabelbinder nochmal fester an den Solarschlauch fixiert.

Damit die Außentemperaturen das Messergebnis nicht verfälschen habe ich dann immer Diagonal über den Bereich Isolierklebeband aufgeklebt.

Die Enden des Isolierklebebands habe ich dann nochmal mit je einem Kabelbinder fixiert und da man weiß, wo keine Wärme rausgeht, geht auch keine rein, habe ich die dickte Wulst unter die anderen Solarschlaufen geschoben. Das Kabel habe ich dann auch zum Patchpanel geführt. Bei mir ist das das mit Abstand längste Kabel mit ca. 15 Metern. Die Länge verfälscht das Ergebnis aber nicht.

Den Sensor für den Rücklauf der Solarleitungen habe ich analog zum Sensor in der Solarschnecke angebracht. Den Sensor habe ca. 20 cm bevor er in den Verteiler kommt an einem Solarschlauch befestigt und auch hier mit Kabelbinder, Gewebeklebeband und Isolierklebeband gearbeitet. Da das Wasser hier die gesamte Solarschnecke durchlaufen hat, sollte es hier am wärmsten sein.

Den letzten Sensor sollte man an einer Stelle anbringen, wo er möglichst genau die Umgebungstemperatur ermitteln kann. Am besten sollte er nicht in der Sonne sein, und weder von Technik (Pumpe, etc.) oder Pooltemperatur beeinflusst werden. Jeder hat ja sicher schonmal ein Thermometer hingehangen. Das Prinzip ist hier dasselbe.

Temperatur Sensoren auslesen

Zunächst muss dem Raspberry gesagt werden, wo die Sensoren angeschlossen sind:

sudo vi /boot/config.txt

Hier am Ende der Datei folgedes hinzufügen:

#Enable OneWire Protocol
dtoverlay=w1-gpio
gpiopin=4

Dann den Raspberry neu starten:

sudo reboot now

Wenn man alles richtig gemacht hat, sollte nach Absetzen dieser Zeile die Anzahl der angeschlossenen Sensoren ausgegeben werden.

cat /sys/devices/w1_bus_master1/w1_master_slave_count

Jetzt lassen sich die Sensoren schon auslesen. Dazu ins Verzeichnis der 1-wire Sensoren wechseln

cd /sys/bus/w1/devices

Mit ls lassen sich die Sensoren anzeigen. Die DS18B20 Sensoren beginnen alle mit “28-“

Dieses Bild hat ein leeres Alt-Attribut. Der Dateiname ist image-38.png

Die Werte lassen sich so auslesen:

cat 28-01204e93c742/w1_slave

Wichtig ist, dass die erste Zeile mit einem YES endet (Verkabelung ist korrekt / Checksumme passt). Die Temperatur steht am Ende der zweiten Zeile. t=18500 bedeutet 18,500°C.

Hat man jetzt alle 5 Sensoren angeschlossen ist es mitunter nicht leicht zu unterscheiden welcher Sensor welcher ist. Am einfachsten steckt man zunächst nur einen, notiert sich welcher Sensor gesteckt ist und welche ID (28-…) der Sensor hat. Dann steckt man den zweiten dazu und so weiter.

Für die Check MK Checks braucht man dann jeweils einen Check bzw. eine Datei. Dazu wechselt man in das Checkverzeichnis. Ich setze hier voraus, dass der Check MK Agent bereits installiert ist. Für das Erstellen des Checks zeige ich nur ein Beispiel. Jeder einzelne Sensor kann auf die gleiche Weise konfiguriert werden.

cd /usr/lib/check_mk_agent/local

Der Name des Checks bzw. der Datei ist frei wählbar. Ich halte den Checknamen und den Dateinamen aus Übersichtlichkeitsgründen immer gleich und verwende einen sprechenden Namen.

vi Pool-Temp-Water.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from os.path import exists
filepath = '/sys/bus/w1/devices/28-3c01e0765485/w1_slave'
sensorname = "Pool-Temp-Water"
minwarn = 28.0
mincrit = 26.0
maxwarn = 31.0
maxcrit = 32.0
fileexists = exists(filepath)
if fileexists:
        file = open(filepath, 'r')
        fileLines = file.readlines()
        file.close()

        count = 0
        crc = 0
        for line in fileLines:
                count += 1
                if count == 1:
                        if line.strip().endswith("YES"):
                                crc = 1
                if count == 2:
                        if crc == 1:
                                pos = line.strip().find('t=')
                                temp1 = line.strip()[pos+2:]
                                templen = 5 - len(temp1)
                                temp = temp1[:2-templen] + "." + temp1[2-templen:]
                                state = "0"
                                stateext = "OK"
                                if float(temp) < minwarn:
                                        state = "1"
                                        stateext = "WARN"
                                if float(temp) < mincrit:
                                        state = "2"
                                        stateext = "CRIT"
                                if float(temp) >= maxwarn:
                                        state = "1"
                                        stateext = "WARN"
                                if float(temp) >= maxcrit:
                                        state = "2"
                                        stateext = "CRIT"
                                print("{} {} temp={};{};{};{};{} {} - Temp {} °C".format(state,sensorname,temp,maxwarn,maxcrit,mincrit,maxcrit,stateext,temp))
                        else:
                                print("3 {} temp=0 Unknown - Wiring problem with sensor".format(sensorname))
else:
        print("3 {} temp=0 Unknown - Wiring problem with sensor. Sensor not found.".format(sensorname))

Die Werte minwarn, mincrit, maxwarn, maxcrit kann jeder für sich individuell konfigurieren. Auch die ID des Sensors muss natürlich auf den jeweiligen Sensor geändert werden.

Jetzt muss die Datei noch ausführbar gemacht werden.

chmod 755 Pool-Temp-Water.py

Kleiner Tipp: Wer sich Arbeit sparen möchte kopiert die Sensoren einfach und passt darin nur die Werte in den Zeilen 3 bis 8 an.

cp Pool-Temp-Water.py Pool-Temp-Solar.py

Bei einem Rescan findet Check MK jetzt die Sensoren. Anschließend kann man sich in CheckMK eine Übersicht als Teil des Check-Ergebnisses in der Hostanzeige anzeigen lassen oder alternativ auch einzelne Sensoren z.B. in einem Langzeitverlauf grafisch.

CheckMK: Ausschnitt der Hostanzeige
CheckMK: Grafisch Dargestellter Temperaturverlauf

Es ist zwar schön, dass man jetzt die Werte im Monitoring hat und diese auch über die Zeit beobachten kann. Was mich vor allem in den ersten Tagen interessiert hat: Welche Differenz zwischen Solarschlauch und Pool gibt es? Auch dafür habe ich einen Check gebaut. Der genau wie die vorigen implementiert wird. Dieser Check vergleicht die Wassertemperatur des Pools mit der Wassertemperatur der Solarschläuche. Ist die Temperatur gleich zeigt der Sensor 0° C an, ist das Wasser im Pool 2° C kühler als in den Solarschnecken, zeigt der Sensor -2° C an und analog dazu zeigt der Sensor 2°C an, wenn die Solarschnecken 2°C wärmer als der Pool sind. In der Nacht kommen da schon mal 2 stellige Minusbeträge an. Am Tag war meine bislang höchste gemessene Differenz 5°C (Pumpe lief).

vi Pool-Temp-WaterSolarDiff.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-
sensorname = "Pool-Temp-WaterSolarDiff"
minwarn = 1.0 
mincrit = 0.5
maxwarn = 10.0
maxcrit = 15.0
from os.path import exists
filepathWater = '/sys/bus/w1/devices/28-3cffe076da73/w1_slave'
filepathSolar = '/sys/bus/w1/devices/28-3c01e07643eb/w1_slave'

fileexists = exists(filepathWater)
if fileexists:
    file = open(filepathWater, 'r')
    fileLinesWater = file.readlines()
    file.close()
    
    fileexists = exists(filepathSolar)
    if fileexists:
        file = open(filepathSolar, 'r')
        fileLinesSolar = file.readlines()
        file.close()
        
        count = 0
        crc = 0
        for lineWater in fileLinesWater:
            count += 1
            if count == 1:
                if lineWater.strip().endswith("YES"):
                    crc = 1
            if count == 2:
                if crc == 1:
                    pos = lineWater.strip().find('t=')
                    temp1 = lineWater.strip()[pos+2:]
                    templen = 5 - len(temp1)
                    tempWater = temp1[:2-templen] + "." + temp1[2-templen:] 
                    state = "0"
                    stateext = "OK"
                    countSolar = 0
                    crcSolar = 0
                    for lineSolar in fileLinesSolar:
                        countSolar += 1
                        if countSolar == 1:
                            if lineSolar.strip().endswith("YES"):
                                crcSolar = 1
                        if countSolar == 2:
                            if crcSolar == 1:
                                pos = lineSolar.strip().find('t=')
                                temp1 = lineSolar.strip()[pos+2:]
                                templen = 5 - len(temp1)
                                tempSolar = temp1[:2-templen] + "." + temp1[2-templen:]
                                tempDiff = float(tempSolar)-float(tempWater)
            

                                if tempDiff < minwarn:
                                    state = "1"
                                    stateext = "WARN"
                                if tempDiff < mincrit:
                                    state = "2"
                                    stateext = "CRIT"
                                if tempDiff >= maxwarn:
                                    state = "1"
                                    stateext = "WARN"
                                if tempDiff >= maxcrit:
                                    state = "2"
                                    stateext = "CRIT"
                                print("{} {} temp={};{};{};{};{} {} - Temp {} °C".format(state,sensorname,tempDiff,maxwarn,maxcrit,mincrit,maxcrit,stateext,tempDiff))
                            else:
                                print("3 temp=0 Unknown - Wiring problem with Solar sensor")
                else:
                    print("3 temp=0 Unknown - Wiring problem with Water sensor")

    else:
        print("3 {} temp=0 Unknown - Wiring problem with solar sensor. Sensor not found.".format(sensorname))
else:
	print("3 {} temp=0 Unknown - Wiring problem with water sensor. Sensor not found.".format(sensorname))

Einer meiner Temperatur Sensoren wurde nach kurzer Zeit nicht mehr ausgelesen. Ich habe den Sensor dann getauscht, aber auch der neue Sensor zeigte keine Temperatur an. Ich habe mich daher entschlossen einen zweiten 1-Wire Bus zu machen, da 1-Wire wohl ein Problem hat sobald die Kabellängen länger werden. (Wie sich im Nachgang herausstellte waren die beiden Sensoren allerdings wirklich defekt, daher hätte ich diesen Aufwand ggf. gar nicht betreiben müssen.)

Ich habe auf GPIO 18 / PIN 12 einen neuen zusätzlichen Dateneingang für 1-Wire definiert und mir die 3.3V + GND von den anderen Sensoren abgezapft. Ich habe dafür auch die anderen Sensoren über das Multibreadboard verbunden und konnte mir so Strom umleiten. Zudem habe ich diesmal um Platz zu sparen ein 4.7kOhm Widerstand zwischen 3.3V und DAT gesteckt. Dafür habe ich ein Jumperwire cable Steker/Stecker geteilt, den Widerstand dazwischen gelötet und freiliegende Adern mit einem Schrumpfschlauch isoliert. Auf ein ca. 30cm langes Telefonkabel habe ich nach bekanntem Muster einen RJ45 Stecker gecripmpt und dort einen RJ45 Verbinder aufgesteckt. Die andere Seite habe ich in das Gehäuse geführt und mit 3.3V, GND und dem neuen Datenport angeschlossen. So kann ich den Sensor beqeuem verbinden.

Der Raspberry muss jetzt nur wissen, dass er auch diesen GPIO Port (18) für 1 Wire nutzen soll. Mit dem folgenden Befehl kann man das realisieren:

dtoverlay w1-gpio gpiopin=18 pullup=0

Sobald der Raspberry neu startet müsste man den Befehl neu absetzen. Daher habe ich den Befehl in die Datei /etc/rc.local geschrieben. Wichtig: Am Ende sollte „exit 0“ stehen bleiben.

vi /etc/rc.local

Anschließend wird im Wire-Bus-Verzeichnis „w1_bus_master2“ angezeigt. Man könnte auf diesem Wege so viele Busse anlegen, wie man freie GPIOs hat. Die Sensoren werden weiterhin direkt in diesem Verzeichnis angezeigt. Sollte man die Busse vertauschen, macht das also keinen Unterschied.

Auslesen der analogen Sensoren PH / TDS

Ich habe unter /etc erst einmal ein Verzeichnis dafür angelgt.

cd /etc
mkdir CQRobot
cd CQRobot

Auf der Webseite des Herstellers findet man dann Anleitungen und Beispiele. Die Datei CQRobot_ADS1115.py benötigt man. Wer die Beispiele ausprobieren will, kann das tun. Für die Sensoren nutze ich diese Scripts, die ich neben der Datei CQRobot_ADS1115.py in /etc/CQRobot ablege:

ReadVoltagePH.py



#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
sys.path.append('../')
import time
from CQRobot_ADS1115 import ADS1115
ADS1115_REG_CONFIG_PGA_6_144V        = 0x00 # 6.144V range = Gain 2/3
ADS1115_REG_CONFIG_PGA_4_096V        = 0x02 # 4.096V range = Gain 1
ADS1115_REG_CONFIG_PGA_2_048V        = 0x04 # 2.048V range = Gain 2 (default)
ADS1115_REG_CONFIG_PGA_1_024V        = 0x06 # 1.024V range = Gain 4
ADS1115_REG_CONFIG_PGA_0_512V        = 0x08 # 0.512V range = Gain 8
ADS1115_REG_CONFIG_PGA_0_256V        = 0x0A # 0.256V range = Gain 16
ads1115 = ADS1115()

#Set the IIC address
ads1115.setAddr_ADS1115(0x48)
#Sets the gain and input voltage range.
ads1115.setGain(ADS1115_REG_CONFIG_PGA_4_096V)
#Get the Digital Value of Analog of selected channel
adc = ads1115.readVoltage(0)
#time.sleep(0.2)
#print("%dmV "%(adc['r']))
print("%d"%(adc['r']))

ReadVoltageTDS.py



#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
sys.path.append('../')
import time
from CQRobot_ADS1115 import ADS1115
ADS1115_REG_CONFIG_PGA_6_144V        = 0x00 # 6.144V range = Gain 2/3
ADS1115_REG_CONFIG_PGA_4_096V        = 0x02 # 4.096V range = Gain 1
ADS1115_REG_CONFIG_PGA_2_048V        = 0x04 # 2.048V range = Gain 2 (default)
ADS1115_REG_CONFIG_PGA_1_024V        = 0x06 # 1.024V range = Gain 4
ADS1115_REG_CONFIG_PGA_0_512V        = 0x08 # 0.512V range = Gain 8
ADS1115_REG_CONFIG_PGA_0_256V        = 0x0A # 0.256V range = Gain 16
ads1115 = ADS1115()

#Set the IIC address
ads1115.setAddr_ADS1115(0x48)
#Sets the gain and input voltage range.
ads1115.setGain(ADS1115_REG_CONFIG_PGA_0_512V)
#Get the Digital Value of Analog of selected channel
adc = ads1115.readVoltage(1)
#time.sleep(0.2)
#print("%dmV "%(adc['r']))
print("%d"%(adc['r']))

Beide Dateien macht man ausführbar.

chmod 755 ReadVoltagePH.py ReadVoltageTDS.py

Daraus resultiert ein Wert in Volt (hier Komma zwischen 1. und 2. Stelle), der vom Sensor zurückgegeben wird. Welchen Wert dieser Wert in PH bzw. ppm dies darstellt gilt es herausfinden. Es macht Sinn, dass man über ausreichende Mittel verfügt Referenzwerte zu schaffen, um die Sensoren zu justieren. Bislang bin ich noch nicht dazu gekommen. Am einfachsten ist es wenn man sich für den PH Sensor Referenzlösung zulegt. Diese löst man nach Anleitung im Wasser auf und gibt dann den Sensor hinein. Nach ca. 1 Minute liest man den Wert aus (am besten den Originalwert in Volt). Dann holt man den Sensor raus, neutralisiert ihn unter fließendem Wasser und wiederholt das mit den anderen PH-Werten die man zur Verfügung hat. Anschließend muss man eine mathematische Funktion bauen, die diese Werte in den PH Wert wandelt. Ich werde den Beitrag ergänzen sobald ich die Funktion für meinen Sensor ermittelt habe. Das wird sicherlich eine ganz gute Referenz sein. Für den TDS Sensor nutze ich z.Zt. ein elektrisches Testgerät. Der TDS Sensor muss nicht kalibriert werden, allerdings der PH Sensor. Bei mir hatte sich über Winter eine Abweichung des PH Wertes um 3 ergeben. Es macht daher Sinn die Werte der Sensoren z.B. mit Wasserteststreifen ab und an zu verifizieren.

Die Check MK / Nagios Checks beider Sensoren sehen so aus:

vi Pool-PHSensor.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
sys.path.append('../')
sys.path.append('/etc/CQRobot')
import time
from CQRobot_ADS1115 import ADS1115
ADS1115_REG_CONFIG_PGA_6_144V        = 0x00 # 6.144V range = Gain 2/3
ADS1115_REG_CONFIG_PGA_4_096V        = 0x02 # 4.096V range = Gain 1
ADS1115_REG_CONFIG_PGA_2_048V        = 0x04 # 2.048V range = Gain 2 (default)
ADS1115_REG_CONFIG_PGA_1_024V        = 0x06 # 1.024V range = Gain 4
ADS1115_REG_CONFIG_PGA_0_512V        = 0x08 # 0.512V range = Gain 8
ADS1115_REG_CONFIG_PGA_0_256V        = 0x0A # 0.256V range = Gain 16
ads1115 = ADS1115()

sensorname = "Pool-PHSensor"
minwarn = 7.2
mincrit = 7.0
maxwarn = 7.4
maxcrit = 7.6 

#Set the IIC address
ads1115.setAddr_ADS1115(0x48)
#Sets the gain and input voltage range.
ads1115.setGain(ADS1115_REG_CONFIG_PGA_4_096V)
#Get the Digital Value of Analog of selected channel
PHmV = ads1115.readVoltage(0)

PH = float([x for x in PHmV.values()][0])/322

state = "0"
stateext = "OK"
if float(PH) < minwarn:
	state = "1"
	stateext = "WARN"
if float(PH) < mincrit:
        state = "2"
        stateext = "CRIT"
if float(PH) > maxwarn:
        state = "1"
        stateext = "WARN"
if float(PH) > maxcrit:
        state = "2"
        stateext = "CRIT"

print("{} {} PH={};{};{};{};{} {} - PH {} ".format(state,sensorname,PH,maxwarn,maxcrit,mincrit,maxcrit,stateext,PH))
vi Pool-TDSSensor.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
sys.path.append('../')
sys.path.append('/etc/CQRobot')
import time
from CQRobot_ADS1115 import ADS1115
ADS1115_REG_CONFIG_PGA_6_144V        = 0x00 # 6.144V range = Gain 2/3
ADS1115_REG_CONFIG_PGA_4_096V        = 0x02 # 4.096V range = Gain 1
ADS1115_REG_CONFIG_PGA_2_048V        = 0x04 # 2.048V range = Gain 2 (default)
ADS1115_REG_CONFIG_PGA_1_024V        = 0x06 # 1.024V range = Gain 4
ADS1115_REG_CONFIG_PGA_0_512V        = 0x08 # 0.512V range = Gain 8
ADS1115_REG_CONFIG_PGA_0_256V        = 0x0A # 0.256V range = Gain 16
ads1115 = ADS1115()

sensorname = "Pool-TDSSensor"
minwarn = 160.0
mincrit = 150.0
maxwarn = 200.0
maxcrit = 210.0 

#Set the IIC address
ads1115.setAddr_ADS1115(0x48)
#Sets the gain and input voltage range.
ads1115.setGain(ADS1115_REG_CONFIG_PGA_2_048V)
#Get the Digital Value of Analog of selected channel
TDSmV = ads1115.readVoltage(1)

ppm = float([x for x in TDSmV.values()][0])/2.51

state = "0"
stateext = "OK"
if float(ppm) < minwarn:
	state = "1"
	stateext = "WARN"
if float(ppm) < mincrit:
        state = "2"
        stateext = "CRIT"
if float(ppm) > maxwarn:
        state = "1"
        stateext = "WARN"
if float(ppm) > maxcrit:
        state = "2"
        stateext = "CRIT"

print("{} {} ppm={};{};{};{};{} {} - {} ppm ".format(state,sensorname,ppm,maxwarn,maxcrit,mincrit,maxcrit,stateext,ppm))

Nutzung der 433 MHz Funksteckdosen

Das Thema hatte ich in der Einkaufsliste schon thematisiert. Ich hatte es im Haus erfolgreich getestet und wollte dann die Pumpe über diese kostengünstige und einfach Lösung schalten. Als am Pool alles aufgebaut war und ich die Steckdose dann schalten wollte passierte bei mir nichts. Nicht einmal die Fernbedienung hat die Steckdose geschaltet. Ob die Pumpen das Störsignal erzeugen oder mein Nachbar einen illegalen Radiosender betreibt kann ich nur mutmaßen. Immerhin kosteten der Sender und der Empfänger nicht die Welt und mit meiner Alternative weiß ich auch sicher, ob der Strom an der Pumpe anliegt. Wer lieber auf die Funklösung setzen möchte kann gerne hier weiterlesen.

Es müssen zunächst ein paar Voraussetzungen installiert werden und aus dem Git Repository von ninjablocks die 433Utils geladen werden.

sudo apt install wiringpi
sudo apt install git-core
cd /etc
git clone https://github.com/ninjablocks/433Utils.git --recursive
cd 433Utils/RPi_utils
make all

Ist das erledigt kann man die Funksteckdose auch schon startklar machen. Erstmal sollte man sicherstellen, dass man die Steckdose mit der Fernbedienung ein- und ausschalten kann. Anschließend startet man den Sniffer und geht mit der Fernbedienung nahe an den 433MHz Empfänger und schaltet die Steckdose ein und aus.

sudo ./RFSniffer

Auf dem Bildschrim erscheinen jetzt zeilenweise Zahlen. Eigentlich sollte nur eine Zahl zum Einschalten und eine zum Ausschalten erscheinen. Bei mir gab es aber auch ein paar Ausreißer. Dennoch findet man die Zahlen relativ einfach raus. Die Zahlen merkt man sich. Den Sniffer kann man mit STRG+C abbrechen.

Jetzt erstellt man die Datei mit der nachher die Steckdose geschaltet wird.

vi SolarPump.cpp

In diese Datei kommt der folgende Inhalt:

#include "../rc-switch/RCSwitch.h"
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
    int PIN = 0; 
    int codeSocketDon = 4461841;
    int codeSocketDoff = 4461844;

    if (wiringPiSetup() == -1) return 1;

    RCSwitch mySwitch = RCSwitch();
    mySwitch.enableTransmit(PIN);

    if (atoi(argv[1]) == 1) {
        mySwitch.send(codeSocketDon, 24);
    } else {
        mySwitch.send(codeSocketDoff, 24);
    }
    return 0;
}

Meine Codes (4461841 und 4461844) müssen natürlich durch die von der jeweiligen Steckdose getauscht werden. Der Quellcode muss noch kompiliert werden.

g++ -DRPI ../rc-switch/RCSwitch.cpp SolarPump.cpp -o SolarPump -lwiringPi

Schon lässt sich die Steckdose schalten (oder auch nicht, wie in meinem Fall).

./SolarPump 1      // Einschalten
./SolarPump 0      // Ausschalten

Wer mit dieser Schaltung ins Rennen gehen will kann dies in einem Python-Skript z.B. so implementieren

import subprocess

# Hier kommt der Code hin, der festlegt, ob die Pumpe geschaltet werden
# soll
. Wenn die Variable pumppower 1 ist soll eingechaltet, bei 0 soll
# ausgeschaltet werden.

if pumppower == 1:
	subprocess.run(["/etc/433Utils/RPi_utils/SolarPump", "l"])
else pumppower == 0:
	subprocess.run(["/etc/433Utils/RPi_utils/SolarPump", "0"])

Schaltung der Solarpumpe mit Relais

Das ist der Part, der für mich persönlich am wichtigsten war. Ich hatte zuvor die Solarpumpe halbwegs intelligent über eine WLAN-Steckdose gesteuert, mit der ich z.B. 3 Stunden nach Sonnenaufgang die Pumpe ein- und 3 Stunden vor Sonnenuntergang die Pumpe ausschalten konnte. Die Steckdose wusste aber nicht, wie warm das Wasser im Pool ist, ob das Wasser in den Solarschnecken ggf. sogar kälter als der Pool ist, usw. Klar, kann man das einstellen. Aber der Blick auf die Wetterapp musste dann schon sein. Und bei Tagen mit wechselhaftem Wetter, lief das ganze sicher auch nicht optimal, v.a. wenn man tagsüber nicht zu Hause ist, aber Abends doch in den Pool will.

Der Anschluss an sich ist realtiv simpel. Trotzdem auch hier nochmals der Hinweis, dass Arbeiten mit Strom lebensgefährlich ist. Diese Arbeiten sollten nur von einem Fachmann durchegeführt werden. Der Anschluss erfolgt nach folgendem Schema

Die Anschlüsse 5V und GND vom Raspberry können über das Multibreadboard abgegriffen werden, an dem auch der 433 MHz Sender und Empfänger angeschlossen sind. Wer diese Module nicht betreibt und kein Multibreadboard einsetzt kann daher auch 5V von PIN2 und GND von PIN6 holen. Der GPIO ist frei wählbar. Ich habe Pin 15 (GPIO 22) genutzt. Daher resultiert für mich daraus die folgende Belegung der GPIO Pins am Raspberry. (Ich habe die 433 MHz Elemente drin gelassen)

Der GPIO Port muss jetzt noch vorbereitet werden. Man kann sich den Status aller GPIO Pins anzeigen lassen.

sudo gpio readall

Der Pin, den ich mir ausgesucht hatte hat den BCM 22 und trägt den Namen GPIO. 3. Der Modus ist IN (also eingehend) und derzeit liegt keine Spannung an (V).

Der Modus des GPIO muss auf Out geändert und anschließend kontrolliert werden.

sudo gpio export 22 out
sudo gpio readall

Sobald das erledigt ist, kann man den GPIO schalten. Mit 1 am Ende der Zeile legt man Spannung an, mit 0 dagegen, nimmt man die Spannung weg. Für unser Relais bedeutet das: Liegt Spannung an, bekommt die Steckdose und somit die Pumpe Strom. Liegt keine Spannung an, bekommt die Steckdose und somit die Pumpe keinen Strom.

// Einschalten
gpio -g write 22 1
// Auslesen
gpio -g read 22
// Ausschalten
gpio -g write 22 0
// Auslesen
gpio -g read 22

Damit ist der Grundstein gelegt. Jetzt muss der Pin nur noch über ein Skript geschaltet werden.

Automatisierung der Schaltung

Für Python werden zusätzliche Pakete benötigt. Diese müssen noch installiert werden.

sudo apt install python-dev python-rpi.gpio

Erst wird ein Verzeichnis erstellt in dem der Automatismus / das Skript erstellt wird.

cd /etc
mkdir PoolAutomatation
vi PoolAutomatation.py

Im Verzeichnis werden noch drei Dateien benötigt, die Variablen enthalten. Diese legt man per z.B. per vi an.

vi SwitchOffDiff.txt
vi SwitchOnDiff.txt
vi TargetTemp.txt
vi TargetTempDivergence.txt
vi StartTime.txt
vi EndTime.txt

Diese Dateien beinhalten dann die Temperaturwerte. Ich habe initial folgende Werte dort hineingeschrieben:

SwitchOffDiff.txt: 0.8 (Ausschalten wenn Differenz zw. Solar und Wasser weniger als x Grad beträgt)
SwitchOnDiff.txt: 1.0 (Einschalten wenn Differenz zw. Solar und Wasser mehr als x Grad beträgt)
TargetTemp.txt: 30.0 (Zieltemperatur, Ausschalten, wenn Temperatur erreicht.)
TargetTempDivergence.txt: 0.2 (Erlaubt eine Abweichung der Zieltemperatur um x Grad. Damit die Pumpe nicht ständig ein und ausschaltet)
StartTime.txt: 08:00 (Uhrzeit nach der die Pumpe frühestens einschalten darf)
EndTime.txt: 20:00 (Uhrzeit nach der die Pumpe ausschalten muss)

Als Resultat schaltet die Pumpe ab, wenn der Pool die Zieltemperatur 30° C erreicht hat oder wenn die Temperatur in der Solarschnecke weniger als 0,8°C wärmer als der Pool ist oder wenn es nach 20 Uhr ist. Die Pumpe schaltet sich ein, wenn die Solarschnecke mehr als 1° C wärmer als der Pool ist und die Poolwassertemperatur mehr als 0,2 Grad unterhalb des Maximalwertes ist und es nach 8 Uhr ist. Geschaltet wird maximal einmal pro Minute.

Wichtig ist, der Punkt (kein Komma!), damit die Zahl auch als Fließkommazahl interpretiert werden kann.

Sollen die Werte später über den Brower anpassbar sein, sollte man sie entsprechend berechtigen.

chmod 766 SwitchOffDiff.txt SwitchOnDiff.txt TargetTemp.txt TargetTempDivergence.txt StartTime.txt EndTime.txt

Das Python-Skript PoolAutomatation.py hat diesen Inhalt. Bitte beachten, dass die Sensor-IDs durch die entsprechenden ersetzt werden. Python achtet bei der Intrepretation auf Tabstopps. Daher bitte darauf achten, dass Tabstopps auch wirklich Tabstopps sind, sonst funktioniert es nicht.

#!/usr/bin/python3
# -*- coding: utf-8 -*-
import time
import datetime
import RPi.GPIO as GPIO

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(22, GPIO.OUT)

while True:
	now = datetime.datetime.now()
	file = open('/etc/PoolAutomatation/TargetTemp.txt', 'r')
	fileLines = file.readlines()
	file.close()
	count = 0
	for line in fileLines:
		count += 1
		if count == 1:
			targettemp = float(line)

	file = open('/etc/PoolAutomatation/SwitchOffDiff.txt', 'r')
	fileLines = file.readlines()
	file.close()
	count = 0
	for line in fileLines:
		count += 1
		if count == 1:
			switchoffdiff = float(line)

	file = open('/etc/PoolAutomatation/SwitchOnDiff.txt', 'r')
	fileLines = file.readlines()
	file.close()
	count = 0
	for line in fileLines:
		count += 1
		if count == 1:
			switchondiff = float(line)

	file = open('/sys/bus/w1/devices/28-3cffe076da73/w1_slave', 'r')
	fileLines = file.readlines()
	file.close()
	count = 0
	crcPoolWater = 0
	for line in fileLines:
		count += 1
		if count == 1:
			if line.strip().endswith("YES"):
				crcPoolWater = 1
		if count == 2:
			if crcPoolWater == 1:
				pos = line.strip().find('t=')
				temp1 = line.strip()[pos+2:]
				templen = 5 - len(temp1)
				TempPool = float(temp1[:2-templen] + "." + temp1[2-templen:])

	file = open('/sys/bus/w1/devices/28-3c01e07643eb/w1_slave', 'r')
	fileLines = file.readlines()
	file.close()
	count = 0
	crcSolar = 0
	for line in fileLines:
		count += 1
		if count == 1:
			if line.strip().endswith("YES"):
				crcSolar = 1
		if count == 2:
			if crcSolar == 1:
				pos = line.strip().find('t=')
				temp1 = line.strip()[pos+2:]
				templen = 5 - len(temp1)
				TempSolar = float(temp1[:2-templen] + "." + temp1[2-templen:])
	
	file = open('/etc/PoolAutomatation/TargetTempDivergence.txt', 'r')
	fileLines = file.readlines()
	file.close()
	count = 0
	for line in fileLines:
		count += 1
		if count == 1:
			targettempdivergence = float(line)

	file = open('/etc/PoolAutomatation/StartTime.txt', 'r')
	fileLines = file.readlines()
	file.close()
	count = 0
	for line in fileLines:
		count += 1
		if count == 1:
			pos = line.strip().find(':')
			StartHour = int(line.strip()[:pos])
			StartMin = int(line.strip()[pos+1:])
			Starttime = now.replace(hour=StartHour, minute=StartMin, second=0, microsecond=0)


	file = open('/etc/PoolAutomatation/EndTime.txt', 'r')
	fileLines = file.readlines()
	file.close()
	count = 0
	for line in fileLines:
		count += 1
		if count == 1:
			pos = line.strip().find(':')
			EndHour = int(line.strip()[:pos])
			EndMin = int(line.strip()[pos+1:])
			Endtime = now.replace(hour=EndHour, minute=EndMin, second=0, microsecond=0)

	pinstate = GPIO.input(22)
	if pinstate == 1:
		if TempPool > targettemp:
			GPIO.output(22, False) 
		if (TempSolar - TempPool) < switchoffdiff:
			GPIO.output(22, False)
		if now > Endtime:
			GPIO.output(22, False)
	if pinstate == 0:
		if ((TempSolar - TempPool) >= switchondiff) and (TempPool < targettemp) and (now > Starttime) and (TempPool < (targettemp - targettempdivergence)):
			GPIO.output(22, True)
	time.sleep(60)

Jetzt das Skript ausführbar machen.

chmod 755 PoolAutomatation.py

Testlauf gefällig?

./PoolAutomatation.py

Beenden kann man das Skript mit STRG + C. Jetzt wäre es sehr müßig ständig auf der Konsole angemeldet sein zu müssen, um das Skript laufen zu lassen. Daher wird das Skript als Service eingebunden, so dass es ständig im Hintergrund läuft.

sudo vi /etc/systemd/system/PoolAutomatation.service

In diese Datei kommt der folgende Inhalt

[Unit]
Description=PoolAutomatation service
After=multi-user.target
[Service]
Type=simple
Restart=always
ExecStart=/usr/bin/python3 /etc/PoolAutomatation/PoolAutomatation.py
[Install]
WantedBy=multi-user.target

Anschließend die Konfig neu einlesen, den Dienst aktivieren und starten.

sudo systemctl daemon-reload
sudo systemctl enable PoolAutomatation.service
sudo systemctl start PoolAutomatation.service

Jetzt sollte der Dienst laufen. Das lässt sich so herausfinden:

systemctl status PoolAutomatation.service

Sollte das Skript mal geändert werden müssen, sollte der Service vorher gestoppt werden:

sudo systemctl stop PoolAutomatation.service
// Änderungen durchführen
sudo systemctl start PoolAutomatation.service

Übersichtswebseite

Ich habe eine Webseite gebaut, auf der ich einfach schnell die Daten auslesen kann. Man kann die Daten zwar auch in Check MK ablesen, aber die Werte die zuvor für die Automatisierung gesetzt wurden kann ich in Check MK nicht anpassen und um mal kurz alle Werte zu sehen ist das hier übersichtlicher.

Ich nutze dafür nginx. Installieren lässt er sich über

sudo apt install nginx

Ich habe bei mir ein gültiges SSL-Zertifikat hinterlegt, damit ich die Webseite auch per https aufgerufen werden kann. Damit das allerdings sauber funktioniert müssen noch recht viele Dinge angepasst werden, die hier den Rahmen sprengen. Daher erkläre ich hier nur das simple http.

Unter /var/www/html/ findet sich die Webseite. Darin befindet sich bereits eine Datei: index.nginx-debian.html
Ich habe diese Datei umbenannt

sudo mv index.nginx-debian.html index.nginx-debian.html.old

und eine neue Index-Datei erstellt. Die Webseite habe ich schlicht gehalten und mittels PHP die Werte der Sensoren dynamisch eingefügt.

sudo vi index.php

In die Datei dann folgendes einfügen:

<HTML>
<body>
<?php
echo '<p>Temp Au&szlig;en: ';

$TempAmbientFile = '/sys/bus/w1/devices/28-3c01e07621a0/w1_slave';
$lines = file($TempAmbientFile);//file in to an array
if ((substr($lines[0], (strlen($lines[0]) -4),3)) == "YES") {
        $TempAmbient = (float)(substr($lines[1], (strlen($lines[1]) - 6), 2).'.'.substr($lines[1], (strlen($lines[1]) - 4), 3));
        echo $TempAmbient.'° C';
} else {
        echo 'Sensor not okay';
}
echo '</p>';

echo '<p>Temp Pumpenraum: ';

$TempPumproomFile = '/sys/bus/w1/devices/28-000000044223/w1_slave';
$lines = file($TempPumproomFile);//file in to an array
if ((substr($lines[0], (strlen($lines[0]) -4),3)) == "YES") {
        $TempAmbient = (float)(substr($lines[1], (strlen($lines[1]) - 6), 2).','.substr($lines[1], (strlen($lines[1]) - 4), 3));
        echo $TempAmbient.'° C';
} else {
        echo 'Sensor not okay';
}
echo '</p>';

echo '<p>Temp Solar: ';
$TempSolarFile = '/sys/bus/w1/devices/28-3c01e07643eb/w1_slave';
$lines = file($TempSolarFile);//file in to an array
if ((substr($lines[0], (strlen($lines[0]) -4),3)) == "YES") {
        $TempSolar = (float)(substr($lines[1], (strlen($lines[1]) - 6), 2).'.'.substr($lines[1], (strlen($lines[1]) - 4), 3));
        echo $TempSolar.'° C';
} else {
        echo 'Sensor not okay';
}

echo '</p>';

echo '<p>Temp SolarR&uuml;cklauf: ';

$TempSolarBackFile = '/sys/bus/w1/devices/28-3c01e0765485/w1_slave';
$lines = file($TempSolarBackFile);//file in to an array
if ((substr($lines[0], (strlen($lines[0]) -4),3)) == "YES") {
        $TempSolarBack = (float)(substr($lines[1], (strlen($lines[1]) - 6), 2).','.substr($lines[1], (strlen($lines[1]) - 4), 3));
        echo $TempPoolwater.'° C';
} else {
        echo 'Sensor not okay';
}
echo '</p>';



echo '<p>Temp Poolwasser: ';
$TempWaterFile = '/sys/bus/w1/devices/28-3cffe076da73/w1_slave';
$lines = file($TempWaterFile);//file in to an array
if ((substr($lines[0], (strlen($lines[0]) -4),3)) == "YES") {
        $TempPoolwater = (float)(substr($lines[1], (strlen($lines[1]) - 6), 2).'.'.substr($lines[1], (strlen($lines[1]) - 4), 3));
        echo $TempPoolwater.'° C';
} else {
        echo 'Sensor not okay';
}
echo '</p>';

echo '<p>Temp Solar/Wasser-Differenz: ';
$TempDiff = $TempSolar - $TempPoolwater;
echo $TempDiff;
echo '</p>';


exec ('/etc/CQRobot/ReadPH.py',$ph);
echo '<p>'.$ph[0].'</p>';
exec ('/etc/CQRobot/ReadTDS.py',$tds);
echo '<p>'.$tds[0].'</p>';


exec ('gpio -g read 22', $gpiostat);
if ($gpiostat[0] == "1") {
	echo '<p style="color:green;">Die Pumpe ist derzeit eingeschaltet.</p>';
} else {
	echo '<p style="color:red;">Die Pumpe ist derzeit ausgeschaltet.</p>';
}


echo '<p>';
echo '<form method="put" action="setvalues.php">';
$fileHandleTargetTemp = fopen('/etc/PoolAutomatation/TargetTemp.txt','r');
$fileContTargetTemp = fread($fileHandleTargetTemp, filesize('/etc/PoolAutomatation/TargetTemp.txt'));
fclose($fileHandleTargetTemp);
echo 'Ziel-Wassertemperatur: <input type="text" name="TargetTemp" value="'.$fileContTargetTemp.'">';
#echo $fileContTargetTemp;

$fileHandleSwitchOffDiffTemp = fopen('/etc/PoolAutomatation/SwitchOffDiff.txt','r');
$fileContSwitchOffDiffTemp = fread($fileHandleSwitchOffDiffTemp, filesize('/etc/PoolAutomatation/SwitchOffDiff.txt'));
fclose($fileHandleSwitchOffDiffTemp);
#echo $fileContSwitchOffDiffTemp;
echo '<br>Ausschalt-Temperaturdifferenz: <input type="text" name="SwitchOffDiffTemp" value="'.$fileContSwitchOffDiffTemp.'">';

$fileHandleSwitchOnDiffTemp = fopen('/etc/PoolAutomatation/SwitchOnDiff.txt','r');
$fileContSwitchOnDiffTemp = fread($fileHandleSwitchOnDiffTemp, filesize('/etc/PoolAutomatation/SwitchOnDiff.txt'));
fclose($fileHandleSwitchOnDiffTemp);
echo '<br>Einschalt-Temperaturdifferenz: <input type="text" name="SwitchOnDiffTemp" value="'.$fileContSwitchOnDiffTemp.'">';

$fileHandleTargetTempDivergence = fopen('/etc/PoolAutomatation/TargetTempDivergence.txt','r');
$fileContTargetTempDivergence = fread($fileHandleTargetTempDivergence, filesize('/etc/PoolAutomatation/TargetTempDivergence.txt'));
fclose($fileHandleTargetTempDivergence);
echo '<br>Erlaubte Ziel-Temperaturabweichung: <input type="text" name="TargetTempDivergence" value="'.$fileContTargetTempDivergence.'">';

$fileHandleStartTime = fopen('/etc/PoolAutomatation/StartTime.txt','r');
$fileContStartTime = fread($fileHandleStartTime, filesize('/etc/PoolAutomatation/StartTime.txt'));
fclose($fileHandleStartTime);
echo '<br>Startzeit: <input type="time" name="StartTime" value="'.$fileContStartTime.'">';

$fileHandleEndTime = fopen('/etc/PoolAutomatation/EndTime.txt','r');
$fileContEndTime = fread($fileHandleEndTime, filesize('/etc/PoolAutomatation/EndTime.txt'));
fclose($fileHandleEndTime);
echo '<br>Endzeit: <input type="time" name="EndTime" value="'.$fileContEndTime.'">';

echo '<br><input type="submit" name="OK" value="OK">' ;
echo '</form>';

echo '</p>';
echo '<p>';
echo 'Zeitpunkt: ';
echo date("d.m.Y H:i:s");
echo '</p>';

$page = $_SERVER['PHP_SELF'];
$refreshinsec = "60";
header("Refresh: $refrehsinsec; url=$page");
echo '<p>';
echo 'Diese Seite aktualisiert sich alle '.$refreshinsec.' Sekunden.';
echo '</p>';
?>

</body>
</HTML>


Auch hier wieder darauf achten, dass die eigenen Sensor-IDs eingesetzt werden. Das Ganze sieht dann so aus:

Anzeige der Webseite

Leider habe ich es bislang nicht hinbekommen, den PH und TDS Wert auf die Webseite zu bringen. Ich kann dem User, unter dem der Webserver läuft nicht die Berechtigung geben auf den Bus zuzugreifen. Leider reicht das hier nicht.

chmod 755 ReadPH.py ReadTDS.py CQRobot_ADS1115.py
chmod 664 /dev/i2c-1

Man könnte per cron die Werte zyklisch auf die Speicherkarte schreiben. Allerdings versuche ich Schreibzugriffe auf die Karte so gering wie möglich zu halten um das Leben der Karte nicht unnötig zu verkürzen. Auch hier werde ich berichten, sobald ich weiß wie es geht.

Wie man auf der Ausgabe der Webseite sieht habe ich zudem ein Problem mit einem Sensor. Ich weiß nicht was da genau los ist, aber wenn ich den Sensor anschließe fallen alle anderen Sensoren aus. Ich habe schon recherchiert. Es scheint so zu sein, als dass 1-Wire-Sensoren anfällig sind für Störungen, wenn die Kabel länger werden. Lange Kabel habe ich. Das könnte also schonmal ein Problem sein. Wer schließt aber bis zu 64 Sensoren mit kurzen Kabeln an? So richtig sinnvoll erscheint mir das Konzept nicht. Ich habe schon etwas im Blick um weitere 1-Wire-Busse aufzumachen und das Problem so zu umgehen. Auch hier gibt es ein Update, wenn ich genaueres weiß.

Ich versuche meine Erfahrungen und Fehler so gut es geht hier niederzuschreiben und die verwendeten Artikel mit Links zu versehen, wenn es denn welche gibt. Wenn über die Produktlinks eingekauft wird erhalte ich einen kleinen Teil davon. Für den Käufer ändert sich am Endpreis nichts. Wenn euch der Blog gefällt, würde ich mich freuen, wenn ihr mich auf diese Weise beteiligen würdet.

Schreibe einen Kommentar