Seite 1 von 1

Song beim PIN Eingang

Verfasst: Samstag 2. Juli 2022, 21:08
von Danoo
Moin,
ich habe schon ein kleines Programm geschrieben, dass beim Eingang einen bestimmten Ton abspielt.
Leider weiß ich nicht ganz wo das Problem liegt, wenn das Programm länger aktiv ist stürzt es einfach ab und es funktioniert nicht mehr.
Findet ihr vielleicht einen Fehler im Programm?
Bin leider echt überfordert was der Fehler sein kann.


Programm:

from RPi import GPIO
from queue import Queue
import time
import vlc

PIN_LEFT_PLAYER = 19
PIN_RIGHT_PLAYER = 21
PIN_Start = 20



def initialize():
queue = Queue()
GPIO.setmode(GPIO.BCM)
for pin in [PIN_LEFT_PLAYER, PIN_RIGHT_PLAYER, PIN_Start]:
GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.add_event_detect(pin, GPIO.FALLING, callback=queue.put, bouncetime=3000)
return queue


def main():
try:
queue = initialize()

while True:
pin = queue.get()
#Tor_Links
if pin == (PIN_LEFT_PLAYER):
p = vlc.MediaPlayer("/home/pi/Desktop/Playsound/songs/Air_Horn.mp3")
p.play()
#tor_rechts
elif pin == PIN_RIGHT_PLAYER:
p = vlc.MediaPlayer("/home/pi/Desktop/Playsound/songs/Air_Horn.mp3")
p.play()

#Start
elif pin == PIN_Start:
p = vlc.MediaPlayer("/home/pi/Desktop/Playsound/songs/Boxing Bell Sound Effect.mp3")
p.play()

finally:
GPIO.cleanup()

if __name__ == "__main__":
main()

Re: Song beim PIN Eingang

Verfasst: Sonntag 3. Juli 2022, 08:20
von __blackjack__
@Danoo: Was heisst denn „abstürzen“ und „funktioniert nicht mehr“ genau? Gibt es eine Fehlermeldung beim Absturz? Bis wo hin kommt es beim Neustart?

Am Code direkt fällt mir nichts auf, ich frage mich allerdings wie oft parallel man Sounds starten kann/darf. Kannst Du denn Absturz an der Anzahl der Abspielvorgänge festmachen?

Re: Song beim PIN Eingang

Verfasst: Sonntag 3. Juli 2022, 09:02
von __deets__
Ich habe sowas bei RPi.GPIO schon öfter erlebt. Keine Ahnung warum. Irgendwas verklemmt da. Ich würde es mal mit pigpio probieren. NICHT (einfach so) mit gpiozero, denn das benutzt defaultmässig RPi.GPIO. Man kann es auf pigpio umlenken.

Re: Song beim PIN Eingang

Verfasst: Sonntag 3. Juli 2022, 09:07
von noisefloor
Hallo,

wie startest du das Programm? Wenn du es über eine systemd Service Unit startest solltest du im Log von systemd eine Fehlermeldung finden.
Wenn du es im Terminal im Vorderggrund laufen lässt sollte da eine Fehlermeldung erscheinen.

Gruß, noisefloor

Re: Song beim PIN Eingang

Verfasst: Sonntag 3. Juli 2022, 14:23
von hyle
Die Lösung wird ganz einfach sein. Erstelle ausserhalb der Schleife die vlc-Objekte mit verschiedenen Variablen. z.B. left_song = vlc.MediaPlayer("/home/pi/Desktop/Playsound/songs/Air_Horn.mp3")
Dann lässt Du diese mit in dem Fall left_song.play() an der jeweiligen Stelle abspielen.

Re: Song beim PIN Eingang

Verfasst: Sonntag 3. Juli 2022, 19:55
von Danoo
__blackjack__ hat geschrieben: Sonntag 3. Juli 2022, 08:20 @Danoo: Was heisst denn „abstürzen“ und „funktioniert nicht mehr“ genau? Gibt es eine Fehlermeldung beim Absturz? Bis wo hin kommt es beim Neustart?

