Utiliser une carte Arduino dans une station météo

Alain Jouve, Professeur de physique au Lycée Rosa Parks, Neuville sur Saône

ENS de Lyon Institut français de l'Education

Publié par

Gérard Vidal

Directeur de collection
ENS de Lyon
Institut français de l'éducation

2015-07-25

Résumé

Les cartes Arduino sont des cartes "opensource" comportant un circuit microcontrôleur permettant de réaliser des acquisitions de données issus de capteurs. Elles sont peu coûteuses et assez faciles à mettre en oeuvre avec quelques lignes de codes de programmation et peuvent gérer une communication sans fil type WiFi ou Bluetooth ce qui est très intéressant dans le cas d'une station météo, il n'est pas nécessaire de se rendre sur le site pour acquérir les mesures.

Les modules XBee sont également des modules de télécommunication sans fil aisés à mettre en oeuvre.

Cet article décrit comment réaliser une station météo interrogeable à distance construite avec une carte Arduino et un module XBee.


Table des matières
Liste des illustrations
Liste des figures Contenant une vidéo

Utiliser une carte Arduino dans une station météo


Table des matières

De nombreux sites proposent des programmes divers et variés notamment le site Arduino qui est très complet. La tâche des développeurs est ainsi facilitée.

Présentation de la station

La station météo se présente telle que le montre la photo ci-dessous.

Vue de la station météo en état de fonctionnement

Cette illustration est mise à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Pas de Modification 4.0 International.

Figure 1. Vue de la station météo en état de fonctionnement

Sur un pied de parasol de 14 kg est fixée une tige métallique normalement destinée à supporter un antenne parabolique.Sur cette tige sont fixés le panneau solaire, l'abri ventilé(blanc) et le boîtier(gris) contenant le montage electronique.

Dans l'abri ventilé se situent trois capteurs :

-un capteur de température

-un capteur de pression

- un capteur d'humidité

Un anémomètre à ailettes surmonte l'abri.

Un boitier protège l'ensemble du montage électronique qui lit et télétransmet les données issues des capteurs. Une petite antenne le surmonte.

La station est énergétiquement autonome et le panneau solaire recharge une batterie de 12 V 12 Ah située dans une boîte étanche placée au pied de la station. Cette batterie alimente le montage électronique et les capteurs.

Un logiciel implanté dans un ordinateur distant permet de récupérer périodiquement les données. La structure générale de l'ensemble est comme l'indique la figure ci-dessous :

Synoptique du système complet

Cette illustration est mise à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Pas de Modification 4.0 International.

Figure 2. Synoptique du système complet

Choix de la carte Arduino

Les cartes Arduino

Les cartes Arduino sont bâties autour d'un microcontrôleur ATméga .

Il existe plusieurs modèle ayant chacun leurs spécificités. La plus courante est la carte Arduino Uno qui a le mérite d'être robuste, et le microcontroleur, monté sur un support, peut être changé en cas de panne. La carte Leonardo présente l'avantage de gérer deux ports série en même temps, mais le microcontrôleur est soudé et ne peut pas être changé.

La carte Arduino

Cette illustration est mise à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Pas de Modification 4.0 International.

Figure 3. La carte Arduino


Ces cartes comportent toutes plusieurs entrées analogiques, des entrées et des sorties numériques. Le convertisseur analogique numérique possède 10 bits et sa tension de pleine échelle est par défaut de 5 V mais peut être règlée entre 2,3 et 5 V.

On peut ajouter des fonctions grâce à des cartes aditionnelles appelées Shield qui se branchent exactement dans les connecteurs de la carte Arduino. On peut superposer ainsi plusieurs Shield

Les contraintes

L'autonomie en énergie impose la consommation d'un faible courant.

Le panneau solaire photovoltaïque doit pouvoir recharger la batterie le jour, même l'hiver.

Les cartes Arduino comportent une mémoire vive de faible capacité : 32 ko pour l'UNO et 28 k pour la carte Leonardo.

Les mesures doivent être assez rapprochées dans le temps pour un suivi précis de l'évolution rapide de la météo locale lors d'orages par exemple. Les données doivent pouvoir être stockées dans la station, puis ensuite téléchargées sur un ordinateur distant.

Il faut téléverser dans l'Arduino plusieurs bibliothèques qui occupent beaucoup de place mémoire, la bibliothèque du port SPI qui gère la carte micro-SD, la bibliothèque du port I2C qui gère la communication avec les capteurs.

Le dispositif Arduino choisi

Le dispositif comporte une carte Arduino UNO S3 surmontée d'un Shield "Carte micro-SD - Xbee".

Le dispositif électronique de la station météo

Cette illustration est mise à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Pas de Modification 4.0 International.

Figure 4. Le dispositif électronique de la station météo

Le Shield comporte un lecteur de carte mirco-SD de 4 Go, ce qui permet d'enregistrer les données dans un fichier de type texte. Le shield comporte également le connecteur pour Xbee.


Le module possède un petit interrupteur qui doit être placé sur la position "usb" lorsqu'on téléverse le programme dans la carte Arduino. Lorsque le téléversement est terminé il faut placer l'interrupteur sur la position "micro" afin que le module Xbee soit mis en service.

Le module de communication sans fil

Le module est directement fixé sur la platine Arduino soit via un module shield pour la carte Uno soit directement sur la carte leonardo DFRobot qui possède un connecteur XBee. Il s'agit d'un module Xbee pro série 1 avec un connecteur"UFL" qui permet de brancher une antenne via un fil de connection. La fréquence d'émission est de 2,4 GHz.

Module radio XBee® "XBP24-AUI-001"

Cette illustration est mise à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Pas de Modification 4.0 International.

Figure 5. Module radio XBee® "XBP24-AUI-001"


Une antenne extérieure est indipensable pour que la portée soit d'une centaine de mètres, car l'ensemble est enfermé dans une boîte en matière plastique. Un autre module Xbee est connecté sur l'ordinateur via un connecteur USB. Il permet au programme Python de communiquer avec la station météo.Ce dernier module possède une antenne intégrée et n'est pas enfermé donc ne nécessite pas d'antenne aditionnelle.

Il faut paramétrer le XBee avec le logiciel XCTU qui est gratuit. Le paramétrtage usine convient sauf pour la mise en veille. Pour que la mise en veille soit commandée par le logiciel il faut fixer le paramètre "SM SleepMode" à la valeur de "Pin Hybernate1":

XCTU : Copies d'écran de la page des paramètres du XBee

Cette illustration est mise à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Pas de Modification 4.0 International.

Figure 6. XCTU : Copies d'écran de la page des paramètres du XBee


Modification du paramètre SM SleepMode

Cette illustration est mise à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Pas de Modification 4.0 International.

Figure 7. Modification du paramètre SM SleepMode


Pour provoquer la mise en veille ou le réveil du XBee, le logiciel implanté dans l'Arduino place une des sorties logiques (la sortie 9 par exemple) à l'état "HIGH" (mise en veille) ou l'état "LOW" (réveil). Il faut réaliser une connection électrique avec un fil entre la sortie 9 de l'Arduino et la patte 9 du module Xbee. ATTENTION : le module XBee n'accepte pas de tension supérieure à 3,3 V, il faut donc que la borne 9 de l'arduino soit branchée sur un pont diviseur qui abaisse la tension de 4,8 V(état haut des sortie Arduino) à 3 V. Le Xbee grille à la longue si on applique la tension de 4,8 V sur une de ses pattes(votre serviteur en a fait l'amère expérience).

Pont diviseur de tension

Cette illustration est mise à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Pas de Modification 4.0 International.

Figure 8. Pont diviseur de tension

Ce pont diviseur sera câblé sur le circuit imprimé qui supporte la carte Arduino. Le fil de liaison entre la sortie du pont diviseur et la patte 9 du Xbee sera soudé sur la connexion du shield qui correspond à la patte 9.


Les capteurs

Généralités

Trois des capteurs utilisés ici sont des capteur électroniques de la marque Sparkfun . Ils utilisent le protocole I2C. Ils ont été acheté chez Lextronic et le site de Sparkfun permet de télécharger la librairie Arduino de chaque capteur avec un exemple d'utilisation.

Le capteur de température et le capteur de pression

Les capteurs fixés sur l'assiette inférieure de l'abri météo

Cette illustration est mise à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Pas de Modification 4.0 International.

Figure 9. Les capteurs fixés sur l'assiette inférieure de l'abri météo

C'est le module BMP180 qui comporte un capteur de température et un capteur de pression. Il faut d'abord lire la température qui est ensuite utilisée pour lire et établir la pression. On peut corriger la valeur de la pression en la ramenant à l'altitude du niveau de la mer. La liaison avec l'Arduino s'effectue à l'aide du protocole I2C.

L'hygromètre

Le module HTU21 de sparkfun mesure à la fois la température et l'hygrométrie. Il faut aussi utiliser le protocole I2C.

L'anémomètre

C'est un anémomètre à ailettes acheté aussi chez Lextronic

Le capteur anénomètre

Cette illustration est mise à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Pas de Modification 4.0 International.

Figure 10. Le capteur anénomètre


Son principe est simple, un interrupteur de type ILS, commandé avec un petit aimant solidaire du moulinet, se ferme à chaque tour. La carte Arduino compte le nombre de tours par seconde. Il faut utiliser un petit montage dit "interrupeur sans rebond" entre la sortie de l'anémomètre et l'entrée de l'arduino pour éliminer les oscillations amorties qui apparaissent parfois lors de la fermeture et ouverture de l'interrupteur. Après étalonnage, avec un anémomètre du commerce, la vitesse du vent est calculée par le logiciel de la carte Arduino.

L'abri

Pour que les mesures soient valables, il faut que les capteurs soient en contact direct avec l'air, mais aussi protégés du soleil et de la pluie. L'abri doit donc être bien ventilé, mais aussi suffisament protecteur pour que les capteurs ne soient pas mouillés par le pluie. On utilise des assiettes de pot de fleur en plastique renversées. L'idée provient du site : Soucabri

L'abri de la station météo

Cette illustration est mise à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Pas de Modification 4.0 International.

Figure 11. L'abri de la station météo

Chaque assiette est séparée de sa voisine par une entretoise de 2 cm de long.Le rebord en creux de chaque assiette est percé de trous afin d'éviter l'accumulation de l'eau de pluie ou de rosée.Les assiettes sont peintes en blanc pour minimiser l'absorption de la lumière du Soleil qui réchaufferait l'abri et rendrait erronée la valeur de la température.

Une connexion à l'aide d'un câble 4 fils permet de relier les capteurs avec l'Arduino avec le procole I2C.


Vue de l''assiette inférieure amovible de l'abri

Cette illustration est mise à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Pas de Modification 4.0 International.

Figure 12. Vue de l''assiette inférieure amovible de l'abri

L'assiette du bas porte les capteurs et est facilement amovible car fixée à l'aide d'écrous à ailettes.


Le boîtier émetteur

Il contient la carte Arduino et son Shield. L'antenne reliée au module XBee est fixée sur boîtier.

Le boîtier émetteur : monté sur le mât support , intérieur du couvercle , partie fixe avec le RIT et les connecteurs

Cette illustration est mise à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Pas de Modification 4.0 International.

Figure 13. Le boîtier émetteur : monté sur le mât support , intérieur du couvercle , partie fixe avec le RIT et les connecteurs

La carte Arduino est fixée sur une plaque imprimée sur laquelle sont fixés trois connecteurs :

  1. Le connecteur d'alimentation
  2. Le connecteur des capteurs utilisant la liaison I2C
  3. Le connecteur relié à l'anémomètre

L'utilisation de connecteurs permet de débrancher facilement le boîtier émetteur et de l'emporter pour une maintenance éventuelle, c'est pour cette même raison que l'antenne et l'ensemble Arduino sont fixés dans le couvercle du boîtier que l'on peut facilement détacher du corps principal fixé sur le tube support.

On trouve aussi sur la carte imprimée, le pont diviseur nécessaire à l'adaptation de la tension de la borne 9 du Xbee, et un autre pont diviseur par 4 alimenté directement par la tension aux bornes de la batterie. La sortie de ce pont diviseur est reliée à la borne A1 de l'Arduino. Le sketch lit la tension appliquée sur cette borne et peut ainsi envoyer à l'ordinateur l'état de la charge de la batterie.

Le boîtier émetteur doit être étanche, mais doit cependant être ventilé pour éviter une surchauffe de circuits électroniques, des trous sont percés en dessous du boîtier et dans la partie supérieure, ces derniers sont recouverts d'une protection évitant que la pluie ne pénètre.

Alimentation de la carte Arduino et des capteurs

Alimentation autonome avec une batterie de 12 V 12 Ah rechargée avec un panneau solaire de 0,15 m2. L'ensemble du dispositif consomme 0,06 A lorsque le module Xbee est en veille, et 0,12 A lorsque le module Xbee est éveillé. L'éveil du Xbee dure 1min30 par 10 min. L'intensité moyenne calculée sur heure est donc 0,07 A Donc l'autonomie de la batterie est de 12/0,07 = 171 h environ. Le panneau solaire photovoltaïque polycristallin a pour puissance maximale Pm = 20 W, avec un courant maximal de 1,2 A. Valeurs données pour une énergie recue par unité de surface de 4000 W/m2, c'est-à-dire lorsque les rayons du Soleil sont orthogonaux à la surface du capteur, au mois de juin à Lyon. L'expérence montre que la simple lumière du jour (même en hiver) suffit à compenser la consommation de la station.

Capteur photovoltaïque

Cette illustration est mise à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Pas de Modification 4.0 International.

Figure 14. Capteur photovoltaïque

On peut éventuellement alimenter la station météo sous 12 V avec une petite ligne électrique issues d'un transformateur situé à l'abri. La consommation de la carte Arduino et des capteurs étant très faible, il n'y a pas de courant important (60 mA), et la chute de tension dans la ligne est réduite. Pour une longueur de 100 m de câble 1 mm2, par exemple, la résistivité du laiton étant ρ = 7,0·10 -8 Ω.m , en utilisant la relation : R = ρ.L S on obtient une résistance de 14 Ω (il faut tenir compte des deux fils) ; a chute de tension serait donc de 0,8V environ.

Le sketch à téléverser dans la carte Arduino

L'EDI Arduino

L'EDI "Arduino" est à télécharger gratuitement sur le site Arduino dans l'onglet "download". On peut l'installer n'importe où sur son disque dur. Il comporte de nombreuses bibliothèques. On tape le programme en langage C. Une fois terminé on le téléverse dans la mémoire de la carte Arduino branchée sur le port USB de l'ordinateur.

Structure générale d'un programme Arduino

Un programme Arduino encore appelé sketch comporte toujours au moins deux procédures :

  1. La procédure "setup" dans laquelle on fixe les valeurs des variables globales, on intialise le port série utilisé et ses caractéristiques, etc ... Le setup est exécuté à chaque mise sous tension ou chaque reset de la carte Arduino et uniquement.
  2. La procédure "loop" qui, comme son nom l'indique, tourne en permanence, sur elle-même tant que l'Arduino est alimenté.
  3. Des fonctions peuvent être définies également (facultatif mais recommandé) en dehors de ces deux précédentes procédures, il faut les implémenter avec la procedure "loop".

Le sketch Arduino

Il est proposé dans les annexes et comporte essentiellement :

Bibliothèques et variables

  • L'importation des bibliothèques pour la gestion de la carte SD, du port I2C, des capteurs , et du port SPI utilisé par la carte SD.
  • La création des variables : par exemple le fichier de sauvegarde des données, les repères temporels, etc ...

Le setup

Le setup contient l'ouverture du port série, et il fixe les paramètres des entrées et sorties. Pour que le lecteur de carte SD fonctionne correctement, il faut que la sortie 10 soit définie en sortie et soit fixée à l'état haut. La borne 9 est définie en sortie et permet de mettre en veille ou de réveiller le XBee. L'éveil est obtenu si elle est à l'état bas, et l'endormissement lorsqu'elle est à l'état haut.

On teste aussi la présence de la carte SD.

La boucle

On trouve dans la boucle

  • une série d'instructions uniquement réalisées lors de la première boucle :

    lecture de la date courante sur l'ordinateur

    test de l'existence et création du fichier de sauvegarde

  • Des instructions effectuées lors de chaque boucle

    1. La lecture du port série et l'exécution des instructions imposées par l'ordinateur :

      1. Lecture des données
      2. Lecture du fichier de sauvegarde de la date courante
      3. Lecture des fichiers antérieurs
    2. L'enregistrement des valeurs toute les minutes
    3. La mise en veille et le réveil du XBee

Des fonctions

  • lecture de la tempéraure, de la pression, de l'hygrométrie, et de la vitesse du vent
  • sauvegardes des données sur la carte SD
  • initialisation de la date et de l'heure
  • calcul de la date et de l'heure
  • lecture du Xbee
  • changement du fichier de sauvegarde
  • lecture de la date et de l'heure sur le module timer RTC (éventuellement)

Initialisation de la station

Il faut d'abord lancer le logiciel situé sur l'ordinateur qui permettera d'intialiser la date et l'heure.. Après la mise sous tension de la carte Arduino, appuyer sur le bouton "reset". Le sketch efffectue d'abord le "setup" puis la "loop". Lors de la première boucle il demande à l'ordinateur la date et l'heure en envoyant le message "T". Le programme Python lit la date sur l'ordinateur et l'envoie à la station.

Le programme Python

Il est donné en annexe. On utilise la biliothèque Tkinter pour créer une interface graphique. La fenêtre qui s'affiche comporte les éléments suivants :

  1. Les valeurs des mesures qui s'affichent en temps réel, rafraîchissement toutes les 10 minutes, avec une indication de l'heure de l'enregistrement de ces mesures.
  2. La commande de détection du module Xbee connecté à l'ordinateur sur le port USB
  3. La commande de lancement des mesures, lorsque l' heure est de la forme hh mm 00
  4. La commande de désactivation de la mise en veille de la station en cas de mise au point ou de maintenance.
  5. La commande de lecture du fichier journalier en cours.
  6. La commande de lecture de tous les fichiers enregistrés sur la carte SD de la station.
  7. Une information sur l'état de veille ou non de la station, avec l'heure de la précédente mise en veille : un voyant est rouge si la station est en veille et est vert sinon.
Interface graphique du programme Python

Cette illustration est mise à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Pas de Modification 4.0 International.

Figure 15. Interface graphique du programme Python

La charge de la batterie est affichée à titre informatif, mais n'est pas enregistrée.

La fermeture de la fenêtre et l'arrêt du programme sont obtenus en cliquant sur le petit carré muni d'une croix rouge.


Deux Timers sont créés dans le programme: l'un de 1 h qui lit les valeurs à chaque heure(facultatif), l'autre de 2 s qui scrute le port XBee et qui détecte s'il se réveille. Il commande la mesure et met l'heure de l'Arduino à jour.

Relevé et enregistrement des mesures

Repérage des dates

Le module Arduino étant livré à lui-même il faut repérer les dates à l'aide de son horloge interne. Il est possible d'utiliser l'instruction millis() (format type long) qui donne le nombre de milliseconde écoulées depuis le "reset" de la carte.Le sketch calcule le nombre de secondes, minutes, heures, jours, mois écoulés depuis le reset. L'horloge interne n'est pas stable et présente une importante dérive. Plusieurs essais ont conduit à corriger le nombre de millisecondes en ajoutant 1250 ms par minute. Cela n'est pas très précis.

On peut utiliser petit module un timer externe branché sur la carte Arduino qui est plus précis, mais qui présente un problème de compatibilité avec la carte SD car il utilise le même port SPI avec un paramétrage différent.

Chaque fois que la station envoie des données vers l'ordinateur, toutes les 10 minutes si l'ordinateur structe le port XBee, elle demande la date et l'heure à l'ordinateur qu'elle corrige si besoin.

Enregistrement des données

Un module Xbee est relié au port USB de l'ordinateur. Un programme écrit en Python , ou autre langage, scrute toutes les deux secondes les messages envoyés par la station. Lorsque le réveil a lieu, le sketch Arduino envoie un message "up" ce qui déclenche une demande de relevé de mesures. Le sketch Arduino relève les mesures et les envoie. Ces mesures sont enregistrées dans un fichier type "csv" dans l'ordinateur. Lorsque la station se place en mode veille la station envoie le message"down" ce qui indique au programme Python que la station est inaccessible.

Si l'ordinateur se met en veille, il n'est pas possible de relever les valeurs de la station météo. Le sketch Arduino enregistre systématiquement(en veille ou non) sur la carte SD de la station les données toutes les minutes dans un fichier type "csv" nommé "mm-jj-U.TXT". avec mm: mois, jj :jour. La bibliothèque SD ne gère pas les fichiers dont le nom est différent de la structure ********.***(8 caractères simples+ '.' + ***)

Structure d'une ligne enregistrée dans le fichier csv : "aaaa;mm;jj;hh,mm,ss;tempe;pression;hygrométrie;vent;"

Le sketch Arduino change automatiquement de fichier d'enregistrement chaque jour à 0 h 00. Il y a donc un fichier par jour.

Le programme python comporte une commande permettant de lire le fichier en cours et une autre commande permettant de lire tous les fichiers enregistrés. Les données sont alors stockées dans l'ordinateur dans un fichier de type "csv" .

Exemple de mesures enregistrée


Date, heure température, pression atm, hygrométrie,vitesse du vent
27/10/2014,1h10,8.36,1025.84,101.38,0.00
27/10/2014,1h11,8.37,1025.89,101.41,0.00
27/10/2014,1h12,8.33,1025.79,101.26,0.00
27/10/2014,1h13,8.29,1025.72,101.23,0.00
27/10/2014,1h14,8.31,1025.76,101.28,0.00
27/10/2014,1h15,8.31,1025.82,101.35,0.00
27/10/2014,1h16,8.38,1025.82,101.50,0.00
27/10/2014,1h17,8.36,1025.80,101.41,0.00
27/10/2014,1h18,8.31,1025.82,101.25,0.00
27/10/2014,1h19,8.29,1025.78,101.28,0.00
27/10/2014,1h20,8.27,1025.80,101.25,0.00
27/10/2014,1h21,8.27,1025.83,101.22,0.00
27/10/2014,1h22,8.29,1025.81,101.36,0.00
27/10/2014,1h23,8.29,1025.95,101.35,0.00
27/10/2014,1h24,8.29,1025.86,101.40,0.00
27/10/2014,1h25,8.29,1025.85,101.40,0.00
27/10/2014,1h26,8.26,1025.87,101.42,0.00
27/10/2014,1h27,8.27,1025.86,101.40,0.00
27/10/2014,1h28,8.24,1025.86,101.35,0.00
27/10/2014,1h29,8.21,1025.85,101.30,0.00
27/10/2014,1h30,8.19,1025.84,101.26,0.00
27/10/2014,1h31,8.18,1025.94,101.23,0.00
27/10/2014,1h32,8.16,1025.88,101.19,0.00
27/10/2014,1h33,8.13,1025.81,101.17,0.00
27/10/2014,1h34,8.12,1025.90,101.14,0.00
27/10/2014,1h35,8.10,1025.86,101.14,0.00
27/10/2014,1h36,8.10,1025.93,101.15,0.00
27/10/2014,1h37,8.09,1025.86,101.17,0.00
27/10/2014,1h38,8.10,1025.83,101.20,0.00
27/10/2014,1h39,8.09,1025.87,101.18,0.00
27/10/2014,1h40,8.07,1025.79,101.17,0.00
27/10/2014,1h41,8.06,1025.78,101.16,0.00
27/10/2014,1h42,8.04,1025.78,101.15,0.00
27/10/2014,1h43,8.04,1025.81,101.15,0.00
27/10/2014,1h44,8.03,1025.76,101.15,0.00
27/10/2014,1h45,8.02,1025.77,101.11,0.00
27/10/2014,1h46,7.98,1025.79,101.03,0.00
27/10/2014,1h47,7.94,1025.78,100.96,0.00
27/10/2014,1h48,7.90,1025.78,100.90,0.00       
                
                
                
            

Annexes

Le sketch Arduino


        
                    #include  < SPI.h>//ouvre le port qui gère la carte SD  


/* SD card attached to SPI bus as follows:
 ** MOSI - pin 11
 ** MISO - pin 12
 ** CLK - pin 13
 ** CS - pin 4*/

#include < SD.h>//carte SD


#include "HTU21D.h"//bibliothèques des capteurs sparkfun humidité
#include SFE_BMP180.h// bibliothèques des capteurs sparkfun pression

#include Wire.h//Pour le port I2C

File Myfile;

char fichier_[12];

SFE_BMP180 pressure;//Capteur de pression et de température
HTU21D myHumidity;// capteur d'hygrométrie


long minute_debut;
char commande_mesure;
char message;
int mois_;
int jour_;
int an_;
int heure_=0;
int minute_=0;
int seconde_ = 0;
int anemometre = 2; //Anemometre connecte sur le port 2 
long date_ini_veille;

boolean debut=true;//A la mise sous tension de l'Arduino, debut = true afin que la boucle loop effectue l'initialisation des données enregistrée et que le Xbeene soit pas mis en mode veille
long date_ini,date_depart,date_depart_;
//long date_courante;
boolean Xbee_reveille=true;
//String reponse="";
char status;

long temps;

void setup()
{
  
  pinMode(anemometre, INPUT);
  Serial.begin(9600); //Port Xbee 
  
  pressure.begin();
  delay(10);
  myHumidity.begin();
  delay(100);
  //Réglage en mode veille du Xbee;
  // La borne 9 de l'Arduino est reliée à la borne9 du Xbee
  
  
  date_depart=millis();
  debut=true;
   pinMode(9, OUTPUT);//borne de sortie pour commander la mise ne veille du Xbee nécéssite le réglage de avec XCTU
   digitalWrite(9, LOW);//mode réveillé du XBee
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);//obligatoire pour que la carte sd soit reconnue
  //SD.begin(4);//Sur leonardo ou mega voir https://learn.adafruit.com/adafruit-data-logger-shield/for-the-mega-and-leonardo
  if (SD.begin(4)){//vérification de la présence de la carte SD
    Serial.println("La carte SD est reconnue");
    delay(1000);
    return;
  } 
  else{
    Serial.println("La carte SD n'est pas reconnue");
  }
 



}



