La température monte…

Pour pouvoir faire le suivi d’une cave à vin, j’avais besoin de plusieurs capteurs de température et d’humidité, pas le top mais tout de même assez précis.

Voici un petit tour des différents capteurs pas trop cher (moins de 6 euros) et facilement accessible que j’ai testé. Ils sont tous utilisable avec une carte Arduino et accepte du 5v (sauf le BME280 qui est en 3v3 mais le breakout permet l’alimentation en 5v). A la fin de l’article, vous trouverez le programme pour les contrôler tous AHAHAH…hum… les faire fonctionner tous.

Le DS18B20 de Maxim (ex Dallas)

Voici un capteur de température, plus petit (boitier TO92), simple mais efficace, précis et avec une résolution à 2 décimales. Il ne fait que la température mais m’a servi à avoir une première référence. Communication en 1-wire.

Le DHT11 de Aosong

Le capteur de base pour les arduinistes avec mesure de la température et l’humidité. Petit capteur pas cher … et pas très précis surtout pour l’humidité relative. Il ne peut pas mesurer de température négative et est limité sur la plage pour l’humidité relative. Il n’a pas de décimale pour les valeurs. Communication en 1-wire spécifique.

Le DHT22 (AM2302) de Aosong

Le grand frère du DHT11, plus gros, plus précis, plus cher. Il peut mesurer des températures négatives et utilise la plage 0 à 100% pour l’humidité relative. Il propose également une résolution à une décimale. Communication en 1-wire spécifique.

Le DHT12 de Aosong

Le petit frère de la famille DHT. Il est plus petit que les deux mais précis et d’un cout moindre que le DHT22. Il a une résolution à une décimale. Communication en I²C ou en single-bus.

Le AM2320 de Aosong

Disons un cousin des DHT, légèrement inférieur en taille que le DHT11, il est noir, à 4 pin comme le DHT12. Communication en I²C ou en single-bus.

Le BME280 de Bosch

Ce capteur permet de mesurer la température, l’humidité et la pression atmosphérique. Il est vraiment tout petit (4mm² à vue d’oeil) et ne consomme quasiment rien, parfait pour être intégré dans un smartphone par exemple. En prenant en compte la pression atmosphérique au niveau de la mer, il permet de calculer l’altitude. Plus cher que les autres, plus de fonctions. Communication en I²C ou SPI.

 

Le montage

Il y a des résistances (ici de 4,7kOhm ou 10kOhm) sur tous les cables Data (DATA, SCL, SDA).

 

Alimentation et branchement en fonction des datasheet.

La librairie softwareWire permet de faire de l’I²C avec d’autres pins de la carte Arduino que les pins réservées (à savoir A4 et A5), vous pouvez choisir celles que vous voulez et surtout vous pouvez donc gérer plusieurs devices I²C qui ont la même adresse en créant plusieurs bus I²C (un par device avec la même adresse). Ici le DHT12 et l’AM2320 sont sur 2 bus différents car ils ont la même adresse.

 

 

Les tests

La sortie de la console de l’IDE Arduino (DHT11 sans DHT22) :

DS18B20 : Temperature : 22.37 *C
DHT11 : Temperature : 21.00 *C , Humidite : 20.00 %
DHT12 : Temperature : 22.00 *C , Humidite : 49.20%
BME280 : Temperature : 25.01 *C , Humidite : 47.20 % , Pressure : 1008.75 hPa , Approx. Altitude : 86.84 m
AM2320 : Temperature : 23.20 *C , Humidite : 51.40%

La sortie de la console de l’IDE Arduino (DHT22 sans DHT11) :

DS18B20 : Temperature : 22.37 *C
DHT22 : Temperature : 23.40 *C , Humidite : 1.00 %
DHT12 : Temperature : 21.90 *C , Humidite : 49.50%
BME280 : Temperature : 24.97 *C , Humidite : 47.22 % , Pressure : 1008.70 hPa , Approx. Altitude : 87.30 m
AM2320 : Temperature : 23.20 *C , Humidite : 51.20%

En effet je n’ai pas réussi a faire fonctionner en même temps les DHT11 et DHT22 ni à récupérer l’humidité avec le DHT22 (à moins que le capteur ne soit cuit).

Analyse

N’ayant pas de d’outil pour faire la calibration, c’est la moyenne et les valeurs les plus proche du thermomètre qui me semble les plus fiables. Le thermomètre affiche 23°C.

Les DS18B20, DHT22 et AM2320 affichent une température très proche de celle du thermomètre. Le DHT12 a 1 degré d’écart et les BME280 et DHT11 2 degrés d’écart. Cependant entre 20 et 30 degrés, les écarts semblent rester constant. Il suffit donc de gérer un offset pour récupérer la bonne température.

concernant l’humidité relative, à part le problème avec le DHT22 et le DHT11 qui est à son minima, les autres capteurs sont aux alentours des 50% en intérieur (les sites météo donne une humidité relative d’environ 70% en extérieur). En soufflant doucement sur les capteurs, l’humidité relative s’envole (+ de 90%), sauf pour le DHT22 qui ne bouge pas et le DHT11 qui mont péniblement à 70% (en soufflant longtemps… dessus). Très bon point pour le BME280 qui est le premier à revenir à un niveau proche de 50% suivi par l’AM2320 un peu plus lent et en dernier les DHT12 et surtout le DHT11.

Conclusion

Malgré la bonne réactivité du BME280 (et juste l’offset sur la température) et le fonctionnement très correct de DHT12 (également avec un offset), le capteur que je vais utiliser sera l’AM2320 qui est plus calibré un peu moins réactif mais un peu moins cher et surtout avec un format plus adapté que le breakout.

 

Des liens qui peuvent être utiles et qui m’ont (fortement) inspirés :

Les différents codes :

Les exemples fournis dans les librairies et

DHT11DHT22 – carnet du maker

DS18B20 – carnet du maker

Playground Arduino

Les librairies pour Arduino :

les librairies disponibles sur Arduino (voir en tête du code)

Adafruit_Sensor
Adafruit_BME280

DHTxx

BME280 – library Adafruit

I²C softwareWire

AM2320_SOFTWIRE // modification de la librairie AM2320 https://github.com/lazyscheduler/AM2320 (à venir sur le github des Fabriqueurs)

Les datasheet :

DS18B20 – Datasheet

DHT11 – Datasheet

DHT22 (AM2302) – Datasheet

DHT12 – Datasheet

AM2320 – Datasheet

BME280 – Datasheet

Petit bonus : voici comment calculer la température en Fahrenheit

Fahrenheit = Celsius x 1.8 + 32.0

et bien sur l’inverse Celsius  = (Fahrenheit – 32) / 1.8

Petit bonus 2 : le BME280 est également un bon capteur de pression. La pression atmosphérique varie en fonction de l’altitude. Si on connait la pression au niveau de la mer, on peut en déduire l’altitude à laquelle se trouve le capteur. Il faut récupérer la pression au niveau de la mer de l’endroit ou se trouve le capteur. Vous trouverez l’information sur des site tel que pression niveau de la mer. La pression atmosphérique est donné en hPa (hectopascal). Puis il suffit d’utiliser la commande de la bibliothèque Adafruit.

readAltitude(xxxx.x); //avec xxxx.x étant la pression en hPa avec une décimale

Le code

Le DHT11 est en commentaire pour faire fonctionner le DHT22.

#include <OneWire.h> // pour le protocole 1-wire avec le DSB1820 // dispo sur les librairies Arduino
#include <Wire.h> // pour le bus I²C
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <dht.h>// DHT12
#include <SoftwareWire.h> // gérer plusieurs devices I2C avec la même adresse sur plusieurs couple pins SDA SCL (autre que les pins standard) https://github.com/Testato/SoftwareWire
#include <AM2320_SOFTWIRE.h> // modification de la librairie AM2320 https://github.com/lazyscheduler/AM2320 // original AM2320 https://github.com/hibikiledo/AM2320 // http://www.instructables.com/id/Connecting-AM2320-With-Arduino/ 
// cela permet d'avoir également la librairie AM2320 hardwire

