Zeitmessung Heizung (Python)

  • Hallo zusammen, bin ganz neu hier und will gleich mal mein Problem schildern:


    Habe eine Heizung, bei dem ein Ventil schaltet, sollte die Heizung anlaufen (also Öl verbrennen). Diese 230V AC Spannung für das Ventil habe nach oben verlegt und will nun erreichen, dass mein Raspberry Pi 3 fleissig die Einschalt- und Ausschaltzeiten mitschreibt.


    KLARTEXT:
    Ich will eine 230 V AC zu 3,3V DC transformieren. Diese 3,3V soll mein Raspberry dann auslesen. Falls ein Spannungssignal von 3,3V anliegt soll eine Ausgabe ("Start Spannungssignal") mit Zeitangabe erzeugt werden. Sobald 0 V anliegen (Brenner der Heizung aus) soll ebenfalls eine Ausgabe("Ende Spannungssignal") mit Zeitangabe geschrieben werden. Am Ende wird die Dauer zwischen Start und Ende berechnet (Ende-Start).


    HARDWARE (siehe Anhang):
    Habe die 230V AC mithilfe eines Wandlers auf 5V DC transformiert. Dann einen kleinen Spannungsteiler (mit 3x820 Ohm) gebaut, um die Spannung im Verhältnis 1:2 zu teilen. Am Ende habe ich noch einen Widerstand 390 Ohm zum Raspberry eingebracht (nicht in Zeichnung vorhanden; glaube der ist unnötig??).



    SOFTWARE Python:
    Habe das ganze mit einem Interrupt-detect Befehl geschrieben. GPIO input ist GPIO13.
    Falls auf GPIO13 irgendeine Änderung (falling oder rising) stattfindet, wird die Interrupt-Schleife gestartet. Danach wieder in der while Schleifen verweilen.


    Python Programm:



    PROBLEM:


    Wenn ich einen normalen 3,3 V Anschluss eines GPIO's benutze funktioniert das Programm einwandfrei.
    Wenn ich nun den Raspi an die Heizung bzw. an die o.g.Schaltung anschließe, gibt mir der Raspi jede Sekunde eine Spannungsignal Ende zurück. :wallbash:


    Ausgabe im Terminal:



    Der Fehler resultiert aus der nicht vorhandenen Start- Ausgabe (GPIO.RISING).
    Was mache ich falsch? Warum bekomme ich keine Start- Ausgabe, warum wird jede Sekunde ein Ereignis "detected"? Glättet der Transformator die Spannung ungenügend?


    Danke für jede Mithilfe!

    Ich bin doch auch nur ein dummer Maschinenbauer :wallbash:

    Edited once, last by wannabeatbox ().

  • Dein Trafo glättet überhaupt nicht, da kommt auch ein Sinus raus! Ich persönlich würde statt des Trafos ein Relais mit 230v spule einbauen und den Kontakt mit den 3,3v vom pi schalten lassen, dann kommt auch dc an. Oder in deine Schaltung einen brückengleichrichter+glättungselko



    Edit: gerade erst gesehen: was ist denn das genau für ein Trafo?

    Ersetzt der Pi das Licht bei Nacht, ist es der Prozessor der gleich kracht :)

    Edited once, last by Dave2526 ().

  • Hallo Dave Danke für deine Antwort!


    Eigentlich dachte ich schon, dass der Trafo glättet. Das Symbol im Schaltbild für den Trafo ist nicht sinnbildlich, hab irgendein Symbol genommen.
    Im Trafo sind schon noch 4 Dioden, Kondensatoren usw. verbaut!
    Hab so einen :
    http://www.ebay.com/itm/90-240…d10829:g:jtMAAOxyUgtTOGFy


    Am Widerstand zum Raspberry hin (nicht in Schaltung eingezeichnet) kanns nicht liegen?


    greets

    Ich bin doch auch nur ein dummer Maschinenbauer :wallbash:

  • Setz vorher Start=0 dann müsste der Fehler weg sein. Desweiteren hast du vergessen nach "open()" auch "close()" zu verwenden, was irgendwann das Script oder gar System zum Absturz bringen kann - nutze stattdessen lieber "with" das kümmert sich nach dem Block selber ums schließen.


    [code=php]
    #!/usr/bin/python2
    # -*- coding: utf-8 -*-
    import time
    from RPi import GPIO


    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(13, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)


    Start=0
    Counter=0



    def Interrupt(channel):
    global Counter, Start
    if GPIO.input(13) == GPIO.HIGH:
    Start = time.time()
    Counter += 1
    print "Counter: %s" % Counter
    with open("/var/www/html/Upload_html_Heizung/Aufzeichnung_zaehlfkt.txt", "a") as f:
    f.write("Sequenz " + str(Counter) + "\n")
    f.write("Start: " + time.asctime(time.localtime()) + "\n")
    print "Start Spannungssignal: " + time.asctime(time.localtime())
    else:
    Ende = time.time()
    print "Ende Spannungssignal: " + time.asctime(time.localtime())
    Dauer = (Ende - Start)/60
    print "Dauer in Minuten: %s" % Dauer
    with open("/var/www/html/Upload_html_Heizung/Aufzeichnung_zaehlfkt.txt", "a") as f:
    f.write("Ende: " + time.asctime(time.localtime()) + "\n")
    f.write("Dauer: " + str(Dauer) + " Min" + "\n")



    GPIO.add_event_detect(13, GPIO.BOTH, callback=Interrupt, bouncetime=500)


    try:
    while True:
    time.sleep(1)
    except:
    GPIO.cleanup()
    print "Sie haben Strg C gedrückt, Messung beendet"
    [/php]


    Allerdings sollte man direkt in der interrupt Callback nicht viel tun, da diese sonst ausgebremst bzw für die Dauer blockiert wird und somit Events verpasst bzw verfälscht werden könnten

  • Hallo meigrafd!
    Danke für die Tipps!


    Dachte ich habe Start auf null gesetzt, oder täusch ich mich? Gleich nach der Definition von GPIO input/output (zehnte Zeile) steht : Start=0. Oder muss ich da noch was anhängen?
    Wegen dem Callback: Was meinst du genau? Ist es sinnvoller 2 Callback's bzw. zwischen 2 Ereignissen dann zu unterscheiden(anstatt alles in einem Callback zu machen)?
    Das mit close werd ich sofort beheben ;)


    Sorry für die vielen Unklarheiten, aber bin eigentlich Maschinenbauer und kein Softwareentwickler ;)

    Ich bin doch auch nur ein dummer Maschinenbauer :wallbash:

  • Mal ein paar Anmerkungen:


    - ein Interrupt ist keine Schleife. sondern ein Ereignis. Und während ein Interrupt aktiv ist, kann kein weiterer erfolgen. Daraus folgt, das man den Interrupt so *kurz* wie möglich macht! Denn sonst verlierst du Ereignisse. Das ist bei deinen noch sehr niederfrequenten Ereignissen verschmerzbar, fällt dir aber dann beim nächsten Projekt auf die Füsse. Darum sollte dein Interrupt Callback nur Ereignisse in eine Queue stecken, und deine Hauptschleife arbeitet die dann ab. Finden sich hier viele Beispiele.


    - Dein Code nimmt implizit an, er würde gestartet bei ausgeschalteten Heizung. Doch dann ist Start logischerweise nicht definiert ! Mit dem Queue-Ansatz kann man das leicht reparieren:


  • Vielen Dank!


    Quote

    - ein Interrupt ist keine Schleife. sondern ein Ereignis. Und während ein Interrupt aktiv ist, kann kein weiterer erfolgen. Daraus folgt, das man den Interrupt so *kurz* wie möglich macht! Denn sonst verlierst du Ereignisse. Das ist bei deinen noch sehr niederfrequenten Ereignissen verschmerzbar, fällt dir aber dann beim nächsten Projekt auf die Füsse. Darum sollte dein Interrupt Callback nur Ereignisse in eine Queue stecken, und deine Hauptschleife arbeitet die dann ab. Finden sich hier viele Beispiele.


    Das heisst du würdest garkeine Callback-Interrupt Funktion mehr benutzen?


    Das mit dem Hauptprogramm hab ich so verstanden: Ich soll nichts mehr in der Interrupt Schleife ausführen, sondern nur die Werte der Variablen im Interrupt dementsprechend anpassen. In der while- Schleife wird dann die neu definierte Variable verwendet, um meine Ausgaben am Terminal, .txt usw zu erzeugen? Richtig so?


    Quote

    - Dein Code nimmt implizit an, er würde gestartet bei ausgeschalteten Heizung. Doch dann ist Start logischerweise nicht definiert ! Mit dem Queue-Ansatz kann man das leicht reparieren:


    Ist das falsch, dass der Code das implizit annimmt? Die Heizung ist doch zu Beginn ausgeschalten. Falls sie einschaltet, steigt die Spannung und wir haben den Wert == 1 auf dem GPIO, Start wird dabei definiert. Wenn sie wieder ausschaltet, fällt der Wert auf 0.

    Ich bin doch auch nur ein dummer Maschinenbauer :wallbash:

    Edited once, last by wannabeatbox ().


  • Dachte ich habe Start auf null gesetzt, oder täusch ich mich? Gleich nach der Definition von GPIO input/output (zehnte Zeile) steht : Start=0. Oder muss ich da noch was anhängen?


    In dem Code aus Beitrag#1 machst du das nirgends. Wenn dann aber direkt als erstes die "else:" greift ist nirgends 'Start' definiert und deshalb erscheint der Error.


    Wegen dem Callback: Was meinst du genau? Ist es sinnvoller 2 Callback's bzw. zwischen 2 Ereignissen dann zu unterscheiden(anstatt alles in einem Callback zu machen)?


    Nein. Siehe __deets__ Erklärung.


    Wenn du der Callback etwas zu tun gibst, sei es direkt oder indem du eine andere Funktion aufrufst, wird die Callback selbst solange blockiert bis alles abgearbeitet wurde. In dem Zeitraum kann dann aber kein weiterer Flankenwechsel verarbeitet werden. Die Callback wird also blockiert.


    Wie __deets__ auch schon erwähnte wäre es sinnvoller in der Interrupt_Callback nur etwas in ein Queue einzufügen und das was dann im Queue drin steht wird vom Script selbst verarbeitet - denn die Callback läuft in einem separaten Thread, kann also unabhängig von dem Thread des Scripts Dinge verarbeiten bzw andersherum.


    Beispiel:



    Es ist bereits vorbereitet auch "pin" unterscheiden zu können. Da du hier aber nur GPIO-13 behandelst reicht nur die Prüfung auf "state".
    Wenn du mehrere GPIO's behandeln willst könnte das so aussehen:



    Es ließe sich natürlich auch der Zeitpunkt des Flankenwechsels festhalten:

    Code
    1. from datetime import datetime
    2. def interrupt_Event(q, channel):
    3. q.put( (datetime.now(), channel, GPIO.input(channel)) )

    ...entsprechende Behandlung sucht ihr euch bitte selber zusammen ;)



    //EDIT: Evtl. hilft auch die Erklärung in folgendem Beitrag: Zufallszahlen Generator mit Ausgabe auf 7 Segment Anzeige...machbar?

  • Vielen Dank meigrafd!


    Ich denke ich verstehe langsam, was ihr meint!
    Vielen Dank für das Programm, das kann ich ja fast schon so benutzen!



    Mit der Definition von Start hast du recht, weiss nicht wo ich da wieder geschaut hab, ich glaub ich halluziniere schon wieder :D


    Werd mich wohl noch etwas mit der "queue" und "partial" auseinandersetzen müssen, ist mir leider bis jetzt noch nicht untergekommen!


    Ich bin einverstanden, dass meine interrupt Schleife nicht optimal ist.
    Aber was ich nicht ganz verstehe: Eigentlich müsste das Programm funktionieren (was es auch tut, wenn ich es mit einem GPIO mit 3.3V versuche). Sobald es aber in die Praxis geht, sprich zur Heizung, erkennt er die if- schleife (GPIO.HIGH) im Callback nicht! Er schreibt ja nur alle paar Sekunden ein Ende (=GPIO.LOW) rein.


    greets

    Ich bin doch auch nur ein dummer Maschinenbauer :wallbash:

    Edited once, last by wannabeatbox ().

  • Okay vielen Dank für eure Hilfe schon mal. Leider funktioniert die Schaltung auch mit dem optimierte Programm von meigrafd nicht so ganz.
    Scheinbar ist die Spannungseingabe etwas holprig:
    Angeschlossen an der Heizung gibt mir das Terminal ausschließlich Ende- Signale, also GPIO.LOW, aus. Und davon sehr viele im Sekundentakt. Ein Start-Signal sucht man vergebens.
    Allerdings ist mir aufgefallen, es reicht schon ein leichtes Bewegen oder Rütteln an den Komponenten und es wird das selbe falsche Signal ausgesendet. Liegt ein Kontaktierungsproblem vor?!


    An der Software sollte es ja nicht liegen:
    Habe einen handelsüblichen Schalter am 3,3V GPIO angesteckt und damit den input verbunden. Wenn ich den Schalter ordnungsgemäß betätige, erhalte ich bei allen verwendeten Programmen ein korrektes Ergebnis (zuerst Start, dann Ende und dann die Dauer). Passt also!



    Zum Programm selbst habe ich als Programmier-Neuling noch Fragen:




    [code=php]def interrupt_Event(q, channel):


    q.put( (channel, GPIO.input(channel)) )[/php]
    Wie ist nun in Worten das interrupt-Ereignis definiert? Was heißt das q.put?



    [code=php]if __name__ == "__main__":
    main()


    [/php]
    Zweiteres: die main() ist für Übersichtszwecke und dass man es irgendwie später nochmal verwenden könnte? Braucht man nicht zwingend oder? Aber was besagen diese 2 Zeilen am Schluss des Skripts? Was wird dadurch gesteuert?
    Vielleicht wären ein paar Kommentare im Programm ganz hilfreich...


    greets

    Ich bin doch auch nur ein dummer Maschinenbauer :wallbash:

    Edited once, last by wannabeatbox ().

  • Hallo wannabeatbox,



    Allerdings ist mir aufgefallen, es reicht schon ein leichtes Bewegen oder Rütteln an den Komponenten und es wird das selbe falsche Signal ausgesendet. Liegt ein Kontaktierungsproblem vor?!


    Einen Pullup- oder Pulldown-Widerstand hast Du verwendet? Denn solche Störungen sind ein Zeichen dafür, dass Deine Strippen als Antenn fungieren und so alles hereinrauscht, was es so gibt. Ein solcher Widerstand bewirkt, dass nur auf Vorgänge Deiner Schaltung reagiert wird und die Störsignale nicht mehr durchkommen.



    Beste Grüße


    Andreas

    Ich bin wirklich nicht darauf aus, Microsoft zu zerstören. Das wird nur ein völlig unbeabsichtigter Nebeneffekt sein.
    Linus Torvalds - "Vater" von Linux

    • Icon-Tutorials (IDE: Geany) - GPIO-Library - µController-Programmierung in Icon! - ser. Devices - kein Support per PM / Konversation

    Linux is like a wigwam, no windows, no gates, but with an apache inside dancing samba, very hungry eating a yacc, a gnu and a bison.

  • Hallo Andreas!Vielen Dank für den Tipp


    Habe einen Pulldown-Widerstand verwendet, da ich für das GPIO.input eine Spannung von 3,3V beim Einschalten der Heizung erwarte:

    Code
    1. GPIO.setup(13, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)


    das meintest du oder?


    Kann es vielleicht sein, dass der Trafo (230V AC zu 5V DC) das Signal nicht ausreichend glättet und ich eine (Rest-)Oberwelligkeit, also Brummspannung, erhalte?
    Oberwelligkeit = minimaler Flankenwechsel mit 50 Herz, mein interrupt wird jedesmal ausgelöst.
    Würde die vielen Signale alle paar Sekunden erklären, aber nicht warum nur Ende-Signale vorhanden sind- bei Flankenwechsel sollte das Programm ja prüfen ob die input- Spannung <0,8V oder >0,8V ist. Bei Heizung ein wäre die Spannung immer größer 0,8V, es sollte also ein Start- Signal resultieren!! Tut es aber nicht!


    zum Nachlesen meines Gedankengangs:Siehe Unterpunkt "Glättung"
    https://de.wikipedia.org/wiki/Gleichrichter#Gl.C3.A4ttung


    greets

    Ich bin doch auch nur ein dummer Maschinenbauer :wallbash:

    Edited once, last by wannabeatbox ().

  • Hallo wannabeatbox,


    kleines Gedankenexperiment:
    Angenommen, Du hast ein sauberes HIGH- oder LOW-Signal. Der Pegelwechsel zu LOW oder HIGH erfolgt - aus einem beliebigen Grund - nicht. Die Spannungsänderung erfolgt nur in den nicht-definierten Bereich 0,8 bis 1,3 V. Wie wahrscheinlich ist es, dass ein Pegelwechsel HIGH => LOW (also FALLING) oder LOW => HIGH (also RISING) erkannt wird?


    Abgesehen davon, dass mir Deine Schaltung merkwürdig vorkommt (zu viele Störfaktoren, zu viel Schwingungen) würde ich mal prüfen, welche Signale Du überhaupt detektieren kannst. Es gibt nicht nur Prüfung auf HIGH / LOW. Es gibt auch noch RISING / FALLING sowie BOTH (was wohl aktuell verwendet wird). Und ich vermute mal, dass von den paarweise erwarteten nur jeweils einer auftritt. Dies ist dann ein Kennzeichen, dass die Schaltung nicht so sauber arbeitet, wie Du es erwartest.


    Beste Grüße


    Andreas

    Ich bin wirklich nicht darauf aus, Microsoft zu zerstören. Das wird nur ein völlig unbeabsichtigter Nebeneffekt sein.
    Linus Torvalds - "Vater" von Linux

    • Icon-Tutorials (IDE: Geany) - GPIO-Library - µController-Programmierung in Icon! - ser. Devices - kein Support per PM / Konversation

    Linux is like a wigwam, no windows, no gates, but with an apache inside dancing samba, very hungry eating a yacc, a gnu and a bison.


  • Zum Programm selbst habe ich als Programmier-Neuling noch Fragen:


    [code=php]def interrupt_Event(q, channel):
    q.put( (channel, GPIO.input(channel)) )[/php]
    Wie ist nun in Worten das interrupt-Ereignis definiert? Was heißt das q.put?


    Normalerweise wird der Callback Funktion bei Aufruf nur der Channel (also die GPIO#) übergeben. Das ist intern in dem RPi.GPIO Module so definiert.
    Durch die Verwendung von 'partial' kann man die Übergabe modifizieren, was oben auch gemacht wird: callback=partial(interrupt_Event, queue)
    'partial()' ist also ein Funktionsaufruf der wir die zu modifizierende Funktion sowie die erweiterte Übergabe/Parameter nennen. Ab da wird der 'interrupt_Event' Funktion zusätzlich auch noch das 'queue' Objekt übergeben und wie dieses Objekt wiederum innerhalb der 'interrupt_Event' Funktion heißt definieren wir mit 'q'.
    Man könntet auch:
    [code=php]def interrupt_Event(bla, blub):
    bla.put( (blub, GPIO.input(blub)) )[/php]machen... Sieht aber nicht wirklich toll aus und verrät auch nichts darüber was sich dahinter verbirgt, nur wenn man angestrengt nachdenkt :fies:
    Das es hier nicht auch 'queue' lautet hab ich aus dem Grund gemacht um zu zeigen dass die Parameter auch anders heißen können


    Jedes mal wenn die 'interrupt_Event' Funktion ausgeführt wird, wird direkt die GPIO# und dessen Status (HIGH / LOW) ins Queue eingefügt. Das geht extrem schnell und bremst die Funktion nicht aus. In der späteren while des Scripts kann man dann den möglichen Inhalt des Queues unabhängig/getrennt abarbeiten. Beim Einfügen ins Queue wird hier ein tuple genutzt.


    [code=php]if __name__ == "__main__":
    main()


    [/php]
    Zweiteres: die main() ist für Übersichtszwecke und dass man es irgendwie später nochmal verwenden könnte? Braucht man nicht zwingend oder?


    Unbedingt not wenig nicht, aber gehört sich eigentlich so ;)
    Angenommen du importiert das Script in ein anderes, dann kannst du in dem anderen gezielt nur Funktionen aufrufen und verhinderst durch die ' if __name__ == "__main__": ' Zeile zudem dass die Funktion direkt beim import ausgeführt wird.
    Am besten fragst du Onkel Google danach.

  • Alles Klar Meigrafd. vielen Dank für die umfassende Antwort (brauchst aber im anderen Thema gleich so abgehen^^)


    Habe nun meinen Onkel gefragt, Elektronikexperte. Der Fehler ist ein sehr leichter wie dummer Fehler meinerseits in der Schaltung! Ich habe den GND von Raspberry ganz einfach nicht mit dem Minuspol meiner Heizung verbunden! Fehler gelöst, alle von euch und von mir geschriebenen Programme laufen!


    Vielen Dank für eure mithilfe!


    greets

    Ich bin doch auch nur ein dummer Maschinenbauer :wallbash: