Grâce à tous les capteurs compatibles avec la carte Arduino, il est très facile de construire une station météorologique complète. Voici comment je procède.

Les composants

Pour réaliser cette station météorologique, il faut d’abord choisir ce qui doit être surveillé. J’ai choisi la température, l’humidité et la pression atmosphérique. On pourrait aussi considérer la quantité de pluie, la luminosité, le taux d’oxygène, etc. Tout est une question de trouver le capteur approprié. Faites attention au nombre limité de broches sur votre carte Arduino. Au-delà d’un certain nombre de capteurs, il peut être nécessaire d’ajouter d’autres composants pour augmenter le nombre d’entrées/sorties de l’Arduino. Ici, nous voulons maintenir une installation simple, les composants suivants ont été utilisés :

– pour la pression, un BMP085

BMP085
  • pour l’humidité un DHT22
large_DHT22

L’avantage de ces capteurs, c’est qu’ils possèdent un capteur de température intégré utilisé pour leur propre compensation. Celui du BMP085 est le plus précis, mais nous enregistrerons quand même les données du DHT22.

Maintenant que nous avons les capteurs, nous devons sauvegarder les données et savoir à quel moment elles ont été mesurées. Nous choisissons donc :

– un adaptateur SD pour stocker les mesures:

– un composant appelé TinyRTC pour Real Time Clock:

Au cœur de tout cela, nous ajoutons un ATMEGA 328P qui sera utilisé avec son oscillateur interne de 8MHz afin de simplifier encore plus l’installation. Pour l’alimentation, nous souhaitons utiliser une pile 9V, donc nous ajoutons un LM075 qui est responsable de la partie régulation.

Ensuite, nous voulons régler la date et visualiser les valeurs des capteurs sans passer par un ordinateur. Nous ajoutons donc un écran LCD Nokia 5110 et quelques boutons-poussoirs pour interagir avec l’ATMEGA.

Quelques résistances pour assurer les conversions de 5/3,3 volts et voilà, il ne reste plus qu’à réaliser le câblage.

Le câblage

Avant de tout souder définitivement, nous travaillons avec un Arduino Uno sur une carte de prototypage. Pour le câblage, il est à noter que le RTC et le BMP085 communiquent tous les deux avec le protocole I2C, ce qui nécessite une seule boucle de fil à travers les ports SDA et SCL de ces deux unités.

Pour la carte SD, le protocole est un peu plus coûteux en câbles et en broches, puisqu’en plus de l’alimentation, il faut 4 broches (MISO, MOSI, CS, SCK).

Le DHT22 est le plus économique utilisant une seule broche pour la mesure.

Nous avons légèrement modifié le TinyRTC en dessoudant la diode et en connectant l’alimentation directement sur les broches de la batterie. En effet, ce composant est censé fonctionner avec une pile bouton rechargeable, assez difficile à obtenir, et de toute façon, dans cet arrangement, l’alimentation sera toujours disponible au moins pour alimenter l’Arduino.

Les autres broches disponibles de l’Arduino sont utilisées pour envoyer un affichage sur l’écran LCD et récupérer des informations de 4 boutons-poussoirs.

Voici le câblage avec un Arduino Uno sans l’écran LCD et les boutons-poussoirs. Cet assemblage peut déjà effectuer des mesures et les stocker sur une carte SD :

 

Source et libraries

À propos de la partie programmation, le plus difficile est de trouver les bonnes bibliothèques qui permettent de communiquer avec toutes les unités. Voici la liste :

  • Tiny RTC : Bibliothèque Adafruit disponible ici
  • SD Breakout : bibliothèque pour SD disponible dans l’IDE Arduino et proposée par Sparkfun
  • DHT22 : Bibliothèque Adafruit disponible ici
  • BMP085 : Bibliothèque Adafruit disponible ici

Nous réalisons qu’un grand nombre de composants signifie également un grand nombre de bibliothèques. Cela peut devenir difficile tant en nombre de broches disponibles qu’en mémoire disponible pour charger le programme et les bibliothèques. C’est un paramètre à prendre en compte le plus tôt possible dans la conception.

Il ne reste alors plus qu’à initialiser les capteurs en assignant les bonnes broches, boucler à travers la mesure et l’écriture sur la carte SD. Dès que la mesure est effectuée, l’ATMEGA est inactif pendant une minute. Comme nous ne réveillons pas l’ATMEGA par une interruption externe, nous ne pouvons autoriser que des veilles de 8 secondes. Nous les faisons jusqu’à ce qu’une minute se soit écoulée. La boucle recommence alors pour la prochaine mesure. Voici le code complet qui est chargé directement sur l’ATMEGA (faites défiler vers le bas pour voir le code complet) :

/*
  SD Card connection:  MOSI - pin 11 ; MISO - pin 12 ; CLK - pin 13 ; CS - pin 10  
*/
 
// Libraries
#include <SD.h> // Library for SD card connection
#include <Wire.h> // Library to communicate with I2C modules
#include "RTClib.h" // Library to use RTC module
#include <Adafruit_BMP085.h> // Library to use BMP085
#include "DHT.h" // library to use DHT sensor
#include <PCD8544.h> // library for LCD Screen
// Low power
#include <avr/sleep.h>
#include <avr/wdt.h>

// watchdog interrupt
ISR(WDT_vect) 
  {
  wdt_disable();  // disable watchdog
  }

void myWatchdogEnable(const byte interval) 
  {  
  MCUSR = 0;                          // reset various flags
  WDTCSR |= 0b00011000;               // see docs, set WDCE, WDE
  WDTCSR =  0b01000000 | interval;    // set WDIE, and appropriate delay

  wdt_reset();
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
  sleep_mode();            // now goes to Sleep and waits for the interrupt
  } 
 
 
// Variable declaration
#define DHTPIN 2 // Digital pin of DHT sensor
#define DHTTYPE DHT22   // DHT 22  (AM2302)
DHT myDHT(DHTPIN, DHTTYPE,3); // Instanciate DHT module
RTC_DS1307 myRTC; // Instanciate RTC module
File myOutputFile; // Instanciate output file
char myData[100]; // Contain current data
Adafruit_BMP085 myBMP; // Instanciate BMP module
static PCD8544 lcd; // instanciate LCD screen

uint16_t year;
uint8_t  month;
uint8_t day; 
uint8_t hour; 
uint8_t minute; 
uint8_t second; 
float temp_BMP;
int temp_BMP_int; // Integer part of temperature
int temp_BMP_dec; // Decimal part of temperature
int32_t pressure_BMP;
float temp_DHT;
int temp_DHT_int;
int temp_DHT_dec;
float humidity_DHT;
int humidity_DHT_int;
int humidity_DHT_dec;

int scroll_pin = A3;
int increment_pin = A2;
int decrement_pin = A1;


void setup()
{
  // Start SD card connection : wait for port to open
  pinMode(10, OUTPUT);
  SD.begin(10);
  analogReference(INTERNAL);    
  // Start I2C connections
  Wire.begin();
  // Start RTC module
  myRTC.begin();
  if (! myRTC.isrunning()) {
    // Following line sets the RTC to the date & time this sketch was compiled
    myRTC.adjust(DateTime(__DATE__, __TIME__));
  }   
  // Start BMP module
  if (!myBMP.begin()) {
	//Could not find a valid BMP085 sensor, check wiring
  }
  // Start DHT module
  myDHT.begin();
  // Start LCD module
  lcd.begin(84, 48);
  // Initiate pin mode
  pinMode(scroll_pin, INPUT);
  pinMode(increment_pin, INPUT);
  pinMode(decrement_pin, INPUT);
  
}