// infos pour le DS18B20
// OneWire DS18S20, DS18B20, DS1822 Temperature Example
// http://www.pjrc.com/teensy/td_libs_OneWire.html
// The DallasTemperature library can do all this work for you!
// http://milesburton.com/Dallas_Temperature_Control_Library
OneWire  ds(9);  // on pin 9 (a 4.7K resistor is necessary)
//adresse du capteur :28 FF A2 41 20 16 04 24 

// infos pour le BME280 temperature humidité pression
#define BME_SCK 13  // SCL
#define BME_MISO 12 // SDO
#define BME_MOSI 11 // SDA
#define BME_CS 10   // CSB ou SS
#define SEALEVELPRESSURE_HPA (1019.2) // pression au niveau de la mer http://www.infoclimat.fr/cartes/observations-meteo/temps-reel/pression-au-niveau-de-la-mer/france.html
//Adafruit_BME280 bme; // I2C
Adafruit_BME280 bme(BME_CS); // hardware SPI
//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI
unsigned long delayTime;

// infos pour le DHT11
// de face : Pin Definition 1.VDD 2.DATA 3.non connecté 4.GND
const byte BROCHE_CAPTEUR_DHT11 = 8;// pin DATA du capteur / Resistance de pullup broche data-Vcc 10k + condensateur 100nf Vcc--||--gnd
// infos pour le DHT22
// de face : Pin Definition 1.VDD 2.DATA 3.non connecté 4.GND
const byte BROCHE_CAPTEUR_DHT22 = 7;// pin DATA du capteur / Resistance de pullup broche data-Vcc 10k + condensateur 100nf Vcc--||--gnd
/* Code d'erreur de la fonction readDHT11() et readDHT22() */
const byte DHT_SUCCESS = 0;        // Pas d'erreur
const byte DHT_TIMEOUT_ERROR = 1;  // Temps d'attente dépassé
const byte DHT_CHECKSUM_ERROR = 2; // Données reçues erronées
// infos pour le DHT12
// de face :   Pin Definition 1.VDD 2.SDA 3.GND 4.SCL
dht12 DHT(0x5c);

// Pou l'AM2320 Pin Definition 1.VDD 2.SDA 3.GND 4.SCL
AM2320_SOFTWIRE sensor(6,5); //pin 5 et 6 de la carte arduinio



void setup(void) {
  Serial.begin(115200);
    Serial.println(F("Test BME280 / DS18B20 / DHT11 / DHT22 / DHT12 / AM2320"));

    bool status;
    
    // default settings
    status = bme.begin();
    if (!status) {
        Serial.println("Could not find a valid BME280 sensor, check wiring!");
        while (1);
    }
    
    delayTime = 2000;

    /* Place la broche du capteur en entrée avec pull-up */
    pinMode(BROCHE_CAPTEUR_DHT11, INPUT_PULLUP);
    pinMode(BROCHE_CAPTEUR_DHT22, INPUT_PULLUP);

    delay(1000); // let sensor boot up
    Serial.println("end setup");
}

void loop(void) {
  //--- DS18B20 end
  byte i;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];
  
  float celsius, fahrenheit;
  
  if ( !ds.search(addr)) {
    //Serial.println("No more addresses.");
    //Serial.println();
    ds.reset_search();
    delay(250);
    return;
  }
  
  //Serial.print("ROM =");
  for( i = 0; i < 8; i++) {
    //Serial.write(' ');
    //Serial.print(addr[i], HEX);
  }

  if (OneWire::crc8(addr, 7) != addr[7]) {
      Serial.println("CRC is not valid!");
      return;
  }
  //Serial.println();
 
  // the first ROM byte indicates which chip
  switch (addr[0]) {
    case 0x10:
      Serial.println("  Chip = DS18S20");  // or old DS1820
      type_s = 1;
      break;
    case 0x28:
      //Serial.println("  Chip = DS18B20");
      type_s = 0;
      break;
    case 0x22:
      Serial.println("  Chip = DS1822");
      type_s = 0;
      break;
    default:
      Serial.println("Device is not a DS18x20 family device.");
      return;
  } 

  ds.reset();
  ds.select(addr);
  ds.write(0x44, 1);        // start conversion, with parasite power on at the end
  
  delay(2000);     // maybe 750ms is enough, maybe not
  // we might do a ds.depower() here, but the reset will take care of it.
  
  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);         // Read Scratchpad

  //Serial.print("  Data = ");
  //Serial.print(present, HEX);
  //Serial.print(" ");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
    //Serial.print(data[i], HEX);
    //Serial.print(" ");
  }
  //Serial.print(" CRC=");
  //Serial.print(OneWire::crc8(data, 8), HEX);
  //Serial.println();

  // Convert the data to actual temperature
  // because the result is a 16 bit signed integer, it should
  // be stored to an "int16_t" type, which is always 16 bits
  // even when compiled on a 32 bit processor.
  int16_t raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3; // 9 bit resolution default
    if (data[7] == 0x10) {
      // "count remain" gives full 12 bit resolution
      raw = (raw & 0xFFF0) + 12 - data[6];
    }
  } else {
    byte cfg = (data[4] & 0x60);
    // at lower res, the low bits are undefined, so let's zero them
    if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
    else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
    else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
    //// default is 12 bit resolution, 750 ms conversion time
  }
  celsius = (float)raw / 16.0;
  Serial.print("DS18B20 : ");
  Serial.print("Temperature : ");
  Serial.print(celsius);
  Serial.println(" *C");
  //--- DS18B20 end
  
  //--- DHT11 start
  float temperature, humidity;
  /* Lecture de la température et de l'humidité, avec gestion des erreurs */
  // N.B. Remplacer readDHT11 par readDHT22 en fonction du capteur utilisé !
  byte retourDHT11=  readDHT11(BROCHE_CAPTEUR_DHT11, &temperature, &humidity);

  switch (retourDHT11) {
    case DHT_SUCCESS: 
       
      /* Affichage de la température et du taux d'humidité */
      Serial.print("DHT11   : ");
      Serial.print(F("Temperature : "));
      Serial.print(temperature, 2);
      Serial.print(" *C , ");
      Serial.print(F("Humidite : "));
      Serial.print(humidity, 2);
      Serial.println(" %");
      break;
   
    case DHT_TIMEOUT_ERROR: 
      Serial.println(F("Pas de reponse !")); 
      break;
   
    case DHT_CHECKSUM_ERROR: 
      Serial.println(F("Pb de communication !")); 
      break;
  }
  
  /* Pas plus d'une mesure par seconde */
  // N.B. Avec le DHT22 il est possible de réaliser deux mesures par seconde
  //delay(1000);
 
  //--- DHT11 end

  //--- DHT22 start
  //float temperature, humidity;
  /* Lecture de la température et de l'humidité, avec gestion des erreurs */
  // N.B. Remplacer readDHT11 par readDHT22 en fonction du capteur utilisé !
/*  byte retourDHT22=  readDHT22(BROCHE_CAPTEUR_DHT22, &temperature, &humidity);

  switch (retourDHT22) {
    case DHT_SUCCESS: 
       
      /* Affichage de la température et du taux d'humidité */
