0% ont trouvé ce document utile (0 vote)
396 vues328 pages

Programmer avec MicroPython

Le livre 'Programmer avec MicroPython' de Nicholas H. Tollervey présente MicroPython, une version allégée de Python adaptée aux microcontrôleurs et systèmes embarqués. Il vise à rendre la programmation accessible à tous, en mettant l'accent sur la simplicité et l'efficacité, tout en encourageant l'innovation et la créativité dans le développement de projets. Le livre s'adresse à ceux qui souhaitent apprendre à programmer avec MicroPython, en fournissant des explications et des exemples pratiques sur divers microcontrôleurs.

Transféré par

mathfondame
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
396 vues328 pages

Programmer avec MicroPython

Le livre 'Programmer avec MicroPython' de Nicholas H. Tollervey présente MicroPython, une version allégée de Python adaptée aux microcontrôleurs et systèmes embarqués. Il vise à rendre la programmation accessible à tous, en mettant l'accent sur la simplicité et l'efficacité, tout en encourageant l'innovation et la créativité dans le développement de projets. Le livre s'adresse à ceux qui souhaitent apprendre à programmer avec MicroPython, en fournissant des explications et des exemples pratiques sur divers microcontrôleurs.

Transféré par

mathfondame
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Programmer avec

MicroPython

Programmation Python de systèmes embarqués à


microcontrôleurs

Nicholas H. Tollervey
Programmer avec MicroPython

Traduction française publiée et vendue avec l’autorisation de O’Reilly


Media, Inc. de Programming with MicroPython
ISBN 9781491972731 © 2018 Nicholas H. Tollervey

© 2018 Éditions First, un département d’Édi8.


12, avenue d’Italie
75013 Paris – France
Tél. : 01 44 16 09 00
Fax : 01 44 16 09 01

Courriel : firstinfo@[Link]
Site Internet : [Link]

ISBN : 978-2-412-03746-1
ISBN numérique : 978-2-412-04336-3
Dépôt légal : mai 2018

Traduction de l’anglais : Olivier Engler


Mise en page : Catherine Kédémos

Cette œuvre est protégée par le droit d’auteur et strictement réservée à


l’usage privé du client. Toute reproduction ou diffusion au profit de tiers, à
titre gratuit ou onéreux, de tout ou partie de cette œuvre est strictement
interdite et constitue une contrefaçon prévue par les articles L 335-2 et
suivants du Code de la propriété intellectuelle. L’éditeur se réserve le droit
de poursuivre toute atteinte à ses droits de propriété intellectuelle devant les
juridictions civiles ou pénales.

Ce livre numérique a été converti initialement au format EPUB par Isako


[Link] à partir de l'édition papier du même ouvrage.
Avant-propos
Vers fin 2012, il m’est venu l’idée de construire un petit ordinateur très peu
gourmand en énergie, mais capable d’exécuter des scripts directement
depuis le niveau du matériel. Je voulais que cela soit très facile à aborder,
quelles que soient les connaissances techniques de chacun dans l’utilisation
d’un équipement manipulant des 0 et des 1 pour contrôler des éléments du
monde réel. Dans mon cas précis, je voulais d’abord me simplifier la vie
pour contrôler les petits robots que je fabriquais à cette époque.
Au départ, je n’avais pas de préférences au niveau du langage de script à
adopter, mais après quelques recherches et en écoutant ce qui semblait être
le langage que les gens auraient le plus apprécié pour coder et appris
facilement, j’ai opté pour le langage Python.
J’avais aussi pensé lancer une campagne de financement participatif sur
Kickstarter, afin de vérifier si les gens seraient séduits par mon idée et de
commencer à fonder une communauté autour de mon projet. La première
campagne Kickstarter qui s’est déroulée fin 2013 a vraiment lancé
MicroPython : elle a rapporté assez pour me permettre de
fabriquer 3000 circuits Pyboard prévus pour exécuter MicroPython.
Presque 2000 personnes ont formé la première communauté, parmi
lesquelles quelques programmeurs très talentueux qui ont donné de leur
temps et leur expertise pour contribuer et améliorer le langage
MicroPython.
Professionnellement, j’exerce dans le milieu de la physique théorique. J’ai
donc une approche assez académique et orientée recherche de la conception
et du développement de MicroPython, au lieu de la simple étude
d’ingénierie habituelle consistant à résoudre un besoin technique. Je pense
que cela a contribué au succès de MicroPython. Au départ, rien ne pouvait
laisser croire que MicroPython allait même simplement fonctionner, que le
langage Python allait pouvoir être compacté au point de pouvoir
fonctionner dans le faible espace mémoire d’un petit microcontrôleur. J’ai
entrepris la création de MicroPython comme un projet de recherche ; j’ai
combiné l’analyse à de nombreuses astuces de programmeur tout en faisant
fi des grands principes de création de logiciel afin de parvenir à faire
fonctionner mon projet.
Ma philosophie d’ensemble pour MicroPython est de le maintenir
minimaliste et utilisable à la fois. Le nom que j’ai choisi le rappelle :
« micro » évoque les microcontrôleurs et les systèmes embarqués, avec un
écho de minimalisme. « Python » est le nom de ce langage très pratique et
facile d’accès pour que le plus grand nombre puisse prendre plaisir à
programmer. D’un côté, pousser trop loin le minimalisme est dangereux
(poussé au paroxysme, le minimalisme est le néant !), mais de l’autre côté,
le fait d’ajouter des fonctions finit par surcharger les maigres ressources
disponibles. Cette recherche d’un équilibre entre minimalisme et
pragmatisme s’applique non seulement à la conception et au développement
de MicroPython, mais à toute l’industrie informatique et même au-delà.
Depuis la première campagne Kickstarter, MicroPython a connu une
croissance phénoménale, et j’ai, avec de nombreux autres membres de la
communauté, investi beaucoup de temps de travail dans MicroPython, au
niveau matériel et logiciel. De nos jours, MicroPython reste fidèle à son
objectif initial : permettre facilement à tout un chacun de fabriquer des
appareils pour contrôler le monde réel. Il est de plus en plus adopté par des
amateurs et des professionnels dans l’éducation et la recherche, comme
dans les projets commerciaux. MicroPython a même vu naître son propre
secteur d’activité industrielle (qui ne va pas manquer de croître !). Je suis
assez bouleversé de voir que ma propre invention a grandi au point
d’intéresser des personnes aussi diverses dans le monde entier, pour des
domaines d’emploi aussi variés.
MicroPython ne cesse de s’améliorer, la communauté des utilisateurs
grandit jour après jour, et le futur semble radieux. J’espère que
MicroPython va continuer à inspirer les gens, à les aider à apprendre et
continuer à servir à construire et contrôler des systèmes, de la machine à
café au satellite. Tous les équipements numériques ont besoin d’être
programmés, et MicroPython constitue un outil efficace et agréable de le
faire.
Nicholas est depuis les débuts un grand défenseur de MicroPython et il a
énormément contribué à son développement, d’abord dans le domaine de la
communauté et de la pédagogie ; il s’est montré indispensable au succès de
la plate-forme BBC micro:bit. Il a une excellente connaissance de
MicroPython, surtout du point de vue de l’utilisateur, ce qui le qualifie
particulièrement pour vous aider à l’apprendre.
Alors, jetez-vous dans ce livre et prenez plaisir à cette fantastique initiation
au monde de MicroPython. Sortez ensuite de votre zone de confort pour
inventer de nouveaux appareils numériques ébouriffants !
— Damien P. George, créateur de MicroPython
Préface
Le langage MicroPython est une réécriture de Python 3 destinée aux
microcontrôleurs et aux systèmes embarqués. Réussir à rendre utilisable un
langage aussi expressif que Python dans un environnement aussi limité que
les systèmes embarqués est un vrai tour de force.
Ce livre s’adresse à tous ceux qui veulent apprendre à programmer pour les
systèmes embarqués avec MicroPython. Je suppose que vous connaissez
déjà un peu le langage Python, sans en être un expert pour autant : vous
devez connaître la syntaxe de Python, savoir définir et appeler une fonction,
et maîtriser les structures de contrôle conditionnel et les boucles. La plupart
des exemples conviennent pour illustrer des cours ; le livre sera donc
profitable aux enseignants comme à ceux qui se forment par eux-mêmes.
J’ai cherché à fournir des explications, des conseils et des idées pour que
vous preniez confiance dans la réalisation de vos propres projets.
Surtout, je veux que ce livre soit agréable à lire et enthousiasmant. Quand
vous l’aurez lu, j’espère que vous allez vous plonger la tête la première
dans vos objets enchantés en MicroPython !
Dans ce livre, nous passerons en revue plusieurs microcontrôleurs qui sont
exploitables en MicroPython. Nous verrons comment développer l’état
d’esprit du développeur de projets embarqués et comment MicroPython
prend le contrôle du matériel pour produire des comportements et des
résultats. Nous terminerons en proposant des pistes pour continuer le
renforcement de vos compétences.
Comme dans tout livre, il m’a fallu faire des choix. Certains trouveront telle
section trop technique, et d’autres la trouveront trop succincte. Je semblerai
trop long sur certains sujets et pas assez sur d’autres. Quels que soient ces
compromis, j’ai voulu écrire quelque chose qui soit facile à lire tout en
fournissant assez d’éléments de contexte et de repères pour que vous
puissiez devenir autonome. En somme, mon objectif ultime est simple :
aider encore plus de gens à adopter cette remarquable technologie qu’est
MicroPython.
Rappelons que MicroPython est un projet encore récent. Embarquer dans
cette aventure à ce stade de sa croissance est excitant : la communauté
grandit jour après jour, de nouveaux outils et techniques apparaissent, et
bien sûr les bogues sont corrigés. La multiplication des conférences, ateliers
et salons dédiés témoigne de l’enthousiasme pour MicroPython et de son
potentiel. Les programmeurs Python apprennent à créer des projets pour
systèmes embarqués, les développeurs de systèmes embarqués découvrent
le plaisir d’utiliser Python, et les enseignants du monde entier considèrent
MicroPython comme une plate-forme prometteuse pour apprendre le
codage aux enfants.
Enfin, du fait que MicroPython est une reformulation du langage Python, il
hérite de la maturité de Python et de sa vaste communauté internationale de
programmeurs déterminés et dynamiques.
Conventions typographiques
• Nom de fichier : nom d’un fichier, d’un répertoire, d’un chemin
d’accès, d’une adresse Web.

• maVar : nom d’une variable ou d’une valeur littérale à saisir.

• [Link]() : nom d’une fonction ou d’une méthode.

• Fichier/Ouvrir : nom d’une commande de menu.


Ce paragraphe est une astuce.

Ce paragraphe apporte une précision au texte qui le précède.

Voici une mise en garde ou un avertissement à ne pas négliger.


Code source des exemples
Tous les exemples sont réunis dans une archive au format ZIP que vous
pouvez télécharger en vous rendant à la page de notre site Web dédiée à ce
livre :

[Link]

La version anglaise de tous les exemples est disponible sur le site de


référence :

[Link]
micropython

Vous pouvez réutiliser les exemples dans vos projets sans devoir nous
demander une autorisation préalable.
CHAPITRE 1
Qu’est-ce que MicroPython ?
MicroPython est une réécriture du langage de programmation Python
prévue spécifiquement pour les microcontrôleurs et les systèmes
électroniques embarqués dans des machines.
Un microcontrôleur est un circuit intégré qui réunit toutes les fonctions
requises pour traiter des données. Un système embarqué est un appareil
informatique qui est intégré dans une machine mécanique ou électrique. En
général, les systèmes embarqués sont basés sur des microcontrôleurs.
Dans ce livre, nous allons découvrir et utiliser le langage MicroPython en
utilisant tour à tour quatre cartes électroniques à microcontrôleur
différentes.
MicroPython est un langage qui fonctionne sur de nombreux appareils différents.
Les quatre cartes qui ont été sélectionnées pour ce livre offrent une bonne idée de
la diversité de l’offre. Si vous disposez d’une carte différente, et qu’elle accepte le
langage MicroPython, les grands principes que vous trouverez dans ce livre
resteront applicables, au prix de légères adaptations du code source des exemples.

Les équipements dont nous allons parler sont assez différents de ce qu’on
appelle un ordinateur. En effet, un ordinateur réunit des composants très
différents : des circuits mémoire, au moins un périphérique de stockage et
une unité de traitement, ces éléments étant physiquement distincts, incarnés
par des circuits intégrés différents. S’y ajoutent des fonctions périphériques
pour la génération de son, les traitements graphiques de la vidéo et les
communications en réseau. Les ordinateurs offrent une polyvalence et une
puissance bien supérieure aux circuits à microcontrôleur présentés dans ce
livre, dont les ressources sont par nature limitées.
Cette précision soulève deux questions importantes :
• Pourquoi vouloir utiliser des appareils si petits et si peu puissants
basés sur un microcontrôleur ?

• Pourquoi au fait utiliser le langage Python ?


Les réponses à ces questions permettent de comprendre pourquoi l’arrivée
du langage MicroPython a tant attiré l’attention.
Pourquoi micro ?
Un ordinateur est un objet merveilleux, presque magique.
De nos jours, vous pouvez dialoguer en vidéo avec une personne située aux
antipodes, alors qu’il y a seulement vingt ans, c’était encore de la pure
science-fiction.
« Toute technologie suffisamment évoluée est indiscernable de la magie. »
— Arthur C. Clarke

Cette citation de l’auteur de science-fiction Arthur Clarke extraite de


Hazards of Prophecy: The Failure of Imagination (Profiles of the Future)
laisse entendre qu’une technologie suffisamment avancée peut être
comparée à de la magie blanche. Ce qu’il a oublié de mentionner, c’est
qu’une technologie suffisamment avancée devient lassante une fois qu’elle
est disponible partout. L’habitude et la facilité d’accès à une technologie
font perdre tout sens d’émerveillement, sauf pour les avancées les plus
remarquables.
Qu’est-ce que je veux dire par cela ?
Je suis suffisamment âgé pour avoir connu un monde sans ordinateurs dans
chaque chaumière. Mes premiers contacts avec l’informatique ont été
magiques. À l’époque, dans les années 1990, j’étais alors étudiant, et je
passais des heures à dialoguer avec des personnes du monde entier par le
biais d’un serveur dans des univers virtuels mais en mode texte.
Ce genre de monde en mode texte s’appelle MOO, et il en reste toujours en
activité.

Voyez [Link]

Pouvoir interagir en temps réel avec des amis si divers était vraiment
époustouflant. J’étais très heureux de pouvoir dialoguer avec d’autres,
même uniquement avec du texte. Puisque je suis d’origine britannique, j’ai
par nature toujours envie de savoir le temps qu’il fait. Pouvoir connaître la
météo de San Francisco, de Tel-Aviv ou de Singapour en temps réel
m’apportait une satisfaction incomparable (et le temps était en général
meilleur que celui de Londres). La magie ne découlait pas seulement de la
possibilité d’accéder à cette information, mais également de la façon dont je
parvenais à y accéder.
En fonction de votre âge, vous allez réagir de l’une des deux manières
suivantes à l’exemple que je viens de donner :

• Vous laissez échapper une larme de nostalgie pour ces jours


merveilleux et enthousiasmants des débuts de l’Internet.

• Cela ne vous fait ni chaud, ni froid ; vous vous demandez même si


j’ai entendu parler des réseaux sociaux.
De nos jours, mes propres enfants ne marquent aucune surprise lorsqu’ils
prennent contact avec leurs grands-parents par vidéo depuis leur téléphone.
Il n’y a plus d’émerveillement, malgré la somme d’ingénierie en jeu.
Comme les générations d’enfants précédentes, mes enfants restent plus
impressionnés et captivés par la magie qu’ils trouvent dans les contes, les
mythes et les légendes.
Pourquoi cela ? Parce que cela capte leur imagination, alors que le fait
d’utiliser des objets du quotidien n’a plus rien de remarquable.
Considérons le monde d’Harry Potter, ce monde peuplé d’objets enchantés,
de forces magiques et d’incantations qui procurent au héros le pouvoir de
transformer son environnement. Le lecteur est plongé dans ce monde parce
qu’il imagine des choses du style « Ce serait formidable si j’avais une cape
d’invisibilité, un balai volant ou si je pouvais lancer un sort pour pouvoir
respirer sous l’eau ! ». Il y a ce sens du merveilleux dans la magie du
monde de l’auteur J.K. Rowling. Il en va de même pour la terre du milieu
de Tolkien, les anciennes légendes grecques, tous les superhéros et les Jedi
dans Star Wars. Se plonger dans un univers magique est très agréable, car
cela aiguise votre capacité d’émerveillement tout en laissant libre cours à
votre imagination. Cela vous donne l’occasion de vous demander « Qu’est-
ce que je ferais si j’étais ce héros doté de pouvoirs magiques ? ».
Quel rapport avec les microcontrôleurs ? Ce sont des dispositifs
informatiques qui poussent le programmeur à se poser le même genre de
questions. Son imagination est libérée, mais comme en littérature et dans la
vie réelle, il reste toujours une tension entre le bon et le mauvais, le vrai et
le faux, le oui et le non.
Comment ? De plus en plus d’objets du quotidien contiennent des
microcontrôleurs, les embarquent, et cela les rend programmables. Un
appareil programmable possède une capacité d’agir et une autonomie à
prendre des décisions dans des situations définies. Ce ne sont plus des
objets inertes, statiques ; ils acquièrent une certaine indépendance et
témoignent de comportements. Un équipement fondé sur un
microcontrôleur reste suffisamment compact pour pouvoir être soudé, vissé,
ou cousu sur un autre objet, ce qui transforme cet objet en un objet
enchanté, car programmable. Plus important encore, toute personne qui
possède le savoir pour lancer une invocation avec du code source peut
influer sur le comportement de cet objet enchanté.
Nous verrons plus loin dans ce livre ce que cela signifie pour un objet que d’être
enchanté, en nous appuyant notamment sur les concepts présentés par le professeur
David Rose du Massachusetts Institute of Technology (MIT).

Comme en littérature, les systèmes embarqués ont ceci de magique qu’ils


attirent et inspirent. Notre monde devient ainsi programmable de la même
façon que les croyances dans des invocations permettent de prendre le
contrôle d’un monde imaginaire. D’une certaine façon, le sens du
merveilleux et la liberté d’imaginer des débuts de l’informatique reviennent
en force. Nous accédons à nouveau à la possibilité de nous demander :
« Qu’est-ce que je ferais si je pouvais programmer ce genre d’appareil ? ».
Comme nous l’avait dit le Grand Seymour Papert, une personne qui
apprend à coder acquiert simultanément la maîtrise d’un représentant les
plus modernes et les plus puissants de la technologie tout en expérimentant
certaines des idées scientifiques les plus fertiles, combinant mathématiques
et création de concepts intellectuels. Il existe de nombreux exemples de
gens assez inspirés pour réaliser des projets passionnants grâce au langage
MicroPython. Et ces exemples recouvrent un très vaste domaine applicatif :
robotique, mesure du monde extérieur, génération de rapports, contrôle de
satellites, instruments de musique novateurs, installations et performances
artistiques, comptage des crevettes dans l’Atlantique Sud, sans compter un
bon nombre de jeux vidéo, et ce n’est qu’une sélection.
Les objets qui sont dotés d’un microcontrôleur embarqué, éventuellement
en réseau, deviennent enchantés de la même façon que les objets des
mondes magiques : il suffit de leur donner des ordres. Comme dans les
histoires de magie, il est impératif d’apprendre comment réaliser les
incantations pour que les objets enchantés se plient à votre volonté.
Et c’est ici qu’entre en jeu le langage Python.
Pourquoi Python ?
Python est un langage de programmation informatique facile d’accès, très
répandu et très expressif (Figure 1.1). En Python, vous écrivez facilement
du code qui reste simple à lire. Python tient également son succès de sa
vaste communauté, bien organisée et très dynamique.
De nombreuses ressources pédagogiques sont disponibles pour tous les
niveaux de compétences en Python. Si vous n’avez jamais utilisé ce
langage, vous trouverez sur le Web de nombreux tutoriels gratuits, ainsi que
des cours magistraux et des possibilités de rencontres dans des clubs.
Lorsque vous aurez fait vos premiers pas en Python, vous pourrez élargir
vos horizons grâce à son excellente documentation et au support technique
disponible.
Vous ne trouverez pas dans ce livre de tutoriel pour débuter en langage Python.

Sur le Web sont disponibles un grand nombre de ressources aussi bien pour les
programmeurs chevronnés qui veulent découvrir le Python comme nouveau
langage que pour les débutants qui n’ont encore jamais écrit de code. La plupart
des ressources sont gratuites, et un grand nombre de livres et de tutoriels sont
consacrés au langage Python. Si vous êtes un vrai débutant, je vous recommande
Python pour les Nuls chez le même éditeur.
Figure 1.1 : En 2016, le langage Python était le troisième langage le plus utilisé dans le monde,
selon l’institut IEEE (Classement IEEE Spectrum The 2016 Top Programming Languages du 26
juillet 2016). Le créateur de Python, Guido van Rossum, a alors fait remarquer à juste titre qu’il
manquait une catégorie pour les systèmes embarqués, correspondant exactement à MicroPython.

Une remarque capitale : vous devez adopter Python 3, pas Python 2.

Python 2 est la version précédente du langage. Elle reste encore très utilisée pour
des raisons d’héritage et d’inertie. Elle ne sera plus du tout supportée à partir
de 2020.
Les deux versions 2 et 3 de Python sont très proches, et il n’est pas impossible
d’écrire du code source Python qui fonctionne dans les deux versions.
Python 3 continue à bénéficier d’importants efforts de développement et reçoit de
nombreuses améliorations par rapport à Python 2. Il offre enfin des possibilités qui
ne seront jamais disponibles dans Python 2.
MicroPython est une réécriture de Python 3. Dans toute la suite de ce livre, je
suppose que vous avez une connaissance minimale de Python 3, de sa syntaxe et de
ses expressions.

MicroPython est en effet une réécriture complète de Python 3. Si l’on


excepte quelques différences qui sont décrites dans les prochains
paragraphes, tout ce que vous connaissez au sujet du langage Python reste
valable pour MicroPython. La différence la plus évidente entre les deux
langages est que MicroPython a été conçu dès le départ pour pouvoir
fonctionner avec des contraintes matérielles énormes, par exemple dans le
ridicule espace de 16 kilooctets de mémoire vive RAM de la carte BBC
micro:bit.
MicroPython s’exécute en prise directe avec le matériel : il n’y a pas entre
le programme et le matériel un système d’exploitation du type Windows,
macOS ou Linux. Tous les services et sous-programmes qui sont
normalement fournis par un système d’exploitation sont pris en charge
directement par MicroPython. Le langage contrôle directement et
totalement le matériel, ce qui fait qu’en fin de compte, MicroPython est le
système d’exploitation.
Si l’on omet quelques exceptions, toutes les caractéristiques du langage
Python sont disponibles dans le langage MicroPython. Bien sûr, en raison
des ressources limitées dont il doit tenir compte, MicroPython n’offre pas
toutes les fonctions de librairies du langage Python habituel. Vous disposez
d’un ensemble soigneusement sélectionné des librairies standard. En
général, cette sélection est faite pour répondre de façon efficace et agréable
avec les attentes des systèmes embarqués.
Vous trouverez la liste des modules disponibles, y compris certains qui ne font pas
partie de la librairie standard comme urequests à l’adresse suivante :
[Link]

Le choix des fonctions non retenues dans cette sélection a été guidé par une
recherche de tous les aspects qui ne concernent pas la majorité des
programmeurs ou qui se révèlent redondants dans le contexte d’un système
embarqué. Toutes les versions de MicroPython comportent les modules
permettant d’interagir avec le matériel, et notamment avec les broches
d’entrée-sortie GPIO, avec les périphériques et avec les composants que
vous pouvez y connecter.
Du simple fait de que MicroPython est basé sur Python 3, vous avez accès
aux possibilités suivantes :

• le style d’orientation objet de Python, même sans les métaclasses ;

• les types de données telles que les chaînes Unicode, les entiers, les
valeurs à virgule flottante et les structures de données du type liste,
ensemble et dictionnaire ;

• la nature très dynamique des objets en Python ;

• les fonctions en tant qu’objets de première classe ;

• la gestion des exceptions avec try, except, finally et toutes les


classes de gestion d’exception standard ;

• les fonctions de génération de valeurs avec le mot-clé yield, les


expressions de génération et les définitions de listes en
compréhension ;

• dans les plus récentes versions de MicroPython, les deux mots-clés


async et await ;
• une bonne partie des fonctions standard de Python.
Du fait que Python est disponible pour des cartes à microcontrôleurs très
diverses, l’étendue de fonctions et de modules disponibles est variable. Les
cartes qui disposent de capacités particulières, par exemple les
communications Wi-Fi, disposent d’un module pour gérer les fonctions Wi-
Fi.
La carte micro:bit est par exemple livrée avec une version allégée du
module standard random, et la version par défaut pour la carte
ESP8266 propose une version encore plus allégée nommée urandom.
Le préfixe u symbolise ici la lettre grecque mu qui signifie micro dans le système
métrique.

Du fait que la carte SP8266 possède des capacités Wi-Fi, les modules
correspondants json et urequests lui sont disponibles. La version pour
micro:bit ne dispose pas de ces modules parce que la carte n’est pas dotée
de fonctions Wi-Fi. En revanche, les deux cartes disposent du module math
qui est indispensable à tout le monde.
Grâce à la souplesse et à l’efficacité de cette reformulation du langage
Python 3, MicroPython ouvre trois horizons intéressants :
1. La vaste communauté des programmeurs Python gagne ainsi
l’opportunité de s’aventurer dans le développement pour systèmes
embarqués tout en conservant son langage favori.
2. Les développeurs de systèmes embarqués qui ont l’habitude de
travailler en langage C ou d’autres langages habituels ont l’occasion
de découvrir la facilité d’utilisation de Python et la richesse de ses
librairies de fonctions.
3. Le débutant en programmation peut dès ses premiers pas faire autre
chose qu’afficher le sempiternel message de bienvenue « Hello
world! ». Il peut s’attaquer à des techniques de contrôle visuel,
sonore, à piloter des actionneurs et à communiquer en réseau.
Vous créez des sites Web avec Django ? Vous faites de l’analyse de données
avec SciPy ? Vous créez des présentations techniques avec Jupyter ? Avec
MicroPython, votre maîtrise du langage Python devient applicable au
monde de l’embarqué ! Pour quelques euros, vous pouvez acquérir une
carte à microcontrôleur, y connecter quelques périphériques, des capteurs
ou des moteurs, et produire quelque chose d’utile et de captivant.
Moi-même, en tant que programmeur Python sans aucune expérience de
l’embarqué, j’ai pris beaucoup de plaisir à faire mes premiers pas avec
MicroPython. Faire clignoter une diode LED en mode interprété avec REPL
de MicroPython est très agréable. Comme vous allez le voir dans la suite du
livre, vous n’aurez pas beaucoup d’efforts à déployer pour concevoir et
réaliser des projets embarqués intéressants.
D’un autre côté, si vous êtes un développeur expérimenté en systèmes
embarqués, vous pouvez à juste titre vous demander « Qu’est-ce qu’il y a
de mal à utiliser le langage C ? ». Absolument rien. Il est hors de question
pour moi de déclencher une guerre des langages totalement stérile. Dans
certaines situations, le langage C reste sans conteste celui qu’il faut utiliser.
Chaque développeur doit utiliser l’outil le mieux adapté à chaque problème.
Cela dit, MicroPython prend l’avantage sur le C dans certaines situations.
Pour le prototypage, MicroPython offre des fonctions très pratiques qui
vous épargnent la création de code temporaire. Je me souviens d’un
programmeur C qui avait réalisé un prototype fonctionnel en MicroPython
en une après-midi, alors qu’il lui aurait fallu une semaine en C. Dans
certains cas, quelques lignes de MicroPython permettent de faire la même
chose que des centaines de lignes de C, et le code résultant est beaucoup
plus facile à maintenir.
MicroPython est en général suffisamment rapide et gère suffisamment bien
les ressources limitées. Le surcroît de vitesse ou d’efficacité en occupation
mémoire que permet le langage C ne compense pas le supplément de
productivité et de facilité d’emploi du langage Python.
D’ailleurs, lorsque l’outil le plus approprié est le langage C, il reste possible
d’écrire des modules pour MicroPython en langage C puis de les utiliser
dans le code source Python. Vous pouvez même écrire du code assembleur
en ligne pour constituer des fonctions MicroPython.
Pour la plupart des débutants en programmation, le fait de réussir à faire
afficher un message de bienvenue par l’ordinateur est une révélation.
Pourtant, pour la majorité des programmeurs, c’est loin d’être un exploit ;
ils se disent « Et alors ? ».
Personne ne peut leur en vouloir de réagir ainsi.
Quand on baigne dans un monde de photos de chatons, de conférences Web,
de réalité virtuelle et de jeux vidéo multijoueurs aux images hyperréalistes,
l’affichage de « Hello world! » est presque désespérante, encore plus si on
vous a laissé croire que ce résultat constitue la première étape de votre
chemin glorieux vers le monde hollywoodien des uber-programmeurs.
Le fait de pouvoir jouer avec un système embarqué et de créer des objets
qui semblent enchantés aide à surmonter la désillusion que ressentent
souvent les débutants en programmation. Il est tout de même plus
intéressant de programmer des objets qui produisent des sons bizarres à la
demande, illuminent un costume, envoient des messages secrets par réseau
ou animent un robot autonome que d’afficher un message de bienvenue sur
un pauvre écran.
Ce sont ces nouvelles opportunités qui poussent les programmeurs Python à
s’intéresser au monde de l’embarqué, les développeurs du monde embarqué
à rejoindre la communauté Python et les programmeurs débutants à se faire
la main entourés par toute la communauté MicroPython. En somme, tout le
monde y gagne.
La programmation pour l’embarqué est amusante. La programmation
Python est amusante aussi. En combinant les deux, vous vous amusez deux
fois plus !
L’arme secrète de Python est sa communauté de programmeurs. Python est
réputé pour son ouverture, son ergonomie, et surtout la générosité des
membres de sa communauté.
Les programmeurs Python, appelés pythonistes ou pythonautes, sont bien
organisés : ils ont fondé la Python Software Foundation (PSF,
[Link]/psf) qui sert de point de ralliement à toute la communauté. La
fondation PSF a pour objectif de promouvoir, de protéger et de faire
progresser le langage Python. Les droits de propriété intellectuelle
appartiennent à la fondation PSF. Pour parvenir à ses fins, la fondation
soutient le développement du langage, fournit une infrastructure technique
pour la communauté Python au sens large, organise et soutient de
nombreuses conférences internationales dédiées à Python, les PyCons, et
attribue des bourses à des individus et à des organisations qui œuvrent en
cohérence avec les objectifs de la fondation.
Vous trouverez tous les détails au sujet du langage, de la fondation, du
programme de bourses, des prochaines conférences et des groupes
d’utilisateurs dans le monde entier sur le site de référence de Python,
[Link]. Pour tout détail spécifique au langage MicroPython, vous
visiterez le site Web propre de ce langage, [Link].
Mais pourquoi y a-t-il un site Web spécifique à MicroPython au lieu de lui
réserver une section dédiée sur le site officiel principal ? C’est lié à
l’histoire remarquable de ce projet.
Cette histoire mérite d’être contée en détail.
La genèse de MicroPython
Le créateur de MicroPython se nomme Damien George.
Damien est un physicien australien qui est venu s’installer au Royaume-Uni
comme postdoctorant à l’université de Cambridge. Son domaine d’expertise
est indiqué sur sa page personnelle du site Web de l’université :
Mes centres d’intérêt tournent autour de la conception de modèles à dimensions multiples et à
la phénoménologie de ces modèles à l’échelle des interactions électro-faibles, telles qu’elles
peuvent être vérifiées sur le site du grand collisionneur de hadrons LHC (Large Hadron
Collider).

Tout en préparant sa maîtrise, Damien a étudié la robotique et a participé


aux compétitions de la Robot Cup ([Link]), une compétition
internationale dans laquelle les robots doivent jouer au football. Il a conçu
et réalisé un langage de script pour ce genre de robot, portant le nom Hush
([Link]). Grâce à son langage, il est devenu facile de
modifier l’intelligence artificielle des robots en quelques instants.
Damien a réuni en une seule personne curiosité, maîtrise du matériel et
talent énorme. Au cours de l’année 2013, s’y est ajouté suffisamment de
temps libre.
Il s’est à cette époque demandé s’il était possible de créer une version du
langage Python pour les microcontrôleurs. Il était tout à fait qualifié pour
l’aventure, de par son expérience avec les systèmes embarqués des robots,
ainsi qu’un projet antérieur dans lequel il avait écrit à partir de zéro son
propre compilateur C pour systèmes embarqués. Quelques mois plus tard, il
avait réussi à produire un prototype fonctionnel, par pur plaisir.
Pour jauger de l’intérêt que pourrait éveiller son projet naissant, Damien a
lancé une campagne de financement Kickstarter. Sa promesse était la
suivante : s’il parvenait à réunir 15 000 livres (environ 20 000 euros), il
s’engageait à terminer sa réécriture de Python et à fournir à tous ceux qui
avaient donné au moins 20 livres une carte à microcontrôleur appelée
PyBoard. Pour officialiser cette aventure, il a créé avec sa femme Victoriya
la société George Robotics Ltd.
Les contributions de Victoriya au projet MicroPython ont été et sont toujours
essentielles pour son succès et sa croissance. On ne rappelle en général pas assez
les sacrifices et efforts des partenaires non techniciens des développeurs de
logiciels libres. Dans ce cas précis, la campagne Kickstarter de MicroPython a
vraiment été le travail de Damien et de Victoriya, chacun soutenant l’autre avec son
propre domaine de compétences.

Au moment de la clôture de la campagne de financement, le montant récolté


avoisinait les 100 000 livres.
Il était évident que le monde désirait MicroPython.
Non seulement le résultat dépassait largement toutes les attentes, mais
Damien n’a pas manqué de tenir sa promesse : il a fait fabriquer et livrer
environ 2 000 cartes PyBoard aux participants financeurs au cours de
l’année 2014.
Et c’est à partir de cette petite graine initiale qu’a grandi la communauté
MicroPython.
Le code source du langage lui-même est rendu disponible avec une licence
de type open source. Il est hébergé sur le site de référence GitHub
([Link]/micropython). Des personnes du monde entier contribuent à
son évolution, en fournissant des correctifs, en écrivant de la
documentation, en concevant des outils et en produisant des adaptations de
MicroPython pour d’autres cartes à microcontrôleur que celles prévues au
départ.
Et nous ne sommes pas encore au bout de notre histoire.
En 2015, l’agence spatiale européenne ([Link]) a décidé d’apporter un
financement à MicroPython afin de vérifier si ce langage serait adapté aux
applications spatiales. Le sujet principal de la recherche concernait la
configuration dynamique des charges utiles, telles que les satellites, grâce
au langage Python. Ce projet ambitieux a pu profiter des importants efforts
de Damien pour rendre MicroPython plus robuste dans les systèmes
embarqués critiques.
Son travail avait surtout visé le déterminisme de la machine virtuelle et la gestion
de l’espace mémoire.
La situation était gagnante pour tout le monde : MicroPython se libérait de
l’attraction terrestre en attirant l’attention sur lui et la communauté des
utilisateurs profitait d’importantes avancées par rapport à la version initiale
de MicroPython.
C’est à peu près à la même époque que la télévision publique britannique
BBC (British Broadcasting Corporation) a annoncé qu’elle décidait de
distribuer gratuitement un petit ordinateur pédagogique à chaque collégien
de 11 ans du Royaume-Uni, soit environ 1 million de pièces. La fondation
Python, la PSF, a été choisie comme partenaire, parce que la BBC avait
demandé d’utiliser le langage Python parmi les langages préférés pour son
appareil. Un partenaire (non nommé ici) avait été chargé de créer le module
d’exécution Python pour la carte micro:bit. Hélas, en avril 2015, ce
partenaire a déclaré forfait.
Damien est alors venu rattraper la situation. Par pur hasard, un des proches
voisins de Damien à Cambridge était l’un des ingénieurs de la société ARM
qui avait été chargé de concevoir la carte électronique du projet micro:bit.
Ce voisin a donc pu fournir à Damien une carte prototype, ce qui lui a
permis de commencer à réaliser le portage du langage MicroPython vers
cette carte.
Un courriel de Damien qui date de début mai 2015 prouve qu’il ne lui a
fallu qu’un peu plus d’une semaine pour obtenir une version de
MicroPython utilisable (reproduit avec son autorisation) :
J’ai ouvert une session sur mbed, j’ai exporté l’exemple blink pour mkit et j’ai compilé
localement avec une chaîne d’outils locale. À partir de là, j’ai réussi à faire compiler
MicroPython et à le faire exécuter sur le mkit ! J’ai été étonné de l’espace mémoire
disponible : je pouvais activer le support des nombres à virgule flottante, des nombres entiers
à précision arbitraire, de la plupart des fonctions Python et même de quelques modules
intégrés. L’interpréteur REPL fonctionnait par la liaison USB-UART avec historique et aide à
la saisie. Je réussissais même à faire fonctionner l’interruption par Ctrl + C pour sortir d’une
boucle infinie. J’ai alors créé un module « pyb » de base avec les classes pour les diodes LED
et les interrupteurs ainsi qu’une fonction de délai. Il devenait déjà possible d’écrire ce genre
de code :

led = [Link](1)
while True:
[Link]()
[Link](100)
Grâce en partie aux efforts de Damien, plus d’un million de collégiens
peuvent profiter d’un appareil doté du langage MicroPython. Mieux encore,
le projet micro:bit de la BBC ([Link]) a attiré l’attention d’autres
pays, notamment parce que le code source et tous les plans électroniques
ont été rendus disponibles dans l’approche open source
([Link]). Par exemple en Allemagne, un groupe de
développeurs a conçu une carte similaire nommée Calliope ([Link]).
Inspirée de la carte micro:bit, elle peut bien sûr exécuter les programmes
MicroPython.
Le succès appelant le succès, MicroPython est devenu visible au niveau
mondial, et a donc été remarqué par la société Adafruit industries.
Adafruit est l’enfant chéri de madame Limor Fried, surnommée « Lady
Ada ». Adafruit est une société de conception et de commercialisation de
matériels open source qui a acquis une réputation internationale en étant en
mesure de proposer des appareils très intéressants, comme sa gamme de
cartes appelées Feather. Le site Web permet d’acheter toutes sortes de
composants, du simple fil à la gamme complète de capteurs et
d’actionneurs, comme par exemple les rubans de diodes LED NeoPixels
très populaires. Le site regorge de ressources pédagogiques et la société
Adafruit a créé un canal sur YouTube ([Link]/adafruit). Les tutoriels
et ressources pédagogiques pour MicroPython constituent parmi les
meilleures pages écrites à ce sujet. Dès que vous êtes en manque
d’inspiration, n’hésitez pas à parcourir les nombreux projets proposés.
Même si vous ne lisez pas l’anglais, vous pouvez survoler ces textes et vous servir
des schémas et du code source.

Un des circuits conçus par Adafruit qui nous intéresse directement ici est la
dernière version de son Circuit Playground Express
([Link]/products/3333). Cette carte à microcontrôleur fonctionne
avec une version adaptée de MicroPython qui porte le nom CircuitPython.
La carte est dotée d’un grand nombre de capteurs et d’éléments
périphériques : un haut-parleur, un microphone, des broches sensibles au
toucher, une série de diodes LED NeoPixels, un accéléromètre, etc.
S’il y a une fonction qui semble manquer à cette carte, c’est la capacité de
communiquer sans fil. Quand on pense à l’intérêt que suscite de plus en
plus le monde de l’Internet des objets ou IoT (Internet of Things), vous
pourriez croire qu’il manque vraiment une fonction indispensable au
langage MicroPython. Ce n’est absolument pas le cas !
La carte micro:bit est dotée d’un système de communication radio très
simple qui la rend parfaitement adaptée aux projets d’Internet des objets.
De même, la carte Circuit Playground Express sait émettre et recevoir des
messages par infrarouge. Nous découvrirons ces possibilités dans le
Chapitre 10.
L’Internet des objets vise à faire communiquer en réseau les objets du
quotidien. Le principe est de doter ces objets de capteurs, de logiciels et de
capacités de communication pour leur permettre d’échanger des données.
Une ampoule d’éclairage, un grille-pain, un réfrigérateur, un pot de fleurs,
une montre, un ventilateur, un avion, un train, une automobile et même un
évier peuvent embarquer un appareil électronique à microcontrôleur, donc
devenir programmable. Le but ultime est de peupler tout notre
environnement d’appareils informatiques capables de se connecter à
Internet. Fort heureusement, il existe déjà des microcontrôleurs très bon
marché, et notamment les ESP8266 et ESP32, parfaitement adaptés à
l’Internet des objets. Et devinez quoi ? Tous deux fonctionnent avec
MicroPython !
La perspective des objets connectés est en même temps angoissante et
excitante.
Elle est angoissante car elle permet à des entreprises dont vous ne
connaissez rien de prendre le contrôle de vos appareils pour surveiller et
récolter des informations à propos des aspects les plus intimes de votre
existence.
Mais elle est simultanément excitante, car ceux d’entre nous qui apprennent
à programmer peuvent reprendre le contrôle de ces objets, et bien sûr créer
leur propre matériel embarqué pour produire des projets intéressants et
utiles.
MicroPython se place au centre de ce mouvement passionnant consistant à
mettre en réseau des microcontrôleurs. En effet, il est déjà disponible sur au
moins deux microcontrôleurs dotés d’une fonction Wi-Fi, le très répandu
ESP8266 et son remplaçant, l’ESP32. L’ESP32 offre également les
communications Bluetooth.
Forts de leur premier succès, Damien et Victoriya ont lancé une seconde
campagne de financement Kickstarter en 2016. L’objectif consistait à
réunir 6 000 livres pour aider au financement de l’adaptation du langage
vers le circuit ESP8266. Ils ont été rejoints dans cette aventure par Paul
Sokolovsky, un des piliers de MicroPython, ayant contribué au langage
depuis le bouclage de la première campagne de financement Kickstarter.
Après avoir recueilli presque 30 000 livres, il était devenu évident à Damien
et Victoriya qu’ils tenaient un autre succès, et le développement a été lancé
immédiatement. Leurs efforts et le soutien des financeurs a permis de faire
fonctionner MicroPython sur cet appareil. Il est devenu possible d’écrire du
code MicroPython pour réaliser des projets embarqués communicants via
Internet en utilisant une carte coûtant moins de cinq dollars. Cette carte
réunit le microcontrôleur ESP8266 et un connecteur d’entrée-sortie
permettant d’ajouter des périphériques.
Le développement du successeur ESP32 est en cours, et il est devenu
utilisable, mais armez-vous de patience si vous voulez vous en procurer un
exemplaire.
Lancez-vous !
Ce livre vous propose de découvrir le langage MicroPython avec les quatre
appareils que nous venons de mentionner. Cela illustre bien la variété des
équipements sur lesquels vous pouvez faire fonctionner MicroPython. Vous
n’aurez aucun mal à recycler les compétences que vous allez acquérir vers
d’autres appareils supportant ce langage.
Ces différentes cartes sont toutes proposées à des prix très raisonnables, et
ce qui les distingue les unes des autres montre la grande souplesse de la
plate-forme MicroPython et la grande diversité de domaines d’application
de l’écosystème MicroPython. Des adaptations de MicroPython vers de
nouveaux appareils apparaissent régulièrement, il est tout à fait possible que
la carte que vous allez utiliser n’existe pas encore à l’heure où nous
écrivons ces lignes. Dans tous les cas, les principes, les techniques et les
compétences qui sont fournies dans ce livre seront faciles à réutiliser vers
les nouveaux appareils.
La suite du livre est organisée en plusieurs sections :

• Les quatre prochains chapitres constituent une introduction à chacune


des quatre cartes qui vont illustrer la suite du livre : la carte PyBoard
avec laquelle tout a commencé, la carte BBC micro:bit, la carte
Circuit Playground Express d’Adafruit et la famille de cartes
ESP8266/ESP32. Chaque chapitre commence par présenter la carte et
ses capacités, puis montre comment installer MicroPython sur la carte
et transférer le code de votre programme (flasher), se connecter à
l’interpréteur REPL de Python et enfin comment faire clignoter une
diode LED. Lorsque vous réussissez à faire clignoter la LED, c’est
que tous les prérequis sont satisfaits.
Le verbe anglais to flash désigne l’effacement d’un espace mémoire suivi de sa
réécriture. Lorsque vous « flashez » MicroPython dans un circuit, vous chargez le
code exécutable ou le script dans le circuit mémoire. L’expression « flash » vient de
l’époque où l’on utilisait des circuits mémoire EPROM qu’il fallait soumettre à une
lumière ultraviolette pour en effacer le contenu avant de pouvoir les recharger. De
nos jours, la mémoire flash est celle des clés USB. Elle n’a plus besoin de lumière
ultraviolette.
• Vient ensuite un chapitre plus théorique au sujet du développement
pour systèmes embarqués. Il se penche sur l’impact, le potentiel et les
pièges éventuels de cet univers. Le cadre utilisé pour présenter les
concepts s’inspire de la liste d’attributs de David Rose dont nous
avons parlé dans une note antérieure.

• Les six chapitres suivants présentent les possibilités du langage


MicroPython avec des exemples pratiques. Nous nous intéressons tour
à tour aux effets visuels, aux moyens d’entrée et aux capteurs, au
connecteur GPIO, à la mise en réseau, aux sons et à la musique et
enfin à la robotique. Tous les exemples ont été conçus afin de pouvoir
être enrichis. Ils doivent pouvoir servir de tremplin pour vos propres
projets.

• Le chapitre suivant se consacre aux particularités de MicroPython. En


effet, écrire du code avec les contraintes des microcontrôleurs oblige à
relever des défis que ne connaissent pas ceux qui utilisent le langage
Python habituel. Nous verrons par exemple comment faire face aux
inévitables erreurs d’espace mémoire. Nous verrons aussi les options
pour améliorer les performances de MicroPython en optimisant
l’exploitation du microcontrôleur.

• En guise de conclusion, nous proposons quelques pistes pour


poursuivre votre aventure.
Ce livre vous propose tout le savoir qu’il vous faut pour vous mettre en
route, vous prendre au jeu, et créer de merveilleuses choses avec
MicroPython.
Et n’oublions pas, écrire en MicroPython est amusant. En avant !
CHAPITRE 2
PyBoard
La carte PyBoard est la première à avoir était conçue et fabriquée
spécialement pour le langage MicroPython (Figure 2.1). Vous pouvez en
acheter une directement sur le site Web de MicroPython ([Link])
ou chez certains revendeurs en France. L’entreprise de Damien et Victoriya
peuvent livrer la carte n’importe où dans le monde.
Figure 2.1 : La carte PyBoard officielle mesure à peu près 4 cm de côté.

La carte se connecte à un PC grâce à son câble micro USB. Deux modes de


communication avec la carte sont possibles : en tant qu’espace de stockage
flash USB ou en tant qu’interpréteur Python en mode série.
Si vous utilisez Windows, il peut être nécessaire d’installer un pilote puis de
configurer des options pour le mode série USB. Les détails appropriés sont fournis
dans le document en anglais que vous trouverez à l’adresse suivante :
[Link]/resources/Micro-Python-Windows-
[Link].

Le plus fréquent problème que subissent ceux qui découvrent MicroPython avec
une telle carte est l’utilisation d’un mauvais câble micro USB. Ces câbles existent
en effet en deux variantes : ceux qui ne transmettent que l’alimentation (ils
suffisent à recharger par exemple un téléphone), et ceux qui transmettent
l’alimentation et les données. C’est ce second type qu’il faut utiliser.

Si vous voyez que la carte est sous tension, car une diode LED s’allume,
mais qu’elle n’est pas détectée en tant que disque USB ou que vous ne
pouvez pas ouvrir une session avec l’interpréteur REPL, c’est sans doute
que vous n’avez pas le bon type de câble.
Cette mise en garde s’applique à tous les types de cartes présentés dans ce
livre.
Une partie de l’espace mémoire flash intégré au microcontrôleur de la carte
PyBoard est dédié à un petit système de fichiers. L’espace de stockage peut
également être agrandi car la carte dispose d’un lecteur pour le format de
carte mémoire SD. Lorsque vous accédez à la carte en tant qu’espace de
stockage flash, vous pouvez transférer des fichiers de et vers la carte grâce à
ce système de fichiers. Si vous transférez vers la carte un script Python
portant exactement le nom [Link], ce script sera exécuté au prochain
démarrage de MicroPython. C’est ce qui permet d’exécuter un script sur la
carte, même lorsqu’elle n’est pas connectée à un PC.
Comme avec le langage Python normal, vous pouvez stocker d’autres
fichiers Python en plus du fichier principal, puis prévoir des directives pour
les importer dans le fichier [Link] pour structurer votre projet en plusieurs
modules.
Une fois que la carte est connectée, vous pouvez utiliser un outil de
communication série pour vous connecter logiquement à la carte afin
d’accéder à l’invite de l’interpréteur REPL de MicroPython. Vous pouvez
alors saisir des commandes Python dans REPL afin de les faire évaluer et
exécuter immédiatement. C’est une excellente technique pour découvrir les
possibilités du langage MicroPython, pour faire des essais et apprendre
comment tout cela fonctionne.
Une particularité intéressante est qu’un script qui a été implanté dans le
système de fichiers et qui est en cours d’exécution devient accessible à
l’interpréteur REPL. Comme dans le langage Python habituel, vous pouvez
interrompre l’exécution par la combinaison Ctrl + C puis avoir accès à tous
les objets qui ont été créés par le script. C’est très intéressant pour la mise
au point. Vous pouvez même basculer dans un mode de collage par la
combinaison Ctrl + E pour copier puis coller des blocs de texte dans
l’interpréteur. Vous utilisez Ctrl + C pour annuler ou Ctrl + D pour lancer
l’évaluation du code qui a été collé puis revenir au mode REPL normal.
Le matériel de la PyBoard
Par plusieurs aspects, la carte PyBoard est la plus puissante des quatre de ce
livre. Elle est dotée d’un microcontrôleur STM32F405RG, un processeur
Cortex M4 à 168 MHz doté de capacités de virgule flottante. Elle dispose
de 1 024 ko d’espace mémoire flash et de 192 ko de mémoire vive RAM.
La carte dispose d’un connecteur micro USB et d’un lecteur de carte micro
SD. Au niveau des capteurs, elle dispose d’un accéléromètre trois axes,
d’une horloge temps réel avec batterie de sauvegarde en option, de deux
interrupteurs (un pour le Reset et l’autre pour l’utilisateur) et de quatre
diodes LED (rouge, verte, jaune et bleue).
Les entrées-sorties pour connecter des périphériques sont réalisées grâce
aux nombreuses broches du connecteur GPIO qui sont réparties sur trois des
quatre côtés de la carte.
Pour son alimentation en énergie, la carte peut se passer du câble micro
USB si vous l’alimentez avec des piles ou une alimentation en courant
continu. Vous connectez le positif de l’alimentation à la broche marquée
VIN et la masse à la broche marquée GND. La tension d’alimentation doit
se situer entre 3,6 V et 10 V.
Faites bien attention au moment de connecter quoi que ce soit à la broche
VIN. La carte ne dispose pas d’une protection en cas d’inversion de
polarité. Vérifiez donc bien que vous n’avez pas effectué le branchement à
l’envers !
Plusieurs périphériques et cartes d’extension sont disponibles pour la carte
PyBoard ; tous sont disponibles sur le site de MicroPython.
Nous verrons plus loin dans le livre comment utiliser une carte d’extension
représentant un écran LCD couleur qui vient s’enficher directement sur la
carte PyBoard. Cet écran offre 160 sur 128 pixels et 16 bits de couleurs
avec rétroéclairage ; c’est un écran tactile de type résistif (Figure 2.2).
Un autre périphérique que nous utiliserons est la carte audio qui vient elle
aussi s’enficher sur la PyBoard. Elle est équipée d’un microphone et d’un
haut-parleur, ce qui permet d’enregistrer et de lire des fichiers sonores
(Figure 2.3).

Figure 2.2 : La carte écran couleur LCD.

Sont également proposés un module Bluetooth, une carte de prototypage


(qui permet de créer des circuits rapidement) et plusieurs types de
servomoteurs pour réaliser des projets mobiles.
Tous les détails concernant le matériel, y compris les schémas, les plans
d’implantation, les fiches techniques des composants de la carte PyBoard et
des cartes d’extension sont disponibles sur le site de MicroPython :
[Link]/en/latest/pyboard/pyboard/hardware.
Figure 2.3 : La carte d’extension audio pour PyBoard.
Configuration de la carte
La carte PyBoard est livrée avec MicroPython déjà implanté. Il est possible
que cela ne soit pas la plus récente version, et il est donc conseillé
d’apprendre à réimplanter le langage. L’opération n’est pas si difficile et
vous garantit de pouvoir jouir des plus récentes mises à jour et correctifs.
Vous commencez par récupérer la dernière version du microgiciel depuis le
site Web de MicroPython ([Link]/download). Vous déconnectez
ensuite tout ce qui peut être connecté à la carte PyBoard. Vous devez relier
la broche marquée DFU à la broche marquée 3,3V. Ces deux broches sont
juste l’une à côté de l’autre. Si vous regardez de très près, vous pouvez
deviner leur légende à l’arrière de la carte. Les deux broches sont mises en
valeur dans la photo de la Figure 2.4.

Figure 2.4 : La broche DFU et la broche 3V3 sur la face avant de la carte PyBoard.

Le moyen le plus efficace et le plus rapide pour réunir ces deux


emplacements consiste à utiliser un strap mâle-mâle (vous en trouverez
chez tous les distributeurs de composants).
Pour réaliser la mise à jour du microgiciel de la carte PyBoard, il vous faut
un outil appelé DFU. Vous avez le choix entre dfu-util et pydfu. Pour le
premier, vous trouverez les instructions d’installation pour Windows,
macOS et Linux sur le site Web de l’outil ([Link]).
Pour pydfu, vous récupérez le script sur le référentiel de MicroPython
([Link]/micropython/micropython/blob/master/tools/[Link]).
Précisons que le script pydfu a besoin des librairies libusb et pyusb. Pour
les deux outils, vous devez avoir ouvert une session de type administrateur.
Connectez maintenant la carte PyBoard à votre ordinateur grâce au câble
USB. Si vous utilisez dfu-util, vous saisissez la commande qui suit (le
préfixe sudo n’est nécessaire que pour un système de type Unix comme
Linux) :

sudo dfu-util --alt 0 -D [Link]

Dans cette ligne, vous devez remplacer le nom de fichier [Link] par
le nom exact du fichier que vous avez téléchargé depuis le site de
MicroPython.
Il est possible qu’un message d’erreur apparaisse si votre système a détecté plus
d’un seul périphérique DFU, comme par exemple la souris Magic Mouse Apple.
Dans ce cas, la solution la plus simple consiste à déconnecter tous les autres
périphériques DFU avant de retenter l’opération.

Si vous avez choisi pydfu pour mettre à jour le microgiciel, vous utilisez la
commande suivante :

sudo python [Link] -u [Link]

Comme dans l’autre exemple, vous devez personnaliser le nom du fichier à


télécharger. Une fois la mise à jour réalisée, vous pouvez déconnecter la
carte PyBoard et enlever le strap qui relie les contacts DFU et 3.3V.
La carte doit ensuite être détectée comme un disque USB flash. Vous devez
voir quatre fichiers :
Dans certaines distributions Linux, il est possible qu’il vous faille procéder
manuellement au montage du périphérique. Quant à Windows, il arrive qu’il tente
de reconfigurer la carte PyBoard comme s’il s’agissait d’un périphérique série.
Dans ce cas, abandonnez le processus et renseignez-vous, dans le petit guide déjà
mentionné, au sujet des soins apportés à des pythons dans le zoo de Redmond.
Vous pourrez alors configurer les paramètres manuellement.
[Link]
Ce script est exécuté dès le démarrage de la carte pour réaliser la
configuration initiale.
[Link]
C’est dans ce script que se trouve votre code. Son exécution est lancée
immédiatement à la fin de celle de [Link].
[Link]
Ce fichier contient quelques informations de base au sujet de la carte
PyBoard.
[Link]
Il s’agit du pilote Windows permettant de configurer le port série USB,
comme décrit dans le petit guide déjà mentionné qui parle de pythons à
Redmond.
Chargez le fichier [Link] dans votre éditeur et remplacez les
commentaires que vous trouvez par les deux lignes suivantes :

import pyb
[Link](4).on()

Le module référencé, pyb, contient les définitions des classes et des


fonctions qui permettent d’exploiter le matériel de la carte PyBoard. Le
petit script ci-dessus se contente d’allumer la diode LED quatre, la bleue.
Vous pouvez enregistrer puis fermer le fichier [Link] puis éjecter ou
démonter le périphérique, comme pour une clé mémoire SD classique. Vous
pouvez alors appuyer sur le bouton Reset qui se trouve juste sous le port
micro USB pour réinitialiser la carte. Vous devriez voir la diode LED bleue
s’allumer.
Toutes nos félicitations ! Vous venez de créer et de faire exécuter votre
premier programme MicroPython sur la carte PyBoard !
Mais nous n’avons fait que la moitié du chemin. Il reste à voir comment
entrer en interaction avec l’interpréteur de la carte PyBoard, REPL. Nous
pourrons ainsi saisir directement des lignes de commande et les faire
évaluer immédiatement.
Si vous utilisez Windows, nous supposons que vous avez mis en place le
pilote USB Windows pour le mode série. Vous pouvez à partir de ce
moment utiliser l’outil putty pour entrer en communication avec la carte
([Link]/putty-latest). Vous utilisez alors le gestionnaire de périphériques
de Windows pour savoir quel port COM gère la carte PyBoard. Dans l’outil
putty, choisissez Session dans le panneau de gauche, cliquez le bouton
radio Serial, indiquez le numéro du port COM que vous avez récupéré dans
la zone de saisie Serial Line puis cliquez le bouton Open.
Sous Linux ou sous macOS, plusieurs commandes permettent d’ouvrir une
session REPL. Personnellement, j’aime utiliser picocom, car il est simple
d’emploi et fonctionne très bien.
La commande picocom n’est pas nécessairement installée sous macOS ou sous
Linux.

Si c’est votre cas, vous avez deux possibilités :


1. Vous pouvez installer la commande avec le gestionnaire de paquetages de votre système,
par exemple brew sous macOS ou apt-get sous Linux.
2. Vous pouvez vous rabattre sur screen en remplacement de picocom, mais cette commande
n’est pas aussi agréable à utiliser. Cela dit, vous n’avez pas besoin de spécifier le débit
avec screen.
Pour sortir de la commande screen, vous utilisez les deux raccourcis Ctrl + A puis
Ctrl + \.
Les autres alternatives à picocom obligent à spécifier la vitesse de la
communication en bauds. Pour communiquer avec l’interpréteur REPL
MicroPython, il faut toujours spécifier la valeur 115200.

Sous macOS, vous ouvrez une fenêtre de terminal puis vous saisissez la
commande suivante :

picocom --baud=115200 /dev/[Link]*


Sous Linux, vous saisissez ceci dans la fenêtre de terminal :

picocom --baud=115200 /dev/ttyACM0

En fonction des autres périphériques détectés par votre système, vous


indiquerez la valeur /dev/ttyACM1 ou un numéro ultérieur. Il vous faudra
peut-être disposer des droits d’accès appropriés pour exploiter les
périphériques de la famille ttyACM. Vous devrez par exemple être membre
d’un des groupes uucp ou dialout. Vous pouvez aussi directement utiliser le
mode superutilisateur avec sudo.
Quel que soit le système ou la méthode de connexion à REPL, vous devez
aboutir à l’affichage des trois chevrons droits habituels dans le monde de la
programmation Python. Vous aurez peut-être besoin de frapper la touche
Entrée, la combinaison Ctrl + C pour faire une interruption ou la
combinaison Ctrl + D pour un redémarrage logiciel, afin de faire apparaître
les trois chevrons de l’invite (au cas où aucun octet n’a pu être lu sur le port
série pendant l’établissement de la connexion).
Vous entrez alors dans le monde de la programmation interactive REPL.
L’invite est la même que celle habituelle sous Python. Si vous connaissez
déjà Python, vous pouvez commencer par essayer les deux fonctions
intégrées dir et help.
Voici un exemple de session d’interpréteur :

>>> print("Hello, World!")


Hello, World!
>>> 1 + 1
2
>>> 7 / 5
1.4
>>> 7 // 5
1
>>> 7 % 5
2
>>> "hello".upper()
'HELLO'
>>> import pyb
>>> [Link](1).on()
>>> [Link](1).off()
>>> while True:
... [Link](1).toggle()
... [Link](500)
...
Dans l’exemple, les trois dernières lignes correspondent à une boucle de
répétition perpétuelle, dont vous sortez par la combinaison Ctrl + C.
L’interpréteur de MicroPython est doté d’un historique de commande et
d’un mécanisme d’aide à la saisie, utilisable avec la touche Tab. Il est
vraiment passionnant de voir à quel point MicroPython permet d’utiliser la
carte.
Dans l’exemple précédent, nous avons utilisé la fonction [Link]() pour
marquer une pause, et c’est ce qui change l’état de la diode LED toutes
les 500 millisecondes.
Selon la carte, la pause est exécutée de façon différente, pour des raisons
historiques.
La fonction standard de Python, [Link](), n’est pas toujours disponible
avec toutes les cartes, mais elle l’est pour la carte PyBoard. Soyez vigilant :
alors que la fonction [Link]() attend une valeur exprimée en secondes,
les autres fonctions de pause comme [Link]() sur la PyBoard ou
[Link]() sur la carte BBC micro:bit attendent une valeur en
millisecondes.
Vérifiez donc toujours l’unité de mesure du paramètre à transmettre à la
fonction !
Félicitations, votre carte PyBoard est maintenant prête à être exploitée.
N’hésitez pas à partir à la découverte du fonctionnement de la carte en
MicroPython. Vous trouverez tous les détails dont vous aurez besoin à
l’adresse suivante : [Link]/.
CHAPITRE 3
BBC micro:bit
La carte BBC micro:bit est un petit système informatique simple mais
puissant, destiné d’abord aux débutants en programmation. Il est facile à
utiliser, très peu coûteux et très compact. La carte a été conçue par la
télévision publique britannique BBC pour promouvoir la créativité
numérique. La BBC s’était donné comme objectif de permettre à n’importe
qui de renforcer ses compétences et sa confiance pour oser réaliser des
choses formidables avec un ordinateur.
Ce n’est pas la première fois que la BBC conçoit un appareil informatique
pour les débutants en programmation. Au cours des années 1980, j’ai moi-
même appris à programmer sur un micro-ordinateur BBC 8 bits
(Figure 3.1). Toutes les écoles du Royaume-Uni en avaient reçu un. Par
chance, mon père était directeur d’école.
Figure 3.1 : Le micro-ordinateur BBC des années 1980.

Mon père est revenu un vendredi soir à la maison avec plusieurs cartons
contenant un écran, l’ordinateur, un tas de fils et des manuels. Il avait
besoin d’apprendre à utiliser l’ordinateur pour son école. Il n’a pas fallu
plus d’une demi-heure à mon frère et moi-même pour lui prendre sa place et
monopoliser la machine (j’avais huit ans à l’époque).
En comparaison des machines actuelles, celle-ci n’était pas particulièrement
puissante, ni même agréable à utiliser : lorsque vous l’allumiez, elle
émettait des sons dans le style blip blip puis se limitait à l’affichage d’un
curseur clignotant. Du haut de mes huit ans, j’étais effrayé à l’idée de saisir
quelque chose au clavier.
Si je me souviens bien, mon premier échange avec l’ordinateur a consisté à
saisir le mot « HELLO », puis à frapper la touche Entrée. Le résultat :
SYNTAX ERROR.
Je n’avais absolument aucune idée de la raison pour laquelle l’ordinateur
pensait que j’avais fait une erreur, mais je me souviens très bien avoir été
incroyablement excité à l’idée de voir qu’un ordinateur avait réagi à
quelque chose que j’avais saisi. Bien sûr, j’avais fait une erreur de syntaxe.
L’ordinateur ne savait pas quoi faire avec mon instruction. J’ai ainsi vite
appris que les ordinateurs ne parlaient pas anglais.
En revanche, cet ordinateur BBC micro était doté d’un langage de
programmation simplifié qui porte le nom BBC Basic. C’était un langage
pas trop difficile à apprendre pour moi, car le nom de la plupart des
instructions qui permettaient de faire faire des choses intéressantes à la
machine était des mots anglais. De plus, toutes les instructions du langage
Basic devaient être saisies sur des lignes numérotées. Vous saviez toujours
où vous en étiez et dans quel ordre les commandes allaient être exécutées.
Voici donc le premier programme Basic que j’ai écrit. Pouvez-vous deviner
ce qu’il faisait ?

10 CLS
20 PRINT "YOU ARE AN IDIOT"
30 GOTO 20

(Pour tout dire, j’ai recopié ce programme depuis un exemple trouvé dans le
manuel, mais je pense que le contenu de la ligne 20 devait être moins
agressif que ma version.)
Vous n’imaginez pas à quel point c’était cool. C’était un peu comme si
j’avais acquis une sorte d’incantation magique qui me permettait de faire
faire à un ordinateur exactement ce que je voulais. Je n’avais plus qu’à
renforcer mes compétences en apprenant les bonnes invocations.
Mais quel est le rapport entre cette aventure d’enfant avec un ancien
ordinateur BBC et cette splendide carte micro:bit et le langage
MicroPython ?
La carte BBC micro:bit est en même temps un appareil et un concept ; ce
concept a été très bien exprimé par le meneur de l’équipe qui avait créé le
micro-ordinateur BBC voici tant d’années :
« Notre objectif est de démocratiser l’informatique. Nous désirons que les gens ne soient pas
contrôlés par les ordinateurs, mais qu’ils les contrôlent. »
— David Allen, éditeur du projet BBC Computer Literacy Project
Je ne serais pas devenu programmeur aujourd’hui si je n’avais pas eu à
l’époque le sentiment profond que j’étais capable de faire faire des choses
intéressantes à un ordinateur.
Avec ce rappel historique et connaissant l’objectif consistant à promouvoir
la créativité numérique, comment la vénérable maison BBC a-t-elle fait
pour concevoir un appareil qui allait enthousiasmer, éduquer et divertir la
génération actuelle des débutants en programmation ?
Le matériel
La carte dont nous parlons prend moins de place qu’une carte bancaire. Sa
surface présente de nombreuses légendes pour les différents composants
directement visibles.
Sur la face avant, nous trouvons deux boutons A et B ainsi qu’une matrice
de cinq sur cinq diodes LED qui constituent un afficheur rudimentaire
(Figure 3.2). Chacune des diodes peut être allumée selon neuf niveaux de
luminosité différents. Toutes les diodes sont rouges.

Figure 3.2 : Face avant de la carte BBC micro:bit.

Le bord inférieur de la carte est utilisé pour les connexions, sous forme d’un
connecteur plaque (edge). Les différents contacts correspondent à des
entrées-sorties à usage général, souvent appelées GPIO (General Purpose
Input Output). C’est sur ces contacts que vous pouvez brancher des
périphériques d’entrée et de sortie.
Le connecteur plaque présente des contacts qui sont appelés broches, même
si ce ne sont que des languettes de cuivre ici. Dans presque tous les autres
modèles de cartes, les entrées-sorties sont incarnées par des broches
métalliques mâles, et non de simples contacts plats. Sur la BBC micro:bit,
cinq contacts sont plus larges et dotés d’un œil dans lequel vous pouvez
brancher directement une pince crocodile pour réaliser une connexion. Les
autres broches sont trop petites pour une connexion directe. Vous utiliserez
pour vous y brancher un connecteur plaque femelle dans lequel vous
insérerez la carte. Ce connecteur pourra généralement être inséré à son tour
dans une plaque d’expérimentation appelée breadboard. Une telle plaque
permet d’effectuer des montages électroniques sans soudure.
La face avant ressemble à un visage : les boutons forment les yeux,
l’afficheur le nez et le connecteur plaque ressemble à des dents. Cette
humanisation de la carte est voulue : cela attire le regard, même lorsqu’elle
n’est pas sous tension.
La face arrière de la carte présente les différents composants qui la
constituent (Figure 3.3).
Figure 3.3 : Face arrière de la carte BBC micro:bit.

Nous retrouvons tous les contacts du connecteur plaque de ce côté (les


légendes indiquent PINS). Les deux petites puces de circuit intégré en bas à
gauche sont la boussole (un circuit XP Freescale MAG3110 qui est un
magnétomètre) et un accéléromètre (un circuit XP Freescale MMA8652).
Grâce à ces deux capteurs, vous pouvez savoir dans quelle direction est
orientée la carte, et quels gestes lui sont appliqués, par exemple en la
tapotant ou la retournant.
Juste au-dessus des deux capteurs, nous trouvons le circuit central, le
microcontrôleur. Il s’agit d’un modèle Nordic nRF51822 ARM Cortex-
M0 sur 32 bits. Il fonctionne à la vitesse de 16 MHz et dispose de 256 ko de
mémoire flash, de 16 ko de mémoire vive RAM et d’un module radio
à 2,4 GHz qui permet des communications Bluetooth à faible énergie BLE
(Bluetooth Low Energy). Vous pouvez deviner l’antenne du système radio
au-dessus du microcontrôleur, sous forme d’une frise de bandes de cuivre
noyées dans le circuit.
Du côté droit de l’antenne, sur le bord supérieur de la carte, nous trouvons
successivement le port micro USB, le même que sur la carte PyBoard. Il
sert à connecter la carte à un ordinateur. Nous trouvons ensuite le bouton de
réinitialisation Reset pour redémarrer la carte. Enfin, dans le coin supérieur
droit, nous trouvons une prise pour brancher une alimentation sur piles.
Sachez dès à présent que les communications Bluetooth sont totalement
ignorées par MicroPython. En effet, les fonctions correspondantes occupent
un espace en mémoire flash en et mémoire vive trop important. En guise de
compensation, MicroPython dispose d’un module nommé radio qui permet
de faire communiquer des cartes par un protocole réseau simplifié.
Tous les schémas du matériel de la carte sont disponibles librement selon
une licence de type open source, à l’adresse suivante : [Link].
Quel est l’intérêt de cette mise à disposition gratuite ?
N’importe qui, vous-même donc, peut utiliser ces schémas pour fabriquer
sa carte. Si vous en avez les compétences et le désir, rien ne vous interdit
d’améliorer la carte pour en faire votre propre produit. Non seulement le
matériel est accessible, mais tous les logiciels du projet sont eux aussi
distribués selon une licence de type open source.
Configuration initiale de la carte
Comme la carte PyBoard, dès que vous branchez la carte BBC micro:bit à
votre PC par le câble USB, elle est détectée comme une clé mémoire USB.
La même connexion permet d’accéder à l’interpréteur REPL de
MicroPython, comme pour la carte PyBoard.
En revanche, le mode d’implantation du code exécutable est différent. Dans
la carte BBC micro:-bit, vous générez un fichier dans un format appelé hex
dans lequel sont réunis le code exécutable pour MicroPython et le code
source. C’est ce fichier au format hex qui est transféré vers la carte par
simple copie vers la clé USB correspondante qui n’est en réalité pas une
vraie clé mémoire USB. Le pilote présente la carte de telle manière que le
système d’exploitation permette d’y transférer des fichiers. Une fois que
votre fichier est transféré, la carte redémarre avec votre programme. La clé
USB semble alors vide.
Sous macOS, il est possible que le système émette un avertissement comme quoi la
clé USB a été mal éjectée. Vous pouvez tout à fait ignorer cet avertissement.

La carte micro:bit est dotée d’un tout petit système de fichiers occupant
environ 20 kilooctets dans sa mémoire flash. Le contenu de ce système de
fichiers est totalement effacé lors de chaque transfert d’un nouveau fichier
hex. En revanche, les fichiers que vous avez transférés sur la carte et ceux
qui ont été générés par votre script MicroPython ne sont pas effacés lors du
redémarrage. Pour lire et écrire des fichiers dans ce système de fichiers
local, vous devez utiliser l’outil dédié qui porte le nom microfs. Nous
verrons un peu plus loin comment s’en servir. Vous pouvez transférer sur la
carte un fichier hex ne contenant que le module d’exécution MicroPython,
sans aucun fichier script Python. Dans ce cas, vous pouvez ensuite
transférer un fichier portant le nom [Link] qui sera détecté par
MicroPython au prochain démarrage puis exécuté, comme sur la carte
PyBoard. Vous pouvez même copier d’autres fichiers servant de modules ;
ils pourront être importés dans le fichier principal comme en langage
Python habituel.
Le point important à ne pas oublier est que le contenu du système de
fichiers est entièrement effacé lorsque vous transférez un nouveau fichier
hex correspondant à un programme.
En ce qui concerne la création du code source de vos programmes, la
solution la plus simple pour la carte micro:bit consiste à utiliser un éditeur
Web.

Python Editor
Afin que le langage Python soit utilisable facilement dans le cadre de la
campagne de distribution officielle de cartes de la BBC, un groupe de
volontaires anglais faisant partie de la Python Software Foundation a réussi
à créer un éditeur fonctionnant dans un navigateur Web. Le code source
correspondant est disponible sur le site de référence GitHub
([Link]/bbcmicrobit/PythonEditor). Vous pouvez utiliser directement
cet éditeur en vous rendant à
l’adresse [Link].
Pour transférer le code exécutable sur la carte micro:bit depuis cet éditeur,
vous devez d’abord le télécharger grâce au gros bouton correspondant dans
la barre de menus. Le résultat est un fichier au format hex qui est stocké
dans le système de fichiers de l’ordinateur, par exemple dans le dossier
Téléchargements. Dans un deuxième temps, vous transférez ce fichier vers
la clé USB correspondant à la carte. En effet, par mesure de sécurité, le
navigateur ne permet pas de transférer directement un fichier vers le
périphérique, ce qui oblige à travailler en deux étapes.
Pendant la création de leur éditeur Web, les bénévoles de la PSF l’ont
montré à de nombreux enseignants et élèves au Royaume-Uni. Les
réactions ont été unanimes : les gens trouvaient pratique la possibilité
d’utiliser l’éditeur sans devoir l’installer, mais l’interface utilisateur leur
semblait trop limitée au point d’annuler cet avantage initial.
L’éditeur Mu
Les utilisateurs voulaient disposer d’un éditeur de texte qui soit simple et
pratique pour un débutant en programmation sans avoir les problèmes de la
version Web. Les volontaires se sont alors mis à la tâche pour concevoir
tout un jeu de modules Python et d’outils permettant d’interagir avec la
carte depuis un micro-ordinateur. Ils se sont ensuite servis de cette série de
modules pour réaliser un éditeur de code qu’ils ont baptisé Mu (Figure 3.4).
Il constitue la solution idéale pour un débutant en programmation.

Figure 3.4 : Vue générale de l’éditeur Mu.

L’éditeur Mu fonctionne sous Windows, macOS, et Linux et notamment sur


le Raspberry Pi. Vous trouverez les instructions pour l’installer et le
télécharger en fonction de votre système d’exploitation sur la page du
projet, [Link].
Dans cet éditeur, vous pouvez transférer directement le programme
exécutable MicroPython sur la carte au moyen du bouton Flash. Mu peut
également directement se connecter à l’interpréteur REPL de la carte car il
détecte automatiquement sa présence. Il offre également une fenêtre pour
des transferts de fichiers de et vers la carte, en liaison avec son petit
système de fichiers.
La philosophie de l’éditeur Mu est minimaliste :

• Rien que l’essentiel. Mu ne réunit que les fonctions indispensables.


L’utilisateur n’est ainsi pas intimidé par une interface trop riche.

• Simplicité avant tout. L’apprentissage de l’éditeur est facile et rapide.


La complexité perturbe les premiers pas du nouveau programmeur.

• Chaque fonction à sa place. Quel que soit le besoin, il n’y a toujours


qu’une seule manière évidente de le réaliser avec l’éditeur Mu.

• Apprendre dans la joie. Il faut prendre plaisir à apprendre. L’éditeur


Mu pousse l’utilisateur à obtenir rapidement un code fonctionnel.
Si, après avoir utilisé l’éditeur Mu pendant quelques heures, vous trouvez
qu’il lui manque telle ou telle fonction, c’est sans doute que vous êtes
devenu suffisamment aguerri pour passer à un vrai éditeur de programmeur.
Vous êtes devenu capable de contrôler les outils qui permettent d’interagir
directement avec la carte micro:bit. Je conseille de n’utiliser Mu que
lorsque vous avez besoin d’un environnement de programmation simplifié,
idéal pour un débutant. Dès qu’il vous manque des fonctions, tournez-vous
vers les outils Python sur lesquels se base Mu pour communiquer avec la
carte, ou utilisez un autre éditeur de code.
Justement, les deux modules utilisés par Mu pour interagir avec la carte se
nomment uflash et ufs. Le premier, uflash
([Link]/en/latest) permet de transférer le code
exécutable MicroPython vers la carte. Le second
([Link]/en/latest) permet d’utiliser le système de
fichiers de la carte. Pour les deux outils, vous devez prononcer le u initial
« micro », comme dans « microflash ». Dans les deux modules ont été
ajoutés des outils sur ligne de commande pour interagir avec la carte et tous
deux sont disponibles sous forme de paquetages Python sur le site de
référence PyPi ([Link]/pypi). Si la version de Python installée
sur votre machine n’est pas trop ancienne, vous pouvez utiliser la
commande pip pour les installer :

$ pip install uflash


$ pip install microfs

L’outil uflash
Si vous utilisez la commande uflash sans aucun paramètre, l’outil va tenter
de détecter une carte micro:bit connectée et y transférer le moteur
d’exécution de MicroPython sans autre code. Pour transférer un script
Python, il suffit d’ajouter le nom du fichier à extension .py comme premier
paramètre de la commande :

$ uflash mon_script.py

Sous l’effet de cette commande, l’outil va fusionner le moteur d’exécution


MicroPython et le code de votre script afin qu’il s’exécute dès le prochain
démarrage de la carte. Si vous ajoutez l’option -w ou --watch, l’outil
bascule en mode surveillance : il va retranscrire et exécuter
automatiquement le fichier dès qu’il va détecter une modification du code
source.
Si vous connectez plusieurs cartes, vous pouvez les préparer en un seul
geste, à condition de fournir les chemins d’accès dans le système de fichiers
correspondant à l’unité de stockage USB de chacune d’elles :

$ uflash mon_script.py
/chemin/vers/MICROBIT1
/chemin/vers/MICROBIT2
Dans cet exemple, nous utilisons des chemins d’accès de style Unix. Sous
Windows, les barres obliques / sont des barres obliques inverses \.
Si vous avez décidé de créer une variante de MicroPython lui-même, vous
pouvez forcer l’outil uflash à utiliser le format hex de cette variante au lieu
de la version standard de MicroPython :

$ uflash -r [Link] mon_script.py

L’option -r peut être remplacée par --runtime=[Link].

L’outil ufs
L’outil ufs peut être comparé à un outil de transfert de fichiers FTP pour
interagir avec le système de fichiers de la carte. Vous pouvez par exemple
saisir la commande ufs ls pour faire afficher la liste des fichiers trouvés
sur la carte. Pour effacer un fichier, vous écrivez ufs rm [Link]. Pour
copier un fichier vers la carte, vous écrivez ufs put chemin/monfic. txt.
Pour lire un fichier depuis la carte vers le répertoire courant, vous écrivez
ufs get chemin/

[Link].

La version de MicroPython pour la carte micro:bit est livrée avec un


module nommé microbit qui sert à entrer en interaction avec la carte.
D’autres modules sont fournis pour réaliser différentes opérations comme la
communication réseau, la génération de son ou encore le contrôle de
périphériques comme les rubans de LED NeoPixels. Le module microbit
équivaut pour la carte micro:bit au module pyb pour la carte PyBoard.

Test de la carte
Pour vérifier que tout fonctionne bien, il suffit de saisir l’exemple qui suit
dans l’éditeur Web ou dans l’éditeur Mu. Vous n’avez ensuite plus qu’à
transférer le programme vers la carte. Quelques secondes plus tard, la carte
a redémarré et la diode LED en plein milieu de l’afficheur doit clignoter.

from microbit import display, sleep

while True:
display.set_pixel(2, 2, 9)
sleep(500)
display.set_pixel(2, 2, 0)
sleep(500)

Rappelons que la fonction micro:bit sleep() attend un paramètre exprimé


en millisecondes.
Si vous avez choisi l’éditeur Mu, vous pouvez accéder à l’interpréteur
REPL directement grâce au bouton correspondant. Si vous n’utilisez pas cet
éditeur, vous devez saisir une ligne de commande proche de celle valable
pour la carte PyBoard. Voici un exemple :

picocom --baud=115200 /dev/ttyACM0

La version de MicroPython pour la carte micro:bit est d’abord destinée aux


débutants, et notamment aux adolescents. C’est la raison pour laquelle vous
pourrez découvrir de nombreux œufs de Pâques cachés dans cette version.
L’interpréteur REPL est un excellent outil pour partir à la chasse aux
fonctions cachées. Commencez par lire la liste des modules disponibles qui
est affichée en réponse à la fonction help().
Toute la documentation de la carte micro:bit ainsi qu’un tutoriel Python
pour les débutants se trouvent dans la page suivante : microbit-
[Link].
CHAPITRE 4
Circuit Playground Express (CPX)
Star Trek est l’un de mes univers de science-fiction de prédilection. Quand
on y réfléchit, une des raisons du succès de cette série est que sa
technologie futuriste est en général au service du bien. Elle favorise le
progrès (la technologie vient en aide aux autres), l’approche est humaine et
ouverte aux autres (la technologie permet aux personnages de vivre, de
travailler et d’échanger entre eux malgré les différences physiques,
physiologiques et culturelles) et la trame consiste en une exploration de
l’univers (les personnages se déplacent en astronefs !).
Une des technologies de Star Trek que je préfère est le tricordeur. C’est
l’appareil qu’utilisent Spock, Bones et d’autres pour mesurer
l’environnement, réaliser des calculs et produire des réactions avec des
lumières clignotantes et des bruits étranges qui sont tout à fait à leur place
dans le monde du XXIIIe siècle.
Je me suis souvent dit qu’il serait vraiment agréable d’avoir le même genre
d’appareil.
Mon rêve s’est concrétisé avec l’apparition du Circuit Playground Express
d’Adafruit. J’imagine avec amusement Spock en train de programmer ce
genre d’appareils en Python.
Le tricordeur est un bon exemple d’un appareil qui semble enchanté, mais
qui n’a rien de magique. La technologie du XXIIIe siècle est tellement
évoluée à nos yeux que nous réagissons comme l’avait prédit Arthur
Clarke : on ne peut distinguer sa technologie de la magie. Circuit
Playground Express constitue l’antidote à ce genre de croyances. Il est dans
le même esprit que le tricordeur, car il réunit un grand nombre de capteurs
et de comportements. Son nom est bien choisi : c’est un terrain de jeu
(playground) qui permet d’apprendre rapidement (Express) comment
fonctionne un système embarqué. Vous pouvez vous aussi partir à
l’exploration de nouveaux mondes, chercher de nouvelles formes de vie et
de nouvelles civilisations, bref, aller là où aucun programmeur Python n’est
encore allé !
La société Adafruit s’est donné pour but de rendre le développement pour
systèmes embarqués agréable et merveilleux. C’est grâce à ses efforts qu’il
est possible à des programmeurs Python de se prendre pour Spock, Bones
ou l’un de ces personnages en uniforme rouge que l’on rencontre dans les
expéditions à la recherche de nouvelles planètes.
Comme les deux cartes des chapitres précédents, vous disposez de deux
moyens pour interagir avec l’appareil : en implantant du code dans le
système de fichiers en mémoire flash ou en utilisant l’interpréteur REPL.
Comme pour les autres circuits, dès que vous connectez la carte à un
ordinateur grâce au câble micro USB, elle est détectée comme s’il s’agissait
d’un périphérique de stockage USB à mémoire flash. Pour accéder à
l’interpréteur REPL, vous procédez exactement comme avec les cartes
PyBoard et BBC micro:bit.
Présentation de la carte CPX
Il existe au moins deux versions de la carte : la première version était basée
sur le microcontrôleur ATmega32u4. Elle ne permet pas d’utiliser
MicroPython. La version suivante est plus puissante car équipée d’un
ATSAMD21G18 ARM Cortex M0. C’est cette version que nous utilisons
dans le livre.
Tout comme le tricordeur de Star Trek, la carte CPX (pour Circuit
Playground Express) regorge de capteurs servant d’entrées et d’actionneurs
servant de sorties. Dans la Figure 4.1, vous pouvez distinguer les légendes à
côté de la plupart des composants. Ce sont soit des symboles, soit des noms.

Figure 4.1 : Vue générale de la carte Circuit Playground Express avec tous ses capteurs.

Si vous tenez la carte en orientant le port micro USB en haut, vous


remarquez d’abord une petite diode LED juste à gauche de ce connecteur.
Tout autour de la carte, nous trouvons 14 contacts, appelés broches, sur
lesquels vous pouvez brancher des pinces crocodiles. Chaque contact
comporte sa légende, et vous pouvez par exemple distinguer en haut la
broche 3,3V et la broche GND pour l’alimentation. Les broches qui ne
concernent pas l’alimentation sont toutes dotées d’une capacité de toucher
capacitif, comme les trois grosses broches 0,1 et 2 de la carte BBC
micro:bit. Sur ces broches, il est possible de détecter le contact d’un doigt.
À six heures (tout en bas), nous trouvons le connecteur pour brancher une
alimentation sur pile entre 3,5 et 6,5 V en courant continu. La carte est
dotée d’un régulateur pour ramener la tension à 3,3 V. Ce connecteur est
notamment adapté à un jeu de trois piles bâton AAA. D’autres options sont
possibles, et notamment des piles lithium-ion ou lithium-polymère.
Un second cercle à l’intérieur de celui des contacts est dédié à 10 diodes
LED de type NeoPixels. Toutes ces diodes sont multicolores (RGB) et
chacune est contrôlable indépendamment au niveau de la luminosité. Vous
disposez de plus de 16 millions de nuances, ce qui permet de créer un
véritable feu d’artifice de lumières ! Ces diodes permettent également
d’informer sur un état : rouge pour du danger, vert pour une situation
correcte, à variation lente pour une situation calme, à clignotement rapide
pour une urgence. Ces diodes NeoPixels sont numérotées de zéro à neuf, la
diode zéro se trouvant en haut à gauche du port USB, à côté de la diode
d’alimentation. Les diodes sont numérotées dans le sens inverse des
aiguilles d’une montre.
Au centre de la carte, nous trouvons trois boutons-poussoirs, A, B et Reset.
Les deux premiers permettent de réaliser des interactions avec l’utilisateur
alors que le troisième sert évidemment à réinitialiser la carte.
Juste au-dessus du connecteur d’alimentation en bas, nous trouvons un
interrupteur inverseur. Il possède un tout petit levier qui peut être amené à
gauche ou à droite. Une fois qu’il est positionné, l’interrupteur reste dans le
même état, à la différence des boutons-poussoirs qui ne restent dans l’état
fermé que tant que vous appuyez. Cet interrupteur est utilisé comme les
deux boutons A et B pour réaliser des interactions avec l’utilisateur, sauf
que la sélection est ici permanente.
Des deux côtés de cet inverseur, nous trouvons un couple de composants
pour les communications infrarouges : à gauche se trouve l’émetteur et à
droite le récepteur. Vous pouvez ainsi faire communiquer la carte avec
d’autres cartes CPX à condition qu’elles soient visibles et pas trop
éloignées. Cette technologie de communication est la même que celle
utilisée dans les télécommandes des téléviseurs.
La carte comporte plusieurs capteurs pour mesurer son environnement.
Juste au-dessus du bouton A, nous trouvons un capteur de lumière
analogique qui permet de mesurer la luminosité ambiante. Au-dessus du
bouton B se trouve une thermistance, c’est-à-dire une résistance dont la
valeur varie en fonction de la température, ce qui permet de la mesurer.
Entre les deux composants infrarouges, juste sous le bouton Reset, nous
trouvons le circuit intégré de l’accéléromètre trois axes. Il permet de
mesurer la force de gravité dans les trois axes X, Y et Z. Ces mesures sont
dynamiques, ce qui permet de détecter les gestes de déplacement et
d’inclinaison. Sous le bouton B, nous trouvons un petit microphone qui
assure la conversion d’un niveau sonore en une tension mesurable.
Tout cela représente un bon nombre de moyens pour connaître son
environnement !
Un autre composant bien pratique se trouve juste sous le bouton A. Ce petit
carré noir est en fait un haut-parleur miniature. En effet, le circuit CPX ne
pourrait pas rivaliser avec un tricordeur s’il ne pouvait émettre des bips, des
blops et des sifflements pour se signaler. Vous pouvez même lui faire jouer
des mélodies assez simples, mais n’espérez pas une qualité sonore
supérieure, car le composant est le même que ceux que l’on glisse dans les
cartes d’anniversaire.
Enfin, nous allons oublier le composant essentiel : le microcontrôleur
ATSAMD21G18 qui se situe juste au-dessus du bouton Reset. Il fonctionne
à une fréquence d’horloge de 48 MHz et dispose de 256 ko de mémoire
flash et de 32 ko de mémoire vive RAM. Juste au-dessus se trouve un
circuit avec 2 Mo d’espace de stockage flash supplémentaire.
Des quatre cartes présentées dans le livre, la carte CPX est clairement la
plus polyvalente et la plus immédiatement exploitable au niveau de ses
entrées et sorties. Comme le suggère son nom, c’est une superbe plate-
forme pour la détection et la réaction à l’environnement, exactement
comme un tricordeur.
Préparation de la carte
Adafruit a choisi d’utiliser une variante du langage MicroPython qu’elle a
nommée CircuitPython.
En développement logiciel, une variante s’appelle une branche (fork). Elle consiste
à faire une copie du code source d’un projet puis à poursuivre le développement de
cette copie devenue indépendante, le code n’étant jamais réintégré au projet initial.

CircuitPython a été créé par Adafruit en tant que logiciel libre. Tous les
contributeurs du monde entier sont invités à aider à l’améliorer.
CircuitPython se maintient à niveau par rapport aux principales nouvelles
versions de MicroPython, sans pour autant suivre chaque progression
mineure de la branche principale de MicroPython. L’amélioration du
langage se poursuit, et toutes les versions sont marquées comme de type
Beta, ce qui signifie que la plupart des interfaces sont stables, mais qu’il
reste quelques bogues. Adafruit produit toute une gamme de cartes capables
d’utiliser le langage MicroPython. Grâce à sa variante CircuitPython,
l’interface de programmation API reste la même parmi tous les circuits
proposés. Autrement dit, si vous apprenez à programmer avec la carte
Circuit Playground Express en CircuitPython, vous pourrez réutiliser vos
compétences pour n’importe quelle autre carte Adafruit fonctionnant avec
CircuitPython.
Lorsque vous achetez votre carte CPX, il est possible que le langage Python
ne soit pas encore installé. Même s’il l’est, quand vous pensez à toutes les
améliorations qui apparaissent sans cesse dans ce langage, vous aurez tout
intérêt à y implanter la plus récente version de CircuitPython pour profiter
des derniers correctifs et des nouvelles fonctions.
Cette mise à jour de la carte CPX est simplifiée grâce à la présence d’un
chargeur d’amorce (bootloader) qui porte le nom UF2. Il suffit de basculer
la carte dans le mode amorceur puis de déposer des fichiers au format
.uf2 sur la carte.
Au moment où j’écris ces lignes, les paquetages de CircuitPython pour
toutes les cartes Adafruit sont rendus disponibles sur le site de référence
([Link]/adafruit/circuitpython/releases). Cherchez sur le site la
plus récente version du fichier UF2 qui correspond à la carte Circuit
Playground Express.
Pour basculer la carte en mode chargeur, vous la reliez à votre ordinateur
puis vous appuyez deux fois sur le bouton Reset. Vous savez que vous êtes
passé en mode chargeur par le clignotement lent et progressif de la petite
diode LED rouge. La guirlande de diodes NeoPixels s’allume en vert et la
carte est alors détectée en tant que périphérique de stockage USB avec le
nom CPLAYBOOT.
Si la guirlande de diodes NeoPixels s’allume en rouge, c’est que le chargeur n’a
pas pu démarrer. Essayez avec un autre câble USB.

Vous pouvez alors copier le fichier .uf2 que vous avez téléchargé vers la
carte puis la redémarrer. Elle doit être détectée cette fois-ci en tant qu’unité
de stockage USB avec le nom CIRCUITPY.
Et c’est tout. En cas de souci ou si vous avez envie de découvrir d’autres
manières de procéder à la mise à jour, vous trouverez des instructions
détaillées sur le site Web d’Adafruit ([Link]/adafruit-uf2-bootloader).
Si vous disposez d’une carte Adafruit qui ne fait pas partie de la famille Express, il
vous faudra utiliser un outil sur ligne de commande pour procéder à la mise à jour
de CircuitPython. Cet outil se nomme « bossac ». Son utilisation est décrite sur le
site Web d’Adafruit.

Une dernière action de configuration, non obligatoire, mais conseillée,


consiste à copier les fichiers des pilotes CircuitPython
([Link]/en/latest/docs/[Link]). Ils
permettent de disposer dès le départ d’une série de librairies de fonctions
CircuitPython pour interagir avec la partie matérielle de la carte. Il y a deux
types de librairies : les librairies fondamentales et les pilotes proprement
dits qui sont basés sur les librairies fondamentales et permettent d’accéder
aux capteurs et aux autres périphériques. Vous pourrez récupérer la plus
récente version du paquetage correspondant sur le site GitHub du projet
([Link]/adafruit/Adafruit_CircuitPython_Bundle/releases). Il suffit
de copier le contenu du fichier archive ZIP vers la carte puis d’importer les
modules dont vous avez besoin comme pour n’importe quelle autre librairie
Python.
Comme dans le cas de la carte PyBoard, dès que vous branchez la carte à
votre ordinateur, vous la voyez sous forme d’un périphérique de stockage
USB. Si vous transférez dans le système de fichiers de la carte un fichier
portant le nom [Link], le code correspondant sera exécuté au prochain
démarrage. Pour vous connecter à l’interpréteur REPL, vous procédez
exactement de la même manière qu’avec la carte PyBoard (en utilisant
l’outil picocom).
CircuitPython permet d’utiliser le nom [Link] à la place de [Link].

Voici un exemple de session d’interpréteur REPL. Cet exemple fait


clignoter la diode LED rouge D13 deux fois par seconde :

>>> from board import D13


>>> import digitalio
>>> import time
>>> led = [Link](D13)
>>> led.switch_to_output()
>>> while True:
... [Link] = not [Link]
... [Link](0.5)
...

Je rappelle que la fonction [Link]() de Python attend un paramètre


exprimé en secondes, pas en millisecondes.

Comme dans toutes les versions de MicroPython, vous sortez de la boucle


perpétuelle de l’exemple par la combinaison de touches Ctrl + C.
Toutes nos félicitations ! Vous avez terminé la préparation de votre carte
Circuit Playground Express. Nous verrons dans les chapitres ultérieurs
comment exploiter toutes ses possibilités. Si vous avez besoin d’accéder à
la documentation de CircuitPython, visitez la page suivante :
[Link].

Si vous vous sentez de taille à le faire, vous pouvez recompiler CircuitPython à


partir du code source, afin de disposer des plus récentes nouveautés.

Le code source de CircuitPython est disponible sur le site GitHub


([Link]/adafruit/circuitpython). Rappelons qu’il s’agit d’une
branche issue du référentiel principal de MicroPython. Vous pouvez cloner
le référentiel de CircuitPython pour commencer. Vous devez avoir installé
sur votre machine le compilateur nommé gcc-arm-none-eabi. Les détails
de l’utilisation du compilateur dépendent du système de compilation, et
dépassent le cadre de ce livre. Normalement, les choses devraient rester
assez simples si vous avez la possibilité d’utiliser un gestionnaire de
paquetages pour installer le compilateur.
À partir du moment où vous disposez de la chaîne de compilation et du
code source, il suffit d’ouvrir une session dans l’interpréteur de votre
système. Basculez dans le répertoire nommé atmel-samd du référentiel que
vous venez de cloner. Vous pouvez alors utiliser l’outil de compilation
make pour reconstruire le microgiciel :

make BOARD=circuitplayground_express

Le paramètre stipule que la cible doit être la carte Circuit Playground


Express. Si la construction réussit, vous obtenez un fichier nommé
[Link] qui se trouve dans le sous-répertoire build-cplay_m0_flash.
Avant de transférer ce fichier, il faut le convertir vers le format UF2 au
moyen d’un outil de Microsoft disponible sur GitHub
([Link]/Microsoft/uf2/blob/master/utils/[Link]). Vous lui
transmettez le chemin d’accès à votre fichier [Link] et vous ajoutez
l’option -o pour décider du nom du fichier de sortie. Voici un exemple
d’utilisation de l’outil sous Linux :
$ ./[Link] [Link] -o
firmware.uf2
Converting to uf2, output size: 410624,
start address: 0x2000
Wrote 410624 bytes to [Link]
dollar

Dans cet exemple, c’est le fichier nommé firmware.uf2 qui doit ensuite être
transféré sur la carte au moyen des instructions fournies plus haut.
CHAPITRE 5
ESP8266 et ESP32
Le phénomène appelé Internet des objets ou IoT (Internet of Things)
concerne tous les équipements embarqués qui sont connectés à Internet tout
en étant intégrés à des objets du quotidien. Cela permet de connaître et de
contrôler l’état des objets. Autrement dit, il y aura des ordinateurs dans tout
ce qui nous entoure. Nous allons être entourés de machines de traitement de
données reliées à Internet, chacune étant identifiable de manière unique.
Et ces objets vont révolutionner le monde réel.
D’une certaine manière, nous quittons le monde des objets qui produisent
des objets ; dorénavant, nous faisons face à des ordinateurs : pour contrôler
l’éclairage public, les grille-pain, l’arrosage des plantes, la gestion des
climatiseurs, pour piloter les avions, les trains et les voitures.
Ces ordinateurs embarqués permettent trois catégories d’opérations :

• Détecter et mesurer des choses : collecter et partager des données au


sujet de l’environnement.

• Contrôler des choses : provoquer des changements dans


l’environnement ou l’appareil lui-même.

• Produire des calculs sur les choses : appliquer des traitements sur les
données et les signaux d’entrée pour rendre le système concerné utile
et autonome.
Comme l’a précisé à juste titre l’expert en cryptographie et sécurité
informatique Bruce Schneier, on peut comparer les capteurs aux yeux et aux
oreilles de l’Internet, les actionneurs et moteurs aux pieds et aux mains, et
l’unité de traitement qu’est le microcontrôleur à une sorte de cerveau.
Il poursuit ainsi :
« C’est la définition classique d’un robot. Nous sommes en train de construire un robot à la
taille de la planète sans même nous en rendre compte. »

L’élément essentiel de cette technologie est un circuit basé sur un


microcontrôleur, comme celui que nous allons découvrir maintenant,
l’ESP8266, et son successeur, l’ESP32.
Le microcontrôleur ESP8266 est peu coûteux (environ un euro pièce) et
dispose du Wi-Fi et de toute la pile de protocoles réseau TCP/IP
(Figure 5.1). Il a été conçu par la société Espressif Systems de Shanghai.
Les capacités intéressantes de ce circuit, combinées à sa compacité et à son
faible coût, l’ont fait remarquer par la communauté des passionnés
dès 2014. Ils ont relevé les manches pour faire fonctionner MicroPython sur
cette puce. Le résultat est qu’il devient facile de découvrir le monde de
l’Internet des objets avec le langage MicroPython. Le coût dérisoire de
l’ESP8266 rend possibles des projets en réseau qui auraient semblé
inaccessibles voici encore peu de temps. Le langage Python rejoint ainsi le
foisonnant mouvement de l’Internet des objets !
Figure 5.1 : Exemple de carte fondée sur l’ESP8266.

Constatant le succès de l’ESP8266, la société Espressif Systems a conçu un


autre microcontrôleur en tant que version améliorée du précédent nommé
ESP32. Il dispose lui aussi du Wi-Fi, mais également du Bluetooth en
double mode : le circuit peut fonctionner en mode normal ou en mode
économie d’énergie (BLE, Bluetooth Low Energy). Ce microprocesseur
possède deux cœurs et dispose d’une fonction d’accélération matérielle
pour la cryptographie, pour exécuter les algorithmes de chiffrement les plus
répandus. Il dispose enfin d’un générateur de nombres aléatoires pour la
sécurité cryptographique. L’adaptation de MicroPython pour le circuit
ESP32 est en cours. De ce fait, MicroPython pour ESP8266 est
actuellement plus stable, mais quelques mois suffiront pour que
l’ESP32 atteigne la même maturité. Les circuits sont suffisamment proches
l’un de l’autre pour qu’au niveau du langage MicroPython, vous ne sentiez
pas de différence : ce que vous écrivez pour l’ESP8266 devrait fonctionner
sans modification sur l’ESP32.
Présentation matérielle
À la différence des autres circuits présentés dans ce livre, les ESP8266 et
ESP32 sont en fait de simples microcontrôleurs montés sur une carte
statique, sans les composants externes, les capteurs et autres diodes LED.
Cela rend la présentation du matériel moins facile, bien que les
caractéristiques générales de ces deux microcontrôleurs restent proches de
celles des microcontrôleurs qui animent les autres cartes.
Le circuit ESP8266 fonctionne à la fréquence d’horloge de 80 MHz et
dispose de 64 ko de mémoire vive pour les programmes et 96 ko de
mémoire vive pour les données. Précisons que la première stocke le code
que le processeur va chercher alors que la mémoire des données sert
évidemment à stocker les données qui sont générées et modifiées. Le circuit
sait exploiter un espace mémoire flash externe, mais l’accès à cette
possibilité va dépendre du modèle exact de carte.
Au niveau Wi-Fi, l’ESP8266 sait gérer les modes b, g et n du protocole sans
fil IEEE 802.11 avec les authentifications WEP ou WPA/WPA2, ainsi qu’en
réseau ouvert. Ce microcontrôleur possède 16 entrées-sorties, dont 13 sont
utilisables dans la majorité des applications.
Son successeur, l’ESP32 est beaucoup plus puissant, puisqu’il dispose d’un
microprocesseur doté de deux cœurs qui fonctionne soit à 160, soit
à 240 MHz selon la configuration. Il dispose de 520 ko de mémoire vive
RAM. Comme son confrère, il peut gérer un espace de mémoire flash
complémentaire, si la configuration l’autorise. Il offre 36 entrées-sortie,
dont 34 sont utilisables en général.
Par rapport à son prédécesseur, l’ESP32 reconnaît en plus les deux modes
Wi-Fi e et i du protocole 802.11, ainsi que les communications Bluetooth,
en version 4.2 classique ou à faible énergie BLE. Au niveau de
l’authentification, il reconnaît les normes WFA, WPA/WPA2 et WAPI. Le
circuit permet un amorçage sécurisé, le cryptage de l’espace mémoire flash
et l’accélération matérielle pour le chiffrement avec les algorithmes AES,
SHA-2, RSA et ECC.
Les cartes construites autour de ces deux circuits intégrés vont en général
ressembler à celle de la Figure 5.1. Les entrées-sorties sont proposées sous
forme de deux rangées parallèles de broches mâles, de type DIL (Dual In-
Line). C’est sur ces broches que vous pouvez brancher des composants
périphériques. En général, vous trouverez sur la carte une ou deux diodes
LED et une paire de boutons, dont un sert à la réinitialisation, Reset.
La fiche technique du microcontrôleur ESP8266 se trouve à l’adresse suivante :

[Link]

La fiche technique du microcontrôleur ESP32 se trouve à l’adresse suivante :

[Link]

Les cartes proposées se rangent en deux catégories principales : les modules


et les cartes de développement.
En général, les modules ne sont pas dotés de moyens pour connecter des
périphériques. De ce fait, il est plutôt conseillé d’acquérir une carte de
développement qui dispose de toutes les connexions permettant de faire des
essais et d’apprendre. Cherchez donc une carte comportant deux rangées de
broches mâles.
Les cartes basées sur l’ESP8266 sont vraiment très abordables. Vous
trouverez des modèles dégriffés en provenance de Chine pour environ un
dollar, mais également des cartes soigneusement conçues et testées comme
celles d’Adafruit, qui vous coûteront environ 5 euros. Les cartes basées sur
l’ESP32 sont plus récentes et ne sont pas encore faciles à trouver, le prix se
situant plutôt autour de 30 euros, mais il va certainement baisser rapidement
au fur et à mesure de la montée en charge de la carte ESP32.
Préparation et configuration

ESP8266
À la différence des trois autres cartes, le langage MicroPython n’est
normalement pas encore installé sur la carte, et vous devez donc le faire
vous-même. Vous verrez, c’est facile.
Vous commencez par récupérer la plus récente version du microgiciel (
firmware) depuis le site de MicroPython
([Link]/download#esp8266). Trois variantes sont disponibles : la
production stable pour les appareils équipés de 1 024 ko, qui est la plus
fréquente, et les productions quotidiennes pour les deux variantes
proposées : 1 024 ko et 512 ko. Vous saurez que vous disposez d’une
version limitée à 512 ko en recevant un message d’erreur en tentant
d’implanter le langage avec la variante à 1 024 ko. Le message sera du style
« Unlikely to work as data goes beyond end of flash » (Suite peu probable
car les données vont au-delà de la fin de la mémoire flash).
Pour réaliser l’écriture (le flashage), vous devez d’abord placer la carte en
mode chargeur d’amorce (bootloader). Le transfert se fait ensuite avec un
outil dédié. Le souci est que la procédure exacte pour basculer la carte dans
ce mode dépend du modèle de carte. Il faut absolument consulter la
documentation du fabricant. Si la carte dispose d’un connecteur USB, d’un
convertisseur USB vers série et si les deux broches DTS et RTS sont
correctement branchées (c’est le cas pour la plupart des cartes de
développement comme la Huzzah d’Adafruit ou la NodeMCU), vous
basculez d’office dans le mode chargeur d’amorce en utilisant la commande
[Link].
Il faut donc installer cet outil [Link] en le récupérant depuis le site de
référence sur GitHub ([Link]/themadinventor/esptool). Vérifiez que
vous installez au moins la version 1.2.1. Vous pouvez aussi utiliser la
commande pip de Python :
$ pip install esptool

Une fois qu’il est installé, vous vous servez de cet outil pour effacer le
contenu de la mémoire flash :

$ [Link] --port PORT erase_flash

Le paramètre PORT doit être remplacé par une référence au port de votre
ordinateur sur lequel est branchée la carte. Sous Linux par exemple, le
paramètre va s’écrire dans le style /dev/ ttyUSB0 ; sous Windows, ce sera
un numéro du style COM4. L’effacement prend un peu de temps, mais l’outil
informe de sa progression.
Vous pouvez ensuite implanter le microgiciel. Dans la ligne de commande
qui suit, vous remplacez PORT par la référence au port réel et le chemin
d’accès par le chemin d’accès qui permet d’atteindre le fichier du
microgiciel que vous venez de télécharger :

[Link] --port PORT --baud 460800


write_flash --flash_size=detect 0 CHEMIN/
[Link]

En cas d’erreur, commencez par réduire la vitesse de transmission du


paramètre --baud pour l’amener à 115 200. Certaines cartes, et notamment
des variantes de la carte NodeMCU, demandent d’ajouter juste après le
paramètre --flash_size un nouveau paramètre, -fm dio.
S’il n’y a pas d’erreur, MicroPython est installé sur la carte. Pour en avoir la
certitude, vous pouvez tenter d’ouvrir une session d’interpréteur REPL, par
exemple avec la commande suivante :

picocom --baud=115200 /dev/ttyUSB0


La session d’interprète REPL suivante fait clignoter une diode LED de la
carte toutes les demi-secondes :

>>> from machine import Pin


>>> import time
>>> led = Pin(2, [Link])
>>> while True:
... [Link](not [Link]())
... [Link](0.5)
...

À la différence des autres cartes, celles basées sur l’ESP8266 ne sont en


général pas détectées en tant que cartes mémoire USB, encore que cela
dépende du modèle exact. Mais rien n’est perdu. Il y a bien un système de
fichiers comme dans toutes les cartes sur lesquelles vous pouvez installer
MicroPython. Vous pouvez donc y transférer un fichier portant le nom
[Link] pour le faire exécuter au prochain démarrage de la carte.
Mais comment faire pour accéder à ce système de fichiers ? Il suffit
d’utiliser l’outil Web nommé WebREPL qui permet de se connecter à la
carte depuis un navigateur en utilisant le Wi-Fi local.
WebREPL combine deux fonctions : la possibilité d’utiliser l’interpréteur
REPL depuis un navigateur Web et celle de transférer et de supprimer des
fichiers dans le système de fichiers de la carte.
Il faut donc d’abord ouvrir la page de l’application WebREPL, sans oublier
de configurer la carte pour qu’elle accepte des connexions de type Web
Socket, avec un mot de passe.
L’application WebREPL est directement accessible sur le site Web de
MicroPython, à l’adresse [Link]/webrepl. Vous pouvez
également l’utiliser localement en récupérant le code source sur le site
GitHub ([Link]/micropython/webrepl).
Il est conseillé de disposer d’un navigateur Web récent. MicroPython
suggère d’utiliser Firefox ou Chrome. En accédant à la page, vous devriez
voir la même chose que dans la Figure 5.2.

Figure 5.2 : Vue générale de l’outil WebREPL pour se connecter à l’ESP8266.

Il reste à définir le mot de passe pour vous connecter à votre carte. Insérez
la carte dans l’ordinateur puis visitez la page de WebREPL. Dès que vous
voyez l’invite de saisie, demandez l’importation du module nommé
webrepl_setup et suivez les instructions qui apparaissent. Le début de
session doit ressembler à ceci :

>>> import webrepl_setup


WebREPL daemon auto-start status: disabled

Would you like to (E)nable or (D)isable it


running on boot?
(Empty line to quit)
> E
To enable WebREPL, you must set password
for it
New password: password
Confirm password: password
Changes will be activated after reboot
Would you like to reboot now? (y/n)
Lors de la première configuration, il vous est ensuite demandé si vous
voulez redémarrer la carte pour faire prendre en compte les modifications.
Une fois ce redémarrage effectué, si vous êtes toujours connecté à REPL,
vous devriez voir apparaître un message dans le style suivant :

WebREPL daemon started on


[Link]

Replacez-vous devant votre ordinateur et faites afficher la fenêtre des


réseaux Wi-Fi détectés. Vous devriez voir un nouveau point d’accès portant
un nom dans le style MicroPython-020436. Le mot de passe initial de ce
réseau est le suivant :

micropythoN

Remarquez bien le N majuscule à la fin. Connectez-vous à ce réseau.


La page de WebREPL ayant déjà été chargée avant de changer de réseau
Wi-Fi, vous devez copier l’adresse URL du démon de WebREPL, celle qui
commence par ws :// dans la zone de saisie tout en haut à gauche de la
fenêtre de WebREPL (revoyez la Figure 5.2).
À droite de cette zone, vous trouvez un bouton intitulé Connect. Cliquez-le
et saisissez le mot de passe, tel que vous l’avez défini dans la procédure de
configuration de webrepl_setup. Vous devriez alors voir apparaître les trois
chevrons <<< qui indiquent que vous êtes face à l’interpréteur Python
REPL.
À partir de ce moment, vous pouvez utiliser les formulaires Web du côté
droit de la fenêtre pour télécharger, récupérer et supprimer les fichiers qui
font partie du système de fichiers.
Vous verrez plus tard qu’il est également possible de configurer la carte
pour qu’elle se connecte à un autre point d’accès sans fil. La carte
mémorise les paramètres correspondants et tente toujours de se reconnecter
au dernier point d’accès utilisé. Une fois qu’elle est connectée, il devient
possible de se connecter à WebREPL en passant par le réseau local, au lieu
de se connecter directement à la carte qui fonctionne en tant que point
d’accès.
Pour tout détail au sujet de la version de MicroPython pour ESP8266,
visitez la page suivante :

[Link]/en/latest/esp8266/.

ESP32
Les cartes basées sur le circuit ESP32 sont relativement récentes et
l’adaptation de MicroPython est encore en cours de finition. De ce fait,
l’implantation du microgiciel est un peu plus complexe. Au final, le résultat
est stable mais attendez-vous à des révisions. C’est pour cette raison que je
ne vais pas utiliser de carte ESP32 dans ce livre. Cela dit, l’interface de
programmation API doit fonctionner de la même façon que celle de
l’ESP8266. Autrement dit, les exemples du livre doivent pouvoir
fonctionner sur l’ESP32 sans modification.
Je vais néanmoins fournir quelques conseils pour les plus courageux d’entre
vous qui veulent surfer à la pointe de la technologie. Ce sont des conseils à
usage limité dans le temps, car la séquence assez complexe pour faire
fonctionner MicroPython sur ce genre de carte va être certainement
beaucoup plus simple sous peu.
Vous voilà prévenu !
Ceux d’entre vous qui ont décidé de continuer à lire cette section réservée
aux grands initiés vont bien s’amuser à lancer une compilation du
microgiciel. Il faut commencer par faire un clone du code du portage de
MicroPython pour l’ESP32 ([Link]/micropython/micropython-esp32).
Les instructions de compilation les plus récentes sont réunies dans le fichier
nommé [Link] qui se trouve dans le sous-répertoire esp32 du
référentiel.
Pour pouvoir reconstruire le microgiciel (firmware), vous devez d’abord
installer un cross-compilateur qui peut produire du code pour le type de
processeur qui équipe l’ESP32, c’est-à-dire un processeur Xtensa. Vous
téléchargez ensuite l’atelier de développement Espressif IDF qui permet à
MicroPython d’exploiter la carte. Vous trouvez toutes les instructions dans
le référentiel correspondant sur GitHub ([Link]/espressif/esp-idf).
Seules les deux premières étapes des instructions d’Espressif sont à réaliser
pour satisfaire les besoins de MicroPython.
Le gisement de code de la société Espressif évolue sans cesse. Vous devez
donc faire attention à bien partir d’une version qui fonctionne avec
MicroPython. Ouvrez le fichier nommé Makefile dans le sous-répertoire
esp32 de MicroPython ; vous devez trouver une ligne qui mentionne
ESPIDF_SUPHASH. Copiez le code de hachage indiqué et utilisez la
commande suivante pour réorienter le référentiel d’Espressif IDF par
rapport à cette version :

$ git checkout <Valeur actuelle de


ESPIDF_SUPHASH>

Si vous ne prenez pas cette précaution et que vous utilisiez une version
d’Espressif IDF qui n’est pas compatible avec MicroPython, cela se soldera
par un message d’avertissement lorsque vous tenterez de produire le code.
La dernière action à réaliser pour satisfaire aux attentes consiste à armer la
variable d’environnement nommée ESPIDF pour qu’elle désigne la racine
du référentiel Espressif IDF. La documentation de MicroPython conseille de
créer un nouveau fichier dans le répertoire esp32 en lui donnant le nom
makefile (ou bien GNUmakefile si le système de fichiers ne distingue pas
les majuscules des minuscules). Ce fichier contiendra les instructions
suivantes :
ESPIDF = path-to-espressif-idf-repository
PORT = /dev/ttyUSB0
FLASH_MODE = qio
FLASH_SIZE = 4MB
#CROSS_COMPILE = xtensa-esp32-elf-

include Makefile

Dans la première ligne, vous remplacez le chemin du référentiel Espressif


IDF par le chemin d’accès approprié. Pour faire référence à votre répertoire
personnel, utilisez plutôt l’écriture $HOME au lieu du signe tilde (~). La
variable PORT doit indiquer le port sur lequel est connectée la carte équipée
de l’ESP32. Sachez qu’il est parfois nécessaire d’indiquer dio pour le
paramètre flash mode au lieu de qio. Si vous n’avez pas ajouté le chemin
d’accès au cross-compilateur Xtensa à vos chemins d’accès automatiques,
vous pouvez montrer comment y accéder au moyen du paramètre
CROSS_COMPILE, en enlevant le signe # dans ce cas.
Une fois ces préparatifs réalisés, il reste deux étapes pour produire le
microgiciel MicroPython. La première consiste à précompiler un certain
nombre de scripts internes au format bytecode en exécutant la commande
suivante dans la racine du référentiel de MicroPython :

$ make -C mpy-cross

La seconde étape demande d’entrer dans le sous-répertoire esp32 pour


lancer la commande make :

$ cd esp32
$ make

Le résultat sera un fichier image du microgiciel stocké dans le sous-


répertoire build. Vérifiez que la carte ESP32 est en mode chargeur
d’amorce (la documentation du fabricant explique comment faire si ce n’est
pas automatique) et vérifiez une fois de plus les paramètres du port et de la
mémoire flash dans le fichier makefile.
Commencez par vider totalement la mémoire flash :

$ make erase

Puis demandez l’implantation de MicroPython avec la commande suivante :

$ make deploy

Vous pouvez ensuite vous connecter à l’interpréteur REPL comme pour les
autres cartes. N’oubliez pas cependant qu’il s’agit d’un nouveau
microcontrôleur et que le portage MicroPython est très récent. Tout ne sera
pas disponible dès le départ.
Nous en avons ainsi terminé avec la présentation et la préparation de
chacune des quatre cartes que nous allons utiliser dans la suite du livre.
Avant de plonger dans la programmation proprement dite, je propose dans
le prochain chapitre de faire une pause pour réfléchir à la nouvelle approche
mentale préconisée pour découvrir et résoudre des problèmes avec des
systèmes embarqués fonctionnant grâce à MicroPython.
CHAPITRE 6
Pensez embarqué !
Ce chapitre vous propose d’adopter un mode de pensée consistant à
concevoir des solutions embarquées pour répondre à des problèmes du
monde réel.
En quoi cette idée est-elle importante ?
Des appareils dotés d’un microcontrôleur, il en existe depuis assez
longtemps, mais ce n’est que récemment qu’ils ont attiré l’attention en
dehors du cercle restreint de leurs passionnés, grâce à la vague de l’Internet
des objets ou IoT (Internet of Things). De plus en plus de gens se montrent
intéressés par ces nouvelles opportunités, en partie sous l’effet de
l’importante couverture qui en a été faite par les médias. Il semblerait que
nous soyons « à l’aube d’un développement révolutionnaire, qui va nous
mener dans un monde dans lequel les innovations seront plus humaines »,
un monde dans lequel « la technologie sera présente dans des milliers
d’objets du quotidien : nos voitures, nos portefeuilles, nos montres, nos
parapluies, et même nos poubelles ».
Les citations ci-dessus sont extraites du livre de David Rose (pour l’instant non
traduit en français) Enchanted Objects. C’est une excellente lecture.

Comme antidote à cet affolement généralisé, nous préconisons une pensée


critique et argumentée. Il est indispensable d’acquérir une vision
d’ensemble pour identifier et évaluer correctement les opportunités afin de
résoudre de vrais problèmes avec des solutions embarquées véritablement
utiles.
Dans sa série de livres Le Guide du voyageur galactique, Douglas Adams a
fort bien décrit ce qui se passe en l’absence de cette approche globale. Il
présente la société Sirius, qui fabrique toutes sortes d’objets du quotidien
contenant des technologies avancées. Voici ce qu’il en dit dans le
volume 4 de sa saga (Salut, et encore merci pour le poisson) :
« Il est très facile de masquer l’inutilité des produits en forçant le trait sur la victoire que
constitue le simple fait de réussir à les faire fonctionner. Autrement dit, et c’est le principe
fondateur sur lequel s’appuie tout le succès galactique de cette entreprise, les erreurs de
conception fondamentales sont totalement masquées par les erreurs d’ergonomie. »

Le mauvais emploi d’une technologie inepte constitue un thème récurrent


chez Adams. On rencontre par exemple dans son monde des portes dotées
d’une personnalité ; non seulement elles sourient et se ferment, mais elles
remercient les gens de les avoir utilisées, et émettent un petit soupir de
satisfaction pour avoir bien travaillé. Tous les personnages du livre se
moquent de ces portes.
L’absence d’une pensée critique et un enthousiasme aveugle pour n’importe
quelle solution technique (il faut une solution technique coûte que coûte)
témoignent d’une absence totale d’intérêt pour les besoins des autres. Pire
encore, les produits issus de cette approche polluent notre environnement
avec des gadgets ennuyeux, incompréhensibles et souvent inutiles.
Je suis persuadé qu’une approche plus efficace du développement de projet
consiste à se mettre à la place des autres, ou au moins à écouter les gens et à
les aider à s’adapter et adapter la technologie à leurs besoins. Pouvez-vous
prétendre savoir exactement ce dont les autres ont besoin ou ce qu’ils
désirent ? Ce n’est qu’en écoutant, en réfléchissant, en évaluant et en faisant
des tests que vous allez acquérir ce genre de connaissances. Ce n’est qu’à
partir de ce moment que vous saurez quels sont les problèmes importants à
résoudre.
Qu’implique ce constat ?
Qu’il faut apprécier la différence et la diversité parce que cela entraîne notre
capacité à ressentir de l’empathie et à comprendre les problèmes et centres
d’intérêt des autres. Qu’il faut admettre ses erreurs, les évaluer et en tirer
des leçons, car ce n’est qu’ainsi que vous saurez vous adapter au
changement ou à la survenue de nouvelles informations. Il faut garder un
esprit ouvert ; après tout, qui a envie d’être considéré comme dépassé parce
qu’il n’a pas vu une solution, aveuglé par sa propres assurance ? Il faut
s’ouvrir à l’extérieur, car ce n’est que par une exploration sans crainte que
l’on fait des progrès.
Et en quoi cela concerne-t-il le langage MicroPython ?
MicroPython invite à progresser par petits pas pour s’adapter et changer les
choses, du fait qu’il est basé sur le langage Python qui favorise l’écriture de
code source clair, simple et dynamique. Vous gagnez en souplesse pour
tester, évaluer puis améliorer votre projet embarqué dans les meilleurs
délais. La vaste communauté Python est peuplée de personnes qui
considèrent comme précieuses ces qualités, ce qui leur garantit de ne jamais
tomber dans le piège des ingénieurs de Sirius Cybernetics.
Si votre projet est destiné à résoudre des besoins très particuliers, ses
qualités restent applicables. Même si vous savez ce que vous voulez, il ne
sera jamais inutile d’ouvrir votre horizon en allant voir comment d’autres
ont résolu des problèmes similaires.
L’avènement et la popularité de MicroPython tiennent pour une large part
au fait qu’il s’agit d’un formidable effort d’ingénierie embarquée qui
s’appuie sur le langage Python. Mais la popularité MicroPython est aussi
liée au fait que ses caractéristiques techniques favorisent une approche du
développement embarqué de type agile et orientée utilisateur.
Comme nous l’avons déjà dit en introduction, MicroPython offre de
nouvelles opportunités à trois types d’utilisateurs :

• Les programmeurs Python peuvent réutiliser leur expérience et leurs


compétences pour produire des systèmes embarqués, ce qui leur
donne l’occasion de rejoindre le mouvement de l’Internet des objets et
des microcontrôleurs.

• Les développeurs de systèmes embarqués découvrent dans


MicroPython une plate-forme pour réaliser des développements
rapides, en toute simplicité.

• Les débutants en programmation ont accès à une plate-forme facile


d’emploi et attrayante pour apprendre à programmer.
L’addition des trois groupes permet de comprendre qu’il y aura beaucoup
de personnes qui seront soit peu au courant des systèmes embarqués, soit
sans expérience du langage Python, soit les deux à la fois. Dans tous les cas,
nombreux sont ceux qui vont se demander ce qu’ils vont pouvoir produire
avec MicroPython et avec les appareils qu’il permet de contrôler.
Si vous êtes par exemple un développeur Python qui travaille avec Django
pour faire du développement Web, vous disposez des compétences et de
l’expérience permettant de concevoir de nouveaux projets basés sur le Web.
Pour pouvoir bien profiter de MicroPython, il va vous falloir acquérir une
connaissance des domaines d’application des systèmes embarqués et de la
valeur qu’ils peuvent apporter. Vous allez peut-être découvrir intuitivement
des manières de résoudre des problèmes en enrichissant des objets
physiques avec des appareils programmables, un peu dans le même état
d’esprit que celui dans lequel vous produisez des solutions pour le Web.
D’un autre côté, si vous êtes un développeur de systèmes embarqués, vous
connaissez déjà les possibilités des systèmes à microcontrôleur ; il vous
restera à comprendre comment MicroPython permet d’aboutir à une
solution fonctionnelle plus vite. Vous serez sans doute surpris de la grande
expressivité du langage Python, en comparaison des autres langages que
l’on rencontre dans les systèmes embarqués. Vous allez en outre découvrir à
quel point la communauté planétaire Python peut vous soutenir en
partageant des solutions logicielles libres et utiles, sans négliger de
continuer à soutenir et à assurer le suivi de ces solutions.
Enfin, en tant que programmeur débutant, vous allez rapidement constater à
quel point Python est facile à apprendre et à quel point la communauté se
soucie des nouveaux programmeurs. Vous verrez que les appareils basés sur
un microcontrôleur permettent d’envisager des projets amusants et très
pédagogiques.
Trois questions fondamentales se posent alors :

• Quels sont les problèmes que je veux résoudre ?

• Comment puis-je les résoudre avec MicroPython ?


• Un système embarqué est-il la meilleure solution pour ce genre de
problème ?
Nous tentons de répondre à la première question au long de ce chapitre.
Une fois que vous connaîtrez les capacités techniques, vous allez pouvoir
imaginer des solutions valables, étudier la façon dont les utilisateurs
réagissent et interagissent avec les systèmes embarqués et comprendre
comment ils peuvent tirer profit de ce genre d’appareil pour améliorer leur
propre existence.
Les chapitres suivants de ce livre développent en termes pratiques des
réponses à la deuxième question.
La réponse à la troisième question ne viendra qu’avec le temps. Et elle ne
peut survenir que si nous adoptons une pensée critique et argumentée, tout
en faisant des essais et en apprenant de nos erreurs au cours de la réalisation
et de l’amélioration de nos projets embarqués. C’est le genre de processus
pour lequel MicroPython est parfaitement adapté.
Toute la suite du chapitre vous propose d’adopter un cadre mental pour
mieux concevoir, évaluer et faire évoluer des projets embarqués pertinents.
Le texte se fonde sur les travaux de David Rose qu’il a publiés dans son
livre Enchanted Objects (non traduit en français). Le travail de Rose permet
d’aller au-delà de blocages, afin de pousser plus loin nos recherches. Il
bouscule la routine mentale qui résulte de nos préjugés et de nos
hypothèses. Même si vous n’êtes pas d’accord avec les détails de
l’approche de David Rose, le processus qu’il propose vous sera utile pour
mieux analyser et imaginer, afin de vous épargner le syndrome de la société
Sirius citée en début de chapitre.
Dans la dédicace de son livre, David Rose indique qu'« une interface plus
humaine entre la technologie et les humains est à votre portée ».
Pour s’en expliquer, l’auteur a établi trois groupes de concepts pour
chercher et identifier des domaines d’utilisation réels de systèmes
embarqués. Ces concepts se fondent sur l’idée que des objets enchantés
excitent l’imagination par la magie, les légendes et les histoires
fantastiques, et que des objets du quotidien peuvent être dotés de propriétés
extraordinaires. Cette nouvelle manière de considérer le développement
embarqué place l’accent sur ce que les objets peuvent faire, sur ce qu’ils
facilitent et comment ils peuvent résoudre les problèmes des gens.
J’apprécie particulièrement sa manière de mettre en valeur le contraste entre
l’approche dynamique, libératrice et imaginative que permet ce point de vue
et l’accumulation de gadgets électroniques impersonnels produits à la
chaîne qui nous entourent actuellement.
Voici ces trois groupes de concepts :

• les motivations humaines ;

• les potentialités d’enchantement ;

• les étapes vers l’enchantement.


Gardez l’esprit critique tout en découvrant ces trois groupes et cherchez à
apprendre comment ces concepts s’appliquent aux problèmes et aux
opportunités de votre existence.
En tenant compte de ces concepts, vous allez trouver des idées pour des
projets futurs utilisant MicroPython et les cartes présentées dans la suite du
livre.
Au final, la question est la suivante : qu’est-ce que vous allez choisir de
construire ?
Motivations humaines
Cette première liste décrit six motivations humaines que Rose considère
comme universelles et fondamentales : omniscience, télépathie, besoin de
sécurité, immortalité, téléportation et expressivité.
Ces motivations sont importantes parce qu’elles fournissent des indices sur ce qui
fait entrer un produit ou un outil en résonance avec ses futurs utilisateurs. Elles
permettent également de classer les grandes catégories de problèmes que vous
voulez résoudre, et incitent à puiser dans l’expérience des autres dans des contextes
similaires. Au fur et à mesure de votre lecture des explications de chacun des
concepts, vous pouvez chercher à imaginer comment ces motivations surviennent
dans votre propre vie et comment elles se manifestent dans votre utilisation d’un
objet ou d’un appareil.

Omniscience
Être omniscient, c’est tout savoir sur tout.
Les gens ont soif de connaître des choses aussi différentes que la physique
ou la vie intime des stars. Tout le monde apprécie d’être au moment critique
la personne qui possède une information précieuse, parce que cela influe
positivement sur notre réputation. Comme l’a bien dit Francis Bacon, « la
connaissance, c’est le pouvoir ».
Il existe déjà un certain nombre d’objets qui permettent d’accumuler ou de
fournir des connaissances. Par exemple, un baromètre permet de prévoir le
temps qu’il va faire ; vous pouvez aussi programmer un indicateur
lumineux pour connaître l’état de la Bourse avec différentes couleurs.
David Rose fait mention d’un appareil cité dans la trilogie À la croisée des
mondes de Philip Pullman, l’aléthiomètre. Cet objet imaginaire est capable
de toujours révéler la vérité.
Est-ce vraiment encore de la science-fiction ?
Vous pouvez par exemple demander à l’appareil quel est le périmètre du
soleil à un centimètre près.
Après tout, ce n’est peut-être bientôt plus de la science-fiction.
Demandez-vous ou demandez à vos futurs utilisateurs d’identifier l’élément
d’information qu’il leur faut toujours avoir sous les yeux. De quelle
manière cette information devrait-elle apparaître dans un objet ?

Télépathie
La télépathie consiste à communiquer par la pensée, notre état et nos
sentiments aux autres.
L’explosion en une décennie des réseaux sociaux prouve que les gens
adorent partager ce qu’ils font, comment ils se sentent et à quoi ils pensent
avec le vaste monde d’Internet. Quelques-uns ne cherchent qu’à attirer
l’attention sur eux.
Ces interactions multiples aident à renforcer notre vie sociale et favorisent
notre appartenance à une communauté. Sont en jeu la façon dont nous
collaborons avec nos amis ou écoutons ceux qui pensent autrement. Notre
vision du monde s’en trouve élargie.
Parmi les objets qui permettent ce genre de communication, citons les
montres intelligentes permettant de se connecter à Twitter, nos telliphones
(smartphones) qui permettent de visiter les sites des réseaux sociaux et
d’échanger par la voix, ainsi que les systèmes de téléconférence des salles
de réunion. Un objet imaginaire du même style est la Pendule de la famille
Weasley dans Harry Potter qui tient à jour le statut de chacun des membres
de la famille : à chaque membre de la famille correspond une main qui
désigne un état particulier, par exemple en train de dormir, au travail ou
perdu.
Cette horloge est-elle vraiment encore un objet de science-fiction ? Songez
simplement à toutes ces applications de détection de présence qui se servent
du GPS et d’un téléphone pour informer vos amis de votre position et de ce
que vous êtes en train de faire.
Connaissez-vous quelqu’un qui est absent mais que vous aimeriez avoir à
côté de vous ? Comment pourriez-vous être relié à cette personne grâce à un
objet ? Sur quel mode envisagez-vous la relation ? Voulez-vous partager
vos états d’âme, vos activités ou simplement votre position ?

Besoin de sécurité
Le besoin de sécurité correspond au désir d’être à l’abri du danger.
Les gens excellent lorsqu’ils se sentent en sécurité, à l’aise. C’est un état
agréable, et lorsque vous êtes parent ou responsable du bien-être de
quelqu’un d’autre, c’est un état que vous voudrez sans doute assurer à votre
protégé.
C’est le même genre de sentiment de sécurité dont nous espérons bénéficier
dans le monde numérique.
Nous utilisons des appareils pour nous protéger physiquement. Les radars
de recul nous évitent d’entrer en collision avec un autre véhicule. La
sécurité collective est assurée par des capteurs qui mesurent le trafic sur les
autoroutes et modifient la vitesse maximale pour garantir une circulation
sûre et optimisée.
Dans la littérature, un objet qui garantit la sécurité est l’épée de Frodon,
décrite dans le Seigneur des anneaux de J.R. R Tolkien. Elle s’illumine
lorsqu’il y a des orques dans les parages.
Tous ces objets amicaux ont néanmoins besoin de recueillir des données à
notre sujet pour fonctionner. Mais, nous voulons que nos données soient
maintenues en sécurité et nous voulons continuer à contrôler la façon dont
elles circulent.
Un contre-exemple est celui de la vidéosurveillance de l’espace public :
tous nos faits et gestes sont filmés avec notre sécurité comme prétexte. Il
suffit de se souvenir du livre 1984 de George Orwell pour avoir un aperçu
littéraire des conséquences possibles d’une perte de contrôle sur ces outils.
Demandez-vous si autour de vous quelqu’un aurait besoin d’attention et
d’aide. Pourquoi ce besoin d’aide ? Avez-vous eu connaissance d’une
situation qui pourrait être dangereuse ou pour laquelle il faudrait déployer
des mesures de sécurité ? Comment la sécurité pourrait-elle être améliorée
par des objets de l’environnement ? Quelles sont les données que ces objets
doivent collecter ? Comment ces données peuvent-elles être conservées en
sécurité ?

Immortalité
L’immortalité fait référence, plus raisonnablement, au désir de vivre une vie
longue et en bonne santé, en restant autonome, actif et curieux jusqu’au
dernier jour.
C’est sans doute dans ce domaine que les systèmes embarqués sont déjà
bien implantés, grâce aux technologies de surveillance de l’état de santé.
Vous possédez peut-être un bracelet Fitbit pour suivre votre activité
quotidienne, et accéder ainsi à un « soi quantifié » ? Vous connaissez peut-
être quelqu’un qui porte un pacemaker. Rose cite dans son livre l’exemple
du couvercle de boîtier à pilules intelligent qui s’allume au moment où il
faut prendre la prochaine pilule, conformément à la prescription. Lorsque le
boîtier est connecté à Internet, il va de lui-même demander le
renouvellement de l’ordonnance pour éviter toute rupture de stock.
Quels sont les aspects de votre existence que vous aimeriez modifier pour
vivre plus pleinement ? Pourriez-vous imaginer un objet pour favoriser ou
décourager certains comportements ? Parmi les objets qui existent déjà pour
entretenir sa forme physique, pourrait-on en améliorer certains en les
associant à un système embarqué ?

Téléportation
Le concept de téléportation fait référence à notre désir de mobilité sans
contrainte.
De nos jours, notre vision du monde n’est plus limitée à notre
environnement géographique immédiat. Pour nous déplacer, nous utilisons
des machines complexes et nous nous soumettons à des procédures pour
conduire une voiture ou prendre un avion. Serait-il possible de rendre les
voyages plus faciles et moins frustrants ?
Comment des appareils pourraient-ils nous permettre de nous rendre là où
nous voulons être ?
Une des récentes innovations dans ce domaine est constituée par les
guidons de navigation pour vélo ; le guidon brille du côté gauche ou droit
pour indiquer la prochaine direction à prendre. Bien sûr, dans moins de dix
ans, nous verrons les rues se remplir de voitures autonomes, des objets
enchantés sur roues qui savent où vous voulez vous rendre.
Il y a déjà beaucoup de systèmes électroniques dans les transports.
Comment pourrait-on simplifier toutes ces technologies ? Que voudriez-
vous voir améliorer dans un mode de transport, ou dans un lieu pour rendre
les déplacements, les arrivées et les départs plus agréables ? Comment
pourrait-on transformer cet univers des transports rempli de machines
impersonnelles pour en faire un lieu accueillant pour faire de chaque
voyage une fête ?

Expressivité
Le concept d’expressivité concerne notre besoin de reconnaissance par
l’utilisation de différents moyens artistiques et par les médias.
Les activités artistiques ont depuis toujours fait usage d’objets, qu’il
s’agisse d’outils pour créer des choses, d’instruments de musique ou de
l’exemple le plus connu d’un objet d’expression révolutionnaire : la presse
d’imprimerie.
De nos jours, nous avons déjà ces objets enchantés que sont les
imprimantes 3D (des exprimantes), qui permettent de construire aussi bien
une pièce détachée introuvable que toute une maison. Les contrôleurs de jeu
de type Guitar Hero permettent de jouer de la musique sans devoir y passer
des années d’apprentissage. Les kits de découverte électronique que sont les
Lego Mindstorms, pour lesquels les cartes Raspberry Pi et Arduino
appréciées des bricoleurs (makers) permettent de construire des objets
programmables, n’étaient il y a peu qu’accessible à une poignée d’initiés.
Ces appareils constituent de nouveaux supports d’expression artistique
programmables.
Si vous avez déjà une pratique artistique, imaginez comment vos outils et
votre équipement pourraient être enrichis. Comment faire d’un objet
programmable une vraie œuvre d’art ?
Tout comme moi, vous pensez peut-être que la plupart des exemples cités
en relation avec les motivations humaines sont déjà disponibles sous forme
d’applications sur l’écran plastique de votre téléphone.
Je suis persuadé que David Rose rétorquerait que toutes ces applications
similaires affichées sur un écran de telliphone ou de tablette empêchent de
profiter de l’excitation et de la facilité d’accès des objets tangibles. Un défi
intéressant consiste à chercher comment ce genre de fonction pourrait être
traduite depuis une application pour téléphone vers un véritable objet
enchanté attirant et pratique. Parvenir à cette transmutation permet de
rendre plus spatiale et plus dynamique la trame de notre monde, au lieu de
la compresser dans de petits objets que nous fourrons au fond de nos
poches.
Et par quels moyens peut-on rendre le monde plus intéressant et plus
dynamique ? Rose répondrait qu’il faut utiliser les possibilités des objets
enchantés.
Potentialités d’enchantement
Les sept concepts de cette catégorie permettent de distinguer les objets
embarqués qui sont enchantés de tous les autres appareils informatiques que
sont les téléphones, les tablettes et les micro-ordinateurs : évidence, kinésie,
accessibilité (financière), tissabilité, durabilité, ergonomie et amabilité.
Ces qualités ont une influence sur la façon dont nous apprenons à maîtriser
les objets, dont ils interagissent avec nous et comment ils prennent place
dans le monde. En parcourant cette liste, voyez comment ces qualités
pourraient s’exprimer dans des objets équipés d’un système embarqué.
Toutes ces qualités sont à mettre en relation avec l’expérience de
l’utilisateur. Les qualités d’un objet jouent sur la capacité d’une personne à
en tirer avantage, et sur la façon dont l’utilisateur développe un ressenti à
son sujet. Nous sommes vraiment entrés dans l’époque de l’interaction
homme-machine, de l’expérience utilisateur (UX) pour les objets
physiques.

Évidence
L’évidence fait référence à la capacité d’un objet à nous fournir juste le
niveau d’information nécessaire pour prendre une décision.
Ce n’est pas la même chose qu’une notification ou qu’une alarme
apparaissant en surimpression d’un écran ou de toute autre intrusion
numérique dans notre quotidien. Il s’agit plutôt de mettre en valeur
l’information qu’il faut au moment le plus opportun.
L’évidence réduit la charge mentale et diminue les interruptions, ce qui
permet une prise de connaissance plus rapide pour décider. C’est nous qui
décidons quand jeter un regard sur un objet : nous choisissons de le
consulter au lieu de le laisser attirer notre attention par des bips, des
vibrations et autres interruptions visuelles.
Parmi les objets qui fournissent une information immédiate et optimale,
citons les feux tricolores ou les aiguilles d’une horloge. Ce sont des objets
sur lesquels nous portons notre regard au moment précis où nous avons
besoin de leur information. Et ces informations sont à lecture immédiate :
rouge, je m’arrête, vert, je passe.
Dès qu’un objet embarqué possède un affichage, des lumières ou des parties
mobiles, il peut être reconfiguré pour que ces propriétés ou son aspect
transmettent une information simple et utile. En tant que britannique, je suis
sensible aux objets évidents, car ils me semblent plus courtois que toutes
ces applications qui se battent pour attirer mon attention.

Kinésie
La kinésie fait référence aux capacités d’un objet à sentir et à répondre à
nos mouvements physiques.
Un objet pourra par exemple détecter notre approche pour déclencher
quelque chose dans l’environnement, comme allumer les lumières ou le
chauffage. Un autre objet pourra être déplacé dans l’espace pour contrôler
un autre objet à distance ou réagir à vos gestes ; le fait de le secouer pourra
signifier une réponse négative. Un objet pourra réagir à un certain rythme
dans les mouvements ou tout simplement basculer en mode veille par le
simple fait que vous le posiez la face avant vers le bas.
Cette façon d’interagir est très différente de celle habituelle consistant à
taper sur des claviers, à toucher des écrans ou à déplacer laborieusement un
pointeur de souris sur un écran pour cliquer des symboles tels que des
boutons, des fenêtres et des corbeilles.
Je m’émerveille de ce potentiel d’expression. Dans le monde non
numérique, un violon traduit puis amplifie les mouvements de l’archet du
joueur pour produire des sons qui ont un important effet sur vos émotions.
Ce résultat dépend d’une longue pratique physique des doigts et des bras du
violoniste. Pourriez-vous imaginer un objet numérique capable d’autant de
nuances d’expression ?
Accessibilité économique
Cette accessibilité est la qualité qui permet de rendre un système embarqué
financièrement accessible à tous.
Par exemple, la carte BBC micro:bit coûte si peu cher qu’il n’est pas
catastrophique qu’un débutant en programmation en détruise une :
l’accessibilité réduit les risques.
Le prototypage et le développement coûtent moins, ce qui permet à plus de
personnes de s’y lancer. Les systèmes embarqués voient leur prix décroître
sans cesse, et notamment ceux qui fonctionnent avec MicroPython ; cela
favorise l’émergence de nouveaux projets sur mesure, car les gens sont
poussés à inventer des appareils exactement adaptés à leurs propres besoins.
De plus, les capacités techniques requises par la plupart des systèmes
embarqués dans les objets enchantés ne nécessitent pas de disposer des
circuits les plus récents, ni des cartes vidéo complexes et autres composants
coûteux. Les appareils restent à la portée de la plupart d’entre nous parce
que les composants dont ils sont constitués sont très peu onéreux.
Autrement dit, l’accessibilité économique favorise l’égalité des chances.

Tissabilité (wearability)
La tissabilité est une qualité qui estime à quel point un circuit embarqué est
intégrable à du tissu, à un vêtement. Elle va nous permettre de nous libérer
des ennuyeuses boîtes grises et noires qui nous entourent ou que nous
portons sur nous.
Une conférence organisée par une communauté de faiseurs du Royaume-
Uni appelée Electromagnetic Field Camp ([Link]) a rencontré un
succès énorme et ravi tous ses participants. Les organisateurs avaient décidé
de distribuer à chacun un badge doté d’un afficheur, du Wi-Fi et de boutons,
le tout fonctionnant sous MicroPython. Jamais un badge n’a eu autant de
succès, car la plupart des participants ont trouvé des usages incroyables à
ces badges en les reprogrammant.
Lors d’une autre conférence Python au Royaume-Uni, des adolescents ont
vidé les entrailles de quelques jouets en peluche, pour y insérer des cartes
micro:bit et les transformer en cybernounours.
En combinant un microcontrôleur et des rubans de diodes LED Neopixels,
nombreux sont ceux qui ont créé des costumes, des chapeaux, des bijoux et
des vêtements entiers qui affichent des informations, par exemple pour
indiquer l’état d’excitation du porteur en mesurant son pouls, ou
simplement pour faire joli, comme le T-shirt Neopixels Knight Rider créé
par Daniel Pope ([Link]/watch?v=UZr3oO5WXJI). Le futur des
vêtements connectés ne va pas se limiter à l’esthétique ou au
divertissement ; de nombreuses applications sont envisagées dans le monde
du travail.

Durabilité
La durabilité concerne la capacité d’un système embarqué à résister à
l’usure du temps.
Le contraste est total avec les plus récents telliphones valant
presque 1 000 € dont l’écran finit par se fissurer et dont le logiciel sera
dépassé en une paire d’années.
Un des musées de ma ville se sert de quelques systèmes embarqués peu
coûteux pour gérer ses vitrines interactives avec des boutons-poussoirs.
Tout fonctionne encore après plus de 10 ans d’utilisation intensive par les
visiteurs. En combinant un système embarqué et un objet robuste du
quotidien, vous augmentez la durée de vie de la partie électronique.

Ergonomie
L’ergonomie mesure de quelle façon l’aspect, le ressenti et les modalités
d’emploi d’un appareil le rendent facile à utiliser.
Nous savons tous nous asseoir sur une chaise sans devoir lire une notice ;
pourrait-on faire d’une chaise une interface utilisateur ? Si vous avez mal au
dos, vous pourriez peut-être tirer profit d’un système qui mesure le temps
que vous passez assis et qui affiche de façon ergonomique quand il sera
devenu sage d’étirer ses jambes.
C’est un exemple de ce qu’il est possible de faire pour revisiter les objets
courants en les enchantant. La technologie peut ainsi tisser des liens avec
l’histoire, avec les traditions et avec les objets banals qui nous entourent.

Amabilité
L’amabilité fait référence à la capacité d’un objet enchanté à provoquer un
attachement émotionnel.
Si nous reprenons l’exemple de la chaise que vous avez enchantée pour
gérer votre mal de dos, cette chaise appartenait peut-être au départ à votre
grand-mère, et les modifications que vous lui apportez seront peut-être très
appréciées de vos descendants, ce qui fait entrer l’objet enchanté dans
l’histoire et la généalogie de votre famille.
Plus généralement, un appareil peut se montrer aimable en offrant un aspect
anthropomorphe, ou plus simplement en ayant une présence élégante au
niveau de la forme et des fonctions. Il est très difficile de s’attacher à un
appareil rectangulaire en plastique et en métal tel qu’un téléphone ; c’est un
concentré de technologie qui permet de faire énormément de choses, mais
vous savez que vous allez retrouver exactement les mêmes applications
quand vous passerez au prochain modèle de la même gamme.
Toutes ces qualités n’impliquent pas que les objets auxquels on incorpore
des systèmes embarqués seront tous utiles. Il est tout à fait possible de
construire un objet qui souffre des mêmes défauts de manque d’humanité et
de personnalité que la plupart des gadgets électroniques qui nous sont
proposés actuellement. David Rose nous encourage à nous éloigner de cette
uniformité de la fabrication à la chaîne pour concevoir des objets
embarqués qui ont de la personnalité et du caractère. Il prétend qu’en tenant
compte des qualités que nous venons d’énumérer, nous saurons mieux
concevoir des objets qui vont entrer en résonance avec les gens et qui vont
les intriguer. Ce n’est qu’à partir de ce moment qu’ils deviendront agréables
à utiliser.
Comment faire pour prendre en compte les motivations humaines et les
qualités d’enchantement que nous venons d’écrire ? David Rose propose
une dernière liste qui décrit les étapes successives sur une échelle
d’enchantement : c’est une sorte de Gradus ad Parnassum moderne.
Gradus ad Parnassum signifie « étapes vers le Parnasse » en latin. Le Parnasse est
une montagne grecque dont le sommet a été consacré à Apollon et aux muses des
arts, des sciences et de la connaissance. Ce nom est attribué à certains livres qui
permettent de progresser pas à pas du débutant au maître. (N.d.T. : le compositeur
Muzio Clementi a par exemple écrit un Gradus ad Parnassum pour apprendre à
jouer du piano.)
Étapes vers l’enchantement
Quel processus adopter pour qu’un objet embarqué devienne enchanté ?
David Rose a identifié cinq étapes qui peuvent être répétées, et qui
constituent les barreaux d’une échelle vers l’enchantement. Elles aident à
créer des objets et des appareils qui répondent aux motivations humaines et
qui possèdent les qualités d’enchantement décrites. Ces cinq étapes sont :
connectivité, personnalisation, socialisation, ludification et narrativisation.
Ces cinq concepts ont pour but de servir de tremplin pour savoir poser les
bonnes questions au sujet du fonctionnement de l’objet.
Rose prétend qu’au minimum, le simple fait de parcourir ces étapes va vous
aider à poser les bonnes questions tout en progressant dans le
développement d’un projet embarqué. La hauteur de l’échelle n’est pas
prédéfinie : plus elle est haute, plus l’objet qui en résulte sera sophistiqué et
enchanté. L’auteur précise que tous les objets ne requièrent pas de pousser
jusqu’à la cinquième étape.

Connectivité
La connectivité fait référence aux capacités d’un objet à détecter et être
sensible à son environnement.
Ce que l’objet mesure ainsi pourra servir de déclencheur pour émettre une
information ou pourra être stocké sous forme de données pour traitement
ultérieur. L’objet peut être connecté à Internet et augmenter ses possibilités
en sous-traitant le stockage et les calculs intensifs à un ordinateur distant,
dans le nuage (cloud).
Demandez-vous comment ces possibilités de détection, de traitement et de
connectivité pourraient enrichir les objets et appareils de votre quotidien.
De quelle importante information pourrions-nous avoir besoin ? Comment
faut-il les collecter ? Que faut-il faire des données ainsi collectées ?
Personnalisation
La personnalisation fait référence à la capacité à modifier le comportement
de l’objet en réaction à son contexte d’utilisation.
En partant des données recueillies dans l’étape précédente, comment ces
données peuvent-elles servir à faire fonctionner l’appareil de façon plus
efficace ? Est-il possible de faire apprendre à l’objet à partir de son
contexte, pour qu’il modifie son comportement ? Que faut-il exactement
mesurer ? Quel aspect de l’appareil doit pouvoir s’adapter ? De quelle façon
l’évolution de l’objet doit-elle rester liée au contexte qu’il détecte ?

Socialisation
La socialisation concerne la création de liens avec l’entourage que sont les
amis, la famille et les collègues.
Cette mise en relation concerne également les objets les uns avec les autres,
afin de proposer une fonction d’ensemble utile. La sonnerie d’un gong pour
signifier qu’il est l’heure de passer à table pourra être retransmise à tous les
membres de la famille grâce aux appareils enchantés qu’ils portent sur eux.
Que voulez-vous faire savoir et à qui ?
Comment faut-il utiliser les objets pour déclencher ces signaux ? Comment
sélectionner les destinataires ? Peut-on modifier le signal en ajoutant des
informations ? Comment l’information est-elle signifiée aux destinataires ?
Comment assurer la confidentialité de ces communications ?

Ludification
La ludification d’un appareil consiste à organiser son comportement pour
qu’il se montre amusant, motivant et qu’il invite à une certaine complicité.
Le but est de faire en sorte que les gens deviennent des utilisateurs curieux
d’exploiter toutes les possibilités de l’objet. Pour y parvenir, vous pouvez
utiliser les techniques habituelles des jeux vidéo (un système pour compter
le score, des étapes d’avancement par succès, etc.). Il ne faut cependant pas
oublier de tenir compte du contexte afin de préserver le respect auquel
s’attend l’utilisateur. Il suffit de se souvenir du petit compagnon d’aide
Clippy que Microsoft avait tenté d’imposer avec son traitement de texte
Word.
Clippy est le nom d’un assistant digital qui avait été intégré à Microsoft Office à la
fin des années 1990. C’était une image de trombone avec des yeux animés qui
surgissait à l’écran au moment le moins approprié. Clippy interrompait votre travail
avec des suggestions qui se voulaient utiles, du style « On dirait que vous êtes en
train d’écrire un courrier. Peut-être voudriez-vous que je... » (suivies d’une série de
rubriques d’aide hors sujet ou trop élémentaires). Il a eu le même destin que les
portes qui vous remercient du Guide du voyageur galactique : Clippy a été la risée
de tous, et a vite disparu.

Est-ce que l’objet doit être amusant à utiliser ? Si oui, comment ? Sinon,
pourquoi ? Quels aspects de l’appareil vont demander un effort de
l’utilisateur ? Devez-vous prévoir de l’encourager d’une manière ou d’une
autre pour qu’il fasse l’effort d’apprendre ces fonctions ? À quel moment
faut-il l’inciter à le faire ? Qu’est-ce que vous prévoyez dans l’objet pour
qu’il prévienne les éventuelles déceptions de l’utilisateur ?

Narrativisation
Par narrativisation, je veux parler de l’inscription de l’objet dans une
histoire qui a du sens.
Les objets peuvent devenir des personnages dans les histoires que
constituent les existences de leurs propriétaires, en fournissant des
interactions utiles et censées. Chaque objet peut aussi par lui-même porter
une histoire qui le rend intéressant et unique.
Pour comprendre quelle puissance apporte la mise en scène d’un objet, il
suffit d’observer des enfants lorsqu’ils se servent d’objets pour jouer : ce tas
de briques Lego est en fait une fusée prête à explorer la planète Mars ; ce
bâton trouvé dans le jardin public est une baguette magique qui ne marche
que sur mes grands-parents.
Cette mise en scène concerne tout autant les objets qui font partie de
scénarios du monde réel. Seuls les gens qui possèdent le bon badge peuvent
pénétrer dans un bâtiment, ce qui exclut ceux qui ne n’ont pas cet objet
magique. Pensez à l’expression « Par cet anneau, je vous déclare unis »
(pour relier deux personnes par un objet). Seuls les officiers de police
portent un certain type de badge (l’objet est dans ce cas un marqueur de
statut). De telles histoires qui impliquent des objets apportent du sens à
notre monde. Est-ce que votre objet enchanté entre dans une mise en
scène ? Comment celle-ci est-elle lisible à travers les comportements de
l’objet ? Est-ce que l’histoire à laquelle l’objet est lié est évidente ?
Comment les gens vont-ils faire pour apprendre ou entrer dans cette
histoire ?
L’objectif ultime de David Rose est de fournir aux individus les moyens
pour trouver des idées neuves afin de créer des systèmes embarqués qui
proposent des interactions plus humaines qu’avec des terminaux, des
interfaces à fenêtres ou des écrans tactiles. Ces objets enchantés incorporent
des systèmes embarqués relativement simples qui ne sont pas des fins en
soi. Ils tirent leur utilité de leur capacité à détecter l’environnement, à
s’adapter, à se connecter, à intéresser et à raconter une histoire d’objet pour
résoudre un problème, pour créer de la valeur, pour ouvrir des possibilités
ou pour augmenter les possibilités de l’utilisateur.
Mais que faut-il savoir au sujet des systèmes embarqués et de MicroPython
pour pouvoir concevoir de tels objets enchantés ? C’est le but que se donne
la suite du livre.
CHAPITRE 7
Expressions visuelles
Un des comportements les plus immédiatement gratifiants que l’on puisse
programmer consiste à faire afficher quelque chose par un ordinateur : faire
clignoter une diode LED, afficher le célèbre message « Hello World! »,
montrer une image ou une animation. Rendre visible une information dans
le monde réel procure nécessairement une certaine satisfaction.
Blinkenlights
Le terme blinkenlights équivaut, pour le monde de l’embarqué, à l’affichage
du traditionnel message « Hello World! » dans le monde de l’informatique
grand format. À partir du moment où vous réussissez à faire clignoter une
diode LED sur une carte, c’est que vous avez correctement configuré la
carte (c’est ce que nous avons fait dans les Chapitres 2 à 5 de ce livre).
De prime abord, ce n’est pas une activité qui semble particulièrement
époustouflante ; une diode LED sert d’abord à faire des décorations ou à
indiquer un état. Pourtant, se familiariser avec un composant aussi simple
permet de pénétrer aisément dans la compréhension du mode de
fonctionnement du langage MicroPython. Il permet par exemple de
comprendre en quoi MicroPython ressemble et en quoi il diverge du
langage Python normal. Pour se lancer dans cette découverte, commençons
par nous intéresser à ce qu’est une diode LED.
Le terme blinkenlights est du pseudo-allemand, que l’on peut traduire en « lumières
clignotantes ». Il est apparu dans les années 1950 sur le mur d’un laboratoire
d’informatique chez IBM, sous forme d’instructions absurdes en pseudo-allemand.
À la fin du texte, on trouvait la mention « RELAXEN UND WATSCHEN DER
BLINKENLICHTEN » (relaxez-vous et admirez les lumières clignotantes). Le club
allemand German Chaos Computer Club a ensuite créé un projet appelé
Blinkenlights ([Link]) qui consistait à prendre le contrôle de
l’éclairage de tous les appartements d’un grand immeuble du centre de Berlin pour
s’en servir comme d’un afficheur monochrome géant avec des messages et des
animations.

Les composants que sont les diodes LED (ou diodes électroluminescentes,
Light Emitting Diode) sont disponibles dans de nombreuses couleurs.
Toutes fonctionnent sur le même principe. Lorsqu’elle est traversée par un
courant électrique et que la tension est suffisante, la diode émet des photons
de lumière. La couleur de l’émission dépend de la quantité d’énergie de ces
photons. Cette quantité d’énergie dépend à son tour des propriétés du
matériau semi-conducteur utilisé. La luminosité de la LED dépend de la
quantité de courant que l’on laisse passer à travers la diode.
Pour interagir avec le monde extérieur, un circuit de microcontrôleur
possède des broches d’entrée et de sortie sur lesquelles apparaît une tension,
ce qui permet de faire circuler du courant. Les diodes LED qui sont soudées
sur une carte, ou celles que vous connectez à la carte par une broche de
sortie GPIO, sont contrôlables en langage MicroPython, ce qui revient à
contrôler le courant qui passe par la broche puis par la diode LED.
Je rappelle que MicroPython fonctionne directement en contrôlant la partie
matérielle. C’est MicroPython qui est le seul intermédiaire entre le
programme source écrit en Python et le matériel du microcontrôleur.
Pendant que votre programme s’exécute, ce sont des fonctions MicroPython
de bas niveau qui contrôlent le fonctionnement de l’appareil et qui décident
donc d’appliquer une tension sur une broche ou une autre du
microcontrôleur, ce qui permet d’y faire circuler du courant.
Par principe, un microcontrôleur n’est pas destiné à héberger un véritable
système d’exploitation tel que Linux. Il n’y a pas de système
d’exploitation ; c’est MicroPython qui remplit ce rôle. Tous les protocoles
de gestion du matériel et l’état de toutes les broches qui contrôlent des
périphériques connectés à la carte sont incarnés par des fonctions de
librairies API Python comme pyb, microbit, digitalio ou machine. Ce sont
ces fonctions qui permettent de faire clignoter une diode LED comme nous
l’avons vu dans les précédents chapitres, et comme nous allons continuer à
le voir dans la suite du livre.
Voyons en quoi ces fonctions permettent d’exploiter plus intelligemment
une simple diode LED. Au lieu de nous contenter de l’allumer et de
l’éteindre, nous allons découvrir comment faire varier artificiellement sa
luminosité.
Dans le monde numérique, il n’y a au départ que des un et des zéros, et
donc des diodes allumées ou éteintes. Il n’y a pas d’état intermédiaire. Nous
ne pouvons pas allumer une diode LED à 50 % de sa luminosité maximale.
Pour y parvenir malgré tout, nous exploitons une technique qui porte le nom
de modulation de largeur d’impulsion, mais on utilise souvent le sigle
anglais PWM (Pulse Width Modulation). Grâce à cette technique, nous
pouvons faire apparaître comme analogique (avec plusieurs niveaux
intermédiaires de brillance) un composant prévu pour fonctionner en tout
ou rien. L’astuce consiste à allumer et éteindre l’alimentation de la diode à
un rythme suffisamment rapide, tout en contrôlant la proportion de temps
allumé et de temps éteint.
La Figure 7.1 montre trois exemples d’utilisation.

Figure 7.1 : Trois exemples de signaux à modulation de largeur d’impulsion PWM.

Les trois signaux sont à la même fréquence, c’est-à-dire qu’il y a trois


allers-retours sur la figure. Le rapport cyclique est différent en revanche. Il
n’y a de tension sur la sortie, et donc du courant pour alimenter le
composant, que lorsque le signal est à l’état haut (100 %). La puissance
moyenne qui est fournie au composant correspond au taux de travail.
Supposons que l’état allumé corresponde à une présence de tension
de 3,3 V et l’état éteint à 0 V. Le premier signal est allumé 50 % du temps,
et l’énergie transmise est donc la moitié de celle d’un signal numérique
allumé en permanence. Le résultat est que la diode va briller deux fois
moins fort qu’en numérique pur. Le deuxième signal est allumé à 25 % du
temps, ce qui fait briller la LED quatre fois moins. Le troisième signal
possède un taux de travail de 75 %, et fait donc briller la diode aux trois
quarts de sa puissance maximale.
Les périphériques qui profitent bien de ce mode de régulation sont ceux qui
ont beaucoup d’inertie, c’est-à-dire ceux qui mettent du temps pour basculer
d’un état à l’autre. Ils réalisent ainsi naturellement un lissage du signal.
En revanche, une diode LED peut commuter très rapidement. Elle va suivre
facilement les basculements entre allumé et éteint du signal PWM. Par
chance, l’œil humain ne parvient plus à détecter les changements d’état
lorsqu’ils sont trop rapprochés (c’est ce qui permet de profiter du
cinématographe et de la télévision). La diode LED va sembler allumée en
permanence à partir du moment où la fréquence des impulsions est
supérieure aux capacités de discernement humaines. La quantité d’énergie
consommée pendant une seconde étant variable, la luminosité de la diode
LED va l’être aussi.
Autrement dit, le lissage (intégration) des impulsions pour obtenir un signal
pseudo-analogique est obtenu soit naturellement avec un périphérique à
inertie, soit par l’inertie de l’œil humain avec un périphérique à
commutation rapide telle qu’une diode LED.
Nous verrons dans un chapitre ultérieur que la technique PWM est tout à
fait adaptée pour contrôler la vitesse de rotation d’un moteur électrique, et
permet même de générer des ondes sonores.

PyBoard
La technologie PWM n’est pas nécessairement disponible sur toutes les
sorties numériques d’un microcontrôleur, et tous les composants ne
l’acceptent pas. Le premier exemple en mode interprété ci-dessous
concerne la carte PyBoard et rappelle comment utiliser la diode LED rouge
de la carte en mode tout ou rien :

>>> red = [Link](1)


>>> [Link]()
>>> [Link]()
>>> [Link]()

Sur la carte PyBoard, la diode rouge correspond au numéro un, la verte au


numéro deux, l’orange au numéro trois et la bleue au numéro quatre. Les
deux premières ne fonctionnent qu’en tout ou rien, mais les diodes orange
et bleue peuvent fonctionner en mode pulsé PWM, ce qui permet de faire
varier leur luminosité. Voici un exemple complet :

Listing 7.1 : Code source 0701_PYB_pwm.py

import pyb

blue = [Link](4)
i = 0

while True:
[Link](5)

i += 1
if i > 255:

i = 0
[Link](i)

Dans l’exemple, la fonction [Link]() sert à marquer une pause


de 5 millisecondes, afin que les changements de luminosité ne s’enchaînent
pas trop rapidement, car l’œil humain ne pourrait plus les percevoir. Les
valeurs d’intensité acceptables, stockées dans la variable i, vont de 0 à 255.
Si vous fournissez une valeur supérieure au maximum, elle est forcée à
zéro.
Sur la carte BBC micro:bit, la luminosité est réduite à 10 niveaux différents,
de 0 à 9, pour chacune des 25 diodes LED de l’afficheur.
Circuit Playground Express (CPX)
Sur la carte CPX (Circuit Playground Express), il est possible de contrôler
la luminosité de la diode rouge en mode PWM. Dans la librairie
CircuitPython, les fonctions correspondantes utilisent d’ailleurs le terme
PWM dans leur nom. Voici un exemple :

Listing 7.2 : Code source 0702_CPX_pwm.py

from board import D13


import time
import pulseio

broche = [Link](D13)

while True:
for i in range(16):
broche.duty_cycle = 2 ** i
[Link](0.1)

La diode LED testée dans cet exemple est symbolisée par l’objet D13 tel
que défini dans la librairie board. Nous nous servons de la librairie standard
time de Python pour pouvoir introduire une pause afin de ralentir un peu les
changements de luminosité. La librairie qui nous intéresse surtout ici est
pulseio, car elle définit la classe nommée PWMOut. Nous demandons la
création d’une instance de cette classe portant le nom broche (un objet
donc). L’attribut de cet objet nommé duty_cycle peut prendre une valeur
entre 0 et 65 536 (soit 16 bits de résolution). La luminosité de la diode LED
est directement proportionnelle à cette valeur.
Sur les cartes ESP8266 ou ESP32, les diodes LED ne peuvent fonctionner
qu’en mode tout ou rien.
Si nous revenons à la carte CPX, elle dispose, en plus de sa diode LED que
nous avons testée, d’une véritable arme secrète pour la production d’effets
lumineux : NeoPixels.
Le ruban NeoPixels
NeoPixels est le nom donné à un ruban de diodes LED multicolores, ou
RGB (Rouge, Vert, Bleu).
Les diodes LED que nous venons de tester dans la section précédente
étaient monochromes. Chaque diode d’un ruban NeoPixels peut afficher
une parmi 16 millions de nuances différentes.
Toutes les cartes présentées dans ce livre peuvent contrôler un ruban
NeoPixels, mais seule la carte CPX de’Adafruit est directement équipée
d’une telle guirlande de diodes. Pour les autres cartes, il faudra acheter un
ruban, un anneau ou un bloc NeoPixels puis le brancher à plusieurs broches
GPIO du microcontrôleur, comme indiqué dans les instructions fournies
avec les diodes. Dans tous les cas, vous utiliserez les fonctions de la
librairie nommée neopixel (sans s) pour exploiter ce genre de composant en
MicroPython. Les détails sont quasiment les mêmes d’une carte à l’autre.
Dans un ruban NeoPixels, chaque diode est en réalité l’association de trois
diodes LED et d’un circuit pilote, le tout suffisamment miniaturisé pour
former un petit cube. Chacune des trois diodes LED élémentaires ne peut
s’allumer que dans l’une des trois couleurs primaires (rouge, vert ou bleu).
En contrôlant l’intensité de chacune des trois diodes, on peut obtenir plus de
16 millions de nuances de couleurs différentes (Figure 7.2).
Figure 7.2 : Ruban de diodes NeoPixels. Existe aussi sous forme d’anneaux, de plaques souples et
de bâtons.

L’effet est spectaculaire, et ce genre de composants se rencontre de plus en


plus dans les décorations de Noël, mais également en décoration intérieure.
Le composant NeoPixels n’a pas que des applications ludiques. Il est très
utile pour effectuer de la signalisation. En cas de détection d’un défaut ou
de danger, il suffit de faire clignoter le bandeau en rouge. Le bleu peut
indiquer une température basse. Une situation anormale pourra être
signifiée par des changements de couleur aléatoires. Vous n’êtes limité que
par votre imagination, et les NeoPixels stimulent aisément la créativité pour
le contrôle de la lumière.
La librairie nommée neopixel de CircuitPython définit une seule classe qui
porte le nom NeoPixel et sert à contrôler un composant NeoPixels. Pour
créer une instance à partir de cette classe, il faut lui fournir une référence à
l’objet qui symbolise la broche sur laquelle est branché le composant
NeoPixels, ainsi que le nombre de diodes que possède ce ruban. Voici à
quoi ressemble le code correspondant pour la carte CPX :

import neopixel
from board import NEOPIXEL

np = [Link](NEOPIXEL, 10)

Dans cet exemple, l’instance de la classe NeoPixel porte le nom np. La


broche du microcontrôleur à laquelle est soudé le début du ruban NeoPixels
est définie dans la librairie board sous le nom NEOPIXEL. Il suffit de
compter pour savoir qu’il y a 10 diodes installées sur la carte CPX ; c’est la
valeur que nous fournissons en second paramètre.
Les références des broches en CircuitPython sont toutes réunies dans la librairie
board. Nous verrons dans le Chapitre 9 comment faire référence aux autres cartes,
même si nous utiliserons certaines de ces broches avant d’atteindre ce chapitre. Par
exemple, sur une carte micro:bit, vous utilisez la mention pin0 prédéfinie dans le
module micro:bit.

Cet exemple illustre l’une des méthodes les plus habituelles d’interagir avec
le matériel en MicroPython : vous commencez par créer une instance d’une
classe qui correspond à la couche de fonctions au niveau du composant ou
du périphérique. Les méthodes définies dans cette classe restent
suffisamment générales pour vous éviter de vous perdre dans les détails à
bas niveau. Vous pouvez ainsi vous concentrer sur le comportement que
vous voulez donner à l’objet. Les attributs de bas niveau sont réglés au
moment de la création de l’instance, et notamment les références aux
broches à utiliser pour connecter le périphérique.
L’objet np de l’exemple précédent constitue un conteneur adressable. Il
joue le rôle d’une liste, ce qui vous permet de décider de l’état de chacune
des diodes indépendamment en utilisant la notation à crochets droits [].
Au niveau technique, ce contrôle est réalisé par les deux méthodes spéciales
__getitem__ et __setitem__.
Il est ainsi possible de faire référence à chacune des diodes d’un ruban
NeoPixels par sa position relative et même de connaître la valeur des trois
composantes rouge, vert et bleu en vigueur pour cette LED. Voyons cela par
une session d’interpréteur REPL qui reprend le début de l’exemple
interprété précédent :

>>> import neopixel


>>> from board import NEOPIXEL
>>> np = [Link](NEOPIXEL, 10,
auto_write=False)
>>> np[0]
(0, 0, 0)
>>> np[0] = (32, 0, 32)

La couleur de chaque diode NeoPixels est définie par un triplet de valeurs


entières entre 0 et 255 pour indiquer la luminosité. Dans le précédent
exemple, nous avons d’abord créé une instance qui est l’objet np puis nous
avons simplement demandé à connaître les valeurs actuelles de luminosité
de la première diode du ruban, celle en position zéro. Le triplet renvoyé
contient trois valeurs égales à zéro (0, 0, 0), ce qui signifie que la diode est
éteinte. Dans la dernière ligne, nous définissons une nouvelle couleur pour
la même diode en demandant d’afficher les deux couleurs primaires rouge
et bleue avec 32 de luminosité, sans allumer la diode verte. L’ordre des trois
valeurs correspond à rouge, vert, bleu. Notez que la dernière des dix diodes
correspond à l’indice 9.
Si vous mettez en pratique cet exemple pendant la lecture, vous vous
demandez peut-être pourquoi la première diode ne s’allume pas en mauve
après exécution de ce programme. Relisez le troisième paramètre de
l’instruction de création de l’objet. Ce paramètre s’écrit auto_write et nous
l’avons forcé à la valeur False. Par défaut, la version Adafruit de la librairie
NeoPixels écrit d’office le nouvel état vers la diode dès que vous lancez la
commande. Toutes les autres versions de la librairie obligent à appeler une
des deux méthodes write() ou show() pour faire prendre en compte la
nouvelle valeur de couleur. Puisque nous utilisons ici la carte CPX Adafruit,
j’ai volontairement ajouté le paramètre pour que le programme se comporte
de la même manière qu’avec les autres cartes. L’instruction qu’il faut
ensuite utiliser pour valider et faire prendre en compte la nouvelle couleur
est :

>>> [Link]()

Dans la version micro:bit de la librairie NeoPixels, la méthode write() a été


renommée show(), car les auteurs ont pensé que les enfants comprendraient
mieux une commande qui signifie « montrer » qu’une commande qui signifie
« écrire » pour allumer des diodes. Dans la version Adafruit, les deux noms
write() et show() déclenchent la même méthode, ce qui garantit la
compatibilité du code source entre les deux matériels.

Lorsque vous avez besoin de modifier toutes les diodes LED d’un ruban
NeoPixels de la même façon, vous tirez avantage de la méthode nommée
fill() qui attend en paramètre un seul triplet de valeurs qu’elle applique
successivement à chacune des diodes LED du ruban NeoPixels :

>>> [Link]((0, 32, 32)) # Cyan pour


toutes les LED
>>> [Link]()

Cette méthode de remplissage n’est pas utilisable dans la version micro:bit de la


librairie neopixel.

La même méthode permet d’éteindre d’un geste toutes les diodes :

>>> [Link]((0, 0, 0))


>>> [Link]()

Cette librairie très simple permet d’obtenir à peu de frais des effets
lumineux très amusants. Voyons comment combiner la librairie dans un
programme générant des changements de couleur perpétuels :
Listing 7.3 : Code source 0703_CPX_neopixels.py

import neopixel
import random
import time
from board import NEOPIXEL

np = [Link](NEOPIXEL, 10,
auto_write=False)
step = 32

while True:
for i in range(10):
for j in range(10):
np[j] = tuple((max(0, val -
step) for val in np[j]))
r = [Link](0, 255)
g = [Link](0, 255)
b = [Link](0, 255)
np[i] = (r, g, b)
[Link]()
[Link](0.05)

Ce programme balaye la guirlande de diodes LED en sélectionnant une


couleur au hasard tout en réduisant la luminosité par pas de 32. L’effet
visuel fait penser à un jeu de lumière circulaire de discothèque. Vous
modifiez la vitesse de rotation en changeant la valeur que vous transmettez
à la méthode [Link](). Vous réglez la vitesse de diminution de
luminosité en changeant la valeur de la variable step. Vous verrez que ce
réglage a aussi un effet sur la longueur de la traîne de l’effet circulaire.
Cet exemple montre à quel point il est facile de créer des objets de
décoration ou d’information, par exemple avec du rouge pour indiquer un
danger.
Un mode de communication visuelle plus dense consiste à afficher des mots
et des images. Deux de nos cartes permettent de réaliser cela très
facilement.
Texte, images et animations

micro:bit
La carte micro:bit propose un afficheur succinct et très souple, constitué de
cinq rangées de cinq lignes de diodes LED rouges miniaturisées. Vous
disposez de 10 niveaux de luminosité numérotés de 0 à 9. Le contrôle par
impulsions PWM est utilisé, mais il n’est pas accessible directement au
programmeur. Les 10 niveaux possibles constituent une couche
d’abstraction, vous isolant des détails techniques du matériel.
Nous avions vu dans le Chapitre 3 que l’objet prédéfini display de la
librairie microbit permettait de lire et d’écrire la valeur de chaque pixel
incarné par une diode LED. En faisant fonctionner cet objet display avec la
classe Image de la même librairie, nous allons pouvoir créer toutes sortes
d’effets intéressants.
Cet objet display est un bon exemple de la façon dont MicroPython rend
accessible la partie matérielle de la carte. Cet objet est créé d’office pour
incarner une partie du matériel qui est toujours disponible. Chaque carte
micro:bit possède cet afficheur. MicroPython prédéfinit l’objet approprié
pour vous éviter de devoir créer une instance pour l’afficheur chaque fois
que vous avez besoin de l’utiliser. Toutes les interactions avec l’afficheur
vont donc utiliser des méthodes qui appartiennent à cet objet prédéfini
display de la librairie microbit.

L’action d’affichage qui vient en premier à l’esprit est celle qui consiste à
faire défiler du texte :

Listing 7.4 : Code source 0704_MIC_hello_world.py

from microbit import display


[Link]("Hello World!")

La méthode [Link]() dispose de toute une gamme d’options :

• En ajoutant le paramètre delay, vous contrôlez le nombre de


millisecondes d’attente entre deux étapes de l’affichage. Autrement
dit, cela permet de régler la vitesse de défilement.

• Le paramètre wait qui est booléen, c’est-à-dire qu’il peut être soit
True, soit False, permet de choisir de suspendre l’exécution du code
jusqu’à la fin du défilement.

• Si vous donnez au paramètre loop la valeur True, le défilement


reprend perpétuellement.

• Enfin, si vous donnez au paramètre monospace la valeur True, tous les


caractères seront affichés avec la même largeur de cinq pixels. Sans ce
paramètre, il y aura toujours une colonne de pixels éteints entre deux
caractères du défilement.
L’instruction suivante illustre cela : ce message en police non
proportionnelle défile sans cesse, et l’instruction n’empêche pas l’appareil
d’effectuer d’autres opérations pendant l’affichage :

[Link]("Hello !", delay=80,


wait=False, loop=True, monospace=True)

Pour faire afficher les caractères un par un au lieu de les faire défiler, vous
remplacez la méthode scroll() par la méthode show() :

[Link]("Danger!")
Cette méthode comprend tous les paramètres de sa collègue scroll(), sauf
le paramètre monospace. Elle en reconnaît un de plus, clear, pour effacer
l’afficheur en fin d’animation si le paramètre vaut True.
Au lieu d’afficher des lettres et des chiffres, la carte micro:bit peut afficher
des sortes d’images dans la matrice de pixels grâce à la classe nommée
Image. Un certain nombre d’images sont déjà prédéfinies dans la librairie.
Voici par exemple comment afficher une émoticône de visage souriant
(Figure 7.3) :

[Link]([Link])

À partir de cet exemple, je suppose que vous avez inséré en début


de code source la directive d’importation de librairie from micro : bit
import * ou son équivalent pour une autre carte.
Figure 7.3 : Affichage d’une émoticône sur la carte micro:bit.

Les 65 images prédéfinies correspondent aux attributs de la classe Image


qui sont écrits en lettres capitales. Vous en trouverez la liste dans la
documentation (microbit-
[Link]/en/latest/[Link]). Vous pouvez faire
afficher la liste dans l’interpréteur REPL en utilisant la commande
dir(image). Comment les concepteurs ont-ils su que ces émoticônes allaient
plaire aux débutants en programmation ? Parce que la plupart ont été
dessinées par une jeune fille anglaise de 11 ans !
Vous pouvez bien sûr définir vos propres images. Il suffit de créer un objet
en créant une instance de la classe Image. L’exemple suivant crée un motif
avec certaines LED plus brillantes que d’autres :

Listing 7.5 : Code source 0705_MIC_image.py

from microbit import display, Image

monImage = Image(
'33333:'
'36663:'
'36963:'
'36663:'
'33333:')

[Link](monImage)

Le résultat doit ressembler à la Figure 7.4.

Figure 7.4 : Affichage d’une image personnalisée sur la carte micro:bit.

Pour définir l’image lors de la création de votre objet à partir de la classe


Image, il suffit de transmettre une chaîne contenant des valeurs numériques
pour la luminosité de chacune des diodes LED qui constituent les pixels.
Vous commencez en haut à gauche et terminez en bas à droite. Chaque fin
de ligne correspond à un signe deux-points. En présentant la chaîne sous
forme de cinq sous-chaînes (comme ci-dessus), il reste assez facile de
deviner le résultat sur l’afficheur. Vous pourriez aussi tout réunir dans une
seule grande chaîne, comme ceci :
'33333:36663:36963:36663:33333:'

Votre objet de classe Image n’est pas limité aux dimensions réduites de
l’afficheur. Vous pouvez par exemple créer une sorte de jeu de labyrinthe.
Seule une portion carrée du labyrinthe complet est affichée à tout moment ;
c’est la zone visible (viewport). Il suffit de définir une chaîne beaucoup plus
longue pour définir une surface plus grande que la zone visible. Il peut
s’avérer plus efficace d’utiliser le second format de création d’une instance
de l’image, comme ceci :

Listing 7.6 : Code source 0706_MIC_bytearray.py

from microbit import display, Image

buf = bytearray(x % 10 for x in


range(100))
i = Image(10, 10, buf)

[Link]([Link](3, 4, 5, 5))

Nous utilisons ici le type standard de Python nommé bytearray qui permet
de stocker des octets de façon très efficace. Ici, il nous sert à stocker des
nombres entre zéro et neuf dans un tampon mémoire qui va représenter la
luminosité des diodes LED. Nous créons la nouvelle image en l’associant à
ce tampon à deux dimensions. Ici, nous demandons de créer une image
de 10 sur 10, chaque ligne contenant des pixels de plus en plus brillants en
allant vers la droite.
En partant de ce réservoir de données d’affichage, nous pouvons découper
une zone carrée qui correspond à la zone visible. Il suffit d’utiliser la
méthode de découpage nommée crop() en lui indiquant la position initiale
dans la zone réservoir et les dimensions de la zone visible. C’est ce que
nous faisons dans la dernière instruction de l’exemple précédent. L’image
est ici temporaire.
La méthode crop() doit recevoir tout d’abord les coordonnées en X et en Y
du coin supérieur gauche de la zone à montrer suivies de la largeur et de la
hauteur de cette zone. Dans l’exemple, nous commençons au point (3,4) et
sélectionnons cinq positions en largeur et cinq en hauteur. Si nous reprenons
l’exemple de labyrinthe, la méthode crop() permet de déplacer la zone
visible au sein du labyrinthe, en fonction des déplacements demandés par le
joueur (au moyen de techniques que nous verrons plus loin).
La classe Image définit d’autres méthodes très utiles : copy() renvoie une
copie exacte de l’image, invert() inverse la luminosité de chaque pixel et
fill(valeur) force tous les pixels à la même valeur demandée. Une fois
que vous avez créé une instance de l’image, vous pouvez tout à fait lire et
écrire la valeur de chaque pixel dans une session de l’interpréteur REPL :

>>> i.get_pixel(1, 2)
1
>>> i.set_pixel(1, 2, 9)

Les deux méthodes attendent en paramètres les coordonnées en X et Y du


pixel considéré. Pour la méthode get_pixel(), cela suffit pour lui faire
renvoyer la valeur actuelle du pixel. Pour la méthode d’écriture
set_pixel(), il faut fournir un troisième paramètre pour la nouvelle
luminosité. Rappelons que la nouvelle valeur ne sera pas prise en compte
par l’afficheur tant que vous n’aurez pas demandé sa réactualisation en
réalisant un appel à la méthode display. show().
Dès que vous devez traiter une image dont les dimensions sont supérieures
à la zone visible, vous pouvez utiliser la méthode de découpage crop() et
les méthodes de décalage de la famille shift_[up(), down(), left(),
right()] pour effectuer facilement des défilements et des translations. Il
suffit d’indiquer une valeur entière pour la valeur de défilement. Ce sont ces
méthodes qui sont utilisées pour réaliser le défilement de l’afficheur avec
[Link]().

Toutes ces méthodes de découpage et de décalage sont fondées sur une


méthode unique et très puissante qui porte le nom blit(). Vous lui
fournissez en paramètre un objet image et les quatre paramètres du
rectangle désiré. La fonction met à jour les données de l’objet image avec
les valeurs de luminosité définies dans la zone correspondant au rectangle
spécifié de l’image d’origine. Voici par exemple comment écrire la méthode
de découpage crop() grâce à la méthode blit() :

Listing 7.7 : Redéfinition de la méthode crop() (0707_MIC_crop.py)

def crop(self, x, y, w, h):


result = Image(w, h) #
Nouvelle image vide "result"
[Link](self, x, y, w, h) # Blit
de self vers result
return result # Renvoi
nouvelle image

Le dernier ensemble de fonctions concernant l’afficheur de la carte


micro:bit est l’animation. En raison de l’orientation pédagogique de cette
carte, ces opérations sont très simples à programmer.
Il suffit en effet d’utiliser un objet itérateur, c’est-à-dire un objet qui peut
renvoyer des éléments les uns après les autres. Vous lui faites renvoyer des
instances d’images qui vont servir d’images successives de l’animation.
L’objet itérateur, qui est la source de ces images, doit être transmis à la
méthode [Link]() qui va les afficher.
Les images successives dans les films et émissions télévisées se nomment en
anglais des frames, littéralement des cadres. En français, pour désigner une image
qui fait partie d’une série affichée à grande vitesse et ne pouvant jamais être
regardée isolément, nous proposons le terme animage.

La classe Image prédéfinit deux séries d’images : ALL_CLOCKS, qui


contient les représentations des 12 positions d’une aiguille d’horloge et
ALL_ARROWS, qui contient des flèches orientées vers les principaux
points cardinaux. Voici par exemple comment faire afficher l’animation
d’une horloge sur la carte micro:bit :
[Link](Image.ALL_CLOCKS, delay=50,
wait=False, loop=True)

Le résultat fait penser à un radar des films de science-fiction des


années 1970.
Rien ne nous empêche d’utiliser une fonction pour générer une image à la
volée pour notre animation :

Listing 7.8 : Code source 0708_MIC_wopr.py

from microbit import *


import random
import array

def animation():
bkl = [Link]('b',
[[Link](0, 9) for i in range(25)])
yield Image(5, 5, bkl)

while True:
[Link](animation())

Le résultat est un affichage de type blinkenlights, un peu comme sur


l’ordinateur WOPR dans lequel s’introduit le héros dans le film Wargames
de 1983.
Les afficheurs à diodes LED sont simples mais leurs possibilités sont
limitées. Ils ne prétendent pas rivaliser avec les afficheurs couleur de nos
telliphones, de nos tablettes et de nos ordinateurs. Les machines dotées de
ces écrans ne fonctionnent normalement pas avec le langage MicroPython,
mais cela ne signifie pas que MicroPython ne puisse pas contrôler un
afficheur un peu plus sophistiqué tel que l’afficheur LCD couleur
disponible pour la carte PyBoard. Découvrons ce composant.
Afficheur LCD couleur pour PyBoard
L’afficheur LCD couleur nommé LCD160CR est un périphérique pour
PyBoard très intéressant. Non seulement c’est un afficheur couleur, mais il
est tactile, ce qui lui permet de remplir le rôle d’un périphérique d’entrée et
d’un périphérique de sortie. Vous pourriez vous en servir pour faire une
version miniature d’une tablette (une picotablette ?).
L’afficheur peut être inséré sur la carte PyBoard dans deux positions
différentes nommées X et Y (Figure 7.5).
Une fois que l’écran est en place, le plus simple consiste à exécuter le
programme de démonstration depuis l’interpréteur REPL :

>>> import lcd160cr_test


To run all tests: test_all(<lcd>)
Individual tests are: test_features,
test_mandel
<lcd> argument should be a connection, eg
"X", or an LCD160CR object
>>> lcd160cr_test.test_all('X')

Dans la dernière ligne de cet extrait, nous effectuons un appel à la fonction


test_all() en spécifiant la position de montage, X ou Y. Vous verrez
d’abord apparaître un rappel des caractéristiques du composant suivi d’une
restitution de fractale bleue. Si c’est le cas, votre système est prêt. Dans le
cas contraire, éteignez tout, débranchez l’afficheur, et rebranchez-le
correctement en appuyant puis redémarrez.
Figure 7.5 : Les deux positions de montage de l’afficheur LCD (à gauche X et à droite Y)

Pour afficher quelque chose à l’écran, c’est très simple :

Listing 7.9 : Code source 0709_PYB_lcd_hello_world.py

import lcd160cr

lcd = lcd160cr.LCD160CR('X')
[Link]()
[Link]('Hello, World!')

Cet exemple crée un objet nommé lcd qui va incarner l’écran, en héritant
de la classe lcd160cr. Nous précisons si l’écran est installé en X ou en Y.
Pour empêcher l’écran d’afficher ses caractéristiques au départ, nous
utilisons la méthode erase() et faisons afficher notre message avec la
méthode write(). Il apparaît sous forme de petits caractères verts dans
l’angle supérieur gauche. Vous voilà face au plus petit écran en texte vert du
monde ! C’est vraiment digne d’un craqueur passé maître dans le
maniement de la langue 1337.
L’écriture 1337, qui se lit LEET, appartient au langage du même nom qui permet
aux programmeurs « non conventionnels » de parler entre eux sans être compris
des autres.

Pour rester dans l’ambiance 1337, voici comment utiliser l’interpréteur


REPL pour contrôler l’afficheur :

>>> import lcd160cr


>>> import pyb
>>> lcd = lcd160cr.LCD160CR('X')
>>> [Link]()
>>> uart = [Link]('XA', 115200)
>>> pyb.repl_uart(uart)

Après cette série d’instructions, tous les dialogues avec l’interpréteur vont
apparaître sur l’écran. C’est votre tour de dialoguer avec un ordinateur
comme vous le voyez faire dans les superproductions américaines.
Redevenons plus sérieux. La compacité de l’écran rend les caractères assez
difficiles à lire. Vous avez le choix entre quatre polices différentes, la taille
par défaut étant la plus petite, qui correspond à zéro. Si vous êtes toujours
face à l’interpréteur REPL, vous pouvez augmenter la taille des caractères
en fournissant une valeur allant jusqu’à trois :

>>> lcd.set_font(1)

Cette méthode set_font() accepte d’autres paramètres en option pour


influer sur l’aspect du texte à afficher. Par défaut, toutes ces options sont à
zéro. Le paramètre nommé scale définit la taille des pixels utilisés pour le
texte. Lorsqu’un pixel est carré, son côté mesure scale plus un, la valeur
maximale étend égale à 63. L’argument bold peut valoir entre 0 et 31 et
définit l’épaisseur du trait des caractères (pour un effet de mise en gras).
L’argument trans décide s’il faut utiliser un fond transparent (valeur un) ou
pas (valeur zéro). Enfin, l’option scroll permet de faire défiler doucement
vers la gauche le texte au fur et à mesure de son arrivée, si la valeur est
égale à 1. Pour une valeur égale à zéro, le texte défile brutalement vers le
haut. En restant dans l’interpréteur REPL, les deux instructions suivantes
choisissent la police de taille 3, sans mise à l’échelle, avec un léger effet de
mise en gras, pas de transparence et un défilement doux :

>>> lcd.set_font(3, scale=0, bold=1,


trans=0, scroll=1)
>>> print("Hello, World!")

Pour influer sur la couleur du texte et de l’arrière-plan, vous disposez de la


méthode set_text_ color(). Les couleurs sont combinées dans une valeur
entière sur 16 bits, combinant les valeurs de nuances du rouge, du vert et du
bleu. La classe lcd160cr définit une méthode statique qui porte le nom
rgb() et qui réalise cette préparation à votre place :

>>> text_colour = [Link](255, 128, 0)


>>> background = [Link](0, 128, 255)
>>> lcd.set_text_color(text_colour,
background)
>>> [Link]("Hello, World!")

Enfin, pour décider de la position initiale du début du texte à afficher, il faut


fournir le couple de coordonnées X et Y du coin supérieur gauche du bloc
de texte :

>>> lcd.set_pos(20, 40)


>>> [Link]("Hello, World!")
L’affichage de texte est indispensable, mais ce sont les affichages
graphiques qui attirent l’attention.
Au niveau élémentaire des pixels, l’interface de cet écran ressemble
beaucoup à celle de la carte micro:bit. Vous modifiez chaque pixel en
fournissant les coordonnées X et Y aux deux méthodes set_pixel() et
get_pixel(). La valeur de couleur rgb est générée comme pour la couleur
de texte. Le script suivant produit un effet de neige vidéo multicolore.

Listing 7.10 : Code source 0710_PYB_lcd_snow.py

import lcd160cr
import random

lcd = lcd160cr.LCD160CR(‘X’)
[Link]()

while True:
r = [Link](0, 255)
g = [Link](0, 255)
b = [Link](0, 255)
colour = [Link](r, g, b)
x = [Link](0, lcd.w)
y = [Link](0, lcd.h)
lcd.set_pixel(x, y, colour)

L’effet de neige vidéo est celui que certains d’entre vous ont pu connaître sur les
téléviseurs analogiques lorsque l’émetteur cessait de fonctionner en fin de soirée.

Vous remarquez que nous utilisons deux constantes prédéfinies nommées


lcd.w et lcd.h pour indiquer les dimensions de l’affichage en largeur et en
hauteur.
Vous disposez de plusieurs méthodes pour tracer des lignes et des formes
simples. Toutes ces méthodes exploitent le concept de pinceau (pen).
L’objet pinceau possède une couleur de tracé pour le contour extérieur
d’une forme et une couleur de remplissage. Pour dessiner quelque chose, il
faut d’abord régler les attributs du pinceau avec la méthode set_pen() puis
utiliser les deux familles de primitives pour les rectangles et les lignes. La
suite de la session d’interpréteur REPL ci-après montre l’utilisation
élémentaire de trois méthodes :

>>> [Link]()
>>> lcd.set_pen([Link](255, 0, 0),
[Link](0, 0, 255))
>>> [Link](20, 20, 40, 40)

La classe LCD 160CR définit un grand nombre de méthodes de tracé graphique.


Toutes sont décrites dans la documentation de MicroPython
([Link]/en/latest/pyboard/library/lcd160cr
.html).

Dans l’exemple précédent, nous choisissons une couleur de tracé rouge et


une couleur de remplissage bleu. Nous dessinons ensuite un rectangle
équilatéral, c’est-à-dire un carré. Le coin supérieur est aux
coordonnés 20,20 et les dimensions en largeur et en hauteur sont
de 40 sur 40.
À partir de ces méthodes primitives, vous pouvez facilement créer quelque
chose de très attirant. Le script assez simple qui suit génère sans cesse des
images dans le style de l’artiste peintre néerlandais Piet Mondrian :

Listing 7. 11 : Code source 0711_PYB_lcd_mondrian.py

import pyb
import lcd160cr
from random import randint, choice,
uniform
lcd = lcd160cr.LCD160CR('X')

MAX_DEPTH = 4
RED = [Link](255, 0, 0)
YELLOW = [Link](255, 255, 0)
BLUE = [Link](0, 0, 255)
WHITE = [Link](255, 255, 255)
BLACK = [Link](0, 0, 0)
COLOURS = [RED, YELLOW, BLUE, WHITE,
WHITE, WHITE]

class Node:

"""
Un noeud d'arborescence du sujet
tableau Mondrian.

"""
def __init__(self, depth=0):

"""
Choix de la couleur, calcul de
profondeur
et ajout de noeuds enfants si pas
trop dense.

"""
[Link] = choice(COLOURS)
[Link] = depth + 1
[Link] = []
if [Link] <= MAX_DEPTH:
[Link] = choice(['h',
'v'])
[Link] = uniform(0.1,
0.9)

[Link](Node([Link]))

[Link](Node([Link]))

def draw(self, x, y, w, h):

"""
Trace ce noeud et ses enfants x
fois.

"""
lcd.set_pen(BLACK, [Link])
[Link](x, y, w, h)
if [Link]:
if [Link] == 'h':
[Link][0].draw(x,
y, int(w * [Link]), h)
[Link][1].draw(x +
int(w * [Link]), y,

int(w * (1.0 - [Link])), h)


else:
[Link][0].draw(x,
y, w, int(h * [Link]))
[Link][1].draw(x, y
+ int(h * [Link]), w,

int(h * (1.0 - [Link])))


while True:
# Produit un nouveau tableau Mondrian
au bout de 4-8 sec.
tree = Node()
[Link](0, 0, lcd.w, lcd.h)
[Link](randint(4000, 8000))

L’écran devrait afficher quelque chose dans le style de la Figure 7.6.

Figure 7.6 : MicroPython sait peindre dans le style Mondrian.

Le résultat est assez convaincant. De tels affichages attirent immédiatement


les utilisateurs.
Une autre capacité aussi importante consiste à faire en sorte que l’appareil
soit sensible à son environnement pour pouvoir y réagir et adapter son
comportement en conséquence. C’est le sujet du prochain chapitre.
CHAPITRE 8
Capteurs et détection
Une caractéristique essentielle d’un système embarqué est sa capacité à
détecter des changements dans son environnement, et notamment les
actions de son utilisateur qui constituent ce que l’on appelle l’expérience
utilisateur (User eXperience). Cet utilisateur n’est pas le programmeur du
système. Dans ces actions, on retrouve par exemple le geste consistant à
secouer l’objet pour redémarrer le programme, la détection du niveau de
lumière pour adapter la luminosité d’une diode LED ou encore l’appui sur
un bouton pour choisir une option parmi plusieurs. Dans tous les cas, cela
suppose que l’appareil est capable de traiter des données d’entrée, en
général au moyen de composants appelés capteurs.
Les appareils présentés dans ce livre ne sont pas dotés d’un clavier, d’une
souris ou d’un écran tactile, à la différence des ordinateurs classiques. Il
faut donc leur associer d’autres moyens d’interaction. Le programmeur peut
se contenter d’émettre des commandes dans l’interpréteur REPL de Python
pour interagir, mais cela ne répond pas aux attentes des utilisateurs du
produit final qui ont besoin de communiquer leurs désirs, leurs ordres et
leurs décisions de la façon la plus intuitive possible, en tenant compte du
contexte, et si possible de façon agréable voire ludique. Voilà pourquoi il est
important de nous intéresser à la façon dont MicroPython permet
d’exploiter des données d’entrée via des capteurs.
Globalement, les circuits présentés dans le livre offrent une grande variété
de modes de détection de l’environnement. Les circuits ESP8266 et
ESP32 n’ont qu’un seul bouton alors que d’autres (les cartes CPX et
micro:bit) sont dès le départ dotées de toute une gamme de composants
permettant des interactions avec l’utilisateur. Dans tous les cas, il est
toujours possible d’ajouter des capteurs externes en les connectant aux
broches du connecteur GPIO. Les principes d’utilisation de ces capteurs
sont facilement transposables d’un circuit à un autre.
Commençons notre exploration des périphériques d’entrée avec le plus
simple qui soit : le bouton-poussoir.
Boutons et toucher capacitif
Un bouton est toujours intrigant : « Et si j’appuyais dessus ? ». En tant que
programmeur, notre mission est de nous assurer que l’utilisateur ne sera
jamais déçu lorsqu’il appuie sur un bouton.
Toutes les cartes possèdent au minimum un bouton de réinitialisation Reset,
et la plupart en ont d’autres, dédiés aux interactions avec l’utilisateur.

micro:bit
La carte BBC micro:bit propose deux boutons utilisateur nommés A et B.
Ils sont incarnés par deux objets définis dans la librairie microbit sous les
noms button_a et button_b. Les méthodes des deux objets sont les mêmes.
Pour que le programme se tienne à l’écoute d’un appui sur un bouton, il
nous faut nous intéresser au concept de boucle de gestion d’événement
(sans oublier d’ajouter une pause, nous verrons pourquoi).
En effet, gérer un capteur d’action utilisateur suppose de se placer en attente
de la survenue d’un événement, ce qui est réalisé en écrivant une boucle
d’événement. Il s’agit d’un groupe d’instructions qui se répète sans cesse
pour attendre puis répondre à la survenue de l’événement. Lorsque
l’événement se produit, il faut le prendre en charge en tenant compte de la
vitesse des événements humains, et non la vitesse de traitement du circuit
informatique. Lorsque vous appuyez sur un bouton, la boucle d’événement
a le temps de faire de nombreux tours pendant la fraction de seconde qu’il
vous a fallu pour appuyer et relâcher. Il faut donc prévoir une pause pour ne
réagir qu’une fois à l’appui unique sur le bouton, et ne pas laisser la boucle
déclencher à répétition les instructions prévues pour réagir à cet appui. La
solution est fort simple : il suffit d’ajouter une instruction de mise en pause
dans la boucle d’événement pour la ralentir afin de ne pas déclencher
plusieurs fois la routine de gestion lorsqu’il n’y a lieu de la déclencher
qu’une fois.
L’exemple qui suit illustre ces deux concepts :
Listing 8.1 : Code source 0801_MIC_boucleven.py

from microbit import *

position = 2

while True: # Boucle d'evenement


sleep(60) # Pause
if button_a.is_pressed():
[Link]()
position = max(0, position - 1)
elif button_b.is_pressed():
[Link]()
position = min(4, position + 1)
display.set_pixel(position, 2, 9)

Dans cet exemple, nous commençons par mettre en place une boucle
perpétuelle pour nous tenir à l’écoute d’un événement tel qu’un appui sur
un bouton. La plus simple boucle perpétuelle consiste à écrire while True :
. Nous installons dans cette boucle un bloc conditionnel qui permet de tester
l’appui sur l’un ou l’autre des deux boutons. Lorsqu’un appui est détecté,
nous modifions la valeur de la variable position pour déplacer le pixel
allumé dans le sens horizontal des X, tout en restant dans la troisième
colonne (colonne numéro 2). Le résultat est que l’appui sur les boutons
permet de déplacer le pixel vers la gauche ou vers la droite. Pour
comprendre aisément l’effet de la pause, il suffit de la mettre en
commentaire (signe #) puis d’essayer le programme.
En effet, cette boucle va trop vite pour la vitesse de réaction d’un humain.
S’il n’y avait pas de pause, la moindre action jetterait le pixel sur le bord
gauche ou droit. L’appui est tellement plus lent que la durée d’exécution
d’un tour de la boucle que le programme a le temps d’enchaîner plusieurs
déplacements. Grâce à la pause, vous n’obtenez qu’un déplacement par
appui. Vous pouvez par exemple modifier le nombre de millisecondes
spécifié en paramètre de la méthode sleep() pour voir à partir de quelle
valeur l’interaction devient inconfortable.
Un bouton est un capteur numérique, au sens où il ne peut être que dans
l’un des deux états appuyé ou relâché. La méthode du bouton nommé
is_pressed() renvoie une valeur de type booléen (soit 0, soit 1 ou bien soit
False, soit True). Mais vous aurez parfois besoin de savoir qu’un bouton a
été appuyé puis relâché avant votre test (à ce moment, le programme était
en train de réaliser une autre action par exemple pour faire défiler du texte).
Dans ce cas, vous pouvez utiliser la méthode was_pressed() qui renvoie
une valeur positive si le bouton a été appuyé au moins une fois depuis le
début du programme, ou depuis votre précédent appel à la même méthode.
Vous pouvez même compter le nombre d’appuis sur un bouton au moyen de
la méthode get_presses(), qui renvoie le décompte et le remet à zéro.
Ces trois méthodes permettent une bonne souplesse dans la gestion des
boutons sur la carte micro:bit. Bien sûr, elles masquent les détails à bas
niveau du fonctionnement de l’interaction avec les boutons.
Le microcontrôleur détecte qu’un bouton est appuyé en mesurant la tension
présente sur la broche d’entrée correspondante. Mais les choses ne sont pas
si simples.
Il faut en effet savoir qu’une broche d’entrée peut être non pas dans un état
parmi deux, mais parmi trois, le troisième étant l’état flottant, indéterminé,
bref inutilisable.
Si la tension présentée sur la broche est égale la tension maximale du circuit
(3,3 volts pour la plupart des cartes), la broche est considérée à l’état haut
(1 ou True). S’il n’y a pas de tension, 0 volt, elle est à l’état bas. Mais pour
que la broche ne puisse être que dans l’un de ces deux états, il faut la forcer
soit à l’état Haut, soit à l’état Bas quand aucun signal n’est présent sur
l’entrée. Sans cette précaution, la tension sur la broche va fluctuer en
fonction des parasites qui sont recueillis dans l’environnement et le
microcontrôleur va détecter des états Haut et Bas sans signification aucune
(N.d.T. : sauf peut-être un message des extra-terrestres ?). En forçant la
broche d’entrée dans l’un des deux états, au niveau matériel, nous
garantissons que la broche aura toujours un état prévisible et stable, même
au repos.

CPX (Circuit Playground Express)


Puisque nous connaissons les particularités essentielles d’une broche
d’entrée, voyons comment exploiter les deux boutons de la carte CPX. Le
script suivant montre que l’on peut exploiter les boutons à un niveau plus
proche du matériel qu’avec la carte micro:bit. L’exemple permet de
contrôler un jeu de lumières en le faisant tourner dans un sens ou dans
l’autre selon le bouton appuyé :

Listing 8.2 : Code source 0802_CPX_boutons.py

import neopixel
import time
import digitalio
from board import NEOPIXEL, BUTTON_A,
BUTTON_B

np = [Link](NEOPIXEL, 10,
auto_write=False)
boutA = [Link](BUTTON_A)
[Link] = [Link]
boutB = [Link](BUTTON_B)
[Link] = [Link]

sensRota = True

while True:
[Link](0.15)
if [Link]:
sensRota = True
elif [Link]:
sensRota = False
for i in range(10):
if sensRota:

i = 9 - i
for j in range(10):
np[j] = tuple((max(0, val -
64) for val in np[j]))
np[i] = (0, 0, 254)
[Link]()

Ce script ressemble beaucoup à celui ce qui nous a permis de tester le ruban


NeoPixels dans le chapitre précédent. La différence la plus visible est
l’utilisation de la librairie digitalio pour créer les deux objets qui incarnent
les boutons A et B.
Les deux boutons sont des instances de la classe DigitalInOut qui définit
toutes les broches numériques pouvant servir en entrée ou en sortie. La
création de chaque instance boutA et boutB fait mention de la broche
concernée, en utilisant une constante écrite en lettres majuscules
(BUTTON_A ou BUTTON_B, définie dans la librairie board). Par défaut,
ce genre d’objet est configuré en lecture d’une entrée, mais vous pouvez
changer cela au moyen des deux méthodes switch_to_output() et
switch_to_input(). Ici, nous n’avons rien à changer. Nous choisissons de
forcer les deux broches à l’état Bas lorsqu’il n’y a pas de signal. Autrement
dit, le signal flottant sera forcé à l’état bas (valeur False). C’est le choix le
plus logique pour un bouton-poussoir : lorsqu’il n’est pas enfoncé, le circuit
est ouvert et il n’y a pas de tension (0 V). La broche ne doit passer à l’état
Haut (True) que si le bouton est sollicité. Pour obtenir cet état, nous
demandons d’obtenir la valeur de l’attribut value de l’objet.
Nous nous servons de cette valeur dans des blocs conditionnels pour régler
la variable qui détermine dans quel sens faire tourner l’effet de lumière,
avec notre variable sensRota.
La carte CPX possède un troisième interrupteur, mais ce n’est pas un
poussoir. Il possède deux positions stables (c’est un inverseur). Nous
pouvons reformuler l’exemple précédent pour utiliser cet interrupteur
comme ceci :

Listing 8.3 : Code source 0803_CPX_switch.py

import neopixel
import time
import digitalio
from board import NEOPIXEL, SLIDE_SWITCH

np = [Link](NEOPIXEL, 10,
auto_write=False)
inverseur =
[Link](SLIDE_SWITCH)
[Link] = [Link]

while True:
[Link](0.05)
for i in range(10):
if [Link]:

i = 9 - i
for j in range(10):
np[j] = tuple((max(0, val -
64) for val in np[j]))
np[i] = (0, 0, 254)
[Link]()

Je vous laisse étudier les détails du fonctionnement en guise d’exercice. Il


n’y a aucun piège, car la technique est la même que celle utilisée pour les
deux poussoirs.

PyBoard
La carte PyBoard propose en plus de son bouton Reset un bouton portant la
légende USR. Son utilisation est un peu différente de celle des boutons des
cartes micro:bit et CPX. Sachez tout d’abord que ce bouton porte le nom
« switch », ce qui correspond plus à un interrupteur qu’à un bouton-
poussoir. L’objet que vous allez créer est défini dans la classe switch :.

Listing 8.4 : Code source 0804_PYB_switch.py

import pyb

led = [Link](1)
sw = [Link]()

while True:
[Link](100)
if sw():
[Link]()

La logique utilisée est proche de celle des deux autres cartes, même si les
détails diffèrent parce que la variante de MicroPython n’est pas la même. Il
y a une boucle de gestion d’événement et un délai. La valeur binaire
(booléenne) du bouton est obtenue en appelant directement l’objet sw. Le
résultat pour l’utilisateur est que la diode LED rouge de la carte PyBoard
est allumée et éteinte alternativement à chaque appui.
La variante de MicroPython pour PyBoard permet d’appliquer une autre
technique pour interagir avec des boutons. Elle consiste à définir une
fonction de rappel (callback). Une fonction de rappel est une fonction qui
est définie par le programmeur, mais jamais appelée par lui directement.
Son exécution est déclenchée par le système lorsque l’événement associé à
cette fonction se produit. Pour détecter l’événement, MicroPython utilise
une interruption. De façon générale, une interruption est un signal que le
microcontrôleur doit prendre en compte en déclenchant une action tout en
suspendant ce qu’il était en train de faire. Dans notre exemple, MicroPython
arme un détecteur qui va déclencher une interruption en l’associant à la
broche à laquelle est relié notre bouton.
Lorsque le bouton est enfoncé, la broche change d’état, en passant de l’état
Bas vers l’état Haut ; le microcontrôleur détecte ce changement et suspend
alors ce qu’il était en train d’exécuter, en sauvegardant les paramètres de
fonctionnement, puis appelle le gestionnaire d’interruption qui a été associé
au bouton. Ce gestionnaire lance l’exécution de votre fonction de rappel,
puis le microcontrôleur est informé que l’interruption a bien été prise en
compte. À partir de ce moment, le microcontrôleur restaure l’état dans
lequel il était au moment de l’interruption et reprend la suite de l’exécution
normale. Le code qui a été suspendu n’a aucune idée qu’il a été interrompu.
Grâce à cette technique d’interruption et de rappel, nous pouvons simplifier
l’exemple précédent ainsi :

Listing 8.5 : Code source 0805_PYB_rappel.py

import pyb

sw = [Link]()

def maFoncRapp():
[Link](1).toggle()
[Link](maFoncRapp)

Dès que vous appuyez sur le bouton, cela provoque l’exécution de la


fonction maFoncRapp(). Le code en cours d’exécution est suspendu. Si
plusieurs interruptions sont déclenchées en même temps, une seule est
prioritaire sur les autres, mais toutes sont prises en compte, dans l’ordre de
leur priorité relative. L’interruption pour un bouton possède la priorité la
plus faible.
Pour supprimer le lien dynamique avec votre fonction de rappel, il suffit
d’écrire l’instruction suivante :

[Link](None)

Les deux cartes micro:bit et CPX permettent d’interagir d’une autre façon
avec vos doigts : par détection du changement de capacité. Le corps humain
est suffisamment volumineux pour avoir une certaine capacité naturelle, en
termes de charge électrique. Il devient ainsi possible de détecter un
changement de capacité sur une broche dès que vous la touchez : vous
modifiez la capacité en transmettant la charge de votre corps.

micro:bit
Sur la carte micro:bit, cette détection tactile n’est possible que sur les
grandes broches 0, 1 et 2. En revanche, sur la carte CPX, toutes les broches
sauf les broches d’alimentation et de masse peuvent servir à détecter un
toucher capacitif.
Comme vous pouvez vous y attendre, programmer une détection de toucher
est très simple sur micro:bit :
Listing 8.6 : Code source 0806_MIC_touch.py

from microbit import display, Image, pin0

while True:
[Link]([Link])
if pin0.is_touched():
[Link]([Link])

Dans l’exemple précédent, nous faisons afficher l’émoticône de visage


heureux (HAPPY) lorsque vous touchez la broche 0 à la place de celle
d’endormi (ASLEEP).

CPX
Avec la carte CPX, vous disposez de la méthode is_touched() pour vérifier
qu’une broche a été touchée. Elle renvoie une valeur booléenne. La
programmation est un peu plus longue sur CPX, mais elle permet d’obtenir
des effets bien plus intéressants :

Listing 8.7 : Code source 0807_CPX_touch.py

import neopixel
import touchio
import digitalio
from board import *

# Bloque la sortie haut-parleur pour


eviter les parasites
spkr =
[Link](SPEAKER_ENABLE)
spkr.switch_to_output()
[Link] = False

np = [Link](NEOPIXEL, 10,
auto_write=False)
touch_a1 = [Link](A1)
touch_a3 = [Link](A3)
touch_a4 = [Link](A4)
touch_a6 = [Link](A6)

while True:
if touch_a4.value:
np[0] = (255, 0, 0)
np[1] = (255, 0, 0)
if touch_a6.value:
np[3] = (0, 255, 0)
np[4] = (0, 255, 0)
if touch_a1.value:
np[5] = (255, 255, 0)
np[6] = (255, 255, 0)
if touch_a3.value:
np[8] = (0, 0, 255)
np[9] = (0, 0, 255)
for j in range(10):
np[j] = tuple((max(0, val - 32)
for val in np[j]))
[Link]()

Le principe du programme est d’allumer deux des diodes LED les plus
proches de la broche que vous touchez. Pour rendre l’effet plus saisissant,
les diodes s’éteignent graduellement lorsque vous éloignez le doigt. Comme
pour les boutons de la même carte CPX, il faut d’abord créer un objet pour
incarner chacune des broches. Ici, nous utilisons la classe TouchIn qui est
définie dans la librairie touchio. Vous fournissez une référence symbolique
à la broche telle que définie dans la librairie board, par exemple A1, A3,
A4 ou A6. Dans la boucle de gestion d’événement, nous prévoyons
plusieurs blocs conditionnels pour tester si l’une des quatre broches choisies
a été touchée ou pas, l’état Haut indiquant qu’elle l’a été. Dans ce cas, nous
allumons la paire de diodes LED voisine.
Rappelons que la broche A0 est également reliée au haut-parleur. Si vous la
touchez, cela va provoquer l’émission d’un craquement sonore désagréable.
C’est pour cette raison que nous avons prévu trois instructions en début de
programme pour créer un objet à partir de digitalInOut en faisant
référence au symbole SPEAKER_ENABLE. En donnant la valeur False à
l’objet qui incarne le haut-parleur, nous le mettons hors fonction, ce qui
supprime tout craquement.
Cet exemple me fait penser au jeu électronique Simon consistant à
mémoriser des séquences de couleurs de plus en plus longues. Sauriez-vous
créer une version de ce jeu sur la carte CPX en utilisant le toucher
capacitif ?

PyBoard
Si la carte PyBoard est équipée de son écran LCD, il devient possible de lui
faire détecter des contacts. Deux méthodes sont très utiles dans ce
domaine : is_touched() qui renvoie une valeur booléenne pour signifier
qu’il y a eu contact sur l’écran et get_patch() qui renvoie une donnée de
type tuple constituée de trois valeurs. La première indique que l’écran a été
touché, et les deux autres correspondent aux coordonnées X et Y du point
de contact. Il devient possible en quelques instructions de créer un
programme pour dessiner avec le doigt.
Listing 8.8 : Code source 0808_PYB_paint.py

import lcd160cr

lcd = lcd160cr.LCD160CR('X')
[Link]()

while True:
a, x, y = lcd.get_touch()

if a:
lcd.set_pixel(x, y, [Link](255,
255, 255))

Il n’est bien sûr pas possible de travailler avec précision sur un aussi petit
écran, et nous ne sommes pour l’instant capables que de dessiner en blanc
sur fond noir (sauriez-vous améliorer ce script ?). Cela dit, c’est un bon
résultat pour sept lignes de code (Figure 8.1).
Figure 8.1 : Le portrait d’un président, mais de quel pays ?
Accéléromètre et boussole
Un téléphone moderne sait à tout moment dans quelle position il est tenu. Il
peut réagir à différents gestes comme le secouement pour déclencher une
action. Cette détection de la position spatiale est devenue indispensable
dans les interactions utilisateur. Lorsque vous tournez votre téléphone,
l’écran tourne d’un quart de tour pour mieux afficher vos photos. Certains
gestes sont associés à certains états, comme le secouement déjà mentionné
ou le fait de poser l’écran contre une table pour arrêter les alertes sonores.
Enfin, si le téléphone est équipé d’une boussole, il peut connaître la
direction dans laquelle il est orienté, ce qui lui permet en combinaison avec
un signal GPS de vous montrer le chemin dans une application de
cartographie, ou de servir directement de boussole.
Toutes ces capacités dépendent de deux composants qui sont
l’accéléromètre et le magnétomètre appelé boussole (compas en anglais).
Un accéléromètre est un appareil électromécanique qui contient une petite
masse montée sur ressort. Cette masse est déplacée par la force de la gravité
ou par une accélération (Einstein a montré que les deux sont équivalentes).
La capacité électrique de la masse varie selon sa distance de la plaque, ce
qui permet de traduire le déplacement mécanique en changement électrique.
Nous exploitons le même phénomène de capacité qu’avec la détection du
toucher capacitif sur les broches. Dans un cas, nous utilisons les propriétés
de stockage d’énergie du corps humain, dans l’autre, le déplacement d’une
masse proche d’une plaque fixe, la masse subissant la force de gravité.
L’accéléromètre qui équipe les trois cartes PyBoard, micro:bit et CPX est en
fait la réunion de trois capteurs pour détecter un déplacement ou une
attraction selon trois axes X (de gauche à droite), Y (en avant et en arrière)
et Z (vers le haut et vers le bas).
Le magnétomètre mesure un champ magnétique (une boussole classique fait
de même de manière simplifiée). Celui de la carte micro:bit est basé sur un
capteur miniature à effet Hall. Il est capable de détecter le champ
magnétique de la terre selon les trois axes X, Y et Z. En sortie du capteur,
nous obtenons une tension proportionnelle à l’intensité et à la polarité du
champ selon chacun des trois axes. Les tensions sont converties en signaux
numériques qui correspondent à l’intensité du champ magnétique. Une fois
que le capteur a été calibré, il devient possible de connaître l’orientation de
l’appareil. Le magnétomètre peut aussi servir de détecteur de métal très
basique.
Les fonctions de librairie API pour les accéléromètres des trois cartes
PyBoard, micro:bit et CPX sont similaires. Elles permettent d’obtenir des
mesures selon les trois axes. L’exemple suivant, destiné à la carte micro:bit,
se sert de l’accéléromètre pour déplacer un pixel enrichi d’une jolie traîne
lumineuse. De nombreux jeux sur cette carte utilisent cette technique
consistant à déplacer un pixel sur l’afficheur :

Listing 8.9 : Code source 0809_MIC_accelero.py

from microbit import *

x = 2
y = 2
seuil = 50
pause = 90
fade = 2

while True:
roulis = accelerometer.get_x()
lacet = accelerometer.get_y()

if roulis < -seuil :


x = max(0, x - 1)
elif roulis > seuil:
x = min(4, x + 1)

if lacet < -seuil:


y = max(0, y - 1)
elif lacet > seuil:
y = min(4, y + 1)

for i in range(5):
for j in range(5):
lumino = max(0,
display.get_pixel(i, j) - fade)
display.set_pixel(i, j, lumino)
display.set_pixel(x, y, 9)
sleep(pause)

Les lignes qui nous intéressent en premier sont celles des appels aux deux
fonctions accelerometer.get_x() et accelerometer.get_y(). Nous
utilisons une variable qui va servir de seuil de sensibilité. Tout mouvement
inférieur à ce seuil n’est pas pris en compte. Dans l’exemple, nous avons
choisi la valeur 50, obtenue en faisant des essais. En effet, l’accéléromètre
est très sensible, et les muscles d’un être humain n’ont pas une précision
suffisante pour ne pas le déclencher de façon involontaire. Grâce à notre
seuil, il faut incliner la carte suffisamment pour que vous ayez le sentiment
d’avoir effectué un mouvement. Dès qu’un matériel doit être mis en
interaction avec un être humain, il est souvent nécessaire de réduire la
sensibilité ou d’additionner des valeurs pour que l’appareil soit pratique à
utiliser.
Le genre d’interactions que propose un tel accéléromètre ressemble à celles
que vous avez pu expérimenter avec une manette de console de jeu afin de
contrôler un personnage ou un véhicule. Les gestes d’inclinaison forment
une métaphore concrète de pilotage d’un autre objet, par exemple la
position d’un pixel. Inversement, un accéléromètre permet de mesurer en
permanence les forces qui s’appliquent à un objet, par exemple un modèle
réduit de fusée.
L’accéléromètre permet également de détecter des gestes tels que le
secouement, la chute libre ou le renversement. Des gestes prédéfinis et
clairement individualisés peuvent être mis en relation avec des commandes
ou des états particuliers. Par exemple, le fait de secouer l’objet peut
correspondre à une réponse négative, pour redémarrer une nouvelle partie.
La chute libre indiquera que l’appareil vient de vous tomber des mains, et
qu’il faut le préparer à un choc. En renversant l’appareil, vous signifiez que
vous ne voulez plus le regarder, puisque vous cachez son afficheur. C’est de
cette manière que fonctionne un téléphone portable qui éteint son écran
lorsque vous l’approchez de votre oreille pour répondre.
Actuellement, seule la carte micro:bit définit une série de gestes. L’exemple
suivant montre comment en utiliser trois :

Listing 8.10 : Code source 0810_MIC_gestes.py

from microbit import *

while True:
if accelerometer.was_gesture('shake'):
[Link]([Link])
elif accelerometer.was_gesture('face
up'):
[Link]([Link])
elif accelerometer.was_gesture('up'):
[Link]([Link])
sleep(100)

Lorsque vous secouez la carte, l’afficheur montre un visage en colère


(ANGRY). Si vous maintenez afficheur tourné vers le haut, il montre un
visage endormi (ASLEEP). Si vous tenez la carte verticalement, elle montre
un visage souriant (HAPPY). La carte micro:bit reconnaît les gestes
prédéfinis suivants :

up,down,left,right,face up,face
down,freefall,3g,6g,8getshake.
La carte micro:bit est également la seule qui dispose d’un magnétomètre. Il
n’est pas très précis, et il faut toujours effectuer d’abord un calibrage en
utilisant la méthode compass().calibrate(). Comme sur certains
téléphones, vous devez par vos gestes remplir tout un cercle sur l’afficheur
en faisant tourner la carte pixel par pixel. Une fois qu’elle est réglée, la
carte micro:bit peut retrouver le nord ou un autre champ magnétique
puissant. Voici par exemple comment faire en sorte que l’afficheur montre
ce qu’il croit être le nord magnétique :

Listing 8.11 : Code source 0811_MIC_boussole.py

from microbit import *

[Link]()

while True:
sleep(100)
aiguille = ((15 - [Link]())
// 30) % 12

[Link](Image.ALL_CLOCKS[aiguille])
Son, lumière et température
Les sons, les lumières et les températures correspondent à peu près aux trois
sens de l’audition, de la vue et du toucher. Bien sûr, les capteurs que nous
allons voir sont loin d’être aussi précis que ceux de l’humain. La carte CPX
possède des capteurs pour ces trois types de signaux.
La détection d’un son se fait classiquement avec un microphone, un
dispositif qui transforme des ondes sonores en signaux électriques. Pour la
carte CPX, le circuit qui réalise cette conversion fonctionne un peu comme
l’accéléromètre : une membrane se met en vibration sous l’effet des ondes
sonores, et cette membrane fait varier une capacité électrique. Le résultat
est que le déplacement mécanique de la membrane est transformé en
variations de tension dans un circuit électrique, cette variation étant ensuite
convertie en un signal numérique.
Pour la détection de la lumière, on peut utiliser une diode LED en sens
inverse. Vous vous souvenez qu’en faisant passer un courant dans le bon
sens à travers une diode LED, on la faisait émettre de la lumière.
Inversement, si on éclaire cette diode LED avec de la lumière, on provoque
le passage d’un courant électrique, dans le sens inverse de celui qui permet
d’allumer la LED. On peut donc détecter une luminosité en mesurant ce
courant. C’est ainsi que fonctionne la détection de lumière sur la carte
micro:bit. Sur la carte CPX, la détection de lumière utilise un
phototransistor, c’est-à-dire un composant conçu uniquement pour détecter
la lumière, au lieu d’utiliser un effet induit comme dans une diode LED à
l’envers. Le résultat est le même : l’énergie lumineuse induit un courant
électrique.
Pour mesurer une température, on utilise une thermistance, c’est-à-dire une
résistance dont la valeur change en fonction de la température. C’est ainsi
que fonctionne la mesure de température sur la carte CPX. Cela dit, la
plupart des microcontrôleurs sur lesquels fonctionne le langage
MicroPython sont dotés d’une diode thermique interne qui sert à surveiller
la température du microcontrôleur (pour le protéger). La tension aux bornes
de cette diode va varier de façon inversement proportionnelle en fonction de
la température. Cette variation permet de mesurer la température du circuit,
et par exemple d’avoir une mesure de la température sur la carte micro:bit.
À partir de ces propriétés des capteurs, voyons comment on peut les
exploiter.
Le prochain exemple montre comment exploiter le microphone de la carte CPX en
utilisant les fonctions API d’une librairie qui n’était pas encore disponible au
moment de l’écriture de ce livre. Normalement, l’interface API devrait avoir évolué
entre-temps si vous procédez à la mise à jour vers la plus récente version de
CircuitPython (visez une version 2.0 au minimum).
L’exemple montre donc les grands principes, mais la version dont vous disposerez
offrira sans doute beaucoup plus de possibilités, et notamment la lecture de flux
audio depuis le système de fichiers, et la possibilité d’enregistrer un échantillon
plus long.

CPX
L’exploitation la plus simple que l’on puisse faire du microphone présent
sur la carte CPX consiste à enregistrer de petites bribes audio en les
stockant dans un tampon mémoire (un buffer). L’exemple qui suit incarne
une chambre d’écho magique. Au démarrage du programme, nous
présentons un compte à rebours visuel avec la guirlande de LED. Toutes les
diodes LED s’allument en vert clair pendant la phase d’enregistrement.
Vous disposez d’environ une seconde. Les diodes s’éteignent puis la carte
rejoue sur le haut-parleur ce qu’elle a enregistré. Si vous appuyez sur le
bouton A pendant la lecture, la lecture se fera au double de la vitesse,
déformant le son avec un effet de dessin animé. Si vous appuyez sur le
bouton B, vous obtenez au contraire un ralentissement, effet que j’appelle
Barry White. Si vous n’appuyez sur aucun bouton, vous entendez répéter le
son qui a été enregistré à sa vitesse nominale.

Listing 8.12 : Code source 0812_CPX_audiorecord.py

import neopixel
import audiobusio
import digitalio
import audioio
import time
from board import *

def kontarbour(np):
""" Affiche un compte a rebours sur
les NeoPixels."""
# Debute dans un etat "off".
[Link]((0, 0, 0))
[Link]()
for i in range(10):
np[i] = (0, 20, 0)
[Link]()
[Link](0.5)
[Link]((0, 128, 0))
[Link]()

def enregistrer():
""" Renvoie un tampon contenant
l'enregistrement."""
buf = bytearray(8000)
with
[Link](MICROPHONE_CLOCK,
MICROPHONE_DATA) as mic:
[Link](buf, len(buf))
return buf

def relire(buf, freq):


"""
Lit le contenu du tampon a une
certaine frequence.
"""
# Prepare le haut-parleur.
speaker_enable =
[Link](SPEAKER_ENABLE)
speaker_enable.switch_to_output(value
= True)
# Lit le tampon.
with [Link](SPEAKER, buf) as
hp:
[Link] = freq
[Link]()
# Bloque pendant la lecture.
while [Link]:
pass

neopixels = [Link](NEOPIXEL,
10, auto_write=False)
boutA = [Link](BUTTON_A)
[Link] = [Link]
boutB = [Link](BUTTON_B)
[Link] = [Link]

kontarbour(neopixels)
tamponAudio = enregistrer()
[Link]((0, 0, 0))
[Link]()

freq = 8000 # Par defaut = vitesse


normale
if [Link]:
freq = 12000 # Bouton A = dessin
anime.
elif [Link]:
freq = 6000 # Bouton B = Barry
White.

relire(tamponAudio, freq)

Intéressons-nous tout d’abord à la fonction d’enregistrement. Nous


commençons par définir un tampon mémoire de type bytearray nommé
buf. Nous l’utilisons dans un objet de la classe PDMIn qui est définie dans la
librairie audiobusio. Pour créer cette instance, nous fournissons une
référence aux deux broches de l’horloge et des données du microphone. La
mention PDM dans le nom de la classe PDMIn signifie Pulse Density
Modulation. Cette technique de modulation de la densité des impulsions
permet de traduire un signal analogique en données binaires. La densité
d’impulsion correspond à l’amplitude du signal sonore ; elle est
proportionnelle au volume sonore. L’objet nommé mic dispose d’une
méthode nommée record() qui sert à stocker dans le tampon les octets
correspondants à la traduction du son entendu par application de la
technique PDM. En fin d’enregistrement, la référence au tampon mémoire
est renvoyée comme valeur de retour de la fonction.
Les trois fonctions que nous définissons, pour enregistrer, relire et
décompter suffisent à faire de la carte CPX un jouet sonore étonnant. Nous
reviendrons sur l’utilisation du haut-parleur de cette carte dans le
Chapitre 11.
En termes de programmation, le capteur de lumière s’utilise différemment
du microphone : nous utilisons une broche d’entrée analogique dont la
tension est proportionnelle à la quantité de lumière détectée. Le principe est
le même pour le capteur de température. Voici un exemple de session
d’interpréteur REPL qui teste ces deux capteurs :

>>> import analogio


>>> from board import *
>>> lumi = [Link](LIGHT)
>>> [Link] # dans un lieeu
sombre
152
>>> [Link] # en plein soleil
12037
>>> temp = [Link](TEMPERATURE)
>>> [Link] # temperature de la
salle
30075
>>> [Link] # apres soufflage
d'air chaud
36405
>>> [Link] # apres
refroidissement de qqs secondes
34208
Vous vous demandez peut-être quoi faire de telles valeurs brutes.
Effectivement, cette cession REPL démontre certaines possibilités, mais les
résultats ne sont pas compréhensibles avec des unités connues. Pour
parvenir à un résultat exploitable, il faut utiliser les fonctions des librairies
qui sont actuellement en cours de développement chez Adafruit
([Link]/adafruit/Adafruit_CircuitPython_Bundle). La réalisation de
ces librairies étant en cours, je ne donnerai pas d’exemple à ce sujet ici. Il
vous suffira de télécharger la plus récente version puis de copier les
librairies dans le système de fichiers de la carte CPX.
Voyons toutefois succinctement comment ces librairies vont être utilisées.
L’exemple qui suit récupère la température en degrés Celsius en
interrogeant par intermittence la carte CPX :

>>> import adafruit_thermistor


>>> import board
>>> thermistor =
adafruit_thermistor.Thermistor([Link]
RATURE, 10000, 10000,
25, 3950)
>>> [Link]
26.60413
Les paramètres de l’objet de la classe Thermistor varient selon le type de
thermistance. L’exemple précédent est valable pour la carte CPX.
Pour la carte micro:bit, il suffit d’écrire l’appel suivant. Notez que la
température mesurée est celle en vigueur à l’intérieur de la puce du
microcontrôleur ; ce n’est pas la température ambiante.

>>> from microbit import temperature


>>> temperature()
24

Sauf si vous lui faites faire des calculs intensifs, le microcontrôleur de la carte
micro:-bit chauffe si peu que sa température interne reste proche de celle de l’air
environnant.
Capteurs externes
Les deux cartes micro:bit et CPX, ainsi que d’une certaine manière la carte
PyBoard, sont dotées dès le départ de plusieurs capteurs, mais dans tous les
cas, vous pouvez en ajouter d’autres en branchant un capteur à des broches
GPIO puis en l’exploitant en MicroPython. Les modalités d’exploitation
d’un capteur externe sont exactement les mêmes que celles présentées dans
ce chapitre pour les capteurs embarqués. La seule différence est que le
capteur externe va être connecté via une ou plusieurs des broches d’entrée-
sortie de la carte.

ESP 8266
Voyons pour finir un exemple très simple pour la carte ESP8266, pas encore
utilisée dans ce chapitre. Il donne le principe général (Figure 8.2).
Figure 8.2 : Détection d’un événement externe avec deux fils.

Branchez un strap dans la broche de masse GND et un autre dans la broche


D5 en laissant l’autre bout libre. Cela nous suffit pour produire le même
signal numérique que l’appui sur un bouton-poussoir. Le code suivant
allume la LED interne à chaque contact entre les deux fils :
from machine import Pin

led = Pin(2, [Link])


bouton = Pin(14, [Link], Pin.PULL_UP)
while True:
[Link]([Link]())

Les deux objets led et bouton sont issus de la même classe Pin. Leur
création précise le numéro de broche et le mode d’utilisation, en sortie pour
la LED et en entrée pour le pseudo-bouton. Pour que la détection du
« bouton » soit fiable, nous demandons l’activation du forçage à l’état Haut
par le troisième paramètre PULL_UP. Pour prouver la détection, nous
donnons à la LED la même valeur que le bouton : si les deux fils se
touchent, l’entrée passe à l’état Haut, ce qui allume la LED.
Après cette découverte des modalités d’affichage, des capteurs et des
broches d’entrée, nous pouvons plonger plus profondément dans
l’exploitation des broches du connecteur d’entrée-sortie GPIO. Nous en
profiterons pour découvrir plusieurs protocoles de communication avec des
périphériques.
CHAPITRE 9
GPIO
Le sigle GPIO (General Purpose Input Output) signifie « entrées-sorties à
usage général ». Il désigne le connecteur sur lequel sont branchés tous les
périphériques permettant de relier le contrôleur au monde extérieur.
Chaque connexion est incarnée par un contact appelé généralement broche
(pin), cette broche étant électriquement reliée à une des pattes de la puce du
microcontrôleur. Avec des instructions en langage MicroPython, vous
pouvez faire apparaître ou pas une tension sur une broche de sortie ou bien
mesurer une tension sur une broche d’entrée, afin de dialoguer avec les
périphériques. Chaque broche est numérotée. En fonction de ses
possibilités, une broche permettra de mettre à disposition certains types de
signaux, ou d’en détecter certains.
Nous allons voir dans ce chapitre comment utiliser les broches de
connexion puis nous passerons en revue trois protocoles de communication
très répandus pour dialoguer avec le monde extérieur : UART, SPI et I2C.
Ces protocoles permettent de standardiser et donc de simplifier les
interactions entre le microcontrôleur et ses périphériques.
Les broches
Le terme broche continue d’être utilisé, même si les cartes qui nous
intéressent dans ce livre n’ont pas toutes des broches de connexion mâles.
Plus généralement, une broche est une zone métallique qui permet d’établir
un contact avec le microcontrôleur. La Figure 9.1 montre par exemple les
contacts plats, appelés néanmoins broches, de la carte micro:bit.

Figure 9.1 : Broches du connecteur plaque de la carte micro:bit.

Dans la figure précédente, aucun des contacts ne ressemble à une broche,


c’est-à-dire un morceau de fil rigide sur lequel on peut enfoncer un
connecteur femelle. Cinq de ces broches sont surdimensionnées et munies
d’un œil, ce qui permet d’y placer une pince crocodile pour réaliser
aisément la connexion. Les autres contacts sont tellement petits que vous
pouvez vous demander comment y réaliser des connexions. La solution
consiste à insérer la carte micro:bit dans un connecteur femelle qui donne
ensuite accès de manière confortable à chacun des petits contacts. Ce
connecteur forme une sorte de station d’accueil, et certains modèles sont
associés à une plaque d’expérimentation sans soudure, comme le montre la
Figure 9.2.
Sur la carte CPX (Circuit Playground Express), tous les contacts peuvent
être utilisés avec des pinces crocodiles. En revanche, la carte PyBoard est
plus classique. Il en existe deux variantes : soit nue (il n’y a que les trous
dans le circuit imprimé dans lesquels vous pouvez souder des fils), soit
prémontée avec des broches femelles dans lesquelles vous pouvez insérer
des fils de connexion avec connecteurs mâles. Enfin, les cartes ESP8266 et
ESP32 sont en général vendues avec des broches mâles soudées sur la carte.
Parmi nos quatre modèles de carte, les ESP sont donc les seules qui
proposent de véritables broches GPIO mâles au sens habituel.
Chaque broche ou contact possède une référence pour pouvoir la distinguer
des autres. Les références sont définies dans des fichiers qui varient selon la
version de MicroPython. Dans celle de la carte micro:bit, les références des
broches sont définies dans la librairie microbit. Dans la carte CPX
combinée à des périphériques Adafruit, elles le sont dans la librairie board.
Enfin, dans les cartes PyBoard et ESP, c’est une classe nommée Pin qu’il
faut utiliser pour créer les objets correspondant aux broches, en précisant
lors de la création certains paramètres, par exemple que la broche va servir
d’entrée numérique.
En général, le circuit imprimé comporte des légendes pour repérer les
broches. Il en existe plusieurs catégories. Les broches d’alimentation
servent à fournir un certain courant électrique sous une tension prédéfinie
afin d’alimenter un périphérique externe. Par exemple, sur la carte
micro:bit, il existe une broche légendée 3V. C’est une broche de sortie qui
fournit une tension continue de 3 V, soit l’équivalent du pôle positif d’une
pile.
L’autre type de broche d’alimentation regroupe toutes les broches de masse,
dont la légende est en général GND (qui signifie ground ou terre). Elles
incarnent l’équivalent du pôle négatif d’une pile. Toutes ces broches
d’alimentation ne servent qu’à fournir de l’énergie électrique et ne sont pas
sous le contrôle de votre programme.
Figure 9.2 : Carte micro:bit insérée dans un connecteur d’extension avec une plaque
d’expérimentation.

Les broches qui nous intéressent sont toutes les autres, car ce sont elles que
nous allons pouvoir exploiter. Toutes ces broches sont évidemment capables
de fonctionner en mode numérique. Votre programme peut forcer une
broche de sortie à présenter une tension nulle ou une tension haute, en
général de 3,3 V (mais parfois aussi 5 V). Certaines broches peuvent être
utilisées en tant que broches analogiques, pour générer ou détecter une
tension située entre les deux bornes extrêmes. Une tension d’entrée
analogique est normalement transmise à un circuit appelé convertisseur
analogique vers numérique (CAN, ou ADC en anglais) qui produit une
valeur numérique. Pour une sortie analogique, c’est l’opération inverse : un
circuit convertisseur numérique vers analogique (CNA, en anglais DAC)
prend une valeur numérique et la convertit en une tension analogique. Il ne
faut pas confondre ce mode de fonctionnement avec le pseudo-mode
analogique que permet le fonctionnement PWM d’une broche numérique,
c’est-à-dire la modulation de largeur d’impulsion (vue dans le Chapitre 7).
Enfin, certaines broches sont en mesure de détecter un changement de
capacité, comme nous l’avons vu dans le Chapitre 8 lorsque nous avons
touché un contact du doigt.
Chacune des broches d’un connecteur GPIO peut être non pas dans un état
d’entrée parmi deux (tension ou pas), mais dans un état parmi trois : l’état
Haut, l’état Bas ou l’état flottant. Ce dernier état est indésirable ; nous
l’évitons en forçant la broche à être par défaut dans un des deux autres états.
L’état flottant résulte de la captation aléatoire des parasites
électromagnétiques qui flottent dans l’environnement de la carte.
Certaines cartes permettent de définir des interruptions qui se déclenchent
lorsque l’état d’une broche change. Le principe consiste à définir une
fonction de rappel (callback) qui va être déclenchée par le gestionnaire
d’interruption. Dans la définition de la fonction, vous indiquez quel type de
changement vous voulez détecter sur quelle broche. Nous avons déjà vu
cela dans le chapitre précédent pour la carte PyBoard. Voici le même
exemple pour une carte ESP8266 :

Listing 9.1 : Code source 0901_ESP8266_interrupt.py

from machine import Pin

def foncRappel(p):
print('Pin', p)
p0 = Pin(0, [Link])
[Link](trigger=Pin.IRQ_FALLING,
handler=foncRappel)

La fonction de rappel est définie par le programmeur. Elle attend en entrée


une référence à un objet qui incarne la broche. Dans l’exemple, cela
correspond à la broche zéro. La condition de déclenchement choisit le
passage de l’état Haut à l’état Bas (Pin.IRQ_FALLING). Rappelons que le
principe d’une interruption est de suspendre l’exécution du programme en
cours pour déclencher la fonction de rappel, avant de reprendre la suite de
l’exécution du programme. Une contrainte importante du gestionnaire
d’interruption est qu’il ne permet pas de réaliser des opérations de
réservation d’espace mémoire. Le code du gestionnaire d’interruption doit
être le plus bref possible.
Si vous testez cet exemple, vous verrez s’afficher le message de
l’instruction print() qui se trouve dans la fonction de rappel lorsque vous
appliquez d’abord une tension à la broche puis coupez cette alimentation.
Lorsque vous mettez en place une communication avec un périphérique en
n’utilisant qu’une seule broche en émission ou en réception, vous établissez
une communication série. Les données sont transmises bit après bit. En
revanche, lorsque vous transmettez la donnée sur plusieurs broches en
parallèle, c’est une communication parallèle. Dès que plusieurs broches
servent à communiquer, on nomme le groupe de broches ainsi formées un
bus.
Même si les légendes sur les circuits permettent de savoir à quoi servent la
plupart des broches, il est souvent nécessaire de consulter le plan de
brochage. La Figure 9.3 montre l’affectation des broches pour la carte
micro:bit.
Figure 9.3 : Plan de brochage de la carte micro:bit.

Vous remarquez qu’à chaque broche correspond un nom et une indication


résumée de sa fonction. Certaines broches ont plusieurs usages, par
exemple pour accéder directement aux diodes LED de l’afficheur. J’ai
décidé de ne pas de fournir les brochages des cartes dans ce livre, car il est
possible qu’ils changent prochainement. Je vous conseille d’aller les
chercher sur le Web. Il suffit de saisir le nom de la carte puis le mot pinout
dans un moteur de recherche.
Connaître les affectations des broches est indispensable, mais de nombreux
périphériques que vous allez raccorder à vos cartes utilisent des protocoles
plus sophistiqués, et ne se limitent pas à des échanges série ou parallèle de
base. Nous allons maintenant découvrir trois des protocoles les plus
répandus. Lorsque vous aurez découvert les principes de chacun d’eux, il ne
devrait pas être difficile de connecter un périphérique puis de consulter sa
fiche technique (disponible sur le site du fabricant) pour bien exploiter le
protocole considéré.
Le protocole série UART
Pour prendre le contrôle du microcontrôleur, vous utilisez l’interpréteur
REPL, en reliant la carte à votre ordinateur grâce au câble USB. La
communication est réalisée par un circuit dédié intégré au microcontrôleur,
le circuit UART (Universal Asynchronous Receiver/Transmitter). C’est ce
circuit qui réalise les conversions entre le mode série et le mode parallèle.
Les messages arrivent à la carte en série, un bit après l’autre (ce sont des
changements entre état Haut et état Bas). Le circuit UART regroupe ces bits
pour former des octets (qui font en général 8 bits). Les octets sont transmis
sur un bus interne pour être exploités par une autre partie du
microcontrôleur. Inversement, pour envoyer un message depuis la carte, le
circuit UART découpe chaque octet en bits qui sont autant de valeurs à
l’état Haut ou Bas.
Ce mode de communication suppose de dédier deux fils aux opérations, un
pour transmettre depuis un appareil A vers un appareil B (la broche
correspondante est en général nommée TX) et un autre fil (généralement
appelé RX) pour recevoir sur A des données depuis l’appareil B.
Plusieurs paramètres doivent être définis pour établir la communication
série. Il faut d’abord convenir d’une vitesse de transmission pour que le
circuit puisse correctement détecter les signaux à l’état Haut et à l’état Bas.
La vitesse de l’échange est exprimée dans l’unité appelée baud. Plusieurs
valeurs standard sont disponibles : 9 600, 14 400, 19 200, 28 800, 38 400,
57 600 et 115 200 bits par seconde.
Il est parfois nécessaire de choisir combien de bits sont regroupés
dans 1 octet, même si au sens strict 1 « octet » (« huit ») devrait
réunir 8 bits, ni plus, ni moins. Vous devez encore décider d’utiliser ou pas
un bit de parité (pour détecter des erreurs de transmission) et choisir le
nombre de bits de stop qui servent à reconnaître la fin de transmission d’un
élément.
Le circuit UART dispose d’une zone mémoire de stockage temporaire. Il
s’agit d’une file d’attente de type FiFo (First In/First Out, premier entré –
premier sorti). Ce tampon permet de stocker des octets lorsqu’ils arrivent
plus vite qu’il est possible de les prendre en compte dans la suite du
traitement.
Par défaut, le circuit UART de toutes les cartes pour MicroPython
fonctionne sur les deux broches nommées TX et RX qui sont connectées au
convertisseur série USB. Autrement dit, le circuit UART se connecte à votre
PC par le port USB. Du côté du PC, vous devez installer une librairie telle
que pySerial ([Link]/pyserial) ou un outil tel que picocom
pour établir une connexion par le port USB, ce qui permet ensuite d’ouvrir
une session d’interpréteur REPL. La vitesse de transmission standard pour
se connecter avec MicroPython de cette façon est égale à 115 200.
Pour communiquer en MicroPython via le circuit UART, il faut donc
commencer par la configuration en spécifiant les broches, la vitesse en
baud, et les autres attributs indiqués ci-dessus. La façon dont chaque carte
exploite et configure le circuit UART varie, mais le principe général reste le
même. Une fois que la liaison est configurée, vous n’avez à gérer dans votre
programme que des octets, et vous pouvez utiliser des instructions standard
telles que read(), readln() ou write(). Cette technique reste la même pour
toutes les cartes. L’exemple suivant, destiné à la carte micro:bit, montre
comment établir une communication, puis lire et écrire vers un PC connecté
par le bus USB série.

Listing 9.2 : Code source 0902_MIC_uart.py

from microbit import *

while True:
msg = [Link]()
if msg:
[Link](msg)

Cet exemple se contente d’afficher les données qu’il a reçues (mode


perroquet). Il utilise la configuration prédéfinie d’UART, et communique
via le port USB. Vous pouvez vous connecter à la carte de la même manière
que pour ouvrir une session REPL ; vous verrez l’afficheur montrer tous les
caractères que vous saisissez. L’exemple est minimaliste, mais il illustre les
grands principes : lecture depuis un tampon mémoire et écriture d’une
réponse.
Il faut préciser que la carte micro:bit crée d’office un objet nommé uart
pour prendre en charge la communication. Sur les autres cartes, c’est à vous
de créer un objet à partir de la classe UART en choisissant la bonne
configuration. Vous devrez sans doute consulter la documentation de la
variante de MicroPython présente sur votre carte. Sachez enfin que le
circuit UART ne se limite pas aux sessions de l’interpréteur REPL et aux
échanges via USB. Il permet de réaliser simplement différentes sortes de
communications entre deux appareils ou plus.
Le protocole SPI
Comme son nom le suggère, l’interface SPI (Serial Peripheral Interface)
est un protocole série qui simplifie les besoins de communication entre
plusieurs périphériques. SPI se distingue singulièrement du standard UART
par plusieurs aspects.
Vous savez qu’UART est un protocole asynchrone, c’est-à-dire qu’il n’y a
pas de signal pour donner le rythme ou synchroniser l’émetteur et le
récepteur des données. Chaque interlocuteur ne connaît que la vitesse de
transmission théorique qui lui a été imposée ; elle doit bien sûr être la même
des deux côtés. Un problème va se poser dès que les deux horloges se
décalent. Si le récepteur prend en compte le signal dans ces conditions, il va
mal interpréter les passages à l’état Haut et à l’état Bas, et les données
seront inutilisables. C’est pour contourner ce problème que l’émetteur d’un
échange UART ajoute des bits de contrôle, et notamment les bits de stop,
pour aider le récepteur à se resynchroniser. Un léger décalage dans la
vitesse des deux interlocuteurs ne constitue en général pas un problème, à
partir du moment où le récepteur réussit à se resynchroniser grâce aux bits
de stop qui suivent chaque octet. Le vrai problème est que cette manière
d’agir augmente la bande passante requise, en obligeant à renvoyer sans
cesse des bits de stop. De plus, cela rend plus complexe la partie matérielle
du circuit UART. Souvent, nous avons besoin de dialoguer avec des
périphériques beaucoup plus simples, qui ne sont donc pas dotés des
fonctions de resynchronisation.
Le protocole SPI a choisi une autre approche : c’est un bus de données
synchrone, et les périphériques sont hiérarchisés.
SPI est synchrone en dédiant un fil au transport d’un signal d’horloge grâce
auquel les appareils savent toujours à quel moment ils doivent prendre en
compte les états Haut ou Bas du fil de données. Cette horloge correspond en
général à la broche nommée SCL (SPI Clock). Toutes les précautions
matérielles et logicielles que l’on doit apprendre dans une communication
UART disparaissent grâce au seul signal d’horloge.
Vous vous demandez peut-être de quel appareil doit provenir le signal
d’horloge de référence, puisqu’il ne doit y en avoir qu’un seul. La réponse
est fournie par le concept de hiérarchie du protocole SPI.
Par convention, un des appareils (en général le microcontrôleur) prend le
rôle d’appareil primaire. C’est lui qui fournit le signal d’horloge ; cet
appareil est le maître, et tous les autres appareils connectés via la liaison
SPI sont ses esclaves.
Personnellement, je n’apprécie pas cette terminologie de maître et d’esclaves, et je
lui préfère les notions de primaire et de secondaire. C’est pourtant la terminologie
en vigueur, et celle que vous trouverez dans toutes les documentations. Je ne vais
donc pas me rebeller, mais je place l’espoir que de futurs ingénieurs choisiront des
termes moins offensants pour décrire les protocoles qu’ils vont inventer.

Alors que le protocole UART utilise les deux liaisons TX et RX, le


protocole SPI parle de MOSI (Master Out, Slave In) et MISO (Master In,
Slave Out). Tous les esclaves émettent et reçoivent les données sur les
mêmes liaisons uniques MOSI et MISO. Il faut donc disposer d’un moyen
pour distinguer chaque esclave. C’est le rôle du quatrième fil, nommé CS
(Chip Select, et parfois SS, Slave Select). Cette liaison sert à sélectionner un
esclave, en lui donnant l’autorisation d’émettre ou de recevoir des données.
Le signal est actif à l’état Bas. Par défaut, la broche CS est maintenue à
l’état Haut. Elle passe à l’état Bas pour autoriser l’esclave correspondant à
prendre la parole. Vous disposez de deux techniques principales pour gérer
cette autorisation.
La Figure 9.4 montre un maître relié par une connexion CS différente à
chacun de ses esclaves. Chaque esclave peut donc être activé par sa liaison
CS. L’inconvénient est que cela oblige à consacrer une broche de l’appareil
maître pour chacun des esclaves.
Figure 9.4 : Configuration SPI avec trois esclaves indépendants.

L’autre mode de connexion consiste à relier les esclaves en cascade, ce qui


permet de n’utiliser qu’un seul fil pour la sélection CS. Tous les esclaves
sont donc sélectionnés en même temps. Comme le montre la Figure 9.5, la
sortie MISO du premier esclave est reliée à l’entrée MOSI du deuxième, et
ainsi de suite, le dernier MISO étant relié au MISO du maître (pas à son
MOSI !). Un esclave qui reçoit des données les retransmet sans aucune
altération lors du train d’impulsions d’horloge suivant. Les données
circulent ainsi en mode série parmi tous les appareils. Quand le signal CS
active les esclaves, chacun peut trouver les données qui lui sont destinées en
se basant sur son adresse dans la séquence. Supposons, comme dans la
figure, trois esclaves, et que chacun attende un octet de données. Nous
envoyons la séquence de 3 octets qui va passer à travers tous les esclaves.
Dès que le signal CS est actif, le premier esclave prend en compte l’octet
qui se trouve en position zéro, le deuxième esclave prend l’octet en
position 1 et le troisième esclave celui en position 2. Cette technique permet
de connecter de nombreux périphériques sans consommer beaucoup de
broches de sortie. Le résultat est équivalent à un circuit élémentaire en
électronique numérique appelé registre à décalage. Ce circuit sert au départ
à effectuer une conversion depuis le mode série vers le mode parallèle.

Figure 9.5 : Configuration SPI avec trois esclaves en cascade.

En regardant les plans de brochage et les légendes sur les cartes, vous
remarquerez les noms des différents signaux SPI (SCLK, MOSI, MISO,
etc.). C’est sur ces broches que vous allez connecter vos appareils SPI.
Dans la mesure où vous seul savez combien d’esclaves vous aurez à relier,
c’est vous qui devez choisir quelles broches dédier à la fonction de
sélection CS. Au niveau de la configuration préalable, il en va comme pour
le circuit UART : chaque circuit diffère légèrement des autres au niveau des
détails de la configuration SPI, même si l’approche générale reste la même.
Vous devez donc consulter la documentation de votre périphérique pour tout
détail, et notamment les fonctions de la librairie. L’exemple de session
d’interpréteur REPL qui suit concerne la carte PyBoard. Elle montre les
étapes essentielles d’une communication par le protocole SPI.

Listing 9.3 : Session REPL sur PyBoard avec SPI.

>>> from machine import SPI, Pin


>>> spi = SPI('X')
>>> cs = Pin('X1', [Link])
>>> [Link](0)
>>> buffer = bytearray(5)
>>> spi.write_readinto(b'hello', buffer)
>>> [Link](1)

L’exemple commence par créer un objet nommé spi, sans oublier de choisir
entre les deux orientations possibles sur la PyBoard, X ou Y. La classe
nommée SPI suppose que l’appareil va devenir le maître. Nous créons
également un objet nommé cs à partir de la classe Pin pour le signal de
sélection de circuit CS. Par convention, le signal est confié à la broche
X1 (voyez le brochage de la carte PyBoard pour savoir où se trouve la
broche). Vous constatez que cette broche est configurée en sortie. Nous
forçons à zéro le signal CS pour sélectionner l’esclave connecté à cette
broche de sélection puis nous créons un tampon mémoire buffer pour y
stocker les données à émettre et à recevoir. Le tampon doit avoir
exactement la même taille que les données à transmettre. Une fois le
message transmis, nous récupérons une réponse dans le même tampon au
moyen de la méthode write_readinto(). Pour finir, nous ramenons le signal
de sélection à 1 pour marquer la fin de l’interaction.
Vous consulterez la documentation du fabricant des esclaves pour savoir
exactement quels octets il faut émettre par le protocole SPI pour les
exploiter correctement. Cela vous sera également indispensable pour savoir
à quoi correspondent les réponses des esclaves. Sachez cependant que la
communauté MicroPython a, dans la plupart des cas, créé une librairie qui
permet d’éviter de plonger dans les détails de gestion de la liaison SPI. Vous
pouvez ainsi vous concentrer sur la logique utile de votre projet. Un bon
exemple est la librairie lcd160cr pour PyBoard déjà utilisée dans un
chapitre précédent pour tester l’écran couleur LCD.
Le protocole I2C
Le troisième et dernier protocole de communication matériel que vous
pourrez utiliser avec MicroPython porte le nom I2C, qui signifie Inter-
Integrated Circuit.
Avons-nous vraiment besoin d’un autre protocole ? Pour le savoir, voyons
quels sont les inconvénients des deux protocoles UART et SPI.
UART ne permet que deux interlocuteurs, sans compter la surcharge
provoquée par la technique asynchrone. Quant au protocole SPI, il vous
oblige à consommer au moins quatre fils, voire plus avec plusieurs esclaves.
Le protocole I2C ne demande que deux fils, comme UART, mais ces deux
fils permettent de gérer plusieurs esclaves, comme SPI. Mieux encore, I2C
permet d’exploiter plusieurs maîtres, qui prennent tour à tour le rôle
principal pour dialoguer avec les différents esclaves connectés au bus.
En revanche, I2C ne peut pas rivaliser avec SPI au niveau de la vitesse de
transmission. Les projets qui ont besoin d’un débit rapide vont en général
opter pour SPI. Les deux protocoles I2C et SPI permettent dans tous les cas
des taux de transfert bien supérieurs à UART.
Le protocole I2C a donc besoin de deux connexions : une pour le signal
d’horloge, appelée SCL, et une pour les données, appelée SDA. Le signal
d’horloge est toujours émis par le circuit qui est maître à ce moment. Au
niveau électrique, les lignes I2C sont dites à drain ouvert, ce qui signifie
qu’elles sont forcées à l’état Haut par défaut. Pour activer un signal, un
appareil doit forcer le signal à l’état Bas, ce qui évite les collisions qui
peuvent se produire lorsqu’un appareil veut forcer le signal à l’état Haut
pendant qu’un autre veut le forcer à l’état Bas (ces situations pourraient
aller jusqu’à provoquer des dégâts réels à un des appareils).
Le protocole est plus complexe qu’UART et SPI. En effet, chaque message
est constitué de deux parties : une partie adresse qui permet de désigner le
destinataire des données qui suivent, puis un ou plusieurs blocs de données.
C’est toujours le maître qui ouvre une communication en forçant à l’état
Haut la ligne SCL, tout en forçant à l’état Bas la ligne SDA. Cette
configuration est détectée par tous les esclaves, qui savent ainsi qu’un
nouveau message va être émis. Le maître émet alors la partie adresse, qui
est une valeur sur 7 bits pour identifier l’esclave désiré, suivi d’un bit pour
indiquer si c’est une lecture (1) ou une écriture (0). Un neuvième bit permet
à l’esclave destinataire de confirmer la réception du bloc d’adresse. Il
réalise cette confirmation en forçant à l’état Bas la ligne SDA, mais il doit
le faire avant la neuvième impulsion d’horloge. S’il n’y parvient pas,
l’échange est interrompu et le maître doit décider comment continuer.
Si la réception du bloc d’adresse a bien été confirmée par le destinataire, les
blocs de données sont transmis de façon synchrone par rapport aux
impulsions de l’horloge du maître transitant par la ligne SCL. Les données
sont transmises sur la ligne SDA, soit par le maître, soit par l’esclave, selon
qu’il s’agit d’une lecture ou d’une écriture. Le nombre de blocs ou de
cadres de données n’est pas limité. La transmission s’arrête lorsque le
maître génère la condition d’arrêt : il commence par forcer de l’état Bas à
l’état Haut la ligne SCL puis la maintient dans cet état Haut, tout en forçant
de l’état Bas à l’état Haut la ligne SDA.
Les modalités de la communication I2C semblent donc plus complexes que
celles du protocole SPI ; c’est vrai au niveau des détails, mais le protocole
est très facile à utiliser dans vos programmes. Comme pour les autres
protocoles, les différences de fonctionnement sont masquées par un
fonctionnement général identique. Voici par exemple une session
d’interpréteur REPL avec la carte PyBoard. Nous supposons que les
connexions sont réalisées pour les deux signaux SCL et SDA en conformité
avec le plan de brochage de cette carte :

Listing 9.4 : Session REPL sur PyBoard avec I2C.

>>> from machine import I2C


>>> i2c = I2C('X')
>>> [Link]()
[46]
>>> [Link](46, b'A')
1
>>> [Link](46, 8)
b'\x00A\x00A\x00A\x00A'
>>> i2c.writeto_mem(46, 0, b'A')
>>> i2c.readfrom_mem(46, 0, 2)
b'\x00A'

Comme dans l’exemple SPI, nous commençons par créer un objet nommé
i2c en précisant l’orientation de la carte. La classe I2C considère par défaut
que l’appareil va être le maître du dialogue. Puisque plusieurs appareils
peuvent être connectés à ce bus I2C, nous utilisons la fonction scan() pour
obtenir leurs adresses. Dans l’exemple, nous n’obtenons qu’une réponse,
pour l’adresse 46. Nous utilisons cette adresse dans la suite du dialogue
pour désigner la cible. Les deux méthodes writeto() et readfrom()
fonctionnent comme vous pouvez vous y attendre : la première envoie un
octet qui symbolise la lettre « A » et la seconde reçoit 8 octets en réponse.
Les deux dernières instructions writeto_mem() et readfrom_mem() montrent
comment écrire puis lire depuis une adresse mémoire particulière, qui est ici
l’adresse zéro, dans une carte référencée ici avec le numéro 46.
Comme pour le protocole SPI, vous n’aurez généralement pas besoin de
gérer directement les détails du protocole I2C, car MicroPython fournit des
librairies de fonctions qui simplifient la programmation. Cela dit, lorsque
vous avez besoin de descendre dans les détails d’I2C, vous tirerez profit de
la lecture des fiches techniques que vous trouverez chez les fabricants des
périphériques concernés. Vous y trouverez aussi la liste des messages qui
permettent de gérer les dialogues.
Autres techniques et protocoles
GPIO
Les techniques présentées dans ce chapitre vous procurent une solide
connaissance de l’exploitation du connecteur d’entrée-sortie GPIO.
Pourtant, vous rencontrerez de temps à autre des techniques moins
répandues. Cette dernière section du chapitre propose de lever le voile sur
ces techniques particulières.
Commençons par la technique de simulation de protocole, appelée en
anglais bit banging. Elle consiste à reproduire par logiciel tous les
comportements d’une interface matérielle pour gérer un protocole, c’est-à-
dire tous les éléments de la synchronisation et des niveaux, ce qui permet
d’obtenir une solution sur mesure, sans achats de composants pour gérer la
communication avec un périphérique. Il s’agit clairement d’une astuce
technique, et elle est particulièrement amusante à exploiter. Si vous
entendez quelqu’un parler de bit banging, sachez que c’est une manière de
se lancer dans du hors-piste pour interagir avec du matériel. Puisqu’il s’agit
d’une technique expérimentale, la simulation de protocole ne peut pas être
décrite plus précisément ici. Sachez qu’il s’agit de tout contrôler par
logiciel, que ce soit la réception de données, la gestion du temps et du
tampon mémoire, et tous les autres aspects du protocole. La mise au point
de ce genre de simulation revient un peu à sonder des eaux troubles jusqu’à
tomber sur un objet. Quand peut-on avoir besoin de ce genre de
contorsions ? En fait, c’est parfois la solution la plus évidente, car elle
simplifie le code ou permet de se passer d’une librairie volumineuse dont
vous n’utilisez que quelques fonctions. C’est parfois indispensable pour
occuper moins d’espace mémoire et consommer moins de ressources de
traitement.
Certains périphériques ne fonctionnent qu’avec un protocole spécifique ; ils
ne reconnaissent ni UART, ni SPI, ni I2C. Par exemple, les rubans de diodes
NeoPixels (également connus sous le nom ws2812) ainsi que la gamme de
doubles capteurs d’humidité/température DHT fonctionnent avec une
interface sur un seul fil. Et ce ne sont que quelques exemples de
composants spécifiques que vous rencontrerez. Dans la plupart des cas,
vous n’aurez pas à vous soucier des détails de mise en œuvre, car
MicroPython possède des librairies de fonctions pour gérer ces
périphériques. Mais, vous rencontrerez de temps à autre un périphérique qui
ne fonctionne selon aucun des protocoles standard, et pour lequel il n’existe
encore aucune librairie. Dans ce cas, vous n’aurez pas d’autre solution que
de relever les manches pour écrire du code de simulation de protocole afin
de pouvoir exploiter le périphérique.
Un des aspects les plus intéressants du contrôle des broches GPIO par
programme avec MicroPython est que vous restez proche du matériel. Les
programmeurs Python sont habitués à travailler à un certain niveau
d’abstraction ; le langage Python s’appuie sur le système d’exploitation
pour lui transmettre ses ordres en direction du matériel. D’un autre côté, les
développeurs pour systèmes embarqués, en adoptant MicroPython,
maintiennent leur relation proche du matériel tout en profitant d’un langage
expressif et de haut niveau qui permet d’obtenir un résultat fonctionnel en
bien moins de temps qu’il n’en faut avec un vrai langage de bas niveau.
En évitant la couche d’abstraction qui vous éloigne du matériel, tout en
profitant de l’expressivité du langage Python, le langage MicroPython peut
exceller en tant qu’outil pédagogique : vous restez suffisamment au contact
du matériel pour profiter d’un contrôle précis sur son fonctionnement, tout
en disposant d’un langage puissant, souple et facile à apprendre. Les
compétences que vous développez en MicroPython sont facilement
réutilisables dans le langage Python normal, ce qui est une preuve de la
capacité de l’écosystème Python à maintenir une excellente continuité à
travers des expériences très différentes.
CHAPITRE 10
Accès réseau
Les cartes sur lesquelles fonctionne MicroPython sont tellement compactes
qu’il serait peu avisé d’y réserver la place requise pour une embase de prise
Ethernet. C’est la raison pour laquelle ces appareils sont presque tous dotés
dès le départ de capacités de communication sans fil. Dans ce chapitre, nous
découvrirons ces possibilités pour les trois cartes Circuit Playground
Express (CPX), BBC micro:bit et ESP8266.
La possibilité de déclencher une action à distance procure toujours une
certaine satisfaction, car cela ressemble à de la magie. Mais mieux encore
que de la magie, c’est tout simplement de la physique ! Quelles sont les lois
physiques exploitées, c’est ce que nous verrons dans ce chapitre d’abord
pour les ondes infrarouges avec la carte CPX, puis avec les ondes
radioélectriques avec les cartes micro:bit et ESP8266/ESP32.
La technique de communication par ondes infrarouges est disponible dans
tous les foyers, puisque c’est celle utilisée pour télécommander nos
téléviseurs. Cette technique fonctionne bien à courte distance, et à condition
qu’il n’y ait pas d’obstacle visuel entre l’émetteur et le récepteur (c’est
pourquoi on oriente la télécommande vers le téléviseur pour lui envoyer un
ordre). Les humains ne peuvent pas voir ces ondes infrarouges, car leur
fréquence est inférieure au minimum détectable par l’homme (une borne de
son spectre lumineux).
Les capteurs photo des téléphones détectent les ondes infrarouges. Pour le vérifier,
il suffit de diriger une télécommande vers votre téléphone tout en prenant une
vidéo. Vous devriez voir clignoter la lumière à chaque appui sur un bouton.

Notre environnement naturel comporte de nombreuses sources d’ondes


infrarouges, à commencer par le soleil, mais aussi les ampoules électriques,
les bougies, et même nos corps qui dissipent leur chaleur dans cette bande
de fréquences. Pour éviter que ces rayonnements naturels perturbent une
communication infrarouge, la technique habituelle consiste à moduler une
porteuse. La fréquence la plus utilisée pour la porteuse à moduler est égale
à 38 kHz, mais d’autres fréquences sont également usitées. Autrement dit,
le transmetteur clignote 38 000 fois par seconde. Il est très peu probable
qu’une autre source infrarouge clignote à la même fréquence, ce qui
garantit au récepteur de pouvoir recevoir les données et rien d’autre. La
modulation est réalisée en jouant sur la durée des impulsions.
La carte CPX d’Adafruit est équipée d’un émetteur et d’un récepteur
infrarouge, ce qui lui permet de dialoguer avec une télécommande de
téléviseur ou de prendre directement le contrôle de ce dernier, mais aussi, et
surtout, de faire communiquer plusieurs cartes.
L’autre moyen principal de communiquer sans fil consiste à utiliser des
ondes radio, c’est-à-dire des radiations électromagnétiques similaires à la
lumière. Ces ondes traversent la plupart des obstacles et offrent une portée
bien supérieure aux ondes infrarouges. Pour transmettre des données,
l’émetteur effectue une modulation d’une des caractéristiques de l’onde :
l’amplitude, la phase ou la fréquence. L’onde émise se propage dans toutes
les directions. Lorsqu’un tel signal rencontre un conducteur électrique, et
notamment une antenne, il y provoque l’apparition d’un très faible courant
alternatif, ce courant pouvant être récupéré puis traité de sorte de retrouver
les données à recevoir.
Les cartes micro:bit et ESP n’exploitent pas les ondes radio de la même
façon. La carte micro:-bit étant orientée pédagogie, ses possibilités de mise
en réseau sont simplifiées au point qu’un collégien doit pouvoir se faire une
représentation mentale de son fonctionnement. À l’opposé, les deux cartes
ESP8266 et ESP32 sont dotées d’une pile de protocoles TCP/IP complète et
communiquent en conformité avec les standards IEEE 802.11 (ceux du Wi-
Fi). Elle permet donc de réaliser des projets s’intégrant à l’Internet des
objets.
Les cartes micro:bit et ESP32 disposent en plus d’une fonction Bluetooth,
et toutes deux offrent la variante à faible consommation BLE (Bluetooth
Low Energy). L’ESP32 est également capable de fonctionner en Bluetooth
standard. Nous ne verrons pas la communication Bluetooth dans ce
chapitre, mais il faut savoir qu’elle est disponible sur certaines cartes. Si
l’on voulait installer les fonctions BLE sur la carte micro:bit, il faudrait
occuper 12 ko des 16 ko de l’espace en mémoire vive RAM et plus
de 100 des 256 ko de la mémoire Flash du programme. Il n’y aurait plus
assez de place pour faire fonctionner MicroPython ! Une poignée de
programmeurs courageux cherchent à concevoir une version de BLE
occupant le moins possible d’espace mémoire, pour faire cohabiter la
fonction avec MicroPython. Il reste à voir s’ils vont réussir à atteindre cet
objectif.
Dans tous les cas, la mise en réseau de ces différents appareils est très
amusante à réaliser parce que cela ressemble à de la magie. Dans la suite du
chapitre, nous allons voir un certain nombre d’exemples qui vous mettront
le pied à l’étrier dans le monde des appareils interconnectés, des réseaux,
des protocoles de l’Internet des objets et bien sûr de MicroPython.
Communications infrarouges CPX
Comme déjà dit, le principe d’une communication infrarouge consiste à
représenter les bits d’une donnée numérique sous forme de présence ou
d’absence d’une impulsion infrarouge à la fréquence de 38 kHz. Pour que
ces données aient du sens, il faut convenir d’un protocole. Il existe une
grande diversité de protocoles pour les appareils infrarouges, mais tous
partagent un même principe : le codage des données est réalisé en jouant sur
la durée entre deux changements du signal. L’excellent Tony DiCola
d’Adafruit a produit un tutoriel vidéo superbe qui montre comment cela
fonctionne et comment capturer les émissions d’une télécommande puis les
rejouer, ce qui permet de prendre le contrôle de votre téléviseur avec votre
système embarqué ([Link]/watch?v=TIbp7DzfOBM).
Le principe est très proche de celui du code Morse qui permet de
transmettre des messages un caractère à la fois, en allumant un signal
pendant différentes durées. Les durées longues correspondent à des traits et
les durées courtes à des points. Chaque caractère est symbolisé par une
combinaison de traits et de points. La lettre A en majuscule correspond à
point-trait. Il y a toujours une pause entre deux caractères, souvent
symbolisée par le signe /.
Dans les deux exemples qui suivent, je vais exploiter le code Morse pour
transmettre un petit texte entre deux cartes CPX. Ce projet n’a pas d’autre
prétention que de montrer comment émettre et recevoir un message
infrarouge avec la carte CPX et le langage CircuitPython.
Un défi majeur va être de bien créer et de bien détecter les points et les
traits, pour pouvoir les distinguer. Au niveau supérieur, nous devrons
pouvoir distinguer les différents caractères, et à un niveau encore supérieur,
le début et la fin d’un message. Les détails font l’objet d’une convention qui
correspond au protocole : il permet de se mettre d’accord sur la
signification du signal. Nous allons décider d’une valeur arbitraire pour
l’unité temporelle de base. Si nous travaillons en microsecondes, nous
pouvons considérer qu’un point correspond à 1 000 µs et un trait
à 2 000 µs. La pause entre deux caractères pourra durer 4 000 µs et la pause
entre deux mots 8 000 µs, répétée une fois.
Pourquoi deux durées à 8 000 µs ? Nous devons détecter les durées lors de chaque
changement de signal entre l’état Haut et l’état Bas, et inversement. Si le signal est
actuellement dans l’état Haut, une durée de 8 000 µs ne pourra pas être distinguée
de la disparition du signal. En répétant cette pause, nous sommes certains d’avoir
détecté au moins une durée de 8 000 µs.

En appliquant ce protocole simple, voyons d’abord comment écrire le code


source pour émettre notre message de bienvenue :

Listing 10.1 : Code source 1001_CPX_ir_send.py

import array
import pulseio
import board

# Table de conversion entre caracteres et


Morse
TAB_CAR_MORSE = {
"A": ".-",
"B": "-...",
"C": "-.-.",
"D": "-..",
"E": ".",
"F": "..-.",
"G": "--.",
"H": "....",
"I": "..",
"J": ".---",
"K": "-.-",
"L": ".-..",
"M": "--",
"N": "-.",
"O": "---",
"P": ".--.",
"Q": "--.-",
"R": ".-.",
"S": "...",
"T": "-",
"U": "..-",
"V": "...-",
"W": ".--",
"X": "-..-",
"Y": "-.--",
"Z": "--..",
"1": ".----",
"2": "..---",
"3": "...--",
"4": "....-",
"5": ".....",
"6": "-....",
"7": "--...",
"8": "---..",
"9": "----.",
"0": "-----",
}

def encoder(msg):
mots = [Link](' ')
tampon_msg = []
for mot in mots:
tampon_msg.extend([8000, 8000, ])
# Nouveau mot
for carac in mot:
tampon_msg.extend([4000])
# Nouveau caractere
for val in
TAB_CAR_MORSE[carac]:
if val == '-':

tampon_msg.extend([2000]) # Trait.
else:

tampon_msg.extend([1000]) # Point.
if mots:
tampon_msg.extend([8000, 8000, ])
# Fin de message
return [Link]('H', tampon_msg)

ir_led = [Link]([Link],
frequency=38000, duty_cycle=2**15)
ir_out = [Link](ir_led)
message = encoder("HELLO WORLD")
ir_out.send(message)

Dans ce code source, nous trouvons d’abord une série de directives pour
importer des librairies puis une table de conversion vers le code Morse.
Nous définissons ensuite une fonction pour traduire la chaîne de caractères
correspondant au message en un tableau compact de valeurs entières non
signées. Ce tableau est ensuite utilisé par un objet de la classe PulseOut
pour émettre le signal, après avoir réalisé la configuration du dispositif.
La partie intéressante concerne la librairie pulseio. Puisque nous voulons
moduler le signal infrarouge, nous nous servons de la classe PWMOut qui
incarne un objet pour produire des impulsions à largeur modulée PWM à la
fréquence demandée (38 kHz) et avec le taux de travail duty_cycle
mentionné. La broche qui correspond à l’émission infrarouge est
symbolisée par REMOTEOUT. Nous nous servons de l’objet correspondant
pour en produire un autre à partir de la classe PulseOut. La méthode send()
de cet objet réalise l’émission proprement dite.
La fonction d’encodage est assez simple puisqu’elle commence par
subdiviser le message en plusieurs mots puis chaque mot en plusieurs
caractères. Nous stockons ensuite les durées successives des points et des
traits en allant chercher les constituants en code Morse correspondant à
chaque caractère. Nous récupérons enfin le tableau produit par la méthode
pour alimenter la méthode d’émission.
Le programme de réception de messages ressemble beaucoup à celui
d’émission. Il définit une table de conversion de Morse vers caractère. Au
lieu d’utiliser la classe pulseOut de la librairie pulseio, nous utilisons la
classe de réception pulseIn. Cela dit, il y a quelques précautions qu’il a
fallu ajouter au code source, comme vous allez le découvrir.

Listing 10.2 : Code source 1002_CPX_ir_receive.py

import array
import pulseio
import board
import time
# Table de conversion entre Morse et
caracteres
TAB_MORSE_CAR = {
".-": "A",
"-...": "B",
"-.-.": "C",
"-..": "D",
".": "E",
"..-.": "F",
"--.": "G",
"....": "H",
"..": "I",
".---": "J",
"-.-": "K",
".-..": "L",
"--": "M",
"-.": "N",
"---": "O",
".--.": "P",
"--.-": "Q",
".-.": "R",
"...": "S",
"-": "T",
"..-": "U",
"...-": "V",
".--": "W",
"-..-": "X",
"-.--": "Y",
"--..": "Z",
".----": "1",
"..---": "2",
"...--": "3",
"....-": "4",
".....": "5",
"-....": "6",
"--...": "7",
"---..": "8",
"----.": "9",
"-----": "0",
}

DUREES = (1000, 2000, 4000, 8000)


def normaliser(brute):
"""
Requantification des durees brutes
"""
for val in brute:
valArrondie = round(val/1000) *
1000
if valArrondie in DUREES:
yield valArrondie

def traduire_morse(elements):
"""
Renvoie le caractere correspondant aux
traits et points Morse
ou bien un point d'interrogation.
"""
return
TAB_MORSE_CAR.get(''.join(elements), "?")

def decoder(valNormales):
"""
Renvoie une chaine en partant d'un
tableau de valeurs normalisees.
"""
# Brise une valeur normalisee en mots,
caracteres et elements.
mots = []
caracs = []
elements = []
for val in valNormales:
if val == 8000:
# Nouveau mot.
if elements:

[Link](traduire_morse(elements))
if caracs:

[Link](''.join(caracs))
elements = []
caracs = []
elif val == 4000:
# Nouveau caractere.
if elements:

[Link](traduire_morse(elements))
elements = []
elif val == 2000:
# Element trait ('-')
[Link]('-')
elif val == 1000:
# Element point ('.')
[Link]('.')
return ' '.join(mots).strip()

ir_in = [Link]([Link],
maxlen=512, idle_state=False)

while True:
while len(ir_in) == 0:
[Link](1)
ir_in.pause()
brutes = [ir_in[i] for i in
range(len(ir_in))]
valNormales = normaliser(brutes)
msg = decoder(valNormales)
if msg:
print(msg)
ir_in.clear()
ir_in.resume()

Nous définissons une fonction de normalisation qui nettoie la durée brute


reçue. En effet, les horloges de deux appareils différents ne seront pas
exactement synchrones, et les durées reçues n’auront pas exactement des
durées égales à 1 000, 2 000, 4 000 ou 8 000. C’est pourquoi la fonction
normaliser() effectue un arrondi au palier de 1 000 µs le plus proche. La
fonction de décodage utilise ces durées pour produire une chaîne de
caractères qui correspond au message original.
L’objet ir_in est une instance de la classe PulseIn. Il est créé en faisant
référence à la broche de réception, en précisant le nombre maximal de
durées élémentaires à stocker en un seul groupe (dans notre cas, 512) et en
déclarant un état de repos par défaut. Au début de la réception d’un nouveau
message, ce qui sera considéré comme le premier signal à prendre en
compte devra être l’état opposé de cet état de repos ou de veille.
Puisqu’il n’y a aucun moyen de savoir à quel moment un message va
commencer, nous mettons en place une boucle de gestion d’événement qui
teste sans cesse si une valeur est arrivée, et se met en pause pour une
seconde s’il n’y en a pas. Dès qu’un message est reçu, le récepteur est mis
en pause pour ne pas continuer à recevoir pendant la phase de traitement de
ce message. La liste nommée brutes est produite par extraction de toutes
les valeurs depuis l’objet ir_in. Les durées sont normalisées puis décodées.
S’il y a effectivement un message, il est affiché, mais il vous faut ouvrir une
session d’interpréteur REPL pour le voir. Finalement, le tampon mémoire
d’entrée de ir_in est vidé pour se tenir prêt au prochain message.
Vous pouvez partir de ces techniques pour créer votre propre protocole, ou
pour programmer avec un protocole existant. Le code Morse n’est
clairement pas le plus efficace, mais il est bien pratique en termes
pédagogiques, car il permet de s’intéresser au concept de durée relative des
signaux infrarouges. Ce mode de communication est facile à mettre en
place, mais n’espérez pas créer ainsi un réseau informatique solide. Pour y
parvenir, mieux vaut se tourner vers les possibilités de communication
radioélectrique que proposent les cartes micro:bit et ESP8266/ESP32.
Communications radio micro:bit
Le circuit d’émission/réception radio de la carte micro:bit propose une
couche de logiciels spécifiques simples à comprendre et consommant très
peu de mémoire et de puissance. En langage MicroPython, ces fonctions
sont regroupées dans la librairie nommée radio de la carte micro:-bit.
La structure de cette librairie est fort simple. Imaginez faire partie d’une
bande d’amis, chacun disposant d’un émetteur/récepteur talkie-walkie.
Avant de vous disperser, vous vous mettez tous d’accord sur le numéro de
canal à utiliser. Une fois que vous êtes à l’écart, vous appuyez sur le bouton
d’émission et parlez. Tous ceux qui sont dans le canal vont vous entendre.
Ce genre de topologie réseau est facile à comprendre par un enfant, parce
qu’il en a en général déjà une expérience pratique !
Au niveau technique, la longueur d’un message est configurable, avec une
valeur standard de 32 octets et une valeur maximale de 251 octets. Les
messages reçus sont stockés dans une file d’attente dont la taille est réglable
aussi. Par défaut, la taille est de 3 ; plus vous l’augmentez, plus vous
consommez de mémoire vive RAM. Lorsque la file d’attente est pleine, les
nouveaux messages sont simplement ignorés. En termes de puissance
d’émission, le réglage par défaut est de 6, la puissance pouvant être réglée
entre 0 et 7. Si vous augmentez la puissance, vous consommez plus
d’énergie, ce qui va vider les piles plus vite. Au niveau du débit, vous
disposez de trois valeurs exprimées en bits par seconde : 250 000, 1 million
(par défaut) ou 2 millions. Le numéro du canal de communication peut être
choisi entre 0 et 100. Les messages peuvent être filtrés en fonction de
l’adresse et du groupe. Cette adresse ressemble à une adresse d’habitation et
le groupe destinataire correspond à un nom, situé à une certaine adresse.
L’interface de programmation API de la librairie radio permet bien sûr de
configurer tous ces paramètres, en plus d’émettre et de recevoir des octets.
Afin de ne pas compliquer la vie à des enfants, la librairie simplifie la
création des chaînes à émettre et à recevoir. L’utilisation du format chaîne
offre une grande souplesse, mais vous pouvez aussi travailler directement
avec des octets si vous avez besoin de plus de contrôle.
La librairie radio consomme de l’énergie ; c’est pourquoi vous devez
l’activer juste avant d’émettre ou de recevoir des messages. Si votre projet
doit fonctionner à la demande, il suffit d’activer la radio juste avant
d’émettre un message puis de l’éteindre le reste du temps. Toujours dans le
souci de rester simple, les deux fonctions correspondantes portent les noms
[Link]() et [Link]().
Pour configurer les paramètres en début de programme, il suffit d’effectuer
un appel à radio. config(). Voici ces paramètres :
length (32 par défaut)
Définit la longueur maximale d’un message en octets, le plafond étant
de 251.
queue (3 par défaut)
Spécifie le nombre maximal de messages à stocker dans la file
d’attente.
channel (7 par défaut)
Règle le canal initial de communication.
power (6 par défaut)
Définit la puissance d’émission du signal entre 0 et 7.
address (0x75626974 par défaut)
Un nom choisi arbitrairement et exprimé sous forme d’une adresse
sur 32 bits pour filtrer les paquets de données entrants.
group (0 par défaut)
Une valeur sur 8 bits entre 0 et 155 à utiliser en combinaison avec le
paramètre address pour filtrer les messages.
data_rate (1 Mb/s par défaut)
Définit la vitesse de transmission.
Si vous craignez que le module radio soit entré dans un état anormal, vous
pouvez restaurer tous les paramètres initiaux par un appel à [Link]().
À partir du moment où vous avez activé le module radio, vous pouvez
émettre une chaîne par un appel à [Link]("Message"). Pour recevoir la
chaîne, vous écrivez quelque chose dans le style msg = [Link]().
Vous obtenez ainsi le premier message trouvé dans la file d’attente, tout en
l’enlevant de la file (afin de faire de la place). Si la file est vide, l’appel à
radio. receive() renvoie la pseudo-valeur None (qui équivaut à 0).
Pour travailler avec des octets au lieu de chaînes, il suffit d’utiliser
radio.send_bytes(message) (la variable message contenant ces octets) et
message = radio.receive_bytes(). La fonction de réception renvoie aussi
la pseudo-valeur None si elle n’a rien trouvé dans la file d’attente. Dans le
cas contraire, elle renvoie le premier message trouvé et l’enlève de la file.
Si vous utilisez des tampons de stockage, vous profiterez de la variante
radio.receive_bytes_ into(tampon). Dans ce cas, le prochain message
trouvé dans la file d’attente d’entrée est copié dans le tampon, en
l’écourtant si nécessaire. La fonction renvoie la pseudo-valeur None s’il n’y
a pas de message à traiter. Si un message a été trouvé, elle renvoie une
valeur entière qui correspond à la longueur de ce message.
Récapitulons ces connaissances avec un exemple simple.

Listing 10.3 : Code source 1003_MIC_radio_hello.py

from microbit import *


import radio

[Link](channel=42)
[Link]()

while True:
sleep(20)
if button_a.was_pressed():
[Link]("Hello")
msg = [Link]()
if msg:
[Link](msg, 80,
wait=False)

Nous commençons par choisir le canal de communication 42, puis nous


activons le module radio. Nous entrons ensuite dans une boucle perpétuelle
qui effectue deux opérations à chaque tour :
1. Si le bouton A a été enfoncé, nous envoyons le message.
2. Si un nouveau message a été reçu, nous le faisons défiler sur
l’afficheur.
Remarquez à quel point la description littérale que nous venons de faire du
fonctionnement de la boucle ressemble à la façon dont le code source
Python est écrit. Cette approche consistant à émettre un message en réaction
à un événement précis puis à vérifier l’arrivée de nouveaux messages se
rencontre dans tous les projets qui exploitent le module radio de la carte
micro:bit.
Cet exemple illustratif n’a pas une grande portée applicative. Découvrons
un projet plus conséquent qui montre à quel point il est simple de réaliser
un programme assez complexe pour exploiter toute une collection de cartes
au sein d’un réseau poste à poste.
Imaginez que vous installiez une carte micro:bit dans un petit boîtier mural
à côté de l’interrupteur de lumière, dans plusieurs pièces de votre
habitation. Les cartes communiquent par des messages que lisent les
personnes présentes dans les pièces. Ce système vous évite par exemple de
devoir crier à gorge déployée qu’il est l’heure de venir à table. Le
programme de chacune des cartes comporte une variable avec le nom de la
carte, afin de savoir laquelle a envoyé chaque message. Pour chaque pièce,
nous définissons une série de phrases types en plus des deux réponses
universelles Oui et Non. Par exemple, le message « Il n’y a plus de papier
WC » ne devra pouvoir être émis que par la carte située dans les toilettes.
Au niveau de l’interface, nous décidons que le bouton A fait passer en revue
les différents messages disponibles. Le message actuel est affiché par
défilement. Le bouton B permet d’envoyer le message sélectionné, tout en
le faisant défiler sur l’afficheur en guise de confirmation.
Lors de la transmission du message, nous plaçons en préfixe le nom de la
carte émettrice.
Toutes les cartes qui reçoivent le message affichent le nom de l’émetteur et
le contenu du message. Certaines cartes peuvent être hors de portée de
l’émetteur. Pour pallier ce souci, les cartes qui reçoivent le message le
retransmettent, ce qui permet au final à toutes les cartes d’être informées.
Bien sûr, du fait que toutes les cartes retransmettent le message, il faut
mettre en place un mécanisme pour éviter les effets d’écho en cascade. Il
suffit d’employer un cache mémoire. Chaque message reçu est associé sous
forme d’une clé à une mention de la date et de l’heure de réception. Les
messages sont supprimés du cache au bout d’un temps prédéterminé.
Autrement dit, si le message est trouvé dans le cache, c’est qu’il a déjà été
traité et doit être ignoré, donc non retransmis. Cette technique est souvent
employée dans les réseaux poste à poste, lorsque tous les membres du
réseau doivent obtenir tous les messages, même les membres
temporairement non connectés. Chaque appareil retransmet
automatiquement les messages aux appareils qui sont dans sa portée.
Voici donc le code source de la première des trois pièces que nous allons
étudier. Il s’agit ici du salon ; le code source Python est largement
commenté.

Listing 10.4 : Code source 1004_MIC_domo_salon.py

import radio
from microbit import *

[Link](length=64)
[Link]()

nom_carte = "Salon"
messages = [
"La TV est disponible.",
"Du bois pour faire du feu.",
"Pensez a nettoyer l'insert.",
"Une flambee ferait du bien !",
"C'est l'heure de l'apero.",
"Oui",
"Non",
]

cache_msg = {}
duree_cache = 1000 * 5 # Soit 5 secondes

position = 0

while True:
sleep(20)
# Vide le cache de messages
heure_actu = running_time()
tab_msgobso = []
# Balayage
for cle, heure_msg in
cache_msg.items():
# Teste l'age du message dans le
cache
if heure_actu > heure_msg +
duree_cache:
tab_msgobso.append(cle)
# Supprime les vieux messages
for stale_message in tab_msgobso:
del cache_msg[stale_message]
# Change de message actuel
if button_a.was_pressed():
position += 1
# Revient en debut de liste si a
la fin
if position == len(messages):
position = 0
# Affiche le nouveau message
actuel
[Link](messages[position],
50, wait=False)
# Envoie le message actuel.
if button_b.was_pressed():
# Le format est "carte:contenu".
[Link]('{}:{}'.format(nom_carte,
messages[position]))
# Attend et affiche message entrant, et
rediffuse
msg = [Link]()
if msg:
if msg not in cache_msg:
# Nouveau message, donc le stocker
cache_msg[msg] = running_time()
# Le rediffuser
[Link](msg)
# L'afficher en defilement
sender, message = [Link](':')
[Link]('{} dit:
{}'.format(sender,message), 50,
wait=False)
Le script commence par régler à 64 octets la longueur des messages puis
allume le module radio. Il définit ensuite le nom du programme puis la liste
des messages qu’il peut émettre. Nous créons ensuite une structure de
données qui va servir de zone de stockage pour le cache, et définissons la
durée de maintien des messages, en choisissant une valeur de cinq
secondes. La préparation se termine par la création d’une variable position
à laquelle nous donnons la valeur 0, ce qui présélectionne le premier
message de la liste.
Nous entrons ensuite dans la grande boucle perpétuelle. Nous vous invitons
à lire les nombreux commentaires qui expliquent comment est géré le cache
mémoire, comment les événements des boutons sont pris en compte et
comment sont traités les messages reçus. Le programme est assez simple, et
si vous le testez avec plusieurs cartes, le résultat sera très amusant.
Voyons maintenant le code source d’une deuxième pièce, par exemple la
salle de bains. Le code source est quasiment le même, sauf les lignes du
début qui définissent le nom de l’appareil et la liste des messages.

Listing 10.5 : Début de 1005_MIC_domo_bains.py

import radio
from microbit import *

[Link](length=64)
[Link]()

nom_carte = "Bains"

messages = [
"On est a court de papier WC.",
"Qui a pris le savon ?",
"Il faudrait deboucher l'evier.",
"Aargh ! Ma brosse a dents ?",
"La douche est libre.",
"Oui",
"Non",
]

# Suite identique au 1004

Voici enfin la variante pour la cuisine.

Listing 10.6 : Début de 1006_MIC_domo_cuisine.py

import radio
from microbit import *

[Link](length=64)
[Link]()

nom_carte = "Cuisine"

messages = [
"A table !",
"Qui veut un express ?",
"On peut vider le lave-vaisselle.",
"On n'a plus de lait.",
"Qui dresse la table?",
"Oui",
"Non",
]
# Suite identique au 1004
Vous songez peut-être déjà à la façon dont vous pourriez vous servir de ce
script. Dans le prochain chapitre, nous allons voir comment gérer les sons et
la musique. Vous pourriez par exemple enrichir le script en ajoutant de la
synthèse vocale ou des vignettes sonores. Vous pouvez aussi vous soucier
de la confidentialité de votre système, car il ne faut pas faire confiance à des
données produites par un utilisateur inconnu avec une autre carte micro:bit.
Comment protéger vos appareils des interférences provoquées par des
systèmes qui sont dans la même portée réseau, mais qui appartiennent à une
tout autre application ?
Il est très amusant de coordonner plusieurs cartes micro:bit, et la nature très
pédagogique de cette carte me pousse à vous présenter une application très
simple, particulièrement appropriée à une salle de classe. Elle produit un
effet visuel que les débutants en programmation adorent : une nuée de
lucioles.
Je rappelle qu’une luciole est un insecte qui émet de la lumière pour
communiquer avec ses collègues. Vous pouvez facilement créer toute une
nuée de lucioles avec des cartes micro:bit sur lesquelles vous installez le
programme suivant :

Listing 10.7 : Code source 1007_MIC_luciole.py

import radio
import random
from microbit import display, Image,
button_a, sleep

# Production des animages de l'animation


flash = [Image().invert()*(i/9) for i in
range(9, -1, -1)]

[Link]()

while True:
# Le bouton A envoie un message
"flash"
if button_a.was_pressed():
[Link]('flash') # Bip
# Lecture de messages entrants
entrant = [Link]()
if entrant == 'flash':
# Si message entrant "flash",
afficher
# l'animation de luciole apres une
courte pause
sleep([Link](50, 350))
[Link](flash, delay=100,
wait=False)
# Rediffuser le message au hasard
apres une pause
if [Link](0, 9) == 0:
sleep(500)
[Link]('flash') # Bip

Je vous laisse lire les commentaires qui parsèment le code source. Pour
résumer, l’appui sur le bouton A d’une carte provoque l’émission d’un
signal flash. Toutes les cartes réceptrices retransmettent ce message.
Lorsqu’une carte reçoit le signal, elle affiche une animation. Au final, on
obtient quelque chose comme une nuée de lucioles qui s’envoient des
signaux les unes les autres. Je rappelle qu’au Royaume-Uni,
environ 1 million de cartes ont été distribuées à tous les collégiens
de 11 ans. Vous imaginez aisément la joie d’une classe de 30 élèves devant
un tel spectacle. L’effet sera encore meilleur dans l’obscurité d’une
discothèque improvisée. Une variante consisterait à remplacer l’appui sur le
bouton A par un geste de secouement, les cartes étant fixées au poignet des
danseurs. Le résultat serait fantastique !
Un dernier aspect des communications radio de la carte micro:bit est la
facilité avec laquelle elle peut dialoguer avec un appareil d’une autre
gamme. Il suffit de brancher une carte micro:-bit prête à recevoir les
messages à votre ordinateur. Le script sur la carte récupère les messages et
les retransmet directement à l’ordinateur via la connexion USB série
(UART). Parallèlement, elle réceptionne les messages de l’ordinateur et les
rediffuse par radio aux cartes du voisinage. Voici le code source sur la carte
micro:bit.

Listing 10.8 : Code source 1008_MIC_recepteur.py

from microbit import *


import radio

[Link]()

while True:
radio_msg = radio.receive_bytes()
if radio_msg:
[Link](radio_msg)
pc_msg = [Link]()
if pc_msg:
radio.send_bytes(pc_msg)

Ce programme très simple sera facile à adapter à vos besoins.


Il vous arrivera peut-être de tomber sur des messages radio qui commencent par
3 octets supplémentaires qui forment toujours la séquence 1, 0, 1. Ces octets
apparaissent seulement lorsque vous mélangez l’émission de chaînes et la réception
d’octets, avec respectivement send() et receive_bytes(). Ces octets
préfixes constituent une technique de contournement qui permet à MicroPython de
devenir compatible avec le protocole d’autres systèmes embarqués utilisant des
ondes radio et voulant communiquer avec une carte micro:bit. Ici, nous ne nous
intéressons qu’au contenu du message, et nous pouvons ignorer ces octets préfixes.
La lecture d’un message en provenance de la connexion USB peut se faire
selon deux modalités. Vous pouvez vous connecter à l’appareil comme pour
ouvrir une session d’interpréteur REPL (REPL utilise le port USB série
pour envoyer et recevoir des caractères avec l’ordinateur). L’autre technique
consiste à écrire un script et à le faire réagir aux messages en provenance de
la carte micro:bit. L’exemple suivant utilise la librairie pySerial pour y
parvenir.

Listing 10.9 : Code source 1009_PC_recepteur.py

"""
Se place en écoute d'une micro:bit et
réagit aux messages reçus.
"""
from [Link].list_ports import
comports as list_serial_ports
from serial import Serial

def trouver_mic():
"""
Cherche le port de l'appareil
connecté.
"""
ports = list_serial_ports()
for port in ports:
# Identifie la carte avec le
fournisseur et l'ID produit
if "VID:PID=0D28:0204" in
port[2].upper():
return port[0]
return None
def obtenir_serial():
"""
Scrute une carte micro:bit et renvoie
un objet serial pour lui parler.
"""
port = trouver_mic()
if port is None:
raise IOError('Carte micro:bit
introuvable.')
return Serial(port, 115200, timeout=1,
parity='N')

serial = obtenir_serial() # Etablit la


connexion série avec la micro:bit.

# Réceptionne des octets et les affiche


while True:
msg = serial.read_all() # msg
contient des octets, pas une chaîne
if msg:
# Ici, vous pouvez scruter msg
pour réagir de façon
# plus précise, par exemple en
utilisant
# [Link](a_reponse) pour le
rediffuser.
print(msg)

Certains programmeurs se sont servis de cette technique pour transformer


une carte micro:bit en manette de jeu sans fil. Voyez par exemple comment
Martin O’Hanlon prend le contrôle de Minecraft avec une carte micro:bit :
[Link]/watch?v=59KqWVwj_Cc.

La carte micro:bit propose donc un bon nombre de possibilités en termes


d’éducation aux réseaux informatiques. Mais pour être vraiment paré à
plonger dans le monde de l’Internet des objets, il faut pouvoir se connecter
à Internet. Autrement dit, il nous faut une carte possédant des capacités Wi-
Fi ; c’est le cas des cartes ESP8266 et ESP32.
Le Wi-Fi d’ESP8266 et ESP32
Tous les exemples qui suivent sont destinés à la carte ESP8266. Je rappelle que
l’adaptation de MicroPython pour l’ESP32 est en cours. Cela dit, les exemples
devraient être valables pour l’ESP32 quasiment sans retouche, car l’interface de
programmation API devrait rester la même.

Lorsque vous réussissez à accéder à Internet avec une carte, tout un monde
s’ouvre à vous : vous disposez de toute la puissance de traitement de ce qui
vous est accessible dans le nuage (cloud), vous profitez des nombreuses
librairies de fonctions, et vous pouvez émettre et recevoir des messages
sans contrainte de distance. C’est tout à fait passionnant, car l’un des
attributs des objets enchantés est justement leur capacité à communiquer
avec des partenaires, des services et des appareils distants. Un exemple
simple est un indicateur du temps qu’il va faire avec une diode LED qui
change de couleur, après interrogation d’un serveur de bulletin
météorologique (en précisant la région désirée).
Le premier défi consiste à réussir à se connecter, ce qui peut se faire de
deux façons : en se connectant en tant que station cliente d’un réseau Wi-Fi
existant, ou bien en configurant la carte comme un point d’accès, ce qui
permet à d’autres appareils de se connecter au réseau Wi-Fi constitué autour
de cette carte. Dans les deux cas, vous utilisez la librairie network, comme
le montre la session REPL suivante :

>>> import network


>>> station = [Link](network.STA_IF)
>>> access_point =
[Link](network.AP_IF)
>>> [Link]()
False
>>> [Link](True)
>>> [Link]()
[(b'WiFi Network Guest Access',
b'61\xc4y\xfd?', 6, -58, 4, 0),
(b'WiFi Network', b'41\xc4y\xfd?', 6, -57,
4, 0)]
>>> access_point.active()
True
>>> access_point.ifconfig()
('[Link]', '[Link]',
'[Link]', '[Link]')
>>>
access_point.ifconfig(dns='[Link]'
)
Vous devez indiquer le nom de l’interface réseau au moment de créer une
instance de la classe WLAN. Dans l’extrait précédent, nous avons créé deux
objets de cette classe : l’objet station créé dans le mode STA_IF (STAtion
InterFace) et l’objet access_point créé dans le mode AP_IF (Access Point
InterFace).
L’objet station incarne un client, ce qui fait qu’il ne connaît pas au départ
les réseaux disponibles. Il débute de ce fait dans l’état inactif. Une fois qu’il
est activé, l’objet peut obtenir la liste des réseaux disponibles sous forme de
plusieurs tuples de données qui contiennent le nom réseau SSID, l’adresse
Mac du point d’accès BSSID, le numéro de canal, l’indication de puissance
de signal RSSI, le mode d’authentification et un drapeau de visibilité.
Cinq valeurs sont possibles pour le mode d’authentification : 0 (réseau ouvert),
1 (WEP), 2 (WPA-PSK), 3 (WPA2-PSK) et 4 (WPA/WPA2-PSK). Le drapeau peut
être égal soit à zéro (réseau visible), soit à un (réseau caché). Pour tout détail, vous
consulterez la documentation de MicroPython ([Link]/micropython-
available-wireless).

L’autre objet, access_point, constitue un réseau Wi-Fi disponible aux autres


cartes. Il commence donc dans l’état actif. Vous utilisez la fonction
ifconfig() pour connaître les paramètres réseau au niveau IP. Vous obtenez
ainsi l’adresse IP, le masque de sous-réseau, l’adresse de la passerelle et
celle du serveur DNS. Vous pouvez intervenir sur ces paramètres en
utilisant la même fonction mais en lui fournissant une valeur nommée,
choisie parmi ip, subnet, gateway et dns, comme dans l’exemple.
Lorsque la carte est utilisée comme point d’accès, elle va présenter un
identifiant SSID dans le style MicroPython-121ce1. Je rappelle que le mot
de passe par défaut est microPythoN avec un N en capitale à la fin.
La première chose à faire est de régler les paramètres Wi-Fi, que votre
appareil serve de point d’accès ou qu’il ait besoin de se connecter à un
réseau Wi-Fi existant.
Pour configurer l’appareil comme point d’accès, vous utiliserez la méthode
nommée config(), comme nous le montrons dans cette session REPL qui
prend la suite de la précédente :

>>> access_point.config('essid')
'MicroPython-121ce1'
>>> access_point.config('authmode')
4
>>>
access_point.config(essid='new_net_name',
password='new_password')
>>> access_point.config('password')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: unknown config param

Pour connaître la valeur actuelle d’un paramètre, vous appelez la méthode


config() en spécifiant le nom du paramètre comme argument. Pour
modifier le paramètre, vous indiquez le nom, le signe égal et la nouvelle
valeur, comme vous pouvez le voir pour le changement du nom de réseau et
du mot de passe ci-dessus. La session se termine par un message d’erreur,
parce que MicroPython refuse d’afficher le mot de passe. Voici les
paramètres auxquels vous avez accès pour configurer votre point d’accès :
mac
Adresse Mac sous forme d’une série d’octets.
essid

Nom de réseau du point d’accès Wi-Fi sous forme d’une chaîne.


Channel

Valeur entière indiquant le canal Wi-Fi.


hidden

Valeur booléenne qui indique si le réseau sera visible ou non.


authmode

Mode d’authentification en vigueur, un de ceux indiqués dans la note


précédente.
Password

Mot de passe sous forme d’une chaîne.


Pensez à personnaliser le nom réseau SSID et le mot de passe le plus tôt possible
lorsque vous utilisez une carte en tant que point d’accès.

Si vous avez besoin de configurer votre appareil en tant que client pour le
connecter à un réseau Wi-Fi existant, vérifiez d’abord qu’il est actif. Vous
pouvez ensuite appeler la méthode connect() :

>>> [Link](True)
>>> [Link]('Network Name',
'password123')
>>> [Link]()
('[Link]', '[Link]',
'[Link]', '[Link]')

Vous constatez que nous utilisons la méthode ifconfig() pour connaître


l’adresse IP de votre appareil.
MicroPython est très prévenant : il mémorise vos paramètres aussi bien
dans le mode station que dans le mode point d’accès ; vous les retrouvez
après chaque redémarrage. Autrement dit, la configuration n’est à réaliser
qu’une fois. Lorsque la carte redémarre, elle tente de se reconnecter au
dernier réseau utilisé ou de se reconfigurer en point d’accès. Lorsque vous
n’avez plus besoin de la fonction, vous pouvez aisément la désactiver ainsi :

>>> access_point.active(False)

Au lieu d’arrêter la fonction, vous pouvez vous contenter de vous


déconnecter :

>>> access_point.disconnect()

Vous disposez de deux possibilités pour tester le statut d’une connexion. La


méthode disconnect() renvoie une valeur booléenne qui décrit l’état. C’est
la valeur True si la carte est connectée à un réseau Wi-Fi et a reçu une
adresse IP. Si la carte est utilisée comme point d’accès, la même méthode
renvoie la valeur True s’il y a au moins un client connecté et la valeur False
sinon. L’autre technique consiste à utiliser la méthode status() qui renvoie
un des statuts possibles pour la connexion sans fil :

STAT_IDLE

Pas de connexion et aucune activité.

STAT_CONNECTING

Connexion en cours d’établissement.

STAT_WRONG_PASSWORD

Échec en raison d’un mot de passe invalide.


STAT_NO_AP_FOUND

Échec de connexion car aucun point d’accès n’a répondu.

STAT_CONNECT_FAIL

Échec suite à une autre raison.

STAT_GOT_IP

Connexion réussie.
Une fois que vous avez établi la connexion, vous pouvez profiter d’Internet.
Lors de la création du circuit ESP8266, Damien avait rendu visite à un
groupe d’amateurs réunis pour la London Python Code Dojo. C’est le genre
d’endroit dans lequel on programme à plusieurs, on se fait des amis et on
apprend les uns des autres. Il avait apporté quelques exemplaires de la carte,
avait fait une présentation rapide de ses possibilités, montrant notamment
comment se connecter à un réseau. Tous les participants n’avaient plus qu’à
s’amuser avec. À la fin de la conférence, tout le monde s’est réuni pour
montrer ce que chacun avait réussi à faire avec la carte. L’exemple que je
vais présenter illustre une mise en réseau simple, telle qu’elle a été réalisée
pour la première fois par des participants à cette réunion, puis améliorée par
Damien lui-même. Un point essentiel est que le code de l’exemple
fonctionne aussi bien avec le langage Python normal, ce qui prouve, si cela
était nécessaire, que MicroPython constitue une adaptation de Python 3
vraiment exhaustive.
Pour établir une connexion Internet, le langage Python définit une librairie
nommée socket. MicroPython possède la même, et nous allons nous en
servir pour prendre contact avec un serveur sur Internet. Si vous connaissez
le nom d’un domaine, vous pouvez obtenir l’adresse IP correspondante,
créer un socket, le connecter à l’adresse IP distante puis afficher toutes les
données que vous recevez en provenance du serveur.
L’exemple suivant montre comment récupérer des données dans une session
d’interpréteur REPL. Nous supposons bien sûr que vous êtes connecté à un
réseau Wi-Fi ayant accès à Internet.

Listing 10.10 : Code source 1010_ESP8266_starwars.py

>>> import socket


>>> addr_info =
[Link]("[Link]
", 23)
>>> addr_info
[(2, 1, 0, '', ('[Link]', 23))]
>>> server_addr = addr_info[0][-1]
>>> s = [Link]()
>>> [Link](server_addr)
>>> while True:
... data = [Link](500)
... print(str(data, 'utf8'), end='')

Vous pouvez remarquer que nous utilisons en début de session la méthode


getaddrinfo() pour récupérer l’adresse IP à partir du nom de domaine.
Cette information est stockée dans le tuple nommé server_addr. Ces
données suffisent pour créer une nouvelle instance de socket qui se
connecte grâce à cette information. Nous entrons ensuite dans une boucle
perpétuelle dans laquelle nous récupérons 500 octets de données à la fois,
données que nous affichons.
Si vous testez le programme, vous devriez voir une sorte d’animation en
mode texte de Star Wars (Figure 10.1). Revoyez l’adresse
[Link]/wiki/ASCII_art#Animated_ASCII_art.
Figure 10.1 : Exemple de ce qui est affiché dans une session REPL avec la version ASCII de Star
Wars.

Vous pouvez non seulement recevoir, mais également émettre des données.
Vous commencez par créer un socket comme auparavant, mais vous utilisez
ensuite la méthode send(). Voici par exemple comment envoyer une requête
pour obtenir le contenu de la page d’accueil du site de MicroPython, en
utilisant la requête http GET :

>>> import socket


>>> addr =
[Link]('[Link]', 80)
[0][-1]
>>> s = [Link]()
>>> [Link](addr)
>>> [Link](b'GET / HTTP/1.1\r\nHost:
[Link]\r\n\r\n')
>>> data = [Link](1000)
>>> [Link]()

À la fin de la session, l’objet nommé data contient le code HTML sous


forme d’octets trouvés à la racine du site Web [Link].
Certains d’entre vous aimeraient sans doute que MicroPython offre la
fameuse librairie requests, présentée comme le « HTTP humanisé »
([Link]/en/master/). Effectivement, cette librairie
permet de réaliser très facilement des requêtes Web en Python. De plus,
dans la mesure où la plupart des cas d’utilisation de cette librairie
concernent des destinations capables de répondre ou de renvoyer des
données au format JSON, ce ne serait peut-être pas trop demander que de
disposer également d’un portage de la librairie json de Python.
En fait, MicroPython propose une librairie nommée urequests (lire micro-
request) qui reprend l’essentiel des fonctions de la fameuse librairie
requests. Cette librairie de fonctions est fournie dans la version de
MicroPython pour ESP8266, avec l’excellente librairie json. Autrement dit,
les sept lignes Python de l’exemple précédent peuvent se ramener aux deux
lignes suivantes :

>>> import urequests as requests


>>> response =
[Link]('[Link]

Dans la mesure où la plupart des interactions de votre code via HTTP


s’adressent à des partenaires qui connaissent le format JSON, il est assez
simple de profiter de services tels que l’interface API Star Wars qui se
trouvent à l’adresse [Link] :

>>> import urequests as requests


>>> response =
[Link]('[Link]
/')
>>> dir(response)
['text', '__init__', '__qualname__',
'close', 'content', 'json', '__module__',
'encoding', 'raw', 'reason', '_cached',
'status_code']
>>> person = [Link]()
>>> person['name']
'Luke Skywalker'
>>> person['homeworld']
'[Link]
>>> person['films']
['[Link]
'[Link]
'[Link]
'[Link]
'[Link]
L’objet nommé response possède la méthode json() qui renvoie un
dictionnaire de données Python que vous exploitez de façon traditionnelle.
Dans la mesure où l’interface API Star Wars est en lecture seule, nous
utilisons le service de conteneur JSON pour montrer comment envoyer des
données via HTTP :

>>> import urequests as requests


>>> import json
>>> data = [Link]({'hello': 'world'})
>>> url =
'[Link]
'
>>> response = [Link](url,
data=data)
>>> [Link]()
{'id': 101}

Envoyer des messages et demander des données est indispensable, mais


vous aurez parfois aussi besoin de répondre à des messages entrants.
MicroPython offre bien une adaptation de la librairie asyncio de Python,
mais elle n’est pas disponible dans la version pour ESP8266, à cause du
manque de place en mémoire. Cela dit, vous pouvez assez facilement
utiliser la librairie socket de Python pour mettre en place un serveur Web
simplifié capable par exemple de renvoyer une représentation au format
JSON de l’état numérique de chacune des broches GPIO de l’appareil.

Listing 10.11 : Code source 1011_ESP8266_webserver.py

import machine
import socket
import json

template = """HTTP/1.1 200 OK


Content-Type: application/json
Content-Length: {length}
Server: MicroPython

{json}"""

pins = [[Link](i, [Link]) for


i in (0,2,4,5,12,13,14,15)]
addr = [Link]('[Link]', 80)
[0][-1]

s = [Link]()
[Link](addr)
[Link](1)

print('En ecoute sur ', addr)

while True:
cl, addr = [Link]()
print('Connexion client depuis ',
addr)
cl_file = [Link]('rwb', 0)
while True:
line = cl_file.readline()
if not line or line == b'\r\n':
break
status = {str(p): [Link]() for p in
pins}
data = [Link](status)
response =
[Link](length=len(data),
json=data)
[Link](response)
[Link]()

Cet exemple constitue un bon point de départ pour créer des serveurs
simples basés sur le circuit ESP8266. En dehors des lignes spécifiques aux
broches, ce n’est rien d’autre que de la programmation de socket standard
en Python. Après avoir mis en place un socket pour se mettre à l’écoute des
connexions entrantes sur le port 80, nous entrons dans une boucle
perpétuelle qui se tient prête à recevoir des connexions. Pour chacune
d’elles, nous lisons la requête jusqu’à détecter une ligne vide. C’est à ce
moment que vous pouvez réaliser le traitement des données reçues, en
explorant par exemple les détails de l’en-tête. Nous obtenons ensuite le
statut de chaque broche, nous le convertissons en une chaîne dans data qui
correspond à une représentation JSON de status. Nous créons une réponse
en injectant les données dans l’élément de réponse HTTP nommé template,
que nous renvoyons au demandeur puis nous refermons la connexion avec
le client.
Voici comment se présenterait le dialogue du point de vue du client dans
une session d’interpréteur REPL Python :

>>> import requests


>>> esp8266_url =
'[Link]
>>> response = [Link](url)
>>> response
<Response [200]>
>>> from pprint import pprint
>>> pprint([Link]())
{'Pin(0)': 1,
'Pin(12)': 1,
'Pin(13)': 1,
'Pin(14)': 1,
'Pin(15)': 0,
'Pin(2)': 1,
'Pin(4)': 1,
'Pin(5)': 1}
Ce comportement devrait sembler familier à tous les programmeurs qui ont
eu à travailler avec des interfaces API standard pour le Web, des points
d’extrémité et des services Web.
Les conditions de fonctionnement particulières de MicroPython, c’est-à-
dire dans des systèmes embarqués ayant des ressources limitées, nous
amènent à terminer ce chapitre en présentant rapidement le protocole de
transmission de données très fréquent dans le monde de l’embarqué :
MQTT.
Le protocole MQTT
Le protocole MQTT (Message Queue Telemetry Transport) est un protocole
d’échange de messages léger de type publier/abonner (souvent abrégé en
pub/sub). Un certain nombre d’appareils clients se connectent à un serveur
centralisé, appelé broker ou courtier, et s’inscrivent comme abonnés à
différents sujets. Ils peuvent également publier des messages dans des
rubriques. En général, plusieurs clients s’abonnent à une même rubrique ;
tous les messages publiés dans cette rubrique sont regroupés et transmis aux
abonnés. Le protocole MQTT et le courtier constituent le mécanisme
unique utilisé par les clients pour se connecter et communiquer. Votre
appareil ESP8266 peut ainsi échanger avec un autre appareil MQTT à
propos d’une rubrique, par exemple dialoguer avec des capteurs de
domotique ou d’autres objets de l’Internet des objets eux aussi connectés au
courtier MQTT.
Les appareils fonctionnant sous MicroPython endossent le rôle de client dans un
réseau MQTT. Un des courtiers open source multiplateformes très utilisés se
nomme Mosquitto, avec deux t ([Link]). J’ai pu le voir en action sur
un Raspberry Pi qui tenait le rôle de courtier caché dans des projets de domotique.

Mosquitto est disponible pour tous les systèmes d’exploitation courants.


Vous consulterez la documentation du produit pour tout détail.
Aucun travail de configuration préalable n’est nécessaire pour jouir d’une
rubrique ; il suffit d’y publier un message. Les conventions de noms des
rubriques sont proches de celles des chemins d’accès URL. Le séparateur
entre plusieurs niveaux est la barre oblique. Cela permet de regrouper des
rubriques dans des thèmes et de faire évoluer les conventions de noms des
nouvelles rubriques. Vos appareils peuvent par exemple utiliser la
convention suivante pour toutes les rubriques concernant des mesures de
température : sensors/NOM_APPAREIL/ temperature. Un appareil peut
ensuite publier des messages dans une rubrique en remplaçant la partie
variable NOM_APPAREIL par un identifiant unique.
Comme déjà dit, les clients s’abonnent à certaines rubriques afin d’obtenir
tous les messages qui y sont publiés. Il est possible d’utiliser les deux
caractères génériques que sont le signe plus et le signe dièse pour s’abonner
en une opération à toutes les rubriques qui répondent à ce filtre.
Le caractère générique plus symbolise un niveau de la hiérarchie. Pour
s’abonner à toutes les mesures de température de tous les appareils quel que
soit leur nom, vous pouvez écrire ceci :

sensors/+/temperature

L’autre caractère générique, le signe #, symbolise tous les niveaux suivants


d’une hiérarchie. Il doit donc toujours apparaître en dernier dans la chaîne
désignant le chemin d’une rubrique. Pour obtenir tous les messages de tous
les appareils pour toutes les mesures, vous écrirez ceci :

sensors/+/#

Le fait qu’un appareil soit connecté au réseau ne signifie pas


automatiquement que cet appareil, le réseau ou la connexion fonctionnent
correctement. Le protocole MQTT définit trois niveaux de qualité de
service (QoS) qui définissent avec quelle ténacité un client ou le courtier
doit réessayer d’acheminer un message. Plus le niveau de qualité demandé
est élevé, plus l’échange est fiable, mais cela augmente les besoins en
ressources :

• 0 Le message n’est transmis qu’une fois sans confirmation.

• 1 Le message est transmis au moins une fois, avec confirmation.

• 2 Le message n’est transmis qu’une fois, mais en utilisant un


couplage en quatre étapes de type handshake pour garantir la fiabilité
de la connexion.
Tous les niveaux de qualité permettent d’envoyer des messages et de
s’abonner. Chaque client choisit donc le niveau de qualité maximale qu’il
peut obtenir. Si par exemple un client publie des messages dans une
rubrique avec le niveau deux, le client B abonné à la même rubrique avec le
niveau de qualité zéro n’obtiendra ces messages qu’avec ce niveau zéro,
même si le message a été transmis avec un niveau deux au départ. De plus,
si le client A s’est abonné à cette rubrique avec le niveau deux et que le
client B publie un message au niveau zéro, le client A n’obtiendra ces
données qu’au niveau zéro. Autrement dit, le niveau de qualité du message
n’est pas augmenté au niveau de l’abonnement si celui-ci est supérieur.
Un courtier peut décider de retenir un message, même si tous les abonnés
actuellement connectés l’ont reçu. En revanche, si un nouvel abonné
apparaît pour la rubrique du message retenu, ce dernier lui sera envoyé.
Lorsqu’un client se connecte, il peut choisir l’état d’un drapeau de
nettoyage de session (clean session). S’il lui donne la valeur False, la
session va perdurer. Si le client est déconnecté, les abonnements sont
mémorisés et tous les messages suivants de niveau de qualité un ou deux
sont maintenus jusqu’à ce que le client parvienne à se reconnecter. En
revanche, s’il donne la valeur True au drapeau, les contenus d’abonnement
sont supprimés lors de la déconnexion.
Enfin et pour parer à toutes les éventualités, MQTT reconnaît le concept de
dernière volonté (will). Un client peut exprimer ce genre de volonté
lorsqu’il se connecte auprès d’un courtier. Il s’agit d’un message spécial,
relatif à une rubrique arbitraire, avec un niveau de qualité et un état de
retenue comme tout autre message. Sa particularité est que le message ne
sera transmis que si le client est déconnecté de façon imprévue du courtier
(connexion morte).
La version de MicroPython pour l’ESP8266 possède une petite librairie
pour MQTT qui porte le nom umqtt (à prononcer micro-mqtt). Elle offre la
plupart des fonctions du protocole, et notamment la publication et
l’abonnement via un objet client unique. La gestion des messages provenant
des rubriques est réalisée en définissant une fonction de rappel (callback).
Seuls les niveaux de qualité zéro et un sont reconnus par umqtt pour
maintenir le code suffisamment compact.
La connexion avec un courtier est incarnée par une instance de la classe
MQTTClient. Elle définit toutes les méthodes requises pour dialoguer avec le
courtier. L’exemple suivant montre comment publier dans une rubrique,
l’opération étant déclenchée dans l’exemple lorsque l’on appuie sur le
bouton Flash.

Listing 10.12 : Code source 1012_ESP8266_mqtt_publish.py

import time
import ubinascii
import machine
from [Link] import MQTTClient

button = [Link](0, [Link])

broker_address = '[Link]'
client_id =
'esp8266_{}'.format([Link](mach
ine.unique_id()))
topic = b'button'

client = MQTTClient(client_id,
broker_address)
client.set_last_will(topic, b'dead')
[Link]()

while True:
while True:
if [Link]() == 0:
break
time.sleep_ms(20)
[Link](topic, b'toggled')
time.sleep_ms(200)
[Link]()

Remarquez que l’identifiant client_id est produit en soumettant


l’identifiant unique_id de la carte à une conversion vers la notation
hexadécimale de type hexlify. La rubrique et le message sont représentés
sous forme d’octets ; vous démarrez et arrêtez une session en appelant
respectivement connect() et disconnect(). Le drapeau de nettoyage de
session peut être forcé à l’état False en ajoutant le paramètre
clean_session=False dans la méthode connect() (le drapeau possède la
valeur True par défaut).
Pour publier un message dans une rubrique, vous utilisez la méthode
publish() qui attend en entrée le nom de la rubrique et le message, sous
forme d’octets. Elle accepte des options supplémentaires telles que retain
(False par défaut) et qos (zéro par défaut, mais vous pouvez monter au
niveau un, en vous souvenant que le niveau deux n’est pas supporté dans
cette version).
Enfin, la méthode de « dernière volonté » set_last_will() utilise
exactement la même signature de paramètres que la méthode publish().
Voici donc comment très simplement s’abonner à une rubrique :

Listing 10.13 : Code source 1013_ESP8266_mqtt_subscribe.py

import time
import ubinascii
import machine
from [Link] import MQTTClient

def callback(topic, message):


"""
Fonction de rappel qui traite les
messages entrants
"""
print((topic, message))

broker_address = '[Link]'
client_id =
'esp8266_{}'.format([Link](mach
ine.unique_id()))
topic = b'button'

client = MQTTClient(client_id,
broker_address)
client.set_callback(callback)
[Link]()
[Link](topic)

while True:
client.wait_msg()

Ce script ressemble beaucoup au précédent qui servait à publier. Parmi les


différences, vous notez la définition d’une fonction de rappel pour faire
quelque chose avec la rubrique et le message reçus. Nous utilisons la
méthode set_callback() pour créer la liaison logique entre la fonction de
rappel et les abonnements du client. La méthode subscribe() permet de
définir un abonnement à une rubrique et la méthode bloquante wait_msg(),
installée dans une boucle, se tient à l’écoute d’une mise à jour. Vous pouvez
remplacer cette méthode par la variante non bloquante check_msg(). Vous
préférez celle-ci si votre programme doit effectuer d’autres traitements au
premier plan pendant la scrutation. Notez enfin que vous pouvez choisir un
niveau de qualité zéro ou un en fournissant l’argument nommé qos dans la
méthode subscribe().
CHAPITRE 11
Paroles et musique
Le son est un média étonnant et intrigant. Il permet d’envoyer un signal (par
exemple avec une sonnerie de téléphone), de créer une œuvre d’art avec de
la musique et bien sûr de communiquer par la parole. Dès qu’un appareil est
capable d’émettre des sons, il peut répondre à ces trois besoins : signaler,
faire de la musique et parler. Enfin, il est très agréable de meubler l’espace
auditif avec des bips et des blops.
Trois de nos quatre cartes sont immédiatement capables de produire du son.
La carte CPX (Circuit Playground eXpress) est dotée d’un haut-parleur, la
carte PyBoard peut être complétée par une carte d’extension AMP Audio
Skin et la carte micro:bit dispose d’une librairie sonore, la seule condition
étant de connecter un haut-parleur à l’une de ses broches GPIO et à la
masse.
Nous allons voir dans ce chapitre comment produire du son, de la musique
et de la parole synthétique en MicroPython.
Ondes sonores et CPX
Comme tout dispositif qui produit du son, le haut-parleur de la carte CPX
produit des vibrations dans l’air. La façon la plus élémentaire de faire
produire ces vibrations consiste tout simplement à mettre sous tension et
hors tension ce périphérique à une fréquence suffisante. La fréquence
d’allumage est directement proportionnelle à la fréquence du son émis. Le
premier exemple qui suit montre comment produire un bip pendant deux
secondes :

Listing 11.1 : Code source 1101_CPX_premier_cri.py

import audioio
import array
import time
import digitalio
from board import SPEAKER, SPEAKER_ENABLE
# Active le haut-parleur
hp_actif =
[Link](SPEAKER_ENABLE)
hp_actif.switch_to_output(value=True)

duree = 2
longueur = 8000 // 1760
onde = [Link]("H", [0] * longueur)
onde[0] = int(2 ** 15 - 1)

with [Link](SPEAKER, onde) as


hp:
[Link](loop=True)
[Link](duree)
[Link]()

Vous modifiez la durée d’émission du signal en intervenant sur la valeur de


la variable duree (sans accent). La production du son est réalisée dans les
quatre dernières lignes. Nous créons un objet hp qui joue de façon continue
l’onde qui lui est transmise. Pour arrêter l’émission, nous appelons la
méthode stop() après avoir laissé émettre le son pendant la durée
demandée, pour nous placer en pause.
Cette technique très simple pour émettre des sons suffit à produire quelque
chose de vraiment utile : un métronome. Dans l’exemple, nous essayons
d’associer les clignotements des diodes NeoPixels à ce rythme. Vous
accélérez et ralentissez le tempo avec les boutons gauche et droit. Vous
appuyez sur les deux boutons à la fois pour revenir au tempo initial
de 120 battements par minute (BPM).

Listing 11.2 : Code source 1102_CPX_metronome.py

# 1102_CPX_metronome.py
import neopixel
import audioio
import digitalio
import array
import time
from board import *

np = [Link](NEOPIXEL, 10)
moinsvite =
[Link](BUTTON_A)
[Link] = [Link]
plusvite =
[Link](BUTTON_B)
[Link] = [Link]

longueur = 8000 // 1760


onde = [Link]("H", [0] * longueur)
onde[0] = int(2 ** 16 - 1)

hp_actif =
[Link](SPEAKER_ENABLE)
hp_actif.switch_to_output(value=True)
hp = [Link](SPEAKER, onde)
duree_blip = 0.02
tempo_moyen = 0.48
tempo = tempo_moyen
changement_tempo = 0.02

while True:
if [Link] and [Link]:
tempo = tempo_moyen
elif [Link]:
tempo = min(tempo +
changement_tempo, 2.98)
elif [Link]:
tempo = max(tempo -
changement_tempo, 0.02)
[Link]((0, 255, 0))
[Link]()
[Link](loop=True)
[Link](duree_blip)
[Link]()
[Link]((0, 0, 0))
[Link]()
[Link](tempo)

Le programme émet le son et allume les diodes simultanément pendant la


durée définie, qui est réglable par les deux boutons. La partie relative à la
production du son peut être extraite du script afin d’obtenir un module
complémentaire qui définit la fonction que nous appellerons bruire(). Vous
pouvez stocker ce code source dans un fichier portant le nom [Link],
fichier que vous transférerez dans le système de fichiers de la carte CPX.

Listing 11.3 : Module complémentaire [Link]

"""
A copier comme module sur la carte Circuit
Playground Express.
"""
import audioio
import digitalio
import array
import time
from board import *

hp_actif =
[Link](SPEAKER_ENABLE)
hp_actif.switch_to_output(value=True)

def bruire(hauteur, duree):


longueur = 8000 // hauteur
onde = [Link]("H", [0] *
longueur)
onde[0]=int(2**16- 1)
with [Link](SPEAKER, onde)
as hp:
[Link](loop=True)
[Link](duree - 0.01)
[Link]()
[Link](0.01) # Silence de
separation

Dans la dernière ligne, j’ai ajouté une toute petite pause pour marquer un
silence afin d’être toujours certain d’entendre le début de chaque nouvelle
émission. Sans cette précaution, nous n’entendrions plus qu’un son continu
si nous appelons la fonction plusieurs fois avec la même fréquence, au lieu
d’entendre des notes séparées les unes des autres par ce petit silence.
La hauteur du son est directement liée au nombre de passages de la sortie
vers le haut-parleur entre l’état Haut et l’état Bas. C’est la même technique
PWM utilisée pour contrôler la luminosité des diodes LED. Nous
définissons une variable longueur qui correspond à la fréquence
d’échantillonnage en sortie que nous divisons par la hauteur de note désirée,
exprimée en hertz. Nous créons ensuite un tampon mémoire qui est le
tableau onde. Il est rempli avec des éléments longueur qui sont des valeurs
entières non signées égales à zéro. Le premier élément de ce tampon est
ensuite rendu équivalent à l’état Haut. De ce fait, la membrane du haut-
parleur se déplace (un aller-retour) une fois par tour de boucle, car le
contenu du tampon est exploité une fois par tour. Le résultat est la
production d’un son à la fréquence demandée. Le haut-parleur émet le son
indéfiniment.
La hauteur de la note détermine la longueur du tampon, et donc la
fréquence des changements d’état. Si vous demandez une note plus grave,
les changements d’état vont se produire moins fréquemment, tout
simplement parce que la boucle va prendre plus de temps pour faire un tour.
La membrane du haut-parleur va donc vibrer moins souvent, ce qui donnera
une note plus grave. Cette technique très simple permet de produire des
sons musicaux, et de retrouver l’état d’esprit dans lequel le grand
philosophe et mathématicien grec Pythagore a découvert les lois de la
musique.
Pythagore est très connu pour son théorème concernant les triangles
rectangles, mais il était fasciné par tous les aspects des mathématiques qu’il
trouvait dans la nature. Il a même fondé une secte de mathématiciens dans
laquelle il était interdit de manger de la viande et des haricots.
Une de ses découvertes mathématiques à propos du son a été de trouver les
proportions entre différentes hauteurs. Lorsque vous grattez une corde
vibrante, elle produit toujours la même hauteur. Pythagore a remarqué que
cette hauteur était liée à la longueur de la corde, et que deux cordes de
longueur différente produisaient des sons qui étaient dans un certain rapport
l’une avec l’autre. Si une première corde est deux fois plus longue qu’une
autre, elle va produire un son deux fois plus grave, c’est-à-dire à une octave
de différence, ce qui correspond à la proportion 2:1. Si une corde est une
fois et demie plus longue que l’autre, l’accord produit par les deux cordes
jouées simultanément se nomme une quinte parfaite. En travaillant avec les
différentes proportions disponibles, on finit par obtenir une gamme. Ce qui
est excellent pour notre cas est que ces proportions sont directement
utilisables pour les valeurs que nous fournissons en tant que fréquences à
notre fonction de génération de son.
La session d’interpréteur REPL qui suit demande l’émission de plusieurs
hauteurs différentes. Elle suppose que vous avez transféré le module
complémentaire [Link] sur la carte :

>>> from music import bruire


>>> a = 440 # Le LA du diapason
>>> bruire(a, 5) # Le jouer pendant
5 secondes
>>> bruire(a // 2, 5) # Le LA une octave
plus grave
>>> bruire(a * 2, 5) # Le LA une octave
plus aigu
>>> bruire(int(a * 1.5), 5) # Un
RE(quinte juste dessous)
>>> bruire(int(a * 0.66), 5) # Un MI
(quinte juste dessus)
En progressant de quinte en quinte, on obtient toutes les notes de la gamme
chromatique à 12 tons utilisée en Occident. Lorsque l’on connaît la
fréquence de la note MI (E en anglais), la quinte parfaite plus aiguë qu’elle
est un SI (B en anglais). Lorsque la note obtenue est trop aiguë, il suffit de
doubler la longueur pour redescendre d’une octave. En ramenant chaque
note successive dans la même octave que la note précédente, j’obtiens les
valeurs approximatives suivantes pour la gamme pour les 12 notes :

notes = {
'b': 493,
'a#': 466,
'a': 440,
'g#': 415,
'g': 392,
'f#': 370,
'f': 347,
'e': 330,
'd#': 311,
'd': 294,
'c#': 277,
'c': 262,
}

Plusieurs valeurs ont été arrondies. Les fréquences exactes produites par le cercle
des quintes est un sujet complexe mais fascinant qui combine musique,
mathématiques et physique.

Pour changer d’octave, il suffit donc de doubler ou de diviser par deux la


valeur désirée. Les fréquences trouvées suffisent à définir un langage
spécialisé pour la musique (un DSL), comme le montre le script suivant.
Notez que dans ce script, nous avons réintégré la fonction bruire() et
l’activation du haut-parleur.

Listing 11.4 : Code source 1104_CPX_frere_jacques.py

import audioio
import digitalio
import array
import time
from board import SPEAKER, SPEAKER_ENABLE
# Activation du haut-parleur
hp_actif =
[Link](SPEAKER_ENABLE)
hp_actif.switch_to_output(value=True)

notes = {
'b': 493,
'a#': 466,
'a': 440,
'g#': 415,
'g': 392,
'f#': 370,
'f': 347,
'e': 330,
'd#': 311,
'd': 294,
'c#': 277,
'c': 262,
}

def bruire(hauteur, duree):


longueur = 8000 // hauteur
onde = [Link]("H", [0] *
longueur)
onde[0]=int(2**16- 1)
with [Link](SPEAKER, onde)
as hp:
[Link](loop=True)
[Link](duree - 0.01)
[Link]()
[Link](0.01) # Petit silence

def jouer(partoche):
fornoteinpartoche:
nom, duree = [Link](':')
bruire(notes[nom], int(duree) / 8)

ligne1 = ['c:4', 'd:4', 'e:4', 'c:4']


ligne2 = ['e:4', 'f:4', 'g:8']
ligne3 = ['g:2', 'a:2', 'g:2', 'f:2',
'e:4', 'c:4']
ligne4 = ['c:4', 'g:4', 'c:8']
frere_jacques = ligne1 * 2 + ligne2 * 2 +
ligne3 * 2 + ligne4 * 2

jouer(frere_jacques)

La fonction jouer() reçoit en entrée une liste de notes, chacune obéissant


au format nom_note :-durée. Le nom de la note doit être l’un des noms
servant de clé dans la structure de donnée de type dictionnaire nommée
notes qui établit les correspondances entre notes et hauteurs. Le paramètre
de durée définit la durée d’émission de chaque note (comme des noires et
des croches). Remarquez que cette durée est divisée par 8, ce qui permet au
programmeur d’indiquer des valeurs entières pour les durées, et non des
fractions de seconde plus malaisées. À la fin du programme, nous
définissons quatre phrases musicales, chacune répétée deux fois. Le résultat
devrait vous rappeler quelque chose...
Vous avez peut-être déjà une idée de la façon dont vous pourriez enrichir ce
langage musical ? Quelles fonctions aimeriez-vous ajouter ? Vous pouvez
commencer à y réfléchir, car nous y reviendrons lorsque nous découvrirons
comment faire de la musique avec la carte micro:bit dans la suite du
chapitre.
Les sons émis jusqu’à présent sont tous des ondes carrées, résultat du
passage brutal de l’état Haut à l’état Bas, mais il est possible de faire
émettre d’autres formes d’ondes pour changer le timbre du son. C’est un
peu comme si vous jouiez les mêmes notes avec des instruments de
musique différents. La forme d’onde détermine la couleur sonore, c’est-à-
dire le timbre. La Figure 11.1 illustre quatre ondes sonores élémentaires.

Figure 11.1 : Quatre formes d’ondes qui produisent quatre timbres différents : sinusoïde, carré,
triangle et dent de scie.

Les quatre ondes ont la même période, c’est-à-dire qu’un cycle dure autant
pour les quatre. La période est inverse de la fréquence, et la note sera
toujours à la même hauteur. La qualité sonore va varier parce que les détails
du passage des états Haut aux états Bas varient.
L’exemple suivant montre comment créer puis faire jouer chacune des
quatre formes d’ondes de la Figure 11.1.

Listing 11.5 : Code source 1105_CPX_ondes.py

import audioio
import digitalio
import time
import array
import math
from board import SPEAKER, SPEAKER_ENABLE

hp_actif =
[Link](SPEAKER_ENABLE)
hp_actif.switch_to_output(value=True)
long = 8000 // 440
sinus = [Link]("H", [0] * long)
triangle = [Link]("H", [0] * long)
dan2si = [Link]("H", [0] * long)
carre = [Link]("H", [0] * long)

# Production des formes d'onde


for i in range(long):
sinus[i] = int([Link]([Link] * 2 *
i / long) * (2 ** 15 - 1) + (2 **
15))
triangle[i] = abs(int(i * ((2 ** 15 -
1) // long)) - 2 ** 14)
dan2si[i] = int(i * ((2 ** 15 - 1)
// long))
if i < long // 2:
carre[i] = (2 ** 16 -1)
# Lecture
print("Sinus")
with [Link](SPEAKER, sinus) as
echanti:
[Link](loop=True)
[Link](2)
[Link]()

print("Triangle")
with [Link](SPEAKER, triangle)
as echanti:
[Link](loop=True)
[Link](2)
[Link]()

print("Dents de scie (Sawtooth)")


with [Link](SPEAKER, dan2si) as
echanti:
[Link](loop=True)
[Link](2)
[Link]()

print("Carré")
with [Link](SPEAKER, carre) as
echanti:
[Link](loop=True)
[Link](2)
[Link]()
Ce code source est facile à comprendre : nous créons quatre tampons
mémoire, un par forme d’onde, nous y chargeons les valeurs appropriées
après des calculs assez complexes, puis nous les lisons l’un après l’autre. Je
laisse le lecteur combiner les deux derniers exemples pour faire jouer la
mélodie de Frère Jacques avec un autre timbre, donc une autre forme
d’onde.
Vous pouvez fabriquer vous-même les formes d’ondes dans votre
programme, mais il est encore plus simple de faire jouer des données audio
stockées dans un fichier. Ces données doivent être au format
monophonique, à 16 kHz et sur 16 bits, dans un fichier au format WAV.
Notez que j’ai récupéré le fichier au format MP3 et je l’ai converti au
moyen de l’excellent logiciel Audacity ([Link]). Pour essayer
l’exemple suivant, vous devez copier le fichier [Link] sur la carte CPX.
Il s’agit du sifflet de l’intercom dans Star Trek.

Listing 11.6 : Code source 1106_CPX_lire_wave.py

import board
import audioio
import digitalio
from board import SPEAKER, SPEAKER_ENABLE

# Indispensable sur Circuit Playground


Express
hp_actif =
[Link](SPEAKER_ENABLE)
hp_actif.switch_to_output(value=True)

f = open("[Link]", "rb")
hp = [Link](SPEAKER, f)

[Link]()
while [Link]:
pass # Bloquant

L’exécution du programme vous permet d’entendre le fameux sifflement.


Ceux qui sont familiers avec le langage Python auront compris que ce
programme charge le contenu du fichier nommé [Link] puis il y fait
référence en tant que flux de donnée source (stream) lors de la création de
l’instance de la classe AudioOut. Il n’y a rien d’autre à faire pour que cela
fonctionne !

PyBoard
Avant de découvrir comment faire de la musique et de la synthèse vocale
avec la carte micro:bit, découvrons ce que peut faire la carte PyBoard avec
sa carte d’extension AMP Audio Skin. Cela donnera l’occasion de découvrir
une autre technique pour produire des sons intéressants.
La carte AMP Audio est dotée d’un petit haut-parleur relié à la sortie d’un
convertisseur numérique vers analogique (CAN) via un petit amplificateur
audio. Le convertisseur traite une valeur numérique pour produire une
valeur analogique sous forme d’une tension variant entre 0 et 3,3 V.
Pour émettre un son, il faut donc alimenter le convertisseur d’une façon
proche de celle utilisée pour la carte CPX. Voici par exemple comment
produire une onde sinusoïdale à une certaine fréquence en hertz :

import math
from pyb import DAC

buf = bytearray(100)
for i in range(len(buf)):
buf[i] = 128 + int(127 * [Link](2 *
[Link] * i / len(buf)))
hauteur = 440 # LA du diapason
dac = DAC(1)
dac.write_timed(buf, hauteur * len(buf),
mode=[Link])
Pour changer de timbre, il suffit de changer de forme d’onde. Vous noterez
peut-être que le volume est assez faible. Pour contrôler le volume de
l’amplificateur, vous le considérez comme un périphérique obéissant au
protocole I2C :

import pyb

def volume(volume):
pyb.I2C(1,
[Link]).mem_write(volume, 46, 0)

Les valeurs possibles pour le volume vont de 0 à 127. Faites des essais pour
trouver le volume le plus confortable.
Pour produire des mélodies avec la carte PyBoard, vous pouvez vous
inspirer de la technique utilisée pour la carte CPX ; cela vous ferait un bon
exercice.
Je vous propose plutôt de découvrir une solution assez excentrique qui
consiste à envoyer des valeurs en rafale au convertisseur, un peu comme
une simulation de protocole (bit banging), puis de juger du résultat sonore.
Voici comment reproduire le bruit blanc que l’on entend lorsqu’un récepteur
radio est mal réglé.

Listing 11.7 : Code source 1107_PYB_bruitblanc.py

import pyb
import random
from pyb import DAC
def volume(val):
pyb.I2C(1,
[Link]).mem_write(val, 46, 0)

volume(127)
convAN = DAC(1)
while True:
[Link]([Link](0, 256))

Le résultat n’est pas très agréable à entendre, mais il prouve qu’il est assez
facile de contrôler l’émission d’un son uniquement avec une séquence
numérique, qui est ici aléatoire. Nous pourrions mettre un peu
d’intelligence dans cette séquence numérique ? Saurions-nous faire comme
Pythagore et découvrir des motifs remarquables dans des suites de nombres
pour produire des sons intéressants ?
Mais bien sûr ! Voyez ce petit exemple.

Listing 11.8 : Code source 1108_PYB_musigen.py

import pyb
from pyb import DAC

def volume(val):
pyb.I2C(1,
[Link]).mem_write(val, 46, 0)

volume(127)
cna = DAC(1)
t = 0
while True:
[Link](int(t*
((t>>9¦t>>13)&25&t>>6)) % 256)
#[Link](int(t*((15&t>>11)%12)&55-
(t>>5¦t>>12)¦t*(t>>10)*32) % 256)

#[Link](int((t*9&t>>4¦t*5&t>>7¦t*3&t//1
024)-1))
t += 1

Ce script ressemble beaucoup au précédent, mais le résultat est plus


intéressant à entendre. Nous y avons prévu trois formules qui utilisent le
temps symbolisé par la lettre t pour générer des motifs numériques qui
permettent d’obtenir des effets sonores tels que des bips, des battements,
des lignes de basse et des bribes de mélodies qui émergent du haut-parleur
comme par magie. Vous remarquez qu’une seule formule est active à tout
moment, les deux autres étant neutralisées par le symbole # de mise en
commentaire. Vous pouvez essayer les trois formules tour à tour. Si les
techniques utilisées pour créer ces motifs musicaux avec si peu de code
vous intéressent, commencez par chercher sur le Web des informations au
sujet de la musique générative. Voyez par exemple le site de Counter
Complex ([Link]
J’imagine qu’entendant cela, Beethoven aurait prononcé les paroles
suivantes (extraites de l’Ode à la joie de sa Neuvième Symphonie et écrites
par le poète Friedrich von Schiller) :
Ô amis, pas de ces accents !
Mais laissez-nous en entonner de plus agréables,
Et de plus joyeux !
De la musique avec micro:bit
La version de MicroPython pour la carte micro:bit possède une librairie très
puissante, nommée music, et un langage de description musicale facile
d’accès. Une sélection de mélodies très connues est déjà implantée sur la
carte. La présence de ces mélodies a deux raisons :
1. En rendant disponible des mélodies aux enfants, cela leur permet de
faire de la musique facilement pour l’ajouter à leurs jeux et leurs
projets. Parmi ces mélodies, certaines sont prévues comme effets
sonores pour représenter des événements ou des émotions.
2. Un débutant en programmation peut facilement apprendre comment
est produit un effet sonore simplement en étudiant la définition des
mélodies préinstallées.
La carte micro:bit n’est pas dotée de haut-parleur. Il faut donc en relier un
comme indiqué dans la Figure 11.2.
Figure 11.2 : Branchement d’un haut-parleur à la carte micro:bit entre broches 0 et GND.

Pour faire des essais, j’ai récupéré un des haut-parleurs d’un casque. Son
impédance est de 32 ohms, mais vous pouvez aussi utiliser un petit haut-parleur du
commerce qui aura en général une impédance de 8 ohms ; son volume sera donc
plus fort.

Pour vérifier que le montage fonctionne, je vous propose d’utiliser


l’interpréteur REPL. L’exemple suivant sait émettre le LA du diapason
pendant 10 secondes (fréquence de 440 Hz). Si vous n’entendez rien,
vérifiez les connexions du haut-parleur. Si c’est une enceinte amplifiée,
vérifiez qu’elle est allumée.

>>> import music


>>> [Link](440, 10000)
En quelques lignes, vous pouvez transformer votre carte en un instrument
de musique expérimental.

Listing 11.9 : Code source 1109_MIC_chatophone.py

import music
from microbit import accelerometer

while True:
[Link](accelerometer.get_x(), 20)

Vous modifiez la hauteur du son en inclinant la carte selon l’axe X ; le son


fait penser à un chat étranglé. Une sortie audio constitue un excellent
moyen pour informer l’utilisateur d’un événement détecté, en l’occurrence
par l’accéléromètre. Pour améliorer cet instrument, afin de lui faire jouer
des notes au lieu d’un cri de chat, il suffit de quantifier les mesures de
l’accéléromètre. La quantification ressemble à un arrondi : toute une série
de valeurs sont rendues équivalentes à une seule. Dans l’exemple qui suit,
toutes les mesures faites par l’accéléromètre dans une certaine plage sont
ramenées à une seule hauteur de son. Au lieu d’une variation progressive de
la hauteur, comme dans un portamento, la quantification fait passer d’une
note à une autre, au sein d’une gamme musicale.

Listing 11.10 : Code source 1110_MIC_pentatonique.py

import music
from microbit import accelerometer

entonnoirs = [
262, # C
294, # D
330, # E
392, # G
440, #A
]

while True:
lecture = abs(accelerometer.get_x())
entonnoir = min(4, max(0, lecture //
200)) # Quantisation
[Link](entonnoirs[entonnoir], 20)

Le programme définit dans un tableau les hauteurs des cinq notes d’une
gamme pentatonique. Chaque note constitue un entonnoir dans lequel
entreront toutes les valeurs dans une plage définie. La mesure renvoyée par
l’accéléromètre est une valeur absolue, c’est-à-dire jamais négative. Cette
valeur sert d’index dans le tableau des entonnoirs, afin de choisir une
hauteur. Les mesures de l’accéléromètre peuvent prendre une valeur entre
zéro et environ mille. Nous calculons donc la valeur de l’index dans le
tableau pour qu’elle soit égale à une valeur entre zéro et quatre, ces chiffres
correspondant aux cinq éléments du tableau. Nous obtenons au final une
carte micro:bit qui joue les cinq notes d’une gamme pentatonique en
fonction de l’inclinaison.
Au lieu de faire produire des mélodies par la carte micro:bit, il est possible
de relire une des mélodies préinstallées. Voici comment :

Listing 11.11 : Code source 1111_MIC_musiques.py

import music

[Link](music.POWER_UP)
[Link]([Link])
[Link](music.BA_DING)
[Link]([Link])
[Link]([Link])
[Link]([Link])
[Link]([Link])
[Link]([Link])
[Link]([Link])
[Link]([Link])
[Link](music.JUMP_DOWN)
[Link](music.JUMP_UP)
[Link]([Link])
[Link]([Link])
[Link]([Link])
[Link]([Link])
[Link]([Link])
[Link]([Link])
[Link]([Link])
[Link]([Link])
[Link](music.POWER_DOWN)

Toutes les mélodies prédéfinies dans la librairie musique sont des


constantes écrites en lettres capitales. Elles sont définies avec une notation
simple d’emploi (un langage de description musicale). Les noms de ces
mélodies sont visibles dans l’exemple précédent. Elles s’écrivent toujours
en majuscules.
Voici par exemple la définition de la mélodie DADADADUM, qui
correspond au début de la Cinquième Symphonie de Beethoven :

>>> import music


>>> [Link]
('r4:2', 'g', 'g', 'g', 'eb:8', 'r:2',
'f', 'f', 'f', 'd:8')
La notation permet de définir de nouvelles mélodies assez facilement. En
effet, chaque note est symbolisée par son nom anglo-saxon comme G, F# ou
Eb, suivi d’une indication de l’octave, suivi de la durée. Neuf octaves sont
disponibles, de 0 (la plus grave) à 8. Le Do du milieu du clavier correspond
à l’octave 4 (celle par défaut). Les lettres des notes peuvent être écrites en
majuscules ou en minuscules. Les durées sont des valeurs numériques : plus
la valeur est grande, plus la durée est longue. Elles sont relatives, comme en
solfège ; une durée de 4 dure deux fois plus longtemps qu’une durée de 2.
Vous disposez d’un nom de note spécial, R, qui correspond à un silence de
la durée mentionnée (R signifie Rest ou pause).
Chaque note est une chaîne de caractères obéissant au format
nom_note¦octave : durée. Par exemple, « C1:6 » correspond au Do (C) de
l’octave 1 à jouer pendant une durée de 6. Notez que le langage de
description musicale (DSL) de la carte CPX fonctionne de la même
manière.
Pour créer une mélodie, il suffit donc de dresser la liste des notes en
utilisant les conventions que nous venons de décrire.
Vous pouvez simplifier les mélodies MicroPython. En effet, le numéro
d’octave et la durée sont mémorisés et restent les mêmes tant que vous n’en
changez pas. Vous l’avez peut-être remarqué dans la définition de
DADADADUM ci-dessus : il n’y a pas de mention de l’octave ou de la
durée pour toutes les notes. Vous n’avez besoin d’indiquer l’octave ou la
durée que lorsque la valeur doit changer. Cela facilite l’écriture et la lecture
des définitions de mélodies, comme le montre la mélodie suivante :

melo = ["C4:4", "D", "E", "C", "C", "D",


"E", "C",
"E", "F", "G:8", "E:4", "F",
"G:8",
"G:2", "A", "G", "F", "E:4", "C",
"G:2", "A", "G", "F", "E:4", "C",
"C", "G3", "C4:8", "C:4", "G3",
"C4:8"]
Comme vous pouvez vous y attendre, vous jouez la mélodie au moyen de la
méthode nommée play(). Vous lui fournissez une liste de définition de
notes correspondant à la mélodie. Vous pouvez fournir deux paramètres
facultatifs : wait et loop. Si vous donnez la valeur False à wait, l’appel à la
méthode devient non bloquant (le programme peut continuer à exécuter
d’autres instructions). Si vous donnez la valeur True à loop, la mélodie est
répétée jusqu’à ce que vous appeliez la méthode stop() ou jusqu’à ce que
l’appel bloquant à cette méthode de lecture soit interrompue. Le
fonctionnement est similaire à celui de la méthode d’affichage show() de
l’objet display. L’exemple suivant joue la mélodie définie auparavant de
façon non bloquante et à répétition :

[Link](melo, wait=False, loop=True)

Vous disposez aussi de la méthode pitch() qui permet de jouer des notes
en indiquant la fréquence. Cette méthode peut elle aussi fonctionner en
mode non bloquant. Enfin, la méthode stop() arrête toutes les musiques en
cours. C’est elle qu’il faut utiliser pour vous libérer de la mélodie incessante
de l’exemple précédent.
Vous pouvez contrôler le tempo de la lecture au moyen de la méthode
set_tempo() qui attend deux paramètres : le nombre de tics qui constituent
l’unité du rythme et le nombre de battements par minute (BPM). Le tic
correspond à la durée d’une note telle que définie dans le langage. Vous
pouvez utiliser la méthode de façon simplifiée car elle dispose de valeurs
par défaut pour les deux paramètres. Par exemple, pour changer de tempo,
vous pouvez écrire set_tem-po(bpm=180). Pour connaître le tempo en
vigueur actuellement, vous utilisez la méthode get_ tempo(). Elle renvoie
un tuple de données contenant la valeur de tics et le BPM.
En cas de problème, vous disposez de la méthode reset() pour ramener
tous les paramètres à leur valeur par défaut : les tics, le BPM, la durée et
l’octave.
Synthèse vocale
Un ordinateur qui peut jouer la musique, c’est intéressant, mais un
ordinateur qui peut parler et se faire comprendre devient franchement plus
proche des humains.
Sur la carte micro:bit, vous ne disposez que d’un afficheur constitué de cinq
LED sur cinq ; il n’est pas facile de bien informer l’utilisateur avec si peu
de moyens. En revanche, en faisant parler la carte, vous pouvez l’informer
de façon efficace, agréable et utile. C’est pour cette raison que MicroPython
pour la carte micro:bit a été doté d’un synthétiseur vocal simplifié. Les
concepteurs de la carte ont dès le départ voulu lui donner un aspect
anthropomorphe, et ce synthétiseur ne fait qu’ajouter à son charme.
Essayons tout de suite cette fonction. Branchez si nécessaire un haut-parleur
à la carte, puis écrivez les deux lignes suivantes qui importent la librairie
speech afin d’appeler la méthode say() :

Listing 11.12 : Code source 1112_MIC_dire_hello.py

import speech

[Link]("Hello!")

Ce synthétiseur n’est pas si rudimentaire, puisqu’il est possible d’intervenir


sur plusieurs paramètres de la voix :

• pitch : hauteur de la voix entre zéro (très aigu) et 255 (très grave) ;

• speed : vitesse d’élocution entre zéro (rapide) et 255 (lent) ;

• mouth : clarté de la prononciation entre 0 (pour parler entre ses dents)


et 255 (pour articuler exagérément) ;
• throat : niveau de tension de la voix entre 0 (très tendu) et 255 (très
relâché).
Il ne semble pas exister de recette miracle pour savoir à l’avance comment
une voix va sonner ; il faut faire des essais. L’exemple suivant offre par
exemple un timbre dans l’esprit Dalek :

[Link]("Ici le DALEK -
EXTERMINATION!", speed=120, pitch=100,
throat=100,
mouth=200)

La méthode say() accepte quatre signes de ponctuation : le tiret – pour


insérer une pause brève, la virgule pour insérer une pause deux fois plus
longue, le point et le point d’interrogation pour terminer une phrase avec
une grande pause. Le point fait descendre la voix, alors que le point
d’interrogation la fait monter.
La méthode say() est bien pratique, puisqu’elle permet d’écrire du texte
pour produire directement le résultat vocal, mais elle n’est pas toujours très
précise. Pour obtenir une meilleure précision du résultat, il faut utiliser des
phonèmes qui sont les unités sonores élémentaires à partir desquels sont
constitués les mots parlés. Ce sont les blocs de construction de la parole.
Les lecteurs français auront deviné que ce synthétiseur vocal a été au départ produit
pour la langue anglaise. Les diphtongues et triphtongues typiques du français ne
sont pas très simples à reproduire, mais l’approche par phonèmes que nous allons
découvrir permet d’obtenir de bons résultats.

En utilisant la méthode pronounce(), vous pouvez travailler avec une


version simplifiée de l’alphabet phonétique international sous forme d’une
chaîne de caractères. Vous y ajoutez des annotations pour la flexion et
l’emphase de la foi. Cela évite de devoir épeler les mots. Il suffit de savoir
comment les prononcer puis de chercher les phonèmes correspondants. Les
tableaux suivants présentent tous les phonèmes que reconnaît le
synthétiseur vocal, avec un exemple du son correspondant à chacun entre
parenthèses :
Voyelles simples

IY spl(ee)n

IH p(i)le

EH b(è)gue

AE fl(a)mme

AA p(o)te

AH (Eu)rope

AO d(o)se

OH b(ô)me

UH ch(ou)

UX f(oo)t

ER b(eu)rre

AX tall(o)nne

IX pép(i)te

Diphtongues

EY p(ai)e

AY (aïe)

OY conc(oi)lotte

AW t(ao)

OW sl(ow)
UW écr(ou)

Consonnes vocales

R (r)aide

L pai(ll)e

W a(ou)ais

W tru(e)lle

Y cai(ll)ou

M da(m)e

N scè(n)e

NX so(ng)

B (b)alle

D (d)onne

G re(g)ain

J ban(j)o

Z (z)oo

ZH plai(s)ir

V sau(v)er

DH (z)o(z)oter
Consonnes muettes

S (S)onne

SH ri(ch)e

F (f)rance

TH (th)in

P (p)oker

T (t)our

K (c)ale

CH (ch)a-(ch)a

/H le (h)âle

Phonèmes non standard

YX fin de diphtongue (Y faible)

WX fin de diphtongue (W faible)

RX R après voyelle (R adouci)

LX L après voyelle (L adouci)

/X H avant voyelle sans attaque ou consonne comme dans (h)oux

DX T comme dans pi(t)ié (T adouci)

Phonèmes spéciaux

UL sitte(lle)
UM astron(om)ie

UN rés(onne)

Q (q)uiproquo (stop de glotte)

Voici par exemple comment saluer avec des phonèmes dans une chaîne :

[Link]("/HEHLOW") #
"Hello"

Si vous fournissez un phonème inconnu, vous provoquez le déclenchement


d’une exception de type ValueError.
Vous savez que les phonèmes sont classés en deux groupes : les voyelles et
les consonnes.
Parmi les voyelles, nous trouvons les voyelles simples dont le son ne
change pas et les diphtongues qui commencent par un son et se terminent
par un autre. Par exemple, le mot huile contient la diphtongue ui qui
commence par U et se termine par I.
Il existe également deux grands groupes de consonnes : les consonnes
vocales qui supposent d’utiliser les cordes vocales (comme M, N et Z), et
les consonnes muettes qui sont chuintantes comme P, T et CH.
L’utilisation des phonèmes n’est pas toujours intuitive. Par exemple, le mot
piscine se prononce « pissine ». Pensez au résultat sonore du mot et non à la
façon dont vous l’épelez. Le mieux est de faire des essais jusqu’à obtenir un
résultat satisfaisant.
Pour vous aider à faire vos premiers pas, servez-vous du résultat que
renvoie la méthode translate(). Elle renvoie les phonèmes utilisés par le
synthétiseur vocal à partir d’un ou de plusieurs mots qui lui sont transmis :

[Link]("Hello")
' /HEHLOW'
Pour rendre le résultat plus intelligible et plus naturel, vous disposez d’un
réglage d’intonation. Il est constitué de huit marqueurs de stress entre 1 et 8.
La valeur est à indiquer juste après le phonème concerné. Par exemple,
l’expression « /HEHLOW » sonne assez robotique. Pour un effet plus
agréable, insérez un marqueur de stress, comme dans « /HEH3LOW » .
Le système de contrôle d’intonation élève ou abaisse la hauteur et allonge le
son de la voyelle, en fonction de la valeur fournie :
1 : voix très émotive.
2 : voix avez beaucoup d’emphase.
3 : intonation allongée.
4 : intonation standard.
5 : intonation resserrée.
6 : intonation neutre, sans changement de hauteur.
7 : intonation descendante.
8 : intonation très descendante.
Ces marqueurs d’intonation permettent de mieux prononcer les mots
difficiles. Si une syllabe n’est par exemple pas suffisamment claire, ajoutez
un marqueur neutre. Vous pouvez également rallonger des mots :

[Link]("/HEH5EH4EH3EH2EH2EH3EH4E
H5EHLP.")

Enfin, et c’est formidable, vous pouvez faire chanter MicroPython en


ajoutant une hauteur de note avant le phonème. Paradoxalement, plus la
valeur est faible, plus la note est aiguë. Les valeurs correspondent à peu
près aux notes de musique montrées dans la Figure 11.3.
Figure 11.3 : Valeurs numériques des hauteurs de note pour chanter.

Pour ajouter une hauteur, vous commencez par le # suivi de la valeur


numérique suivie du phonème. Cette hauteur reste la même pour toute la
suite, jusqu’au prochain changement. Les préfixes de hauteur ne sont
utilisables qu’avec la méthode sing(). Voici comment l’utiliser :

Listing 11.13 : Code source 1113_MIC_chanteur.py

import speech

solfege = [
"#115DOWWWWWW", # Doh
"#103REYYYYYY", # Re
"#94MIYYYYYY", # Mi
"#88FAOAOAOAOR", # Fa
"#78SOHWWWWW", # Soh
"#70LAOAOAOAOR", # La
"#62SIYYYYYY", # Si
"#58DOWWWWWW", # Doh
]
chanson = ''.join(solfege)
[Link](chanson, speed=100)

Pour augmenter la durée d’une note, répétez la voyelle ou la consonne


vocale comme le montre l’exemple précédent. Pour une diphtongue, vous la
rallongez en la subdivisant dans ses sons élémentaires. Par exemple, la
diphtongue « OY » peut être allongée en écrivant « OHOHIYIYIY ».
Que pourrions-nous envisager en combinant synthèse vocale et musique ?
La première mélodie au monde chantée par un ordinateur l’a été en 1961.
C’était un IBM 704 qui avait chanté une mélodie américaine très connue,
Daisy Bell. Le réalisateur Stanley Kubrick a fait un clin d’œil (ou d’oreille,
en l’occurrence) à cet événement. Dans son film de 1968 2001, l’Odyssée
de l’espace, l’ordinateur du vaisseau spatial, HAL 9000, fredonne cette
chanson de moins en moins fort pendant que l’astronaute le débranche. La
chanson Daisy Bell est donc devenue un morceau d’anthologie dès qu’il
s’agit de faire chanter un ordinateur. Je vais donc me plier à cette tradition
en vous proposant le projet suivant dans lequel votre carte micro:bit va
fredonner cette fameuse mélodie.
Pour vérifier cela, il faut regarder le film dans sa version originale anglaise. Dans la
version française, HAL chante Au clair de la lune.

Listing 11.14 : Code source 1114_MIC_daisybell.py

import speech

ligne1 = [
'#26DEYYYYYYYYY',
'#31ZIYIYIYIYIYIYIY',
'#39DEYYYYYYYYY',
'#52ZIYIYIYIYIYIYIY',
'#46GIXV',
'#42MIYIY',
'#39YAOW',
'#46AEAEAEN',
'#39SERER',
'#52DUXUXUXUXUXUXUXUXUXUXUXUX']

ligne2 = [
'#35AYYYYYYMM',
'#26/HAEAEAEAEAEAEF',
'#31KREYYYYYYY',
'#39ZIYIYIYIYIYIYIY',
'#46AXLL',
'#42FAOR',
'#39DHER',
'#35LUHUHUHV',
'#31AXAXV',
'#35YUXUXUXUXUXUXUXUXUXUX']

ligne3 = [
'#31IHT',
'#29WOWNT',
'#31BIY',
'#35ER',
'#26STAYYYYY',
'#31LIHSH',
'#35MAE',
'#39RIXIXIXIXIXIXIXIXIXIXIXIXIXJ',
'#35AYY',
'#31KAEAEAEAENT',
'#39ER',
'#46FAOAOAORD',
'#39ER',
'#46KAA',
'#52RIXIXIXIXIXIXIXIXIXIXIXIXIXJ']

ligne4 = [
'#52BUHT',
'#39YUXUXL',
'#31LUXK',
'#35SWIYIYIYIYT',
'#52ER',
'#39PAAAAAAN',
'#31ER',
'#35SIYIYIYT',
'#31UHV',
'#29ER',
'#26BAY',
'#31SIH',
'#39KUXL',
'#35MEYYYYD',
'#52FER',
'#39TUXUXUXUXUXUXUXUXUXUXUX']

[Link](''.join(ligne1))
[Link](''.join(ligne2))
[Link](''.join(ligne3))
[Link](''.join(ligne4))

Pour alléger la lecture du code, j’ai réparti chaque ligne en phonèmes. Les
lignes sont lues l’une après l’autre en regroupant tous les constituants de
chacune en une chaîne unique. Pour les durées des notes, c’est le fruit de
mes essais.
Et voici les paroles de la chanson Daisy Bell (écrite par Harry Dacre
en 1892), à comparer avec la transcription phonétique dans le code source :
Daisy, Daisy,
Give me your answer, do!
I’m half crazy
All for the love of you!
It won’t be a stylish marriage,
I can’t afford a carriage,
But you’ll look sweet on the seat
Of a bicycle built for two!

(Daisy, Daisy,
Répondez-moi, enfin !
Je deviens fou,
Fou d’amour pour vous.
On se mariera sans éclat.
Payer un carrosse, je n’peux pas.
Mais vous serez la plus jolie,
Sur mon vélo qui est fait pour deux !)

Les deux méthodes pronounce() et sing() peuvent influer sur le timbre


comme la méthode say(), avec les paramètres speed, mouth et throat.
Faites des essais et faites confiance à vos oreilles. Pourquoi ne pas changer
la façon de chanter la mélodie ?
Avec ce que ce chapitre vous a montré, vous savez maintenant faire bruire,
parler et chanter vos projets pour divertir et informer vos utilisateurs. Je
propose de clore le chapitre avec un script branché qui transforme une carte
micro:bit en beatbox :

Listing 11.15 : Code source 1115_MIC_beatbox.py

import speech
from microbit import sleep, button_a,
button_b, display, Image

gap = 220 # Longueur d'un silence


bass_drum = "BUH" # Son de grosse caisse
de beatbox
snare = "CHIXIX" # Son d'une caisse
claire de beatbox
roll = "DGDG" # Son d'un fla de
batterie beatbox
pause = "" # Pause de longueur
"gap"

# Deux sequences (des listes). Un temps


par ligne
motif1 = [ # Cool
bass_drum, pause, pause, pause,
snare, pause, pause, pause,
bass_drum, bass_drum, bass_drum,
pause,
snare, pause, roll, roll,
]

motif2 = [ # Hardcore
bass_drum, snare, snare, snare,
bass_drum, snare, roll, roll,
bass_drum, bass_drum, bass_drum,
snare,
bass_drum, roll, roll, bass_drum,
]

# Joue un son ou un silence


def beat_box(son):
if son:
[Link]([Link])
sleep(10)
[Link]()
[Link](son)
else:
sleep(gap)

# Lit tous les sons de la sequence


def lire(seq):
fortop inseq:
beat_box(top)

choix = motif1 # Sequence initiale

# Reboucle sur une sequence


while True:
# Choix de sequence avec boutons A et
B
if button_a.was_pressed():
choix = motif1
elif button_b.was_pressed():
choix = motif2
# Lecture de la sequence
lire(choix)
CHAPITRE 12
Robots
Les robots sont des machines sympathiques, du moins tant que l’on ne
craint pas de les voir un jour remplacer les humains. Les appareils
pédagogiques tels que la carte micro:bit ont permis à des milliers
d’amateurs de créer de petits robots, prouvant par cela que la robotique
pouvait être accessible et amusante. Nous allons découvrir dans ce chapitre
deux projets de robots pour montrer comment le langage MicroPython peut
vous permettre de préparer votre propre invasion. Les deux projets utilisent
la carte micro:bit, mais les techniques présentées sont faciles à réutiliser
avec les autres cartes fonctionnant sous MicroPython.
Trundle bot
Ce robot sur roues aime se promener. Il dispose à l’avant d’un capteur de
distance analogique pour détecter les obstacles. S’il en trouve un, il se
tourne vers la gauche ou vers la droite jusqu’à ne trouver aucun obstacle et
reprend son chemin. Ce robot est très simple : il faut moins d’une heure
pour l’assembler à partir de quelques pièces. Le code pour piloter ce robot
est très simple aussi, et permet de voir comment le faire se déplacer en
contrôlant deux servomoteurs.
Ce robot a été créé par le très talentueux Radomir Dopieraslki, l’un des nombreux
bénévoles qui ont permis de faire fonctionner MicroPython sur la carte micro:bit.
Radomir adore créer de nouveaux robots pour les cartes MicroPython ; vous en
trouverez des exemples sur son site [Link].

Ce robot avait été choisi pour un atelier de construction de robots lors de la


conférence EuroPython 2016. Sa simplicité rend faciles les modifications et
adaptations en fonction des matériaux disponibles et de l’objectif du
propriétaire. Les constructeurs étaient aussi bien des programmeurs Python
expérimentés, que des non-techniciens et même des enfants.
Les composants à réunir sont peu nombreux et très bon marché :

• une carte micro:bit ;

• deux servomoteurs à rotation continue ;

• un châssis Pololu Carrier équipé d’un capteur de distance analogique


Sharp GP2Y-0A60SZLF fonctionnant en 3 V ;

• deux roues à bandages ;

• une roulette orientable Pololu ;

• une source d’alimentation portable comme un porte-piles pour fournir


entre 3,3 et 4,2 V.
Ajoutez à cela des accessoires pour construire le châssis et réaliser les
connexions avec des fils. Il vous faut donc du ruban adhésif double face,
des bracelets caoutchouc, éventuellement deux yeux autocollants. Ne
cherchez pas à faire quelque chose qui soit aussi bien construit que le robot
de la Figure 12.1. L’essentiel est que le robot fonctionne. Vous pourrez
toujours améliorer son aspect plus tard. Les Figures 12.2 et 12.3 montrent
un autre exemplaire du même robot construit avec du carton, des plaques de
plastique collantes et des caoutchoucs. Vous conviendrez qu’il a un certain
charme ; il est surtout facile et amusant à construire, même par un débutant
en robotique.
Figure 12.1 : Un robot baladeur très simple.
Figure 12.2 : Un autre exemplaire avec un châssis improvisé maintenu par de l’adhésif, des fils et
du caoutchouc.

Figure 12.3 : Le même robot Trundle bot avec ses deux yeux devant. La micro:bit est maintenue en
place par de l’adhésif double face.

Vous avez toute latitude pour les détails de la construction, l’essentiel étant
de vous amuser. Ce qui compte, c’est de ne pas vous tromper dans le
câblage. À première vue, la Figure 12.4 peut sembler un peu complexe,
mais vous allez voir qu’il n’en est rien.

Figure 12.4 : Plan de câblage du robot Trundle bot.

Une fois que vous avez construit le châssis et connecté les composants,
vous pouvez passer à la programmation et au contrôle des moteurs, et
prendre des mesures de distance pour ne pas provoquer de collision.
Les servos sont connectés aux broches 0 et 1, ainsi qu’à la masse et au 3 V.
Ils vont recevoir des valeurs analogiques pour contrôler la vitesse de
rotation, et donc la direction. Vous contrôlez la vitesse de rotation en jouant
sur la largeur d’une impulsion. Le contrôle d’un servomoteur est particulier,
au sens où l’arrêt du moteur correspond à une certaine largeur d’impulsion.
Pour faire tourner le moteur, l’impulsion doit être soit plus longue, soit plus
courte que cette référence. Plus l’impulsion s’écarte de la valeur
correspondant à l’arrêt, plus la vitesse de rotation est grande, en marche
avant ou en marche arrière.
Ce contrôle des moteurs est incarné par une classe nommée Servo :

Listing 12.1 : Début du code source 1201_MIC_trundlebot.py

import microbit

class Servo:
def __init__(self, broche, trim=0):
[Link] = broche
[Link] = trim
[Link] = 0
[Link].set_analog_period(20)

def reguler(self, vite):


[Link].write_analog(int(25 + 100
* (90 + vite) / 180 + [Link]))
[Link] = vite

La méthode constructeur __init__() qui sert à créer une instance de la


classe reçoit en entrée le numéro de la broche qui va servir à contrôler la
rotation du moteur, ainsi qu’un paramètre d’ajustement fin nommé trim. En
effet, les servomoteurs sont rarement conformes à leurs performances
théoriques, ce qui rend ce réglage fin indispensable. Le paramètre de vitesse
détermine bien sûr la vitesse de rotation du moteur. La méthode
constructeur se termine en définissant la fréquence PWM au moyen de la
méthode set_analog_period(). Notre classe définit une seconde méthode
pour modifier la vitesse de rotation qui peut aller de –90 (à fond en arrière)
à 90 (à fond en avant) en passant par 0 (stop).
La suite du même fichier de code source présente la définition de la classe
qui incarne le robot lui-même :

Listing 12.2 : Suite du code source 1201_MIC_trundlebot.py

class Robot:
def __init__(self):
# Penser a ajuster les valeurs de
trim.
self.servo_g =
Servo(microbit.pin0, 2)
self.servo_d =
Servo(microbit.pin1, 1)

def avancer(self, distance):

[Link]([Link]
_S)
self.servo_g.reguler(-90)
self.servo_d.reguler(90)
[Link](int(distance * 2000
/ 17))
[Link]()

def tourner(self, angle):


if angle > 0:

[Link]([Link]
_E)
self.servo_g.reguler(-90)
self.servo_d.reguler(-90)
[Link](int(angle * 64 /
9))
else:

[Link]([Link]
_W)
self.servo_g.reguler(90)
self.servo_d.reguler(90)
[Link](int(-angle * 64 /
9))
[Link]()

def stopper(self):

[Link]([Link]
ND)
self.servo_g.reguler(0)
self.servo_d.reguler(0)

def regarder(self):
return microbit.pin2.read_analog()

Cette définition regroupe toutes les méthodes qui permettent à une instance
de la classe Robot de jouer le rôle de pilote.
L’initialisation demande de créer deux instances de la classe Servo, une
pour chaque moteur. Comme le rappelle le commentaire, si vous constatez
que le robot dérive vers la droite ou vers la gauche, vous intervenez sur le
second paramètre de création d’instance, pour ajuster le trim. Les quatre
méthodes suivantes servent à avancer, à tourner, à s’arrêter et à mesurer la
distance. La méthode avancer() se sert de son paramètre d’entrée distance
pour déterminer la durée de rotation des moteurs. Vous remarquez que les
deux moteurs fonctionnent en sens inverse, ce qui est normal puisqu’ils sont
installés sur deux roues qui se font face. La méthode tourner() effectue un
déplacement avec un angle et la méthode stopper() arrête le robot. Les
trois méthodes contrôlent les servomoteurs de telle manière que vous
n’avez pas besoin de vous soucier des détails. Cela permet au programmeur
de se concentrer sur le comportement du robot, au lieu de devoir gérer les
détails des muscles représentés par les servomoteurs. Dans le même esprit,
la méthode regarder() renvoie la lecture de distance analogique en
interrogeant le capteur qui est branché sur la broche 2. Notez que la valeur
renvoyée est en logique inversée : une petite valeur correspond à une
grande distance.
Il ne reste plus qu’à écrire la boucle principale du programme dans laquelle
nous créons une instance de la classe Robot. Nous contrôlons ensuite le
comportement dans une boucle d’événement :

Listing 12.3 : Fin du code source 1201_MIC_trundlebot.py

robot = Robot()
while True:
[Link](5)
if [Link]() > 700:
[Link](20)
distance_g = [Link]()
[Link](-40)
distance_d = [Link]()
[Link](20)
if distance_g < distance_d:
[Link](60)
else:
[Link](-60)
En début de boucle, nous faisons avancer un peu le robot. S’il détecte une
grande valeur au retour de l’appel à regarder(), il applique une stratégie
très simple : il mesure d’abord la distance libre après s’être tourné un peu
vers la gauche, puis il se tourne un peu vers la droite pour faire une seconde
mesure ; il décide alors d’aller dans la direction où il y a le plus d’espace
libre devant lui. Ce n’est pas encore de l’intelligence artificielle du niveau
de l’ordinateur HAL 9000, mais cela suffit largement pour que le robot
trouve toujours le moyen de continuer sa route.
Vous obtenez ainsi un objet mobile semi-indépendant dont le comportement
est régi par une sorte de cerveau. Cela reste un robot élémentaire, mais c’est
justement une bonne base pour l’améliorer et augmenter son potentiel.
Avez-vous déjà une idée de ce que vous pourriez faire pour enrichir son
comportement ? Vous pourriez peut-être lui faire suivre des objets au lieu de
les éviter ? Quel genre de retouches pourriez-vous envisager au niveau du
matériel ?
Bit:Bot, un robot de course
Vous trouverez déjà dans le commerce un certain nombre de robots en kit
pour micro:bit. Nous allons en découvrir un afin de voir comment
MicroPython peut prendre le contrôle d’un robot plus complexe que le
précédent.
Ce robot, nommé Bit:Bot, est proposé par la société 4tronix
([Link]/bitbot). C’est un autre robot baladeur, mais il est bien
mieux équipé que le précédent : il possède deux rangées de LED NeoPixels,
un bruiteur monoton, deux capteurs pour suivre une ligne, deux capteurs de
lumière, un connecteur d’accueil pour la carte micro:bit et un porte-piles
avec un interrupteur (Figure 12.5). Le montage se fait en quelques minutes.
Le contrôle des moteurs est plus sophistiqué que celui du robot précédent,
car chacun des moteurs possède une connexion pour choisir le sens de
marche et une autre pour la vitesse. Pour faire tourner un moteur, il suffit de
faire passer la broche correspondante de l’état Bas à l’état Haut :

pin8.write_digital(0) # Sélection de
la marche avant
pin0.write_digital(1) # Sélection de
la vitesse maximale

Pour régler la vitesse de rotation, il faut remplacer le second appel ci-dessus


à write_digital() pour la broche 0 (pin0) par un appel à sa collègue
analogique :

pin0.write_analog(511)
Figure 12.5 : Le robot Bit:Bot de 4tronix.

Il s’agit encore d’une utilisation de la technique PWM. La plage de valeurs


autorisées va de 0 à 1 023. L’exemple précédent règle donc la vitesse de
rotation à la moitié, puisque 511 est à peu près la moitié de 1 023.
Les 12 diodes NeoPixels sont toutes reliées à la broche 13. Nous utilisons la
librairie neopixel standard de la micro:bit. Le bruiteur ne peut émettre
qu’une note ; il est contrôlé par un signal numérique sur la broche 14. Les
broches 11 (pour la gauche) et cinq (pour la droite) servent à détecter une
ligne sous le châssis, ce qui permet de faire suivre une ligne tracée au sol.
Enfin, les deux capteurs de lumière sont reliés aux broches 16 (capteur de
gauche) et 11 (capteur de droite) et à la broche 2 (pour lire la valeur
analogique correspondant à la luminosité).
Vous pouvez créer un robot télécommandé en utilisant une seconde carte
micro:bit et en les faisant dialoguer avec leur module radio. Cela suppose
de définir les signaux à échanger entre les deux cartes ; les signaux sont
émis par la première carte puis traduits en ordres sur la carte réceptrice du
robot. Il reste à voir comment faire que l’interface utilisateur soit intuitive.
Vous pouvez par exemple utiliser la détection d’inclinaison latérale de la
carte de contrôle pour contrôler la direction du robot. En inclinant la même
carte en avant et en arrière, vous pouvez le faire avancer et reculer. En
mesurant le degré d’inclinaison, nous devrons pouvoir graduer le
changement de vitesse ou d’angle de rotation. Nous pouvons nous servir
d’un des deux boutons comme klaxon avec le bruiteur et l’autre pour
allumer et éteindre les diodes NeoPixels. Dans ce projet, nous n’utilisons
pas les capteurs de ligne et de lumière.
Les grands principes étant définis, il nous faut mettre en place un protocole
pour envoyer les ordres. Nous avons besoin d’envoyer quatre types de
signaux : vitesse, rotation, klaxon et lumière. La solution la plus simple
consiste à envoyer une chaîne de caractères contenant les quatre valeurs
séparées par un signe de ponctuation tel que le signe deux-points. Si les
deux cartes se sont mises d’accord sur l’ordre des valeurs, le récepteur peut
décomposer la chaîne pour obtenir chacune des quatre valeurs. Chaque
message à transmettre serait conforme au format suivant :

vitesse:angle:klaxon:lumiere

Certaines des valeurs seront analogiques, comme la vitesse et l’angle, et


d’autres numériques, comme l’activation du klaxon et des lumières. Il ne
reste plus qu’à se mettre d’accord sur un canal radio.
Voici d’abord le script complet pour la carte micro:bit de pilotage du robot
(l’émetteur).

Listing 12.4 : Code source 1202_MIC_bitbot_emetteur.py

# Emetteur Bit:Bot
from microbit import *
import radio

[Link](channel=44)
[Link]()
# Limites des mesures d'inclinaison
max_tilt = 1000
min_tilt = 199

while True:
# Lecture des capteurs
y = accelerometer.get_y() # Avant /
Arriere
x = accelerometer.get_x() # Gauche /
Droite
a = button_a.was_pressed() # Klaxon
b = button_b.was_pressed() # Lumieres

# Data de pilotage a envoyer


# [vitesse, direction, bruiteur,
neopixels]
bloc_commande = [0, 0, 0, 0]
if x < -min_tilt and y < -min_tilt:
# En avant gauche
[Link](Image.ARROW_NW)
bloc_commande[0] = max(y, -max_tilt)
bloc_commande[1] = max(x, -max_tilt)
elif x < -min_tilt and y > min_tilt:
# En arriere gauche
[Link](Image.ARROW_SW)
bloc_commande[0] = min(y, max_tilt)
bloc_commande[1] = max(x, -max_tilt)
elif x > min_tilt and y < -min_tilt:
# En avant droite
[Link](Image.ARROW_NE)
bloc_commande[0] = max(y, -max_tilt)
bloc_commande[1] = min(x, max_tilt)
elif x > min_tilt and y < min_tilt:
# En arriere droite
[Link](Image.ARROW_SE)
bloc_commande[0] = min(y, max_tilt)
bloc_commande[1] = min(x, max_tilt)
elif y > min_tilt:
# En arriere
[Link](Image.ARROW_S)
bloc_commande[0] = min(y, max_tilt)
elif y < -min_tilt:
# En avant
[Link](Image.ARROW_N)
bloc_commande[0] = max(y, -max_tilt)
if a:
# Klaxonner
bloc_commande[2] = 1
if b:
# Allumer/eteindre LED
bloc_commande[3] = 1
if any(bloc_commande):
msg = '{}:{}:{}:
{}'.format(*bloc_commande)
[Link](msg)
else:
[Link]()
sleep(20)

La presque totalité du script est une boucle de gestion d’événements. Nous


lisons les valeurs de l’accéléromètre et des boutons. En fonction des
réponses de l’accéléromètre, nous définissons les valeurs pour la vitesse et
l’angle de rotation. Pour les deux boutons, nous armons un drapeau binaire.
Le pilote peut ainsi vérifier ce que la carte a compris de vos gestes, et
l’afficheur confirme avec une flèche la direction qui va être transmise. Le
message correspondant aux données de pilotage est alors transmis par ondes
radio.
La carte réceptrice qui va contrôler le robot est plus simple. Elle doit
d’abord recevoir le signal, le décrypter, convertir les valeurs pour la vitesse
et l’angle de rotation afin de piloter les moteurs puis réagir aux demandes
pour le klaxon et les lumières.

Listing 12.5 : Code source 1203_MIC_bitbot_recepteur.py

# Recepteur
from microbit import *
import radio
import neopixel

[Link]([Link]) #Logocranien :-)

couleur = (244, 0, 244) # Couleur des LED


NeoPixels
np = [Link](pin13, 12)
lumiere = False

[Link](channel=44)
[Link]()

def bouger(vitesse, braquage):


# On part d'une position stop
enavant = 0
gauche = 0
droite = 0
if vitesse > 0:
# En avant toute
enavant = 1
gauche = 1000 - vitesse
droite = 1000 - vitesse
elif vitesse < 0:
# Marche arriere
gauche = 1000 + (-1000 - vitesse)
droite = 1000 + (-1000 - vitesse)
if braquage < 0:
# A droite
droite = min(1000, droite +
abs(braquage))
gauche = max(0, gauche -
abs(braquage))
elif braquage > 0:
# A gauche
gauche = min(1000, gauche +
braquage)
droite = max(0, droite -
braquage)

# Envoi des ordres aux moteurs


pin8.write_digital(enavant)
pin12.write_digital(enavant)
pin0.write_analog(gauche)
pin1.write_analog(droite)

while True:
pin14.write_digital(0) # Arret du
klaxon
try:
msg = [Link]()
except:
msg = None # Attention aux
reseaux inconnus
if msg is not None:
# Extraction des donnees du
message
vitesse, braquage, klax, lumi =
[int(val) for val in [Link](':')]
bouger(vitesse, braquage) # On se
bouge
if klax:
# Coup de klaxon
pin14.write_digital(1)
if lumi:
# Inversion lumieres
if lumiere:
[Link]()
lumiere = False
else:
lumiere = True
for i in range(12):
np[i] = couleur
[Link]()
else:
# Pas de message ? On ne fait rien
!
bouger(0, 0)
sleep(20)
Nous avons défini une méthode nommée bouger() dans laquelle sont réunis
tous les détails concernant le contrôle des broches sur lesquelles sont
branchés les moteurs. La méthode utilise en entrée les deux paramètres de
vitesse et d’angle pour décider du sens de rotation des moteurs et de la
vitesse de rotation.
Dans la boucle d’événements, nous commençons par arrêter le klaxon après
un bref bip, puis nous nous tenons en attente d’un message radio. Dès qu’un
message est reçu, nous y réagissons.
Si vous possédez plusieurs cartes micro:bit et plusieurs robots, vous pouvez
organiser une course. Pour éviter les interférences, assurez-vous d’utiliser
des canaux radio différents entre chaque couple de programmes. Profitez-en
pour faire afficher un numéro de coureur différent sur l’afficheur de chaque
robot.
Je vous propose pour finir de réaliser par vous-même quelques
améliorations pour tester vos nouvelles compétences en robotique. Faites en
sorte qu’en appuyant sur les deux boutons à la fois, on puisse entrer et sortir
du mode autopilote. L’autopilote doit pouvoir travailler de deux façons
différentes : soit il suit une ligne tracée au sol, soit il se dirige vers une
source de lumière. Vous utiliserez pour cela les capteurs disponibles sur le
châssis. N’hésitez pas à les mettre à contribution !
CHAPITRE 13
L’esprit de MicroPython
Les chapitres précédents se sont consacrés à de nombreux aspects
techniques de MicroPython : les librairies disponibles, les entrées-sorties,
les interactions avec le matériel, les différents protocoles, et autres savoir-
faire permettant de réaliser des projets intéressants et utiles. Cependant,
nous n’avons pas encore abordé la façon d’utiliser ce genre de compétences
pour écrire du code conforme à l’esprit du langage MicroPython, c’est-à-
dire, comme on le dit dans la communauté, du code « pythonesque ». Pour
comprendre ce que cela signifie, il nous faut faire un pas en arrière et
envisager le langage Python aussi bien en termes de langage de
programmation que de culture ; ce n’est qu’ensuite que nous pourrons voir
comment écrire du code MicroPython régulier, tout en tenant compte des
fortes contraintes que posent les systèmes embarqués.
Tout d’abord, à quoi est due la grande popularité du langage Python ?
Qu’est-ce qui motive les bénévoles pour contribuer si intensément à la
communauté Python ? Et pourquoi Python est-il devenu un langage de
choix pour l’apprentissage ?
Le créateur du langage, Guido van Rossum, avait imaginé Python dans les
années 1990 comme point de départ pour un vaste projet qu’il avait intitulé
La programmation informatique pour tous : une expédition aventureuse
pour les programmeurs du futur. L’introduction du dossier de présentation
de son projet fournissait des indices pour comprendre la popularité de
Python :
Dans les années 70, le centre de recherche Xerox Parc se demandait :
« Pourra-t-on voir un jour un ordinateur sur chaque bureau ? » Nous savons
maintenant que c’est possible, pourtant ces millions d’ordinateurs
personnels n’ont pas donné plus de pouvoir aux utilisateurs. Les ordinateurs
actuels sont souvent très rigides : l’utilisateur moyen n’a le droit de changer
que quelques options en utilisant un assistant (en réalité, une boîte de
dialogue prédéfinie). Pour tout le reste, il doit demander l’intervention d’un
expert en programmation.
Cela nous amène à poser la question suivante : « Que se passerait-il si les
utilisateurs pouvaient eux-mêmes programmer leur machine ? » Nous
rêvons d’un monde dans lequel tout utilisateur d’un ordinateur pourra
soulever le capot et améliorer ses applications. Nous sommes persuadés que
cela changera radicalement la nature des logiciels et leurs outils de création.
Nous comparons la capacité pour tous à lire et à écrire du logiciel à
l’apprentissage de la lecture et de l’écriture, et nous prédisons que ces
nouvelles compétences vont entraîner des évolutions profondes de la
société. Le matériel informatique est devenu suffisamment performant et
bon marché pour rendre possible une éducation à l’informatique pour les
masses : la prochaine révolution se produira quand la majorité des
utilisateurs disposeront des savoirs et des capacités pour créer et modifier
eux-mêmes les logiciels.
L’approche de Python consiste justement à offrir plus de pouvoir à
l’utilisateur. Il est donc normal que les gens l’apprécient énormément.
Python est également connu pour être un langage informatique expressif et
agréable à utiliser. Mais pour quelle raison exactement ?
Je pense que la réponse se niche dans la profession de foi du langage, une
sorte de poème qui s’appelle le Zen de Python. Vous pouvez faire afficher
ce texte en anglais en ouvrant une session d’interpréteur REPL Python puis
en saisissant la commande import this :

>>> import this


Le Zen de Python, de Tim Peeters (V.F. de
O. Engler)

Le beau est meilleur que le laid.


Explicite est meilleur qu'implicite.
Simple est meilleur que complexe.
Complexe est meilleur que compliqué.
Rester au même niveau est meilleur
qu'imbriquer.
Aérer est meilleur que densifier.
La lisibilité est importante.
Les cas particuliers ne le sont pas assez
pour s'affranchir des règles.
Cela dit, le côté pratique est préférable
à la pureté.
Les erreurs ne doivent jamais rester
silencieuses.
Sauf si elles ont été forcées au silence.
En cas d'ambiguïté, il faut se refuser de
deviner.
Il doit y avoir une seule manière possible
de faire une chose.
Même si elle n'est pas évidente au départ
(à moins d'être Hollandais).
Maintenant est meilleur que jamais.
Mais jamais est souvent meilleur que la
précipitation.
Si une solution est difficile à expliquer,
c'est une mauvaise idée.
Si une solution est facile à expliquer,
c'est peut-être une bonne idée.
Les espaces de noms sont une superbe idée,
profitons-en plus souvent !
Ce texte permet de comprendre ce que c’est qu’écrire du code dans l’esprit
pythonesque. L’auteur, Tim Peeters, décrit son document comme une
présentation succincte des principes d’écriture Python tels que recueillis
auprès de son créateur, sous forme de 20 aphorismes, dont seuls 19 sont
présents dans le texte. Cette présentation pleine d’humour invite à
rechercher la simplicité, la clarté, le pragmatisme, sans perdre le sens du
plaisir d’écrire. Cette approche tranche franchement avec l’esprit des
langages de programmation habituels, qui sont complexes, obscurs, fouillés
et sérieux. Python pousse à trouver des solutions simples, élégantes et
faciles à comprendre à tous les problèmes de programmation. Comme l’a
rappelé Alex Martelli dans son livre Python Cookbook, « se vanter d’avoir
trouvé une technique de programmation futée n’est pas considéré comme
une prouesse particulière dans la culture Python ».
La simplicité et le pragmatisme de Python sont des qualités préservées dans
le langage MicroPython.
Le Zen de MicroPython
Il existe également un Zen de MicroPython. Il est caché sous forme d’un
œuf de Pâques dans la version pour micro:bit. Pour en prendre
connaissance, il suffit d’ouvrir une session d’interpréteur REPL et de saisir
la directive import this.
Plusieurs œufs de Pâques sont cachés dans la version micro:bit de MicroPython.
Les programmeurs curieux se feront un plaisir de les trouver. Un indice : si vous
affichez le texte d’aide de la fonction help(), vous pourrez être placé sur la
bonne piste, si vous savez ce que vous cherchez (par exemple parmi les noms des
modules).

>>> import this


Le Zen de MicroPython, par Nicolas H.
Tollervey

Codez,
Bricolez,
Moins c'est plus,
Restez simple,
Faites léger et beau à la fois.

Osez ! Démontez tout ! Apprenez et amusez-


vous !
Exprimez-vous avec MicroPython.

Codez dans la joie ! :-)

Ce bref texte combine humour et sagesse, mais sa caractéristique principale


est sa brièveté (il est de l’auteur de ce livre :). C’est tout à fait approprié,
quand on songe aux ressources limitées des systèmes embarqués sur
lesquels fonctionne MicroPython.
Cette compacité est un exemple à suivre dans l’écriture de votre code
source. Rappelons en effet que certaines plates-formes, et notamment la
carte micro:bit, n’offrent que 16 ko de mémoire vive, et que sa pile d’appels
(call stack) a une profondeur limitée à 5 niveaux d’appel.
La pile d’appels est une petite zone mémoire dans laquelle est stockée l’adresse de
retour lorsque vous appelez une fonction depuis une autre. Autrement dit, avec cinq
niveaux d’appels maximum, vous ne pouvez appeler que quatre fois une fonction
de l’intérieur d’une autre en cascade.

Ces contraintes extraordinaires n’empêchent pas de réaliser toutes sortes de


solutions fonctionnelles. Mais pour y parvenir, il vous faut acquérir
certaines techniques spécifiques à MicroPython. C’est l’objet de toute la
suite du chapitre.
Gestion mémoire
L’espace mémoire limité est sans doute la principale source de problèmes
pour un programmeur débutant en MicroPython. Rappelons qu’il existe
deux catégories d’espace mémoire. La mémoire flash est permanente ; c’est
ici qu’est stocké votre programme MicroPython. La mémoire vive RAM
perd son contenu à la mise hors tension. C’est l’espace de travail dans
lequel sont notamment stockées les valeurs des variables. L’espace de
mémoire flash est en général de plusieurs centaines de kilooctets, alors que
la mémoire vive RAM peut se résumer à 8 ko (il y en en général bien plus,
tout dépendant de la carte).
Ne confondez pas l’espace de mémoire flash avec le système de fichiers d’une clé
mémoire flash USB.

Sur certaines cartes, et notamment la micro:bit, la mémoire flash est limitée


elle aussi, ce qui définit une taille maximale pour votre script. Un script
converti en un fichier au format hex peut être limité à 2 ko. Vous pouvez
cependant stocker des fichiers de plus grande taille dans le système de
fichiers, qui offre environ 20 ko. Dans tous les cas, vous devez savoir que
vous pouvez réduire la taille de votre script par plusieurs techniques : en
supprimant les commentaires, en réduisant l’indentation à deux espaces au
lieu des quatre de coutume en Python, ou en supprimant les lignes vides.
Vous pourrez ainsi placer un peu plus de code dans la mémoire flash.
Si vous ne modifiez pas sa configuration, MicroPython fonctionne de la
même façon que CPython : il commence par analyser le script Python, le
compile pour produire un fichier dans un format intermédiaire (un
intercode) appelé format codoctet (bytecode) puis lance l’exécution de ce
dernier dans une machine virtuelle. Ce processus consomme de la mémoire
vive à deux moments : au moment de la compilation de Python vers le
format codoctet, et lors de l’exécution du codoctet dans la machine
virtuelle. Un autre phénomène qui dépense de l’espace mémoire est la
fragmentation de la zone du tas (heap). Nous y reviendrons.
Pour traduire le code source Python en codoctet, le compilateur a besoin
d’occuper un peu d’espace mémoire. Une fois la compilation terminée, cet
espace mémoire redevient disponible pour MicroPython. Le codoctet est
stocké en mémoire vive. Dans certains cas, et notamment lorsque vous avez
compilé plusieurs modules de librairies (ce qui a pour effet que le codoctet
est stocké en mémoire vive), vous rencontrerez une erreur d’exception
mémoire en arrivant à la directive import, parce que le compilateur n’a plus
d’espace mémoire pour travailler.
Une autre cause d’erreur mémoire liée à la compilation découle de la
création d’objets globaux en mémoire vive par un module de librairie au
moment de l’importation (la mémoire n’est plus disponible pour les tâches
de compilation suivantes). Évitez donc que du code s’exécute lors de
l’importation. Organisez-vous pour que le code d’initialisation soit exécuté
une fois que tous les modules de librairies ont été importés. Vous disposez
ainsi du maximum d’espace mémoire pour la compilation.
Si vous avez appliqué ces techniques et que vous aviez toujours un
problème de mémoire pendant la compilation, il reste encore deux autres
options. Vous choisirez l’une ou l’autre selon la version de MicroPython
utilisée.

Congélation de code
Il est en effet possible de « congeler » le code d’un module de librairie ou
du code intermédiaire codoctet. Cela consiste à injecter le code
correspondant dans l’image du microgiciel (firmware) que vous implantez
ensuite dans le microcontrôleur. Les détails de réalisation variant d’un
microcontrôleur à l’autre, vous consulterez les instructions, généralement
fournies dans un fichier texte README. D’ailleurs, certaines variantes de
MicroPython ne permettent pas cette congélation. Dans ses grandes lignes,
la procédure est la suivante : vous stockez le code Python à congeler dans
un sous-répertoire (différent selon qu’il s’agisse d’un module de librairie ou
de codoctet). Vous demandez ensuite la production d’une nouvelle version
du microgiciel, en spécifiant éventuellement une commande pour la
congélation du code puis vous réimplantez le microgiciel sur la carte. Le
code que vous avez ainsi congelé dans le microgiciel devient ensuite
accessible par une directive import.
S’il n’est pas possible ou pas approprié de recompiler le microgiciel de
MicroPython, vous pouvez précompiler le codoctet intermédiaire sur votre
PC pour obtenir un fichier exécutable au format .mpy. Vous pouvez alors
transférer ce fichier dans le système de fichiers de la carte ; le fichier n’a
plus besoin d’être compilé ni interprété. Vous utilisez à cet effet le cross-
compilateur MicroPython qui se trouve dans le sous-répertoire nommé mpy-
cross du code source du langage. Ce compilateur fonctionne sur tous les
systèmes de type Unix. De façon abrégée, il correspond à la commande
suivante :

$ ./mpy-cross mon_module.py

Vous obtenez en sortie un fichier portant le nom mon_module.mpy qu’il ne


reste plus qu’à transférer dans le système de fichiers de la carte. Pour
l’utiliser, il suffit d’utiliser une directive d’importation comme pour
n’importe quelle autre librairie Python, en écrivant import mon_ module.

Écologie de la mémoire
L’exécution du code intermédiaire codoctet demande d’utiliser de l’espace
mémoire en mémoire vive RAM. Plusieurs techniques et astuces sont
disponibles pour réduire l’empreinte mémoire pendant la phase d’exécution.
Plusieurs portages de MicroPython, mais pas tous, reconnaissent le mot-clé
nommé const, qui ressemble beaucoup à la directive #define du langage C.
Sous l’effet de ce mot-clé, le compilateur va remplacer les références au
nom de l’élément de donnée par les valeurs littérales. Cela diminue la taille
du code intermédiaire et donc l’occupation mémoire. Vérifions cela par un
exemple :

from micropython import const


LIGNES = const(33)
_COLS = const(0x10)
a = LIGNES
b = _COLS
La valeur fournie en paramètre de const doit pouvoir être comprise pendant
la compilation comme une valeur numérique entière. Dans l’exemple, la
valeur de LIGNE va occuper de l’espace dans le dictionnaire des éléments
globaux, parce qu’elle doit être disponible depuis les autres modules de
librairies (elles doivent pouvoir l’importer). En revanche, la valeur _COLS
n’est pas disponible en dehors du module courant, par la présence du
caractère de soulignement. De ce fait, elle ne va occuper aucun espace en
mémoire RAM.
Une autre technique pour économiser la mémoire lorsque vous définissez
des structures de données constantes consiste à adopter le type d’objets
bytes si vous comptez congeler le code intermédiaire codoctet. En effet, les
objets de type bytes sont immuables (à la différence de bytearray) et le
compilateur va les maintenir dans l’espace de mémoire flash au lieu de les
transférer dans l’espace de mémoire vive. Vous pouvez ensuite utiliser la
librairie ustruct de MicroPython pour réaliser les conversions entre le type
bytes et les autres types du langage Python.

L’astuce s’applique à d’autres types de valeurs immuables comme les


chaînes de caractères, les nombres entiers, réels (à virgule flottante) et
complexes. Les valeurs correspondantes seront placées en mémoire flash.
Le seul espace consommé en mémoire vive sera celui nécessaire pour y
stocker une référence à l’objet qui réside en mémoire flash.
Notez que dans la version actuelle de MicroPython, les structures de
données immuables que sont les tuples ne bénéficient pas encore de cette
possibilité, bien qu’elles contiennent des valeurs immuables.
Si vous êtes habitué à rédiger du code source Python dans des
environnements moins contraignants, vous avez sans doute l’habitude de
créer beaucoup d’objets. Il vous faudra peut-être modifier vos habitudes de
programmation pour utiliser la mémoire vive plus efficacement.
Par exemple, lorsque vous avez besoin de rabouter deux chaînes (opération
de concaténation), vous pouvez l’exprimer des deux façons suivantes :
marsouin1 = "mar" + "soin" # Mauvais
marsouin2 = "mar" "soin" # Bon

La chaîne résultante est strictement la même, mais dans la première


technique, vous obligez le compilateur à créer deux objets de type chaîne et
à consommer de l’espace mémoire pour réaliser la concaténation et
produire le troisième objet. La seconde écriture réalise la concaténation
pendant la compilation.
Pour vos opérations de lecture et d’écriture de chaînes de caractères en
relation avec un flux tel qu’un fichier (stream), il est préférable de travailler
par petits pas. Au lieu de créer un objet chaîne volumineux qui consomme
beaucoup d’espace mémoire, cherchez à travailler avec de petites chaînes.
Dans le même domaine, lorsque vous avez besoin de collecter des données,
par exemple en relation avec le protocole I2C, mieux vaut définir un tampon
au préalable au lieu de créer à répétition de nouveaux objets dans une
boucle :

# Mauvais
while True:
var = [Link](100)
# Suite du traitement

# Bien
tampon = bytearray(100)
while True:
[Link](tampon)
# Suite du traitement du contenu de
tampon

La première boucle de l’exemple ci-dessus demande de créer un nouveau


tampon mémoire à chaque tour de boucle. Il est beaucoup plus efficace de
créer d’abord le tampon avant d’entrer dans la boucle, d’autant que cela
réduit la survenue du phénomène de fragmentation mémoire, sur lequel
nous reviendrons.
Une autre technique pour préserver l’espace mémoire consiste à utiliser une
représentation plus efficace en fonction de la valeur à stocker. Par exemple,
une valeur numérique entière occupe normalement 4 octets. Pour stocker
des valeurs numériques dont vous savez que la valeur ne sera jamais très
grande, vous avez intérêt à utiliser le type octet bytes. Par exemple, la
mention (1, 2, 3) va occuper plus de mémoire que b’\1\2\3’. Et si ces
valeurs étaient définies dans un module dont vous congelez le codoctet
intermédiaire, vos objets du type bytes seraient placés en mémoire flash et
non en mémoire vive.

Fragmentation mémoire
Vous devez être conscient du problème que peut causer la fragmentation de
l’espace mémoire. Supposez que vous demandiez de créer deux objets
portant les noms A et B. Si à un certain moment de l’exécution, l’objet A
n’est plus utile (il ne fait plus l’objet d’aucune référence), alors que l’objet
B reste requis, vous faites apparaître un trou dans l’espace mémoire. Seul
un objet dont la taille n’est pas supérieure à celle de A pourra être stocké
dans le trou laissé. Si votre programme effectue beaucoup de créations et de
suppressions d’objets, cette fragmentation de l’espace mémoire va prendre
des proportions gênantes. Vous pourriez vous trouver dans une situation où
il y a beaucoup d’espace mémoire libre, mais très peu de blocs mémoire de
taille suffisante pour stocker certains objets trop volumineux. Vous
aboutissez ainsi à une erreur mémoire alors qu’il reste de l’espace
mémoire !
C’est la raison pour laquelle mieux vaut utiliser un seul objet de taille fixe,
par exemple pour faire un tampon réutilisable. Lorsque j’ai besoin de
grands tampons permanents, je les crée en tout début de programme, avant
que la fragmentation ait pu commencer à apparaître.
Le ramasse-miettes
La réutilisation et le nettoyage de l’espace mémoire sont gérés
automatiquement dans MicroPython par son mécanisme de ramasse-miettes
(garbage collector). Vous savez qu’il existe dans la mémoire une section
spéciale appelée tas (heap). C’est dans ce tas qu’est stocké un objet lorsque
vous le créez. Lorsque l’objet disparaît, c’est-à-dire qu’il n’y a plus aucune
référence qui permettrait de l’utiliser, le bloc de mémoire de tas
correspondant est automatiquement réutilisé par le ramasse-miettes pour
d’autres objets. Ce processus est automatique, et c’est pourquoi vous n’avez
en général pas à vous soucier de la gestion manuelle de la mémoire.
Vous pouvez cependant prendre le contrôle de ce mécanisme grâce à la
librairie gc pour forcer la récupération des objets redondants au moyen de
sa fonction [Link](). Notez cependant que si vous avez besoin de faire
appel vous-même au ramasse-miettes, vous obtiendrez de meilleures
performances si vous le faites souvent par petites touches.

Débogage MicroPython
La librairie gc ainsi que la librairie micropython contiennent des fonctions
pour examiner l’utilisation de l’espace mémoire, notamment pour la mise
au point. Les deux fonctions gc.mem_ free() et gc.mem_alloc() permettent
par exemple de connaître respectivement la quantité d’espace libre et celle
d’espace occupé dans la zone du tas. La fonction micropython.mem_ info()
affiche un bilan de l’occupation mémoire lorsque vous l’appelez sans aucun
paramètre. Elle va présenter la table de l’occupation dans la zone du tas si
vous lui transmettez un paramètre arbitraire comme (1) :

>>> import micropython


>>> micropython.mem_info(1)
stack: 448 out of 15360
GC: total: 102080, used: 2832, free: 99248
No. of 1-blocks: 45, 2-blocks: 14, max blk
sz: 68, max free sz: 6194
GC memory layout; from 20003140:
00000:
h=hhhBhhhhhBTBhhh==h=hh===hBhTh=hSBhTh=hh=
hSBhTh=hh=hShhhh=Th==h
00400:
=Bh=Bh=h=Shhh=======h=====================
======================
00800:
========================hB..h...h========h
========h=....h=......

(suivent xx lignes de blocs vides et


libres)

18c00:
..........................................
..
Voici la signification des symboles utilisés :

• . : bloc libre

• h : bloc de tête (head)

• = : bloc de queue (tail), parfois aussi « t » au lieu de « = »

• m : bloc de tête marqué

• T : tuple

• L : liste
• D : dict

• F : float

• B : codoctet (byte code)

• M : module
Chaque lettre dans ce tableau représente un bloc mémoire unique
de 16 octets. En effet, la réservation d’espace mémoire se fait au minimum
par bloc de 16 octets.
Une autre fonction extraordinairement utile pendant le débogage s’écrit
micropython.alloc_ emergency_exception_buf(). Normalement, dans une
session d’interpréteur REPL, et si les conditions mémoire sont vraiment à la
limite, vous n’obtiendrez pas de trace détaillée d’une erreur lorsque vous
subissez une exception. Vous obtiendrez seulement le nom de l’exception.
Apprenez qu’il est possible de demander à MicroPython de préréserver
plusieurs octets en mémoire vive pour y placer un tampon d’exception, ce
qui vous permet de récupérer des informations de trace très utiles. C’est
indispensable pour le débogage, même si cette technique ne doit pas être
considérée comme normale dans les versions finalisées de vos projets,
puisqu’elle consomme de l’espace mémoire. Une bonne technique consiste
à prévoir la fonction tout au début de votre script principal [Link] afin
qu’elle soit active pour tout le reste du code. Pour la taille du tampon, une
valeur de 100 octets semble raisonnable en pratique :

import micropython

micropython.alloc_emergency_exception_buf(
100)

Une dernière astuce de débogage que j’aimerais partager a été conçue au


départ par Carlos Pereira Atencio. Il s’agit du pseudo point d’arrêt. Dans le
langage Python normal, si vous utilisez un débogueur, vous pouvez poser
un point d’arrêt en désignant le numéro d’une ligne du code source ;
l’exécution du programme sera suspendue juste avant les instructions
correspondantes, ce qui permettra de visiter l’état du programme pendant
cette pause dans l’exécution. Il n’existe pas de débogueur pour
MicroPython, mais vous pouvez mettre en place une boucle perpétuelle
juste avant la partie du code qui vous intéresse. MicroPython va gentiment
attendre que l’interpréteur REPL soit connecté et que vous ayez frappé la
combinaison Ctrl + C. Il vous suffit d’ajouter une boucle de type while
True : pass. Cette astuce ne peut pas rivaliser avec un véritable point
d’arrêt, et n’est pas applicable dans toutes les situations, mais cela vous
permet de simuler la suspension d’exécution comme pour un point d’arrêt,
et donc d’interroger les valeurs des variables et réaliser d’autres opérations
d’investigation.
Considérations de performances
Les microcontrôleurs sur lesquels fonctionnent les programmes écrits en
MicroPython sont beaucoup moins performants que les microprocesseurs
des ordinateurs habituels. Cela peut souvent poser des problèmes, qui vont
vous obliger à appliquer des techniques pour augmenter les performances
de votre code.
La première condition pour obtenir du code source performant consiste à
utiliser les algorithmes les plus appropriés et de la façon la plus efficace.
C’est du ressort du programmeur, et cela n’entre pas dans le périmètre de ce
livre. En effet, c’est à chacun de devenir un programmeur compétent, c’est-
à-dire un programmeur qui sait où trouver et comment appliquer un
algorithme efficace pour résoudre chacun de ses problèmes.
Un autre domaine d’amélioration des performances consiste à bien utiliser
les ressources, et notamment l’espace mémoire, ce que nous avons vu dans
les pages précédentes.
Une dernière technique, tellement évidente qu’elle est souvent oubliée,
consiste à vérifier que le code exécutable fait réellement ce que vous pensez
qu’il doit faire.
Du fait qu’il est impossible d’ajouter un débogueur pour voir in vitro ce que
fait le programme MicroPython pendant son exécution, il ne vous reste qu’à
utiliser l’interpréteur REPL, en insérant aux bons endroits des instructions
d’affichage print(). Rappelons que lorsque vous suspendez l’exécution du
programme, tous les objets sont présents, et vous pouvez les inspecter de
façon interactive grâce à REPL.
Certains microcontrôleurs qui fonctionnent avec MicroPython ne sont pas
dotés d’une unité de traitement des nombres à virgule flottante, ou nombres
réels. Autrement dit, les opérations sur les réels sont alors toutes réalisées
par logiciel, et sont donc beaucoup plus lentes. Si cela vous concerne,
cherchez dans la mesure du possible à transformer vos opérations pour
qu’elles utilisent des valeurs entières et n’utilisez l’arithmétique à virgule
flottante que dans les portions de code où la sanction en termes de
performance ne sera pas trop visible.
Profilage
Si après avoir appliqué ces techniques, vous continuez à souffrir de
problèmes de performances, il va falloir effectuer un profilage du code. En
supposant que votre version de MicroPython dispose de la librairie time,
vous pouvez demander de chronométrer n’importe quelle méthode ou
fonction en la décorant avec une fonction de mesure, comme ceci :

def timed_function(f, *args, **kwargs):


myname = str(f).split(' ')[1]
def new_func(*args, **kwargs):
t = time.ticks_us()
result = f(*args, **kwargs)
delta = time.ticks_diff(time.ticks_us(),
t)
print('Function {} Time =
{:6.3f}ms'.format(myname, delta/1000))
return result
return new_func

Une fois que vous avez ainsi repéré un goulet d’étranglement, plusieurs
options sont offertes, en complément de l’application des techniques
d’optimisation indiquées plus haut.

Génération de code machine


Certaines cartes permettent de demander au compilateur de générer des
codes d’instructions ARM natives, et non du code intermédiaire codoctet.
La plupart du code source Python peut recevoir cette optimisation, mais les
gestionnaires de contexte et les générateurs ne le permettent pas. Si vous
utilisez raise(), vous devez fournir un paramètre. Pour demander cette
compilation vers le code machine, il suffit de décorer la fonction concernée
avec [Link]. Évidemment, le prix à payer est que vous
augmentez la taille du code exécutable, mais le résultat fonctionne deux fois
plus vite qu’avec du code intermédiaire.
Pour certains microcontrôleurs, vous pouvez demander de générer un
troisième format en sortie de compilation, en plus du codoctet et du code
natif : le code viper. En ajoutant la fonction de décoration
[Link](), vous faites générer une version optimisée du code
machine pour l’architecture de processeurs ARM, ce qui permet d’améliorer
le traitement des valeurs numériques entières ; ceci dit, toutes les fonctions
de Python ne sont pas supportées avec cette technique.
La production de code natif et de code viper constitue une sorte de
compilateur à la volée (JIT, Just In Time) manuel, comme celui qui est
intégré à PyPy. Rappelons qu’un compilateur JIT commence à analyser le
code source et bascule de la génération de codoctet vers celle de code natif
si les conditions sont réunies, améliorant ainsi automatiquement les
performances. Cela dit, un compilateur à la volée est complexe, et ne
trouvera pas sa place sur un microcontrôleur. L’optimisation manuelle de
certains blocs de code est en général suffisante.

Code assembleur
Une autre technique permettant d’améliorer les performances consiste à
faire injecter du code assembleur en ligne dans le cas des microcontrôleurs
qui obéissent à l’architecture Thumb2 de ARM. Nous n’avons pas la place
dans ce livre pour présenter en détail le langage assembleur, mais l’exemple
suivant montre comment définir une fonction avec de l’assembleur en ligne,
fonction que nous pouvons appeler depuis le code Python :

@micropython.asm_thumb
def asm_add(r0, r1):
add(r0, r0, r1)

Vous constatez que la fonction doit être décorée par


micropython.asm_thumb. Les fonctions assembleur acceptent jusqu’à quatre
paramètres d’entrée, qui doivent absolument porter les noms r0, r1, r2 et
r3. Dans l’exemple précédent, il y a deux paramètres que la fonction
additionne, formant l’équivalent de r0 = r0 + r1 en Python. Par
convention, le résultat est stocké dans le registre r0, et c’est son contenu qui
est renvoyé sans requérir de mention explicite.

Langage C
Une dernière technique consiste à écrire le code source en langage C, à le
recompiler en MicroPython puis à l’utiliser comme module d’extension. Le
langage MicroPython est écrit en langage C ANSI. Il est modulaire, bien
testé et de nombreux exemples montrent comment procéder. Ceux d’entre
vous qui ont une expérience du langage C doivent cependant être avertis
d’un point : ici aussi, il ne faut pas oublier les règles du Zen de
MicroPython. On n’écrit pas en langage C pour microcontrôleur comme
pour un ordinateur habituel. Il faut rester concis, toujours garder en
mémoire les ressources limitées, notamment en termes de mémoire, et
chercher à être efficace.
Il n’est pas inutile de rappeler pour conclure que MicroPython reste pour
l’essentiel du langage Python. Dans les scripts pas trop longs, l’écriture
dans l’esprit pythonesque conviendra tout à fait. Si vous rencontrez des
problèmes mémoire ou de performances, appliquez le Zen de MicroPython
et les différentes techniques présentées dans ce chapitre. Le pire qui puisse
vous arriver sera d’apprendre de nouvelles techniques. Vous finirez toujours
par surmonter l’obstacle, et sauterez de joie quand enfin votre superbe
démonstration blinkenlight s’exécute sans ralentissement ni erreur
mémoire !
CHAPITRE 14
Ce n’est qu’un début
MicroPython est un projet assez récent, mais il progresse à grands pas.
Le langage fait l’objet d’adaptations vers de nouveaux matériels. Sa
réalisation est sans cesse peaufinée. Grâce au phénomène micro:bit, des
milliers de ressources pédagogiques sont disponibles. De grands acteurs du
domaine des faiseurs (makers) et des fabricants de composants l’ont adopté
dans leurs produits (par exemple la société Adafruit). Grâce à l’Agence
spatiale européenne, il y aura bientôt du code MicroPython en orbite. Dans
le monde entier, les conférences Python (PyCon) font de plus en plus de
place aux sujets concernant MicroPython (certaines distribuant des badges
interactifs MicroPython aux participants). De nouvelles librairies
apparaissent continuellement pour faciliter l’exploitation de nouveaux
périphériques.
Le moment est idéal pour rejoindre le mouvement et participer à
l’exploration de la programmation pour systèmes embarqués en Python.
La lecture de ce livre n’est qu’une étape. Si vous possédez une carte
fonctionnant avec MicroPython, ne l’abandonnez pas dans un tiroir.
Organisez un week-end de codage, servez-vous de la carte pour un projet
professionnel ou comme point de départ d’un atelier à proposer à votre
groupe d’utilisateurs local. Si vous avez des enfants, intéressez-les à la
programmation (pourquoi ne pas créer une alarme parentale qui prévient
l’enfant que vous entrez dans sa chambre ?). En quelques projets, vous
aurez découvert les possibilités de MicroPython pour produire des projets
divers dans des domaines inimaginables.
Rejoindre la communauté
Nous avons indiqué dans l’introduction que la communauté Python était
l’une des plus efficaces dans l’univers de la programmation informatique.
La Python Software Foundation (PSF) est le point de rencontre
incontournable des bénévoles qui veulent faire progresser les technologies
open source en relation avec le langage Python.
Vous pouvez rejoindre le mouvement en soutenant la fondation dans ses
missions et même en devenir un acteur. La PSF possède un comité
d’attribution de bourses pour Python. Si vous avez une idée d’atelier
MicroPython et cherchez des financements, présentez un dossier. Les
formalités sont simples et le comité répond rapidement.
Aller plus loin
Si vous voulez partager votre code source MicroPython, adapter
MicroPython vers une nouvelle carte ou produire des librairies de fonctions
MicroPython, commencez toujours par le site officiel de MicroPython. Vous
y trouverez des liens vers le code source, le référentiel des librairies
adaptées à partir de celles de Python et les actualités concernant les travaux
de portage en cours.
Pour discuter avec d’autres utilisateurs de Python, visitez la section
francophone du site de la Python Software Foundation : [Link].
Voyez aussi la page d’accueil
francophone du site officiel :
[Link]/moin/FrenchLanguage.

Pour MicroPython, il y a le canal IRC en anglais #micropython sur


Freenode et une communauté microbit sur Slack.
Si vous vous sentez prêt à contribuer à la communauté, il faut commencer
par parcourir la documentation en ligne de MicroPython pour découvrir la
structure du projet, les conventions de codage et les attentes en termes de
qualité de ce que vous livrerez, notamment en réalisant des tests.
Des tutoriels commencent à apparaître (certains en français), ainsi que des
descriptions de projets MicroPython. La société Adafruit en propose déjà un
certain nombre. Visitez aussi le site [Link]. En 2015 a été organisée
une tournée mondiale micro:bit pendant laquelle des pythonistes ont
constitué une chaîne d’échange par courriel en produisant toutes sortes de
projets éducatifs en MicroPython pour la carte micro:bit.
Alors, plongez, et n’oubliez pas.. :

Codez,
Bricolez,
Moins c'est plus,
Restez simple,
Faites léger et beau à la fois.
Osez ! Démontez tout ! Apprenez et amusez-
vous !
Exprimez-vous avec MicroPython.

Codez dans la joie ! :-)

- Le Zen de MicroPython
Sommaire

Couverture
Programmer en MicroPython - programmation
embarquée de microcontrôleurs avec Python
Copyright
Avant-propos
Préface
Conventions typographiques
Code source des exemples

CHAPITRE 1. Qu’est-ce que MicroPython ?


Pourquoi micro ?
Pourquoi Python ?
La genèse de MicroPython
Lancez-vous !

CHAPITRE 2. PyBoard
Le matériel de la PyBoard
Configuration de la carte
CHAPITRE 3. BBC micro:bit
Le matériel
Configuration initiale de la carte

CHAPITRE 4. Circuit Playground Express (CPX)


Présentation de la carte CPX
Préparation de la carte

CHAPITRE 5. ESP8266 et ESP32


Présentation matérielle
Préparation et configuration

CHAPITRE 6. Pensez embarqué !


Motivations humaines
Potentialités d’enchantement
Étapes vers l’enchantement

CHAPITRE 7. Expressions visuelles


Blinkenlights
Le ruban NeoPixels
Texte, images et animations
Afficheur LCD couleur pour PyBoard

CHAPITRE 8. Capteurs et détection


Boutons et toucher capacitif
Accéléromètre et boussole
Son, lumière et température
Capteurs externes

CHAPITRE 9. GPIO
Les broches
Le protocole série UART
Le protocole SPI

Le protocole I2C
Autres techniques et protocoles GPIO

CHAPITRE 10. Accès réseau


Communications infrarouges CPX
Communications radio micro:bit
Le Wi-Fi d’ESP8266 et ESP32
Le protocole MQTT

CHAPITRE 11. Paroles et musique


Ondes sonores et CPX
De la musique avec micro:bit
Synthèse vocale

CHAPITRE 12. Robots


Trundle bot
Bit:Bot, un robot de course
CHAPITRE 13. L’esprit de MicroPython
Le Zen de MicroPython
Gestion mémoire
Considérations de performances

CHAPITRE 14. Ce n’est qu’un début


Rejoindre la communauté
Aller plus loin

Vous aimerez peut-être aussi