dimanche 29 juillet 2012

Garduino [3] : un petit détour avec Python, un logger graphique

Etant avant tout développeur, j'ai profité des essais de mesures pour mon proto de Garduino pour réaliser un petit logiciel sur mon Mac en Python, qui dialogue (de manière très basique via l'interface Série) avec l'Arduino. J'avais besoin d'observer les données recueillies sur plusieurs heures, voir plusieurs jours.

L'objectif est de faire un simple petit collecteur de données (logger) afin de mémoriser les mesures (envoyés par l'Arduino) et les surtout les afficher sur l'écran sous forme graphique.

C'est assez simple, le programme Arduino effectue l'ensemble des mesures soit si on presse le bouton du circuit, soit si il reçoit l'ordre la liaison USB. Une fois les mesures effectuées, les données sont retournées et le logiciel mémorise celles-ci. Il y a 3 mesures : température (en °C), luminosité (%) et hydrométrie (%).

Le logiciel Python sur Mac utilise la librairie pygame pour l'affichage graphique, c'est une librairie que je connais bien (elle est spécialisée dans le jeu d'arcade) je l'utilise ici pour ces capacités graphiques et parce que je la connait bien.


Communication
L'arduino est d'origine équipé d'une prise USB qui permet de se connecter à l'ordinateur hôte qui sert a programmer. Cette connexion USB est en fait aussi une liaison série classique, ce qui va faciliter la communication.
Cette facultée de communication série est déjà très utile pour debugger un logiciel Arduino puisqu'elle permet de renvoyer des éléments qui s'afficheront dans la console de l'environnement de programmation.
  • Arduino, la langage propose en standard la librairie Serial. la liaison s'initiale simplement dans le setup() par la commande Serial.begin(9600) ou 9600 est la vitesse de communication en bauds. On peut ensuite lire et écrire avec les commandes read et write (ou print pour envoyer en chaine de caractères)...
  • Python, la liaison sera géré par la librairie pyserial qui gère ça très bien mais qu'il faut installer
Coté Arduino
Sur l'Arduino le logiciel boucle sur l'attente d'un ordre de mesure (au choix température, luminosité ou humidité) et effectue la mesure à réception pour ensuite la renvoyé sur le port série.
La mesure est normalisée et calibrée pour être lisible directement.
  • La température est renvoyé en °C, précédemment calibré via l'équation de Steinhart
  • La luminosité est renvoyé en % (0 il fait noir, 100%, maximum du capteur), c'est simplement une règle de trois pour passer de la valeur brute (0..1023) à un % (0..100).
  • L'humidité du sol en %, en cours de calibration mais qui tient compte de la température actuelle
Coté Python
J'utilise la librairie pygame car je la connait très bien et que ces capacités graphiques sont plutôt sympa. Elle est de plus compatible avec toutes les plateformes (Linux, Mac et Windows). Elle est par contre totalement autonome du système (basé sur SDL) et ne permet de gérer facilement une interface utilisateur (GUI), il faut se la faire soit même.
Pour la réalisation de petits projets précédents j'avais réalisé un embryon de GUI pour mes petits jeux : fgame, du coup je m'en sers ici.
Le logiciel python, nommé (pour l'instant) : log-duino présente un écran principal qui permet de configurer ou de lancer le suivi des mesures. Il faut que l'Arduino soit branché avant de lancer le logiciel afin que l'Arduino soit référencé par le système (via pyserial) dans la liste des ports séries.
Le logiciel envoi des commandes a interval paramétrable pour l'Arduino :
  • 'M' pour une mesure de l'humidité (toutes les 2 minutes)
  • 'T' pour une mesure de la température (toutes les 40 secondes)
  • 'L' pour une mesure de luminosité (toutes les 20 secondes)
En retour il analyse en permanence ce qui arrive sur le port série pour récupérer les données envoyés par l'Arduino. Ces données sont affichées en temps réel sous forme de valeur dans le bloc de gauche et aussi sur la courbe graphique (bloc de droite).
Les touches + et - permettent de zoomer sur le graphique.
La touche ESC ou la bouton "Stop" permettent de revenir au menu principal.

Les logiciels (Arduino et Python) sont en cours de développement, mais seront bientôt disponible (en l'état) sur mon compte github: log-duino.

Les étapes suivantes vont être d'améliorer les 2 logiciels suivant les axes suivants :
  • Arduino : Effectuer les mesures (notamment l'humidité) sans bloquer la boucle principal afin de pouvoir gérer d'autres actions en parallèle sans problème. Car actuellement la mesure de l'humidité du sol, pour être fiable, nécessite de nombreuses secondes...
  • Python : améliorer le graphique en ajoutant des échelles, la possibilité de cliquer sur la courbe, un bouton pour changer d'échelle, un mode de représentation non temporel liant température et humidité
  • Python : gérer une petit base de données locale (mysql) pour conserver les données d'une session a l'autre.


3 commentaires:

  1. Bonjour, avec les beaux jours et après quelques expérience hivernale autour d'arduino et de python, je m’oriente vers le jarduino et le programme log en python m'intéresse pour mieux comprendre cette bibliothèque pygame que j'avais déjà croisée sur le web mais que je n'avais pas bien compris. Merci d'avance si vous pouvez m'indiquer un lien vers ce programme. Erwan

    RépondreSupprimer
  2. Bonjour,
    Je réalise un projet similaire au tien, je serais très intéressé par ton programme python mais je ne le trouve pas sur Github.. Pourrais tu me l'envoyer par mail?

    RépondreSupprimer
  3. heey,
    Actuellement j’ai comme « projet » de développer un logger pour un capteur solaire.
    En ce moment j’ai câblé l’arduino DUE et l’horloge RTC … j’ai commencé par faire un premier programme que je vous le laisserai ci-dessous. mon programme doit mettre a l’heure le système manuellement ensuite l’ envoyer a l’horloge puis régler la fréquence d’acquisition et lancer les mesures. Je dois récupérer et afficher les mesures puis arrêter les mesures quand je veux.
    Voici le premier sketch:
    #include « Wire.h »
    #define DS1307_I2C_ADDRESS 0×68
    //Pour convertir des nombres décimaux normal à des nombres décimaus codés binaire
    byte decToBcd(byte val)
    {
    return ( (val/10*16) + (val%10) );
    }
    //Pour convertir des nombres binaires à des nombres normales
    byte bcdToDec(byte val)
    {
    return ( (val/16*10) + (val%16) );
    }

    void setDateDs1307
    (byte seconde, // 0 à 59 secondes
    byte minute, // 0 à 69 minutes
    byte heure, // 1 à 23 heures
    byte joursdanslasemaine, // 1 à 7 jours
    byte joursdanslemois, // 1 à 28jours ou 1 à 29jours ou 1 à 30jours ou 1 à 31jours
    byte mois, // 1 à 12mois
    byte annee) // 0-99
    {
    Wire.beginTransmission(DS1307_I2C_ADDRESS);
    Wire.write(0);
    Wire.write(0×80);
    Wire.write(decToBcd(seconde));
    Wire.write(decToBcd(minute));
    Wire.write(decToBcd(heure));
    Wire.write(decToBcd(joursdanslasemaine));
    Wire.write(decToBcd(joursdanslemois));
    Wire.write(decToBcd(mois));
    Wire.write(decToBcd(annee));
    Wire.endTransmission();
    }
    // Obtient la date et l’heure du DS1307.
    void getDateDs1307
    (byte *seconde,
    byte *minute,
    byte *heure,
    byte *joursdanslasemaine,
    byte *joursdanslemois,
    byte *mois,
    byte *annee)
    {
    // Réinitialiser le pointeur de registre.
    Wire.beginTransmission(DS1307_I2C_ADDRESS);
    Wire.write(0);
    Wire.endTransmission();
    Wire.requestFrom(DS1307_I2C_ADDRESS, 7);

    *seconde = bcdToDec(Wire.read() & 0x7f);
    *minute = bcdToDec(Wire.read());
    *heure = bcdToDec(Wire.read() & 0x3f); //Besoin de changer cela si 12 heures am / pm
    *joursdanslasemaine = bcdToDec(Wire.read());
    *joursdanslemois = bcdToDec(Wire.read());
    *mois = bcdToDec(Wire.read());
    *annee = bcdToDec(Wire.read());
    }
    void setup()
    {
    byte seconde, minute, heure, joursdanslasemaine, joursdanslemois, mois, annee;
    Wire.begin();
    Serial.begin(9600);

    seconde = 4;
    minute = 3;
    heure = 7;
    joursdanslasemaine = 5;
    joursdanslemois = 17;
    mois = 4;
    annee = 8;
    setDateDs1307(seconde, minute, heure, joursdanslasemaine, joursdanslemois, mois, annee);
    }
    void loop()
    {
    byte seconde, minute, heure, joursdanslasemaine, joursdanslemois, mois, annee;
    getDateDs1307(&seconde, &minute, &heure, &joursdanslasemaine, &joursdanslemois, &mois, &annee);
    Serial.print(heure, DEC);
    Serial.print(« : »);
    Serial.print(minute, DEC);
    Serial.print(« : »);
    Serial.print(seconde, DEC);
    Serial.print( » « );
    Serial.print(mois, DEC);
    Serial.print(« / »);
    Serial.print(joursdanslemois, DEC);
    Serial.print(« / »);
    Serial.print(annee, DEC);
    Serial.print( » jours_dans_lasemaine: »);
    Serial.println(joursdanslasemaine, DEC);
    delay(1000);
    }

    Mon problème c’est que je n’arrive pas à avoir la date et l’heure du pc mais que des 0 partout.
    Il me semble que l’horloge n’arrive pas à démarrer !!!
    Voici ce que m’affiche le port com :
    0:0:0 0/0/0 jours_dans_lasemaine:0
    0:0:0 0/0/0 jours_dans_lasemaine:0
    0:0:0 0/0/0 jours_dans_lasemaine:0
    0:0:0 0/0/0 jours_dans_lasemaine:0
    0:0:0 0/0/0 jours_dans_lasemaine:0
    0:0:0 0/0/0 jours_dans_lasemaine:0
    0:0:0 0/0/0 jours_dans_lasemaine:0

    HEEEELP pouvez-vous m'aider svp :( :(

    RépondreSupprimer