Am Code direkt fällt mir nichts auf, ich frage mich allerdings wie oft parallel man Sounds starten kann/darf. Kannst Du denn Absturz an der Anzahl der Abspielvorgänge festmachen?
Einmal die Fehler Meldung, :

[00000000149f6040] alsa audio output error: cannot open ALSA device "default": Connection refused
[00000000149f6040] main audio output error: Audio output failed
[00000000149f6040] main audio output error: The audio device "default" could not be used:
Connection refused.
[00000000149f6040] main audio output error: module not functional
[000000798c12c90] main decoder error: failed to create audio output
[0000000014a53b50] vlcpulse audio output error: PulseAudio server connection failure: Connection terminated
ALSA lib pulse.c:242: (pulse_ connect) PulseAudio: Unable to connect: Connection terminated
[0000000014a53b50] alsa audio output error: cannot open ALSA device "default": Connection refused
[0000000014a53b50] main audio output error: Audio output failed
[0000000014a53b50] main audio output error: The audio device "default" could not be used:
Connection refused.
[0000000014a53b50] main audio output error: module not functional
[0000007eecc166d0] main decoder error: failed to create audio output

Re: Song beim PIN Eingang

Verfasst: Sonntag 3. Juli 2022, 19:56
von Danoo
__deets__ hat geschrieben: Sonntag 3. Juli 2022, 09:02 Ich habe sowas bei RPi.GPIO schon öfter erlebt. Keine Ahnung warum. Irgendwas verklemmt da. Ich würde es mal mit pigpio probieren. NICHT (einfach so) mit gpiozero, denn das benutzt defaultmässig RPi.GPIO. Man kann es auf pigpio umlenken.
Wie funktioniert das genau, habe dazu auch im Internet nicht viel gefunden :)

Re: Song beim PIN Eingang

Verfasst: Sonntag 3. Juli 2022, 20:52
von Danoo
hyle hat geschrieben: Sonntag 3. Juli 2022, 14:23 Die Lösung wird ganz einfach sein. Erstelle ausserhalb der Schleife die vlc-Objekte mit verschiedenen Variablen. z.B. left_song = vlc.MediaPlayer("/home/pi/Desktop/Playsound/songs/Air_Horn.mp3")
Dann lässt Du diese mit in dem Fall left_song.play() an der jeweiligen Stelle abspielen.
Okay ich würde das mal morgen ausprobieren da ich meinen PI momentan nicht dabei habe :D.
Meinst du das so?


Bin leider noch nicht so lange und bin noch ein kleiner Anfänger :lol:



from RPi import GPIO
from queue import Queue
import time
import vlc

PIN_LEFT_PLAYER = 19
PIN_RIGHT_PLAYER = 21
PIN_Start = 20



def initialize():
queue = Queue()
GPIO.setmode(GPIO.BCM)
for pin in [PIN_LEFT_PLAYER, PIN_RIGHT_PLAYER, PIN_Start]:
GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.add_event_detect(pin, GPIO.FALLING, callback=queue.put, bouncetime=3000)
return queue


def main():
try:
queue = initialize()

while True:
pin = queue.get()
#Tor_Links
if pin == (PIN_LEFT_PLAYER):
left_song.play()

#tor_rechts
elif pin == PIN_RIGHT_PLAYER:
right_song.play()

#Start
elif pin == PIN_Start:
begin_song.play()

begin_song = vlc.MediaPlayer("/home/pi/Desktop/Playsound/songs/Boxing Bell Sound Effect.mp3")
right_song = vlc.MediaPlayer("/home/pi/Desktop/Playsound/songs/Air_Horn.mp3")
left_song = vlc.MediaPlayer("/home/pi/Desktop/Playsound/songs/Air_Horn.mp3")



finally:
GPIO.cleanup()

if __name__ == "__main__":
main()

Re: Song beim PIN Eingang

