aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSébastien Dailly <sebastien@chimrod.com>2021-11-15 10:28:20 +0100
committerSébastien Dailly <sebastien@dailly.me>2022-02-01 18:38:02 +0100
commit43ff6e6762a671a0498411d832a7a1d413a196a4 (patch)
treea5836d8cf25bf9b5f87b1fd4e30b61f4146b7cf4
parentc43aaf18a258b0bf9b8e598f3ee9baea4f209e7a (diff)
Raspberry pi and buildroot
-rw-r--r--content/Informatique/2021-11-piaudio.rst443
-rw-r--r--content/images/buildroot/bluetooth.pngbin0 -> 8997 bytes
-rw-r--r--content/images/buildroot/buildroot.pngbin0 -> 65022 bytes
-rw-r--r--content/images/buildroot/connexion.jpegbin0 -> 50794 bytes
-rw-r--r--content/images/buildroot/pulseaudio.pngbin0 -> 25623 bytes
5 files changed, 443 insertions, 0 deletions
diff --git a/content/Informatique/2021-11-piaudio.rst b/content/Informatique/2021-11-piaudio.rst
new file mode 100644
index 0000000..b7649a7
--- /dev/null
+++ b/content/Informatique/2021-11-piaudio.rst
@@ -0,0 +1,443 @@
+.. -*- mode: rst -*-
+.. -*- coding: utf-8 -*-
+
+===========================================
+Découverte de buildroot avec un raspberrypi
+===========================================
+
+:date: 2021-11-15
+:summary: Comment construire un système complet pour un raspberry pi avec
+ Buildroot ? Présentation détaillée d’un projet permettant de
+ construire un serveur de son.
+
+.. _`configs/config`: https://git.chimrod.com/piaudio.git/tree/configs/config
+.. _`build.sh`: https://git.chimrod.com/piaudio.git/tree/build.sh
+.. _`post-build.sh`: https://git.chimrod.com/piaudio.git/tree/board/post-build.sh
+.. _`post-image.sh`: https://git.chimrod.com/piaudio.git/tree/board/post-image.sh
+
+Présentation
+============
+
+Il y a quelque temps, j’avais monté un système audio avec un raspberrypi pi 0,
+permettant de brancher la carte à ma chaine hifi et diffuser ainsi la musique
+dans le salon. En équipant la carte d’un DAC de qualité, je centralise la
+musique qui peut etre jouée depuis n’importe quel PC, et comme la carte est
+équipée d’une connexion bluetooth, cela permet aussi d’y connecter les
+smartphones.
+
+Dans mon `montage originel`_, je partais d’une distribution raspbian que
+j’avais modifié manuellement. À la suite d’une erreur de manipulation, j’ai dû
+refaire le paramétrage une seconde fois, et c’est depuis quelque chose qui me
+préoccupait : il faudrait que je puisse disposer d’un système déjà configuré
+qui fonctionnerait « out of the box », sans que j’aie à toucher à la
+configuration.
+
+.. _`montage originel`: https://linuxfr.org/users/chimrod/journaux/utiliser-un-pi-zero-comme-serveur-de-son-et-lecteur-bluetooth
+
+J’ai découvert entre-temps buildroot_ (via le projet showmewebcam_) qui est un
+ensemble de scripts permettant de construire un système complet. Il prend en
+charge un grand nombre d’architectures différentes, et possède une
+configuration de base pour construire les applications les plus courantes. Il
+ne reste plus qu’à l’utiliser pour reconstruire un client pulseaudio sur une
+carte raspberrypi 0, qui offre un service via le wifi et le bluetooth.
+
+.. _buildroot: https://buildroot.org/
+.. _showmewebcam: https://github.com/showmewebcam/showmewebcam/
+
+Je vous propose ici de suivre le détail des modifications apportées pour
+construire le système (le projet est disponible sur mon `dépôt git`_ – mais pas
+l’image finale).
+
+
+.. _`dépôt git`: https://git.chimrod.com/piaudio.git/about/
+
+.. admonition:: Article avancé
+ :class: warning
+
+ Je ne pensais pas qu'un jour j'écrirais ce genre d'en tête, mais il s'agit ici
+ d'un article avancé, qui nécessite de connaître le fonctionnement du système
+ linux.
+
+ Nous allons en effet construire un système de zéro, pour un usage spécifique,
+ ce qui oblige à sélectionner les services dont nous avons besoin (et savoir
+ lesquels sélectionner), et comment les configurer pour obtenir le système
+ attendu.
+
+ Aucune interface graphique ne sera installée sur la carte, et toute la
+ configuration devra être anticipée pour que la carte démarre dès son
+ branchement.
+
+Construction du système
+=======================
+
+Initialisation
+--------------
+
+Je fournis un script `build.sh`_ qui prends en paramètres :
+
+1. une architecture
+2. une commande buildroot (`all` pour lancer la création complète du système)
+
+pour l’instant, seules deux architectures ont été testées : un raspberrypi3
+(modèle A), et un raspberrypi0w :
+
+.. code:: bash
+
+ ./build.sh
+ usage: BUILDROOT_DIR=../buildroot ./build.sh {boardname} all
+ boardname: raspberrypi0w, raspberrypi3
+
+Lors de l’exécution de la commande, le système va fusionner le paramétrage par
+défaut de la carte, qui est chargé dans la configuration de buildroot, ainsi
+qu’une liste de paquets spécifiques qui sont propres à notre installation, et
+listés dans le fichier `configs/config`_
+
+Sélection des paquets
+---------------------
+
+Cette configuration par défaut inclue les paquets nécessaires pour faire
+fonctionner le réseau (wifi), pulseaudio, et le bluetooth (ainsi que les
+dépendances pour mettre toute cette architecture en place). Si l’on souhaite
+modifier un paquet, on peut regarder ceux disponibles en lançant une interface
+de sélection :
+
+.. code:: bash
+
+ ./build.sh raspberrypi3 menuconfig
+
+.. image:: {static}/images/buildroot/buildroot.png
+ :alt: L’interface menuconfig
+ :align: center
+
+Toutefois, je n’aime pas cette manière de procéder : la configuration ainsi
+générée se retrouve spécifique à une carte donnée, et l’on perd la possibilité
+d’appliquer cette même configuration sur un matériel différent.
+
+Aussi, et pour être sûr d’avoir un système qui soit reproductible, je préfère
+noter les paquets dont j’ai besoin, et je les reporte dans le fichier
+`configs/config`_ qui sera utilisé lors de la prochaine construction.
+
+Configuration du système
+------------------------
+
+En parallèle de la construction du système, nous avons à nous occuper de sa
+configuration initiale.
+
+Cela est réalisé à travers deux scripts, le premier, `post-build.sh`_, est
+exécuté après l’installation des paquets, le second, `post-image.sh`_ après la
+finalisation de l’image finale. Dans le premier nous allons modifier les
+fichiers de configuration du système, alors que dans le second nous traiterons
+les paramètres donnés lors du boot.
+
+Je présente ci-dessous quelques modifications apportées au système pour le
+faire correspondre exactement au besoin :
+
+Carte en lecture seule
+~~~~~~~~~~~~~~~~~~~~~~
+
+Étant donné que nous n’aurons pas d’interface pour nous connecter à la carte,
+elle va etre éteinte « sauvagement » régulièrement. Je préfère donc la basculer
+en lecture seule, ce qui permet aussi d’etre sûr que tout le système restera
+immuable sur la durée.
+
+
+.. code-block:: bash
+
+ sed -ie '/^\/dev\/root/ s/rw 0 1/ro 0 0/' "${TARGET_DIR}/etc/fstab"
+
+La modification est faite avec `sed`, et permet de suite de voir un peu comment
+on va modifier le système :
+
+* Chaque modification sera faite à l’aide des outils disponibles dans bash
+ (sed, cp, cat, …), il faut donc déjà etre à l’aise avec le terminal avant
+ d’aller modifier la configuration
+* Chaque command nécessite de savoir comment sont construits les fichiers que
+ l’on veut modifier
+* Dans le cas où nous ajoutons des nouvelles entrées dans un fichier, il faudra
+ contrôler *avant* que cette entrée n’est pas déjà présente
+* Et enfin, nous allons avoir du boulot pour modifier chaque élément un à un…
+
+La variable `${TARGET_DIR}` est fournie par buildroot et permet de cibler
+l’image que nous sommes en train de construire (par opposition au système hote
+dans lequel nous compilons le système). Ici, nous modifions bien le fichier
+`/etc/fstab` final, et non pas notre système linux !
+
+.. admonition:: Bonne pratique
+ :class: hint
+
+ Au vu des risques en cas d’erreur de saisie, il est bon de rajouter cette
+ ligne en début de script :
+
+ .. code-block:: bash
+
+ # Traite les variables non définies comme des erreurs lors de la substitution.
+ set -u
+
+ Nous serons ainsi sûrs de ne pas modifier notre système par erreur (ce qui ne
+ devrait pas arriver, car nous ne travaillons pas sous `root`)
+
+Ajout du wifi
+~~~~~~~~~~~~~
+
+Pour activer le wifi, nous allons laisser systemd faire le travail,
+`wpa_supplicant` propose en effet un service paramétré, qui active l’interface
+spécifiée dans le nom (ici wlan0), il suffit donc de créer un lien symbolique
+pour que celui-ci soit activé au démarrage :
+
+.. code-block:: bash
+
+ # Create the link to interface wlan0 directly in the system configuration
+ ln -sf /usr/lib/systemd/system/wpa_supplicant@.service "${TARGET_DIR}/usr/lib/systemd/system/multi-user.target.wants/wpa_supplicant@wlan0.service"
+
+(par contre, cela implique d’installer systemd sur la carte, ce qui surcharge
+les dépendances, mais cela simplifie notre travail finalement…)
+
+La configuration de la carte est comme d’habitude dans un fichier
+`wpa_supplicant.conf` qui n’est pas versionné, mais qui sera copié s’il est
+présent dans le répertoire.
+
+.. code-block:: bash
+
+ if [ -f "$BR2_EXTERNAL_PIAUDIO_PATH/wpa_supplicant.conf" ]; then
+ cat "$BR2_EXTERNAL_PIAUDIO_PATH/wpa_supplicant.conf" > "${TARGET_DIR}/etc/wpa_supplicant/wpa_supplicant-wlan0.conf"
+ …
+ fi
+
+La variable `$BR2_EXTERNAL_PIAUDIO_PATH` correspond au répertoire de base du
+système, c’est-à-dire notre répertoire racine. Il s’agit également d’une autre
+variable d’environnement fournie par buildroot qui nous permet de ne pas avoir
+à nous soucier de l’emplacement du script quand nous lançons notre compilation.
+
+Pulseaudio
+~~~~~~~~~~
+
+Vient ensuite pulseaudio. Nous n’avons pas à nous occuper de le démarrer avec
+le système car cela est fourni par un paquet ayant été sélectionné.
+
+Par contre, puisque le système est en lecture seule, nous avons quelques
+modifications à réaliser pour que le service fonctionne : nous allons créer un
+répertoire, et le monter en mémoire au démarrage afin que pulseaudio puisse y
+stocker ses fichiers :
+
+.. code-block:: bash
+
+ create_missing_dir "/var/lib/pulse"
+ if ! grep -qE '/var/lib/pulse' "${TARGET_DIR}/etc/fstab"; then
+ cat << __EOF__ >> "${TARGET_DIR}/etc/fstab"
+ tmpfs /var/lib/pulse tmpfs rw 0 0
+ __EOF__
+ fi
+
+Ensuite, nous allons modifier le fichier `/etc/pulse/system.pa` afin d’y
+ajouter les modules bluetooth, et la connexion distante :
+
+.. code-block:: bash
+
+ if ! grep -qE '^load-module module-native-protocol-tcp' "${TARGET_DIR}/etc/pulse/system.pa"; then
+ cat << __EOF__ >> "${TARGET_DIR}/etc/pulse/system.pa"
+ load-module module-bluetooth-policy
+ load-module module-bluetooth-discover
+ load-module module-native-protocol-tcp auth-ip-acl=127.0.0.1;192.168.0.0/24;2a01:e35:8ac8:0e00::/64 auth-anonymous=1
+ __EOF__
+ fi
+
+Bluetooth
+~~~~~~~~~
+
+D’abord, pour activer le bluetooth, nous allons indiquer au noyau de le charger
+au démarrage :
+
+.. code-block:: bash
+
+ cat << __EOF__ >> "${BINARIES_DIR}/rpi-firmware/config.txt"
+ dtparam=krnbt=on
+ __EOF__
+
+Cette option ajoutée récemment dans les raspberrypi va nous éviter d’avoir à
+lancer les commande `bt-attach` ou `hciattach`. le bluetooth est
+automatiquement branché sur le port uart, à la vitesse maximale prise en charge
+par la carte. Cela résoud en une ligne énormément de problèmes rencontrés lors
+de la configuration du bluetooth si l’on avait voulu faire cela manuellement.
+
+Voir la liste des options possibles sur la page suivante : https://github.com/raspberrypi/firmware/tree/master/boot/overlays
+
+Par contre, il faut libérer pour cela la connexion série, qui est configurée
+par défaut sur le port `ttyAMA0`. Il nous faut supprimer la ligne suivante dans
+le fichier `cmdline.txt` : `console=ttyAMA0,115200`
+
+Ensuite, comme précédemment, nous allons également créer un répertoire en
+mémoire pour pallier le système en lecture seule de la carte :
+
+.. code-block:: bash
+
+ create_missing_dir "/var/lib/bluetooth/"
+ if ! grep -qE '/var/lib/bluetooth' "${TARGET_DIR}/etc/fstab"; then
+ cat << __EOF__ >> "${TARGET_DIR}/etc/fstab"
+ tmpfs /var/lib/bluetooth tmpfs rw 0 0
+ __EOF__
+ fi
+
+La configuration du bluetooth est lue dans le fichier
+`/etc/bluetooth/main.conf`, c’est donc là que nous allons y stocker les
+paramètres génériques : classe de l’appareil et timeouts :
+
+.. code-block:: ini
+
+ [General]
+
+ Class = 200428
+ DiscoverableTimeout = 0
+ PairableTimeout = 0
+
+ [Policy]
+ AutoEnable=true
+
+Enfin, nous avons besoin d’autoriser les connexions externes, et ce, sans aucun
+accès au système. Cela s’effectue par le biais d’un *agent* qui demande la
+confirmation d’un code auprès des deux parties (celui qui veut se connecter, et
+celui qui accepte les connexions) pour éviter les erreurs. Ici, nous faire fi
+de la sécurité, et ouvrir les connexions à n’importe qui. Les risques sont
+limités (le système est en lecture seule, aucune des applications de base
+permettant de détourner l’ordinateur n’est installée).
+
+C’est un problème que j’avais rencontré dans mon système d’origine, et qui
+nécessitait de se connecter sur le raspberrypi pour autoriser le périphérique.
+J’ai finalement trouvé une solution (qui se résume en quelques lignes, mais qui
+m’a occupé quelques heures avant d’en arriver là).
+
+On va se créer un service *systemd* (puisqu’il est là…) qui lance un agent
+perpétuel chargé d’accepter toutes les connexions :
+
+.. code-block:: bash
+
+ cat << __EOF__ > "${TARGET_DIR}/etc/systemd/system/bt-agent.service"
+ [Unit]
+ Description=Bluetooth Agent
+ After=bt-audio.service
+ Requires=bt-audio.service
+
+ [Service]
+ Type=simple
+ ExecStartPre=bt-adapter --set Discoverable 1
+ ExecStart=bt-agent -c NoInputNoOutput
+ RestartSec=5
+ Restart=always
+ KillSignal=SIGUSR1
+
+ [Install]
+ WantedBy=bluetooth.target
+ __EOF__
+
+Au passage, ce service déclare le bluetooth en mode « découvrable », ce qui rend
+vraiment le service ouvert à tous.
+
+Connexion série
+~~~~~~~~~~~~~~~
+
+Enfin, il faut que nous puissions nous connecter à la carte pour contrôler
+qu’elle démarre correctement. Or, nous n’avons installé aucun serveur ssh :
+celle-ci est donc vérouillée. Par contre, le raspberrypi 0 nous offre une
+chance, puisqu’il est possible de s’y connecter en le branchant directement en
+usb, mais encore faut-il activer cette option au démarrage…
+
+.. note::
+
+ Cela fonctionne également avec un raspberrypi 4 ou un raspberrypi 3 A, mais
+ pour ce dernier, il faut trouver un cable USB male/male pour faire la
+ connexion. Dans mes tests avec ce dernier, j’ai triché et installé dropbear
+ qui est un serveur ssh !
+
+Il suffit d’ajouter une ligne dans le fichier `cmdline.txt` et le tour est joué
+:
+
+.. code-block:: bash
+
+ if ! grep -qE 'modules-load=dwc2,g_serial' "${BINARIES_DIR}/rpi-firmware/cmdline.txt"; then
+ sed '/^root=/ s/$/ modules-load=dwc2,g_serial/' -i "${BINARIES_DIR}/rpi-firmware/cmdline.txt"
+ fi
+
+Build
+-----
+
+Il ne nous reste plus qu’à lancer la compilation du système, et attendre le
+résultat :
+
+.. code:: bash
+
+ ./build.sh raspberrypi0w all
+
+Une fois terminé, on peut copier l’image générée et la brancher dans le
+raspberry pi
+
+.. code:: bash
+
+ sudo dd if=output/raspberrypi0w/images/sdcard.img of=/dev/XXX bs=4k
+
+Démarrage et premier lancement
+==============================
+
+.. image:: {static}/images/buildroot/connexion.jpeg
+ :alt: La carte connectée en USB
+ :align: center
+
+
+Une fois la carte branchée, on peut se connecter dessus à l’aide de la commande
+suivante :
+
+.. code:: bash
+
+ screen /dev/ttyACM0
+
+Récupérer le nom de la connexion pulseaudio
+-------------------------------------------
+
+.. code:: console
+
+ # su -s /usr/bin/sh pulse
+ $ pactl info
+ …
+ Default Sink: alsa_output.0.stereo-fallback
+
+C'est cette valeur qui est à utiliser dans le tunnel que nous allons mettre en
+place. Sur notre pc de bureau, nous allons modifier le fichier `/etc/pulse/default.pa` et ajouter cette ligne :
+
+.. code:: bash
+
+ load-module module-tunnel-sink sink_name=rpi_tunnel server=tcp:${IP}:4713 sink=alsa_output.0.stereo-fallback
+
+Comme notre raspberrypi se connecte en DHCP, son adresse IP est fournie par le
+routeur. Je conseille de lui associer une IP fixe afin que la configuration ne
+change pas :)
+
+.. image:: {static}/images/buildroot/pulseaudio.png
+ :alt: La connexion pulseaudio
+ :align: center
+
+
+Nommage du service bluetooth
+----------------------------
+
+Un dernier point que je n’ai pas réussi à comprendre, le nom du service
+bluetooth est `Bluez` au lieu du nom de la carte (`piaudio`). Cela semble être
+du au fait que la carte soit en lecture seule. La solution consiste à basculer
+la carte en écriture, relancer le service bluetooth, et redémarrer :
+
+.. code:: bash
+
+ mount -o remount,rw /
+ systemctl restart bluetooth
+ reboot
+
+Cette opération est à faire une fois seulement, il n’est pas nécessaire d’y
+revenir ensuite.
+
+Se connecter en bluetooth
+-------------------------
+
+.. image:: {static}/images/buildroot/bluetooth.png
+ :alt: La connexion bluetooth
+ :align: center
+
+Comme nous avons tout automatisé, c’est encore plus simple ! en quelques clics
+l’association est faite !
+
diff --git a/content/images/buildroot/bluetooth.png b/content/images/buildroot/bluetooth.png
new file mode 100644
index 0000000..1614169
--- /dev/null
+++ b/content/images/buildroot/bluetooth.png
Binary files differ
diff --git a/content/images/buildroot/buildroot.png b/content/images/buildroot/buildroot.png
new file mode 100644
index 0000000..15b8a05
--- /dev/null
+++ b/content/images/buildroot/buildroot.png
Binary files differ
diff --git a/content/images/buildroot/connexion.jpeg b/content/images/buildroot/connexion.jpeg
new file mode 100644
index 0000000..6655e46
--- /dev/null
+++ b/content/images/buildroot/connexion.jpeg
Binary files differ
diff --git a/content/images/buildroot/pulseaudio.png b/content/images/buildroot/pulseaudio.png
new file mode 100644
index 0000000..d5e59d2
--- /dev/null
+++ b/content/images/buildroot/pulseaudio.png
Binary files differ