Eingang soll mind. 40ms gesetzt sein, um zu schalten

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
unique24
User
Beiträge: 69
Registriert: Donnerstag 5. Juli 2018, 14:51

Hallo,

für einen Raspi möchte ich, das ein Eingang mind. 40ms anliegen muss, um ihn zu übernehmen.
Das debounce löst ja gleich aus und sperrt danach .. was ich so nicht möchte

würde funktionieren, aber ist der Code zu kompliziert?

Zuerst muss ich prüfen ob der Eingang sich geändert hat
Dann muss ich prüfen ob die zeit mind. 40ms waren
Dann muss ich prüfen das die Funktion nur einmal schaltet, wenn der Eingang geschlossen bleibt

Code: Alles auswählen

    def checkTriggers(self):
        while True:
            current = datetime.datetime.now().timestamp() * 1000
            
            # wenn sich der Eingang ändert
            sw1 = GPIO.input(Coop.PIN_SWITCH_1)
            if (sw1 != Coop.PIN_SWITCH_1_PREV):               
                # Letzten Wert halten
                Coop.PIN_SWITCH_1_PREV = sw1
                Coop.PIN_SWITCH_1_LAST_TIMESTAMP = current
            else:
                if((sw1 == Coop.PIN_SWITCH_1_PREV) and (Coop.PIN_SWITCH_1_LAST_TIMESTAMP + Coop.TRESHOLD) < current):
                    # Wert nur einmal auslösen
                    if(Coop.PIN_SWITCH_1_BLOCK != sw1):
                        Coop.PIN_SWITCH_1_BLOCK = sw1
                        # Steigende Flanke
                        if(not sw1):
                            logger.info("Schalter rechts bei Tür gedrückt")
       time.sleep(0.01)
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

@unique24: um Zeiten zu messen, ist time.monotonic besser als datetime.now().timestamp().
if ist keine Funktion, die ganzen Klammern also unsinnig. Coop kommt aus dem Nichts und Du schreibst dort ständig in irgendwelche Konstanten, was total verwirrend ist.
Das time.sleep ist falsch eingerückt.
Die while-Schleife wird ja nie verlassen.
Wenn sw1 != Coop.PIN_SWITCH_1_PREV, dann ist garantiert sw1 == Coop.PIN_SWITCH_1_PREV.

Code: Alles auswählen

    def check_triggers(self, coop):
        last_level = None
        last_event_level = None
        while True:
            current = time.monotonic()
            level = GPIO.input(coop.PIN_SWITCH)
            if level != last_level:
                # wenn sich der Eingang ändert
                last_level = level
                coop.timestamp = current
            elif coop.timestamp + coop.treshold < current and last_event_level != level:
                # Wert nur einmal auslösen
                last_event_level = level
                if not level:
                    # Steigende Flanke
                    logger.info("Schalter rechts bei Tür gedrückt")
            time.sleep(0.01)
Das ist aber ein Busy-Wait-Loop.

Code: Alles auswählen

    def check_triggers(self, coop):
        # auf Tastendruck warten
        GPIO.wait_for_edge(channel, GPIO.RISING)
        # 40ms auf Loslassen warten
        if GPIO.wait_for_edge(channel, GPIO.FALLING, timeout=40) is None:
            logger.info("Schalter rechts bei Tür gedrückt")
unique24
User
Beiträge: 69
Registriert: Donnerstag 5. Juli 2018, 14:51

Hallo,

danke, da habe ich einiges zu überarbeiten. Aber vorweg:
Dein letzter Code ... der schaltet wenn man den Taster los lässt, oder?

Wenn ich den Eingang schließe und mind 40ms geschlossen habe, soll danach die Ausgabe erfolgen ... auch wenn der Eingang noch geschlossen ist.

Und mit ".wait_for_edge" ... benötige ich pro Eingang eine Funktion, richtig? oder kann ich für "Channel" ein Array angeben?

EDIT:

hab nun dies gefunden:

Code: Alles auswählen

# wait for up to 5 seconds for a rising edge (timeout is in milliseconds)
channel = GPIO.wait_for_edge(channel, GPIO_RISING, timeout=5000)
if channel is None:
    print('Timeout occurred')
else:
    print('Edge detected on channel', channel)
Bewirkt das Timout das das Edge mind. 5sek anliegen muss? Also genau was ich suche?
Wenn innerhalb der 5sek der Eingang wieder abfällt, bekomme ich ein Timeout?
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

Nee, umgekehrt, wenn nicht innerhalb von 5 Sekunden der Eingang ansteigt (raising) bekommst Du einen Timeout.
unique24
User
Beiträge: 69
Registriert: Donnerstag 5. Juli 2018, 14:51

Ahhh ... jetzt hab ich es verstanden. Das macht den code sehr schlank :-)

Heißt aber ich benötige für jeden Eingang einen eigenen Thread? Denn der Code wartet ja an der Position auf einen Input, oder?

Herzlichen Dank!
unique24
User
Beiträge: 69
Registriert: Donnerstag 5. Juli 2018, 14:51

oder kann ich es so kombinieren?:

Code: Alles auswählen

def my_callback(channel):
        # 40ms auf Loslassen warten
        if GPIO.wait_for_edge(channel, GPIO.FALLING, timeout=40) is None:
            logger.info("Schalter rechts bei Tür gedrückt")

GPIO.add_event_detect(channel, GPIO.RISING, callback=my_callback)  # add rising edge detection on a channel
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

Wie sieht denn dein Hauptprogramm aus, in dem du die ganzen Events verarbeitest?
Benutzeravatar
__blackjack__
User
Beiträge: 14251
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Eventuell lohnt sich auch ein Blick auf das `gpiozero`-Modul anstelle von `RPi.GPIO`. Das hat beispielsweise ein `Button`-Objekt dem man mit dem Attribut `when_held` eine Rückruffunktion geben kann, die nach `hold_time` Sekunden (auch ein Attribut) etwas macht.

Edit: Also auf das Beispiel von unique24 übertragen:

Code: Alles auswählen

#!/usr/bin/env python3
from gpiozero import Button

BUTTON_PIN = ...


def callback(button):
    logger.info("Schalter rechts bei Tür gedrückt")


def ...(...):
    ...
    button = Button(BUTTON_PIN)
    button.hold_time = 0.04
    button.when_held = callback
    ...
“All tribal myths are true, for a given value of 'true'.” — Terry Pratchett, The Last Continent
unique24
User
Beiträge: 69
Registriert: Donnerstag 5. Juli 2018, 14:51

gpiozero ist interessant ... aber ich bräuchte auch die 40ms beim loslassen ..."when_held" ist nur beim schließen ... gibt es auch eine Funktion beim loslassen?

Neben Tastern habe ich auch einen Rauchmelder und 2 Reed Kontakte ... dort soll die Flanke erkannt werden und jeder Status muss mind. 40ms anliegen, um ihn zu übernehmen.
unique24
User
Beiträge: 69
Registriert: Donnerstag 5. Juli 2018, 14:51

Hallo,

ich benötige wohl doch nochmal bitte Eure Hilfe.

Mein code verkürzt:

Code: Alles auswählen

# !/usr/bin/env python3
# -*- coding: utf-8 -*-

from threading import Thread
import time
import RPi.GPIO as GPIO

PIN_SENSOR_TOP = 5
PIN_SENSOR_BOTTOM = 24


def setup_pins():
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(PIN_SENSOR_BOTTOM, GPIO.IN)
    GPIO.setup(PIN_SENSOR_TOP, GPIO.IN)


def check_trigger_sensor_top():
    while True:
        # auf Tastendruck warten
        GPIO.wait_for_edge(PIN_SENSOR_TOP, GPIO.FALLING)
        # 40ms auf Loslassen warten
        if GPIO.wait_for_edge(PIN_SENSOR_TOP, GPIO.RISING, timeout=40) is None:
            print("check_trigger_sensor_top")


def check_trigger_sensor_bottom():
    while True:
        # auf Tastendruck warten
        GPIO.wait_for_edge(PIN_SENSOR_BOTTOM, GPIO.FALLING)
        # 40ms auf Loslassen warten
        if GPIO.wait_for_edge(PIN_SENSOR_BOTTOM, GPIO.RISING, timeout=40) is None:
            print("check_trigger_sensor_bottom")


class Coop:
    def __init__(self):
        print("start")
        setup_pins()

        t_sensor_bottom = Thread(target=check_trigger_sensor_bottom)
        t_sensor_bottom.setDaemon(True)
        t_sensor_bottom.start()

        t_sensor_top = Thread(target=check_trigger_sensor_top)
        t_sensor_top.setDaemon(True)
        t_sensor_top.start()

        while True:
            time.sleep(4)


if __name__ == "__main__":
    Coop()
Schließe ich den TOP erhalte ich:
check_trigger_sensor_top
check_trigger_sensor_top
check_trigger_sensor_top
Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/lib/python3.7/threading.py", line 917, in _bootstrap_inner
self.run()
File "/usr/lib/python3.7/threading.py", line 865, in run
self._target(*self._args, **self._kwargs)
File "test.py", line 29, in check_trigger_sensor_bottom
GPIO.wait_for_edge(PIN_SENSOR_BOTTOM, GPIO.FALLING)
RuntimeError: Error waiting for edge

check_trigger_sensor_top
check_trigger_sensor_top

bei BOTTOM erhalte ich gar eine Ausgabe
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

GPIO ist nicht gerade für seine Stabilität bekannt, wenn dann noch Threads dazu kommen, ...
__init__ ist dazu da, eine Instanz zu initialisieren, nicht, dass die Methode ewig läuft. Die Klasse an sich ist auch total nutzlos und kann weg.
`as` bei import ist dazu da, das Modul umzubenennen, GPIO wird aber gar nicht umbenannt.
Am Ende muß GPIO.cleanup aufgerufen werden.
unique24
User
Beiträge: 69
Registriert: Donnerstag 5. Juli 2018, 14:51

Ok, dann bleibt es. bei meinem Loop :-)

Hab die Codezeilen hier drastisch reduziert und spiegeln nicht mein Programm wieder. Aber danke!
Antworten