Bewässerungsanlage - Steuerung mit FHEM

Sie befinden sich hier:
Smart Home Lösungen
»
Bewässerungsanlage im Eigenbau
»
Steuerung mit FHEM

Automatische Ansteuerung über FHEM

Nachdem ich in den vorherigen Abschnitten step-by-step den Aufbau der Bewässerungsanlage und auch schon die Programmierung des Microcontrollers beschrieben hatte, fehlt eigentlich nur noch die automatische Steuerung über FHEM.

Meine Lösung in FHEM möchte ich in diesem Abschnitt vorstellen.

Steuern und Überwachen der Bewässerungsanlage

Die notwendigen Funktionalitäten in FHEM können in zwei Abschnitte eingeteilt werden. Das ist einerseits die Ansteuerung der Ventile (Relais) über den Microcontroller (Wemos D1 Mini) und andererseits die Ermittlung eines Gießprogrammes über den berechneten Gießindex. Die Überwachung der Anlage kann durch Senden von Informationen auf das Smartphone über Telegram erfolgen.


Ansteuerung der Ventile aus FHEM per MQTT

Anlegen eines MQTT Servers (Broker) in FHEM

Damit FHEM über das MQTT-Protokoll kommunizieren kann, muss ein MQTT-Server vorhanden sein. Das kann z.B. ein öffentlicher MQTT-Broker sein, wie z.B. Mosquitto. Ich bevorzuge lokale Lösungen, um die Abhängigkeiten von externen Systemen zu minimieren. Aus diesem Grund habe ich den in FHEM verfügbaren internen MQTT-Server verwendet.


define mq_Server MQTT2_SERVER 1883 global;
attr mq_Server autocreate simple;

Mit „autocreate simple“ wird ein neues MQTT2-Device in FHEM angelegt, sobald es sich erstmalig mit dem MQTT-Server verbindet.


MQTT Device für Relaissteuerung in FHEM

Mit der erstmaligen Verbindung des MQTT-Clients (Microcontroller) zum MQTT-Server, wurde ein entspr. Device in FHEM angelegt. Dabei wird automatisch eine Readinglist mit den Topics angelegt, die mein Wemos D1 Mini namens IoT3 abonniert (subscribed) hatte.

Da ich über die FHEM-Oberfläche die Ventile bzw. Relais schalten möchte, hatte ich im Anschluss daran manuell ein setList angelegt. Hier der wesentliche Auszug aus der fhem.cfg zu diesem MQTT-Device namens EG_au_RK_1 (ErdGeschoß_aussen_RelaisKasten_1):


define EG_au_RK_1 MQTT2_DEVICE IoT3;
attr EG_au_RK_1 IODev mq_Server;

attr EG_au_RK_1 readingList 
IoT3:/aussen/R1/state:.* R1
IoT3:/aussen/R2/state:.* R2
IoT3:/aussen/R3/state:.* R3
IoT3:/aussen/R4/state:.* R4
IoT3:/aussen/R5/state:.* R5
IoT3:/aussen/R6/state:.* R6
IoT3:/aussen/R7/state:.* R7
IoT3:/aussen/R8/state:.* R8
IoT3:/aussen/P1/state:.* P1
IoT3:/aussen/mode:.* mode

attr EG_au_RK_1 setList 
R1:ON,OFF /aussen/R1/set $EVTPART1
R2:ON,OFF /aussen/R2/set $EVTPART1
R3:ON,OFF /aussen/R3/set $EVTPART1
R4:ON,OFF /aussen/R4/set $EVTPART1
R5:ON,OFF /aussen/R5/set $EVTPART1
R6:ON,OFF /aussen/R6/set $EVTPART1
R7:ON,OFF /aussen/R7/set $EVTPART1
R8:ON,OFF /aussen/R8/set $EVTPART1

Nun lassen sich die Ventile (Relais) einzeln über die FHEM-Oberfläche schalten:

Schalten der Ventile (Relais) über setList

Um die Optik ein wenig aufzuwerten, kann man die einzelnen Relais in eine Readinggroup zusammenfassen:


