Taster entprellen mit der Funktion bouncetime

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
PatrickH
User
Beiträge: 11
Registriert: Freitag 11. November 2016, 09:10

Hallo,
ich möchte für mein Projekt ein Taster interrupt gesteuert abfragen. Nun hat dieser Taster die unangenehme Eigenschaft zu Prellen.
In meinem Programm habe ich in der Funktioen GPIO.add_event die Möglichkeit dieses mittels bouncetime zu unterdrücken.... theoretisch.
Leider wird diese Funktion mindstens zwei mal ausgeführt. Wenn ich mit extremen Werten für bouncetime heran gehe (z.b. 5000) kommt der zweite event eben später, aber er kommt.

Code: Alles auswählen

#!/usr/bin/python
import RPi.GPIO as GPIO
import time, sys
import urllib2
import datetime

# Variabelen deklarieren
Schalter_PIN = 12   # Eingang GPIO1
EVENT = 'person_enter_leave'
BASE_URL = 'https://maker.ifttt.com/trigger/'
KEY = 'hier ist der key'

# Pin-Nummern verwenden (nicht GPIO-Nummern!)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(Schalter_PIN, GPIO.IN)

#Funktion definieren, um beim Schalterwechsel den IFTTT trigger zu setzen 
def send_event(pin):
    response = urllib2.urlopen(BASE_URL + EVENT + '/with/key/' + KEY)
    print(response.read())

# switch_on-Funktion aufrufen, wenn das Signal wechselt
GPIO.add_event_detect(12, GPIO.BOTH, bouncetime=200)
GPIO.add_event_callback(12, send_event)

# mit minimaler CPU-Belastung auf das Programmende durch Strg+C warten
try:
  while True:
    time.sleep(5)
except KeyboardInterrupt:
  GPIO.cleanup()
  print("\nBye!")
  sys.exit()
Angeschlossen habe ich den Schalter Über einen pull up Widerstand von 10k Ohm (von pin1 3,3Volt) und einen Vorwiderstand von 1k Ohm an Pin 12. Der Schalter liegt nun mit der einen Seite zwischen den beiden Wiederständen und mit der anderen Seite an Masse (pin6)

Es würde mich freuen, wenn hier jemand eine Lösung finden könnte.
LG
Patrick
Zuletzt geändert von Anonymous am Sonntag 13. November 2016, 18:40, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@PatrickH: Nur um das offensichtliche auszuschliessen: Das ist ein *Taster* und kein *Schalter* und Du reagierst explizit auf *beide* Flanken. Dir ist klar, dass die Rückruffunktion aufgerufen wird wenn man den Taster drückt *und* wenn man ihn wieder loslässt‽

Edit: Die Rückruffunktion kann man übrigens schon bei `add_event_detect()` angeben.

Und der Kommentar über das CPU-schonende warten stimmt auch nicht ganz, denn es wird immer noch ab und zu Python-Code in einer Schleife ausgeführt. `signal.pause()` wäre noch ein klein wenig weniger CPU-Zeit. :-)
PatrickH
User
Beiträge: 11
Registriert: Freitag 11. November 2016, 09:10

Hallo BlackJack,
vielen Dank für die schnelle Antwort. Ja, mir ist klar, dass auf beide Flanken reagiert wird. Das ist auch so gewollt. Leider halt pro Flanke zwei Mal.
Das bedeutet der *Taster* wird gedrückt (es werden zwei Events getriggert) .... und losgelassen (es werden wieder zwei Events getriggert)
In Summe vier getriggerte Events anstelle von zweien.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Die traurige Wahrheit ist - RPi.GPIO ist Mist. Auch andere berichten von dem Problem: https://www.raspberrypi.org/forums/view ... 32&t=50833


Nimm besser PIGPIO. Das funktioniert fuer mich deutlich zuverlaessiger. Alternativ kannst du natuerlich auch um dein Problem rumkodieren, indem du dir merkst, ob du steigend/fallend schon bekommen hast. Ist aber natuerlich murksig...
BlackJack

Das ist von 2013, hat das echt noch keiner gefixt? :shock:
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Keine Ahnung. Das Funktionsprinzip von RPi.GPIO ist auch eher mau. Es arbeitet auf den Linux-eigenen GPIO-sysfs-Abstraktionen. Klingt gut, ist faktisch schlecht. Man bekommt nur mit vom Kernel, *das* was passiert ist - aber nicht was. Das muss man dann selber einlesen, und das haengt natuerlich davon ab, wieviel Zeit zwischen Ereignis & Scheduler entscheided "jetzt ist Python mal wieder dran" so vergeht.

