Alternative zu sleep - Wartezeit, die nicht das gesammte Programm lahm legt

Heute ist Stammtischzeit:
Jeden Donnerstag 20:30 Uhr hier im Chat.
Wer Lust hat, kann sich gerne beteiligen. ;)
  • Hallo!

    Wie einige inzwischen schon wissen, arbeite ich an einer Steuerung meiner Garagentore über die BLYNK-App.

    Ich kann inzwischen:

    • Das Steuersignal an die Tore übergeben
    • Endschalter auslesen
    • gezielt in die Lüftungsstellung fahren

    Nun hab ich noch das Problem, dass das Tor ja bis zu 22 Sekunden braucht um zwischen zwei Endlagen hin und her zu fahren. Wenn man nicht vor dem Tor steht, ist es am Handy schwer nachzuvollziehen, ob sich das Tor noch bewegt (/bewegen müsste) oder ob etwas schiefgegangen ist.

    Deswegen würde ich bei Start der Torfahrt gern für 22 Sekunden ein Signal an die App senden lassen, dass dem Nutzer sagt "Tor ist ggf. beschäftigt". Wenn ich dafür aber sleep nutze, friert mir ja das ganze Programm für 22 Sekunden ein. Das bedeutet, dass ich weder dieses Tor Stoppen noch das andere Starten kann.

    Ich dachte ja an eine while-Schleife und einen "Marker", der immer gesetzt wird, wenn ich an der App eine Taste drücke aber darauf reagiert er nicht:

    Vielen Dank für eure Hinweise!

    Michael

  • Alternative zu sleep - Wartezeit, die nicht das gesammte Programm lahm legt? Schau mal ob du hier fündig wirst!

  • Moinsen,

    Du kannst mit time.monotonic verwenden, wenn du eine damit eine Startzeit erfasst.
    In der Konsequenz kannst du mit dieser Variablen mit einer Wartezeit additiv verknüpfen so das du wieder oder mit einer While-Schleife eine Überschreitung mit der nun neuen aktuellen Systemzeit vergleichen.

    Python
    from time import monotonic
    
    start = monotonic()
    runtime = start + 10 # warte 10 sek.
    
    while monotonic() < runtime:
        pass
    
    # jetzt geht es weiter.

    Diese Abfrage kann man dann auch noch in einen Thread auslagern, so das andere Programmteile unbehelligt weiter laufen können.

    Besser wäre es jedoch wenn du diese Prozesse in eine OOP CLASS auslagerst, diese Wartebefehl dann als Thread ausführst, und dann einen internen Merker nutzen.

    Franky

  • Hallo,

    Ich dachte ja an eine while-Schleife und einen "Marker", der immer gesetzt wird, wenn ich an der App eine Taste drücke aber darauf reagiert er nicht:

    Wo wird denn dein 'Marker' gesetzt wenn die Schleife läuft und wie kommt die Info, in die Schleife?

    Wenn wir deine erste Funktionen nehmen, dann setzt du den Marker = 1, danach Marker = 0 und dann startet die Schleife und es passiert nichts anderes, als das 'iL' hochgezählt wird.

    Wenn du dir Zustände merken willst bzw. auf die Änderung von Zuständen reagieren willst, dann benötigst du eine Klasse.

    Hast du schon ein mal mit Klassen gearbeitet?

    https://docs.python.org/3/tutorial/classes.html

    Kannst du mal den ganzen Code posten?

    Unabhängig von dem Problem, klar wenn du nach 22 Sekunden kein Signal bekommst, dann lief was schief, aber es kann ja sein, dass da schon nach 2 Sekunden etwas blockierst und du den Motor noch 20 Sekunden gegen Anschlag laufen lässt. Ich würde vielleicht die Drehbewegung des Motors und die Endlagen überwachen. Ist zwar auch konstruktiv wieder etwas Aufwand, wäre mir aber lieber, um schneller reagieren zu können. Das aber nur als Anregung, ich kenne deinen Aufbau nicht und die Möglichkeiten, fiel mir eben nur ein.

    Grüße

    Dennis

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

  • Monotonic habe ich probiert (Code sieht unten): Das hat die gleiche Auswirkung wie Sleep: Die Steuerung nimmt das Signal an, setzt es aber erst nach ABlauf der Zeit um.

    Der Marken würde gesetzt, wenn man den Programmteil für dieses Tor oder das Andere neu anspricht, ehe die Zeit rum ist.

    Mit Klassen habe ich noch nicht gearbeitet. Da muss ich mich erstmal reinlesen.

    Hier der Code (der ist aber wirklich sehr unordentlich):

    Das mit der Endlage ist kein Problem: Das Motorsteuergerät bekommt vom Pi pro Tor 1 Signal, welches dann als START oder STOP umgesetzt wird. Die Endlage erkennt der Motor selbstständig.

    Einmal editiert, zuletzt von flokki1 (29. April 2023 um 12:17) aus folgendem Grund: Code eingefügt

  • Zitat

    ... Nun hab ich noch das Problem, dass das Tor ja bis zu 22 Sekunden braucht um zwischen zwei Endlagen hin und her zu fahren. Wenn man nicht vor dem Tor steht, ist es am Handy schwer nachzuvollziehen, ob sich das Tor noch bewegt (/bewegen müsste) oder ob etwas schiefgegangen ist.

    Wie wäre es wenn Du den Motor(Antrieb) via Hallsensor (oder Reedschalter abfragst), GPIO sind ja noch welche frei denke ich, mein Gedanke ist solange Du einen Impuls bekommst läuft er noch in eine der Richtungen!

    RaspbeeryPi 4B 4GB Rev: 1.4, RasbianOS.

    Seit kurzem noch ein Pi4 8GB mit Ubuntu!

  • Wenn du es "professionell" machen willst, kannst du threading benutzen. Python unterstütz das mit einer Standard-Library. Ist aber alles andere als trivial.

    Für einfache Aufgaben ist eine Programmstruktur mit einer sogenannten Superloop einfacher - benutzt man üblicherweise gerne in Mikrocontrollern, geht aber in jeder anderen Umgebung auch. Die Superloop ist (wie der Name ausdrücken soll) eine übergeordnete (Endlos-)Schleife. Die ruft nacheinander reihum alle benötigten Funktionen auf. Keine der Funktionen darf mit Sleep() blockieren. Wo eine bestimmte Zeit lang pausiert werden soll, merkt sich die Funktion die Zeit (mit time() oder millis() oder monotonic() oder was immer sich da gerade anbietet) und bricht einfach untätig ab, wenn sie aufgerufen wird, aber die Zeit für ihre Aktivität noch nicht gekommen ist. So aktiviert man in schneller Folge alle (nötigen) Funktionen, aber diese werden selbst jeweils nur dann tätig, wenn es für sie auch geboten ist. Sinngemäß ungefähr so:

    Die Variable zeit muss zwischen den Funktionsaufrufen erhalten bleiben - klar, wenn sie bei jedem Aufruf der Funktion mit 0 initialisiert wird, klappt das ja nicht. Python bietet meines Wissens leider keine so einfache Möglichkeit wie C mit static, aber man kann das sicher irgendwie hinbiegen, notfalls mit einer globalen Variablen.

    Oh, man kann hier unliebsame Nutzer blockieren. Wie praktisch!

  • Nein, bitte keine globalen Variablen. Hier geben sich einige User große Mühe um Fragen rund ums programmieren zu beantworten und versuchen dabei den Fragenden das Programmieren so beizubringen, dass robuster und erweiterbarer Code entsteht. Wenn wir jetzt mit so was anfangen, ist die ganze Mühe dahin. Das wäre wie wenn du plötzlich Schaltpläne lieferst, die irgendwie hingemurkst sind.

    Wenn man das so wie du machen will, dann muss man 'time' eben zurückgeben und wieder übergeben.

    Grüße

    Dennis

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

  • Deshalb sagte ich "notfalls". Und wozu gibts überhaupt globale Variablen, wenn man die deiner Meinung nach sowieso nicht verwenden darf?

    Wenn du eine vernünftige Art kennst, Variablen in Python innerhalb von Funktionen persistent zu machen, dann her damit. Auch die Formulierung "meines Wissens" habe ich bewußt gewählt, damit Leute wie du Gelegenheit haben, die entsprechenden Konstrukte zu zeigen - wenn sie es denn können.

    Zurückgeben und wieder übergeben von Variablen (deine Formulierung "muss man eben..." ist übrigens bevormundend - kann man, muss man aber keinesfalls) macht den Code jedenfalls auch nicht viel besser - und ist letztlich auch nur der kleine Bruder von globaler Variable, nur durch die Hintertür.

    Oh, man kann hier unliebsame Nutzer blockieren. Wie praktisch!

  • Wenn du eine vernünftige Art kennst, Variablen in Python innerhalb von Funktionen persistent zu machen, dann her damit

    Das hat er doch:

    Wenn du dir Zustände merken willst bzw. auf die Änderung von Zuständen reagieren willst, dann benötigst du eine Klasse.

    ;)

  • Ich glaube nicht, dass ihr mit der Anregung objektorientierter Programmierung den armen flokki da abholt, wo er gerade ist. Diese reflexartigen Antworten, die selten zur Situation passen, finde ich ehrlich gesagt bestenfalls anstrengend.

    Oh, man kann hier unliebsame Nutzer blockieren. Wie praktisch!

  • Ich gehe besser mal nicht darauf ein, bevor mir irgendetwas rausrutscht, was ich später bereue. :no_sad:

    Nur soviel: flokki1 wird ziemlich sicher eine Antwort bekommen, wenn er fragt wie er diese Klasse erstellen kann, falls er/sie/es Probleme damit haben sollte. Sollte mich echt wundern, wenn Dennis89 da nicht helfen würde.

  • flokki1

    In deinem Script benutzt du das Adafruit_DHT das ist veraltet und man benutzt jetzt adafruit_dht.

    Du musst auch ein try-ecxept für den DHT einbauen, da die Dinger auch mal nichts auswerfen können.

    Python
    def read_dht(dht):
        try:
            temperature = dht.temperature
            humidity = dht.humidity
            print(f"Temp: {temperature:.1f} *C, Humidity: {humidity}%")
        except RuntimeError as e:
            print("Reading from DHT failure: ", e.args)
            return 9999, 9999 # gib im Fehlerfall eine unwahrscheinliche Zahl zurück
        return temperature, humidity

    Hast du die BlynkLib von der Blynk-Homepage?

    Besser waere auch gpiozero statt rpi.gpio zu benutzen.

    Du hast da 2x time importiert. Ich würde dir raten alle Importe am Anfang des Skriptes zu packen. Das wird sonst super unübersichtlich. Hast du ja aber selbst schon erkannt.

    Dann setzt du da Variablen wie Marker, iL und iR auf 0 und danach gleich wieder auf 1 ohne damit etwas zu machen.

    Mit solch Flags will man eigentlich das Verhalten eines Programms beeinflussen. Sprich , wenn Flag True, dann mach den einen Teil im Programm, wenn Flag False, dann mach einen anderen Teil des Programms und lass den anderen Teil in Ruhe.

    Hier ein blödes Bsp.:

    Du hast da auch Counter die nur was zählen und nicht benutzt werden.

    Es gibt jeweils 2x

    Python
    Schliesszeit_kurz= 2.25
    Schliesszeit_lang= 16.25

    und 2x:

    Python
    def v3_write_handler(value):

    Mit den Pin Bezeichnungen kann man auch nicht wirklich was anfangen.

    Sind das jeweils 2 Endschalter je Torflügel?

    Python
    GPIO.setup(11, GPIO.IN, pull_up_down = GPIO.PUD_UP)
    GPIO.setup(9, GPIO.IN, pull_up_down = GPIO.PUD_UP)
    GPIO.setup(25, GPIO.IN, pull_up_down = GPIO.PUD_UP)
    PIO.setup(8, GPIO.IN, pull_up_down = GPIO.PUD_UP)

    Dann gehört auch noch eine main() - Funktion in das Script. Alles was kein Import, Klasse, Funktion oder Kostante ist gehört da rein.

    Die Namenwahl für deine Funktionen ist auch schwierig. Da sollte kurz und knapp ein Name stehen der beschreibt was die Funktion macht. Funktionen sollen sich nur um ein Teilproblem kümmern dadurch spart man Code und alles wird leserlicher.

    Wenn du mal irgendwann bei 500 Zeilen Code angekommen bist und da herrscht Chaos, hat sicher keiner mehr Lust sich da rein zulesen.

  • Ich glaube nicht, dass ihr mit der Anregung objektorientierter Programmierung den armen flokki da abholt, wo er gerade ist. Diese reflexartigen Antworten, die selten zur Situation passen, finde ich ehrlich gesagt bestenfalls anstrengend.

    Mit dataclasses etwas holperig, dafür eine schöne Repräsentation. Die Attribute sind frei ausgedacht.

    Python
    Zustand(aktiv=False, auf=False, ab=False)
    Zustand(aktiv=True, auf=True, ab=False)

    Das gleiche noch mal ohne Klasse:

    Python
    {'aktiv': False, 'auf': False, 'ab': False}
    {'aktiv': True, 'auf': True, 'ab': False}

    Die Nachteile ohne Klassen in einer Sprache, dessen Konzept OOP ist, kommen erst in komplexeren Programmen zur Geltung.

    Ob das direkt am Anfang ein sinnvoller Schritt ist, mit Klassen anzufangen, kommt auf die Lernwilligkeit an.

    Das ist sehr viel auf einmal.

  • Hallo und wieder viele Dank für eure Hinweise.

    Das ist ja viel - da wird es schwer auf alles zu Antworten.

    Die Idee mit dem Hall-Sensor würde mich technisch noch am meisten interessieren. Kurzfristig würde ich aber ungern mehr Hardware in der Garage installieren wollen (das wären jetzt wieder 20 m Kabel).

    Ehrlich gesagt, überfordert mich das mit den Klassen ein wenig. Ich habe mir mehrere Videos angeschaut (der lange link auf Englisch ist schwierig für mich) und verstehe noch nicht so recht, was die Klasse da jetzt ändern würde? Das ist doch am Ende nur eine "gesammelte" Variable - oder?

    @keepfear - Auch dir danke für die Hinweise. Ich schrieb ja, dass ich noch aufräumen muss. Einiges ist/ war mir aber noch nicht bekannt. :)

  • Kannst du erklären welche Pins was machen?

    Python
    GPIO.setup(11, GPIO.IN, pull_up_down = GPIO.PUD_UP)
    GPIO.setup(9, GPIO.IN, pull_up_down = GPIO.PUD_UP)
    GPIO.setup(25, GPIO.IN, pull_up_down = GPIO.PUD_UP)
    PIO.setup(8, GPIO.IN, pull_up_down = GPIO.PUD_UP)

    und

    Code
    #GPIO.setup(TuerL, GPIO.OUT)
    #GPIO.output(TuerL, GPIO.HIGH)
    #GPIO.setup(TuerR, GPIO.OUT)
    #GPIO.output(TuerR, GPIO.HIGH)
  • Eine Klasse ist ein Bauplan für eine Instanz der Klasse. Die Instanz hat Methoden und Attribute. Wenn sie keine Methoden hat oder keine Attribute, ist die Nutzung einer Klasse sehr wahrscheinlich überflüssig. Oftmals tendiert man dazu, Klassen zu verwenden, obwohl man nicht unbedingt welche benötigt. Dann gibt es auch Spezial-Methoden, die das Verhalten der Instanz beeinflussen.

    Computer benötigen keine Klassen, um Code auszuführen. Das Konzept der Klassen ist für Menschen gedacht.

    Wenn du z.B. die Funktion open("datei.txt") in Python nutzt, liefert diese Funktion eine Instanz der Klasse TextIOWrapper zurück, der die Methoden read, seek usw. hat.

    Öffnet man eine Datei z.B. im raw-modus (bytes) so liefert open() ein BufferedReader zurück, der auch die Methoden read und seek hat. Dadurch hat man eine gewisse Abstraktion und kann die resultierenden Objekte einfacher verwenden.

    Also indirekt nutzt du schon OOP, ohne es zu wissen.

    Hier geht es darum, ob es wirklich notwendig ist, dass du deine eigenen Klassen schreibst.

    Notwendig ist es nicht. Wie bereits gezeigt, kann man die Nutzung globaler Variablen auch mit Funktionen bewerkstelligen. Das hat noch den weiteren Vorteil, dass man Code einfacher testen kann, wenn alles in kleine Funktionen aufgeteilt ist. Wenn man z.B. Klassen testen will, muss man mehr Code schreiben, um diese zu testen, da Klassen ja mehr als nur eine Methode haben (meistens) und auch Attribute, die getestet werden müssen.

    PS: Globale Variablen sind zulässig, wenn man sie als "Konstanten" anlegt. Es gibt in Python die Konvention, dass groß geschriebene Namen Konstanten sind, die nicht neu zugewiesen werden sollen.

    Ich weiß nicht ob die Zuordnungen so stimmen, aber wenn man so programmiert, gibt es nicht diese Nachfragen.

Jetzt mitmachen!

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