[Tuto] Programmation non bloquante pour Arduino & Co

  • Nico! C’est quoi le programme du jour ?
  • Heu… T’es sûr de vouloir le savoir?
  • Bah oui, ça fait trop longtemps, j’en peux plus d’attendre!
  • Ok, alors si on parlait Arduino et temps réel? Je vais te montrer comment on programme des événements récurrents de façon non bloquante!

Une petite contextualisation me semble nécessaire. En ce moment je travaille sur une interface MIDI, pour synchroniser un microcontrôleur (Teensy) avec des instruments de musique. Pour la faire courte, la Teensy va donner un tempo en BPM, et les instruments MIDI vont se caler dessus.

La Teensy est géniale dans ce contexte, parce qu’elle peut se faire passer pour un périphérique MIDI standard et son API dispose de toute une batterie de fonctions pour envoyer des instructions MIDI à qui veut bien les écouter.

Mais pour l’instant, pas besoin de rentrer dans le protocole d’horloge midi, on va se contenter d’afficher du texte dans le moniteur série, et après si vous êtes sages, on fera clignoter des LEDs. Houlala, mais quel programme trépidant!

1. Approche naïve pour gérer un événement récurrent

Si vous êtes en train de lire ceci, il y a de forte chances pour que vous ayez testé votre première Arduino avec l’exemple blink.

Rien de choquant, on est tous passé par là :).

Affichons plutôt des messages, ce sera plus pratique pour la suite:

Si j’ai maintenant envie d’afficher « Paf » toutes les 500ms et « Pif » toutes les 3 secondes… Comment faire?
On pourrait utiliser un compteur, mettre un delay(500), à chaque tour afficher Paf, et tous les 6 tours afficher Pif.

Ça peut vite devenir très compliqué l’histoire. Et d’autant plus que pendant l’appel à delay(), il ne se passe rien, le programme est bloqué pendant 500ms.
C’est la méga-cagade si on doit faire d’autres traitements en parallèle (lecture d’entrées, affichage…).

2. Approche non bloquante

Pour remédier à cela, on doit avoir une approche non bloquante, basée sur l’heure actuelle (ou plus précisément sur le nombre de millisecondes (ou microsecondes) écoulées depuis le démarrage du programme).
On va utiliser les fonction millis() ou micros(), selon la précision désirée (respectivement à la milliseconde ou à la microseconde près).

L’exemple proposé dans l’IDE Arduino est BlinkWithoutDelay, et on peut l’adapter ainsi pour notre exemple:

Dans ce cas, à chaque tour de loop(), on va regarder si la durée nécessaire est passée, et si ce n’est pas le cas on laisse filer (et du coup on peut faire autre chose plutôt que d’attendre bêtement).

3. Approche plus encapsulée

On est déjà bien mieux que dans le tout premier exemple, mais il y a des variables globales qui se baladent, des grosses conditions dans les if(), ce n’est pas encore très élégant.

Pour mon projet, j’ai encapsulé ce fonctionnement dans une classe C++ histoire de faciliter l’utilisation:

Pour reprendre notre exemple précédent, on aurait donc:

C’est plus léger. Plus de variables globales pour stocker des états dont on se fout royalement dans le programme principal.
Vous noterez que l’heure (ici en microsecondes) est lue une seule fois, puis passée en paramètre aux méthodes de test. Ça permet d’éviter des décalages de temps causés par l’exécution du code qui peut être « longue » (~100µs pour un analogRead() par exemple). En faisant comme ça, on travaille sur la même référence de temps pour toutes les opérations.

4. Application : Faire clignoter une LED de façon contrôlée et non bloquante

Je dis LED, mais le concept s’applique évidemment au contrôle de moteurs pas à pas, à l’envoi de signaux d’horloge MIDI, et que sais-je encore.

Voici l’interface de la classe :

Si je veux faire clignoter une LED à 3Hz, pendant 5 impulsions, il me suffit d’écrire:

Si je veux faire 3 clignotements à 5 Hz chaque seconde, il me suffit d’écrire le programme suivant:

Les deux projets sont dispo sur GitHub et dans les bibliothèques Arduino avec des exemples!