/*      Serial.print("DHT22   : ");
      Serial.print(F("Temperature : "));
      Serial.print(temperature, 2);
      Serial.print(" *C , ");
      Serial.print(F("Humidite : "));
      Serial.print(humidity, 2);
      Serial.println(" %");
      break;
   
    case DHT_TIMEOUT_ERROR: 
      Serial.println(F("Pas de reponse !")); 
      break;
   
    case DHT_CHECKSUM_ERROR: 
      Serial.println(F("Pb de communication !")); 
      break;
  }
  
  /* Pas plus d'une mesure par seconde */
  // N.B. Avec le DHT22 il est possible de réaliser deux mesures par seconde
  //delay(1000);
  //--- DHT22 end

  //--- DHT12 start
  unsigned long b = micros();
  dht::ReadStatus chk = DHT.read();
  unsigned long e = micros();

  //Serial.print(F("Read sensor: "));

  switch (chk)
  {
    case dht::OK:
      //Serial.print(F("OK, took "));
      //Serial.print (e - b); Serial.print(F(" usec, "));
      break;
    case dht::ERROR_CHECKSUM:
      Serial.println(F("Checksum error"));
      break;
    case dht::ERROR_TIMEOUT:
      Serial.println(F("Timeout error"));
      break;
    case dht::ERROR_CONNECT:
      Serial.println(F("Connect error"));
      break;
    case dht::ERROR_ACK_L:
      Serial.println(F("AckL error"));
      break;
    case dht::ERROR_ACK_H:
      Serial.println(F("AckH error"));
      break;
    default:
      Serial.println(F("Unknown error"));
      break;
  }

  Serial.print("DHT12   : ");
  Serial.print(F("Temperature : "));
  Serial.print((float)DHT.getTemperature()/(float)10);
  Serial.print(" *C , ");
  Serial.print(F("Humidite : "));
  Serial.print((float)DHT.getHumidity()/(float)10);
  Serial.println(F("%"));


  //Serial.print(F(", Dew Point (degrees C): "));
  //Serial.println(DHT.dewPoint());
  //--- DHT12 end

  //--- BME280 start
  printValues();
  //--- BME280 end

  //--- AM2320 start
  // reveil du device
  switch(sensor.Read()) {
    case 2:
      Serial.println("CRC failed");
      break;
    case 1:
      Serial.println("Sensor offline");
      break;
    case 0:
      Serial.print("AM2320  : ");
      Serial.print(F("Temperature : "));
      Serial.print(sensor.t);
      Serial.print(" *C , ");
      Serial.print(F("Humidite : "));
      Serial.print(sensor.h);
      Serial.println(F("%"));
      break;
  }

  //--- AM2320 end

  Serial.println("");

  delay(delayTime);

}
//-------------
void printValues() {
    Serial.print("BME280  : ");
    Serial.print("Temperature : ");
    Serial.print(bme.readTemperature());
    Serial.print(" *C , ");
    Serial.print("Humidite : ");
    Serial.print(bme.readHumidity());
    Serial.print(" % , ");

    Serial.print("Pressure : ");
    Serial.print(bme.readPressure() / 100.0F);
    Serial.print(" hPa , ");
    Serial.print("Approx. Altitude : ");
    Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
    Serial.println(" m");
}
//-------------
byte readDHT11(byte pin, float* temperature, float* humidity) {
  
  /* Lit le capteur */
  byte data[5];
  byte ret = readDHTxx(pin, data, 18, 1000);
  
  /* Détecte et retourne les erreurs de communication */
  if (ret != DHT_SUCCESS) 
    return ret;
    
  /* Calcul la vraie valeur de la température et de l'humidité */
  *humidity = data[0];
  *temperature = data[2];

  /* Ok */
  return DHT_SUCCESS;
}
//-------------
byte readDHT22(byte pin, float* temperature, float* humidity) {
  
  /* Lit le capteur */
  byte data[5];
  byte ret = readDHTxx(pin, data, 1, 1000);
  
  /* Détecte et retourne les erreurs de communication */
  if (ret != DHT_SUCCESS) 
    return ret;
    
  /* Calcul la vraie valeur de la température et de l'humidité */
  float fh = data[0];
  fh *= 256;
  fh += data[1];
  fh *= 0.1;
  *humidity = fh;
 
  float ft = data[2] & 0x7f;
  ft *= 256;
  ft += data[3];
  ft *= 0.1;
  if (data[2] & 0x80) {
    ft *= -1;
  }
  *temperature = ft;

  /* Ok */
  return DHT_SUCCESS;
}
/**
 * Fonction bas niveau permettant de lire la température et le taux d'humidité (en valeurs brutes) mesuré par un capteur DHTxx.
 */
