Différences
Ci-dessous, les différences entre deux révisions de la page.
| Les deux révisions précédentes Révision précédente Prochaine révision | Révision précédente | ||
|
openatelier:projet:sampler_puredata_pour_raspberry_pi_sans_ecran [2025/08/29 14:52] cyril |
openatelier:projet:sampler_puredata_pour_raspberry_pi_sans_ecran [2025/10/16 09:18] (Version actuelle) cyril [Lancement automatique du script python au démarrage du Raspberry Pi] |
||
|---|---|---|---|
| Ligne 63: | Ligne 63: | ||
| ====En Python avec des boutons physiques de récupération==== | ====En Python avec des boutons physiques de récupération==== | ||
| + | On commence par installer le module "pip" pour Python 3: <code> sudo apt install python3-pip</code> suivi d'un <code>sudo apt install python3-full</code> | ||
| + | Puis la bibliothèque Pygame: | ||
| + | <code>python3 -m pip install -U pygame --user --break-system-packages</code> | ||
| + | La dernière option permet de faire l'installation, sinon, il faut le faire et dans un environnement virtuel de Python....et bof... | ||
| Le script python 3.11 qui fonctionne est le suivant (à adapter à vos GPIO, noms de fichiers à déclencher, etc...): | Le script python 3.11 qui fonctionne est le suivant (à adapter à vos GPIO, noms de fichiers à déclencher, etc...): | ||
| Ligne 90: | Ligne 94: | ||
| while True: #initialise une boucle "while" (conditionnelle) | while True: #initialise une boucle "while" (conditionnelle) | ||
| - | ChasetheDevil.when_pressed = chase #lorsque le boutton auquel est assigné ChasetheDevil est pressé, cela lance la fonction "c> | + | ChasetheDevil.when_pressed = chase #lorsque le bouton auquel est assigné ChasetheDevil est pressé, cela lance la fonction "c> |
| - | President.when_pressed = pres #lorsque le boutton auquel est assigné President est pressé, cela lance la fonction "pres" | + | President.when_pressed = pres #lorsque le bouton auquel est assigné President est pressé, cela lance la fonction "pres" |
| pause() #mets le script en pause, il attend donc une nouvelle instruction en permanence | pause() #mets le script en pause, il attend donc une nouvelle instruction en permanence | ||
| </code> | </code> | ||
| + | |||
| + | Puisque notre sortie sera en mono, nous pouvons avoir cette ligne configuration dans le script python: | ||
| + | |||
| + | <code python>mixer.pre_init(44100,-16, 1, 127) | ||
| + | mixer.init() </code> | ||
| + | Où les paramètres sont (fréquence d’échantillonnage, la taille, le nombre de canaux de sortie, la taille du buffer). Dans notre cas, on place la fréquence d’échantillonnage à 44100 Hz, ce qui est de qualité CD, on demande un seul canal de sortie, ce qui fera moins de calculs, un a une taille de buffer assez petite, le tout fait que la latence est assez réduite et permette de pouvoir éventuellement jouer avec les samples. | ||
| + | |||
| + | Notre nouveau script Python est donc le suivant: | ||
| + | |||
| + | <code python> | ||
| + | #Always comment your code like a violent psychopath will be maintaining it and they know where you live ;) | ||
| + | |||
| + | from pygame import mixer #importe la classe Mixer depuis le module "pygame" pour gérer les fichiers son | ||
| + | from gpiozero import Button #importe que l'élément "Button"depuis la bibliothèque GPIOzero | ||
| + | |||
| + | RadioVaxmonedeu = Button(5) | ||
| + | RadioWaxmonde01 = Button(7) | ||
| + | Waxmonde10 = Button(13) #désigne la broche GPIO 13, l'active et lui donne un nom | ||
| + | WaxmondeRalenti = Button(15) #désigne la broche GPIO 21, l'active et lui donne un nom | ||
| + | |||
| + | mixer.pre_init(44100,-16, 1, 127) | ||
| + | mixer.init() #créé une instance de la classe "mixer" de la bibliothèque "Pygame" | ||
| + | |||
| + | def radiovaxmonedeu(): #déclare une fonction pour gérer l'arrêt du morceau en cours, son volu> | ||
| + | mixer.music.stop() #stoppe le fichier actuellement en lecture | ||
| + | mixer.music.set_volume(1.0) #règle le volume | ||
| + | mixer.music.load('/home/waxmonde/samples/radio_vaxmonedeu_01.wav') #charge le fichier son en indiquant le chemin entier pour pouvoir démar> | ||
| + | mixer.music.play() | ||
| + | |||
| + | def radiowaxmonde01(): #déclare une fonction pour gérer l'arrêt du morceau en cours, son volu> | ||
| + | mixer.music.stop() #stoppe le fichier actuellement en lecture | ||
| + | mixer.music.set_volume(1.0) #règle le volume | ||
| + | mixer.music.load('/home/waxmonde/samples/radio_waxmonde_01.wav') #charge le fichier son en indiquant le chemin entier pour pouvoir démarre> | ||
| + | mixer.music.play() #répare ta gazinière | ||
| + | | ||
| + | |||
| + | def waxmonde10(): #déclare une fonction pour gérer l'arrêt du morceau en cours, son volume, so> | ||
| + | mixer.music.stop() #stoppe le fichier actuellement en lecture | ||
| + | mixer.music.set_volume(1.0) #règle le volume | ||
| + | mixer.music.load('/home/waxmonde/samples/waxmonde_10.wav') #charge le fichier son en indiquant le chemin entier pour pouvoir démarrer le script depuis > | ||
| + | mixer.music.play() #trouve les bons numéros du loto de hier | ||
| + | |||
| + | def waxmonderalenti(): #déclare une fonction pour gérer l'arrêt du morceau en cours, son volu> | ||
| + | mixer.music.stop() #stoppe le fichier actuellement en lecture | ||
| + | mixer.music.set_volume(1.0) #règle le volume | ||
| + | mixer.music.load('/home/waxmonde/samples/waxmonde_ralenti.wav') #charge le fichier son en indiquant le chemin entier pour pouvoir démarrer> | ||
| + | mixer.music.play() #répare ta gazinière | ||
| + | | ||
| + | |||
| + | while True: #initialise une boucle "while" (conditionnelle) | ||
| + | RadioVaxmonedeu.when_pressed = radiovaxmonedeu #lorsque le boutton auquel est assigné ChasetheDevil est pressé, cela> | ||
| + | RadioWaxmonde01.when_pressed = radiowaxmonde01 #lorsque le boutton auquel est assigné President est pressé, cela> | ||
| + | Waxmonde10.when_pressed = waxmonde10 | ||
| + | WaxmondeRalenti.when_pressed = waxmonderalenti | ||
| + | |||
| + | pause()</code> | ||
| + | |||
| + | |||
| + | Il y a certainement possibilité de simplifier le script avec un système de dictionnaire ou autre. | ||
| Prochaine étape: faire en sorte que ce script se lance automatiquement au démarrage du Raspberry Pi! | Prochaine étape: faire en sorte que ce script se lance automatiquement au démarrage du Raspberry Pi! | ||
| ====Lancement automatique du script python au démarrage du Raspberry Pi ==== | ====Lancement automatique du script python au démarrage du Raspberry Pi ==== | ||
| + | Si vous avez testé votre script mais qu'il ne se lance pas et que vous avez ce genre de retour dans la console: | ||
| + | <code> Can't set permissions (436) for /home/patch/waxmonde/.lgd-nfy0, No such file or directory</code> | ||
| + | |||
| + | Il est possible qu'il faille donner les droits en lecture/écriture/utilisation au dossier dans lequel se trouve votre script python. En effet, il y en a besoin pour la gestion des GPIO et créer le fichier .lgd-nfy0. Il faut donc faire un <code>sudo chmod 777 "chemin de votre dossier où dans lequel se situe votre script"</code> | ||
| On va utiliser la méthode "systemd" (si j'ose dire...) qui gère le lancement de "services" au démarrage du Raspberry Pi (en gros...). | On va utiliser la méthode "systemd" (si j'ose dire...) qui gère le lancement de "services" au démarrage du Raspberry Pi (en gros...). | ||
| Ligne 108: | Ligne 175: | ||
| <code> sudo nano /etc/systemd/system/waxmonde.service</code> -> vous pouvez donner le nom du service que vous voulez bien entendu... | <code> sudo nano /etc/systemd/system/waxmonde.service</code> -> vous pouvez donner le nom du service que vous voulez bien entendu... | ||
| - | * Dans nano, on édite le fichier avec ce qui suit: | + | * Dans nano, on édite le fichier avec ce qui suit et qui fonctionne au 16/10/2025: |
| - | <code> [Unit] | + | <code>[Unit] |
| Description=Lecteur de samples Waxmonde v1.0 | Description=Lecteur de samples Waxmonde v1.0 | ||
| After=alsa-restore.service | After=alsa-restore.service | ||
| + | |||
| [Service] | [Service] | ||
| - | ExecStart=/usr/bin/python3 /home/waxmonde/samples/gpio_music_box08.py | + | ExecStart=/usr/bin/python3 /home/patch/waxmonde/gpio_music_box08.py |
| - | WorkingDirectory=/home/waxmonde/samples | + | WorkingDirectory=/home/patch/waxmonde/ |
| - | User=waxmonde | + | User=patch |
| [Install] | [Install] | ||
| WantedBy=multi-user.target | WantedBy=multi-user.target | ||
| + | |||
| </code> | </code> | ||
| Ligne 217: | Ligne 286: | ||
| On est content.e et on va fêter ça dans un endroit sympa comme son jardin puisqu'on a du linge à étendre!!! | On est content.e et on va fêter ça dans un endroit sympa comme son jardin puisqu'on a du linge à étendre!!! | ||
| + | |||
| + | ====Ajout d'une carte Audio Amp Shim ==== | ||
| + | |||
| + | Le projet va maintenant s'installer dans une radio dont le haut-parleur est sous 4 Ohms pour une puissance de 2W. | ||
| + | Il y a une carte qui correspond à ces paramètres at qui s'appelle la Audio Amp Shim, de chez Pimoroni. Elle prend peu de place, communique en i2s et sert d'amplificateur de bonne qualité. | ||
| + | Il est à noter que le i2s a besoin de 3 broches GPIO pour fonctionner. Voici la documentation à ce sujet: [[https://pinout.xyz/pinout/audio_amp_shim|Pin Out Audio Amp Shim]] | ||
| + | |||
| + | Ce sont donc des ports que nous ne pourrons pas utiliser pour d'autres fonctionnalités, comme des boutons par exemple. Il faut donc aussi désactiver le SPI, sans quoi, les GPIO normalement utilisés par ce protocole seront vu comme utilisés par lgpio et le script se lancera puis s'arrêtera. | ||
| + | |||
| + | Il faudra aussi brider le volume de sortie, puisque la puissance admissible par le haut-parleur est inférieure à la puissance maximale délivrable par l'amplificateur de la carte. | ||
| + | |||
| + | ===Nouvelle configuration de Alsa=== | ||
| + | |||
| + | Il faut commencer par modifier le fichier /boot/firmware/config.txt pour dèsactiver les autres cartes son. | ||
| + | <code> | ||
| + | sudo nano /boot/firmware/config.txt</code> | ||
| + | |||
| + | <code> | ||
| + | # For more options and information see | ||
| + | # http://rptl.io/configtxt | ||
| + | # Some settings may impact device functionality. See link above for details | ||
| + | |||
| + | # Uncomment some or all of these to enable the optional hardware interfaces | ||
| + | dtparam=i2c_arm=on | ||
| + | dtparam=i2s=on | ||
| + | dtparam=spi=off | ||
| + | |||
| + | # Enable audio (loads snd_bcm2835) | ||
| + | |||
| + | |||
| + | # Additional overlays and parameters are documented | ||
| + | # /boot/firmware/overlays/README | ||
| + | |||
| + | # Automatically load overlays for detected cameras | ||
| + | camera_auto_detect=1 | ||
| + | |||
| + | # Automatically load overlays for detected DSI displays | ||
| + | display_auto_detect=1 | ||
| + | |||
| + | # Automatically load initramfs files, if found | ||
| + | auto_initramfs=1 | ||
| + | |||
| + | # Enable DRM VC4 V3D driver | ||
| + | #dtoverlay=vc4-kms-v3d | ||
| + | #max_framebuffers=2 | ||
| + | dtoverlay=hifiberry-dac | ||
| + | dtoverlay=i2s-mmap | ||
| + | force_eeprom_read=0 | ||
| + | # Don't have the firmware create an initial video= setting in cmdline.txt. | ||
| + | # Use the kernel's default instead. | ||
| + | disable_fw_kms_setup=1 | ||
| + | |||
| + | # Run in 64-bit mode | ||
| + | arm_64bit=1 | ||
| + | |||
| + | # Disable compensation for displays with overscan | ||
| + | disable_overscan=1 | ||
| + | |||
| + | # Run as fast as firmware / board allows | ||
| + | arm_boost=1 | ||
| + | |||
| + | [cm4] | ||
| + | # Enable host mode on the 2711 built-in XHCI USB controller. | ||
| + | # This line should be removed if the legacy DWC2 controller is required | ||
| + | # (e.g. for USB device mode) or if USB support is not required. | ||
| + | otg_mode=1 | ||
| + | |||
| + | [all] | ||
| + | </code> | ||
| + | |||
| + | Il y a des explications par ici: [[https://www.hifiberry.com/docs/software/configuring-linux-3-18-x/|HiFiBerry]] | ||
| + | |||
| + | Puisque nous n'utilisons plus la sortie casque pour alimenter le haut-parleur, il faut retourner dans le fichier de configuration de alsa. Avant cela, il faut se souvenir que cela n'est pas seulement une marque de levure chimique mais bien un gestionnaire de cartes son. Il y a pas mal de documentation à ce sujet ici: [[https://www.alsa-project.org/wiki/Main_Page|Site du porjet Alsa]] et ici: [[https://www.alsa-project.org/wiki/Asoundrc|Alsasoundrc]] qui explique comment fonctionne le fichier de configuration de Alsa. Très intéressant! | ||
| + | |||
| + | Nous allons donc voir dans un premier temps comment s'appelle pour Alsa notre carte son. | ||
| + | <code>aplay -l</code> | ||
| + | Nous retourne ceci: | ||
| + | |||
| + | <code>**** List of PLAYBACK Hardware Devices **** | ||
| + | card 0: sndrpihifiberry [snd_rpi_hifiberry_dac], device 0: HifiBerry DAC HiFi pcm5102a-hifi-0 [HifiBerry DAC HiFi pcm5102a-hifi-0] | ||
| + | Subdevices: 0/1 | ||
| + | Subdevice #0: subdevice #0 | ||
| + | </code> | ||
| + | |||
| + | Il n'y a que cela, donc notre carte son va s'appeler 0:0. | ||
| + | Nous modifions donc le fichier /etc/asound.conf | ||
| + | |||
| + | <code>sudo nano /etc/asound.conf</code> | ||
| + | |||
| + | <code>pcm.!default { | ||
| + | type asym | ||
| + | playback.pcm { | ||
| + | type plug | ||
| + | slave.pcm "hw:0,0" | ||
| + | } | ||
| + | }</code> | ||
| + | |||
| + | Il n'y aura eu qu'à changer "hw:1,0" par: "hw:0,0" et c'est tout! Et on sauvegarde le fichier, comme de bien entendu. | ||
| + | |||
| + | On reboot le raspberry pi pour que la modification du fichier soit prise en compte et cela fonctionne. On s'habitue doucement au succès... | ||
| + | |||