https://github.com/toxnico/DMTimer

https://github.com/toxnico/DMOscillator

Capturer du son avec une Arduino Uno

Je vous ai déjà parlé de ma femme?

Bon. Après la chute des dominos, elle veut maintenant photographier un ballon de baudruche qui explose, en projetant de la farine qu’on aura préalablement introduite dans le ballon susmentionné. Je ne me prononcerai pas, car je suis assez mal placé pour juger de la folie des gens. Je vais d’ailleurs le prouver sous peu… Non, et puis ça peut quand même avoir un chouette rendu, ce ballon!

Et du coup, j’ai commencé à bricoler avec un micro à électret, pour voir comment je pouvais récupérer le signal sur une Arduino. De fil en aiguille, j’ai fini par carrément enregistrer un fichier son à partir du montage. Qui parlait de folie?

1. Connecter un micro à électret

Voici comment ça se passe. Comme d’hab, s’il y a des électroniciens (des vrais) dans la salle, j’attends les conseils avisés… Je ne suis pas du tout expert dans le domaine.

A l’oscilloscope, on peut voir qu’en soufflant dans le micro, on obtient un pic à environ 0.1V, ce qui est trop peu pour être traité par l’ADC de Arduino. Nous devons donc…

2. Amplifier le signal du micro

Rhooo mais quelle transition!

Pour l’amplification, j’ai pensé à un bête transistor; mais je recherche surtout un gain en tension, donc j’ai opté pour un petit amplificateur opérationnel, le LM358.

Les attentifs verront que RF=320k, alors que par la suite je parle de 220k. Vous referiez une capture, vous?

Selon la fiche technique du LM358, la tension maximale de sortie est égale à la tension d’alimentation moins 1.5v. Dans mon cas, la tension d’alimentation étant de 5v, je peux espérer un maximum de 3.5v en sortie.

Dans ce montage en mode inverseur, le gain en tension est égal à RF/RI, soit 220/4.7 = 47. Donc un pic à 0.1v à l’entrée ressortirait à 4.7v. Sauf qu’en vrai, on l’a vu, il sera plafonné à 3.5v environ. Mais pour la démo, on s’en fout en fait.

3. Acquérir le signal sur l’Arduino et le retransmettre à un PC par le port série

Alors maintenant, l’idée est de lire le signal de sortie de l’ampli, et de le resservir au PC pour construire un semblant de fichier son, brut de brut.

La première étape consiste donc écrire un petit programme pour l’Arduino, qui lit la valeur du signal à intervalles réguliers (4KHz) et les rebalance aussitôt à travers le port série.

J’ai décidé de brider à 4KHz, parce qu’une lecture avec analogRead(9) prend un temps non négligeable: 100 microsecondes.

Pour être large, une mesure toutes les 250μs, c’est bon. Et c’est de toute façon bien plus rapide que de trouver comment faire un μ sur un clavier suisse.

4. Lire les valeurs brutes depuis le port série du PC, et les enregistrer dans un fichier

Tout est dans le titre. Un peu de python fera l’affaire

Fonctionnement: on démarre le programme, on braille « Ah! Ah! Ah! » (ou une bonne vieille exclamation si chère à Gilles de la Tourette) dans le micro, et on quitte avec Ctrl+C. Il en ressort un fichier output.raw

5. Importer le fichier raw dans Audacity

Je vous préviens d’abord, le son est tout à fait crado.

Il faut préalablement installer Audacity.

Démarrer Audacity, et aller dans Fichier>Importer>Données brutes(Raw).

Sélectionner le fichier créé au point 4.

  • Fréquence d’échantillonnage : 4000Hz.
  • Encodage: Unsigned 8 bits PCM.
  • 1 canal.

6. Écouter l’horreur

AAAhkssssshhhAAHkkssshhhLAPINksshhhhCULssshhh

Tests unitaires en C++

Monsieur Jourdain: Adieu Sganarelle, quoi de prévu pour aujourd’hui?

Sganarelle: Les tests unitaires.

Monsieur Jourdain: Ah… Mais vous êtes au courant que même si l’on sait que c’est bien, on n’aime pas faire ça…

Sganarelle: Ouais, mais là j’ai un argument choc: le développement sur microcontrôleurs!

Monsieur Jourdain: …

Ça annonce du lourd, n’est-ce pas?

J’ai eu très récemment besoin d’écrire une classe de pilotage de moteur pas à pas pour un microcontrôleur type Arduino (Teensy pour être exact, mais là n’est pas la question).

La classe en question doit gérer l’accélération et décélération du moteur en début et fin de mouvement.

Vous allez me dire, il existe déjà des bibliothèques pour ça. Hé bien oui, mais vous commencez à me connaitre, et j’étais très curieux de développer mon propre contrôleur de moteur. C’était d’ailleurs très instructif de se replonger dans les maths de terminale S pour calculer tout ce bordel; notamment réapprendre la notion d’intégrale pour calculer la distance parcourue pendant la durée du mouvement, avec une accélération et décélération constantes en début et fin de mouvement.

J’en ai aussi profité pour découvrir des outils mathématiques en ligne extrêmement pratiques, comme Desmos ça, c’est la courbe de vitesse par rapport au temps

En résumé, j’ai dû écrire une série de fonctions de calcul purement mathématiques pour calculer la durée d’accélération, le nombre de pas selon l’accélération et la vitesse max, ce genre de trucs.

J’aurais pu y aller empiriquement, et débugger avec des traces dans la console Arduino, à base de Serial.println(), mais franchement… franchement… C’est super relou de débugger sur ce genre de plateforme…

Alors dans un premier temps, j’ai écrit ces fonctions en Python (oui, je m’y suis mis et je kiffe pas mal), et comme Python dispose en standard d’un environnement de tests unitaires, c’était cool.

Puis, une fois mes fonctions validées en Python, je les ai transposées en C++ dans une classe statique. Mais là, c’est méga chiant, parce que les langages sont quand même très différents :/

J’ai cherché un environnement de tests unitaires en C++ (j’en avais de mauvais souvenirs en termes de dépendance, et c’est essentiellement pour ça que je suis passé par Python). Et j’ai trouvé la perle: Catch

C’est HYPER simple à mettre en place. Aucun héritage, ni nommage conventionnel à faire.
Juste UN fichier d’en-tête à inclure, et une syntaxe à la cool pour écrire les tests 🙂

Imaginons que j’aie à tester une classe qui permet d’effectuer des opérations très complexes, telles qu’ajouter deux nombres, calculer une puissance ou pire, diviser deux nombres (float).

Pour info, REQUIRE arrête l’exécution des tests en cas d’erreur, alors que CHECK indique l’erreur et continue avec les autres tests. Pour plus de détails (notamment sur l’approximation des nombres à virgule flottante), voir la documentation officielle.

Et qu’est-ce qu’il dit?

Être ou ne pas être… Python

Voilà bien longtemps que je me dis qu’il faudrait me mettre à Python.
Selon les adeptes de ce langage, une fois qu’on y a touché, c’est comme le crack. Ou disons comme le fondant au chocolat.

Mais il n’y a pas moyen, je n’arrive pas à m’y coller… Et à chaque fois que j’essaie de me lancer pour un nouveau projet, je tombe toujours sur le même problème: Python 2 ou python 3?

Beaucoup de librairies ne sont disponibles qu’en Python 2, alors que Python 3 existe officiellement depuis… 2008. Oui, 9 ans. Et à chaque fois, les librairies qui me seraient utiles pour le projet du moment n’ont pas été portées sur Python 3, alors finalement je pars sur du bon vieux C#.

Allez, je réessaye, on verra bien… Cette fois, j’ai trouvé Pillow, le portage de PIL en Python 3, qui devrait faire l’affaire. En avant Guingamp!

[PS] Je viens d’apprendre que le serpent qui se mord la queue est un symbole qui s’appelle Ouroboros
L’image provient du site http://www.animogen.com. Il a l’air malin maintenant…

Créer des templates dans Atom

Salut les barbus!

Vous qui programmez (et je sais qu’il y en a, pas la peine vous cacher!), peut-être utilisez-vous l’éditeur Atom?

C’est ce que j’utilise, en tout cas pour développer en C++ sur mes différents joujoux (choux, cailloux, genoux, hiboux, joujoux, poux… oui c’est bien un x… Mais c’est bizarre à lire quand même), Arduino, ESP8266 et Teensy.

Et s’il y a bien une chose qui me fatigue, c’est de devoir toujours taper la même chose à chaque fois que je crée une nouvelle classe:

maclassequitue.h:

maclassequitue.cpp:

Ce n’est pas grand chose, mais c’est monstre rébarbatif de devoir se taper ça à chaque fois.

J’ai trouvé un plugin Atom permettant de créer des templates pour générer ce qu’on veut en deux coups de cuillère à pot. Il s’appelle Atom Smart Templates.

Une fois installé, voici comment j’ai créé ma template pour générer les deux fichiers à partir d’un nom de classe:

  • Menu Packages/Smart Templates/Open templates folder
  • Dupliquer le répertoire de la template de base (BaseTemplate), et l’appeler cpp (nom totalement arbitraire)
  • Virer tout sauf le fichier index.js (qui contient la configuration de notre template)
  • Créer deux fichiers: header.template et implementation.template (noms tout aussi arbitraires)

Le plus gros est fait 🙂

Voici le contenu de mon index.js:

La variable params contient la liste des paramètres à demander à l’utilisateur pour pouvoir générer les fichiers.

Chaque item généré dans la fonction rules prend en source nos deux fichiers .template, et construit leurs noms finaux en fonction du paramètre ClassName qui a été saisi.

Ensuite, nous devons créer notre fichier header.template:

Et le fichier implementation.template:

Facile, non?
Et surtout maintenant, quand je veux créer une nouvelle classe, je n’ai qu’à faire un clic droit dans le répertoire concerné, Create files from template, clic sur C++ class, je rentre le nom de ma classe et paf!

Mais c’était quand déjà ? (partie 2)

Salut à tous!

Allez, je mets les pieds dans le plat. Vous vous rappelez mon dernier article à l’arrière goût nostalgique? Non? Mais siiiii… Il est juste en dessous, là… J’expliquais que cet article avait pris le pas sur ce que je désirais réellement écrire ce jour-là. Hé bien voilà, depuis longtemps, je cherche des dates. C’est con hein, mais par exemple, « C’était quand la dernière fois que j’ai changé de caleçon? »

Pardon? Ah. On me fait signe en régie que cet exemple n’est pas génial. Disons plutôt « je fais le plein de ma voiture tous les combien? Et combien ça me coûte, en cumulé? »

Ou encore « c’était quand la dernière fois que j’ai eu mal au crâne? » Ça pourrait être intéressant de savoir si j’ai plus souvent mal à la tête le vendredi ou le samedi, par exemple. Ou plus l’hiver que l’été.

Alors j’ai attrapé mon plus beau clavier et j’ai développé une application web qui permet justement de tracer les événements de ma vie, qu’ils soient ponctuels ou récurrents (c’est surtout intéressant pour les récurrents), et dont je veux pouvoir retrouver les dates plus tard, voire établir des statistiques.

statistics-exampleJe peux aussi faire un rapprochement entre les répétitions du vendredi soir et le mal de crâne du samedi matin (bien que cette corrélation fût déjà établie…)

Et surtout, plus que tout: je saurai enfin tous les combien on change la pile de la pendule du salon! Non mais c’est vrai quoi, elle les bouffe ou bien?

Alors j’ai mis en ligne whenwas.it, et je l’utilise évidemment, puisqu’à la base je l’ai conçu selon mes besoins.

J’y note les cafés que je bois, les jours où j’achète des clopes, les matins où je me lève avec le mal de crâne, quand je fais le plein (et dans ce cas j’indique les montants pour pouvoir calculer automatiquement des cumuls), et pas mal d’autres événements récurrents.

Je me suis dit que ce genre d’application pourrait être utile à pas mal de monde, alors les inscriptions sont ouvertes.

L’interface est responsive, et construite à base d’agrumes avec le très agréable framework materializecss.

Mais qu’est-ce que que je raconte avec mes citrons… Voyez plutôt:

when-was-it-nico

Et si vous pensez que ça peut vous être utile, alors n’hésitez pas!

Tschüss!