void loop(){

  //double T,P,p0;
  if (debut==true){
    initialisation_jour_heure();
    changt_fichier();
    delay(1000);
    if (!SD.exists(fichier_)){ 
      Myfile = SD.open(fichier_, FILE_WRITE);
      Myfile.println("date ; heure; température; pression; hygrométrie;vent :");
      Myfile.close();
      
    }
    else{
      
    }

    
    delay(1000);
    
    date_ini=millis();
    debut =false;
    Serial.write("down");
    delay(100);
    digitalWrite(9,HIGH);
    date_ini_veille=millis();
    Xbee_reveille = false;
  }


  
  if (Serial.available()){//Si le port Xbee recoit une donnée de la part de l'ordinateur par les ondes wifi

    commande_mesure=Serial.read();//Lecture de la valeur sur le port Xbee
    delay(100);
    
    if (commande_mesure==49){//Si la valeur est de 49; valeur 1 en code ASCII envoyé par processing : mesures instantanées
                   
      delay(1000);

      //envoie des quatres valeurs les unes après les autres, cela se traduit par un chaîne de caractère recue par le programme python
      Serial.print(mesure_temperature());//envoie sur le Xbee
      delay(400);//delais technique
      Serial.print(';');//séparation des données 
      delay(10);
      Serial.print(mesure_pression(mesure_temperature()));
      delay(400);
      Serial.print(';');
      delay(10);
      Serial.print(mesure_hygrometrie());
      delay(400);
      Serial.print(';');
      delay(10);
      Serial.print(vitesse_vent());
      delay(400);  
      Serial.print(';');
       delay(10); 
      Serial.print(analogRead(0));//Envoyer surle port Xbee la valeur du niveau de la batterie
      delay(10);
      
      delay(3000);//délais technique
      
      
      initialisation_jour_heure();
      delay(2000);
      date_ini=millis();
       Serial.flush();
      
    }    
    if (commande_mesure==50){//Si la valeur est de 50; valeur 2 en code ASCII envoyé par processinglectures des données du jour en cours
      
   if (SD.exists(fichier_)){ 
        //Serial.print(fichier_);
        Myfile = SD.open(fichier_); 
        while(Myfile.available()){
          Serial.print( char(Myfile.read()));
          
        }
        delay(10);
        Myfile.close();
    }}
    
    if (commande_mesure==51){   // Si la valeur est 3 : valeur 51 en code ASCII: Lecture des données des fichiers non encore lus (_U) de la carte SD ces fichiers sont alors nommés _L
      
      
       delay(100);
        File racine = SD.open("/");
        racine.rewindDirectory();
   while(true) {
     
     File fichier_donnees =  racine.openNextFile();
     char fichier_tmp[12]; 
     if (! fichier_donnees) {
       // no more files
       //Serial.println("**nofiles**");
       //racine.rewindDirectory();
       break;
     }
     
     Serial.println(fichier_donnees.name());
      if (fichier_donnees.name()[6]=='U'){  
         //boolean transfere = false;
         for(int n = 0;n <12;n++){
         fichier_tmp[n]=fichier_donnees.name()[n];      
         }
         fichier_donnees.close();
         //Serial.println(fichier_tmp);
         Myfile = SD.open(fichier_tmp); 
         
         while(Myfile.available()){
            Serial.print( char(Myfile.read()));
          }
         /*transfere=true;
          delay(10);
          if (transfere==true){
          char fichier_tmp_[12];
          for(int n = 0;n 12;n++){
         fichier_tmp_[n]=fichier_tmp[n];      
         }
          fichier_tmp_[6]='L';
          File Myfile_tmp = SD.open(fichier_tmp_,FILE_WRITE); 
          Myfile.seek(0);
          while(Myfile.available()){
          Myfile_tmp.write(Myfile.read());
          
          } 
          Myfile_tmp.close();
          
          }*/
          Myfile.close();
          
          
       }
     else{fichier_donnees.close();}
   }
   //racine.close();
  } 
 }
  
  /**********************************************************************************************************************
  *                                                    MESURES toutes les 60 s- mise en veille                                       *
  ***********************************************************************************************************************/
    double tempe_;
    double pression_;
    float hygro_;
    float vent_;
    if (millis()>=date_ini + 60000){//mesure effectuée environ toutes les minutes
                digitalWrite(9,LOW);//réveil du Xbee
                delay(1000);//délais technique dû au temps de réveil du Xbee
                tempe_ =mesure_temperature();
                delay(400);
                pression_= mesure_pression(tempe_);
                delay(400);
                hygro_=mesure_hygrometrie();
                delay(400);
                vent_=vitesse_vent();
                delay(1100);
                determination_date();//Calcul de la date de la mesure
                
                sauv_donnees(tempe_,pression_,hygro_,vent_);//sauvegarde dans le fichier du jour des dates et valeurs
               
               
                
                date_ini=millis();//initialisation de la date de la mesure en milliseconde
                digitalWrite(9,HIGH);
              }
  if ((millis()=date_ini_veille + 600000)(Xbee_reveille == false)){//réveil du Xbbe toutes les 10 min pendant 1 min pour permettre la communication avec l'ordi
              digitalWrite(9,LOW);
              delay(100);
              Serial.write("up");
              
              Xbee_reveille = true;
  }
  if ((millis()>=date_ini_veille + 690000)(Xbee_reveille == true)){//réveil du Xbbe toutes les 10 min pour permettre la communication avec l'ordi
              Serial.write("down");
              delay(100);
              digitalWrite(9,HIGH);
              date_ini_veille=millis();
              Xbee_reveille = false;
  }
//Serial.flush();
}
  /**********************************************************************************************************************
  *                                                    Diverses fonctions                                       *
  ***********************************************************************************************************************/
double mesure_temperature(){// fontion de mesure de la température codée sur un entier
  double T;
  status = pressure.startTemperature();//la mesure de la température s'effectue sur le module sparkfun BMP201 et utilisation de la bibliothèque de ce module pour lancer la mesure
  if (status != 0)//si le module répond
  { 
    delay(status);//délais imposé par le module

    // Retrieve the completed temperature measurement:
    // Note that the measurement is stored in the variable T.
    // Function returns 1 if successful, 0 if failure.

    status = pressure.getTemperature(T);
  }//lecture de la température
  return T;//renvoie la valeur de T

double mesure_pression(double T){//fonction de mesure de la pression codée sur un entier
  double P;//défintion de la variable pression
  status = pressure.startPressure(3);//demarrage de la mesure de la pression
  if (status != 0)//si le  module répond
  { 
    delay(status);//délais imposé par le module
    status = pressure.getPressure(P,T);//lecture de la pression sur le module
    delay(50);
  }//imposé par le calcul qui suit
  float p0 = pressure.sealevel(P,200);//Pression corrigée de l'altitude de 200 m, changer la veleur selon le lieu
  delay(50);//imposé par le calcul précédent
  return p0;//renvoie la pression corrigée de l'altitude
}
float mesure_hygrometrie(){//fonction de mesure de le l'hygrométrie codée sur un réel
  float humd = myHumidity.readHumidity();  //lecture de la valeur de l'humidité sur le module sparkfun
  return humd; //renvoie la valeur de l'humidité
}

float  vitesse_vent(){//Mesure et Calcul la vitesse du vent : http://www.lextronic.fr/P28025-capteur-anenometre.html
  unsigned long vent= 0;

 vent=pulseIn(anemometre,HIGH,800000);
 //Serial.println(vent);
  //Serial.println(vent2);
  float valeur=0;
  if (vent>0){
  valeur=(70000.0*6.2/vent)*3.6;
//Serial.print("valeur=");
//Serial.println(valeur);
}
  //formule donnée par le constructeur du moulinet
  //Serial.print(valeur);
 
  return valeur;
}


String reponse_Xbee(){//fonction de lecture des donnees recues par le Xbee lors 
  int n=0;
  while(!Serial.available()){
    n++;
    if(n>50000){break;}};
  char byte_recu = Serial.read();//le xbee envoie un succession de charactère
  String reponse_="";
  while (byte_recu!=-1){ //si on lit  -1 sur le port série cela signifie que la communication n'a pas lieu et on répète tant qu'on reçoit des charactère


    if (byte_recu!=-1){//on vérifie de nouveau car la boucle while comporte deux lectures et la deuxième peut être mauvaise
      reponse_=reponse_+byte_recu;
    }//on crée une chaîne de caractère en concaténant les charactères reçus
    byte_recu = Serial.read();//lecture du charactère
    delay(20);
  }  //délais technique
  //Xbee.println(reponse_);
  return reponse_ ; //renvoie de la chaîne réponse
  //Xbee.flush();
}
      
void sauv_donnees(float tempe_,float pression_, float hygro_,float vent_){//Sauvegarde des données dans le fichier de la date courante
    if (SD.exists(fichier_)) {
    Myfile = SD.open(fichier_, FILE_WRITE); 
    
    
    Myfile.print(an_);
    Myfile.print(";");
    Myfile.print(mois_);
    Myfile.print(";");
    Myfile.print(jour_);
    Myfile.print(";");
    Myfile.print(heure_);
    Myfile.print(";");
    Myfile.print(minute_);
    Myfile.print(";");
    Myfile.print(seconde_);
    Myfile.print(";");
    Myfile.print(tempe_);
    Myfile.print(";");
    Myfile.print(pression_);
    Myfile.print(";");
    Myfile.print(hygro_);
    Myfile.print(";");
    Myfile.println(vent_);
    //Myfile.println(":");
    //Myfile.print("\r\n");
    
    delay(10);
          
  
  Myfile.close();}
else {changt_fichier();}
}
void initialisation_jour_heure(){//fonction nécessaire si on ne dispose pas d'un module timing extérieur
  
                  
                  String reponse="";
                  boolean reponse_=false;
                  //int n=0;
                  //Serial.println("initialisation de la date");
                  Serial.flush();//effacement des données sur tampon de réception du port Xbee
                  delay(1000);
                  Serial.print('T');
                  delay(2000);
                  
                  reponse=reponse_Xbee();
                   
                  
                 delay(1000);
              
                  if (reponse!=""){
                  //Serial.println(reponse);
                  //Serial.println("date recue");
                  
                  int repere_1=  reponse.indexOf(';');
                  int repere_2=  reponse.indexOf(';',repere_1+1);
                  //Serial.println(repere_1);
                  int repere_3 = reponse.indexOf(';',repere_2+1);
                  //Serial.println(repere_2);
                  int repere_4 = reponse.indexOf(';',repere_3+1);
                  //Serial.println(repere_3);
                  int repere_5 = reponse.indexOf(';',repere_4+1);
                  //Serial.println(repere_4);
                  if((repere_1!=0)(repere_2!=0)(repere_3!=0)(repere_4!=0)(repere_5!=0)){
                  
                    an_=reponse.substring(0,repere_1).toInt();
                    mois_=reponse.substring(repere_1+1,repere_2).toInt();
                 
                 // Serial.println(mois_ini);
                  jour_=reponse.substring(repere_2+1,repere_3).toInt();
                  // Serial.println(jour_);
                  
                  heure_=reponse.substring(repere_3+1,repere_4).toInt();
                   //Serial.println(heure_);
                  
                  minute_=reponse.substring(repere_4+1,repere_5).toInt();
                   //Serial.println(minute_);
                   
                  seconde_=reponse.substring(repere_5+1).toInt();
                  //Xbee.println(minute_);
                   //changt_fichier();
                   //delay(1000);
                 date_ini=seconde_*1000;}}
                   
                   Serial.flush();
}
void determination_date(){//fonction nécessaire si on ne dispose pas d'un module timing extérieur
    boolean changt_date = false;
    long date_presente = millis();
    long temps_ecoule=date_presente-date_ini+1250;
    temps_ecoule=temps_ecoule/1000;
    if ((seconde_+temps_ecoule)59){seconde_ = seconde_+temps_ecoule;}
    else{seconde_=(temps_ecoule-59);
    if (minute_<59){minute_=minute_+1;}
        else{minute_=0;
        if(heure_<23){heure_=heure_+1;}
             else{heure_=0;
             
             
             
             
                  switch (mois_) {
                  case 1 :if (jour_<31){jour_=jour_+1;}
                                 else{jour_ = 1; 
                                       mois_= 2;}
                              break;
                              case 2 :if (jour_<28){jour_=jour_+1;}
                                 else{jour_ = 1; 
                                       mois_= 3;}
                              break;
                              case 3 :if (jour_<31){jour_=jour_+1;}
                                 else{jour_ = 1; 
                                       mois_= 4;}
                              break;
                              case 4 :if (jour_<30){jour_=jour_+1;}
                                 else{jour_ = 1; 
                                       mois_= 5;}
                              break;
                              case 5 :if (jour_<31){jour_=jour_+1;}
                                 else{jour_ = 1; 
                                       mois_= 6;}
                              break;
                              case 6 :if (jour_<30){jour_=jour_+1;}
                                 else{jour_ = 1; 
                                       mois_= 7;}
                              break;
                              case 7 :if (jour_<31){jour_=jour_+1;}
                                 else{jour_ = 1; 
                                       mois_= 8;}
                              break;
                              case 8 :if (jour_<31){jour_=jour_+1;}
                                 else{jour_ = 1; 
                                       mois_= 9;}
                              break;
                              case 9 :if (jour_<30){jour_=jour_+1;}
                                 else{jour_ = 1; 
                                       mois_= 10;}
                              break;
                              case 10 :if (jour_<31){jour_=jour_+1;}
                                 else{jour_ = 1; 
                                       mois_= 11;}
                              break;
                              case 11 :if (jour_<30){jour_=jour_+1;}
                                 else{jour_ = 1; 
                                       mois_= 12;}
                              break;
                              case 12 :if (jour_<31){jour_=jour_+1;}
                                 else{jour_ = 1; 
                                       mois_= 1;
                                     an_= an_+1;}
                              break;
                 }
              
             
             
           changt_fichier();
         delay(1000);}} 
          
         }
            
          
   
}

void changt_fichier(){
  int n=0;
  String string_mois;
  String string_jour;
  if (mois_<10){string_mois = "0"+ String(mois_);}
  if (mois_>=10){string_mois =  String(mois_);}
  if (jour_<10){string_jour = "0"+ String(jour_);}
  if (jour_>=10){string_jour =  String(jour_);}
  String nom_fichier=string_mois+"-"+string_jour+"-"+"U"+"."+"TXT";
  
  while(n<11){
  fichier_[n]=nom_fichier[n];
  n++;
  
}
//Serial.println("nom du fichier ="+nom_fichier);
if (!SD.exists(fichier_)){  
      Myfile = SD.open(fichier_, FILE_WRITE);//Creation du fichier de sauvegarde
      Myfile.println("date ; heure; temperature; pression; hygrometrie;vent :");//Premiere ligne de la sauvegarde
      Myfile.close();//Fermeture du fichier 
  }
}
]]

Le programme Python complet


 # -*- coding: utf-8 -*-

# Module de lecture/ecriture du port Xbee station météo
from Tkinter import *
from serial import *
import time
import threading
from datetime import datetime,date,timedelta
from time import gmtime, strftime
import platform
from serial.tools import list_ports
import os, io,itertools
import signal
import sys
import pylab
from PIL import Image, ImageTk
import tkMessageBox


if platform.system() == 'Windows':#Détection du système , utilisation du registre windows 
                    import _winreg as winreg
else:
                    import glob
port_Xbee=''

                
path = 'HARDWARE\\DEVICEMAP\\SERIALCOMM'
marche=True            
debut=True
forcage=False        
mise_en_veille_active=True

            
def enumerate_serial_ports():   # fonction listant l'ensemble des ports série(USB) utilisés sur la machine
                                            
                                                
                 #définition du chemin de la clef du registre donnant la lsite des ports séries en activité
                try:
                    key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path) #défintion de la clef du registre des ports séries
                except WindowsError:
                    raise Exception
            
                for i in itertools.count():                       #pour l'ensemble des ports série détectés,les lister dans l'ordre croissant 
                    try:
                        val = winreg.EnumValue(key, i)          # valeur des différents ports
                        yield (str(val[1])) #  , str(val[0]))   #La fonction  yield permet de garder la liste des ports classés
                        #print(str(val[i]))
                    
                    except EnvironmentError:
                        print("erreur")
                        break
            
            
            
