Beim Autor dieses Artikels stehen drei Blumentöpfe auf der Fensterbank im Büro. Ein Pi Zero W, ein A/D-Wandler und ein preiswertes Bewässerungskit sorgen dafür, dass die Pflanzen keinen Durst leiden müssen.
Auch inspiriert durch einen Artikel von Charly Kühnast [1], spukte dem Autor schon länger die Idee durch den Kopf, per Computer die Feuchtigkeit seiner drei Topfpflanzen im Büro zu messen, um sie bei Bedarf automatisch zu gießen. Als er bei der Recherche im Online-Handel auf ein preisgünstiges Kit aus Sensoren und Pumpen stieß, war der Zeitpunkt gekommen, das Thema anzugehen.
Die erste Idee zum Bewässern der drei Blumentöpfe war, eine Pumpe zu verwenden und über ein Ventilsystem zu regulieren, welcher Topf Wasser erhält. Online-Recherchen förderten allerdings keine preisgünstigen Systeme zutage; das Thema landete also erst einmal wieder in der Schublade.
Eine spätere Suche endete bei einem Bewässerungskit der chinesischen Firma Wayintop, das für weniger als 30 Euro vier einzelne Pumpen, vier Feuchtigkeitssensoren, ein Relaismodul und einen passenden Schlauch enthält [2]. Das fiel in den Preisrahmen, der dem Autor vorschwebte.
Die Steuerung sollte ein Raspberry Pi übernehmen. Was der Autor allerdings in seiner Vorfreude überlas: Die Sensoren des Wayintop-Kits liefern Analogsignale. Das stellt beim vom Hersteller vorgesehenen Einsatz mit einem Arduino Uno kein Problem dar, die Verwendung an einem Raspberry Pi erfordert aber noch einen Analog-Digital-Wandler. Die Wahl fiel hier auf einen MCP3008 [3], der nur um die 2,50 Euro kostet.
Teile und herrsche
Um das geplante Szenario für die automatische Bewässerung umzusetzen, ergaben sich die folgenden drei Teilaufgaben:
- Die Hardware so aufbauen, dass die Sensoren Messwerte liefern und sich die Pumpen einzeln ansteuern lassen.
- Die Messwerte erfassen und verstehen. Letzteres heißt zum Beispiel, zu ermitteln, in welcher Weise das Gießen die Messwerte verändert.
- Einen Algorithmus ableiten, der ein automatisches Gießen erlaubt.
Der RasPi muss dabei zwei Geräteklassen steuern: die Sensoren, die Daten über die Feuchtigkeit liefern, sowie die Pumpen, die der RasPi nach Bedarf an- und ausschaltet.
Neben dem Steuern und dem Auslesen der Messwerte gehört zu Schritt 1 auch der Aufbau der Stromversorgung. Die Pumpen und Sensoren benötigen lediglich 3,3 Volt, die ein Raspberry Pi liefern kann. Daneben braucht auch der A/D-Wandler Spannung, ebenso wie die Steuerung des Relaismoduls.
Prinzipiell könnte der Raspberry Pi die Pumpen direkt mit Strom versorgen. Dazu verbindet der Admin den Masseanschluss der Pumpen mit dem Masse-Pin des RasPi. Den Pluspol der Pumpen steckt er in diesem Fall an je einen GPIO-Port des RasPi (General Purpose IO). Auf diese Weise lassen sich die Pumpen an- und abschalten. Allerdings ziehen ihre Motoren beim Anlaufen nicht unbedingt gleichmäßig viel Strom. Daher erscheint die Variante geeigneter, die Pumpen mit einer externen Stromversorgung zu steuern, die das Relaismodul bereitstellt.
Die Sensoren benötigen ebenfalls 3,3 Volt Spannung, um zu funktionieren. Hier muss der Elektrogärtner bedenken, dass je nach Qualität und Beschichtung der Sensoren das permanente Unter-Strom-Halten dazu führt, dass sie korrodieren. Das passiert, weil in der Erde eine chemische Reaktion stattfindet. Die Messung der Feuchte über einen Widerstand verursacht zugleich eine Elektrolyse. Daher ist es durchaus sinnvoll, die Sensoren nur dann mit Strom zu versorgen, wenn sie auch Messdaten liefern sollen. Wie sich in der Praxis zeigte, ist das gar nicht so häufig der Fall – dazu später mehr.
Beim Aufbau und Testen kam ein Breadboard zum Einsatz. Um das ganze Konstrukt möglichst sparsam umzusetzen, fiel die Wahl zudem auf einen Pi Zero W als Steuerrechner (Abbildung 1).
![<a href="#artRef-f1">Abbildung 1</a>: Der detaillierte Steckplan zum Aufbau der Bewässerungsanlage und alle Listings des Artikels warten auf der DELUG-DVD und online <a href="#artRef-i6">[6]</a>.](/wp-content/uploads/2020/05/b01_fullsketch_v2_steckplatine-206x300.jpg)
Abbildung 1: Der detaillierte Steckplan zum Aufbau der Bewässerungsanlage und alle Listings des Artikels warten auf der DELUG-DVD und online [6].
Hands on
Im ersten Schritt ging es darum, Sensor und A/D-Wandler anzuschließen. Der MCP3008 besitzt Anschlüsse auf zwei Seiten. Die der einen dienen zum Ansteuern und für die Stromversorgung, die der anderen beherbergen die Eingangskanäle. Abbildung 2 verdeutlicht die Verschaltung des MCP3008 mit dem Raspberry Pi.

