Die Datenleitungen der Feuchtigkeitssensoren verbindet man 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 Python-Programm aus Listing 1 schaltet einen Sensor ein und liest den Wert aus.
Listing 1
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 Spannungsversorgung 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 Plus-Minus-Spur des Breadboards.
Das Relaismodul benötigt 3,3 Volt und Masse sowie für jedes zu steuernde Relais ein Kabel an einem GPIO. Die Stromversorgung erfolgt über das entsprechende Modul, 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 Plus-Pol 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.

Abbildung 4: Der Aufbau der Anlage für einen einzelnen Blumentopf.
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.
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, gilt die offizielle Pflege-Empfehlung: “Einmal am Tag gießen, im Winter braucht die Pflanze weniger.” Daraus leitete der Autor den in Listing 2 dargestellten Gießalgorithmus ab. 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 2
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
Der Autor entwickelte auf dieser Basis den Python-Code aus Listing 3, der in mehreren Threads läuft. Ein Thread bedient dabei jeweils einen Blumentopf mit Sensor und Relais. Die Konfigurationsdatei config.yml in Listing 4 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 3
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'])
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
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'])





