Seite 1 von 1

Timer starten bei Event

Verfasst: Mittwoch 16. Oktober 2019, 15:10
von ernschd
Hallo,

ich habe einen Raspberry, den ich per Drucktaster herunterfahre. Dazu habe ich das Script von hier für mich angepasst:

Code: Alles auswählen

...
# Interrupt-Routine fuer die Taste
def buttonISR(pin):
  global duration
  global LOG_FILE
  global myScript

  if not (GPIO.input(pin)):
    # Taste gedrueckt
    if duration == 0:
      duration = time.time()
  else:
    # Taste losgelassen
    if duration > 0:
      elapsed = (time.time() - duration)
      duration = 0
      if elapsed >= T_SHUT:
        subprocess.call(['python', 'sndsystemshutdown.py'])
        print('Shutdown: System halted');
        subprocess.call(['shutdown', '-h', 'now'], shell=False) 
      elif elapsed >= T_PRELL:
        print('running ' + myScript); 
...
Ein kurzer Tastendruck führt mein Script aus, ein Druck länger als 5 Sekunden fährt den Raspberry herunter.
Ich würde nun gerne ein Audiosignal abspielen, damit der Anwender weiß, wann die 5 Sekunden vorbei sind.

Ich hatte vor, einen Timer zu starten, wenn die Taste gedrückt wird, und zu beenden, wenn sie unter 5 Sekunden losgelassen wird. Nach 5 Sekunden soll das Signal abgespielt und der Timer wieder beendet werden.
Leider passiert einfach gar nichts.

Hat jemand eine Idee, wie ich das Problem lösen könnte?

Vielen Dank im voraus.

Re: Timer starten bei Event

Verfasst: Mittwoch 16. Oktober 2019, 16:49
von __blackjack__
@ernschd: Erst mal Kritik zu dem Programm von der verlinkten Webseite:

Eingerückt wird in Python mit vier Leerzeichen pro Ebene, nicht mit zwei. Da das alle machen, ist es blöd wenn jemand aus der Reihe tanzt, weil Einrückung in Python ein wichtiger Bestandteil der Syntax ist und nicht nur reine Kosmetik.

Dann verwendet das Programm noch Python 2. Sollte man echt nicht mehr machen.

``as`` bei Importen ist zum Umbenennen. `GPIO` wird aber gar nicht umbenannt, also ist das der falsche Weg.

Auf Modulebene gehört nur Code der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. ``global`` hat in einem sauberen Programm nichts zu suchen.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase). `buttonISR` ist also unkonventionell. Aber auch das ISR und das gefasel von ”Interrupts” in den Kommentaren ist Unsinn. Das ist keine „interrupt service routine“ denn dazu müsste man ja irgendwie auf Interrupts heran kommen können. Das können Kernelmodule, aber keine Programme im ”Userland”. Und ob hinter einem Aufruf dann ein paar Schichten drunter tatsächlich technisch gesehen ein Interrupt die Ursache war, weiss man nicht. Je nach verwendeter Bibliothek stehen die Chancen dafür unterschiedlich.

`duration` ist als Name auch falsch sowie der Kommentar „Zeitdauer des Tastendrucks“. Da wird ein Zeit*punkt* gespeichert, keine Dauer. Und als Wert für ”kein Wert” sollte man auch keine magische Zahl nehmen, sondern einen Wert der wirklich kein Zeitpunkt sein kann. Für so etwas gibt es `None`.

Statt `time()` sollte man `monotonic()` nehmen, sonst kann man in bestimmten Situation probleme bekommen, zum Beispiel bei der Umstellung zwischen Sommer/Winterzeit, oder wenn der ntpd eine eventuell falsch laufende Uhr korrigiert.

Warnungen sind nicht dazu da unterdrückt zu werden, sondern man sollte die Ursachen beseitigen. Das ist bei `GPIO` in der Regel das am Ende eines Programms nicht aufgeräumt wurde (`GPIO.cleanup()`) und welch Wunder: Dieses Programm macht das auch nicht.

`sys.exit()` sollte man nur aufrufen wenn man tatsächlich einen Rückgabecode an den Aufrufer zurückmelden möchte, nicht weil man den Code nicht so geschrieben hat, dass das Programm auch ganz normal ans Ende kommen kann. Dazu sind die innere ``while``-Schleife und das ``try``/``except`` falsch herum verschachtelt.

Zwischenstand (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
"""
shutdown/reboot Raspberry Pi mittels Taste
"""
import os
import subprocess
import sys
import syslog
import time

from RPi import GPIO

#: GPIO-Port, an dem die Taste gegen GND angeschlossen ist GPIO 5, Pin 29 (GND
#: waere daneben auf Pin 30)
BUTTON_PIN = 5

#: Schwelle für Shutdown (in Sekunden), wird die Taste kürzer gedrückt, erfolgt
#: ein Reboot.
SHUTDOWN_THRESHOLD = 3

#: Entprellzeit für die Taste.
DEBOUNCE_DELAY = 0.05


class ShutdownButton:
    def __init__(self, pin):
        self.pin = pin
        self.start_time = None
        GPIO.setup(self.pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
        GPIO.add_event_detect(self.pin, GPIO.BOTH, callback=self.on_edge)

    def on_edge(self, pin):
        assert pin == self.pin
        if not GPIO.input(self.pin):
            # Taste gedrueckt
            if self.start_time is None:
                self.start_time = time.monotonic()
        else:
            # Taste losgelassen
            assert self.start_time is not None
            if self.start_time > 0:
                elapsed_time = time.monotonic() - self.start_time
                self.start_time = None
                if elapsed_time >= SHUTDOWN_THRESHOLD:
                    syslog.syslog("Shutdown: System halted")
                    subprocess.run(["shutdown", "-h", "now"])
                elif elapsed_time >= DEBOUNCE_DELAY:
                    syslog.syslog("Shutdown: System rebooted")
                    subprocess.run(["shutdown", "-r", "now"])


def main():
    if os.getuid() != 0:
        print("Programm benoetigt root-Rechte!")
        sys.exit(1)

    try:
        GPIO.setmode(GPIO.BCM)
        _button = ShutdownButton(BUTTON_PIN)
        syslog.syslog("Shutdown.py started")
        try:
            while True:
                time.sleep(300)
        except KeyboardInterrupt:
            syslog.syslog("Shutdown terminated (Keyboard)")
            print("Bye")
    finally:
        GPIO.cleanup()


if __name__ == "__main__":
    main()
Zum Problem: Die Funktion wird ja nur bei Flanken aufgerufen also kann das so ja auch nicht funktionieren. Du könntest Dir mal `threading.Timer` anschauen.

Aus einem Python-Programm heraus Python-Programme zu starten riecht komisch. Warum kann man die nicht als Module verwenden?