Verfasst: Montag 4. Juli 2022, 17:15
von hyle
hyle hat geschrieben: Sonntag 3. Juli 2022, 14:23 Meinst du das so?
Fast! Die drei Zeilen direkt über while True: setzten meinte ich eigentlich, denn unter der Schleife werden diese nie erreicht. :wink:

Btw. Poste Code bitte immer in einem Codeblock, sonst gehen die Einrückungen verloren! Verwende dazu den vollständigen Editor und dort den </> Button.

Re: Song beim PIN Eingang

Verfasst: Montag 4. Juli 2022, 17:46
von Sirius3
Statt Code zu kopieren benutzt man die passenden Datenstrukturen:

Code: Alles auswählen

from queue import Queue
from pathlib import Path
from RPi import GPIO
import vlc

PIN_LEFT_PLAYER = 19
PIN_RIGHT_PLAYER = 21
PIN_START = 20

SONG_PATH = Path("/home/pi/Desktop/Playsound/songs")

SONGS = {
    PIN_START: "Boxing Bell Sound Effect.mp3",
    PIN_LEFT_PLAYER: "Air_Horn.mp3",
    PIN_RIGHT_PLAYER: "Air_Horn.mp3",
}


def initialize():
    queue = Queue()
    GPIO.setmode(GPIO.BCM)
    for pin in [PIN_LEFT_PLAYER, PIN_RIGHT_PLAYER, PIN_START]:
        GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
        GPIO.add_event_detect(pin, GPIO.FALLING, callback=queue.put, bouncetime=3000)
    return queue


def main():
    try:
        queue = initialize()
        songs = {
            pin: vlc.MediaPlayer(SONG_PATH / song)
            for pin, song in SONGS.items()
        }
        while True:
            pin = queue.get()
            songs[pin].play()
    finally:
        GPIO.cleanup()

if __name__ == "__main__":
    main()

Re: Song beim PIN Eingang

Verfasst: Montag 4. Juli 2022, 20:09
von Danoo
Okay :)
Dankeschön

Re: Song beim PIN Eingang

Verfasst: Freitag 8. Juli 2022, 22:22
von Danoo
Moin, ich habe mir mal den Code genauer angekuckt und ausprobiert. leider spielt er nur ein mal den Ton ab.
Habt ihr noch einen Lösungsvorschlag? :D

Sirius3 hat geschrieben: Montag 4. Juli 2022, 17:46 Statt Code zu kopieren benutzt man die passenden Datenstrukturen:

Code: Alles auswählen

from queue import Queue
from pathlib import Path
from RPi import GPIO
import vlc

PIN_LEFT_PLAYER = 19
PIN_RIGHT_PLAYER = 21
PIN_START = 20

SONG_PATH = Path("/home/pi/Desktop/Playsound/songs")

SONGS = {
    PIN_START: "Boxing Bell Sound Effect.mp3",
    PIN_LEFT_PLAYER: "Air_Horn.mp3",
    PIN_RIGHT_PLAYER: "Air_Horn.mp3",
}


def initialize():
    queue = Queue()
    GPIO.setmode(GPIO.BCM)
    for pin in [PIN_LEFT_PLAYER, PIN_RIGHT_PLAYER, PIN_START]:
        GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
        GPIO.add_event_detect(pin, GPIO.FALLING, callback=queue.put, bouncetime=3000)
    return queue


def main():
    try:
        queue = initialize()
        songs = {
            pin: vlc.MediaPlayer(SONG_PATH / song)
            for pin, song in SONGS.items()
        }
        while True:
            pin = queue.get()
            songs[pin].play()
    finally:
        GPIO.cleanup()

if __name__ == "__main__":
    main()
[/quote]

Re: Song beim PIN Eingang

Verfasst: Samstag 9. Juli 2022, 13:39
von DeaD_EyE
Ich habe mal das Programm in zwei Teile aufgesplittet. vlc_simple kann nur eine Funktion: play(song) und die Logik wann play(song) ausgelöst wird in einer anderen Datei.

vlc_simple.py

Code: Alles auswählen

from __future__ import annotations

from pathlib import Path

import vlc

# Nur eine Instanz des MediaPlayer
# Gleichzeitiges abspielen ist dann nicht möglich
_player = vlc.MediaPlayer()


def play(song: str | Path) > bool:
    song = Path(song)
    if not song.exists():
        print(f"Datei existiert nicht: {song}")
        return False

    # die Methode set_media benötigt eine vlc.Media instanz
    _player.set_media(vlc.Media(song))
    # Player stoppt nach set_media
    # Player mit dem neuen audio starten
    _player.play()
    return True

event_player.py

Code: Alles auswählen

import time
from pathlib import Path

from RPi import GPIO

from vlc_simple import play

SONG_PATH = Path("/home/deadeye/Musik")
# hier gehe mit Absicht explizit vor
# kann man natürlich in einer Dict-Comprehension machen
SONGS = {
    17: SONG_PATH / "南宮嘉駿-回憶總想哭 ft.彭清「回憶總想哭,一個人太孤獨」【動態歌詞版MV】-XN1ubsjTz1U.mp3",
    27: SONG_PATH / "Led Zeppelin -  Stairway To Heaven ᴴᴰ (Legendado_Tradução PTBR)-D9ioyEvdggk.flac",
    22: SONG_PATH / "Magic Sword - In The Face Of Evil-G02wKufX3nw.opus",
}


def song_callback(pin):
    # Pin nummer wird übergeben, wenn der Callback durch GPIO aufgerufen wird
    # dann wird auf die entsprechende Value der SONGS zugegriffen
    # das wird dann der Funktion play übergeben. (im anderen script)
    play(SONGS[pin])


def initialize():
    GPIO.setmode(GPIO.BCM)
    for pin, song in SONGS.items():
        print("Init Pin", pin)
        GPIO.setup(pin, GPIO.IN, GPIO.PUD_UP)
        GPIO.add_event_detect(
            pin, GPIO.FALLING, callback=song_callback, bouncetime=3000
        )
        # bouncetime 3 Sekunden ?
        # sobald eine fallende Flanke erkannt wird,
        # ruft GPIO die callback-Funktion mit der Pin-Nummer als einziges Argument auf
        # das bestimmt dann, welcher Song gespielt wird.


def main():
    initialize()
    # busy loop um zu verhindern, dass das Programm einfach beendet wird.
    # Der main-Thread schläft immer 10 Sekunden lang,
    # während der Daemon-Thread von GPIO im Hintergrund aktiv ist
    while True:
        time.sleep(10)


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("Exit")
    except Exception as e:
        print(e)
        # ist nicht so die tolle Art, aber GPIO.cleanup soll ja noch ausgeführt werden
        # wenn eine Exception ausgelöst wird, mit der man nicht rechnet und das würde dann
        # verhindern das nächste Statement auszuführen
        # in diesem Fall GPIO.cleanup()
        # andere Möglichkeit:
        # import atexit
        # atexit.register(GPIO.cleanup)
        
    GPIO.cleanup()
Ich höre nur nichts, da ich einen RPi0W2 zum Testen verwendet habe und mein Mini-HDMI-Adapter leider nicht finde.
Beim RPi4 hatte ich jetzt keine Lust, das Gehäuse nur zum Testen auseinander zu bauen.


Kurzes Beispiel mit atexit (für GPIO.cleanup):

Code: Alles auswählen

deadeye@rpi0w2:~ $ python fail.py 
Auch ein SystemExit lässt atexit noch alle registrierten Funktion aufrufen
Yay, der callback ist ausgeführt worden. Könnte auch ein GPIO.cleanup sein
Und der Code:

Code: Alles auswählen

import atexit


atexit.register(lambda: print("Yay, der callback ist ausgeführt worden. Könnte auch ein GPIO.cleanup sein"))
raise SystemExit("Auch ein SystemExit lässt atexit noch alle registrierten Funktion aufrufen")

# Hier endet das Programm und die registrierten Funktionen werden durch
# atexit aufgerufen