def detection_port_Xbee():
                global Xbee
                liste_des_ports=enumerate_serial_ports()
                for i in liste_des_ports :#Détetection du port série(USB) sur lequel est branché le module xbee
                    print(i)
                    try :
                       with Serial(port=i, baudrate=9600, timeout=1, writeTimeout=1) as Xbee:
                         if Xbee.isOpen():
                            time.sleep(1)
                            Xbee.write('+++')
                            time.sleep(1.2)
                            ligne = Xbee.readline()
                            if (ligne=='OK'+chr(13)) :
                                port_Xbee=i
                                #return port_Xbee
                                break
                            Xbee.close()
                    except :
                        continue
                                
                Xbee = Serial(port=port_Xbee, baudrate=9600, timeout=1, writeTimeout=1)
                chaine = "Xbee branché sur le port :" +port_Xbee
                B1.config(state=DISABLED)#change l état des boutons 
                B3.config(state=NORMAL)
                B5.config(state=NORMAL)
                text1.set(chaine )  #indication du port utilisé
                detection_creation_fichier_sauvegarde()  #Détecte si le fichier de sauvegarde est présent   
            #
            
            
def Configuration_Xbee() :#Entrée dans le mode configuration du Xbee relié à l'orinateur (pas utilisé)
                
                    while Xbee.isOpen():
                        time.sleep(1)
                        Xbee.write('+++')               #activation du mode config du Xbee
                        time.sleep(1.2)
                        ligne = Xbee.readline()
                        if (ligne=='OK'+chr(13)) :      #Vérification de la réponse du Xbee
                            print(ligne)
                            print( 'mode config')
                            Xbee.write('ATID'+chr(13))  #lecture de n° identification du Xbee
                            time.sleep(1)
                            ligne = Xbee.readline()
                            time.sleep(0.2)
                            print(ligne)
                            
                            
                            
                            
                            
                            Xbee.write('ATCN'+chr(13)) # on quitte le mode config
                            time.sleep(1)
                            ligne = Xbee.readline()
                            time.sleep(0.2)
                            print(ligne)
                            reponse =raw_input("Taper q pour quitter le mode config :")# Demande à l'opérateur si on doit quitter 
                                                                                        
                                                                                    #le mode config la fonction input n'est pas adaptée en python 2 : 
                                                                                    #interprétation du code
                            print reponse
                            if (reponse == 'q'):
                                break
                    print('terminé')
            #Envoie de données de  la carte  Arduino vers l'ordinateur:

def lecture_des_donnees(moment_d_acquisition) :#Commande à l'arduino de lire les données
                if (Xbee.isOpen()) :
                    
                    Xbee.write('1')     #code 1 : indique à l'arduino qu'il faut lire les données
                    b.stop()#arrêt du timer de 10 s pour eviter les conflits de lecture du port série
                    time.sleep(2)#pause de 2s 
    
                    mesure_ = Xbee.readline()#lecture du port série de la ligne envoyé par l'Arduino
                    time.sleep(2)#mise en veille technique
                    mesure=mesure_.split(";")#Sépare les données et les transforme en tableau : le';' est envoyépar l'arduino entre chaque valeur
                    print mesure#écrit la ligne dans la console pour vérification 
                    if (len(mesure)==5):#si il y a 5 éléments dans le tableau la lecture est sans doute bonne
                        
                        
                        temperature=mesure[0] #l'élément 1 du tableau est la tempé
                        #print('Temperature = '+temperature)
                        text_tempe.set( "Tempé =" +temperature[:4] + "°C")
                        
                        pression=mesure[1] #l'élément 2 du tableau est la pression
                        #print('Pression = '+ pression)
                        text_pression.set("Pression = "+pression[:4] + " hPa")
                        
                        hygro = mesure[2]#l'élément 3 du tableau est l'hygrométrie
                        #print('Hygrométrie =' +hygro)
                        text_hygro.set("Hygrométrie = " + hygro[:4]+" % hum")
                        
                        vent=mesure[3]#l'élément 4 du tableau est la vitesse du vent
                        text_vent.set("vent ="+vent[:4]+"km/h")
                        batterie = mesure[4]#l'élément 5 du tableau est la tension aux bornes de la batterie
                        #print('Batterie =' +batterie)
                        
                        y=int(float(batterie)/860*700)#donne un nombre de pixel proportionnel à la tension de charge de la batterie
                        tension=round(float(batterie)/1024.0*16.0,1)#Permet d'obtenir la tension à partir du nombre binaire 
                        texte=str(tension)+ " V"
                        
                        #print(y)
                    
                        text2.set("Mesures : Temperature = " + temperature +"°C ;" +" Pression = " +pression+"hPa ;"+" Hygrométrie = " + hygro + "%"+vent+" km/h")
                        
                        text3.set("Heures des données affichées : "+ str(moment_d_acquisition.hour) +":"+ str(moment_d_acquisition.minute))
                        can1.delete(ALL)#effacemement du canvas de charge de batterie
                        
                        if (tension>=9.0) :
                            poly2=can1.create_polygon(0, 0, y, 0,y,50,0,50, fill='green')#Si la charge de la batterie est bonne on trace un rectangle vert
                            
                        else :
                            poly2=can1.create_polygon(0, 0, y, 0,y,50,0,50, fill='red')#Si la charge de la batterie est insuffisant on trace un rectangle rouge
                               
                        #tracé d'un rectangle de couleur et de longueur proprotionnelle à la tesnion aux bornes de la batterie
                        texte1=can1.create_text(y, 12, text=texte, font="Arial 12 italic", fill="black")
                        texte2=can1.create_text(350, 30, text="charge batterie", font="Arial 16", fill="blue")
                        return [temperature,pression,hygro,vent,batterie]#La fonction de lecture des données renvoie les valeurs
                        Xbee.flushInput()#efface les données du tampon de réception du port série
                        b.start();#redémarrage du timer de 2 s : lecture du port série
                    else :
                        can3.delete(ALL)
                        text2.set("La station météo n'est pas prête")#Si la lecture est mauvaise
                        can2.create_oval(5,5,45,45,fill='red')#le voyant rouge s'allume
                        can3.create_text(200,24, text =' Rouge : station en mode veille')#indique que la station ne répond pas
                        return [0,0,0,0] 
                        Xbee.flushInput()#efface les données du tampon de réception du port série
                        b.start() #redémarrage du timer de 2 s : lecture du port série    
                else :
                    print('Le module Xbee ne répond pas')    
                    return [0,0,0,0]    
                    
                    b.start();#redémarrage du timer de 2 s : lecture du port série
                    
