Probleme mit der Interruptsteuerung

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
raspido
User
Beiträge: 31
Registriert: Montag 10. November 2014, 19:42
Kontaktdaten:

Hey Leute,

ich habe bei meiner Idee bzw. bei dessen Umsetzung so ein wenig meine Probleme.

Aber fangen wir erstmal an, mit dem was ich vor habe und wie die Hardware bestückt ist:

Ich habe ein Gehäuse hier liegen, welches über einen Sabotagekontakt verfügt. Dieser Sabotagekontakt soll überwacht werden über den Raspberry Pi. Sobald dieser ausgelöst wird, soll eine E - Mail versendet werden. Aber da man das Gehäuse auch mal gerne öffnen würde ohne gleich E-Mail und Co in Gang zu setzen verfügt das Gehäuse, nach der Nachrüstung über einen Schlüsselschalter. Sobald ich diesen betätige soll ein Auslösen des Sabotagekontaktes ignoriert werden. Aber das Betätigen des Schlüsselschalters soll in beiden Fällen (beim Öffnen und Schließen) eine Aktion ausführen. Für die Zukunft soll einfach Protokolliert werden, dass der Schlüssel betätigt wurde (mit Zeit und Datum) und welcher Vorgang (Freischalten / Sperren). Für den Versuchszweck reicht mir aber erstmal ein einfache Print auf dem Bildschirm.

Das gleiche gilt fürs erste auch für den Sabotagekontakt. Ich möchte jetzt nicht 1.000 Mails verschicken. Ein einfaches Print reicht hier auch wieder aus.

Sabotagekontakt = GPIO21
Schlüsselschalter = GPIO20

Hier wäre auch mein Code für diese Zwecke:

Code: Alles auswählen

#!/usr/bin/python
import RPi.GPIO as GPIO
import time
bounce_time = 200
PIN_A = 20
PIN_B = 21
a_value = 0
GPIO.setmode(GPIO.BCM)

GPIO.setup(PIN_A, GPIO.IN)
GPIO.setup(PIN_B, GPIO.IN)

def inputLow(channel):
   if GPIO.input(PIN_A) == GPIO.LOW:
        if GPIO.input(PIN_B) == GPIO.HIGH:
                print ("Tuer offen!")
   else:
        print ("Schloss offen!")

def main():
        GPIO.add_event_detect(PIN_A, GPIO.BOTH, callback=inputLow, bouncetime=bounce_time)
        GPIO.add_event_detect(PIN_B, GPIO.BOTH, callback=inputLow, bouncetime=bounce_time)

        while True:
                time.sleep(1)

if __name__ == '__main__':
   try:
       main()
   except (KeyboardInterrupt, SystemExit):
       print "\nQuit\n"
       GPIO.cleanup()
Im großen ganzen Funktioniert der Code auch.

Jetzt kommt das große Aber, er führt die Print - Ausgabe nicht nur 1x aus, sondern solange bis sich am Zustand der Melder (Sabotagekontakt bzw. Schlüsselschalter) ändert. Und das ist eigentlich nicht gewollt. Es hängt vermutlich mit der while Schleifen zusammen oder? Aber diese muss ja vorhanden sein, damit das Progamm auch dauerhaft läuft. Es soll in Zukunft als Hintergrunddienst laufen.


Ich hoffe mir kann man irgendwie helfen. Danke schon mal im vorraus.

Michael
Hobby-Programmierung - Also KEIN Profi-Progger
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Namen sind nicht nur einfach Schall und Rauch. LOCK_PIN und DOOR_PIN sind bessere Namen als PIN_A und PIN_B.

Es gibt im System mehr als die zwei Zustände auf die du reagierst. Für sowas kann man ein Objekt verwenden, das den komplexen Systemzustand repräsentiert und die Zustandsübergänge verwaltet und dabei Invarianten garantiert. Ungefähr so (ungetestet):

Code: Alles auswählen

#!/usr/bin/python

from __future__ import print_function

import RPi.GPIO as GPIO
import time


BOUNCE_TIME = 200
LOCK_PIN = 20
DOOR_PIN = 21


