Bonjour à tous,
Le but du projet est d’utiliser un téléphone à cadran S63 comme lecteur de mp3.
Matériel : téléphone S63, Raspberry Pi Zero W, DAC externe, Raspberry Pi OS Lite.
Fonctionnement :
1- au décroché dial-tone.mp3 est joué (fréquence 440Hz)
2- lorsque l’utilisateur compose un numéro, le son s’arrête et les impulsions créées par le cadran rotatif sont comptées pour connaitre le numéro saisi par l’utilisateur.
3- une fois les différents chiffres stockés, le numéro final sert de recherche de fichier mp3. Exemple si 42 est saisi le fichier 42_texteexemple.mp3 est joué.
4- juste avant la lecture de 42_texteexemple.mp3 le fichier recherche.mp3 est joué.
5- si le aucun fichier ne commence par 42 le fichier non2.mp3 est joué. L’utilisateur doit alors raccrocher pour recommencer.
Dans mon code l’étape 1 fonctionne, mais l’étape 2 ne fonctionne pas. Le fichier lis le fichier mp3 mais ne semble pas voir les numéros du cadran.
Voici le code avec toutes les fonctions
import RPi.GPIO as GPIO
import pygame
import os
import time
from typing import Optional
# Constants for GPIO pins
GPIO_PIN_OFF_HOOK = 25
GPIO_PIN_DIAL = 17
# Configuration des GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIO_PIN_OFF_HOOK, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(GPIO_PIN_DIAL, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# Initialisation de pygame pour la lecture audio
pygame.mixer.init()
# Chemin du répertoire des sons
SOUND_DIR = "/home/user/sounds"
def play_sound(file: str) -> None:
"""Plays an audio file if it exists."""
if os.path.exists(file):
print(f"Lecture du fichier : {file}")
pygame.mixer.music.load(file)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy() and GPIO.input(GPIO_PIN_OFF_HOOK) == GPIO.LOW:
time.sleep(0.1)
else:
print(f"Fichier {file} non trouvé.")
def stop_sound() -> None:
"""Stops the currently playing audio."""
if pygame.mixer.music.get_busy():
print("Arrêt de la lecture audio.")
pygame.mixer.music.stop()
def wait_for_off_hook() -> None:
"""Waits for the off-hook signal and plays the dial tone."""
print("En attente de décroché...")
while GPIO.input(GPIO_PIN_OFF_HOOK):
time.sleep(0.1)
print("Décroché détecté.")
play_sound(os.path.join(SOUND_DIR, "dial-tone.mp3"))
def read_dial() -> Optional[int]:
"""Reads the number of pulses from the dial and returns the corresponding digit."""
dial_count = 0
print("Lecture du cadran...")
while GPIO.input(GPIO_PIN_OFF_HOOK) == GPIO.LOW:
while GPIO.input(GPIO_PIN_DIAL) == GPIO.LOW:
time.sleep(0.01)
while GPIO.input(GPIO_PIN_DIAL) == GPIO.HIGH:
time.sleep(0.01)
dial_count += 1
time.sleep(0.1) # Debouncing
if dial_count == 10:
return 0
return dial_count
def find_sound_file(dialed_number: str) -> Optional[str]:
"""Searches for a sound file corresponding to the dialed number."""
print(f"Recherche de fichier pour le numéro composé : {dialed_number}")
for file in os.listdir(SOUND_DIR):
if file.startswith(dialed_number + "_"):
print(f"Fichier trouvé : {file}")
return file
print("Aucun fichier trouvé pour le numéro composé.")
return None
def main() -> None:
"""Main function to handle the phone interaction."""
try:
while True:
wait_for_off_hook()
dialed_number = ""
composition_started = False
while GPIO.input(GPIO_PIN_OFF_HOOK) == GPIO.LOW:
digit = read_dial()
if digit is not None:
if not composition_started:
stop_sound() # Stop dial tone sound once dialing starts
composition_started = True
dialed_number += str(digit)
print(f"Numéro composé jusqu'à présent : {dialed_number}")
if dialed_number:
search_file = find_sound_file(dialed_number)
if search_file:
play_sound(os.path.join(SOUND_DIR, "recherche.mp3"))
play_sound(os.path.join(SOUND_DIR, search_file))
else:
play_sound(os.path.join(SOUND_DIR, "non2.mp3"))
stop_sound() # Stop sound if the phone is hung up
except KeyboardInterrupt:
print("Programme interrompu par l'utilisateur.")
finally:
stop_sound()
GPIO.cleanup()
print("Nettoyage des GPIO et arrêt du programme.")
if __name__ == "__main__":
main()
Je commence tout juste en Python et avec le Raspberry il doit y avoir une logique qui me manque.
J’ai essayer avec un LLM de trouver le soucis mais sans succès.
Merci pour votre aide.
Bonjour,
C’est bien cette étape qui ne fonctionne pas ?
Donc qui correspond à ce code ?
def read_dial() -> Optional[int]:
"""Reads the number of pulses from the dial and returns the corresponding digit."""
dial_count = 0
print("Lecture du cadran...")
while GPIO.input(GPIO_PIN_OFF_HOOK) == GPIO.LOW:
while GPIO.input(GPIO_PIN_DIAL) == GPIO.LOW:
time.sleep(0.01)
while GPIO.input(GPIO_PIN_DIAL) == GPIO.HIGH:
time.sleep(0.01)
dial_count += 1
time.sleep(0.1) # Debouncing
if dial_count == 10:
return 0
return dial_count
Donc la question est est-ce que le programme voit les impulsions ?
ça peut être un problème logiciel, mais aussi matériel.
Vous pouvez ajouter des print
pour voir si le programme passe par certains endroits par exemple:
dial_count += 1
print ("Nombre dial_count = {dialcount} )
time.sleep(0.1) # Debouncing
A+
Merci @jelopo.
Le problème venait bien de la logique de boucle et de la lecture du cadran.
J’ai pu identifier cela avec les print.
Lorsque l’utilisateur compose le numéro 8 la roue du cadran en retournant à sa position initiale ouvre et ferme 8 fois le circuit du GPIO 17.
Voici le code fonctionnel si cela peux intéresser quelqu’un :
import RPi.GPIO as GPIO
import pygame
import time
import os
# Configuration des GPIO et des chemins de fichiers
GPIO_PINS = {"PIN_DECROCHE": 25, "PIN_CADRAN": 17}
SOUND_DIR = "/home/user/sounds"
SOUNDS = {
"DIAL_TONE": os.path.join(SOUND_DIR, "dial_tone_20.mp3"),
"RECHERCHE": os.path.join(SOUND_DIR, "recherche.mp3"),
"NON_TROUVE": os.path.join(SOUND_DIR, "non3.mp3"),
"NOTIF": os.path.join(SOUND_DIR, "notif2.mp3")
}
# Initialisation de pygame pour la lecture audio
pygame.mixer.init()
# Déclaration globale de la variable sound_stopped
sound_stopped = False
def setup_gpio():
"""Configuration des GPIO."""
print("Configuration des GPIO...")
GPIO.setmode(GPIO.BCM)
for pin in GPIO_PINS.values():
GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
print("GPIO configurés.")
def play_sound(file_path):
"""Joue un fichier audio."""
global sound_stopped # Déclaration de l'utilisation de la variable globale
print(f"Lecture du son : {file_path}")
sound_stopped = False # Réinitialiser sound_stopped à chaque nouvelle lecture
pygame.mixer.music.load(file_path)
pygame.mixer.music.play()
def wait_for_sound_to_finish_or_raccroche():
"""Attend la fin du son ou l'action de raccrocher."""
while pygame.mixer.music.get_busy():
if GPIO.input(GPIO_PINS["PIN_DECROCHE"]) == GPIO.HIGH:
print("Le combiné a été raccroché pendant la lecture.")
pygame.mixer.music.stop()
return False
time.sleep(0.1)
return True
def stop_sound():
"""Arrête la lecture audio."""
print("Arrêt de la lecture audio.")
pygame.mixer.music.stop()
def read_dial():
"""Lit les impulsions du cadran et retourne le chiffre correspondant."""
global sound_stopped # Déclaration de l'utilisation de la variable globale
print("Lecture du cadran...")
pulses = 0
last_state = GPIO.input(GPIO_PINS["PIN_CADRAN"])
last_pulse_time = None
start_time = time.time()
# Temps pour détection d'impulsion stable (debouncing)
debounce_time = 0.005 # 5ms
pulse_timeout = 0.2 # 200ms pour considérer qu'il s'agit du même chiffre
while True:
if GPIO.input(GPIO_PINS["PIN_DECROCHE"]) == GPIO.HIGH:
print("Le combiné a été raccroché.")
stop_sound()
return None
current_state = GPIO.input(GPIO_PINS["PIN_CADRAN"])
if current_state != last_state:
time.sleep(debounce_time) # Attendre pour stabiliser la lecture
current_state = GPIO.input(GPIO_PINS["PIN_CADRAN"]) # Relecture pour confirmation
if current_state != last_state:
if not sound_stopped:
stop_sound()
sound_stopped = True
if current_state == GPIO.HIGH:
current_time = time.time()
if last_pulse_time is None or (current_time - last_pulse_time) >= pulse_timeout:
if pulses > 0:
break # Le chiffre est terminé
pulses = 1 # Nouvelle série d'impulsions
else:
pulses += 1
last_pulse_time = current_time
last_state = current_state
time.sleep(0.001)
if pulses == 0:
print("Aucune impulsion détectée.")
return None
pulses_mod = pulses % 10 if pulses > 0 else 10
print(f"Nombre d'impulsions lues : {pulses}, chiffre obtenu : {pulses_mod}")
return pulses_mod
def get_dialed_number():
"""Retourne le numéro composé par l'utilisateur."""
print("Obtention du numéro composé...")
number = ""
digit_timeout_long = 3.0 # Temps d'attente (en secondes) après chaque chiffre pour vérifier si un autre chiffre est composé
last_digit_time = None
while True:
if GPIO.input(GPIO_PINS["PIN_DECROCHE"]) == GPIO.HIGH:
print("Le combiné a été raccroché.")
stop_sound()
return None
digit = read_dial()
if digit is not None:
digit = digit if digit != 10 else 0
number += str(digit)
last_digit_time = time.time()
print(f"Numéro actuel : {number}")
# Si plus de 3 secondes se sont écoulées sans nouvelle impulsion, le numéro est terminé
if last_digit_time and (time.time() - last_digit_time >= digit_timeout_long):
print(f"Numéro final composé : {number}")
return number
time.sleep(0.01)
def find_audio_file(number):
"""Recherche un fichier audio correspondant au numéro composé."""
print(f"Recherche du fichier audio pour le numéro : {number}")
for file in os.listdir(SOUND_DIR):
if file.startswith(f"{number}_") and file.endswith(".mp3"):
file_path = os.path.join(SOUND_DIR, file)
print(f"Fichier audio trouvé : {file_path}")
return file_path
print("Aucun fichier audio trouvé.")
return None
def main():
"""Boucle principale du programme."""
setup_gpio()
try:
while True:
if GPIO.input(GPIO_PINS["PIN_DECROCHE"]) == GPIO.LOW:
print("Le combiné a été décroché.")
play_sound(SOUNDS["DIAL_TONE"])
dialed_number = get_dialed_number()
if dialed_number:
print(f"Numéro composé : {dialed_number}")
audio_file = find_audio_file(dialed_number)
if audio_file and wait_for_sound_to_finish_or_raccroche():
play_sound(audio_file)
wait_for_sound_to_finish_or_raccroche()
else:
play_sound(SOUNDS["NON_TROUVE"])
wait_for_sound_to_finish_or_raccroche()
while GPIO.input(GPIO_PINS["PIN_DECROCHE"]) == GPIO.LOW:
time.sleep(0.1)
stop_sound()
time.sleep(0.1)
except KeyboardInterrupt:
print("Programme arrêté par l'utilisateur.")
finally:
print("Nettoyage des GPIO et arrêt de pygame.")
GPIO.cleanup()
pygame.mixer.quit()
if __name__ == "__main__":
main()
Mon problème principal est résolu.
Axe d’amélioration de ce code :
Une fois le premier chiffre reçu, le son dial_tone est arrêté. Cela entraine un petit délai qui fait que si l’utilisateur compose rapidement le numéro suivant, il est mal interprété.
Pistes :
1- Tester avec mpg321 comme lecteur audio au lieu de pygame.
2- Baisser le son à 0 au lieu de l’arrêter
Le raspberry pi zero W est très peu puissant, si vous avez des astuces sur son optimisation je suis preneur.
Merci