Abbildung 2: Der Admin schließt den Raspberry Pi zunächst an den A/D-Wandler MCP3008 an.
Das Ansteuern des A/D-Wandlers erfolgt über das SPI-Interface, das der Admin dazu über Raspi-config aktivieren muss. Alternativ wählt er manuell die passenden Kernel-Parameter für den Bootloader aus und startet den Raspberry Pi neu.
Im nächsten Schritt verbindet der Admin die Daten-Ports des MCP3008 mit den passenden Pins des RasPi [4] – nicht alle GPIOs stellen SPI-Funktionen bereit. Die doppelte Erdung (GND) und die 3,3 Volt, die der Wandler benötigt, holt er sich vom Breadboard. Das erweitert den Pi Zero W, um nicht zu viele Kabel und Pins zu verbrauchen.
Die Datenleitungen der Feuchtigkeitssensoren verbindet der Admin nun der Reihe nach mit den Eingangskanälen des A/D-Wandlers. Die Erdung holen sich die Sensoren über die Masseanschlüsse des RasPi. Die Spannung von 3 Volt beziehen sie über die GPIOs 4, 17 und 22, damit der Raspberry sie vor der Messung an- und danach abschalten kann.
Nun steht ein erster Test an: Das einfache Programm aus Listing 1 schaltet einen Sensor ein und liest den Wert aus [5].
Listing 1
hummon2.py
# Testskript für Sensoren
import datetime
import time
import Adafruit_GPIO.SPI as SPI
import Adafruit_MCP3008
import RPi.GPIO as GPIO
SPI_PORT = 0
SPI_DEVICE = 0
mcp = Adafruit_MCP3008.MCP3008(spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE))
port=4
GPIO.setmode(GPIO.BCM)
GPIO.setup(port, GPIO.OUT)
while True:
GPIO.output(port,1)
time.sleep(5)
hum = mcp.read_adc(0)
print (str(datetime.datetime.now())+\
"Sensor: "+str(hum))
GPIO.output(port,0)
time.sleep(55)
Die Stromversorgung der Pumpen und des Relaismoduls erfolgt über ein getrenntes Stromversorgungsmodul. Das steckt auf dem Breadboard (Abbildung 3) und erhält seinen Eingangsstrom über ein Netzteil oder per USB. Im Aufbau mit dem Breadboard ist der 5-Volt-Ausgang abgeschaltet, und 3,3 Volt liegen auf der zweiten »+-«-Spur des Breadboards.
Das Relaismodul benötigt 3,3 Volt und Masse sowie ein Kabel an einem GPIO für jedes zu steuernde Relais. Die Stromversorgung erfolgt über das Stromversorgungsmodul, drei Steuerleitungen gehen an den RasPi.
Der Masseanschluss der Pumpen liegt auf der Masse des Stromversorgungsmoduls auf dem Breadboard (Abbildung 1, grüne Leitungen). Der Pluspol jeder Pumpe liegt auf dem mittleren Pol des jeweiligen Relais. Vom rechten Verbinder des Relais geht eine Leitung zur 3,3-Volt-Leitung des Stromversorgungsmoduls. Den kompletten Schaltplan zeigt Abbildung 3.

