Table des matiéres
Do not index
Do not index
Hide CTA
Hide CTA
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 :
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.
Protocole
Voici la description schématique du protocole de communication dans sa globalité
Pour une vue plus détaillé voici la partie permettant l’amorçage de la communication avec le DHT
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
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 :
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;
}