define rg_Relais readingsGroup EG_au_RK_1:FILTER=NAME=.*:R.;
attr rg_Relais alias Relaiskasten 1;
attr rg_Relais commands { 
"R1.ON" => "set %DEVICE R1 OFF", "R1.OFF" => "set %DEVICE R1 ON",
"R2.ON" => "set %DEVICE R2 OFF", "R2.OFF" => "set %DEVICE R2 ON",
"R3.ON" => "set %DEVICE R3 OFF", "R3.OFF" => "set %DEVICE R3 ON",
"R4.ON" => "set %DEVICE R4 OFF", "R4.OFF" => "set %DEVICE R4 ON",
"R5.ON" => "set %DEVICE R5 OFF", "R5.OFF" => "set %DEVICE R5 ON",
"R6.ON" => "set %DEVICE R6 OFF", "R6.OFF" => "set %DEVICE R6 ON",
"R7.ON" => "set %DEVICE R7 OFF", "R7.OFF" => "set %DEVICE R7 ON",
"R8.ON" => "set %DEVICE R8 OFF", "R8.OFF" => "set %DEVICE R8 ON"
};
attr rg_Relais mapping {
'R1' => 'R1 - Pumpe',
'R2' => 'R2 - Ventil 1',
'R3' => 'R3 - Ventil 2',
'R4' => 'R4 - Ventil 3',
'R5' => 'R5 - Ventil 4',
'R6' => 'R6 - Ventil 5',
'R7' => 'R7 - Ventil 6',
'R8' => 'R8 - Ventil 7'
};
attr rg_Relais nostate 1;
attr rg_Relais valueIcon {
'R1.ON' => 'sani_pump@green', 'R1.OFF' => 'sani_pump@red',
'R2.ON' => 'sani_irrigation@green', 'R2.OFF' => 'sani_irrigation@red',
'R3.ON' => 'sani_irrigation@green', 'R3.OFF' => 'sani_irrigation@red',
'R4.ON' => 'sani_irrigation@green', 'R4.OFF' => 'sani_irrigation@red',
'R5.ON' => 'sani_irrigation@green', 'R5.OFF' => 'sani_irrigation@red',
'R6.ON' => 'sani_sprinkling@green', 'R6.OFF' => 'sani_sprinkling@red',
'R7.ON' => 'sani_sprinkling@green', 'R7.OFF' => 'sani_sprinkling@red',
'R8.ON' => 'sani_sprinkling@green', 'R8.OFF' => 'sani_sprinkling@red'
};

Das Ergebnis sieht wie folgt aus:

Zusammenfassung der Ventile (Relais) in eine Readinggroup

Manueller Start des Gießprogramms über einen Dummy

Der Gießprogramm "P1" wird über ein Dummy gestartet,


define EG_au_GP_1 dummy;
attr EG_au_GP_1 alias Gießprogramm Rabatte;
attr EG_au_GP_1 cmdIcon Viel:weather_rain_heavy Wenig:weather_rain_light Töpfe:weather_pollen Pause:rc_RED;
attr EG_au_GP_1 devStateIcon Viel:weather_rain_heavy@green Wenig:weather_rain_light@green Töpfe:weather_pollen@green Pause:general_aus@red;
attr EG_au_GP_1 icon sani_irrigation;
attr EG_au_GP_1 setList Viel Wenig Töpfe Pause;
attr EG_au_GP_1 webCmd Viel:Wenig:Töpfe:Pause;

welches ein Notify triggert:


define ny_EG_au_GP_1 notify EG_au_GP_1.* {
								// Merken des Zisternenfüllstandes, um daraus im Anschluß die Gießmenge zu ermitteln
	if ($EVENT ne "Pause") {	// EG_au_LS_1 = Levelsensor aka Pegelsonde
		my $VolumePrev = ReadingsVal("EG_au_LS_1","Volume","0");
		fhem("setreading EG_au_LS_1 VolumePrev $VolumePrev");
	}

	if ($EVENT eq "Viel") {
		fhem("set mq_Server publish /aussen/P1/set ".ReadingsVal("du_Giessen_Logik","viel_plan","0|0|0"));
	} elsif ($EVENT eq "Wenig") {
		fhem("set mq_Server publish /aussen/P1/set ".ReadingsVal("du_Giessen_Logik","wenig_plan","0|0|0"));
	} elsif ($EVENT eq "Töpfe") {
		fhem("set mq_Server publish /aussen/P1/set ".ReadingsVal("du_Giessen_Logik","toepfe_plan","0|0|0"));
	} else {
		fhem("set mq_Server publish /aussen/P1/set ".ReadingsVal("du_Giessen_Logik","pause_plan","0|0|0"));
	}
}