byte readDHTxx(byte pin, byte* data, unsigned long start_time, unsigned long timeout) {
  data[0] = data[1] = data[2] = data[3] = data[4] = 0;
  // start_time est en millisecondes
  // timeout est en microsecondes
 
  /* Conversion du numéro de broche Arduino en ports / masque binaire "bas niveau" */
  uint8_t bit = digitalPinToBitMask(pin);
  uint8_t port = digitalPinToPort(pin);
  volatile uint8_t *ddr = portModeRegister(port);   // Registre MODE (INPUT / OUTPUT)
  volatile uint8_t *out = portOutputRegister(port); // Registre OUT (écriture)
  volatile uint8_t *in = portInputRegister(port);   // Registre IN (lecture)
  
  /* Conversion du temps de timeout en nombre de cycles processeur */
  unsigned long max_cycles = microsecondsToClockCycles(timeout);
 
  /* Evite les problèmes de pull-up */
  *out |= bit;  // PULLUP
  *ddr &= ~bit; // INPUT
  delay(100);   // Laisse le temps à la résistance de pullup de mettre la ligne de données à HIGH
 
  /* Réveil du capteur */
  *ddr |= bit;  // OUTPUT
  *out &= ~bit; // LOW
  delay(start_time); // Temps d'attente à LOW causant le réveil du capteur
  // N.B. Il est impossible d'utilise delayMicroseconds() ici car un délai
  // de plus de 16 millisecondes ne donne pas un timing assez précis.
  
  /* Portion de code critique - pas d'interruptions possibles */
  noInterrupts();
  
  /* Passage en écoute */
  *out |= bit;  // PULLUP
  delayMicroseconds(40);
  *ddr &= ~bit; // INPUT
 
  /* Attente de la réponse du capteur */
  timeout = 0;
  while(!(*in & bit)) { /* Attente d'un état LOW */
    if (++timeout == max_cycles) {
        interrupts();
        return DHT_TIMEOUT_ERROR;
      }
  }
    
  timeout = 0;
  while(*in & bit) { /* Attente d'un état HIGH */
    if (++timeout == max_cycles) {
        interrupts();
        return DHT_TIMEOUT_ERROR;
      }
  }

  /* Lecture des données du capteur (40 bits) */
  for (byte i = 0; i < 40; ++i) {
 
    /* Attente d'un état LOW */
    unsigned long cycles_low = 0;
    while(!(*in & bit)) {
      if (++cycles_low == max_cycles) {
        interrupts();
        return DHT_TIMEOUT_ERROR;
      }
    }

    /* Attente d'un état HIGH */
    unsigned long cycles_high = 0;
    while(*in & bit) {
      if (++cycles_high == max_cycles) {
        interrupts();
        return DHT_TIMEOUT_ERROR;
      }
    }
    
    /* Si le temps haut est supérieur au temps bas c'est un "1", sinon c'est un "0" */
    data[i / 8] <<= 1;
    if (cycles_high > cycles_low) {
      data[i / 8] |= 1;
    }
  }
  
  /* Fin de la portion de code critique */
  interrupts();
 
  /*
   * Format des données :
   * [1, 0] = humidité en %
   * [3, 2] = température en degrés Celsius
   * [4] = checksum (humidité + température)
   */
   
  /* Vérifie la checksum */
  byte checksum = (data[0] + data[1] + data[2] + data[3]) & 0xff;
  if (data[4] != checksum)
    return DHT_CHECKSUM_ERROR; /* Erreur de checksum */
  else
    return DHT_SUCCESS; /* Pas d'erreur */
}
Test 6 capteurs
Posted in arduino, Projet en cours, Technique.

Laisser un commentaire

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

Time limit is exhausted. Please reload CAPTCHA.