DHT11 : collecter la température et l’humidité de l’air
Do not index
Do not index
Hide CTA
Hide CTA
Video preview

Une surprenante trouvaille

J’étais parti pour vous faire le comparatif de 4 sondes de température trouvées dans ma boite de module électronique quand je tombe sur le module KY-015 qui est basé sur le très connu DHT11. Après avoir compris que ce composant n’est pas tout à fait comme les autres, car il embarque son propre contrôleur et à qui il faut s’adresser de manière très spécifique, me voilà immédiatement animé d’un but : je vais pouvoir expliquer à mon audience les bases d’un protocole de communication grâce à ce composant.

Description du composant DHT11

Le DHT11 est composé d’un détecteur d’humidité de type résistif, et d’un capteur de température analogique de type NTC. Il embarque un microcontrôleur 8-bit qui offre quelque fonctionnalités lui permettant d’être stable et résistant aux interférences. La fiche technique lui vante, en tout cas, un rapport qualité prix imbattable. Mais comme d’habitude faites toujours attention à sa provenance et à qui vous achetez le composant.
Vous pouvez attendre de ce composant une précision relative de plus ou moins 5% pour ce qui est de son capteur d’humidité et pour la température de plus ou moins 2 degrés. Ce qui, comparé à un capteur de température digital comme le DS18B20, est environ 4 fois moins bien.
Que l’on soit sur le capteur d’humidité ou de température, la résolution de lecture du DHT11 est de 1 degré pour la température et de 1% pour l’humidité. Pour un capteur qui irait dans une résolution plus fine, il faudrait alors s’orienter vers un DHT22.

Caractéristiques techniques

Tension de fonctionnement
3.3 V à 5.5V
Fenêtre de mesure de l’humidité
20% à 90% HR
Précision de la mesure de l’humidité
±5%
Résolution de la mesure de l’humidité
1%
Fenêtre de mesure de la température
0ºC to 50ºC
Précision de la mesure de la température
±2ºC
Résolution de la mesure de la température
1ºC
Distance de transmission maximale
20m
Voici le module KY-015 présenté dans la vidéo :
notion image

Montage

Pour le montage, selon si vous avez le module KY-015 suivez alors le montage de gauche. En revanche si vous avez le DHT11/DHT22 seul, dans ce cas suivez le montage de droite. Remarquez que même si le condensateur n’est pas présent sur le montage de gauche, vous pouvez toujours le rajoutez si vous avez de nombreuses erreurs de lecture dû au bruit électrique généré par votre alimentation.
notion image

Protocole

Voici la description schématique du protocole de communication dans sa globalité
notion image
Pour une vue plus détaillé voici la partie permettant l’amorçage de la communication avec le DHT
notion image
Le schéma suivant décrit la manière dont sont transmis les bits et la valeur qu’ils ont selon la durée du signal
notion image

Structure de données

Au total, le DHT envoie 5 octets à chaque fois qu’on le sollicite pour connaitre l’humidité relative et la température.
  • 1 octet pour la valeur entière de l’humidité
  • 1 octet pour la valeur décimale de l’humidité
  • 1 octet pour la valeur entière de la température
  • 1 octet pour la valeur décimale de la température
  • 1 octet final qui sert de somme de contrôle.
Voici un schéma résumant la structure de données :
notion image

Une approche compréhensive

Dans la première partie de la vidéo nous étudions le protocole de communication du composant et le code source qui résulte de la première partie nous permet de mettre en évidence l’amorçage de la communication avec le DHT11. Le code décrit dans cette partie ne permet pas de lire l’humidité ni la température car nous ne mettons en avant que la partie consistant à initialiser une communication avec le DHT.
#define PIN_DHT 7
unsigned long init_chronos = 0;
unsigned long chronos_step_1 = 0;
unsigned long chronos_step_2 = 0;

int elapsed_step_1 = 0;
int elapsed_step_2 = 0;

void wakeUpDHT();

void setup() {
  //Amorce de la communication série
  Serial.begin(9600);

  //Paramétrage initale du port en mode INPUT
  pinMode(PIN_DHT, OUTPUT);

  //On met le port à haut pour une durée de sécurité
  digitalWrite(PIN_DHT, HIGH);
  delay(1000);

  //On appelle notre fonction de test
  wakeUpDHT();
}

void loop() {

}

void wakeUpDHT(){
  //On coupe le courant sur le port de données (STEP 00)
  digitalWrite(PIN_DHT, LOW);

  //On attend une periode de plus de 18 ms
  delay(20);

  //On repasse à l'état haut (STEP 01)
  digitalWrite(PIN_DHT, HIGH);

  //On attend 30µs
  delayMicroseconds(30);

  //On change le mode du port (STEP 02)
  pinMode(PIN_DHT, INPUT);
  
  //Boucle qui attend que se soit le DHT qui change la tension à LOW (BOUCLE 01)
  while(digitalRead(PIN_DHT) == HIGH);

  //On lance le chronometre #CHRONOS_00 (Attention, sur une UNO, micros() est précis a 4 microseconde)
  init_chronos = micros();

  //Boucle qui attend que se soit le DHT qui change la tension à HIGH (BOUCLE 02)
  //Devrai durer environ 80µs
  while(digitalRead(PIN_DHT) == LOW);

  //On collecte le premier relevé du chonometre #CHRONOS_01
  chronos_step_1 = micros();

  //Boucle qui attend que se soit le DHT qui change la tension à LOW (BOUCLE 03)
  //Devrai durer environ 80µs
  while(digitalRead(PIN_DHT) == HIGH);

  //On collecte le deuxieme relevé du chonometre #CHRONOS_02
  chronos_step_2 = micros();
  
  //A ce stade le DHT va commencer à envoyer de la data.
  //Pour cette premiere demonstration on ignore la data mais on affiche les valeurs de chronometre
  elapsed_step_1 = chronos_step_1 - init_chronos;
  elapsed_step_2 = chronos_step_2 - chronos_step_1;

  Serial.println(elapsed_step_1);
  Serial.println(elapsed_step_2);
}

