GPIO abhängig von Messwert schalten

Heute ist Stammtischzeit:
Jeden Donnerstag 20:30 Uhr hier im Chat.
Wer Lust hat, kann sich gerne beteiligen. ;)
  • Hallo zusammen,
    ich habe am PI einen Temp-Sensor angeschlossen (DS18B20), der tut auch tadellos seinen Dienst. Nun möchte ich, das der PI abhängig von dem gemessenen Temperaturwert einen anderen GPIO (dort hängt ein Relais dran) geschaltet wird.

    Ich bin standfest in der Elektronik, aber in der Programmierung habe ich meine Schwächen. Ich habe keine Idee wie das zu realisieren ist.

    Über einen Tipp oder einen Entwurf des Codes (bitte mit Erklärung) wäre ich sehr dankbar.

  • Ich weiß nicht, wie das in Python gemacht wird und bin ganz sicher niemand, der Ahnung vom Programmieren hat, aber wäre das eine Aufgabenstellung für den Arduino, dann wäre das eine einfache Bedingung (If)

    if (digitalRead(TempPin)) > XY
    {
    digitalWrite(Relaispin,HIGH);
    }
    else...


    So oder so ähnlich würde ich das zumindest lösen.

  • [code=php]#!/usr/bin/python
    # coding=utf-8

    import spidev
    import time

    spi = spidev.SpiDev()
    spi.open(0,1)
    antwort = spi.xfer([1,128,0])

    if 0 <= antwort[1] <=3:
    wert = ((antwort[1] * 256) +antwort[2]) * 0.00322
    fprozent = ((wert / 2.85) * 100)
    #Bodenfeuchtigkeit in Prozent ausgeben
    print("%.1f" % fprozent)
    else:
    pass
    [/php]


    Das funktioniert auch einwandfrei. Nun möchte ich, das GPIO18 (oder ein anderer) geschaltet werden wenn der mit dem obigen Skript ermittelte Wert einen Soll-Wert unterschreitet. Da stehe ich irgendwie auf dem Schlauch.

    Einmal editiert, zuletzt von MarkusO (8. Mai 2015 um 22:33)

  • Hiermit schreibe ich einen Wert in eine Textdatei. Soweit so gut.

    [code=php]
    #!/bin/bash

    WDIR=/usr/local/tomatenpi
    ETCDIR=$WDIR/etc/
    OUTDIR=$WDIR/output

    MOISTPROZENT=$($WDIR/vh400.py);
    logger "$MOISTPROZENT Prozent Bodenfeuchtigkeit"
    echo $MOISTPROZENT > $ETCDIR/vh400.text
    [/php]


    Nun lese ich den Wert im Python wieder ein und möchte, das wenn dieser Wert über 80 liegt, der GPIO 18 auf HIGH gesetzt wird, ansonsten soll er auf LOW sitzen.

    [code=php]

    #!/usr/bin/python
    # coding=utf-8


    import RPi.GPIO as GPIO
    import time

    GPIO.setmode(GPIO.BCM)
    GPIO.setup(18, GPIO.OUT)

    lines = []
    fobj = open("etc/vh400.text", "r")
    for line in fobj:
    line.rstrip()
    lines.append(line)
    fobj.close()

    print lines[0] # Ist nur zum testen drin, fliegt später raus

    while True:
    try:
    mw = lines[0]
    if mw[0]>80.0:
    GPIO.output(18, GPIO.HIGH)
    else:
    GPIO.output(18, GPIO.LOW)
    time.sleep(3)

    except KeyboardInterrupt:
    GPIO.cleanup()
    exit()
    [/php]

    Die except-Anweisung ist ebenfalls nur zum testen drin. Das Skript ignoriert meinen eingelesenen Wert total und schaltet GPIO 18 immer auf High. Ich bin leider kein Programmierer und erarbeite mir das alles Stück für Stück, aber hier komme ich im Moment nicht weiter.

  • Ich habe mal kommentiert, evtl. hilft das weiter

  • lines=[] braucht er da er später append() darauf anwendet. Setzt er vorher kein lines=[] schlägt das append() fehl.

    mw ist in der Tat keine Liste sondern der Wert von lines[0]. Desweiteren wäre fraglich ob das wirklich ein float Wert ist, also 80.0 überhaupt darauf anwendbar ist.

    Warum du ein bash Script und dann noch mal ein Python Script verwendest, ist mir schleierhaft. Der DS18B20 ist ein 1-wire Sensor und kann nach entsprechendem laden der Kernel Module w1-gpio und w1-therm direkt über die virtuelle Datei /sys/bus/w1/devices/*/w1_slave ausgelesen werden (* mit der jeweiligen ID ersetzen)

    Das könnte in Python wie folgt aussehn:

    [code=php]
    import re, time

    # define pathes to 1-wire sensor data
    pathes = (
    "/sys/bus/w1/devices/10-000801b5a7a6/w1_slave",
    "/sys/bus/w1/devices/10-000801b5959d/w1_slave"
    )

    def read_sensor(path):
    value=None
    try:
    f = open(path, 'r')
    line = f.readline()
    if re.match(r"([0-9a-f]{2} ){9}: crc=[0-9a-f]{2} YES", line):
    line = f.readline()
    m = re.match(r"([0-9a-f]{2} ){9}t=([+-]?[0-9]+)", line)
    if m:
    value = str(float(m.group(2)) / 1000.0)
    f.close()
    except (IOError), e:
    print time.strftime("%x %X"), "Error reading", path, ": ", e
    return value

    # read sensor data
    for path in pathes:
    data = read_sensor(path)
    if data:
    print '{} -> {}'.format(path, read_sensor(path))
    else:
    print '{} -> None'.format(path)
    time.sleep(1)

    [/php]

    Alternativ ginge auch: https://github.com/timofurrer/w1thermsensor

    Die Rückgabe bei der von mir gezeigten Funktion ist ein float Wert, also mit Nachkommata-Stelle. Das kannst du dann wie bei Dir auf einen bestimmten Wert prüfen und dann den GPIO schalten. Allerdings solltest du das noch erweitern und prüfen ob der GPIO bereits auf HIGH gesetzt ist um diesen ggf auch wieder auf LOW zu stellen falls der Wert unter deine Grenze fällt :fies:

  • Ich hole mal etwas weiter aus, tut mir Leid das ich das erst jetzt tue.

    Ich möchte Tomaten automatisch bewässern. Das sollte man mit "temperiertem Wasser tun, also nicht aus der Leitung. Dazu habe ich eine Regentonne. Nun habe ich in die Regentonne einen Schwimmerschalter eingebaut und einen Schlauchanschluß inkl. Magnetventil zur Wiederbefüllung der Regentonne durch Leitungswasser (das steht darin dann ja ab).

    Ich habe in der Threaderöffnung Blödsinn erzählt. Ich lese keinen DS18B20 aus sondern einen VH-400 von Vegetronix Link.

    Das erledige ich mit diesem Skript:
    [code=php]
    #!/usr/bin/python
    # coding=utf-8

    import spidev
    import time

    spi = spidev.SpiDev()
    spi.open(0,1)
    antwort = spi.xfer([1,128,0])

    if 0 <= antwort[1] <=3:
    wert = ((antwort[1] * 256) +antwort[2]) * 0.00322
    fprozent = ((wert / 2.85) * 100)
    #Substratfeuchte in Prozent ausgeben
    print("%.0f" % fprozent)
    else:
    pass
    [/php]
    Funktioniert wunderbar.


    Dieses Script startet das obige und schreibt den gemessenen Wert in eine Textdatei.
    [code=php]
    #!/bin/bash

    WDIR=/usr/local/tomatenpi
    ETCDIR=$WDIR/etc/
    OUTDIR=$WDIR/output

    MOISTPROZENT=$($WDIR/vh400.py);
    logger "$MOISTPROZENT Prozent Bodenfeuchtigkeit"
    echo $MOISTPROZENT > $ETCDIR/vh400.text
    [/php]
    Funktioniert auch wunderbar.


    Nun wollte ich, das Bewässert wird, wenn:

    1. Die Bodenfeuchtigkeit unter 60% fällt
    2. Das Magnetventil geschlossen ist (um nicht frisches Leitungswasser zu Verwässern)
    3. Der Schwimmerschalter meldet das genug Wasser in der Tonne ist (um die Pumpe zu schützen)

    Dieses Skript habe ich nun angefangen und es sieht so aus.
    [code=php]
    #!/usr/bin/python
    # coding=utf-8

    import RPi.GPIO as GPIO
    import time

    GPIO.setmode(GPIO.BCM)
    GPIO.setup(18, GPIO.OUT)
    GPIO.setup(21, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)


    fin = open("etc/vh400.text", "r")
    mw = fin.read()
    fin.close()

    while True:
    # Hinterlegung der Variablen für Messwert und RelaisPGIO-Input
    mw = mw
    input_value = GPIO.input(21)
    #Value auf 1 für NC-Kontakt, auf 0 fü NO-Kontakt
    if mw <"60" and input_value == 0:
    GPIO.output(18, GPIO.HIGH)
    time.sleep(10)
    GPIO.output(18, GPIO.LOW)
    GPIO.cleanup()

    else:
    GPIO.cleanup()
    exit()
    [/php]

    Es reagiert auf den angeschlossenen Schalter an GPIO21 und wässert nur wenn dieser inaktiv ist. Perfekt (denke ich). Beim Ende (nach 10 Sekunden Bewässerung) wirft er jedoch folgenden Fehler aus:

    Traceback (most recent call last):
    File "relaistest.py", line 19, in <module>
    input_value = GPIO.input(21)
    RuntimeError: Please set pin numbering mode using GPIO.setmode(GPIO.BOARD) or GPIO.setmode(GPIO.BCM)


    Ich habe leider keinen blassen Schimmer was ich tun kann.

  • Hallo Markus,

    Code
    if mw <"60" and input_value == 0:
            GPIO.output(18, GPIO.HIGH)
            time.sleep(10)
            GPIO.output(18, GPIO.LOW)
            GPIO.cleanup()

    Mir gefällt das GPIO.cleanup() nicht. Entferne dies bitte und lasse das Programm nochmal laufen.

    Wenn das Programm die Endlosschleife erneut durchläuft (nach cleanup() ist die Information über Eingänge und Ausgänge gelöscht), dann muss es eine Fehlermeldung beim nächsten Versuch, über

    Code
    input_value = GPIO.input(21)

    etwas einzulesen, geben - der Status von Pin 21 ist nämlich nicht mehr bekannt. Genau das teilt Dir das Laufzeitsystem mit der Fehlermeldung mit.


    Beste Grüße

    Andreas

    Ich bin wirklich nicht darauf aus, Microsoft zu zerstören. Das wird nur ein völlig unbeabsichtigter Nebeneffekt sein.
    Linus Torvalds - "Vater" von Linux

    Linux is like a wigwam, no windows, no gates, but with an apache inside dancing samba, very hungry eating a yacc, a gnu and a bison.

    Einmal editiert, zuletzt von Andreas (11. Mai 2015 um 22:51)

  • Nun sieht es so aus, aber das Relais läßt nach 10 Sekunden nicht wieder los. (GPIO ist LOW)

    [code=php]

    #!/usr/bin/python
    # coding=utf-8

    import RPi.GPIO as GPIO
    import time

    GPIO.setmode(GPIO.BCM)
    GPIO.setup(18, GPIO.OUT)
    GPIO.setup(21, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)


    fin = open("etc/vh400.text", "r")
    mw = fin.read()
    fin.close()

    while True:
    mw = mw
    input_value = GPIO.input(21)
    if mw <"60" and input_value == 0:
    GPIO.output(18, GPIO.HIGH)
    time.sleep(10)
    GPIO.output(18, GPIO.LOW)
    else:
    GPIO.cleanup()
    exit()
    [/php]

  • Bitte nicht lachen, ich bin blutiger Anfänger in Programmierung.

    Ich dachte ich definiere mw weiter oben mit mw = fin.read(). Input_value = GPIO.input(21) soll den GPIO definieren dessen Status ich abfragen will (0 oder 1).

    Ist das so nicht korrekt?

    • Offizieller Beitrag

    input_value hab ich gekonnt überlesen, vergiss den Teil der Frage ;)

    Zitat

    fin = open("etc/vh400.text", "r")
    mw = fin.read()
    fin.close()

    Ändert sich dieser Wert irgendwann mal? Wenn ja, muss dieser Teil mit in die While-True Schleife rein, denn sonnst fragst du es einmal ab und er wer sich nie Ändern.

    Was du bestimmt machen wolltest, war sowas hier
    [code=php]
    def read_file()
    with open("/etc/vh400.text", "r") as fin:
    mw = fin.read()
    return mw

    #und dann das anstelle des mw=mw
    mw = read_file()
    [/php]

    with open ist die schönere Variante wenn ums Dateihandling geht. Mehr dazu steht u.a. hier

  • Danke für Deinen Input, das probiere ich heute Abend gleich aus.

  • :fies: Da fehlt ne Einrückung nach dem Doppelpunkt :fies: Und auch ein Doppelpunkt in der 'def' Zeile :fies: Er nutzt aber auch einen anderen Pfad.
    [code=php]
    def read_file():
    with open("/usr/local/tomatenpi/etc/vh400.text", "r") as fin:
    mw = fin.read()
    return mw
    [/php]

    Aber um auch noch mal auf Beitrag#10 zurück zu kommen: Du brauchst kein bash Script um die Rückgabe eines Python Scripts in eine Datei schreiben zu lassen um dann ein weiteres Python Script die Datei auszulesen und zu verarbeiten. Das ist überflüssiges Chaos ;)
    Bleib einfach bei Python, lese den Sensor aus und verwende dessen Ausgabe direkt im selben Python Script.

    Bedeutet also du nimmst dir den Teil zum auslesen des Sensors und packst das in eine Funktion:

    [code=php]

    def read_sensor(bus, device):
    try:
    spi = spidev.SpiDev()
    spi.open(bus, device)
    to_send = [0x1, 0x128, 0x0]
    respons = spi.xfer(to_send)
    spi.close()
    except Exception, e:
    print "Error: " + str(e)
    return respons
    [/php]

    Dann nimmst du dir das andere Python Script zur Hand und packst den Code ebenfalls hier in dieses eine wo auch schon "read_sensor" drin ist. Komplett sieht das dann wie folgt aus:

    [code=php]
    import spidev
    import RPi.GPIO as GPIO
    import time

    GPIO.setmode(GPIO.BCM)
    GPIO.setup(18, GPIO.OUT)
    GPIO.setup(21, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

    def read_sensor(bus, device):
    try:
    spi = spidev.SpiDev()
    spi.open(bus, device)
    to_send = [0x1, 0x128, 0x0]
    respons = spi.xfer(to_send)
    spi.close()
    except Exception, e:
    print "Error: " + str(e)
    return respons

    try:
    while True:
    messwert = read_sensor(0, 1)
    if messwert < 60 and GPIO.input(21) == GPIO.LOW:
    GPIO.output(18, GPIO.HIGH)
    time.sleep(10)
    GPIO.output(18, GPIO.LOW)

    time.sleep(0.5)

    except (KeyboardInterrupt, SystemExit):
    GPIO.cleanup()
    print "\nQuit\n"
    [/php]

    ..und die anderen Scripts brauchste wie gesagt nicht mehr..

    "time.sleep(0.5)" brauchst du damit das Script nicht 100% CPU Last erzeugt. "try:" und "except:" fangen Fehler ab oder führen entsprechendes aus wenn du STRG+C drückst o.ä.

  • Danke meigrafd,

    ich habe Dein Skript mal genutzt, bei unverändertem Testaufbau zu gestern. Hier läßt GPIO18 aber das Relais trotz des time.sleep(10) nicht wieder los. Und den Status von GPIO 21 ignoriert er auch gänzlich.

    Hast Du da noch eine Idee zu?

    Einmal editiert, zuletzt von MarkusO (12. Mai 2015 um 23:40)

  • So, ich habe etwas weiter gebastelt und so läuft es prima. Wenn der Messwertert unter, testhalber, 30 ist, startet die Bewässerung mit 30 Sekunden Verzögerung. Sobald der Messwert wieder oberhalb Sollwertes liegt, geht die Bewässerung umgehend aus.
    Die 30 Sekunden müssen sein um einen Stack Overflow zu vermeiden (IOError 24)

    Auch messe ich den Wert des Sensors nun mit im Skript.

    [code=php]
    #!/usr/bin/python
    # coding=utf-8

    import spidev
    import RPi.GPIO as GPIO
    import time

    GPIO.setmode(GPIO.BCM)
    GPIO.setup(18, GPIO.OUT)
    GPIO.setup(21, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

    try:
    while True:
    spi = spidev.SpiDev()
    spi.open(0,1)
    antwort = spi.xfer([1,128,0])
    spi.close()

    if 0 <= antwort[1] <=3:
    wert = ((antwort[1] * 256) +antwort[2]) * 0.00322
    fprozent = ((wert / 2.85) * 100)

    messwert = fprozent
    if messwert < 30 and (GPIO.input(21) ==0):
    GPIO.output(18, GPIO.HIGH)
    else:
    GPIO.output(18, GPIO.LOW)
    time.sleep(30)

    except (KeyboardInterrupt, SystemExit):
    GPIO.cleanup()
    print "\nQuit\n"
    [/php]

    Nächstes Ziel, Schwimmerschalter der Regentonne auslesen, wenn der auf "0" geht, dann soll ein Magnetventil öffnen um die Tonne wieder aufzufüllen. Ist ja im Prinzip beinahe dasselbe wie oben.

    Einmal editiert, zuletzt von MarkusO (13. Mai 2015 um 21:32)

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!