void loop()
{
  // Retrieve RTC date
  DateTime myDateTime = myRTC.now();
  year =  myDateTime.year();
  month = myDateTime.month();
  day = myDateTime.day();
  hour = myDateTime.hour();
  minute = myDateTime.minute();
  second =  myDateTime.second();
  
  // Retrieve BMP085 data
  temp_BMP = myBMP.readTemperature();
  temp_BMP_int = int(temp_BMP); 
  temp_BMP_dec = int((temp_BMP - temp_BMP_int)*100.);   
  pressure_BMP = myBMP.readPressure();
  
  // Retrieve DHT22 data 
  temp_DHT = myDHT.readTemperature();
  temp_DHT_int = int(temp_DHT); 
  temp_DHT_dec = int((temp_DHT - temp_DHT_int)*100.);   
  humidity_DHT = myDHT.readHumidity();
  humidity_DHT_int = int(humidity_DHT); 
  humidity_DHT_dec = int((humidity_DHT - humidity_DHT_int)*100.);   
     
  sprintf(myData, "%u/%u/%u    %u:%u:%u    %i.%i    %li    %i.%i    %i.%i", year, month, day, hour, minute, second, temp_BMP_int, temp_BMP_dec, pressure_BMP, temp_DHT_int, temp_DHT_dec, humidity_DHT_int, humidity_DHT_dec); 
  myOutputFile = SD.open("Data.txt", FILE_WRITE);
  myOutputFile.println(myData);
  myOutputFile.close();   
  
  // LCD Display
  // Date
  sprintf(myData, "%u/%u/%u", day, month, year);   
  lcd.setCursor(0, 0);
  lcd.print("              ");
  lcd.setCursor(0, 0);
  lcd.print(myData);
  // Time
  sprintf(myData, "%u:%u:%u", hour, minute, second);  
  lcd.setCursor(0, 1);
  lcd.print("              ");
  lcd.setCursor(0, 1);
  lcd.print(myData);
  // BMP085 temp
  sprintf(myData, "%i.%idegC", temp_BMP_int, temp_BMP_dec);  
  lcd.setCursor(0, 2);
  lcd.print("              ");
  lcd.setCursor(0, 2);
  lcd.print(myData);
  // DHT22 temp
  sprintf(myData, "%i.%idegC",  temp_DHT_int, temp_DHT_dec);  
  lcd.setCursor(0, 3);
  lcd.print("              ");
  lcd.setCursor(0, 3);  
  lcd.print(myData);
  // BMP085 pressure
  sprintf(myData, "%liPa", pressure_BMP);  
  lcd.setCursor(0, 4);
  lcd.print("              ");
  lcd.setCursor(0, 4);
  lcd.print(myData);
  // DHT22 humidity
  sprintf(myData, "%i.%i%%", humidity_DHT_int, humidity_DHT_dec);  
  lcd.setCursor(0, 5);
  lcd.print("              ");  
  lcd.setCursor(0, 5);
  lcd.print(myData);

  // Enter setup mode
  if (digitalRead(scroll_pin) == HIGH) {
    waitForButtonRelease(scroll_pin);
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Setup mode");
    year = setValue(year, "Year", 2000,2100);
    month = setValue(month, "Month",1,12);
    day = setValue(day, "Day",1,31);
    hour = setValue(hour, "Hour",0,23);
    minute = setValue(minute, "Minute",0,59);
    second = setValue(second, "Seconds",0,59);
    // Write down new date
    myRTC.adjust(DateTime(year, month, day, hour, minute, second));
  }else{
    // Enter sleep mode for 60 seconds
    myWatchdogEnable (0b100001);  // 8 seconds    
    if (digitalRead(scroll_pin) != HIGH){
      myWatchdogEnable (0b100001);  // 8 seconds
    }
    if (digitalRead(scroll_pin) != HIGH){
      myWatchdogEnable (0b100001);  // 8 seconds
    }
    if (digitalRead(scroll_pin) != HIGH){
      myWatchdogEnable (0b100001);  // 8 seconds
    }
    if (digitalRead(scroll_pin) != HIGH){
      myWatchdogEnable (0b100001);  // 8 seconds
    }
    if (digitalRead(scroll_pin) != HIGH){
      myWatchdogEnable (0b100001);  // 8 seconds
    }
    if (digitalRead(scroll_pin) != HIGH){
      myWatchdogEnable (0b100001);  // 8 seconds
    }
    if (digitalRead(scroll_pin) != HIGH){    
      myWatchdogEnable (0b100000);  // 4 seconds
    }

  }
  delay(100);
  
  
}

uint16_t setValue(uint16_t value, char * label, uint16_t min_value, uint16_t max_value){
  // We enter this method on scroll button push, wait for it to be released
  waitForButtonRelease(scroll_pin);
  // Scroll button will exit this method
  while (true){
    delay(10);
    // Display year and helping symbols
    lcd.setCursor(0, 2);    
    lcd.print("              ");
    lcd.setCursor(0, 2);    
    sprintf(myData, "%s: %u",label, value);  
    lcd.print(myData);
    lcd.setCursor(0, 4);        
    lcd.print("-    Set    +");
    // Wait for action
    if (digitalRead(scroll_pin) == HIGH){
      waitForButtonRelease(scroll_pin);
      return value;
    }
    else if (digitalRead(decrement_pin) == HIGH) {
      waitForButtonRelease(decrement_pin);
      value--;
      if (value == min_value-1){
        value = max_value;
      }
    }
    else if (digitalRead(increment_pin) == HIGH ){       
      waitForButtonRelease(increment_pin);
      value++;
      if (value == max_value+1){
        value = min_value;
      }
    }
  }
}