Abbildung 3: Der Schaltplan für die Verbindung der Pumpen mit dem Relaismodul.
Zum Anschalten des Relais setzt ein Programm den verbundenen GPIO-Port auf »0«, zum Abschalten auf »1«. Abbildung 4 zeigt den Aufbau für einen einzelnen Sensor und eine einzelne Pumpe.
Die Software
Das Steuern der Bewässerung entpuppt sich als Geduldsprobe für Entwickler: Wie sich zeigt, verläuft die Wechselwirkung zwischen der Bewässerung und einer messbaren Änderung der Bodenfeuchte nicht wirklich binär.

Abbildung 4: Der Aufbau der Anlage für einen einzelnen Blumentopf.
Tatsächlich misst der Sensor – abhängig von der Position des Schlauchendes und des Sensors im Topf – mal mehr Feuchte, als der Pflanze zur Verfügung steht, und mal weniger. Ein Algorithmus, der unbekümmert weiter gießt, bis der Sensor Feuchtigkeit meldet, ertränkt die Pflanze womöglich. Zugleich sollte der digitale Gärtner berücksichtigen, dass unterschiedliche Pflanzen unterschiedlich viel Durst haben.
Aber zurück auf Anfang: Die mit dem Programm aus Listing 1 ermittelten Messwerte des Sensors liefern den Startpunkt. Bei trockener Erde ergibt sich ein Rückgabewert im Bereich um 840. Hängt der Sensor dagegen im Wasser, liegt der Wert bei 500. Nun gilt es, das Verhalten des Sensors zu erkunden, wenn der Blumentopfeigner gießt, während der Sensor im Boden steckt und ein Programm die Werte regelmäßig ausliest.
Im Aufbau steckte der Autor den Sensor zunächst an den Rand des Blumentopfs und ließ den Schlauch in der Mitte enden. Nachdem die Pumpe für fünf Sekunden lief, sprang der Messwert auf 620. Das Wasser versickerte allerdings schnell: Bereits nach wenigen Minuten lag der Wert wieder bei 820, zeigte also Trockenheit an. Nach zwei weiteren Pumpstößen von fünf Sekunden pendelte sich der Messwert um 800 ein und fiel dann vorerst nicht mehr weiter ab.
Für den Ficus, an dem die Experimente stattfanden, gibt es die offizielle Pflegeempfehlung: “Einmal am Tag gießen, im Winter braucht die Pflanze weniger”. Daraus leitete der Autor den in Listing 2 dargestellten Algorithmus ab.
Listing 2
Gießalgorithmus für den Ficus
Miss ein Mal pro Zeiteinheit die Feuchte.
Ist der Messwert größer als der Schwellwert, dann:
Gieße für X Sekunden.
Miss nach Y Sekunden erneut (Y ist kleiner als Zeiteinheit).
Ist der Schwellwert nicht erreicht, dann:
Gib einen zusätzlichen Stoß Wasser
Goto 01
Pro Blumentopf muss das Programm dabei die Werte für Zeiteinheit, Schwellwert, X und Y individuell einstellen und am besten aus einer Konfigurationsdatei lesen.
Listing 3
Die vollständige Steuerung (Teil 1)
import datetime
import time
import Adafruit_GPIO.SPI as SPI
import Adafruit_MCP3008
import RPi.GPIO as GPIO
import threading
import yaml
import pprint
import smtplib
from influxdb import InfluxDBClient
class PotThread(threading.Thread):
# args ist ein pot-dict
def __init__(self, group=None, monitoronly=False, influxclient=None, target=None, threadname=None, debug=None, args=()):
threading.Thread.__init__(self, group=group, target=target, name=threadname)
if 'pot' in args:
self.potconfig=args['pot']
else:
self.potconfig={}
if threadname:
self.threadname=threadname
else:
if self.potconfig and "name" in self.potconfig:
self.threadname = self.potconfig['name']
else:
self.threadname = "Unknown"
self.debug = debug
self.active = True
self.influxclient = influxclient
self.monitoronly = monitoronly
def run(self):
measurements = []
while True:
if self.active:
humidity = self.get_sync_humidity(self.potconfig['sensorchannel'], self.potconfig['sensorgpio'])
if self.debug:
print (str(datetime.datetime.now())+" "+self.threadname+" Humidity: "+str(humidity))
if self.influxclient:
measurement = {
'measurement': 'humidity',
'tags': {
'name': self.threadname
},
'time' : time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
'fields': {
'level':humidity
}
}
measurements.append(measurement)
try:
self.influxclient.write_points(measurements)
measurements=[]
except:
print ("Influx failed for "+self.threadname)
if humidity > int(self.potconfig['schwellwert']):
if self.debug:
print (str(datetime.datetime.now())+" "+self.threadname+" Pump on ")
self.pump_on(self.potconfig['pumpsekunden'], self.potconfig['relaisgpio'])
if self.debug:
print (str(datetime.datetime.now())+" "+self.threadname+" Pump off ")
time.sleep(self.potconfig['messpause2'])
humidity2 = self.get_sync_humidity(self.potconfig['sensorchannel'], self.potconfig['sensorgpio'])
if humidity2 > int(self.potconfig['schwellwert2']):
if self.debug:
print (str(datetime.datetime.now())+" "+self.threadname+" Pump on ")
self.pump_on(self.potconfig['pumpsekunden2'], self.potconfig['relaisgpio'])
if self.debug:
print (str(datetime.datetime.now())+" "+self.threadname+" Pump off ")
time.sleep(self.potconfig['messpause'])
[...]
Der Autor entwickelte auf dieser Basis den Code in den Listings 3 bis**5, der in mehreren Threads läuft. Ein Thread bedient dabei jeweils einen Blumentopf mit Sensor und Relais. Die Konfigurationsdatei in Listing 6 enthält neben den Zeit- und Schwellwerten auch die GPIO-Ports für das Ein- und Ausschalten der Relais sowie die Kanäle, an denen am A/D-Wandler die Sensoren hängen.
Listing 4
Die vollständige Steuerung (Teil 2)
[...]
def pump_on(self, seconds, gpio):
if not self.monitoronly:
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(gpio, GPIO.OUT)
GPIO.output(gpio, 0)
time.sleep(int(seconds))
GPIO.output(gpio, 1)
def get_sync_humidity(self, sensorchannel, sensorgpio):
SPI_PORT = 0
SPI_DEVICE = 0
lock = threading.RLock()
lock.acquire()
mcp = Adafruit_MCP3008.MCP3008(spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE))
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(sensorgpio, GPIO.OUT)
GPIO.output(sensorgpio, 1)
time.sleep(3)
hum = mcp.read_adc(sensorchannel)
GPIO.output(sensorgpio, 0)
mcp._spi.close()
lock.release()
return(hum)
def set_active(self, active):
if self.debug:
print (self.threadname+" Setting active to: "+str(active))
self.active = active
[...]
Fazit
Für alle drei Töpfe muss der Autor am Ende noch empirisch die Schwellwerte aller Kombinationen aus Pflanze und Füllung des Topfs (Erde, Substrat) ermitteln (Listing 6). Ein vierter Sensor, den Abbildung 1 nicht auflistet, kommt schließlich in den Wassertank. Ihn überwacht ein eigener Thread, das rote Kabel steckt der Admin an einen weiteren freien GPIO-Port des Raspberry Pi Zero [5]. Fehlt im Tank das Wasser, sendet das Skript eine E-Mail und blockiert die anderen Threads, bis der Admin sich bequemt, den Tank wieder aufzufüllen.
Listing 5
Die vollständige Steuerung (Teil 3)
[...]
if __name__ == '__main__':
configfile = open("config.yml", "r")
configyml = configfile.read()
configfile.close()
config=yaml.load(configyml, Loader=yaml.Loader)
influxclient = None
if "influx" in config:
influxclient = InfluxDBClient(config['influx']['server'], 8086, config['influx']['user'], config['influx']['password'], config['influx']['database'])
debug = config['debug']
monitoronly = False
if "monitoronly" in config:
monitoronly = config["monitoronly"]
children = []
for pot in config['pots']:
pt = PotThread(debug=debug, influxclient=influxclient, monitoronly=monitoronly, args=(pot))
pt.start()
children.append(pt)
tankpot = {}
tankthread = PotThread(monitoronly=True, debug=debug, args=(tankpot))
while True:
tankhum = tankthread.get_sync_humidity(config['tank']['sensorchannel'], config['tank']['sensorgpio'])
if debug:
print ("Tank: "+str(tankhum))
if tankhum > config['tank']['schwellwert']:
mailserver = smtplib.SMTP(config['mail']['server'])
mailserver.sendmail(config['mail']['from'], config['mail']['to'], "Please fill water tank")
mailserver.quit()
print ("Please fill tank")
for pot in children:
pot.set_active(False)
else:
for pot in children:
pot.set_active(True)
time.sleep(config['tank']['messpause'])
Die Daten für den Tanksensor finden sich in der YAML-Datei im Block »tank«, die E-Mail-Parameter stehen im Block »mail«. Je nach Konfiguration des lokalen E-Mail-Systems sollte der Admin den Code anpassen, sodass er eine ausführlichere E-Mail mit sauberem Betreff enthält, damit die Nachricht nicht im Spam landet. Die Konfigurationsdatei enthält zudem den Parameter »debug«, der das Skript im Ablauf gesprächiger macht.
Für größere Bepflanzungen, wie einen Blumenkasten, müssten pro Kasten mehrere Sensoren und Wasserzuführungen zum Einsatz kommen. Hier wäre dann wieder etwas Experimentieren angesagt, um die gewünschte durchschnittliche Bodenfeuchte zu erreichen. Die Messwerte tragen die Skripte zugleich in eine Influx-Datenbank ein. Auf deren Basis verschafft sich der Admin dann mittels Grafana eine Übersicht, um die Parameter nach der Analyse auf längere Sicht noch besser anzupassen.(kki/jlu)
Der Autor
Konstantin Agouros arbeitet als Head of Open Source & AWS Projects bei der Matrix Technology AG und berät dort mit seinem Team Kunden zu den Themen Open Source, Sicherheit und Cloud. Sein Buch “Software Defined Networking: Praxis mit Controllern und OpenFlow” ist bei De Gruyter erschienen.
Listing 6
Die Konfigurationsdatei config.yml
pots:
- pot:
sensorchannel: 1
sensorgpio: 19
pumpsekunden: 15
pumpsekunden2: 5
schwellwert: 825
schwellwert2: 805
messpause: 60
messpause2: 1200
relaisgpio: 16
name: ficus
- pot:
sensorchannel: 2
sensorgpio: 26
pumpsekunden: 10
pumpsekunden2: 5
schwellwert: 825
schwellwert2: 785
messpause: 60
messpause2: 1200
relaisgpio: 20
name: orchid
- pot:
sensorchannel: 3
sensorgpio: 13
pumpsekunden: 10
pumpsekunden2: 5
schwellwert: 825
schwellwert2: 785
messpause: 60
messpause2: 1200
relaisgpio: 21
name: dickblatt
tank:
sensorchannel: 0
sensorgpio: 17
messpause: 7200
schwellwert: 830
mail:
server: 1.2.3.4
from: watering@local
to: gaertner@local
influx:
server: 2.3.4.5
user: garten
password: gartenpw
database: gartendb
debug: True
monitoronly: True
Infos
-
Einführung: Charly Kühnast, “Nächste Station: Gießen”, LM 06/2015, S. 51, https://www.linux-magazin.de/34672
-
Bewässerungsanlage: https://www.amazon.de/WayinTop-Automatische-Bew%C3%A4sserung-Bew%C3%A4sserungssystem-Bodenfeuchtesensor/dp/B07TJQSHR2/
-
Analog-Digital-Wandler: https://www.conrad.de/de/p/microchip-technology-mcp3008-i-p-datenerfassungs-ic-analog-digital-wandler-adc-extern-pdip-16-651456.html
-
Raspberry Pi GPIOs: https://www.raspberrypi.org/documentation/usage/gpio/
-
AnalogPython-Bibliothek von Adafruit: https://github.com/adafruit/Adafruit_Python_MCP3008
-
Steckplan und Dateien zum Artikel: http://www.linux-magazin.de/static/listings/magazin/2020/05/iot






