Aujourd'hui, on plotte.
Quand je teste des trucs avec Arduino, je suis régulièrement frustré par la difficulté à visualiser mes données, particulièrement quand il s'agit de capteurs. Le retour texte par le moniteur série, c'est pas mal, mais rien ne vaut un beau plot temporel (désolé, j'utiliserai l'anglicisme "plot" de to plot, déformation professionnelle ).
Dans cette optique, j'ai regardé ce qui se faisait en termes d'outils pour afficher les données du port série. Mon propre essai d'il y a deux ou trois ans était assez décevant, donc j'ai cette fois repéré trois librairies pour répondre à mon problème :
- ArduinoPlot de gregpinero (github ArduinoPlot) : ce qui m'a plu dans ce projet, c'est que le code est en python et utilise wxWidgets, dont je me sers également en ce moment pour des projets de logiciel avec GUI. L'utilisation est simple, l'interface propre, et visiblement configurable.
- arduino-plotter de devinaconley (github arduino-plotter) : plot en temps ou en XY, configuration de l'affichage du plot à partir de l'Arduino, possibilité de changer le layout facilement.
- RealTimePlotter de sebnil (github RealtimePlotter) : il expose exactement la problématique qui me préoccupe dans sa description (Yay!), le format de message série est explicitement simple, l'interface a l'air simple et on peut la modifier en live.
Principe
Avant de rentrer dans mon retour d'expérience, un petit mot sur le principe très général des plotters et de leur mise en oeuvre ici.
Il faut distinguer deux programmes : celui qui tourne sur l'Arduino (et qui est chargé de collecter les données et de les envoyer sur le port série), et celui qui tourne sur le PC (et qui lit le port et affiche le graphe). On définit un port pour la communication (COMx ou /dev/ttyX), puis le programme côté Arduino envoie des données à une certaine fréquence, tandis que le programme côté PC met à jour son affichage périodiquement ou à réception des données. Les trois librairies sont je parle respectent ce principe, vous devrez donc d'une part, uploader un code sur l'Arduino, d'autre part lancer le programme de visualisation sur votre PC.
Pour tester tout ça, j'ai utilisé un montage basique avec un capteur DHT 22 branché sur le port 2 de mon Arduino Uno et alimenté en 3.3V.
Résultats
Le principal critère dans cette évaluation est d'abord la facilité de mise en oeuvre : je n'ai pas envie de passer des heures à adapter le système à mon problème pour visualiser mes données ou à chercher quel script lancer. Si ça ne marche pas, out. Ensuite viennent les aspects simplicité du code (et donc "hackabilité" pour mes propres systèmes) et configuration de l'interface (pour différences échelles de temps, amplitudes des données, etc.). Je n'ai pas vraiment regardé ces derniers points ici, mais je poursuivrai sûrement ça bientôt.
ArduinoPlot
Première déception, le code qui va lire le port série utilise une méthode (read_all) qui n'existe pas dans ma version de la librairie. J'ai également galéré à installer wxWidgets sur la machine où je teste (je l'utilse sur un autre ordi sans problème), même avec un virtualenv. Bon, trop d'efforts, pas de réconfort, on laisse tomber.
arduino-plotter
Lors de mon premier test, j'ai laissé tomber assez rapidement, parce que je trouvais le code peut clair à mettre en oeuvre. Je viens de retester ce soir, et en fait, cette librairie est plutôt bien ! Déjà, le fait de configurer l'affichage à partir de l'Arduino est vraiment sympa, pour faire évoluer l'affichage dynamiquement (hop, je presse un bouton connecté à un pin et j'affiche tel ou tel signal). Le code côté Arduino est super simple (voir plus bas).
Quelques petits défauts quand même : l'affichage n'est pas continu (pas de ligne mais des points), ce qui fait qu'entre deux données qui ont un grand écart (par exemple, mon taux d'humidité qui passe de 15 à 10%), on a du vide. L'affichage en abscisse est aussi assez bizarre, mais je ne me suis pas trop penché sur le code côté plotter. Autre défaut, le graph se reset parfois, je pense quand l'Arduino reboot, ce qui n'est pas forcément génial, mais ne pose pas de problème si on ne touche pas au montage.
/* =========================================================================================== Example used in Quick-Start + DHT sensor (R1D1) ------------------------------------------------------------------------------------------- Plotter v2.3.0 https://github.com/devinaconley/arduino-plotter by Devin Conley =========================================================================================== */ // Includes and definitions for sensor : #include <Adafruit_Sensor.h> #include "DHT.h" #define DHTPIN 2 #define DHTTYPE DHT22 DHT dht(DHTPIN, DHTTYPE); // Plotting lib #include "Plotter.h" // Containing variables double x,t,h; Plotter p; void setup() { h= 0.0; t= 0.0; p.Begin(); dht.begin(); p.AddTimeGraph( "R1D1 - test DHT 22", 500, "t (temp)", t , "h (hum)", h ); // for adding x : "x (sin)", x , } void loop() { // Reading temperature or humidity takes about 250 milliseconds! h = dht.readHumidity(); // Read temperature as Celsius (the default) t = dht.readTemperature(); // Check if any reads failed and exit early (to try again). if (isnan(h) || isnan(t)){ return; } // The following value will not be sent actually (R1D1) x = 10*sin( 2.0*PI*( millis() / 5000.0 ) ); delay(500); p.Plot(); // usually called within loop() }
Screen_arduino-plotter_DHT22.png 363,61 Ko
28 téléchargement(s)
RealTimePlotter
Le premier que j'ai réussi à faire fonctionner. J'ai passé un certain temps pour obtenir un affichage correct, et surtout à comprendre quel type de message envoyer à partir de l'Arduino (je suis pas sûr d'avoir tout compris). L'affichage est robuste aux erreurs dans la communication. Le code côté Arduino n'est pas forcément super clair, il est probable que pas mal de parties pourraient être intégrées dans une classe et séparées du code principal.
#include <Adafruit_Sensor.h> #include "DHT.h" #define DHTPIN 2 // what digital pin we're connected to // Uncomment whatever type you're using! //#define DHTTYPE DHT11 // DHT 11 #define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321 DHT dht(DHTPIN, DHTTYPE); uint8_t buffer[20]; //Buffer needed to store data packet for transmission int16_t data1 = 1; int16_t data2 = 2; int16_t data3 = 3; int16_t data4 = 4; bool debug = false; uint8_t buffer2[20]; void setup() { // put your setup code here, to run once: Serial.begin(115200); dht.begin(); } int16_t value = 0; int8_t direction = 5; void loop() { // Reading temperature or humidity takes about 250 milliseconds! int16_t h = (int16_t)dht.readHumidity(); // Read temperature as Celsius (the default) int16_t t = (int16_t)dht.readTemperature(); // Check if any reads failed and exit early (to try again). if (isnan(h) || isnan(t)) { return; } // put your main code here, to run repeatedly: //Serial.write(value); value = (value + direction); if (value > 100) direction = -10; else if (value < -50) direction = 10; //data3 = value; //plot(value, value/2, value/4, value/8); for (uint8_t i = 0; i<7; i++) { switch (i) { case 0: Serial.print(t); break; /*case 1: Serial.print(h); break; case 2: Serial.print(0); break; case 3: Serial.print(0); break; case 4: Serial.print(0); break; case 5: Serial.print(0); break;*/ } if (i < 7) Serial.print(" "); } Serial.println(""); delay(500); } uint8_t variableA = {0x00}; void plot(int16_t data1, int16_t data2, int16_t data3, int16_t data4) { int16_t pktSize; buffer[0] = 0xCDAB; //SimPlot packet header. Indicates start of data packet //buffer[1] = 4*sizeof(int16_t); //Size of data in bytes. Does not include the header and size fields buffer[1] = 1; buffer[2] = 5; buffer[3] = 6; buffer[4] = 7; buffer[5] = 8; pktSize = 2 + 2 + (4*sizeof(int16_t)); //Header bytes + size field bytes + data if (!debug) { Serial.print(data1); Serial.print(" "); Serial.print(data2); Serial.print(" "); Serial.print(data3); Serial.print(" "); Serial.print(data4); Serial.print('\r'); } else { Serial.print("Size: "); Serial.println(pktSize, HEX); for (int i = 0; i<pktSize; i++) { Serial.print(buffer[i], HEX); Serial.print(" "); } Serial.println(); } }
Bonus stage : Graph (https://www.arduino..../tutorial/graph)
A posteriori, j'ai découvert Graph, qui fonctionne aussi avec Processing. L'affichage n'est pas top, mais le code est ultra simple, donc ouvert à évolutions.
Conclusion :
À première vue, j'ai commencé à écrire ce post dans l'idée de recommender RealTimePlotter. Après mes quelques essais supplémentaires, je pense que arduino-plotter est plus prometteur, et je vais sûrement creuser plus en détail ce que l'on peut faire avec. J'aime bien l'idée que l'Arduino envoie la config du plotter, changer le nombre de variables envoyé est enfantin, bref, c'est facile d'utilisation.
Pour ArduinoPlot, je vous invite à la tester si vous pouvez facilement installer les librairies requises dans leur bonne version. Vu que j'avais deux autres librairies à tester, je n'y ai consacré que le minimum de temps.