void waitForButtonRelease(int btnPin){
  while (digitalRead(btnPin) == HIGH){    
  }
}

Packaging

Il est temps d’effectuer le montage final pour l’utilisation en extérieur.

Nous avons opté pour deux boîtiers en plastique dur découpés à l’aide d’une scie sauteuse et assemblés avec de la colle. Nous souhaitons rendre l’écran LCD indépendant, car il ne résisterait pas aux conditions extérieures, et pouvoir l’utiliser sur plusieurs stations météorologiques. Le code permet de le déconnecter sans interrompre la mesure. Et le bouton Reset permet de le connecter à tout moment en redémarrant l’ATMEGA.

Pour le montage des capteurs, un côté du boîtier est amovible pour faciliter l’accès à la carte SD et à la batterie. Enfin, une fenêtre est créée pour connecter l’écran LCD via un câble ruban IDE. De petits trous sont faits sur un autre côté pour permettre l’échange d’air entre l’extérieur et les capteurs de pression, température et humidité. Les capteurs sont positionnés en bas afin que la chaleur générée par l’ensemble n’interfère pas avec les mesures. Tout est inséré sous un boîtier en aluminium, également découpé à l’aide d’une scie sauteuse, assemblé par rivets et peint en blanc pour ne pas conserver la chaleur et fausser les mesures. Toutes les jointures sont scellées avec du silicone pour rendre la station résistante à la pluie.

L’assemblage est suspendu dans le boîtier en aluminium par un système de crochets, et le tout est accroché à un arbre avec une sangle.

User manual

  1. Nous insérons la carte SD avant de connecter la batterie.
  2. Après avoir connecté la batterie, nous connectons l’écran à l’aide du câble à nappe IDE.
  3. Une pression sur le bouton RESET permet de redémarrer l’unité et de faire fonctionner l’écran.
  4. Une pression longue (supérieure à 8 secondes) sur le bouton SET permet d’entrer en mode de configuration.
  5. Nous pouvons ensuite régler la date et l’heure avec les boutons + et -.
  6. Après avoir vérifié toutes les données à l’écran, nous débranchons à chaud et nous plaçons le tout dans le boîtier en aluminium.
  7. À la fin des mesures, nous pouvons déconnecter la batterie et récupérer la carte SD.
  8. Le contenu de la carte SD peut être lu sur un ordinateur.
IMG_2423

Les données sont enregistrées sur la carte SD dans le format suivant :

2014/1/29 23:4:37 24.43 99933 23.0 45.29 
2014/1/29 23:4:47 24.95 99926 23.0 45.29 
2014/1/29 23:4:57 25.39 99919 23.0 45.40 
2014/3/28 21:0:0 26.6 99938 23.10 45.29

Nous pouvons lire le jour, l’heure, la température et la pression provenant du BMP085, ensuite la température et l’humidité à partir du DHT22. Nous pouvons les importer et tracer automatiquement des graphiques en utilisant la feuille Excel suivante : WeatherStationAnalysis.xlsm

Quelques améliorations

Le principal inconvénient de cette station est l’alimentation électrique. De nombreux composants peuvent être alimentés avec 3,3 V, alors que nous fournissons 5 V, chacun effectuant sa propre conversion, à l’exception de la carte SD où la conversion est faite via des résistances. En outre, pour obtenir les 5 V, nous alimentons le LM075 avec 9 V. En résumé, beaucoup de pertes alors que tout peut être alimenté directement avec 3,3 V. Mais est-il possible d’alimenter un ATMEGA avec 3,3 V ? C’est la question à résoudre pour améliorer grandement cet assemblage.

Pour réduire l’impact de ce défaut de conception, nous ne prenons des mesures qu’une fois par minute, et pendant le temps intermédiaire, l’ATMEGA est en mode veille, ce qui divise par deux le courant.

Une dernière amélioration serait évidemment de choisir une horloge capable de réveiller l’ATMEGA toutes les minutes, ce qui permettrait d’éteindre tous les composants pendant une minute complète entre deux mesures, en utilisant l’horloge pour fournir une interruption externe.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *