Menu
Créer un système linux embarqué sur mesure avec Yocto

Créer un système linux embarqué sur mesure avec Yocto

 Cet article est le résultat de la collaboration entre nos experts humains et nos intelligences artificielles (en savoir +). 

 

Retour d’expérience sur un projet IoT avec Yocto pour Raspberry Pi

Yocto est un projet open source fascinant qui vise à créer un ensemble de modèles, d'outils et de méthodes permettant de produire des systèmes Linux embarqués personnalisés. Le projet Yocto est soutenu par la Linux Foundation et est largement utilisé dans l'industrie pour développer des systèmes d'exploitation optimisés pour des appareils embarqués comme les smartphones, les routeurs, les systèmes automobiles, et bien d'autres dispositifs électroniques.

Dans cet article, Henry-Joseph Audéoud partage son expérience sur l'utilisation de Yocto pour un projet basé sur un Raspberry Pi, en y détaillant les étapes et les défis rencontrés.

 

Introduction

Yocto n'est pas simplement un outil, mais un ensemble de projets coordonnés permettant la construction de distributions Linux sur mesure. Il utilise des outils comme BitBake, un moteur de construction, et OpenEmbedded, une collection de recettes et de classes pour la construction de logiciels. Ces outils facilitent la gestion des dépendances, la compilation croisée et l'automatisation du processus de construction. De plus, le modèle de projet est basé sur un ensemble de variables et de routines, organisés en couches empilées les unes sur les autres. Ceci permet de personnaliser facilement la configuration d’une application pour l’adapter aux besoins du projet.

J’ai utilisé un ancien modèle de Raspberry Pi, modèle 1B révision 2, nommé « groseille », pour un projet de surveillance de température. Initialement, groseille fonctionnait sous une distribution Alpine Linux et utilisait un capteur de température 1-Wire (DS18B20) pour transmettre la température via un serveur HTTP. Le but de cette aventure était de remplacer cette configuration par une distribution construite avec Yocto — que j'ai appelée GroOS. Compilation d’un noyau personnalisé supportant le protocole 1-Wire, ajout d’un serveur SSH et du lien série (pour le débogage uniquement), et enfin, développement et déploiement d’une petite application web, écrite en Rust, pour afficher la température ambiante.

 

remplacement_distribution Alpine Linux en distribution Yocto
Figure 1 : Architecture de groseille, avant (à gauche) et après (à droite) le travail de cet article

 

 

Déroulement du projet

Dans la théorie, le projet s’est déroulé en quatre parties, que je vous présente ci-dessous. D’abord, j’ai mis en place la structure pour la machine cible que j’avais ; ensuite, j’ai mis en place les liens série & SSH pour pouvoir surveiller et déboguer le système ; j’ai configuré le noyau Linux afin d’avoir la mouture qu’il me fallait ; et enfin, j'y ai déployé une application dédiée. Ça, c’est le monde théorique — en pratique, on imagine le nombre d’essais-erreurs qu’il a fallu pour converger sur la solution fonctionnelle. Et avec Yocto, il faut bien réfléchir avant d’essayer : il y a énormément de tâches à faire, et même si (heureusement) le système de construction est suffisamment intelligent pour mettre en cache les résultats intermédiaires, il n’est pas toujours capable de détecter la portée exacte de modification d’une variable : toucher à quelque chose dans la définition de la machine par exemple peut amener à une recompilation quasi-intégrale.

 

Compilation & configuration initiale

Première étape du projet, compiler une distribution que groseille serait capable d’exécuter. La couche meta-raspberrypi contient le BSP pour cette machine-là ; cette couche est même indiquée officiellement dans les couches compatibles avec Yocto. Les principales différences entre un Raspberry Pi générique comme décrit dans cette couche et groseille sont l’absence de Bluetooth & Wi-Fi (groseille est trop ancien pour avoir ces périphériques), l’absence d’interface graphique (j’utilise groseille en mode serveur uniquement) et la présence dans le Device Tree du périphérique 1-Wire branché sur un port GPIO.

J’ai donc créé la couche meta-groseille pour l’occasion, en utilisant l’outil bitbake-layers create-layer, et j’y ai créé le fichier conf/machine/raspberrypi-1brev2.conf avec ce contenu :

    require conf/machine/raspberrypi.conf 