# Dises Statement wird nur erreicht, wenn man die vorherige Exception nicht auslöst.
1 / 0 # Hier wird eine Exception ausgelöst, die nicht abgefangen wird.
Ich hoffe, dass das ein bisschen weiterhilft. Pfade usw. müssen natürlich angepasst werden, da es nicht mit deinen Daten übereinstimmt.

Re: Song beim PIN Eingang

Verfasst: Sonntag 10. Juli 2022, 16:42
von Sirius3
@Danoo: der Player kann wohl nur einmal abspielen, da muß man mit Media arbeiten:

Code: Alles auswählen

from queue import Queue
from pathlib import Path
from RPi import GPIO
import vlc

PIN_LEFT_PLAYER = 19
PIN_RIGHT_PLAYER = 21
PIN_START = 20

SONG_PATH = Path("/home/pi/Desktop/Playsound/songs")

SONGS = {
    PIN_START: "Boxing Bell Sound Effect.mp3",
    PIN_LEFT_PLAYER: "Air_Horn.mp3",
    PIN_RIGHT_PLAYER: "Air_Horn.mp3",
}


def initialize():
    queue = Queue()
    GPIO.setmode(GPIO.BCM)
    for pin in [PIN_LEFT_PLAYER, PIN_RIGHT_PLAYER, PIN_START]:
        GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
        GPIO.add_event_detect(pin, GPIO.FALLING, callback=queue.put, bouncetime=3000)
    return queue


def main():
    try:
        queue = initialize()
        media_player = vlc.MediaPlayer()
        songs = {
            pin: vlc.Media(SONG_PATH / song)
            for pin, song in SONGS.items()
        }
        while True:
            pin = queue.get()
            media_player.set_media(songs[pin])
            media_player.play()
    finally:
        GPIO.cleanup()

if __name__ == "__main__":
    main()
@DeaD_EyE: auch wenn vlc intern viel mit globalem Zustand arbeitet, muß man das ja nicht in Python auch machen. Das extra Modul ist dann auch etwas übertrieben für eine Funktion.
Callbacks arbeiten intern mit eigenen Threads, da muß man immer aufpassen und ein externes C-Modul würde ich darin nicht aufrufen, sondern den Callback so einfach halten wie möglich.
Das ist vor allem immer seltsam, wenn im Hauptthread nur geschlafen wird.
GPIO.cleanup sollte in einem finally-Block aufgerufen werden, damit man wirklich sicher ist, dass das ausgeführt wird. Stell Dir vor, jemand drückt Ctrl+C im except-Block.
`atexit` ist da unnötig komplex.

Re: Song beim PIN Eingang

Verfasst: Montag 11. Juli 2022, 14:46
von DeaD_EyE
GPIO.cleanup sollte in einem finally-Block aufgerufen werden, damit man wirklich sicher ist, dass das ausgeführt wird. Stell Dir vor, jemand drückt Ctrl+C im except-Block.
`atexit` ist da unnötig komplex.
Wenn danach nichts mehr kommt, wird der Interpreter beendet und auch dann werden die registrierten Funktionen aufgerufen. Das passiert nicht, sofern noch non-deamon-Threads laufen.
Das einzige was den Aufruf von atexit verhindert, ist z.B. ein Signal. Da hilft der finally-Block dann auch nicht mehr.
Callbacks arbeiten intern mit eigenen Threads, da muß man immer aufpassen und ein externes C-Modul würde ich darin nicht aufrufen, sondern den Callback so einfach halten wie möglich.
Du meinst sicherlich die Callbacks von RPi.GPIO. Callbacks sollte man kurz halten, aber bei einer Bouncetime von 3 Sekunden ist das egal. Mir gefällt das mit den Queues überhaupt nicht, auch wenn es bei Threads der sichere Standard-Weg ist. Eins ist anzumerken, wenn mal ein Fehler bei einem Callback auftritt, der durch C aufgerufen wird und im Callback ein Bug ist, kann das sogar den Interpreter killen.