Die hinterlegte Gießdauer wird aus Readings des Dummy "du_Giessen_Logik" gelesen und als Payload bereitgestellt. Da der Wemes D1 Mini (MQTT-Client) diese Topics subscribed hat, reagiert er darauf und startet das entspr. Gießprogramm. Die Logik, die sich hinter den übergebenen Werten verbirgt, hatte ich im Abschnitt Programmierung des Microcontrollers (Callback-Funktion) ausführlich beschrieben. Hier ein Auszug aus dem Dummy, welches die Parameter enthält:


define du_Giessen_Logik dummy;
setreading du_Giessen_Logik viel_plan 8|8|5;
setreading du_Giessen_Logik wenig_plan 5|5|3;
setreading du_Giessen_Logik toepfe_plan 0|0|3;
setreading du_Giessen_Logik pause_plan 0|0|0;
 

An der Oberfläche sieht das wie folgt aus:

Start Gießprogramm über Dummy

Automatischer Start des Gießprogramms über ein at-Befehl

Der automatische Start des Gießprogramms zu einer bestimmten Uhrzeit kann über ein at-Befehl ausgelöst werden. Damit wird eine Perl-Funktion aufgerufen, in der - je nach Gießindex - das jeweilige Gießprogramm ausgewählt und gestartet wird.


define at_Giessen at *05:15:00 {myGiessenUtils_at_Giessen()}

In myGiessenUtils_at_Giessen() erfolgt ein abschließender Test, ob es denn aktuell gerade regnet. In diesem Fall würde das Gießen ausfallen. Des Weiteren wird über myGiessenUtils_lastCheck() geprüft, ob zwischen dem Zeitpunkt der Ermittlung des Gießindex (am Vorabend) und dem Beginn der Gießaktion (am frühen Morgen) sich irgendetwas an den Bedingungen geändert hat. Wenn dies nicht der Fall ist, dann kann das Gießen beginnen.

Für die Verlegung des Starts in die frühen Morgenstunden gab es zwei Gründe:

  • Das Gießen in den Morgenstunden wird allgemein empfohlen, da abends durch den noch warmen Boden eine höhere Verdunstung erfolgt. Dadurch ist eine Abend-Gießung nicht so effektiv.
  • Durch abendliches Gießen entsteht ein kühles Mikroklima, was zu vehementen Protesten meiner Frau geführt hatte. Schließlich wollte sie die lauen Sommerabende auf der Terrasse genießen und nicht durch die einsetzende Kühle als Folge des Gießens nach Innen vertrieben werden.

sub myGiessenUtils_at_Giessen(){

	my $State = "";

	if (ReadingsVal("du_Giessen_Auto","state","off") eq "on") {
	
		if (ReadingsVal("EG_au_MS_1","isRaining","no") eq "yes") {
			myGiessenUtils_resetValues("Pause");
			myGiessenUtils_sendMessage("Es regnet gerade => Giessen fällt aus");
			return;
		}

		if (myGiessenUtils_lastCheck() != 0){
			$State = ReadingsVal("du_Giessen_Logik","state","Pause");
			fhem("set EG_au_GP_1 $State");
		}
	}
}

Damit das Gießprogramm nicht Sommer wie Winter täglich zu der eingestellten Zeit versucht zu starten, ist es sinnvoll, einen Dummy zu definieren, worüber man die Automatik generell ein- bzw. ausschalten kann. Das kann über die Oberfläche oder mit einen weiteren Dummy erfolgen, über den zwischen Sommer- und Winterbetrieb umgeschaltet wird oder der at-Befehl deaktiviert bzw. aktiviert werden kann. Ich schalte den Dummy direkt über die Oberfläche, da ich im Herbst nicht nur die Automatik in FHEM, sondern auch den Relaiskasten manuell ausschalte. Der Relaiskasten muss nicht den ganzen Winter über (Standby)-Strom verbrauchen, wenn sowieso nicht gegossen wird.


define du_Giessen_Auto dummy;
attr du_Giessen_Auto alias Automatik Rabatte;
attr du_Giessen_Auto cmdIcon an:rc_GREEN aus:rc_RED;
attr du_Giessen_Auto devStateIcon an:general_an@green aus:general_aus@red;
attr du_Giessen_Auto eventMap on:an off:aus;
attr du_Giessen_Auto icon fts_shutter_automatic;
attr du_Giessen_Auto webCmd an:aus;

An der Oberfläche sieht das wie folgt aus:

Start Gießautomatik über Dummy

Statusabfrage aller Relais in FHEM

Die Statusabfrage aller Relais kann ebenfalls über ein Dummy erfolgen, dass ein Notify triggert:


define EG_au_RK_STS dummy;
attr EG_au_RK_STS alias Ventil (Relais) Status;
attr EG_au_RK_STS cmdIcon STS:rc_INFO@yellow;
attr EG_au_RK_STS icon sani_sprinkling;
attr EG_au_RK_STS setList STS;
attr EG_au_RK_STS webCmd STS;

define ny_EG_au_RK_STS notify EG_au_RK_STS.*:* set mq_Server publish /aussen/STS/get;

An der Oberfläche sieht das wie folgt aus:

Statusabfrage aller Relais über Dummy

Versendung Statusinfo per Telegram, wenn MQTT Client online oder offline geht

Durch die Hinterlegung einer LastWill-Message kann die Zustandsänderung (mode) online vs. offline des MQTT-Clients über ein Notify per Telegram auf ein Smartphone gesendet werden. Das setzt natürlich voraus, dass ein Telegram-Bot in FHEM vorher konfiguriert wurde.


define ny_EG_au_RK_1_Mode notify EG_au_RK_1:mode:.* {
	fhem ("set tg_Bot message $NAME $EVTPART1");
}

Restart MQTT-Client über HTTP-Request

Wie zuvor beschrieben, wollte ich mir noch die Möglichkeit einräumen, den MQTT-Client per HTTP-Request zu starten. Dies kann notwendig werden, wenn der MQTT-Client bei einem Verbindungsabbruch den automatischen Reconnect - aus welchen Gründen auch immer - nicht schafft. In diesem Fall greife ich per Webserver über FHEM-HTTPMOD auf den MQTT-Client zu.

Hierbei wird die URL http://192.168.0.40/restart aufgerufen. Wenn als Response auf der Webseite als Text „OK“ steht, dann läuft der Webserver und der MQTT-Client wurde erfolgreich gestartet. Um den Response der Website visuell darzustellen, werte ich hierbei das Internal „httpbody“ über ein userReading aus.


define EG_au_WS_1 HTTPMOD none 0;
attr EG_au_WS_1 alias Relaiskasten 1 Webserver;
attr EG_au_WS_1 cmdIcon Restart:rc_YELLOW;
attr EG_au_WS_1 devStateIcon OK:general_an@green FAILED:general_aus@red;
attr EG_au_WS_1 icon rc_WEB;
attr EG_au_WS_1 set01Name Restart;
attr EG_au_WS_1 set01NoArg 1;
attr EG_au_WS_1 set01URL http://192.168.0.40/restart;
attr EG_au_WS_1 showBody 1;
attr EG_au_WS_1 timeout 5;
attr EG_au_WS_1 userReadings state {(InternalVal($name,"httpbody","") eq "OK")?"OK":"FAILED"};
attr EG_au_WS_1 webCmd Restart;

An der Oberfläche sieht es wie folgt aus:

Restart MQTT-Client über HTTP-Request

Ermittlung des Gießindex

Auf die Bedeutung des Gießindex bin ich bereits im Abschnitt Entwurf der Steuerungslogik ausführlich eingegangen. Die konkrete Ermittlung des Gießindex erfolgt über einen at-Befehl, der eine Procedure mit der Gießlogik aufruft. Alle Schwellwerte wurden in Readings einer Dummy-Variable hinterlegt, damit sie bei Bedarf angepasst werden können, ohne direkt am Sourcecode Änderungen vornehmen zu müssen.

Der at-Befehl triggert zwar stündlich. Die Abfrage der Parameter auf denen der Gießindex basiert (Außentemperatur und Ertrag der PV-Anlage), erfolgt aber nur zwischen 09:00 und 18:00Uhr. Um 20:00Uhr erfolgt der Vorschlag des Gießprogrammes (Viel, Wenig, Töpfe, Pause) unter Einbeziehung weiterer Parameter (Füllstandänderung Zisterne, Regendauer). Das ermittelte Gießprogramm wird per Telegram auf mein Smartphone gesendet und kann bei Bedarf noch bis zum Start des Gießens angepasst werden.

Hier die wesentlichen Parameter für die Gießlogik:


define at_Giessen_Logik at +*01:00:00 {myGiessenUtils_at_Giessen_Logik($hour)};