MACHINEOVERRIDES:append = ":raspberrypi" 
# Remove features not supported by raspberrypi 1B rev2 
MACHINE_FEATURES:remove = "bluetooth wifi apm" 
# Enable 1-Wire GPIO bus in the DTB 
ENABLE_W1 ?= "1"  
La figure 2 montre l’empilement des 3 couches qu’on utilise (avec meta-rust, la 4ème, abordée plus tard dans cet article). La variable BBFILE_PRIORITY_<layer> dans le fichier de description de la couche (conf/layer.conf) permet de donner la priorité de chaque couche, c’est-à-dire conceptuellement l’ordre d’empilement des couches dans la figure.
empilement des 3 meta-couches
Figure 2 : Empilement des 4 couches du projet

 

Dans mon répertoire de travail, j’ai déclaré cette couche dans bblayers.conf à côté des autres couches que j’utilise ; et j’ai défini la machine que j’utilise (MACHINE=raspberrypi-1brev2) dans local.conf. Et ensuite… eh bien 3h de compilation à partir de zéro pour compiler l’image core-image-minimal, et un bon bout de place sur mon disque dur pour avoir le résultat — on ne peut pas en vouloir à Yocto, qui a quand même pris la peine de compiler les compilateurs, natif (pour ma machine de compilation) et croisé (pour l’ARMv6 qu’intègre groseille), le noyau Linux, la libc, les différents services utilisés par défaut, bash et toutes sortes d’autres outils semblables.

 

Outils de développement et de débogage

Dans notre projet, nous avons eu besoin de la console de développement sur lien série et d’un serveur SSH pour faciliter le développement et le débogage. Ce sont des exigences classiques, et sont donc bien intégrés dans l’écosystème de Yocto. La couche meta-raspberrypi sait comment configurer le lien série, et l’ajout de la fonctionnalité se fait en un rien de temps, ainsi que le serveur SSH qui est intégré à Yocto — on peut même choisir facilement entre les implémentations OpenSSH et Dropbear.

Un point sur lequel il est bon d’insister est qu’il ne faut surtout pas que ces outils de débogage sortent en production — tout le modèle de sécurité, entre autres, pourrait en être remis en question. Or, pour pouvoir s’assurer de cela, il vaut mieux raisonner en disant « que dois-je ajouter pour le développement ? » plutôt que « que dois-je retirer pour la mise en production ». Ici, nous avons donc la distribution groos, qui contient l’image de production ; et on utilise les capacités d’empilement de Yocto pour construire une deuxième distribution, groos-dev, basée sur la première, qui ajoute ces outils de développement. Ces deux distributions correspondent aux fichiers conf/distro/groos.conf et, avec une dépendance claire de l’un vers l’autre, conf/distro/groos-dev.conf :
    require conf/distro/groos.conf 
EXTRA_IMAGE_FEATURES ?= "dev-pkgs ssh-server-dropbear debug-tweaks" 
ENABLE_UART ?= "1" 

3 lignes pour la version de développement, et plus qu’à choisir la distribution visée avec DISTRO=groos ou DISTRO=groos-dev dans le répertoire de travail (local.conf) ! Avec Yocto, on passe plus de temps à réfléchir à la façon de faire les choses qu’à écrire les lignes nécessaires pour les mettre en place.

Une question intéressante est s’il faut plutôt faire une machine, une image ou une distro dédiée dev. Car il est techniquement possible de les faire ailleurs, et même ça aurait un sens : l’activation de l’UART sur la carte semble très liée au matériel ; et les IMAGE_FEATURES, elles, très liées à l’image produite. Toutefois, après discussion et réflexion, j’ai plutôt attaché le concept de « machine de développement » à un matériel spécifique dédié au développement, comme une carte d’évaluation ou quelque matériel semblable, et d'un autre côté, une image de développement a des limites car elle est décrite dans une recette comme les autres, ce qui rend beaucoup plus difficile les modifications du comportement des autres recettes. Les paramètres d’une distribution, eux, s’appliquent à toutes les recettes.

 

Configuration du noyau pour la lecture des capteurs de température