La version définitive

Dans la seconde partie de la vidéo, nous réutilisons le code source écrit lors de la première approche, on le nettoie des parties résultantes de la pédagogie de la première moitié et on étoffe le code jusqu’à obtenir le résultat définitif.
#define PIN_DHT 7

//Variable qui contiendra les 5 octets recu du DHT
byte data_DHT[5];

//Variable qui contiendra les messages a afficher de l'humidité et de la température
char message[42];

//Déclaration des signatures des fonctions que nous utilisons
void wakeUpDHT();
void readDataFromDHT();
byte readSingleByte();
bool checkDataIntegrity();

void setup() {
  //Amorce de la communication série
  Serial.begin(9600);

  //Paramétrage initale du port en mode INPUT
  pinMode(PIN_DHT, OUTPUT);

  //On met le port à haut pour une durée initiale de sécurité
  digitalWrite(PIN_DHT, HIGH);
  delay(1000);
}

void loop() {
  //On reveil le DHT
  wakeUpDHT();

  //Reveillé, on lit les 5 octets qu'il va nous envoyer
  readDataFromDHT();

  //On affiche les valeurs recu sur le moniteur série
  if(checkDataIntegrity()){
    sprintf(message, "Humidité : %u.%u%% - Température : %u.%u°C", data_DHT[0], data_DHT[1], data_DHT[2], data_DHT[3]);
    Serial.println(message);
    
  }
  else{
    Serial.println("read error");
  }

  //On attend 1 seconde avant de relire
  delay(1000);
}

void wakeUpDHT(){
  //On coupe le courant sur la port de données
  digitalWrite(PIN_DHT, LOW);

  //On attend une periode de plus de 18 ms
  delay(20);

  //On repasse à l'état haut (STEP 01)
  digitalWrite(PIN_DHT, HIGH);

  //On attend 30µs
  delayMicroseconds(30);

  //On change le mode du port (STEP 02)
  pinMode(PIN_DHT, INPUT);
  
  //Boucle qui attend que se soit le DHT qui change la tension à LOW (BOUCLE 01)
  while(digitalRead(PIN_DHT) == HIGH);

  //Boucle qui attend que se soit le DHT qui change la tension à HIGH (BOUCLE 02) (environ 80µs)
  while(digitalRead(PIN_DHT) == LOW);

  //Boucle qui attend que se soit le DHT qui change la tension à LOW (BOUCLE 03) (environ 80µs)
  while(digitalRead(PIN_DHT) == HIGH);
}

void readDataFromDHT(){
  //On amorce la lecture de 5 octets que l'on va recevoir du DHT
  for(byte i = 0; i < 5; i++){
    data_DHT[i] = readSingleByte();
  }
  //Une fois la lecture des 5 octets terminé on se remet dans l'etat pour la prochaine lecture
  pinMode(PIN_DHT, OUTPUT);

  //En remetant le port à l'etat haut, on force DHT à se remetre à dormir.
  digitalWrite(PIN_DHT, HIGH);
}

byte readSingleByte(){
  //Variable qui va stocker l'octet en cours de lecture
  byte byte_read = 0;
  
  //Pour former un octet entier on va avoir besoin de lire 8bits consécutifs
  for (byte i = 0; i < 8; i++) {
    //Boucle qui attend que le DHT change la tension à HIGH (BOUCLE 01)
    while(digitalRead(PIN_DHT) == LOW);
    
    //Attente de 40µs
    delayMicroseconds(40);

    //Si apres 40µs le signal est toujours à HIGH on peux en conclure que le bit que l'on vient
    //de recevoir est un 1, dans le cas contraire, c'etait un 0 et on a juste à ignorer ce tour
    //de boucle
    if(digitalRead(PIN_DHT) == HIGH){
      //on utilise 2 opérateurs binaire pour enregistrer le résultat dans l'octet en cours
      byte_read |= (1 << (7 - i));
      //si le signal est a HIGH, nous devons attendre la fin pour passer au prochain tour de boucle
      while(digitalRead(PIN_DHT) == HIGH);
    }
  }
  //L'octet entierement lu, nous renvoyons le résultat
  return byte_read;
}

bool checkDataIntegrity(){
  //Si la donnée n'est pas corrompu, data_DHT[4] est censé etre la somme du reste du tableau
  if(data_DHT[4] - data_DHT[3] - data_DHT[2] - data_DHT[1] - data_DHT[0] == 0){
    return true;
  }
  return false;
}
 
Cyril

Written by

Cyril

Hobbyiste en électronique, programmation et IA