Arduino est un environnement de développement relativement simple, en particulier lorsqu'on considère uniquement son IDE bon enfant. Mais l'élément clé est aussi et surtout le framework Arduino qui, qu'on l'apprécie ou non, reste une base pour de nombreux projets, tantôt relativement conséquents (comme FastLED, par exemple). Comment utiliser ce framework sans pour autant souffrir de l'utilisation de l'IDE standard relativement limité ? La réponse tient en deux mots : Arduino CLI !
Lorsque je dis « IDE simpliste » en parlant de celui d'Arduino, je pense tout naturellement aux versions 1.8.x, et ce, sans mauvaises arrière-pensées, puisque j'ai parfaitement conscience que c'est un choix délibéré de la part des développeurs que de proposer quelque chose de très accessible. N'étant déjà pas à la base très amateur d'applications graphiques, l'utilisation de l'IDE « officiel » a toujours été contraignante pour moi (sans pour autant être un véritable supplice, on ne parle pas d'Eclipse non plus).
« Et l'IDE 2.0 ? » me demanderez-vous peut-être, ce à quoi je répondrai simplement que je n'ai pas pour habitude de « suid root » la moindre application qui passe, particulièrement téléchargée en version binaire et encore moins quelque chose comme un IDE. Oui, je sais, c'est pour la sandbox Chrome/Chromium puisque l'IDE 2.0 est basé sur Electron IDE qui est écrit en Node.js. Ce à quoi j'ajouterai « raison de plus », puisqu'à ce jeu-là, autant utiliser VSCode/VSCodium et PlatformIO.
Ceci étant posé et dit, embrayons et passons au sujet du jour : Arduino en ligne de commandes. Si, comme moi, vous avez testé ce genre de choses il y a quelques années, avec arduino-mk [1] par exemple, vous n'avez peut-être pas un très glorieux souvenir de vos efforts. Cela était certes fonctionnel, mais de loin pas aussi modulaire et évolutif, en termes de plateformes, que l'IDE officiel. Ainsi, avez-vous fini par considérer cette solution comme une voie sans issue, sachant que certaines plateformes, comme les ESP32, disposent de leur propre environnement (ESP-IDF) pouvant parfaitement intégrer une couche de compatibilité avec le framework Arduino.
1. Arduino CLI
Seulement voilà, sans qu'on vous le dise, pour ne pas dire carrément « dans votre dos », un certain nombre de développements ont été faits et les choses ont drastiquement évolué. Il existe, depuis presque 4 ans, un outil en ligne de commandes, officiel, écrit en Go et parfaitement à même de vous dispenser de l'utilisation de l'IDE graphique : Arduino CLI.
Et lorsque je dis « vous passer », c'est réellement vous en passer totalement puisque cet outil (qui est un simple binaire statique, merci Go) gère non seulement la compilation et le flashage des cartes, mais il fait également office de Boards Managers, exactement de la même façon que l'IDE courant. Et là encore, « exactement » veut dire « avec les mêmes sources JSON que celles qu'on peut ajouter dans les préférences de l'outil graphique ». Il s'agit très précisément de l'équivalent de l'IDE, mais débarrassé de son interface et réécrit en Go.
Et personne ne nous a prévenus ! Pas le moindre faire-part, pas un coup de fil, ni même un petit mot dans la boîte à lettres pour vous annoncer la bonne nouvelle. Je sais, je partage totalement votre désappointement... Même s'il y a eu une vidéo officielle de Massimo Banzi en 2018 [2] (de mauvaise foi ? Moi ? Nooooon).
Dans ce qui va suivre, nous allons emprunter, une fois n'est pas coutume, la voie de moindre facilité, délibérément (et sous GNU/Linux en plus, je sais que ça énerve certains). Mais la quasi-totalité des explications est totalement transposable à n'importe quel système d'exploitation supporté nativement par le projet. En effet, si vous pointez votre navigateur sur https://github.com/arduino/arduino-cli, à la section « Releases », vous trouverez différentes versions binaires à télécharger directement pour votre système : GNU/Linux Intel (32 et 64 bits), GNU/Linux ARM (64 bits, 32 bits v7 et 32 bits v6), macOS (Intel et ARM/Apple Silicon) et, bien entendu, Windows (32 et 64 bits).
En effet, étant donné que le projet est écrit en langage Go et que ce dernier compile statiquement les exécutables, arduino-cli se résume, de base, à un unique fichier. Différents éléments se téléchargent automatiquement en fonction des cartes que vous souhaitez utiliser, comme avec l'IDE Arduino, mais le socle même de l'outil est un unique exécutable que vous téléchargerez depuis GitHub et il ne s'agit pas d'un installeur.
2. Installation et configuration
Si vous me lisez depuis un certain temps, vous connaissez certainement mon aversion pour l'exécution de binaires obtenus du Net. Certains argueraient du fait que la construction locale n'apporte pas réellement de bénéfice en termes de sécurité tant que l'on n’audite pas l'ensemble du code. Ce n'est pas faux, mais propager un binaire intégrant une bombe logique est bien plus facile que d'intégrer cela dans un code source au vu et au su de tous les développeurs. Et c'est généralement une bonne pratique. Nous allons donc partir des sources, ce qui dans le cas d'Arduino CLI est véritablement un jeu d'enfant grâce à Go.
Bien entendu, ceci suppose de disposer d'un compilateur Go, mais, là encore, l'installation est d'une simplicité déconcertante et totalement indépendante du système de gestion de paquets de votre distribution, en plus de ne pas nécessiter un quelconque passage en root. Il vous suffit d'aller sur le site officiel du langage Go [3], de télécharger une archive (en l'occurrence go1.19.1.linux-amd64.tar.gz) et de la décompresser, par exemple, dans votre répertoire personnel, pour obtenir une arborescence ~/go. Ce qui se résume à :
On ajoutera ensuite, tout bonnement, le chemin ~/go/bin dans le PATH via une modification du ~/.bashrc (si Bash est votre shell) et vous aurez alors à votre disposition la dernière version de Go.
On se penchera ensuite sur Arduino CLI en récupérant les sources depuis le dépôt GitHub avec Git :
Et ne restera plus, ensuite, qu'à lancer la construction avec :
Notez toute la beauté de Go qui se charge, comme un grand, de récupérer les dépendances, les installer et compiler le tout sans vous obliger à installer manuellement quoi que ce soit d'autre. On reconnaît là une tendance actuelle partagée également par d'autres langages dits « modernes », comme Rust.
Nous obtenons un énorme exécutable arduino-cli qu'il ne nous est pas nécessaire d'installer, puisque là encore, nous ajouterons, tout simplement, ~/arduino-cli dans le PATH et le tour sera joué.
Ceci fait, et pour améliorer immédiatement le confort d'utilisation, nous pourrons utiliser la commande arduino-cli pour nous générer un profil de complétion automatique pour notre shell. Dans le cas de GNU Bash, ceci se fera avec :
Nous serons alors à même de compléter les commandes avec la touche de tabulation comme nous pouvons le faire avec le reste des commandes du système (si vous avez bash-completion installé, paquet synonyme pour Debian/Ubuntu/Mint). Notez que Bash n'est pas le seul shell supporté, nous avons également Zsh, Fish et même PowerShell pour Windows (et GNU/Linux, parce que oui, vous pouvez avoir PowerShell sous GNU/Linux, allez comprendre...).
Passons ensuite à la réelle configuration puisque, dans l'état, Arduino CLI fonctionne, mais ne sait rien faire. Commençons par initialiser un fichier de configuration par défaut avec :
Celui-ci, ~/.arduino15/arduino-cli.yaml, va alors contenir :
Le format par défaut est YAML, mais d'autres sont supportés (JSON, TOML, YAML, propriété Java, HCL, envfile et INI). Vous pourriez laisser cela en l'état, mais personnellement, j'aime modifier :
- le répertoire par défaut des croquis (un artefact des toutes premières versions d'Arduino) :
- le fait d'exporter automatiquement les binaires (ELF et .bin) dans un sous-répertoire build/ du répertoire du croquis :
- désactiver la télémétrie (on est jamais trop prudent) :
- désactiver les notifications concernant les mises à jour (je préfère le faire de ma propre initiative) :
Tout cela est très secondaire, mais il n'en va pas de même pour la liste des URL du gestionnaire de cartes. Par défaut, Arduino CLI, comme l'IDE Arduino, ne connaît que les cartes officielles et celles des « partenaires ». Pour en ajouter d'autres, il faut spécifier les URL des fichiers JSON pour chaque plateforme ainsi :
J'ai pour habitude de travailler avec des ESP8266, ESP32 et STM32 en environnement Arduino. Les URL sont donc respectivement :
- https://arduino.esp8266.com/stable/package_esp8266com_index.json
- https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json
- https://github.com/stm32duino/BoardManagerFiles/raw/main/package_stmicroelectronics_index.json
Une fois le arduino-cli.yaml modifié, il ne reste plus qu'à télécharger ces fichiers et ré-indexer la liste des cores avec :
Dès lors, il devient possible de lister les cores disponibles :
Puis d'installer ceux de son choix :
Les cores sont les outils, les compilateurs et les bibliothèques spécifiques à une plateforme, regroupés sous forme de packages, placés dans ~/.arduino15/packages. Remarquez que cet emplacement est le même que celui utilisé par l'IDE et qu'Arduino CLI utilisera ceux déjà installés de cette manière. Autre point important, il est possible de désigner une version spécifique devant être installée. Ceci se fait en suffixant la désignation du core avec un @ et la version. Exemple : esp32:esp32@2.0.4 (ceci n'est pas dit par hasard, l'exemple que nous allons voir plus loin ne fonctionne qu'avec la version 2.0.4 et non la 2.0.5).
Pour chaque core, nous avons plusieurs cartes et périphériques supportés, en particulier avec des « familles » très étoffées et diversifiées comme ESP8266 et ESP32. Vous pouvez lister et rechercher dans cet ensemble avec :
Ici, nous venons de chercher les cartes utilisant le microcontrôleur ESP32-C3 (base RISC-V) de chez Espressif, et différentes déclinaisons sont disponibles. Il est également possible de lister les cartes actuellement connectées au système, avec :
Comme vous pouvez le voir, cette détection n'est pas parfaite puisque toutes les cartes ne s'identifient pas clairement. Plusieurs occurrences de la commande « remonte » donc des informations qui ne sont pas les mêmes puisqu'il s'agit plus d'un jeu de devinette que d'une véritable détection dans ce cas précis (mais ce comportement est assez perturbant, je trouve). Ici, une minuscule carte « C3FH4 RGB », à base d'ESP32-C3 et incluant une matrice de 25 LED adressables, est identifiée presque correctement. Mais, comme avec l'IDE standard, c'est à vous de connaître le matériel dont vous disposez et de choisir une plateforme en conséquence (la bonne ou quelque chose de proche et de compatible).
3. Utilisation d'Arduino CLI
Dans la sortie de la commande précédente figure un élément très important de l'utilisation de cet outil : la désignation de la plateforme ou carte à utiliser. La documentation parle de FQBN pour « Fully Qualified Board Name », ou « nom de carte pleinement qualifié » (qui n'est pas sans rappeler les FQDN des domaines Internet), et c'est une désignation que l'on retrouve dans les options de commandes utilisables. Un FQBN est composé de trois parties, séparées par des doubles points : le fabricant, l'architecture et l'identifiant de carte. Dans la sortie précédente, ainsi que dans les résultats de notre recherche, nous voyons, par exemple esp32:esp32:esp32s3box, fabricant/vendeur/marque « esp32 », architecture « esp32 » et carte « esp32s3box ». Idem si nous installons le core arduino:avr (vendeur:architecture), nous trouvons par exemple arduino:avr:leonardo, pour la carte Leonardo d'Arduino sur architecture Atmel/Microchip AVR.
Pour procéder à quelques tests, nous allons nous pencher sur la carte C3FH4 RGB et le code de démonstration qui lui est associé, disponible sur GitHub [4]. Le code en lui-même n'a pas grande importance, mais est constitué de plusieurs exemples d'utilisation de la matrice de LED, dont RGBWstrandtest.ino. Cette carte n'est pas disponible dans la liste, mais nous pouvons, tout simplement, estimer qu'il s'agit du module de développement standard Espressif (ESP32C3 Dev Module).
Pour compiler le croquis, nous nous plaçons donc dans le répertoire en question et utilisons simplement :
La compilation échoue pour une raison évidente, il nous manque la bibliothèque Adafruit NeoPixel sur laquelle repose l'exemple. Quelle coïncidence, ceci va justement nous permettre de voir comment ajouter les bibliothèques à notre configuration. La directive lib de la commande arduino-cli nous permet de gérer les bibliothèques et nous fournie les mêmes fonctionnalités que celles de l'IDE. Nous commençons donc par rechercher ce qui nous intéresse :
Notez que la sortie précise le fichier d’en-tête fourni par la bibliothèque, mais que nous ne disposons pas de possibilité de recherche sur cet élément précis (mais rien ne vous empêche de rediriger la sortie vers un grep --color -B 20 -i suivi du nom du fichier). L'élément manquant étant identifié, nous pouvons directement l'installer :
Et récidiver la tentative de compilation qui, cette fois, aboutie :
Notez que --fqbn est la version longue de l'option -b ayant le même effet (voir arduino-cli help pour plus d'info). Une option -v peut être ajoutée pour obtenir une sortie plus verbeuse détaillant les commandes et messages de compilation. Étant donné notre modification du arduino-cli.yaml, nous obtenons le résultat dans build/ :
Cependant, comme avec l'IDE Arduino, la compilation effective se fait dans un sous-répertoire de /tmp, build/ n'est pas un élément indispensable, mais une simple facilité pour éventuellement analyser le binaire (ELF). Nous pouvons immédiatement enchaîner sur le flashage avec :
Tout ce que nous avons à faire est d'utiliser la directive upload en spécifiant, en plus, le port avec l'option -p. Comme il s'agit d'un ESP32, c'est tout naturellement esptool.py qui entre en œuvre et la carte est programmée comme il se doit.
Notez ici un point important : arduino-cli n'est pas make et l'utilisation de upload n'implique pas la compilation via compile. Nous pouvons cependant combiner les deux opérations, via compile, en utilisant l'option -u (comme upload) et en spécifiant le port à utiliser avec -p. Ainsi, la compilation embraye directement sur la programmation de la flash. Remarquez également que la compilation ne semble pas incrémentale, plusieurs commandes compile provoqueront les mêmes opérations, que le binaire ou les fichiers objets existent déjà ou non. Ceci est davantage un problème du core ESP32 que d'Arduino CLI (l'IDE a le même comportement avec les ESP32).
À ce stade, nous avons peu ou prou une version équivalente à l'IDE en ligne de commandes, mais quelques autres points de correspondance doivent être détaillés. Avec l'IDE, en particulier sur les plateformes Espressif, nous pouvons ajuster la configuration de la carte et préciser la taille de la flash, la présence de PSRAM, la fréquence du CPU, le type de partitionnement de la flash ou encore le niveau de verbosité des messages.
Ce type d'options est également parfaitement configurable avec Arduino CLI. Vous pouvez en consulter la liste avec la commande arduino-cli board details et en spécifiant le FQBN via -b comme précédemment. Vous retrouvez, dans sa sortie, absolument toutes les informations sur la carte, des versions du support aux outils nécessaires, en passant par les différents paramètres réglables (ceux actifs étant affichés en vert). C'est lors de la compilation que vous pouvez spécifier ces éléments. Repérer celui ou ceux que vous désirez ajuster, comme FlashSize et/ou DebugLevel, puis utiliser --board-options pour renseigner ces « propriétés » :
Une autre façon de procéder consiste à inclure ces éléments dans le FQBN, ce qui donne : arduino-cli compile -b esp32:esp32:esp32c3:FlashSize=16M,DebugLevel=info. Notez que dans au moins une version d'Arduino CLI, et pour la plateforme ESP32-C3, l'option --board-options ne fonctionnait pas et n'avait aucun effet, alors que esp32:esp32:esp32c3:CDCOnBoot=cdc faisait le travail parfaitement. Cette option active l'interface USB/CDC de l'ESP32-C3 dès le boot et permet de se servir de Serial de façon habituelle (le « vrai » port devenant Serial0), plutôt que d'utiliser USBSerial).
Je pense qu'à ce stade, nous avons l'équivalent de l'IDE en ligne de commande. Quoi ? Le moniteur ? Oh, c'est arduino-cli monitor, mais très franchement, autant utiliser quelque chose de moins basique, comme GNU Screen, par exemple.
4. Simplifions-nous la vie
Savez-vous ce qui me pose le plus problème avec l'IDE Arduino, en particulier face au duo VSCode+PlatformIO que je ne porte pas spécialement dans mon cœur vu sa lourdeur ? Le fait de systématiquement devoir me rappeler quel croquis est destiné à quelle carte, et basculer l'environnement d'un support à l'autre sans cesse, sachant que cette configuration perdure de session en session. C'est excessivement pénible.
Ainsi, la première chose que je me suis demandée en testant Arduino CLI était : « est-ce qu'il y a un moyen d'associer un code/répertoire à une carte ? ». Et la réponse est « oui ». Ceci passe par l'ajout et l'utilisation d'un fichier sketch.json qui comme son nom l'indique est au format JSON. Seuls trois éléments sont pris en charge (pour l'instant) : le FQBN, le port pour flasher et un nom. Dans notre petit exemple, ceci deviendra :
Si ce fichier est détecté en compagnie des sources de votre projet, il sera automatiquement utilisé, sachant qu'une option spécifiée en ligne de commande aura la prévalence. Ceci signifie donc que, dès maintenant, nous pouvons tout simplement utiliser arduino-cli compile et arduino-cli upload, sans rien n'avoir à spécifier d'autre. Voilà clairement une innovation majeure par rapport à l'IDE !
Dans un ordre d'idée sensiblement différent, mais tout aussi intéressant, imaginons le code suivant :
Nous avons là la définition d'une macro représentant une chaîne de caractères que nous utilisons, plus loin, pour l'envoi sur le port série. Ceci affichera par défaut « coucou monde » mais avec Arduino CLI, nous pouvons très facilement compiler cela avec :
Et, bien entendu, ceci changera le texte affiché lors de l'exécution. L'exemple simpliste ne montre pas l'étendue des possibilités et une application plus démonstrative concernerait, par exemple, la réglette lumineuse à LED hackée en afficheur multicolore dans le numéro 13 [5]. J'ai deux versions de ce montage chez moi, une longue de 168 LED et une plus petite de seulement 72. Avec cette approche et Arduino CLI, je n'ai plus besoin de gérer deux versions ou de changer la valeur de LED_COUNT avant chaque compilation. Je passe simplement le nombre de LED en argument de cette façon et ma vie s'en trouve bien simplifiée. Essayez de faire cela avec l'IDE...
5. Conclusion
Tout n'est pas parfait, loin de là, mais pourrait l'être dans les mois qui viennent. Pour les ESP8266 et ESP32, la mise à jour OTA est, sans l'ombre d'un doute, une fonctionnalité très attrayante. Malheureusement, son support est pour l'instant relativement sommaire avec Arduino CLI et les ESP32. En effet, s'il est possible de flasher un firmware de cette manière, ceci se limite à l'utilisation du port TCP par défaut (3232 pour un ESP32), et ce, sans authentification. Ajoutons également que la résolution mDNS ne fonctionne pas, alors même que arduino-cli board list --format json, listera effectivement le nom d'hôte de la cible :
arduino-cli upload -p esp32arducli.local échouera lamentablement, car l'argument n'est pas reconnu comme valide, alors même qu'en utilisant l'adresse, 192.168.0.169, cela fonctionnera sans problème. Sur ce point précis, et c'est réellement dommage, l'IDE graphique est malheureusement plus fonctionnel, sachant que l'OTA sans authentification, et en utilisant le port TCP par défaut, est de l'inconscience pure en termes de sécurité (ne le faites surtout jamais).
On notera au passage que ceci n'est peut-être qu'un bug ou une fonctionnalité partiellement implémentée puisque dans le cas des ESP8266 Wemos D1 mini, la mise à jour OTA fonctionne et l'outil nous demande de saisir le mot de passe :
Je n'ai pas trouvé non plus de moyen de spécifier l'adresse IP en guise de port dans un sketch.json (IP seule ou précédée de network://). Mais c'est quelque chose qui peut, pour l'instant, être géré à l'aide d'un simple Makefile. Tout comme le flashage avec authentification et un port TCP exotique peut être fait manuellement en utilisant directement l'outil Espressif : python3 ~/.arduino15/packages/esp32/hardware/esp32/2.0.4/tools/espota.py -r -i esp32c3RGB.local -p 3000 -a 654987546 -f build/esp32.esp32.esp32c3/baseOTAeeprom32c3.ino.bin. Ce n'est pas idéal, c'est un bricolage, mais ça fonctionne.
Prenons donc notre mal en patience, car à moins d'être un bon développeur Go (ce qui est loin d'être mon cas), il n'y a pas d'autres choix que d'attendre que quelqu'un règle ces problèmes. Ce qui arrivera tôt ou tard... je l'espère. Et, ce jour-là, Arduino CLI sera alors un vrai « sans faute », rendant l'IDE graphique secondaire, sinon totalement obsolète pour un certain nombre d'entre nous, voire pour tout le monde. En effet, Massimo laisse entendre, dans la vidéo de présentation d'Arduino CLI, que cet outil est destiné à former la base des futurs outils, IDE inclus.
Références
[1] https://github.com/sudar/Arduino-Makefile
[2] https://www.youtube.com/watch?v=3vtIisvxewc
[3] https://go.dev/doc/install