Garnicht.
Wenn es danach geht, könnte man auch einen externen Trockenlaufschutz direkt an der Pumpe installieren.
Schwimmerschalter wären dafür geeignet.
Garnicht.
Wenn es danach geht, könnte man auch einen externen Trockenlaufschutz direkt an der Pumpe installieren.
Schwimmerschalter wären dafür geeignet.
GPIO's täglich zu bestimmter Uhrzeit ein, bzw. Ausschalten? Schau mal ob du hier fündig wirst!
Hallo an alle,
erst mal vielen Dank für die vielen Tipp's und Rückmeldungen.
Ich habe mir jetzt in Bezzo's Kleinwarenladen folgendes Relais [Anzeige] bestellt.
Und folgenden Code geschrieben:
from gpiozero import LED
from time import sleep
motorschalter = LED(27)
entwaesserungsventil = LED(23)
entlueftungsventil = LED(26)
temp=-4 #Der Wert soll noch durch die Online-Temperaturmessung erweitert werden
if temp <= 0:
motorschalter.on()
sleep(0.1) #kurzer Impuls reicht ja ....
entwaesserungsventil.on()
entlueftungsventil.on()
print("Entleere Wasserleitung ...")
sleep(600) # Die Ventile müssen ja nicht den ganzen Winter offen sein, es soll ja nur das Wasser ablaufen, d.h. 5 min sollten reichen
print("Winterbetrieb")
else:
motorschalter.off()
sleep(0.1) #kurzer Impuls reicht ja ....
entwaesserungsventil.off()
entlueftungsventil.off()
sleep(1)
print("Sommerbetrieb")
Alles anzeigen
so sollte es doch eigentlich funktionieren.
Vlt. Arbeite ich auch mit zwei hintereinanderschalteten Relais, einem normalen "on/off" Relais und dem von Bezzo, dann sollte ich eigentlich auf der sicheren Seite sein.
Btw: Falls der Rasp abschmiert, neu startet und dabei die GPIO's alle "Resettet", würden ja auch die Ventile zur Entleerung der Leitung schließen - quasi "Sommerbetrieb" - bis der Temp.-Sensor dann wieder meldet, es ist zu kalt für Sommer und das Procedere "Winterbetrieb" neu startet.
Mal ne ganz andere Frage zu meinem Projekt: Da ich ja hauffenweise Kabel nach außen führen muss, und ich das mit einem D-SUB25 (Auch bekannt als LPT-1 Druckerkabel parallel) gemacht habe, stellt sich die Frage, wie lange darf das Kabel sein, um ein Analogsignal zum Raspberry auf einen A/D1115 zu senden? Gibt es da Probleme bei ca. 15 m länge? Hintergedanke: Evtl. will ich eine Durchflussmessung an der Pumpe anschließen, damit ich ungefähr weiß, wie viel Wasser ich mir von der örtl. Wasserversorgung gespart habe. Zum Einsatz würde ein AS010 von autosen kommen (Flow: 9 ... 150 l/min, U: 10 ... 33 V DC), lt. Hersteller liefert der Sensor 4 - 20 mA)
Die Ventile für die Beregnung laufen mit 24 VAC (nur zur Info)
Mal ne ganz andere Frage zu meinem Projekt: Da ich ja hauffenweise Kabel nach außen führen muss, und ich das mit einem D-SUB25 (Auch bekannt als LPT-1 Druckerkabel parallel) gemacht habe, stellt sich die Frage, wie lange darf das Kabel sein, um ein Analogsignal zum Raspberry auf einen A/D1115 zu senden? Gibt es da Probleme bei ca. 15 m länge? Hintergedanke: Evtl. will ich eine Durchflussmessung an der Pumpe anschließen, damit ich ungefähr weiß, wie viel Wasser ich mir von der örtl. Wasserversorgung gespart habe. Zum Einsatz würde ein AS010 von autosen kommen (Flow: 9 ... 150 l/min, U: 10 ... 33 V DC), lt. Hersteller liefert der Sensor 4 - 20 mA)
4 - 20 mA sind schön. Die kann man ohne Probleme verlängern: https://www.mikrocontroller.net/topic/222785
Eine Anmerkung war, dass ein Blitzschlag die Spannung auch ändern kann und dadurch dann den Strom beeinflusst.
Der Sensor fängt erst bei 4 mA an, was als Drahtbruchsicherung gilt. 0 mA bzw. 0V == Drahtbruch.
Der ADC kann nur analoge Spannungen verarbeiten, also muss man mit einem Widerstand den gewünschten Spannungsfall erzeugen.
Die maximale Größe des Widerstands steht im Datenblatt des Sensors.
Falls das nicht angegeben ist, wird die Betriebsspannung von bis angegeben.
Bei dem Sensor sind es 10 - 33 V.
Wenn man z.B. 3.3 V als Maximalspannung für den ADC bestimmen möchte:
Dann musst du den AD1115 mit 3.3V über VDD versorgen.
Das ist dann auch die digitale Spannung für I2C und die analoge Bezugsspannung.
Den Sensor mit mindestens 10V aber nicht mehr als 33V einspeisen. Über die Spannung wird der Strom erzeugt.
Der Bereich des ADC geht von 0 bis 65535 (16 Bit), aber die Skalierung fängt erst bei 4mA bzw. 9l/min an.
Die Spannung für den unteren Messbereich kann man sich ausrechnen.
Der min_wert sollte nur bei einem kaputten Sensor oder Kabelbruch unterschritten werden.
Da man immer Ungenauigkeiten hat, kann man den unteren Messbereich auch einfach mit dem ADC erfassen
und diesen dann als Wert für den unteren Messbereich übernehmen.
Hinterher normalisierst du den Eingangs-Wert (auf 0 - 1) und danach skalierst du den normalisierten Wert auf 9 - 150 l/m.
def analog2unit(value, low_in, high_in, low_out, high_out):
return (value - low_in) / (high_in - low_in) * (high_out - low_out) + low_out
value_max = 2**16 - 1
value_in = value_max
flow_low = 9
flow_high = 150
min_wert = 1307
analog2unit(value_in, min_wert, value_max, flow_low, flow_high)
Alles anzeigen
Wenn zwei unterschiedliche Sensoren eingesetzt werden, könnte man sich aus der Funktion eine Klasse machen oder mit functools.partial arbeiten.
Bezüglich der Abschaltung der GPIOs müsste man es einfach mal testen.
Die einfache Variante zum Testen: reboot
Hab ich auch nicht vor, ich schalte dem bistabilen Relais ein normales Relais davor, das arbeitet mit vcc vom Rasp.
Alles anzeigen4 - 20 mA sind schön. Die kann man ohne Probleme verlängern: https://www.mikrocontroller.net/topic/222785
Eine Anmerkung war, dass ein Blitzschlag die Spannung auch ändern kann und dadurch dann den Strom beeinflusst.
Der Sensor fängt erst bei 4 mA an, was als Drahtbruchsicherung gilt. 0 mA bzw. 0V == Drahtbruch.
Der ADC kann nur analoge Spannungen verarbeiten, also muss man mit einem Widerstand den gewünschten Spannungsfall erzeugen.Die maximale Größe des Widerstands steht im Datenblatt des Sensors.
Falls das nicht angegeben ist, wird die Betriebsspannung von bis angegeben.
Bei dem Sensor sind es 10 - 33 V.
Wenn man z.B. 3.3 V als Maximalspannung für den ADC bestimmen möchte:
Dann musst du den AD1115 mit 3.3V über VDD versorgen.
Das ist dann auch die digitale Spannung für I2C und die analoge Bezugsspannung.
Den Sensor mit mindestens 10V aber nicht mehr als 33V einspeisen. Über die Spannung wird der Strom erzeugt.
Der Bereich des ADC geht von 0 bis 65535 (16 Bit), aber die Skalierung fängt erst bei 4mA bzw. 9l/min an.
Die Spannung für den unteren Messbereich kann man sich ausrechnen.
Der min_wert sollte nur bei einem kaputten Sensor oder Kabelbruch unterschritten werden.
Da man immer Ungenauigkeiten hat, kann man den unteren Messbereich auch einfach mit dem ADC erfassen
und diesen dann als Wert für den unteren Messbereich übernehmen.Hinterher normalisierst du den Eingangs-Wert (auf 0 - 1) und danach skalierst du den normalisierten Wert auf 9 - 150 l/m.
Code Alles anzeigendef analog2unit(value, low_in, high_in, low_out, high_out): return (value - low_in) / (high_in - low_in) * (high_out - low_out) + low_out value_max = 2**16 - 1 value_in = value_max flow_low = 9 flow_high = 150 min_wert = 1307 analog2unit(value_in, min_wert, value_max, flow_low, flow_high)
Wenn zwei unterschiedliche Sensoren eingesetzt werden, könnte man sich aus der Funktion eine Klasse machen oder mit functools.partial arbeiten.
Bezüglich der Abschaltung der GPIOs müsste man es einfach mal testen.
Die einfache Variante zum Testen: reboot
Also wenn ich das Richtig verstehe, brauche ich, wenn ich das Ding mit 12 V versorgen möchte, einen 600 Ohm Widerstand?
Den Code "def analog2unit ..." kann ich so übernehmen?
Hab an der ADS1115 schon einen Temp-Sensor hängen ...
Beim ADS handelt es sich um folgenden: CQRobotADS1115 [Anzeige]
Die Spannung, mit der der Sensor versorgt wird (10-33VDC), bestimmt nicht die Ausgangsgröße (4-20mA). Die Ausgangsgröße wird durch den erfassten Messwert und die Auswerteelektronik erzeugt. Die Elektronik gibt dann einen Konstantstrom aus und der liegt immer zwischen 4mA und 20mA. Den gewünschten Spannungsfall legt man mit dem Widerstand fest.
Du musst also mit den Werten rechnen, die der ADC erfassen kann. Da dieser mit 3.3V betrieben wird, da der RPI auch mit 3.3V arbeitet, muss man das Eingangssignal auf 3.3V abfallen lassen. Mehr Spannung == kaputter ADC und ggf. kaputter RPI
Hier eine Abbildung, die es ungefähr wiedergibt.
Auf der linken Seite hast du den Sensor (der auch mit 10-33VDC eingespeist wird), in der Mitte jeweils der Widerstand zum Erzeugen des Spannungsfalls. Rechts die gemessene Spannung. Bezugspotential sind die 0V.
Zum Ausprobieren kannst du den Sensor ohne ADC an einem Widerstand anschließen.
Im Datenblatt steht die Pinbelegung. Wenn du mit 12V arbeiten möchtest, dann L+ 12V an Pin 1 anschließen.
Pin 2 ist die Temperatur und Pin 4 ist die Durchflussmenge.
Die Angabe im Datenblatt scheint falsch zu sein, da sich diese nicht mit der Grafik übereinstimmt:
ZitatPin 1 & Pin 4 versorgen Sensor mit Strom; Analogausgang auf Pin 2 ist optional
An Pin 2 kommt dann ein 165 Ohm Widerstand und die andere Seite an die Masse (-) der Batterie/Netzteils.
Bei Pin 4 genau das Gleiche. Eine Seite des Widerstands an den Pin 4 und die andere Seite an Masse.
Der Sensor gibt einen Strom aus, der Widerstand bestimmt den Spannungsfall.
Mit einem Messgerät solltest du dann an beiden Widerständen eine Spannung zwischen ~0V und 3.3V messen.
Die 0V werden nie erreicht, da immer 4 mA mindestens ausgegeben werden.
ZitatAlles anzeigenAusgänge / Eingänge
Analogausgang 4 ... 20 mA
Formel
Analogausgang Wasser [4...20 mA]: Q [l/min] = 9,375 x (I - 4 mA)
Temperatur [4...20 mA]: T [°C] = 9,375 x (I - 4 mA) - 25
Hallo liebe Raspifreunde,
mittlerweile hat sich auch mein Schwipp-Schwager (Freund meiner Schwester) dem Script mal angenommen und deutlich mehr Struktur reingebracht.
Allerdings läuft es noch nicht, es kommt immer ein Fehler.
Ich finde den Grund aber nicht.
Hier der bearbeitete Code:
#!/usr/bin/env python3
from gpiozero import LED
from time import sleep
import schedule
import time
import enum
from abc import ABC, abstractmethod
import Adafruit_ADS1x15
def bewaessern_an(relais):
relais.on()
def bewaessern_aus(relais):
relais.off()
# ---- abstrakte Sensor Basisklasse
class sensor(ABC):
def __init__(self):
super().__init__()
@abstractmethod
def get_value(self):
pass
# ---- Sensorklassen
# DS18B20 Sensoren
class ds18b20_sensoren:
def __init__(self):
self.sensoren = DS18B20()
self.count = sensoren.device_count()
self.names = sensoren.device_names()
def get_value(sensornummer):
if sensornummer >= self.count:
print("program faulty; unknown sensor number")
wert = -999.
else:
wert = self.sensoren.tempC(sensornummer) * 9.0 / 5.0 + 32.0
return wert
# Bodentemperatursensor
# (falls es immer nur einen gibt, kann man beide Klassen verschmelzen)
class ds18b20_sensor(sensor):
def get_value():
return ds18b20_sensoren(0) # Bodensensor hat Nr. 0 ?
# Bodenfeuchtesensor roh
class AD51x15_sensor(sensor):
GAIN = 1
def __init__(self):
sensor = Adafruit_ADS1x15.ADS1115()
def get_value():
sensor.start_adc_comparator(0, # Channel number
20000, 5000, # High threshold value, low threshold value
active_low=True, traditional=True, latching=False,
num_readings=1, gain=GAIN)
sleep(0.1) # wait for conversion
return adc.get_last_result()
# ---- Hilfklassen
# Sensor liefert einfachen gleitenden Mittelwert
class mittelwert(sensor):
def __init__(self, sensor, anzahl):
self.sensor = sensor
self.anzahl = anzahl
self.sum = 0
def get_value():
self.sum = self.sum + seld.sensor.get_value()
ergebnis = self.sum / self.anzahl
self.sum = self.sum - ergebnis
return ergebnis
class bereich(enum.Enum):
drueber = 1
drunter = 2
# Sensor liefert
# Grenzwertschalter mit Hysterese
# der Schaltabstand entspricht der doppelten Hysterese
class grenzwert(sensor):
def __init__(self, sensor, grenzwert, hysterese):
self.sensor = sensor
self.grenzwert = grenzwert
self.hysterese = hysterese
self.aktuell = bereich.drunter
def get_value():
if self.sensor.get_value() < self.grenzwert - self.hysterese:
self.aktuell = bereich.drunter
elif self.sensor.get_value() > self.grenzwert + self.hysterese:
self.aktuell = bereich.drueber
return self.aktuell
# ---- Objekte
temperatur_mittel = mittelwert(ds18b20_sensor, 20)
temperatur_grenzwert = grenzwert(temperatur_mittel, 3, 2) # Frostgrenze, Hysterese
feuchte_mittel = mittelwert(AD51x15_sensor, 20)
feuchte_grenzwert = grenzwert(feuchte_mittel, 30, 2) # Feuchtegrenze ?, Hysterese
rasen_1=LED(12) # Rasenbewässerung 1, bei den Rosen
rasen_2=LED(13) # Rasenbewässerung 2, beim Bambus
rasen_3=LED(19) # Rasenbewässerung 3, beim Wintergarten/Holzlege
rasen_4=LED(21) # Reserve Rasenbewässerung 4,
topfblumen=LED(22) # Topfblumenbewässerung
entlueftung_1=LED(23) # Entlüftung Garagendach
entlueftung_2=LED(26) # Entleerung Lichtschacht
motor=LED(27) # Motorschalter Pumpe
# invertieren der Ansteuerlogik für die Pumpe
class pumpe:
def on():
motor.off()
def off():
motor.on()
# ---- Smart Garden Logik
class gartenzustand(enum.Enum):
winter = 1
ok = 2
trocken = 3
class aktorzustand(enum.Enum):
an = 1
aus = 2
class smartgarden:
def __init__(self):
self.gartenzustand = gartenzustand.ok
def schalten(was, wie):
temperatur_bereich = temperatur_grenzwert.get_value() # die beiden müssen peridisch gerufen werden
feuchte_bereich = feuchte_grenzwert.get_value()
while TRUE:
alter_zustand = self.gartenzustand
match self.gartenzustand:
case gartenzustand.winter:
if temperatur_bereich == bereich.drueber:
self.gartenzustand = gartenzustand.ok
elif alter_zustand != self.gartenzustand:
pumpe.off()
sleep(0.1)
entlueftung_1.on()
entlueftung_2.on()
print("Entleere Wasserleitung ...")
sleep(600)
print("Winterbetrieb")
### sollen die während des gnzen Winters entlüftet bleiben?
### sonst nach einer längern Zeit schließen, vllt. mittel Zähler
case gartenzustand.ok:
if temperatur_bereich == bereich.drunter: # kalt?
self.gartenzustand = gartenzustand.winter # ja
elif feuchte_bereich == bereich.drunter: # trocken?
self.gartenzustand = gartenzustand.trocken # ja
elif alter_zustand != self.gartenzustand:
pumpe.on()
sleep(0.1)
entlueftung_1.off()
entlueftung_2.off()
sleep(1)
print("Sommerbetrieb")
case gartenzustand.trocken:
if temperatur_bereich == bereich.drunter: # kalt?
self.gartenzustand = gartenzustand.winter # ja
elif feuchte_bereich == bereich.drueber: # trocken?
self.gartenzustand = gartenzustand.ok # ja
elif wie == aktorzustand.an:
was.on()
else:
was.off()
if alter_zustand == self.gartenzustand:
break
# ---- Festlegung der Schaltzeiten
def main():
schedule.every().day.at("03:00").do(rasen_1, aktorzustand.an)
schedule.every().day.at("03:20").do(rasen_1, aktorzustand.aus)
schedule.every().day.at("03:40").do(rasen_2, aktorzustand.an)
schedule.every().day.at("04:00").do(rasen_2, aktorzustand.aus)
schedule.every().day.at("04:20").do(rasen_3, aktorzustand.an)
schedule.every().day.at("04:40").do(rasen_3, aktorzustand.aus)
schedule.every().day.at("05:00").do(rasen_1, aktorzustand.an)
schedule.every().day.at("05:20").do(rasen_1, aktorzustand.aus)
schedule.every().day.at("05:40").do(rasen_2, aktorzustand.an)
schedule.every().day.at("06:00").do(rasen_2, aktorzustand.aus)
schedule.every().day.at("06:20").do(rasen_3, aktorzustand.an)
schedule.every().day.at("06:40").do(rasen_3, aktorzustand.aus)
schedule.every().day.at("08:00").do(topfblumen, aktorzustand.an)
schedule.every().day.at("08:20").do(topfblumen, aktorzustand.aus)
schedule.every().day.at("12:00").do(topfblumen, aktorzustand.an)
schedule.every().day.at("12:20").do(topfblumen, aktorzustand.aus)
schedule.every().day.at("18:00").do(topfblumen, aktorzustand.an)
schedule.every().day.at("18:20").do(topfblumen, aktorzustand.aus)
while True:
schedule.run_pending()
time.sleep(1)
if __name__ == "__main__":
main()
Alles anzeigen
und hier die Fehlermeldung:
Traceback (most recent call last):
File "/home/pi/Gartenbewässerung/smartgarden.py", line 152
match self.gartenzustand:
^
SyntaxError: invalid syntax
self.gartenzustand ist doch definiert? Oder?
Hallo,
und deutlich mehr Struktur reingebracht.
ja, findest du?
Ich denke, er wechselt Python mit einer anderen Programmiersprache.
self.gartenzustand ist doch definiert? Oder?
Die Fehlermeldung sagt nicht, dass ein Name nicht definiert ist, sondern weist auf einen Synatx-Fehler hin.
Siehe:
re — Regular expression operations — Python 3.10.2 documentation
Falls das 'match' gemeint war. Der Import dazu fehlt auch.
Grüße
Dennis
Ich denke, er wechselt Python mit einer anderen Programmiersprache.
Java, oder?
Meine Berührungen mit Java sind - zum Glück - schon sehr viele Jahre her, aber irgendwie klingelte es da...
Und while TRUE: bzw. case sieht man auch eher selten in Python.
Java, oder?
Ja, soweit ich weis geht da nicht viel ohne Klassen.
Python benötigt Klassen, wenn man sich zum Beispiel etwas merken will. Wenn man keinen Sinn darin sieht, wieso man ständig Namen an 'self' bindet, dann kann die Klasse eigentlich auch weg. So zumindest meine Erfahrung. (Auch wenn die nicht all zu groß ist)
Grüße
Dennis
Und while TRUE: bzw. case sieht man auch eher selten in Python.
Das liegt daran, dass match und case erst mit Python 3.10 eingeführt worden ist.
Beides sind Soft-Keywords und können weiterhin als normale Namen verwendet werden.
Viele, die diese Konstruktion sehen, vermuten die gleiche Funktionalität wie beim switch/case in C.
Structural Pattern Matching kann aber mehr.
Da RPi OS Bullseye aber nur Python 3.9 zur Verfügung stellt, sieht man die Anwendung hier im Forum eher selten.
Das liegt daran, dass match und case erst mit Python 3.10 eingeführt worden ist.
Beides sind Soft-Keywords und können weiterhin als normale Namen verwendet werden.
Danke für diese Infos!
Dann schaue ich mir Python 3.10 diesbezüglich mal etwas genauer an (nicht auf dem Raspi).
Das liegt daran, dass match und case erst mit Python 3.10 eingeführt worden ist.
Beides sind Soft-Keywords und können weiterhin als normale Namen verwendet werden.
... und was wäre die Alternative dazu?
Ganz normal mit if-elif-else Verzweigungen. Das structural pattern matching nutzt auch unter anderem isinstance.
Ein Beispiel zu konstruieren, ist gar nicht so einfach.
reply_err = {"status": "error"}
reply_ok = {"status": "ok", "result": 33}
reply_broken ={"foo": "bar"}
# mit match - case
def is_ok1(reply):
match reply:
case {"status": "ok", "result": value}:
return value
case {"status": status}:
raise ValueError(f"Status is {status}")
case _:
raise ValueError("Invalid reply")
# ohme match - case
def is_ok2(reply):
status = reply.get("status")
result = reply.get("result")
if status == "ok" and result is not None:
return result
elif status is not None:
raise ValueError(f"Status is {status}")
else:
raise ValueError("Invalid reply")
Alles anzeigen
Der Vorteil beim neuen Syntax ist, dass man sofort sieht welcher case zutrifft, sofern man es richtig macht und dort einsetzt, wo es auch einen Sinn ergebigt.
Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!