define du_Giessen_Logik dummy;

setreading du_Giessen_Logik energy_cum 0;		# kumulierte Energie
setreading du_Giessen_Logik energy_limit 30;	# Schwellwert Energie
setreading du_Giessen_Logik temp_cum 0;			# kumulierte Temperatur
setreading du_Giessen_Logik temp_limit 20;		# Schwellwert Temperatur
setreading du_Giessen_Logik pause 0;			# Anzahl Tage Giesspause
setreading du_Giessen_Logik index 0;			# aktuell ermittelter Gießindex
setreading du_Giessen_Logik index_prev 0;		# gemerkter Gießindex vom Vortag
setreading du_Giessen_Logik toepfe_limit 25;	# Schwellwert Gießprogramm Töpfe
setreading du_Giessen_Logik wenig_limit 50;		# Schwellwert Gießprogramm Wenig
setreading du_Giessen_Logik viel_limit 100;		# Schwellwert Gießprogramm Viel

Das Reading energy_limit wird automatisch per at-Befehl am ersten Tag eines Monats festgelegt. Um diesen Schwellwert zu ermitteln, wird auf den monatlichen PV-Durchschnittsertrag der letzten Jahre x 0,75 / 30 Tage zurückgegriffen.


define at_EnergyLimit at *00:00:01 {if ($mday == 1) {myGiessenUtils_setEnergyLimit($month)}};


sub myGiessenUtils_setEnergyLimit($){
	my($curmonth) = @_;
	my $value = ReadingsVal("du_Energy_Stat","M_Mittel_".sprintf("%02d",$curmonth),"0" );
	my @fld="";
	my $energy_limit = 30;
	if ($value ne "0"){
		@fld = split(" ", $value);
		$energy_limit = round($fld[0]/30*0.75,0);
	}
	fhem("setreading du_Giessen_Logik energy_limit $energy_limit");
}

Nach dem Gießen wird wiederum eine Telegram-Message auf das Smartphone gesendet, welches das ausgeführte Gießprogramm und die vergossenen Wassermenge mitteilt. Anhand von Schwankungen in der Gießmenge kann in etwa erahnt werden, ob es verstopfte Sprühdüsen gibt oder die Anlage fehlerfrei läuft.

Da meine Pegelsonde aus Energiespargründen nicht permanent Messwerte liefert, sondern nur alle 15 Minuten auf Füllstandsänderung prüft, kann die Füllstandsänderung nicht sofort nach Beendigung des Gießvorganges ermittelt und gesendet werden. Deshalb wird mit dem Ende des Gießvorgangs erstmal nur in einem Reading ein Flag als Indikator gesetzt, dass das Gießen beendet wurde. Nach Ablauf des 15 minütigen Intervalls wird (bei gesetztem Flag) die Volumenänderung ermittelt und versendet. Zum Abschluss wird sich das aktuelle Zisternenvolumen als Ausgangswert für den nächsten Gießvorgang gemerkt.


fhem ("setreading du_Giessen_Logik Finished 1");

define ny_EG_au_LS_1 notify EG_au_LS_1:Volume.* {
 if (ReadingsVal("du_Giessen_Logik","Finished",0) == 1){
 	my $VolumePrev = ReadingsVal("EG_au_LS_1","VolumePrev",5000);
	myGiessenUtils_sendMessage(($VolumePrev-$EVTPART1)." Liter");

	fhem("setreading EG_au_LS_1 VolumePrev $EVTPART1");
	fhem("setreading du_Giessen_Logik Finished 0");
 }
}

Gießlogik vs. Gießen

Gießlogik und das eigentliche Gießen sind strikt getrennt, d.h. der Gießindex wird über die Readings von du_Giessen_Logik ermittelt. Das eigentliche Gießen erfolgt über du_Giessen. Somit kann auch völlig losgelöst von der Gießlogik ein Gießvorgang über die FHEM Oberfläche ausgelöst werden.

Zum Abschluss hier noch das komplette Perl-Modul, welches die Gießlogik enthält:


Kontakt

Senden Sie mir Ihre Fragen oder Anregungen über die Kontaktbox oder direkt per Email. Sie können mich natürlich auch über die gängigen sozialen Netze erreichen.

kontakt@kaempf-nk.de

Fragen / Anregungen?

Sicherheitsabfrage:
Datenschutzhinweis: Die eingegebenen Daten werden nicht an Dritte weitergegeben.