Suivant les besoins du projet, la configuration du noyau Linux peut être une étape nécessaire. Ici, hormis les spécificités de la machine elle-même, déjà prises en compte par le BSP de meta-raspberrypi, j’ai désactivé le support des fonctionnalités Bluetooth & Wi-Fi comme modules, afin qu’ils ne soient pas chargés par groseille qui n’a pas ces interfaces-là, et j’ai intégré le support du protocole 1-Wire dans le noyau. Le noyau se charge donc de la communication 1-Wire elle-même, et offre les données à l’application via un fichier dans le sysfs.

Yocto est conçu pour apporter des modifications au noyau Linux, qui est lui-même facilement configurable. Le cœur du travail ici a été de fournir le bon fichier .cfg, définissant les variables classiques CONFIG_* de Linux, et d’indiquer sa présence à Yocto pour qu’il l’intègre tout seul à la chaîne de compilation du noyau. Je l’ai fait en ajoutant le fichier recipes-kernel/linux/linux-raspberrypi_%.bbappend ci-dessous. Comme l’extension l’indique, le fichier ne définit pas une nouvelle recette pour bitbake (c’est le rôle du fichier raspberrypi_%.bb, fourni par meta-raspberrypi), mais “surcharge” cette recette déjà décrite.
    FILESEXTRAPATHS:prepend := "${THISDIR}/files:" 
SRC_URI:append = " file://raspberrypi-1brev2.cfg" 

Développement et déploiement de l'application web

Le développement de l'application web pour afficher la température (appelée gtwa pour Groseille’s Temperature Web Application) a été réalisé en Rust, un langage de programmation mettant en avant la performance, la fiabilité et la productivité. Avec 5 mois d’expérience sur Rust, et l’aide de deux librairies que j’ai apprises à manipuler sur le tas, il m’aura fallu deux heures de développement et une cinquantaine de lignes de code pour écrire le gros de l’application : lecture de la valeur de température, et génération à la volée de la page HTML contenant la valeur de température. Comme quoi, Rust, ça peut être simple et efficace !

Une fois écrite, ce n’est pas terminé : il faut la déployer. L'intégration dans Yocto s’est faite assez rapidement, l’essentiel de la recette étant écrite automatiquement, à l’aide de l’extension cargo-bitbake (cargo est le gestionnaire de paquets de Rust). Le support Rust est intégré à Yocto depuis la version 3.4 Honister. Cette intégration fait que les applications Rust sont intégrées très facilement avec un simple inherit cargo dans la recette. Toutefois, le support officiel fait que, pour une version de Yocto donnée, la version du compilateur Rust est fixée. Pour accéder aux versions plus récentes du compilateur Rust, nous avons choisi d’intégrer la couche meta-rust, historiquement à l’origine des modifications de Yocto, et qui permet d’avoir ces versions récentes de compilateur. La couche meta-rust-bin aurait été utilisable aussi, avec la différence que meta-rust compile le compilateur Rust lui-même, alors que meta-rust-bin récupère les compilateurs de référence fournis par la communauté Rust. Bien que plus rapide, cela sort un peu de la logique de maîtrise totale des logiciels utilisés. Au fond, on a déjà compilé le compilateur C, et ça n’a gêné personne, n’est-ce pas ?

À tout cela, il a fallu rajouter le démarrage automatique de l’application, à l’aide de sysvinit utilisé comme système d’initialisation. Pour cela, l’applicatif doit fournir dans /etc/init.d/ un script permettant de démarrer et d’arrêter le service. Ensuite, la recette décrivant l’application hérite de la classe update-rc.d définie dans Yocto, ce qui permet au système de savoir que ce service est à lancer au démarrage. Ces quelques lignes-là sont donc à placer dans la recette gtwa.bb :
    inherit update-rc.d 
INITSCRIPT_NAME = "gtwa" 
 
do_install:append() { 
    install -Dm 755 ${S}/tools/gtwa.init ${D}/${sysconfdir}/init.d/gtwa 
    sed -i 's,/usr/bin/,${bindir}/,g' ${D}${sysconfdir}/init.d/gtwa 
} 

Retour d’expérience