class State:

    def __init__(self):
        self.lock_is_open = False
        self.door_is_open = False

    def open_lock(self, channel):
        if not self.lock_is_open:
            self.lock_is_open = True
            print('opening lock')

    def close_lock(self, channel):
        if self.lock_is_open:
            self.lock_is_open = False
            print('closing lock')

    def open_door(self, channel):
        if not self.door_is_open:
            self.door_is_open = True
            if self.lock_is_open:
                print('opening door')
            else:
                print('alarm!')

    def close_door(self, channel):
        if self.door_is_open:
            self.door_is_open = False
            print('closing door')


def main():
    state = State()
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(LOCK_PIN, GPIO.IN)
    GPIO.setup(DOOR_PIN, GPIO.IN)
    GPIO.add_event_detect(
        LOCK_PIN,
        GPIO.RISING,
        callback=state.open_lock,
        bouncetime=BOUNCE_TIME,
    )
    GPIO.add_event_detect(
        LOCK_PIN,
        GPIO.FALLING,
        callback=state.close_lock,
        bouncetime=BOUNCE_TIME,
    )
    GPIO.add_event_detect(
        DOOR_PIN,
        GPIO.RISING,
        callback=state.open_door,
        bouncetime=BOUNCE_TIME,
    )
    GPIO.add_event_detect(
        DOOR_PIN,
        GPIO.FALLING,
        callback=state.close_door,
        bouncetime=BOUNCE_TIME,
    )
    while True:
        time.sleep(1)


if __name__ == '__main__':
    try:
        main()
    except (KeyboardInterrupt, SystemExit):
        print('\nQuit\n')
    finally:
        GPIO.cleanup()
Oder du hast einen Pull-Up-Widerstand vergessen und bekommst Britzeln.
In specifications, Murphy's Law supersedes Ohm's.
raspido
User
Beiträge: 31
Registriert: Montag 10. November 2014, 19:42
Kontaktdaten:

Ne die Widerstände sind drin.

Werde dein Script morgen mal testen und Rückmeldung geben, wie es läuft.

Und bislang hab ich nur "Funktionsbasiert" was zusammengezaubert. Objektorientierung war bislang nich so ganz meines. Zumindest im größeren.

So Script angeguckt und ich denke ich weiß wie es Funktioniert. Ich hoffe es tuts auch wie erhofft.

Eine Frage aber noch, wenn ich noch zusätzliche Funktionen einbinde bzw. über Module einbinde, kann ich die ja einfach oben mit ergänzen oder?

Also z.B.:

Code: Alles auswählen

import Alarmmeldung as am
[...]
def open_door(self, channel):
        if not self.door_is_open:
            self.door_is_open = True
            if self.lock_is_open:
                print('opening door')
            else:
                print('alarm!')
                
                am.senden()
[...]
Ist das so richtig gedacht von mir?

Michael
Hobby-Programmierung - Also KEIN Profi-Progger
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Ja, so ist das gedacht.
In specifications, Murphy's Law supersedes Ohm's.
raspido
User
Beiträge: 31
Registriert: Montag 10. November 2014, 19:42
Kontaktdaten:

Sry, dass ich erst heute Melde. Hatte gestern einiges zu tun und kam nicht so richtig dazu es zu testen. Heute kam ich endlich dazu und muss leider rückmelden, es gab Fehlermeldung und ich blicke nicht ganz durch wo der Schuh drückt.

Ich muss sagen, ich hab das jetzt auf einem RPi 1 getestet, somit die GPIO Pins verändert, sollte aber normal ja kein Problem geben.

Folgende Meldung erhalte ich wenn ich das ganze starten will:

Code: Alles auswählen

Traceback (most recent call last):
  File "./interrupt.py", line 59, in <module>
    main()
  File "./interrupt.py", line 50, in main
    GPIO.add_event_detect(LOCK_PIN, GPIO.FALLING, callback=state.close_lock, bouncetime=BOUNCE_TIME)
RuntimeError: Conflicting edge detection already enabled for this GPIO channel
Ich habe nichts anderes am RPi angeschlossen und SPI ist auch nicht "aktiviert".



Michael

Änderung:

Ich habe glaub ich den Fehler gefunden, und zwar das die Pins Doppelt beansprucht werden. Also 1x bei Steigenden Flanken und 1x bei Fallenden Flanken. Nur wenn ich ein Stück je Pin raus nehme, passiert garnichts mehr.
Hobby-Programmierung - Also KEIN Profi-Progger
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Anscheinend kann man mit GPIO.add_event_detect() nicht mehr als eine Callback-Funktion pro Pin registrieren. Dann muss man auf fallende und steigende Flanken eben selber testen:

Code: Alles auswählen

#!/usr/bin/python

from __future__ import print_function

import RPi.GPIO as GPIO
import time


BOUNCE_TIME = 200
LOCK_PIN = 20
DOOR_PIN = 21


class State:

    def __init__(self):
        self.lock_is_open = False
        self.door_is_open = False

    def lock_callback(self, channel):
        if GPIO.input(channel):
            self.open_lock(channel)
        else:
            self.close_lock(channel)

    def door_callback(self, channel):
        if GPIO.input(channel):
            self.open_door(channel)
        else:
            self.close_door(channel)

    def open_lock(self, channel):
        if not self.lock_is_open:
            self.lock_is_open = True
            print('opening lock')

    def close_lock(self, channel):
        if self.lock_is_open:
            self.lock_is_open = False
            print('closing lock')

    def open_door(self, channel):
        if not self.door_is_open:
            self.door_is_open = True
            if self.lock_is_open:
                print('opening door')
            else:
                print('alarm!')

    def close_door(self, channel):
        if self.door_is_open:
            self.door_is_open = False
            print('closing door')


def main():
    state = State()
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(LOCK_PIN, GPIO.IN)
    GPIO.setup(DOOR_PIN, GPIO.IN)
    GPIO.add_event_detect(
        LOCK_PIN,
        GPIO.BOTH,
        callback=state.lock_callback,
        bouncetime=BOUNCE_TIME,
    )
    GPIO.add_event_detect(
        DOOR_PIN,
        GPIO.BOTH,
        callback=state.door_callback,
        bouncetime=BOUNCE_TIME,
    )
    while True:
        time.sleep(1)


if __name__ == '__main__':
    try:
        main()
    except (KeyboardInterrupt, SystemExit):
        print('\nQuit\n')
    finally:
        GPIO.cleanup()
Immer noch ungetestet, weil ich keinen Raspi habe.
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
noisefloor
User
Beiträge: 3853
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

du kannst pro Pin noch 1x `add_event_detect` setzen (aber an jedes Event durchaus mehrere Callbacks binden).

Ein mögliche Lösung für dein Problem ist auf dieser Webseite beschrieben.

Gruß, noisefloor

EDIT: pillmuncher war schneller :-)
raspido
User
Beiträge: 31
Registriert: Montag 10. November 2014, 19:42
Kontaktdaten:

@pillmuncher: Ich habe Code getestet und komme irgendwie noch nich richtig dahinter. Ich hab ein anderen Code aus dem I-Net vorher mal getestet und da kam non Stop Ausgaben, was nicht gewollt war, es sollte nur dann eine Ausgabe / Ereignis erfolgen, wenn sich der Zustand änderte und nicht ständig. Nur bei dem Code von dir, kommt mal etwas und dann mal etwas nicht. Wobei, dass wo nichts kommt fast häufiger ist. Ich hab den Code auch minimal geändert, aber das sollte eigentlich nicht das Problem sein. Ich habe folgende Zeilen weiter nach oben unter dem Bereich, wo die Pins "ausgewählt" werden geschoben:

Code: Alles auswählen

GPIO.setmode(GPIO.BCM)
GPIO.setup(LOCK_PIN, GPIO.IN)
GPIO.setup(DOOR_PIN, GPIO.IN)
Sonst hab ich testweise nur bei den Funktionen das channel gegen entsprechende Pinbezeichnung getauscht. Wollte mal gucken ob sich dann was tut und so.

