Partie technique Data painting

De Learning Lab Environnements Connectés
Sauter à la navigation Sauter à la recherche

Partie Electronique

Cette partie a pour but d'expliciter la réalisation technique du projet. C'est en d'autre mot la recette à suivre pour construire le projet

Matériel utilisé

  • Une carte Arduino Yun
  • Un capteur de température et d'humidité HTU21D-F
  • Un module GPS avec son shield : EM406
  • Un écran LCD-Grove et son shield adaptable à la carte Arduino
  • Une batterie d'alimentation 5v 6000mAh Leitz
  • Une carte SD SanDisk 32 Go

Voici les différents éléments en image :

carte arduino utilisée
capteur de température
Module GPS avec son shield
Shield grove pour arduino
Ecran LCD
Batterie d'alimentation
carte de stockage micro-SD




Voici les liens vers les différentes datasheet des composants :
présentation Arduino Yun
datasheet GPS
datasheet capteur de température/humidité
datassent écran LCD

Branchements

Pour commencer à brancher le système il est nécessaire de comprendre les différents moyens de communications des capteurs avec la carte.
Pour commencer, parlons du GPS. Le GPS utilise la liaison série pour communiquer (TX/RX). Le shield a deux fonction de communication : UART et DLINE. Cette différence n'est qu'une variation des pins utilisées. Nous avons fonctionné, lors de notre projet, en mode DLINE. Attention ! Il serait donc logique de connecter les pins 2 et 3 sur les 0 et 1 de la carte. Cependant le code créée utilise des interruptions et la carte arduino yun ne supporte pas les interruptions sur la pin 0. Il faut donc l'associer à la pin 10.

Module GPS

Ce module (EM406 comme indiqué plus haut) est un GPS avec son shield. Cependant nous n'avons pas branché directement notre shield sur notre par rapport aux problème d'interruptions (Cf "Branchement")
Notons qu'il est nécessaire d'utiliser la librairie correspond au GPS, ici Tiny.h. Vous devez télécharger cette librairy et l'installer sur le logiciel Arduino. Si vous ne savez pas installez de library sur Arduino voici un tutoriel complet : http://wiki.mchobby.be/index.php?title=Installation_d'un_librairie_Arduino
Voici donc le code utilisé pour récupérer la latitude et la longitude d'une position. Ces données sont brutes et seront traitées après récupération avec le logiciel processing.


#include <SoftwareSerial.h>
#include <TinyGPS.h>

int RXPIN = 10;
int TXPIN = 1;
//#define GPSBAUD 4800

TinyGPS gps;
SoftwareSerial gpsSerial(RXPIN, TXPIN);

void getgps(TinyGPS &gps);

void setup()
{
 Serial.begin(4800);
 gpsSerial.begin(4800);
 
 Serial.println("Attend un signal");
}

 void getgps(TinyGPS &gps)
 {
 float latitude, longitude;
 
 gps.f_get_position(&latitude, &longitude);
 Serial.println(latitude);
 Serial.println(longitude);
 }

void loop(){
 
 while(gpsSerial.available() > 0){
   byte c = gpsSerial.read();
   if(gps.encode(c)){
    Serial.write(c);
    getgps(gps);
   }
 }
  }

Capteur Humidité & Température

Le capteur d'humidité et de température sont monté sur un même circuit : le HT21D-f. Il fonctionnera en liaison I2C avec la carte Arduino Yun. Voici le schéma de branchement :

carte arduino utilisée

Une fois le branchement effectué, testez le fonctionnement du capteur seul. Avec le logiciel Arduino, allez sur Croquis >> Inclure une bibliothèque >> Gérez les bibliothèques , tapez Adafruit HTU21D-F Library et installez la bibliothèque. Puis allez dans Fichier >> Exemples >> Adafruit HTU21D-F Library >> HTU21DF_test Téléverser le programme et vérifiez dans le moniteur série l'affichage de la température et de l'humidité. Si tout marche bien, un affichage semblable devrait apparaitre :

Temp : xx°C Hum : xx %
Temp : xx°C Hum : xx %
Temp : xx°C Hum : xx %
Temp : xx°C Hum : xx %
...

Stockage des données sur carte Micro-SD

La carte de développement Arduino Yun dispose d'un lecteur de carte micro-SD. Gràce à quelques lignes de commandes simples, nous pouvons dialoguer avec ce périphériques afin de stocker des données dessus. En l’occurrence ici, les valeurs de nos différents capteurs. Avant de plugger la carte mémoire dans la Yun, connecter la micro-SD à votre PC, vérifiez bien qu'elle est vide et formaté en FAT32, puis, créez à la racine un dossier "arduino". Celui-ci est indispensable afin d'établir la connexion entre la carte SD et l'Arduino Yun.

Vous pouvez à présent placer votre carte mémoire dans le slot, puis lancer le programme ci-dessous. Afin de vérifiez le fonctionnement du programme, rebrancher la carte SD à votre ordinateur, un fichier "datalog.txt" doit avoir apparu avec le mot "test" écrit à l'intérieur.

#include <FileIO.h>

void setup() {
  Bridge.begin();
  Serial.begin(9600);
  FileSystem.begin();

  while(!Serial);  
  Serial.println("Filesystem datalogger\n");
}

void loop() {
File dataFile = FileSystem.open("/mnt/sd/datalog.txt", FILE_APPEND);
if (dataFile) {
    dataFile.println("test");
    dataFile.close();
  }
else {
    Serial.println("error opening datalog.txt");
  }
  delay(15000);
}


Affichage sur l'écran LCD

Nous voulons ensuite afficher nos informations sur l'écran LCD. Celui utilisé ici utilise le système Grove. Il est donc nécessaire d'utiliser un shield Grove adapté à la carte Arduino. Attention ! Lors de ce projet nous avons utilisé une Arduino Yun, qui a parfois des problèmes de masse avec le shield installé. Si c'est le cas il faut alors rallonger les broches du shield afin qu'il n'y ait aucun contact avec la carte ( excepté les broches évidemment).
Le protocole de fonctionnement de cet LCD est l'I2C. Il est nécessaire d'installer une bibliothèque spécifique pour son fonctionnement. Cette bibliothèque se nomme "rgb_lcd.h" . Pour l'installer suivez le protocole indiqué plus haut en modifiant la recherche par "lcd grove".
Vous pouvez suivre la même démarche donnez pour le capteur de température (example...etc). Sinon voici le code à téléviser :



#include <Wire.h>
#include "rgb_lcd.h"

rgb_lcd lcd;

const int colorR = 150;
const int colorG = 50;
const int colorB = 100;

void setup() 
{
    // set up the LCD's number of columns and rows:
    lcd.begin(16, 2);
    
    lcd.setRGB(colorR, colorG, colorB);
    
    // Print a message to the LCD.
    lcd.print("Salut la compagnie");

    delay(5000);
}

void loop() 
{
    // set the cursor to column 0, line 1
    // (note: line 1 is the second row, since counting begins with 0):
    lcd.setCursor(0, 1);
    // print the number of seconds since reset:
    lcd.print(millis()/1000);

    delay(1000);
}

Programme final

Une fois tout ceci réalisé il faut alors relier toutes les parties sur un même code. Il faut bien implanter toutes les bibliothèques et vérifier tous les branchements.
Nous avons utiliser une petite astuce afin d'afficher sur le LCD successivement la température, l'humidité et l'altitude. Toutes les données sont alors stockées dans la micro SD jusqu'à la fin de l'utilisation du système. Certaines lignes du programme sont commentées afin car inutile si il n'y a pas d'utilisation du port série de l'ordinateur pour un fonctionnement en autonomie. Cependant celles-ci peuvent être décommentées afin de vérifier le fonctionnement du croquis étape par étape. Voilà le code final :

#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <Wire.h>
#include "Adafruit_HTU21DF.h"
#include "rgb_lcd.h"
#include <FileIO.h>

int RXPin = 10;
int TXPin = 1;
int GPSBaud = 4800;
const int colorR = 150;
const int colorG = 50;
const int colorB = 100;
int a=2;
int b=0;
int c=0;

TinyGPSPlus gps;
SoftwareSerial gpsSerial(RXPin, TXPin);
Adafruit_HTU21DF htu = Adafruit_HTU21DF();
rgb_lcd lcd;

void setup()
{
  Bridge.begin();
  Serial.begin(9600);
  FileSystem.begin();
  gpsSerial.begin(GPSBaud);
  lcd.begin(16, 2);
  lcd.setRGB(colorR, colorG, colorB);
  //Serial.println(F("A simple demonstration of TinyGPS++ with an attached GPS module"));
}

void loop()
{
  while (gpsSerial.available() > 0)
    if (gps.encode(gpsSerial.read()))
      displayInfo();
  // If 5000 milliseconds pass and there are no characters coming in
  // over the software serial port, show a "No GPS detected" error
  if (millis() > 5000 && gps.charsProcessed() < 10)
  {
    Serial.println(F("No GPS detected"));
    while(true);
    }
  /*Serial.print("Temp: "); Serial.print(htu.readTemperature());
  Serial.print("\t\tHum: "); Serial.println(htu.readHumidity());*/
    
  //delay(1000);
}

void displayInfo()
{
  Serial.print(F("Location: ")); 
  if (gps.location.isValid())
  {
    Serial.print(gps.location.lat(), 6);
    Serial.print(F(","));
    Serial.print(gps.location.lng(), 6);
    File dataFile = FileSystem.open("/mnt/sd/datalog.csv", FILE_APPEND);
if (dataFile) {
    dataFile.print(gps.location.lat(),6);
    dataFile.print(",");
    dataFile.print(gps.location.lng(),6);
    dataFile.print(",");
    dataFile.close();
}
  }
  else
  {
    Serial.print(F("INVALID"));
  }

  Serial.print(F(" Altitude: "));
  if (gps.altitude.isValid())
  {
   double alti =0;
   alti =gps.altitude.meters();   
   Serial.println(alti);
   File dataFile = FileSystem.open("/mnt/sd/datalog.csv", FILE_APPEND);
if (dataFile) {
    dataFile.print(alti);
    dataFile.print(",");
    dataFile.close();

  }
  }
else 
{
    Serial.print(F("INVALID"));
  }
if (htu.begin())
{
float temperature =0;
float humidite  =0;
temperature = htu.readTemperature();
humidite = htu.readHumidity();
Serial.print("Temp: "); Serial.print(temperature);
Serial.print("\t\tHum: "); Serial.println(humidite);
File dataFile = FileSystem.open("/mnt/sd/datalog.csv", FILE_APPEND);
if (dataFile) {
    dataFile.print(temperature);
    dataFile.print(",");
    dataFile.println(humidite);
    dataFile.close();
  }
}
  else 
  {
  Serial.print("pas de sensor");
  }

  if (a>b && a>c)
  {
  lcd.clear();  
  lcd.setCursor(0, 0);
  lcd.print("temp: ");
  lcd.print((htu.readTemperature()));
  lcd.print((char)223);
  lcd.print("C");
    a=0; b=2;
    }
else if (b>a && b>c) 
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("humidity: ");
  lcd.print((htu.readHumidity()));
  lcd.print((char)045);
  
  b=0; c=2;
  }
else if (c>a && c>b) 
{
   lcd.clear();
   lcd.setCursor(0, 0);
   lcd.print("Alt:");
   lcd.print((gps.altitude.meters()));
   lcd.print("m");
  a=2; c=0;
  }
  Serial.println(); 
  delay(2000);
}

Partie Processing

Une fois la donnée récupérée, elle est stockée sur la carte micro SD dans un fichier csv avec ce formattage :

latitude,longitude,altitude,température,humidité  <--- 1ère mesure
latitude,longitude,altitude,température,humidité  <--- 2ème mesure
latitude,longitude,altitude,température,humidité  <--- 3ème mesure
latitude,longitude,altitude,température,humidité  <--- 4ème mesure
...

voici un exemple de dix mesures captées avec le dispositif :

45.451343,4.387325,516.40,16.07,43.03
45.451343,4.387325,516.40,16.04,43.47
45.451347,4.387387,515.40,15.75,44.35
45.451347,4.387387,515.40,15.68,44.58
45.451339,4.387518,515.40,15.48,44.78
45.451351,4.387597,511.60,15.12,45.52
45.451351,4.387597,511.60,14.98,46.01
45.451366,4.387747,513.10,14.96,46.49
45.451366,4.387747,513.10,14.95,46.91
45.451389,4.387815,515.40,14.95,46.83
45.451389,4.387815,515.40,14.98,46.78

Le fichier obtenu (ici "data.csv") est par la suite importé sur processing et interprété en points de différentes taille et couleur avec le code suivant :

Table table;
float[] latitude;
float[] longitude;
int[] altitude;
int[] temperature;
float[] x;
float[] y;

//-------------------------------------------------------------------------------------------------------------
//--- Données à modifier pour adapter le tracé
int maxMapAlti = 30; //diametre des cercles en fonction de l'altitude (en px)
int decalage = 250; //taille des bordures de la zone de rendu (en px)
//-------------------------------------------------------------------------------------------------------------