Le projet offre une excellente intégration tant de plateformes matérielles que de logiciels courants. L’agrégation des différentes pièces de logiciel dans un seul projet spécifique se fait donc très facilement. L’ajout du support d’une plateforme matérielle ou d’un logiciel dédié peut se faire sans liens trop forts avec les autres parties, ce qui permet un partage et une réutilisation facile de ce travail. Le projet Yocto a fourni un gros effort de documentation — avec plus de 800 variables disponibles dans la dernière version Scarthgap, et un bon paquet de concepts à maîtriser, il le fallait bien. Et c’est assez agréable quand on se questionne sur un détail précis, après avoir lu une recette quelque part. Le projet Yocto bénéficie aussi d’une communauté assez large de développeurs et d'utilisateurs.

Yocto offre également une maîtrise totale de la chaîne de production, ce qui est particulièrement utile dans un contexte professionnel où la personnalisation et le contrôle des composants logiciels sont essentiels. La configuration initiale peut certes être complexe, le résultat final permet une intégration et une gestion optimales des systèmes embarqués.

Cependant, ce que n’explique pas la documentation, c’est l’avantage et l’inconvénient de telle ou telle façon de faire. Et une recherche sur internet aidera, mais seulement en partie : c’est une bonne façon d’avoir un avis, mais où l’aimable individu qui nous répond n’a pas en tête les contraintes et le contexte de votre projet. En effet, on peut modifier à peu près n’importe quelle variable à peu près n’importe où, et ça marche… on peut souvent utiliser telle ou telle variable pour le même effet, et ça marche aussi… mais avec des avantages et des inconvénients. Vous mélangez les termes de distribution, image et machine ? La plupart du temps, ça n’est pas bien grave, Yocto comprend… jusqu’au moment où vous changez de machine ou d’image. Vous voulez mettre le code applicatif dans le même dépôt que la recette de compilation « parce que c’est une application dédiée » ? OK ; mais bon courage pour la gestion des versions, et donc la maintenance. Et ce qui aide alors un débutant, c’est de pouvoir échanger de temps en temps avec un expert qui peut suivre le projet, échanger des idées avec lui et l’aiguillera sur ces détails.

Et, ce qui vient aussi perturber un débutant est le temps de recompilation qui, suivant la modification faite, peut être plus ou moins long. En attendant que l’ordinateur travaille, on aurait tendance à aller creuser autre chose. Et c’est très bien… sauf quand, à force de mixer plusieurs modifications pour gagner du temps, la pédale nous revient dans le tibia, et l’ensemble plante — mais laquelle de modifications a fait planter le bazar ? Quand on n’arrive même plus à avoir la ligne série, cela peut s’avérer difficile. Être un peu méticuleux, savoir revenir en arrière et essayer de façon itérative peut s’avérer plus qu’utile.

 

Conclusion

  Le projet Yocto est un outil puissant pour les développeurs cherchant à créer des distributions Linux adaptées à des besoins spécifiques avec un haut degré de personnalisation. Malgré les défis, il offre une flexibilité et un contrôle inégalés sur la configuration et le déploiement des systèmes d’exploitation Linux embarqués. L'expérience partagée montre que, avec du temps et de la patience, Yocto peut transformer un projet simple en une solution robuste et personnalisée pour des environnements spécifiques.

Et, comme depuis le début je vous parle de groseille, mais que vous ne l’avez encore pas vu, voici une photo de lui en plein travail, avec le capteur de température qu’il utilise, et la page web qu’il nous renvoie — simple et fonctionnel. Alors, envie d’en faire autant de vos propres mains ?

 


 

 

Références

 


 

 

 

 

Nos équipes, toujours en quête de savoir, organisent régulièrement des conférences, des ateliers et même des lunchs (parce qu'apprendre en mangeant, c'est quand même plus sympa). Ces moments d'échanges sont enregistrés, retranscrits et confiés à nos IA (merci à ChatZen, notre ChatGPT interne) qui se chargent de les transformer en super articles. Grâce à ce travail d'équipe, nous rendons nos connaissances accessibles et enrichissantes pour tous. Si nos collaborateurs en profitent, autant en faire profiter toute la communauté !  

 

Retour aux articles

C'est à lire...