Ich empfehle dieser Tage PIGPIO. Das arbeitet mit einem DMA-Kanal, der einfach alle GPIOs permanent mit 1M sampled. Aus einem Echtzeitthread wird man dann benachrichtigt (in C), oder per Socket. Und zwar gleich mit Flanke & ggf. mehreren Events, die seit dem letzten mal angefallen sind. Viel robusteres Design.
PatrickH
User
Beiträge: 11
Registriert: Freitag 11. November 2016, 09:10

Hallo,
vielen Dank für die vielen Antworten.
Aufgrund dieser Infos konnte ich nun auch nachlesen, dass bei event getriggerten Ereignissen RPi.GPIO nicht zu empfehlen ist, und das scheinbar mindestens schon seit 2013.
Okay, soweit so gut. Nun wird mein weiteres Vorgehen PIGPIO einbeziehen.
Dies muss ich vermutlich erst noch installieren...

Code: Alles auswählen

sudo apt-get update
sudo apt-get install pigpio python-pigpio python3-pigpio
jedoch ist die Verwendung einigermaßen verschieden...
Ich würde mich über eine Hilfestellung bezüglich meines Ursprungs Programmes für die Verwendung von PIGPIO freuen
LG
Patrick
PatrickH
User
Beiträge: 11
Registriert: Freitag 11. November 2016, 09:10

Hallo,
nach einiger Recherche habe ich nun folgenden code mit pigpio zum laufen bekommen

Code: Alles auswählen

#!/usr/bin/python
import pigpio
import time, sys
import urllib2
import datetime
import time

# Variabelen deklarieren
Schalter_PIN = 18   # Eingang GPIO1 (BCM)
EVENT = 'person_enter_leave'
BASE_URL = 'https://maker.ifttt.com/trigger/'
KEY = 'hier ist ein key'
pi = pigpio.pi()
counter = 0

# GPIO 18 = Input
pi.set_mode(Schalter_PIN, pigpio.INPUT)
#pi.set_pull_up_down(Schalter_PIN, pigpio.PUD_UP)


#Funktion definieren, um beim Schalterwechsel den IFTTT trigger zu setzen 
def send_event(gpio, level, tick):
    global counter
    if counter < 2: 
        response = urllib2.urlopen(BASE_URL + EVENT + '/with/key/' + KEY)
        print(response.read())
        print(counter)
        counter = counter + 1
    else:
        time.sleep(2)
        counter = 0
# switch_on-Funktion aufrufen, wenn Signal wechselt
pi.callback(Schalter_PIN, pigpio.EITHER_EDGE, send_event)

#auf das Programmende durch Strg+C warten
try:
  while True:
    time.sleep(5)
except KeyboardInterrupt:
  print("\nBye!")
  sys.exit()
das Ergebnis sieht so aus:

Code: Alles auswählen

>>> ================================ RESTART ================================
>>> 
Congratulations! You've fired the person_enter_leave event
0
Congratulations! You've fired the person_enter_leave event
1
Congratulations! You've fired the person_enter_leave event
0
Congratulations! You've fired the person_enter_leave event
1
Congratulations! You've fired the person_enter_leave event
0
Congratulations! You've fired the person_enter_leave event
1
Congratulations! You've fired the person_enter_leave event
0
Congratulations! You've fired the person_enter_leave event
1
Congratulations! You've fired the person_enter_leave event
0
Congratulations! You've fired the person_enter_leave event
1
Congratulations! You've fired the person_enter_leave event
0
Congratulations! You've fired the person_enter_leave event
Bedeutet auch mit pigpio kann ich ein Schalterprellen nicht beseitigen (also die Auswirkungen davon). Scheinbar wird die Anzahl des events gespeichert und nacheinander der callback ausgelöst.
Vielleicht hat jemand Erfahrung und kann mir hierzu einen tipp geben.
LG
Patrick
Zuletzt geändert von Anonymous am Donnerstag 17. November 2016, 21:00, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@PatrickH: Ich sehe da jetzt aber auch nicht das Du die Filterfunktion verwendest‽
PatrickH
User
Beiträge: 11
Registriert: Freitag 11. November 2016, 09:10

Hallo BlackJack
ja, so richtig habe ich keine Filterfunktion a la "bouncetime" in der pigpio gefunden. Daher habe ich mir die Geschichte mit der if - else Abfrage ausgedacht.
Möglicherweise könnte noch die Funktion "edge_detect" zum Ziel führen.
LG
Patrick
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Natuerlich kann PIGPIO nicht magisch das Prellen eliminieren - woher soll es wissen, dass dein Schalter prellt, statt das er so arbeiten soll?

