Références :
[[http://www.uugear.com/portfolio/dht11-humidity-temperature-sensor-module/]] \\
[[https://skyduino.wordpress.com/2013/04/27/tuto-capteur-temperature-humidite-dht11/]] \\
[[http://learning.grobotronics.com/2013/07/measuring-humidity-with-dht11-sensor/]] \\
Ce module, très répandu sur le commerce, contient :
- un capteur de température et d'humidité.
- a high-performance 8-bit microcontroller
Ca ressemble à ça :
^ Frontside | Backside |
^ {{::dht11_1.jpg?direct&300*200 |}} | {{::dht11_2.jpg?direct&300*200 |}} |
-----------------------------------------------------------------------------------------------------------------
Coté frontsize, de fauche à droite, S=Signal (in et out, voir la suite) NC V=5V ou 3,3V (alimentation) G=Ground=0V
Coté backside, la broche G=Ground, V=5V ou 3,3V (alimentation) et S=Signal (in et out, voir la suite)
Pour l'utiliser, voici le schéma de montage :
^ Théorique | Pratique |
^{{:dth11_schema_1.jpg?direct&300 |}} | {{:dth11_schema_2.gif?direct&300 |}} |
__**Le protocole de communication :**__
Le DHT11 a la particularité d’utiliser une seule broche pour communiquer avec son maitre.
Bien sûr cela aurait trop facile d’utiliser un bus 1-Wire classique …
La communication se fait donc au moyen d’un protocole propriétaire demandant des timings bien précis.
Le datasheet du capteur étant en chinois (et uniquement en chinois) j’ai un peu galéré pour comprendre le truc …
Mais j’ai réussi à trouver une version – plus ou moins bien – traduite en anglais qui m’a permis de comprendre comment marche le capteur :
http://www.micro4you.com/files/sensor/DHT11.pdf
Une trame « type » se compose de trois grandes parties :
- Le maitre réveille le capteur au moyen d’un signal de « start ».
- Le capteur sort de son sommeil et envoie une réponse pour dire qu’il est vivant.
- Le capteur envoie 40 bits de données (5 octets) contenant la température (2octets), le taux d’humidité (2octets) et une checksum (1octet).
{{::DHT11_Graph_1.jpg?direct&700*150 |}}
--------------------------------------------------------------------------------------------------------------
Ce qu’il faut noter c’est que le capteur a une sortie dite à « collecteur ouvert », cela signifie que le capteur ne sort pas des niveaux logiques 0v/5v mais utilise un transistor en sortie :
- Si le capteur veut envoyer un « 0 » il force le signal à 0v en rendant son transistor (entre DATA et GND) passant.
- Si le capteur veut envoyer un « 1 » il laisse le signal tel-quel en rendant son transistor bloquant.
{{::DHT11_Graph_2.jpg?direct&700*150 |}}
--------------------------------------------------------------------------------------------------------------
Le DHT11 est une grosse féniasse, il passe sont temps à dormir:)
Si vous voulez qu’il vous envoie ses données il faut le secouer en lui envoyant un verre d’eau dans la gueule un signal de start.
Le signal de start consiste en un état bas (0v) de 18 millisecondes et d’un état haut (5v) de 40 microsecondes.
{{::DHT11_CommunicationProtocol_1.jpg?direct&700*150 |}}
--------------------------------------------------------------------------------------------------------------
Un paquet de données complet fait 40 bits, soit 5 octets. Ce paquet se décompose comme suit :
^ Octet | Contenu |
^ 1 | Humidité en % |
^ 2 | zéro |
^ 3 | Température en degré Celsius |
^ 4 | Zéro |
^ 5 | Checksum (= humidité + température) |
Certains se demanderont pourquoi les octets 2 et 4 sont à zéros.
C’est simple, le capteur n’a qu’une précision de 8 bits, la partie décimale est donc toujours vide😉
__**Comment différencier un bit « 1 » d’un bit « 0 » ?**__
^ {{::DHT11_Graph_3.jpg?direct&400 |}} | {{::DHT11_Graph_4.jpg?direct&400 |}} |
--------------------------------------------------------------------------------------------------------------
^ Bit à coder | Temps bas ( us) | Temps haut ( us) |
^ 0 | 50 | 28 |
^ 1 | 50 | 70 |
-------------------------------------------------------------------------------------------------------------
__**Code C pour un RBPI**__
-------------------------------------------------------------------------------------------------------------
/*
* dht11.c:
* Simple test program to test the wiringPi functions
* DHT11 test
*/
#include
#include
#include
#include
#define MAXTIMINGS 85
#define DHTPIN 7
int dht11_dat[5] = { 0, 0, 0, 0, 0 };
void read_dht11_dat()
{
uint8_t laststate = HIGH;
uint8_t counter = 0;
uint8_t j = 0, i;
float f; /* fahrenheit */
dht11_dat[0] = dht11_dat[1] = dht11_dat[2] = dht11_dat[3] = dht11_dat[4] = 0;
/* pull pin down for 18 milliseconds */
pinMode( DHTPIN, OUTPUT );
digitalWrite( DHTPIN, LOW );
delay( 18 );
/* then pull it up for 40 microseconds */
digitalWrite( DHTPIN, HIGH );
delayMicroseconds( 40 );
/* prepare to read the pin */
pinMode( DHTPIN, INPUT );
/* detect change and read data */
for ( i = 0; i < MAXTIMINGS; i++ )
{
counter = 0;
while ( digitalRead( DHTPIN ) == laststate )
{
counter++;
delayMicroseconds( 1 );
if ( counter == 255 )
{
break;
}
}
laststate = digitalRead( DHTPIN );
if ( counter == 255 )
break;
/* ignore first 3 transitions */
if ( (i >= 4) && (i % 2 == 0) )
{
/* shove each bit into the storage bytes */
dht11_dat[j / 8] <<= 1;
if ( counter > 16 )
dht11_dat[j / 8] |= 1;
j++;
}
}
/*
* check we read 40 bits (8bit x 5 ) + verify checksum in the last byte
* print it out if data is good
*/
if ( (j >= 40) &&
(dht11_dat[4] == ( (dht11_dat[0] + dht11_dat[1] + dht11_dat[2] + dht11_dat[3]) & 0xFF) ) )
{
f = dht11_dat[2] * 9. / 5. + 32;
printf( "Humidity = %d.%d %% Temperature = %d.%d *C (%.1f *F)\n",
dht11_dat[0], dht11_dat[1], dht11_dat[2], dht11_dat[3], f );
}else {
printf( "Data not good, skip\n" );
}
}
int main( void )
{
printf( "Raspberry Pi wiringPi DHT11 Temperature test program\n" );
if ( wiringPiSetup() == -1 )
exit( 1 );
while ( 1 )
{
read_dht11_dat();
delay( 1000 ); /* wait 1sec to refresh */
}
return(0);
}
-------------------------------------------------------------------------------------------------------------
__**Code PYTHON pour un RBPI**__
-------------------------------------------------------------------------------------------------------------
import RPi.GPIO as GPIO
import time
def bin2dec(string_num):
return str(int(string_num, 2))
data = []
GPIO.setmode(GPIO.BCM)
GPIO.setup(4,GPIO.OUT)
GPIO.output(4,GPIO.HIGH)
time.sleep(0.025)
GPIO.output(4,GPIO.LOW)
time.sleep(0.02)
GPIO.setup(4, GPIO.IN, pull_up_down=GPIO.PUD_UP)
for i in range(0,500):
data.append(GPIO.input(4))
bit_count = 0
tmp = 0
count = 0
HumidityBit = ""
TemperatureBit = ""
crc = ""
try:
while data[count] == 1:
tmp = 1
count = count + 1
for i in range(0, 32):
bit_count = 0
while data[count] == 0:
tmp = 1
count = count + 1
while data[count] == 1:
bit_count = bit_count + 1
count = count + 1
if bit_count > 3:
if i>=0 and i<8:
HumidityBit = HumidityBit + "1"
if i>=16 and i<24:
TemperatureBit = TemperatureBit + "1"
else:
if i>=0 and i<8:
HumidityBit = HumidityBit + "0"
if i>=16 and i<24:
TemperatureBit = TemperatureBit + "0"
except:
print "ERR_RANGE"
exit(0)
try:
for i in range(0, 8):
bit_count = 0
while data[count] == 0:
tmp = 1
count = count + 1
while data[count] == 1:
bit_count = bit_count + 1
count = count + 1
if bit_count > 3:
crc = crc + "1"
else:
crc = crc + "0"
except:
print "ERR_RANGE"
exit(0)
Humidity = bin2dec(HumidityBit)
Temperature = bin2dec(TemperatureBit)
if int(Humidity) + int(Temperature) - int(bin2dec(crc)) == 0:
print "Humidity:"+ Humidity +"%"
print "Temperature:"+ Temperature +"C"
else:
print "ERR_CRC"
-------------------------------------------------------------------------------------------------------------
__**Code pour un ARDUINO**__
-------------------------------------------------------------------------------------------------------------
/** Broche "DATA" du DHT11 */
const byte DHT11_PIN = 5;
/** Code d'erreur de la fonction readDHT11() */
enum {
DHT11_SUCCESS, //!< Pas d'erreur
DHT11_TIMEOUT_ERROR, //!< Temps d'attente dépassé
DHT11_CHECKSUM_ERROR //!< Données reçues erronées
};
/** Fonction setup() */
void setup() {
/* Initialisation du port série */
Serial.begin(115200);
Serial.println(F("DHT11 DEMO"));
}
/** Fonction loop() */
void loop() {
/* Variables d'usage */
byte temperature, humidity;
/* Lecture de la température et de l'humidité + gestion des erreurs */
switch (readDHT11(DHT11_PIN, &temperature, &humidity)) {
case DHT11_SUCCESS:
/* Affichage de la température et du taux d'humidité */
Serial.print(F("Humidite (%): "));
Serial.println((int) humidity);
Serial.print(F("Temperature (^C): "));
Serial.println((int) temperature);
break;
case DHT11_TIMEOUT_ERROR:
Serial.println(F("Temps d'attente depasse !"));
break;
case DHT11_CHECKSUM_ERROR:
Serial.println(F("Erreur de checksum !"));
break;
}
/* Pas besoin de rafraichir l'affichage très souvent */
delay(2000);
}
/**
* Lit la température et le taux d'humidité capté par un capteur DHT11
*
* @param pin Broche sur laquelle est câblé le capteur
* @param temperature Pointeur vers la variable stockant la température
* @param humidity Pointeur vers la variable stockant le taux d'humidité
* @return DHT11_SUCCESS si aucune erreur, DHT11_TIMEOUT_ERROR en cas de timeout, ou DHT11_CHECKSUM_ERROR en cas d'erreur de checksum
*/
byte readDHT11(byte pin, byte* temperature, byte* humidity) {
/* data[] -> buffer contenant les données du cpateur
* counter -> compteur permettant de savoir quel bit est reçu (bitwise)
* index -> compteur permettant de savoir quel octet est reçu (bitwise)
* timeout -> compteur pour le timeout
*/
byte data[5] = { 0 }, counter = 7, index = 0;
unsigned int timeout;
/* Conversion du numéro de broche Arduino en ports/masque binaire "bas niveau" */
/* Utiliser les registres du microcontrôleur est bien plus rapide que digitalWrite() */
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)
/* Réveil du capteur */
*ddr |= bit; // OUTPUT
*out &= ~bit; // LOW
delay(18); // Temps d'attente à LOW causant le réveil du capteur
*out |= bit; // HIGH
delayMicroseconds(40);
*ddr &= ~bit; // INPUT
/* Attente de la réponse du capteur */
timeout = 0;
while(!(*in & bit)) /* Attente d'un état LOW */
if (++timeout == 10000)
return DHT11_TIMEOUT_ERROR;
timeout = 0;
while(*in & bit) /* Attente d'un état HIGH */
if (++timeout == 10000)
return DHT11_TIMEOUT_ERROR;
/* Lecture des données du capteur (40 bits) */
for (byte i = 0; i < 40; ++i) {
/* Attente d'un état LOW */
timeout = 0;
while(!(*in & bit))
if (++timeout == 10000)
return DHT11_TIMEOUT_ERROR;
/* Mise en mémoire du temps courant */
unsigned long t = micros();
/* Attente d'un état HIGH */
timeout = 0;
while(*in & bit)
if (++timeout == 10000)
return DHT11_TIMEOUT_ERROR;
/* Si le delta Temps est supérieur à 40µS c'est un "1", sinon c'est un "0" */
if ((micros() - t) > 40)
data[index] |= (1 << counter); // "1"
// Le tableau data[] est initialisé à "0" par défaut😉
/* Si le compteur de bits atteint zéro */
if (counter-- == 0) {
counter = 7; /* On passe à l'octet suivant */
++index;
}
}
/* Format des données :
* [0] = humidité en %
* [1] = zéro
* [2] = température en degrés Celsius
* [3] = zéro
* [4] = checksum (humidité + température)
*/
*humidity = data[0];
*temperature = data[2];
/* Vérifie la checksum */
if (data[4] != (data[0] + data[2]))
return DHT11_CHECKSUM_ERROR; /* Erreur de checksum */
else
return DHT11_SUCCESS; /* Pas d'erreur */
}
-------------------------------------------------------------------------------------------------------------
__**Code pour un ARDUINO en utilisant une librairie déjà prète à l'emploi**__
-------------------------------------------------------------------------------------------------------------
#include
dht11 DHT11;
#define DHT11PIN 2
void setup()
{
Serial.begin(9600);
Serial.println("DHT11 TEST PROGRAM ");
Serial.print("LIBRARY VERSION: ");
Serial.println(DHT11LIB_VERSION);
Serial.println();
}
double Fahrenheit(double celsius)
{
return 1.8 * celsius + 32;
}
void loop()
{
Serial.println("\n");
int chk = DHT11.read(DHT11PIN);
Serial.print("Read sensor: ");
switch (chk)
{
case 0: Serial.println("OK"); break;
case -1: Serial.println("Checksum error"); break;
case -2: Serial.println("Time out error"); break;
default: Serial.println("Unknown error"); break;
}
Serial.print("Humidity (%): ");
Serial.println((float)DHT11.humidity, 2);
Serial.print("Temperature (oC): ");
Serial.println((float)DHT11.temperature, 2);
Serial.print("Temperature (oF): ");
Serial.println(Fahrenheit(DHT11.temperature), 2);
delay(2000);
}