void setup() {
  size(displayWidth, displayHeight);
  frameRate(60);
  table = loadTable("datas.csv");
  int nbreLignes = table.getRowCount();

  latitude = new float[nbreLignes];
  longitude = new float[nbreLignes];
  altitude = new int[nbreLignes];
  temperature = new int[nbreLignes];

  for (int i=0; i<=table.getRowCount()-1; i++) {
    latitude[i] = table.getRow(i).getFloat(0);
    longitude[i] = table.getRow(i).getFloat(1);
    altitude[i] = table.getRow(i).getInt(2);
    temperature[i] = table.getRow(i).getInt(3);
  }

  //-------------------------------------------------------------------------------------------------------------
  //--- Formule de transformation de coordonées polaires en coordonées x, y à l'échelle du monde
  //-------------------------------------------------------------------------------------------------------------

  float[] xWorld = new float[nbreLignes];
  float[] yWorld = new float[nbreLignes];
  for (int i=0; i<=table.getRowCount()-1; i++) {
    xWorld[i] = (longitude[i]+180)*((width-(2*decalage))/360);
    float latRad = latitude[i]*PI/180;
    float mercN = log(tan((PI/4)+(latRad/2)));
    yWorld[i] = ((height-(2*decalage)/2))-((width-(2*decalage))*mercN/(2*PI));
  }

  //-------------------------------------------------------------------------------------------------------------
  //--- Mise à l'échelle des coordonnées pour qu'elle apparaissent à l'échelle de la fenêtre de visualisation
  //-------------------------------------------------------------------------------------------------------------

  float xMax = max(xWorld);
  float xMin = min(xWorld);
  float yMax = max(yWorld);
  float yMin = min(yWorld);
  float ratioGPS = (xMax - xMin) / (yMax - yMin);
  float windowWidth = width-(2*decalage);
  float windowHeight = height-(2*decalage);
  float ratioWindow = windowWidth/windowHeight;

  x = new float[nbreLignes];
  y = new float[nbreLignes];
  if (ratioGPS < ratioWindow) {
    for (int i=0; i<=table.getRowCount()-1; i++) {
      y[i] = map(yWorld[i], yMin, yMax, decalage, height-decalage);
      x[i] = map(xWorld[i], xMin, xMax, decalage, ratioGPS*(height-decalage));
    }
  } else {
    for (int i=0; i<=table.getRowCount()-1; i++) {
      x[i] = map(xWorld[i], xMin, xMax, decalage, width-decalage);
      y[i] = map(yWorld[i], yMin, yMax, decalage, (width-decalage)/ratioGPS);
    }
  }
}

//-------------------------------------------------------------------------------------------------------------
//--- Affichage du tracé
//-------------------------------------------------------------------------------------------------------------

int timer = 1;

void draw() {
  //-------------------------------------------------------------------------------------------------------------
  //--- Centrage des éléments
  //-------------------------------------------------------------------------------------------------------------
  translate(width/2-(max(x)-min(x))/2-decalage, height/2-(max(y)-min(y))/2-decalage);
  colorMode(RGB, 255);
  background(255);
  colorMode(HSB, 100);
  
  //-------------------------------------------------------------------------------------------------------------
  //--- Lignes entre les points (mettre le timer à 2 pour que cela fonctionne)
  //-------------------------------------------------------------------------------------------------------------

  stroke(5);
  for (int i=0; i<=timer-2; i++) {
    stroke(map(temperature[i], 0, 30, 50, 100), 90, 90);
    strokeWeight(5);
    line(x[i], y[i], x[i+1], y[i+1]);
  }
  
  //-------------------------------------------------------------------------------------------------------------
  //--- placement des points suivant les coordonées x, y, la température et l'altitude
  //-------------------------------------------------------------------------------------------------------------

  noStroke();
  for (int i=0; i<=timer-1; i++) {
    fill(map(temperature[i], 0, 30, 50, 100), 90, 90);
    ellipse(x[i], y[i], map(altitude[i], 0, 800, 1, maxMapAlti), map(altitude[i], 0, 800, 1, maxMapAlti));
  }

  //-------------------------------------------------------------------------------------------------------------
  //--- dégradé de couleurs pour la température
  //-------------------------------------------------------------------------------------------------------------
  /*
  for (int i=0; i<=20; i++) {
   fill(50+i*2.5, 100, 100);
   rect(50+52*i, 50, 50, 25);
   text(int(map(50+i*2.5,50,100,0,30)) + "°C",50+52*i, 49);
   }
   */
  
  //-------------------------------------------------------------------------------------------------------------
  //--- Mise à jour du timer pour faire apparaitre tous les points jusqu'à la fin de l'animation
  //-------------------------------------------------------------------------------------------------------------
  //save("img"+timer+".png");
  if (timer >= table.getRowCount()) {
    timer = table.getRowCount();
  } else {
    timer++;
  }
}
Exemple du tracé obtenu à partir des mesures et du code processing

Partie Maquettage

Plan des différents étages de la maquette

Les différents composants électroniques étaient disposés sur une maquette découpé à la découpeuse laser sur du bois d'une épaisseur de 3mm.
Voici les plans en version PDF :