def sauvegarde_donnees(donnees) :#Sauvegarde des données dans un fichier csv
                if os.path.isfile('C:\IFE\Météo\documents_contribution\programme python\donnees_meteo.csv') :
                    fichier = open('C:\IFE\Météo\documents_contribution\programme python\donnees_meteo.csv',"a")
                    #fichier.write( strftime("%a, %d %b %Y %H:%M", gmtime())+';')
                    for i in range(6) :
                        
                        fichier.write(str(donnees[i]))
                        fichier.write(';')  
                    fichier.write('\n')
                    fichier.close()              
                else :
                    print('Le fichier n''existe pas')
                    donnees=[0,0,0,0]   #pour éviter une erreur d'écriture
                    
                    
def test_date_recuperation():
                     if os.path.isfile('C:\IFE\Météo\documents_contribution\programme python\donnees_meteo.csv') :
                        fichier = open('C:\IFE\Météo\documents_contribution\programme python\donnees_meteo.csv',"r")
                                           
                        chaine = fichier.read()
                        print chaine
                        fichier.close()
                       
                             
def detection_creation_fichier_sauvegarde():  #Vérification de l'existence d'un fichier de sauvegarde, sinon le créer      
                if (os.path.isfile('C:\IFE\Météo\documents_contribution\programme python\donnees_meteo.csv')!=True) :
                    fichier = open('C:\IFE\Météo\documents_contribution\programme python\donnees_meteo.csv',"w")
                    fichier.close()  
                
def fermer_programme():
                """Fonction appelée quand vient l'heure de fermer notre programme"""
                print("C'est l'heure de la fermeture !")
                if (Xbee.isOpen()) :
                    Xbee.close()
                if (a) : 
                    a.stop()
                if(b) :    
                    b.stop()
                fenetre_meteo.destroy()
                    
def mise_enveille_Xbee(moment_d_acquisition):
                if (Xbee.isOpen()) :
                    time.sleep(1)
                    Xbee.write('5') # 5 est le code de mise en veille du Xbee station
                    time.sleep(1)
                    Xbee.write(str(moment_d_acquisition.hour))#Donne l'heure à la station 
                    
                    time.sleep(1)
                    Xbee.write(str(moment_d_acquisition.minute))#Donne les minutes à la station
                    time.sleep(1)
            
            
    
    
    
               
def mesures() :
                #fonction appelée par le timer toutes les 60s, la mesure n'est ici effectuée que chaque heure
                    global debut
                    global forcage
                    B2.config(state=DISABLED)#le bouton arret des mesures est désactivé
                    
                  
                    #if( ((moment_d_acquisition.minute%2)==0)and (moment_d_acquisition.second==0)):#toutes les heures (((moment_d_acquisition.minute)==0)and (moment_d_acquisition.second==0)):#chaque heure en réalité
                    moment_d_acquisition = datetime.now()
                    if (moment_d_acquisition.minute>0):#pour que l'acquisition n'ai lieu qu'a l'heure pile
                         global acquisition_effectuee#variable globale indiquant que l'acquisition est effectuée 
                         acquisition_effectuee =False   
                    if (((moment_d_acquisition.minute==0)and (acquisition_effectuee==False))or(debut==True)or(forcage==True)):#démarrage du timer à une heure pleine(((moment_d_acquisition.minute)==0)and (moment_d_acquisition.second==0)):#chaque heure en réalité
                        b.stop()#L'acquisition a lieu lorsque le timer de 2 s est à l'arrêt
                        
                        time.sleep(1.5)#Mise en veille technique.
                        Xbee.flushInput()   #effacement des données entrantes du port   série      
                       
                        debut=False #indique que ce n'est plus la première mesure
                        forcage=False   #indique que l'on de force pas la lecture de la meusre à un moment quelconque
                        acquisition_effectuee=True;
                        donnees=lecture_des_donnees(moment_d_acquisition) #appel de la lecture des données sur l'Arduino distant
                        jour = datetime.now()
                        date_= str(jour.day)+"/"+str(jour.month)+"/"+str(jour.year)#lecture de la date et de l'heure
                        heure_=str(jour.hour)+"h"+str(jour.minute)
                        donnees.insert(0,heure_)
                        donnees.insert(0,date_)
                        print(donnees)
                        sauvegarde_donnees(donnees)#appel de la fonction de sauvgarde des données
                        #mise_enveille_Xbee(moment_d_acquisition)#met en veille le Xbee pour préserver les batteries de la staion météo autonome
                        Xbee.flushInput()
                        b.start()
                    B2.config(state=NORMAL)#le bouton arret des mesures est activé
                     
def scrutation_mise_en_veille() :#fonction applée par le timer de 2 s pour srcuter la mise en veille de la station
                    examen_veille=Xbee.readline()#lecture du port série
                    time.sleep(0.1);
                    
                    print ("Le port Xbee indique : "+examen_veille)
                    if(examen_veille=="up"):#si la station envoie up, elle se réveille
                        can3.delete(ALL)
                        can2.create_oval(5,5,45,45,fill='green')
                        can3.create_text(200,24, text ='Vert : station accessible.')
                        B7.config(text="Desactiver la mise en veille")
                        forcage_mesures()
                        Xbee.flushInput()
                    if(examen_veille=="down"):#si la station envoir down, elle se met en veille
                        can3.delete(ALL)
                        can2.create_oval(5,5,45,45,fill='red')  
                        heure_courante = datetime.now()
                        phrase=' Rouge : station en mode veille à '+str(heure_courante.hour)+' h' +str(heure_courante.minute)+'.'
                        can3.create_text(200,24, text =phrase)
                        Xbee.flushInput()            
                    if (examen_veille=="T"):
                         b.stop()
                         time.sleep(1.0);
                         heure_courante = datetime.now()
                         date_heure = str(heure_courante.year) + ";"+str(heure_courante.month) + ";" + str(heure_courante.day) + ";"+str(heure_courante.hour)+";"+str(heure_courante.minute)+";"+str(heure_courante.second)
                         Xbee.write(date_heure)
                         print(date_heure)
                         time.sleep(1.0)   
                         b.start()
                         Xbee.flushInput()         

class MyTimer: #classe définissant un timer
    def __init__(self, tempo, target, args= [], kwargs={}): 
        self._target = target 
        self._args = args 
        self._kwargs = kwargs 
        self._tempo = tempo 
  
    def _run(self): 
        self._timer = threading.Timer(self._tempo, self._run) 
        self._timer.start() 
        self._target() 
  
    def start(self): 
        self._timer = threading.Timer(self._tempo, self._run) 
        self._timer.start() 
  
    def stop(self): 
        self._timer.cancel() 
        
def lance_mesures() :#lance les mesures
    
    B3.config(state=DISABLED)#le bouton de lancement des mesures est déactivé
    B4.config(state=NORMAL)#
    print('debut des mesures')
    debut=True
    
    #mesures() #une première serie de mesure est prise au début
    moment_d_acquisition = datetime.now()
    while  (moment_d_acquisition.second!=0) :#afin que le démarrage du timer ne prenne effet que lorsque l'heure est de la forme x h y h 00s
                moment_d_acquisition = datetime.now()
                
                
    B6.config(state=NORMAL)   
    a.start() 
    b.start()
    mesures()
         
                             
def arret_mesures() :
    B3.config(state=NORMAL)
    B4.config(state=DISABLED)
    debut=True
    b.stop()
    a.stop() 
    
def forcage_mesures():
    global forcage
    forcage= True
    mesures()
    
    
def recup_mesures_total():
     code_lecture = '3'
     lecture_mesures(code_lecture)
     
def recup_mesures() : 
    code_lecture = '2'
    lecture_mesures(code_lecture)       
                   
def lecture_mesures(code_lecture):#permet la récupération des mesures des 48 h précédentes
     
     b.stop()
     time.sleep(1)
     Xbee.write(code_lecture)
     #Lecture des mesures
     time.sleep(2)
    
     M = Xbee.readlines()  
     
     
     
     #décompose M en autant de lignes qu'il y a d'heures M=M_.split(':')
     print M
                
                                      
     for n in range(0,len(M)-1):
         A = M[n].split(';')
         date_=A[2]+"/"+A[1]+"/"+A[0]
         A[9]=A[9][0:4]
         
         heure_ = A[3]+"h"+A[4]
         mesures_=[date_,heure_,A[6],A[7],A[8],A[9]]   
         print mesures_
         try :
                    sauvegarde_donnees(mesures_) 
         except :
                tkMessageBox.showwarning('Warning','Accés au fichier impossible ! Veuillez le fermer !') 
                b.start()
     
     b.start()
    
                       
def desactive_veille() :#désactive la mise en veille de l'Arduinostation  pour la maintenance
          global mise_en_veille_active
          if (Xbee.isOpen()) :
                    time.sleep(1)
                    Xbee.write('7') #code indiquant qu'il faut sortir ou entrer du mode éco
                    time.sleep(1)
                    
                    if (mise_en_veille_active==True) :#informe que  le mode éco est actif
                        mise_en_veille_active=False   
                        B7.config(text='Activer la mise en veille')                           
                    elif (mise_en_veille_active==False) :#informe que  le mode éco est déactivé
                        mise_en_veille_active=True   
                        B7.config(text='Désactiver la mise en veille')                       
             #######################################################################
             #             Programme principal                                     #
             #######################################################################                                
fenetre_meteo=Tk()#créaction d'une fenêtre graphique
fenetre_meteo.title("Mesures de la station météo")
fenetre_meteo.geometry("800x800")

text1 = StringVar() 
text1.set("Activer la recherche du Xbee")  #initialise le message : il faut lancer la détection du Xbee
text2 = StringVar() 
text2.set("Mesures : ")  #
text3 = StringVar() 
text3.set("Temps : ")
text_tempe = StringVar()
text_tempe.set("°C")
text_pression=StringVar()
text_hygro=StringVar()
text_vent=StringVar()
label_tempe =Label(fenetre_meteo,textvariable=text_tempe,relief=RAISED,font=("Sans Serif", "24", "bold"),fg ="red") 
label_tempe.place(x=20,y=400)  #affichage de la température
label_pression =Label(fenetre_meteo,textvariable=text_pression,relief=RAISED,font=("Sans Serif", "24", "bold"),fg ="blue") 
label_pression .place(x=20,y=450) #affichage de la pression
label_hygro =Label(fenetre_meteo,textvariable=text_hygro,relief=RAISED,font=("Sans Serif", "24", "bold"),fg ="orange") 
label_hygro.place(x=20,y=500) #affichage de l'hygrométrie
label_vent = Label(fenetre_meteo,textvariable=text_vent,relief=RAISED,font=("Sans Serif", "24", "bold"),fg ="green") 
label_vent.place(x=20,y=550)#affichage de la vitesse du vent
B1=Button(fenetre_meteo,text="Détection du Xbee",command=detection_port_Xbee,state=NORMAL,width=100)#Bouton d'arrêt du programme
B1.place(x=20,y=10)#bouton de détection du Xbee 
croix=PhotoImage(file='C:/IFE/Météo/documents_contribution/programme python/croix.gif')#image de la croix du bouton de fermeture

B2=Button(fenetre_meteo,image=croix,text='Fermer',command=fermer_programme,state=NORMAL)#Bouton de fermeture du programme
B2.place(x=600,y=700)
L1=Label(fenetre_meteo,textvariable=text1)#Label Texte affichant l'état d erecherche du Xbee

L1.place(x = 160, y=50)
B3=Button(fenetre_meteo,text="Lancer les mesures ",command=lance_mesures,state=DISABLED,width=100)#bouton de lancement des mesures
B3.place(x=20,y=200)#
B4=Button(fenetre_meteo,text="Arrêter les mesures ",command=arret_mesures,state=DISABLED,width=100)#bouton d'arrêt du timer de 1 h
B4.place(x=20,y=250)
B5=Button(fenetre_meteo,text="SD : récuperer les mesures enregistrées",command=recup_mesures,state=NORMAL,width=50)
B5.place(x=100,y=700)#commande la lecture du fichier du jour en cours
B7=Button(fenetre_meteo,text="SD : récuperer les mesures enregistrées les jours précédents",command=recup_mesures_total,state=NORMAL,width=50)
B7.place(x=100,y=650)#commande la lecture de tous les fichiers 
B6=Button(fenetre_meteo,text="Forçage des mesures",command=forcage_mesures,state=DISABLED,width=50)
B6.place(x=100,y=100)#commande manuelle la lecture des mesures 
B7=Button(fenetre_meteo,text="Désactiver la mise en veille",command=desactive_veille,state=NORMAL,width=50)
B7.place(x=400,y=100)# Bouton permettant de désactivier la mise en veille du Xbee Distant
L2=Label(fenetre_meteo,textvariable=text2)
L2.place(x = 20, y=300)#Label d'affichage 
L3=Label(fenetre_meteo,textvariable=text3)
L3.place(x = 20, y=350)

can1 = Canvas(fenetre_meteo,bg='yellow',height=50,width=700)#canvas d'affichage de l'état de la batterie fond
can1.place(x= 20, y = 600)
can2 = Canvas(fenetre_meteo,height=50,width=50)#canvas d'affichage de l'état de la batterie 
can2.place(x=650,y=450)
can3 = Canvas(fenetre_meteo,height=50,width=400)
can3.place(x=400,y=400)

poly2=can1.create_polygon(0, 0, 0, 0,0,50,0,50, fill='red')#canvas d'affichage de l'état de la batterie en rouge si pas assez chargée 
txt0=can1.create_text(350, 30, text="charge batterie", font=("Arial"," 16"), fill="blue")#Texte indiquant l'état de la batterie
texte1=can1.create_text(0, 12, text="0 V" , font=("Arial"," 12"," italic"), fill="black")
a = MyTimer(60, mesures, ["MyTimer"])   #un timer de 1 h   demande  la lecture des données et leur enregistrement

b = MyTimer(2, scrutation_mise_en_veille, ["MyTimer"])#un timer de 2 s scrute le port Xbee 

fenetre_meteo.mainloop()