Du kannst zB die Funktion http://abyz.co.uk/rpi/pigpio/python.htm ... tch_filter ausprobieren, oder http://abyz.co.uk/rpi/pigpio/python.htm ... ise_filter
Benutzeravatar
DeaD_EyE
User
Beiträge: 1206
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Du möchtest folgende Funktion verwenden: http://abyz.co.uk/rpi/pigpio/python.htm ... tch_filter

Den Schalter/Taster kann man auch Hardwareseitig mit einem Kondensator zu entprellen.
Wie das geht, kann man hier nachlesen: http://www.mikrocontroller.net/articles/Entprellung.
Im Artikel steht unter anderem auch, dass Hardware-Entprellung immer seltener gemacht wird,
da die Mikrocontroller das Softwaremäßig billiger lösen können.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
PatrickH
User
Beiträge: 11
Registriert: Freitag 11. November 2016, 09:10

Hallo,
der "glitch_filter" oder "noise_filter" hören sich vielversprechend an... das wird mein nächster Versuch.

@DeaD_EyE: hardwareseitig habe ich schon Versuche mit Kondensator gefahren (noch mit rpi.GPIO). War jedoch nur mäßig erfolgreich. Vermutlicherweise weil die Ladekurve vom Kondensator recht lange im "verbotenen Bereich" rum dümpelt.

Sollten die oben genannten Funktionen nicht klappen werde ich das mal mit einem Wechsler und RS-Flipflop testen.

LG
Patrick
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich wuerde ja denken Kondensator + Schmitt-Trigger, und gut ist. Aber wie gesagt, das geht in Software, und eigentlich sollte das mit dem PIGPIO-Ding funktionieren.
PatrickH
User
Beiträge: 11
Registriert: Freitag 11. November 2016, 09:10

Hallo,
also mein glitchFilter macht mir Sorgen...
Eingebunden habe ich diesen auf folgende Art

Code: Alles auswählen

#!/usr/bin/python
import pigpio
import time, sys
import urllib2
import datetime
import time

# Variabelen deklarieren
Schalter_PIN = 18   # Eingang GPIO1 (BCM)
EVENT = 'person_enter_leave'
BASE_URL = 'https://maker.ifttt.com/trigger/'
KEY = 'hier key einfügen'
pi = pigpio.pi()
counter = 0

# GPIO 18 = Input
pi.set_mode(Schalter_PIN, pigpio.INPUT)

#Funktion definieren, um beim Schalterwechsel den IFTTT trigger zu setzen 
def send_event(gpio, level, tick):
 global counter
 print (counter, 'Auslösungen')
 counter = counter + 1

# switch_on-Funktion aufrufen, wenn Signal wechselt
pi.gpioGlitchFilter(Schalter_PIN, 300)
pi.callback(Schalter_PIN, pigpio.EITHER_EDGE, send_event)

#auf das Programmende durch Strg+C warten
try:
  while True:
    time.sleep(5)
except KeyboardInterrupt:
 # GPIO.cleanup()
  print("\nBye!")
  sys.exit()
und als Ergebnis kommt dies...

Code: Alles auswählen

>>> ================================ RESTART ================================
>>> 

Traceback (most recent call last):
  File "/home/pi/Documents/Python Projects/Python 2/person_enter_leave_v3.py", line 28, in <module>
    pi.gpioGlitchFilter(Schalter_PIN, 300)
AttributeError: pi instance has no attribute 'gpioGlitchFilter'
>>>
es scheint, ich habe die Funktion noch nicht richtig angewendet.

@BlackJack: wenn Du mir mal zeigen könntest wie ich den Quelltext in Python-Codebox-Tags setzen kann, würde ich das gerne so tun.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Aus dieser Doku http://abyz.co.uk/rpi/pigpio/python.htm ... tch_filter hast du den Aufruf fuer glitch-Filter offensichtlich nicht. Woher dann?
PatrickH
User
Beiträge: 11
Registriert: Freitag 11. November 2016, 09:10

@_deets_: Doch schon, nur von einer anderen Stelle: http://abyz.co.uk/rpi/pigpio/cif.html#gpioGlitchFilter
Das war offensichtlich nicht so die richtige Stelle
PatrickH
User
Beiträge: 11
Registriert: Freitag 11. November 2016, 09:10

Hurra, der glitch_filter scheint zu klappen.
Kaum macht man es richtig, funktionniert's.
Nun kann ich mich meiner nächsten Aufgabe widmen... mit hilfe der urllib2 meinem aufgerufenen link noch JSON Daten mitzugeben

Vielen Dank für Eure Unterstützung,
Patrick
Antworten