GPIO: HBouncetime mehrere Logeinträge

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
jusu
User
Beiträge: 2
Registriert: Donnerstag 13. September 2018, 09:37

Hallo Zusammen,
Ich schlage mich momentan mit einem Problem rumm, bei dem ich wirklich nicht weiter komme. Habe schon einiges probiert, konnte aber noch auf kein Ergebnis kommen.
Es geht um diesen Code:

Code: Alles auswählen

#!/usr/bin/env python
#coding: utf8 

import time
import sys, os
import subprocess
import datetime
import RPi.GPIO as GPIO
 
# Zählweise der Pins festlegen
GPIO.setmode(GPIO.BCM)
# GPIO 21 als Eingang festlegen
GPIO.setup(21, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)

#Grenzwerte Microstops,
#OGW (oberer Grenzwert definiert die Zeit ab wann Störung ermittelt wird)     

OGW = 60#in sek
MS1 = 3
MS16 = 16
#Änderungen der Werte erst nach Neustart wirksam


# Initialisierung der Variablen
i = 0
mstp = 0
x = 0
strg = 0
microges1 = 0
microges16 = 0

sg = 0

MG1 = 0
MG16 = 0
 
# Ereignis-Prozedur für Eingang HIGH
def doIfHigh(channel):
    # Zugriff auf Variablen ermöglichen
    global i, mstp, strg, x, mg, sg,MS1, MS16, MG1, MG16, microges1, microges16
    
    
    mstp = 0
    strg = 0
    #Micostrops werden wieder auf Null gesetzt
    MG1 = 0
    MG16 = 0
    
    
    # x wird durch die whileschleife jede Sekunde um 1 erhöht
    x = x + 1

    # Schleifenzähler erhöhen
    i = i + 1
    #Um 00:00:00 werden die GesamtMicrostops zurück gesetzt, damit am nächsten Tag wieder alles Null ist
    if time.strftime('%H:%M:%S') == "00:00:00":
        microges1 = 0
        microges16 = 0
        sg = 0

    if x >= OGW:
        
        strg = strg + 1
        sg = sg + 1
        x = 0

    elif x>=MS16:
        
        MG16 = MG16 + 1
        microges16 = microges16 + 1
        x = 0
    elif x>=MS1:
        
        MG1 = MG1 + 1
        microges1 = microges1 + 1
        x = 0
    else:
        x = 0
        

        
    
    #Datei öffnen und schreiben
    Zieldatei = "/home/pi/Schreibtisch/Messwerte/log_"+time.strftime('%Y%m%d')+".txt"
    f=open(Zieldatei,'a')
    if i % 1000==0:#Bei jeder 100' Zählung wird eine Zusammenfassung in die Datei geschrieben
        time.sleep(1.0)
        f.write('{:%d.%m.%Y %H:%M:%S}'.format(datetime.datetime.now())+"; MSI: "+str(MG1)+";MSII: "+str(MG16)+"; Stoerung: "+str(strg)+"; MSGI: "+str(microges1)+"; MSGII: "+str(microges16)+"; STG: "+str(sg)+'\n' )
        
    else:
        time.sleep(1.0)
        f.write('{:%d.%m.%Y %H:%M:%S}'.format(datetime.datetime.now())+"; MSI: "+str(MG1)+";MSII: "+str(MG16)+"; Stoerung: "+str(strg)+'\n')
       
    f.close()
  #Datei öffnen und schreiben

    
    #Testfunktion: Wenn Eingang HIGH ist, Ausgabe in Datei schreiben
    #time.sleep(1.0)
    print ("Eingang HIGH " + str(i))
    print ("Messwert: ", i, "MSI: ", str(MG1),"MSII: ", str(MG16), "Stoerung: ", strg,"--MSGI: ", str(microges1),"MSGII: ", str(microges16),"STG: ", str(sg))

    
 
# Ereignis deklarieren

GPIO.add_event_detect(21, GPIO.RISING, callback = doIfHigh, bouncetime = 100) #bouncetime in mS, 1/bouncetime = maximale Stückzahl pro Sekunde, hohe Bouncetime verbessert Messung
time.sleep(0.25)
 
