Python-Script funktioniert nach dem start nach einiger Zeit nicht mehr

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

    ich habe ein Python-Script geschrieben mit dem ich per Taster an den GPIO-Pins die wiedergabe der Musik (Volumio) steuere.

    Das Programm läuft soweit ohne Probleme allerdings nicht dauerhaft.

    Wenn ich das Programm starte läuft es für ein paar minuten, wenn ich dann aber die Taster eine Zeit lang nicht benutze, funktioniert das Script nicht mehr.

    Ich bin anfänger in sachen Python und würde jetzt gerne wissen ob bzw was ich etwas falsch mache.

    Einmal editiert, zuletzt von Android_Garden (14. Februar 2015 um 14:14)

  • Python-Script funktioniert nach dem start nach einiger Zeit nicht mehr? Schau mal ob du hier fündig wirst!

  • Ich habe auch keine Ahnung von Python, aber selbst Leute mit Ahnung werden dir mit so wenig Infos kaum helfen können.
    Dein Skript (wenigstens in Teilen) wäre hilfreich, wie du es startest, ob Fehlermeldungen auftauchen, usw.

    --
    man ist das System-Anzeigeprogramm für die Handbuchseiten von Linux.

  • Ja das war ein fehler von mir, die Infos kommen sofort verzeihung.

    Eine Fehlermeldung wird mir nicht ausgegeben. Egal ob ich das Script per Crontab beim systemstart lade, oder ob ich es im Hintergrund ausführen lasse, es funktioniert sofort nach dem start problemlos. Nach ein paar minuten funktioniert es jedoch nicht mehr. Ich denke daher das es an meiner schleife liegt die sich irgendwann mal beendet.

    Wie gesagt ich bin völlig neu im Thema python und wäre über Hilfe bei meinem Problem dankbar.

    Hier der Code den ich ausführe.

  • Das einfachste wäre, solange das Script noch entwickelt wird, es innerhalb eines screen 's laufen zu lassen (unabhängig davon was es macht usw), dann kannst du auch jederzeit in diesen screen wechseln und dir mögliche Ausgaben angucken.

    Ein screen ist eine virtuelle Konsole, die weiter läuft auch wenn du dich abmeldest.

    Eine andere Möglichkeit wäre es in den Hintergrund zu schicken, siehe dazu Forumsuche nach: Autostart
    (oder in den FAQ Bereich unter Linksammlung)


    Allerdings glaube ich hat dein Script noch ein paar Fehler, wie zum Beispiel:
    [code=php]
    connected = False
    while connected == False:
    connected = True
    [/php]
    Das macht so kein Sinn ;) Aber selbst wenn, würde der Rest des Scripts nicht mehr ausgeführt werden da diese while den Rest blockieren würde... Scripts werden von oben nach unten abgearbeitet. Das Script würde dann aber an dieser Stelle verweilen, solange es in dieser while hängt würde der andere Code nicht beachtet werden.
    Du kannst sozusagen von Glück reden das diese while so wie es da steht nicht funktioniert, da dessen Bedingung gleich wieder beendet wird...

    Die anderen Abläufe finde ich auch etwas seltsam - mag so funktionieren aber ist meiner Meinung nach nicht sehr effektiv.
    Aber auch schon die aller ersten Zeilen in dem Script deuten darauf hin, dass das Script entweder copy&pasted wurde, oder nicht wirklich verstanden wird was da passiert und was geschieht wenn man es anders machen würde. Was zum Beispiel bewirkt das setzen einer Variablen als "global" und warum macht das so wie du es gesetzt hast kein Sinn? :s


    Erklär uns mal bitte was das Script machen soll - wie sind die Abläufe die das Script beachten oder umsetzen soll. Was soll es können :huh:


    PS: In der Überschrift ist kein Hinweis auf ein mögliches Problem. Auch die Beschreibung im ersten Beitrag gibt kein Hinweis auf ein mögliches Problem. Erst im nachfolgenden Beitrag merkst du beiläufig an das sich das Script nach einer gewissen Zeit selbst beendet... Das deutet aber auf einen Fehler im Script hin. Also selbst wenn du es scheinheinlich so startest als sollte es dauerhaft laufen, würde es das nicht.

  • Ich habe mir den code zusammengesucht und mittels "try and error" rumgebastelt, da wie ich bereits sagte noch nie etwas mit python gemacht habe.
    Aber das ganze Thema Raspberry Pi und Python interessiert mich immer mehr und möchte daher viel mehr darüber lernen.

    Diese schleife beim Verbinden finde ich jetzt nachdem ich sie mir so alleine nochmals anschaue irgendwie als sinnlos, danke für den Hinweis.

    PS: ich werden die Überschrift des Threats ändern. Verzeihung für die schlechte einführung in dieses Thema.

  • Gut dann also erst mal ein paar Grundlagen:

    In Python ist es für gewöhnlich so aufgebaut das man sog. Anweisungen deklariert (festlegt). Variablen die man ausserhalb der Anweisungen deklariert sind 'global', variablen die innerhalb einer Anweisung deklariert werden sind 'local'

    Beispiel:
    [code=php]txt = "hallo"

    def test():
    print txt
    txt = "tschuess"

    test()
    print txt[/php]

    Führt man das so aus wird nur 2x "hallo" ausgegeben, da die Anweisung die globale Variable so nicht verändern kann, und das ist auch gut so ;)

    Dh die 2.Zeile in deinem Script, nach dem import, macht so also eigentlich kein Sinn und kann weggelassen werden, da die nachfolgende Festlegung der Variablen bereits global sind.

    Allgemein würde ich lieber mit Anweisungen arbeiten, da es dann nicht nur übersichtlicher wird sondern auch besser mit gearbeitet werden kann.

    Dann zu der while Schleife die so eigentlich kein Sinn macht.
    Wichtig sind auf jedenfall die "try:" und "except" Zeilen, da hiermit ein Fehler abgefangen und entsprechend reagiert werden kann. Tritt also innerhalb eines "try:" ein Problem auf, weil er zB nicht connecten kann, wird ein "except" ausgelöst worauf dann eben reagiert werden kann.

    Der Grundgedanke der hinter dieser Schleife steckt ist vermutlich, das der Rest des Scripts nicht ausgeführt werden soll solange keine Verbindung hergestellt werden konnte. Die Schleife soll alle 5 Sekunden erneut ausgeführt werden, was mit dem sleep am Ende erreicht wird, beziehungsweise wird die Bedingung der while aufgehoben sobald eine Verbindung hergestellt wurde...

    Also in diesem Zustand macht das noch kein Sinn:
    [code=php]
    connected = False
    while connected == False:
    connected = True
    try:
    client.connect(TEST_MPD_HOST, TEST_MPD_PORT)
    except SocketError as e:
    connected = False
    if connected == False:
    print "Couldn't connect. Retrying"
    time.sleep(5)
    [/php]

    Besser wäre (ohne dies jetzt zu extrem abzuändern):
    [code=php]
    connected = False
    while connected == False:
    try:
    client.connect(MPD_HOST, MPD_PORT)
    connected = True
    except SocketError as e:
    connected = False
    if connected == False:
    print "Couldn't connect. Retrying"
    time.sleep(5)
    [/php]Hier war aber auch noch ein weiteres Problem, nämlich das du versucht hast TEST_* Variablen zu verwenden die aber nirgends festgelegt wurden...
    Auch sind einheitliche Einrückungen sehr wichtig in Python, also der Abstand von links. Nach einem Doppelpunkt folgt eine Einrückung die aber nachfolgend im selben Abstand sein muss. Dabei ist es aber egal ob es 2 Leerzeichen, oder 4 oder 6 oder 8 sind. Ich persönlich bevorzuge entweder 2 Leerzeichen ODER Tabs. Vermischen darf man das aber auch nicht also entweder Leerzeichen ODER Tabs.


    Die nachfolgende Schleife ist ebenfalls nicht so effektiv, da das gesamte Script beendet wird sobald die Verbindung ein mal verloren ging bzw ein Fehler auftrat.
    Und hier ist jetzt auch der Grund wieso man überwiegend mit Anweisungen arbeiten sollte: Den selben Code immer und immer wieder zu wiederholen macht es nicht nur unübersichtlich sondern auch umständlich mal eine Kleinigkeit zu ändern. Es wäre also nun von Vorteil den Verbindungsaufbau in eine Anweisung zu packen sodass man diese am Anfang der jetzt folgenden Schleife aufrufen kann um eine Verbindung erneut herstellen zu können.

    Aber bevor ich darauf eingebe würd ich von dir gerne ne Beschreibung haben was du mit den GPIO's eigentlich erreichen möchtest - also welche Funktionsweise soll da erfüllt werden :huh:

  • Erst mal vielen dank meigrafd für die Erläuterung!

    Dann zu meiner Situation:
    Ich habe einen raspberry auf dem ich volumio installiert habe um in meinem Bad Internetradio abspielen und musik streamen zu können. Im Bad hab ich ein kleines tableau mit 5 Tastern die mit dem raspberry verbunden sind. Die Auswertung der Taster funktioniert bereits ;) Über die Taster will ich dann die Musik lauter, leiser, Titel vor bzw zurück und play/pause schalten.
    Ich glaube mein Problem ist das ich bislang in meiner Tätigkeit als Administrator nur mit cmd und poweshell scripten gearbeitet habe und deshalb eben auf die Schleife gekommen bin damit das Programm immer weiter läuft aber das is ja bei Python eigentlich nicht nötig. Wenn ich das Programm aufrufe dann läuft es solange ich es nicht beende oder durch ein error beendet wird.

    Wenn ich das jetzt richtig verstanden habe: ich packe die befehle in eine Funktion dann mach ich eine if Abfrage und lasse als Resultat darauf die Funktion aufrufen.


  • und deshalb eben auf die Schleife gekommen bin damit das Programm immer weiter läuft aber das is ja bei Python eigentlich nicht nötig.

    Doch, ist es ;)
    Generell werden Scripts ja von oben nach unten abgearbeitet. Ist das Ende der Datei erreicht wird der Prozess beenden ;)

    Wenn ich das jetzt richtig verstanden habe: ich packe die befehle in eine Funktion dann mach ich eine if Abfrage und lasse als Resultat darauf die Funktion aufrufen.

    Hm, jein ;)

    Also du Musst nicht unbedingt alles in eine Funktion packen.. In diesem Fall gäbe es aber mehrere Möglichkeiten ;) Es könnte schon reichen nur das Verbinden in eine Funktion/Anweisung zu packen damit man diese im späteren Verlauf noch mal aufrufen kann...


    Allerdings glaube ich wäre es, aufgrund deiner Beschreibung, besser das Script auf Basis von Interrupts umzusetzen.
    Zum einen spart das ne menge CPU Leistung ein, man könnte dann aber auch auf sleep's verzichten und zu guter letzt wird der Code dann auch meiner Meinung nach etwas übersichtlicher :fies:

    Das hat eben den Vorteil das man nicht ständig selber den Status eines Tasters prüfen muss sondern sobald dieser seinen Status ändert wird eine sog. Callback Anweisung ausgeführt und dort erledigt man dann entsprechende Dinge.


    Ich programmier das mal eben, wenn du dann Fragen hast dann bitte gezielt das hinterfragen was du nicht verstehst ;)
    Ist etwas umfangreicher geworden um Fehlermeldungen usw abfangen zu können, aber auch weil zB "ping()" nicht wirklich etwas zurückgibt usw aber ich ein vernünftiges Reconnect Handling realisieren wollte, deshalb sinds einige paar mehr Anweisungen :D

    [code=php]#!/usr/bin/python
    # -*- coding: utf-8 -*-
    #
    # v0.11 by meigrafd
    #
    import sys
    from time import sleep, time
    import mpd
    import RPi.GPIO as GPIO
    import signal
    #------------------------------------------------------------------------

    MPD_HOST = "localhost"
    MPD_PORT = "6600"
    MPD_PASSWORD = "volumio" # password for Volumio / MPD

    #GPIO pins
    Play = 17
    Lauter = 27
    Leiser = 22
    Next = 23
    Prev = 24

    # only one of following:
    PULL = GPIO.PUD_DOWN #GPIO -> GND
    #PULL = GPIO.PUD_UP #GPIO -> 3V3

    #------------------------------------------------------------------------

    # to use RaspberryPi gpio pin numbers
    GPIO.setmode(GPIO.BCM)
    # set up GPIO input channels
    GPIO.setup(Play, GPIO.IN, pull_up_down = PULL)
    GPIO.setup(Lauter, GPIO.IN, pull_up_down = PULL)
    GPIO.setup(Leiser, GPIO.IN, pull_up_down = PULL)
    GPIO.setup(Next, GPIO.IN, pull_up_down = PULL)
    GPIO.setup(Prev, GPIO.IN, pull_up_down = PULL)

    #------------------------------------------------------------------------

    def connectMPD(client):
    try:
    client.timeout = 10
    client.connect(MPD_HOST, MPD_PORT)
    except mpd.ConnectionError, err:
    if "Already connected" in err:
    print err
    return True, None
    else:
    return False, err
    if MPD_PASSWORD:
    try:
    client.password = MPD_PASSWORD
    except mpd.CommandError, err:
    disconnectMPD(client)
    return False, err
    return True, None

    def disconnectMPD(client):
    try:
    client.disconnect()
    except mpd.ConnectionError:
    pass

    def reconnectMPD(client):
    connected = False
    while not connected:
    connected, error = connectMPD(client)
    if not connected:
    print "Error: %s" % error
    print "Couldn't connect. Retrying"
    sleep(5)
    return connected

    def _ping(client):
    try:
    client.ping()
    except mpd.ConnectionError, e:
    connected = reconnectMPD(client)

    def Interrupt_event(pin):
    _ping(client)
    PlayStat = client.status()
    if GPIO.input(Play) == GPIO.HIGH:
    if PlayStat['state'] == "stop" or PlayStat['state'] == "pause":
    client.load("MyRadio")
    client.play()
    elif PlayStat['state'] == "play":
    client.stop()
    client.clear()
    elif GPIO.input(Lauter) == GPIO.HIGH:
    vol = int(PlayStat['volume'])
    if vol >= 95:
    client.setvol(100)
    else:
    SetVol = vol + 5
    client.setvol(SetVol)
    elif GPIO.input(Leiser) == GPIO.HIGH:
    vol = int(PlayStat['volume'])
    if vol <= 5:
    client.setvol(0)
    else:
    SetVol = vol - 5
    client.setvol(SetVol)
    elif GPIO.input(Next) == GPIO.HIGH:
    client.next()
    client.play()
    elif GPIO.input(Prev) == GPIO.HIGH:
    client.previous()
    client.play()
    else:
    print "ERROR! Unknown GPIO pin triggered: %s" % pin

    #------------------------------------------------------------------------


    try:
    GPIO.add_event_detect(Play, GPIO.RISING, callback=Interrupt_event, bouncetime=150)
    GPIO.add_event_detect(Lauter, GPIO.RISING, callback=Interrupt_event, bouncetime=150)
    GPIO.add_event_detect(Leiser, GPIO.RISING, callback=Interrupt_event, bouncetime=150)
    GPIO.add_event_detect(Next, GPIO.RISING, callback=Interrupt_event, bouncetime=150)
    GPIO.add_event_detect(Prev, GPIO.RISING, callback=Interrupt_event, bouncetime=150)
    client = mpd.MPDClient()
    con = reconnectMPD(client)

    #keep script running
    signal.pause()
    except (KeyboardInterrupt, SystemExit):
    disconnectMPD(client)
    GPIO.cleanup()
    print("\nQuit\n")
    [/php]


    PS: Du kannst auch in der Konsole einfach 'python' eintippen und dann den Code da rein schreiben/pasten um zu sehen ob etwas funktioniert o.ä.. Das ist zum entwickeln einfacher ;)

  • So ich habe das ganze Script jetzt nochmal neu aufgebaut mit anlehnung an das Beispiel von meigrafd.

    ich hoffe es ist jetzt ein wenig besser als mein erster versuch ;)

    Das Script startet problemlos und läuft dann auch. Allerdings habe ich wieder das Problem, wenn ich die Taster eine Zeit lang nicht benutze, funktioniert das Script erst wieder wenn ich es manuell starte. Wenn ich es nicht starte bleiben die Tastendrücke ohne funktion.

  • Du hast leider ein paar kleine "Fehler" eingebaut :fies:

    Die GPIOs am Anfang als Variable zu definieren erleichtert es den Rest besser zu verstehen, aber auch diese später zu ändern denn dann brauch man nur den Wert der Variable ändern..

    print "connected"
    Ist an der Stelle nicht korrekt. Ob eine Verbindung tatsächlich zustande kam wird nur beim "return True, None" festgestellt. Auch die Einrückung passt nicht.

    print "disconnected"
    Hier das selbe wie mit dem ersten Problem..

    signal.pause()
    Ist an der Stelle wo du es gesetzt hast eigentlich auch falsch. Das sollte innerhalb des letzten "try:" aber nach den add_event_detect's stehen.

    GPIO.cleanup()
    Fehlt bei dir leider ebenfalls?


    Änder nicht so viel auf einmal - denn sonst ist die Fehlersuche schwieriger ;)


    Wie startest du es denn?
    Starte es mal in einem screen und wenn es dann mal wieder nicht geht dann guckst du in den Screen obs vllt eine Fehlermeldung gab. Bei mir hat es aber gut funktioniert, wobei ich nur 5 Minuten gewartet hatte...

  • Ok vielen Dank mal wieder ;)

    das mit den beiden print ist noch vom Testen und hab ich vergessen zu löschen ;) und signal.pause() habe ich angepasst.

    Egal ob ich es per "sudo python NameDesScripts.py", im Hintergrund oder via Crontab beim Systemstart starte, es ist immer das gleiche Ergebnis.

    Ich versuche es jetzt nochmals nach den Änderungen und anschließend mit de screen.

    [Edit]
    Hab es jetzt angepasst, wenn ich jetzt eine taste drücke, kommt die Fehlermeldung "Broken pipe"

    Einmal editiert, zuletzt von Android_Garden (17. Februar 2015 um 08:23)

  • Also erstmals sorry für den Doppelpost

    ich hab das Script jetzt überarbeitet:

    So funktioniert das ganze jetzt. Der reconnect() funktionierte nicht bei mir, ich habe einfach den connectMPD() "verbaut". ;)

    Vielen Dank nochmals für die Unterstützung

  • Beim def _ping(): ist ein Einrückungsfehler nach dem except , wodurch die try except überflüssig wird :fies:

    Merk dir einfach das nach einem Doppelpunkt eine Einrückung folgen muss, ansonsten wird die nachfolgende Zeile nicht zu dieser "Abfrage" dazu gewertet.

  • Die Einrückungsfehler sind beim Copy&Paste aus gedit entstanden, im Script passt alles bei mir ;)

    da fällt mir ein, drei Fragen hätte ich noch:
    1. Gibt es eine Dokumentation zu den Modulen z.B. RPi.GPIO oder MPD wo man auch die möglichen parameter usw nachlesen kann?

    2. Beim "import RPi.GPIO as GPIO". Ist der Grund für eine derartige schreibweise, dass "GPIO" als eine Variable mit dem inhalte "RPi.GPIO" deklariert wird?

    3. Was genau bezweckt "GPIO.cleanup()"? Sollte man dies immer ans ende eines Scripts stellen?

Jetzt mitmachen!

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