Song beim PIN Eingang

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Danoo
User
Beiträge: 14
Registriert: Dienstag 12. April 2022, 12:50

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()
Benutzeravatar
__blackjack__
User
Beiträge: 13116
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@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?
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
__deets__
User
Beiträge: 14543
Registriert: Mittwoch 14. Oktober 2015, 14:29

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.
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

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
Benutzeravatar
hyle
User
Beiträge: 96
Registriert: Sonntag 22. Dezember 2019, 23:19
Wohnort: Leipzig

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.
Alles was wir sind ist Sand im Wind Hoschi.
Danoo
User
Beiträge: 14
Registriert: Dienstag 12. April 2022, 12:50

__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
Danoo
User
Beiträge: 14
Registriert: Dienstag 12. April 2022, 12:50

__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 :)
Danoo
User
Beiträge: 14
Registriert: Dienstag 12. April 2022, 12:50

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()
Benutzeravatar
hyle
User
Beiträge: 96
Registriert: Sonntag 22. Dezember 2019, 23:19
Wohnort: Leipzig

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.
Alles was wir sind ist Sand im Wind Hoschi.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

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()
Danoo
User
Beiträge: 14
Registriert: Dienstag 12. April 2022, 12:50

Okay :)
Dankeschön
Danoo
User
Beiträge: 14
Registriert: Dienstag 12. April 2022, 12:50

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]
Benutzeravatar
DeaD_EyE
User
Beiträge: 1021
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

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.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@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.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1021
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

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.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Antworten