# Eigentlicher Programmablauf
while 1:
    x=x+1
    time.sleep(1.0)

Problem ist, dass Bouncetime nicht richtig funktioniert, da es in meiner .txt Datei öfters doppelteinträge gibt, obwohl eigentlich nur ein Event durchgeführt wurde. Ich habe auch schon recherchiert, dass das ein öfter auftretendes Problem bei Bouncetime ist.
Um dem Problem entgegenzuwirken habe ich vor dem f.write ein sleep von 1. Sekunde eingebaut, dass nur jede Sekunde ein Eintrag in die Logdatei geschrieben wird.
Leider funktioniert dies nicht richtig und es gibt trotzdem Dopplungen.

Habe ich ein Brett vor dem Kopf, oder warum funktioniert das nicht?

Über Hilfe wäre ich sehr Dankbar!

Viele Grüße,
jusu
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du begehst einen Kardinalfehler in der GPIO-Programmierung: in deiner Callback-Funktion fuer ein Event sollte so wenig Code wie moeglich stehen. Der sollte nur das absolut minimal notwendige tun, um den Hauptthread, der bei dir nur sinnlos im Kreis roedelt darueber zu informieren, dass es zu einem Ereignis gekommen ist.

Dazu bietet sich eine queue an, die man dann im Main-Thread pruefen kann, und dabei gleichzeitig nicht mehr Zeit verschlafen muss:

Ungetestet:

Code: Alles auswählen

from functools import partial
from Queue import Queue
...

def callback(queue, channel):
        queue.put(channel)

def setup():
      queue = Queue()
       ...
       GPIO.add_event_detect(21, GPIO.RISING, callback = partial(callback, queue), bouncetime = 100)
       return queue

def main():
       queue = setup()
       while True:
            event = queue.get() # blockiert, bis was da ist
            ... # schreib in deine datei etc.
Desweiteren habe ich die Erfahrung gemacht, dass GPIO ziemlicher Mist ist. Wenn du verlaesslichere Funktion willst, benutz pigpio.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Und es fallen noch diverse andere Dinge auf:

- deine Reset-Funktionalitaet ist sehr fragil, weil sie darauf baut, dass der Code genau in der Sekunde laeuft, wo die Zeit 00:00 ist. Das kann aber nicht garantiert werden. Viel besser ist stattdessen in meinem Beispiel den Code so zu aendern, dass du einen Zeitstempel (datetime.datetime()) bekommst, wann das Ereignis stattgefunden hat. Das hat eine Tag-Eigenschaft (day), und die kannst du vergleichen mit dem Tag des letzten Ereignisses. Wenn sich das unterscheidet, resettest du & speicherst den neuen Tag als Vergleichstag.
- Dateien oeffnet man mit with

Code: Alles auswählen

with open(dateiname, mode) as outf:
       outf.write(...)
Dann muss man auch kein close machen.

- du solltest die Datei nur oeffnen, wenn du auch was schreiben willst. Statt immmer, und dann einfach nix tun.
- das ganze + str Gefummel ist unuebersichtlich und wird so nicht gemacht. Stattdessen benutzt man format:

"Messwert: {} MSI: {}, MSII: {} Stoerung: {} ....").format(i, MSG1, MG16, strg, migcroges1, ...)

Das ist natuerlich nur ein Ausschnitt.

- statt einen Zaehler mitlaufen zu lassen, der vermeintlich Sekunden zaehlt, und das nicht wirklich tut weil time.sleep(1) nur ungefaehr eine Sekunde schlaeft wenn's gut laeuft, und im worst case viel laenger, merk dir den Zeitpunkt des letzten Ereignisses (siehe ersten Punkt), und bestimme die Differenz zur tatsaechlich verflossenen Zeit.
jusu
User
Beiträge: 2
Registriert: Donnerstag 13. September 2018, 09:37

Hallo __deets__,
Vielen Dank für das schnelle Feedback. Mit dem Vergleich von zwei Zeitstempeln klingt wirklich sehr gut. Danke für die Idee!

Bei den weiteren Punkten hast du auch echt recht, so wirklich sauber ist der Code echt noch nicht :roll:

Danke :-)
Antworten