@noisefloor: Hast du ggf. auch sonst noch eine Idee? Hab den Code getestet und bei Steigendenflanken reagiert wie er soll. Also er gibt eine Ausgabe und gut. Jedoch bei Fallender Flanke gibt er ständig Ausgaben aus. Das stört total :-( Ich komme irgendwie nicht so wirklich dahinter.


Werde Heute Abend mal den Taster und den Schalter auf einem Reststückplatine löten und aus dem Steckbrett befreien. Ich hoffe es ist nicht das mein Problem. Also das Steckbrett. Aber werden wir sehen.



Michael
Hobby-Programmierung - Also KEIN Profi-Progger
Benutzeravatar
noisefloor
User
Beiträge: 3853
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

also ich habe zwar 2 Raspis, aber keine Bauteile, um das hier mal schnell nachzustellen.

Poste bitte nochmal die letzte Version deines Codes, wo das mit der steigenden Flanke reagiert und mit der fallenden die Dauerausgabe macht.

Gruß, noisefloor
raspido
User
Beiträge: 31
Registriert: Montag 10. November 2014, 19:42
Kontaktdaten:

@noisefloor: Das ist der Code aus deinem Link, den du weiter oben gepostet hast. Habe einfach Pinnummer geändert und los.

Also das hier: http://raspi.tv/2014/rpi-gpio-update-an ... ling-edges



Michael
Hobby-Programmierung - Also KEIN Profi-Progger
Benutzeravatar
noisefloor
User
Beiträge: 3853
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

axo... hast du mal testweise noch die `bouncetime` eingebaut? Viele Taster sind ja nicht entprellt.

Gruß, noisefloor
raspido
User
Beiträge: 31
Registriert: Montag 10. November 2014, 19:42
Kontaktdaten:

Ich habs zuerst nicht ergänzt und dann passierte es so, wie beschrieben.

Habe gerade nochmals bouncetime eingebaut und plötzlich wurde nur noch Fallende Flanken erkannt. Die Steigende Flanke wurde scheinbar registriert, aber nicht mehr ausgegeben. Weil das dauernd neukommen von Meldungen pausiert, zumindest solange der Taster gedrückt wurde.

Wie gesagt werde meinen Schalter & Taster mit Widerstände und so auf einem Stückchen Restplatine zusammen löten. Nicht das dort was auch noch zusätzlich stört. Nächste Woche wollte ich mir noch Kondensatoren besorgen um die bouncetime "Hardwaremäßig" zu ersetzen.

Softwaremäßig gefällt mir zwar das andere noch ein wenig besser, weil dort schon einiges drin ist. Aber erstmal wäre es schön, wenn es überhaupt laufen würde. :K


Michael
Hobby-Programmierung - Also KEIN Profi-Progger
raspido
User
Beiträge: 31
Registriert: Montag 10. November 2014, 19:42
Kontaktdaten:

So ich hab noch mal in meinem Kästchen mit den Kleinteilen rumgewühlt und bin fündig geworden. Ich hab entsprechende Kondensatoren gefunden und nun die Taster und Schalter mittels der Schaltung auf eine Platine gebracht und Zusammengelötet:

Bild

An den Raspberry Pi angeschlossen und nochmals den Code von "pillmuncher" mir dazu genommen und getestet. Nun läufts wie gehofft und der Code funktioniert auch Top.

Nur leider musste ich feststellen, meine gewählten Schlüsselschalter oder die entsprechenden Lötstellen, wo ich die Drähte angelötet habe nicht ganz optimal sind. Werde das aber nachher noch mal genauer untersuchen. Ggf. werde ich den Kondensator noch mal etwas umbauen und direkt am Schlüsselschalter montieren. Und gucken ob man dann bessere Ergebnisse erziehlt.

Den bisschen Wackeln an den Lötfahnen und er löst ein Ereignis aus, ggf. werde ich mir aber auch Gedanken machen müssen ob man da sonst noch was optimieren kann.

Oder hat jemand vielleicht von euch noch eine Idee?

Übrigens hab ich mir folgende Schlüsselschalter gekauft: Schlüsselschalterlink

Ich weiß recht günstig, sollten normal aber eigentlich ihren Zweck erfüllen. Oder hat jemand "optimalere" gefunden die nicht gleich richtig teuer sind?



Michael

PS:

@alle: Danke bis hier hin

@pillmuncher: Danke für den umfangreichen und super ausgeführten Code. Ich werd da nur noch paar Feinheiten anpassen müssen, großteils erfüllt der so schon mein Zweck. Werde mich aber noch mal intensiver auseinander setzen dürfen mit Python und Co.
Hobby-Programmierung - Also KEIN Profi-Progger
Antworten