Come leggere i dati dal sensore DHT11 senza usare una libreria
Il sensore di temperatura e umidità DHT11 è uno tra i più popolari nei progetti Arduino quando è necessario acquisire un valore di temperatura e di umidità. Solitamente per leggere i dati da questo sensore viene utilizzata una libreria che permette con poche linee di codice di ottenere il dato di temperatura e dell’umidità.
Esistono numerosi tutorial che mostrano come impiegare queste librerie, in questo articolo cercheremo di capire invece, come leggere i dati del sensore DHT11 utilizzando direttamente le funzioni di Arduino UNO.
Nello schema seguente potete osservare i collegamenti che ho effettuato tra la UNO e il sensore DHT11
Come si può notare i dati vengono ricevuti e trasmessi impiegando un solo filo.
Per prima cosa controlliamo nel datasheet del DHT11 come avviene lo scambio dei dati tra il microcontrollore e il sensore.
Sapendo che i dati transitano su un singolo filo dobbiamo utilizzare una porta digitale della UNO impostandola primariamente come uscita, per inviare la richiesta di acquisizione dati al sensore, secondariamente come ingresso, per ricevere i dati provenienti dal sensore.
Leggendo il datasheet notiamo che per ricevere dati dal sensore DHT11 è necessario inviargli un segnale digitale rispettando determinati tempi tra un livello logico basso ed uno alto; analizziamo nel dettaglio come sono composti questi segnali.
Una volta alimentato il sensore DHT11, questo si pone, dopo circa 1 secondo, in attesa di ricevere un comando. Avendo inserito una resistenza di pull-up, sulla linea dati sarà presente un segnale logico alto.
Per avviare la trasmissione dei dati dobbiamo mettere (tramite la porta della scheda UNO) la linea dati al livello logico basso per almeno 18 ms, e successivamente metterla nuovamente a livello logico alto, come visibile nel diagramma seguente:
In questa fase dobbiamo configurare la porta digitale come un ingresso. Infatti il sensore risponderà mettendo la linea dati a livello logico basso per 80 microsecondi per poi tenerla a livello alto per altri 80 microsecondi:
ora il sensore invierà al microcontrollore 40 bit di dati composti in questo modo, il bit a livello basso sarà composto da un livello logico basso di 50 us seguito da un livello logico alto di circa 27 us, mentre un bit a livello alto sarà composto da un segnale di livello basso per 50 us seguito da un livello logico alto per 70 us.
Quindi riepilogando avremmo una sequenza di trasmissione di questo tipo:
I 40 bit trasmessi sono suddivisi in questo modo:
I primi 8 bit contengono il valore intero del dato di umidità, i seguenti 8 bit contengono il valore decimale del dato di umidità, i successivi 8 bit contengono il valore intero del dato di temperatura, i seguenti 8 bit contengono il valore decimale del dato di temperatura. Gli ultimi 8 bit contengono il valore di CRC.
Il codice seguente permette di acquisire il dato di umidità e di temperatura. I commenti nel codice spiegano in dettaglio le istruzioni utilizzate:
int pin = 2; unsigned long duration; unsigned long tmr = 0; unsigned long tmr_prec = 0; byte impulsi; int message[42]; byte Htemperatura; byte Ltemperatura; byte Humidita; byte Lumidita; byte checksum; void setup() { //init seriale Serial.begin(115200); //definisco la porta 2 come uscita //in questa porta è collegato il sensore dht11 pinMode(pin, OUTPUT); } void loop() { delay(1000); //definisco la porta 2 come uscita //in questa porta è collegato il sensore dht11 pinMode(pin, OUTPUT); //sappiamo che sul pin c'è un livello logico alto //avvendo inserito la resistenza di pullup //metto la linea alivello basso per 20 millisecondi (>18ms) digitalWrite(pin, LOW); delay(20); //metto nuovamente la linea a livello logico alto digitalWrite(pin, HIGH); //imposto la lestta porta come ingresso //in modo da ricevere i dati dal sensore dht11 pinMode(pin, INPUT); delayMicroseconds(10); //inizializzo la variabile a 0, questa variabile consente di tenere //traccia del numero di bit ricevuti impulsi = 0; //eseguo un ciclo continuo while (true) { //controllo solo quando è presente un livello alto if (digitalRead(2) == HIGH) { //memorizzo il tempo in microsecondi tmr_prec = micros(); //eseguo un ciclo fintanto che il livello è alto while (digitalRead(2) == HIGH) { ; } //quando il livello logico diventa basso //misuro quanto tempo è durato il livello logico alto duration = micros() - tmr_prec; //memorizzo questo tempo nell'array message[impulsi] = duration; //incremento la variabile per tenere traccia dei bit inviati //dal sensore dht11 impulsi++; } //quando ho raggiunto 41 bit interrompo il ciclo while //i bit sono 41, invece che 40, perchè questo codice //misura anche il primo livello alto di 80us che indica l'inizio //della trasmissione deii 40 bit di dati if (impulsi == 41) break; } //ora che ho un array contenente i microsecondi di tutti i livelli alti che sono stati inviati //dal sensore dht11 devo convertirli in bit digitali //trasformo i millisecondi in bit //verifico che il primo dato sia di circa 80 us if ( message[0] > 75 && message[0] < 95) { //bit di start ok //elaboro i primo 8 bit e converto, in funzione della larghezza del //livello alto, se è un bit di valore 1 o di valore 0 for (byte ciclo = 0; ciclo < 8; ciclo++) { if (message[ciclo + 1] > 15 && message[ciclo + 1] < 35) bitClear(Humidita, 7 - ciclo); if (message[ciclo + 1] > 65 && message[ciclo + 1] < 80) bitSet(Humidita, 7 - ciclo); } //continua coni successivi 8 bit for (byte ciclo = 0; ciclo < 8; ciclo++) { if (message[ciclo + 9] > 15 && message[ciclo + 9] < 35) bitClear(Lumidita, 7 - ciclo); if (message[ciclo + 9] > 65 && message[ciclo + 9] < 80) bitSet(Lumidita, 7 - ciclo); } //continua coni successivi 8 bit for (byte ciclo = 0; ciclo < 8; ciclo++) { if (message[ciclo + 17] > 15 && message[ciclo + 17] < 30) bitClear(Htemperatura, 7 - ciclo); if (message[ciclo + 17] > 65 && message[ciclo + 17] < 80) bitSet(Htemperatura, 7 - ciclo); } //continua coni successivi 8 bit for (byte ciclo = 0; ciclo < 8; ciclo++) { if (message[ciclo + 25] > 15 && message[ciclo + 25] < 30) bitClear(Ltemperatura, 7 - ciclo); if (message[ciclo + 25] > 65 && message[ciclo + 25] < 80) bitSet(Ltemperatura, 7 - ciclo); } //continua coni successivi 8 bit for (byte ciclo = 0; ciclo < 8; ciclo++) { if (message[ciclo + 33] > 15 && message[ciclo + 33] < 30) bitClear(checksum, 7 - ciclo); if (message[ciclo + 33] > 65 && message[ciclo + 33] < 80) bitSet(checksum, 7 - ciclo); } } //visualizzo i dati grezzi for (byte ciclo = 0; ciclo < 41; ciclo++) { Serial.print(message[ciclo]); Serial.print(" "); } //visualizzo i dati elaborati Serial.println(""); Serial.print("Umidita "); Serial.print(Humidita); Serial.print("."); Serial.print(Lumidita); Serial.print(" temperatura "); Serial.print(Htemperatura); Serial.print("."); Serial.print(Ltemperatura); Serial.print(" CheckSUM"); Serial.println(checksum); }
Questo semplice codice ha permesso di capire come acquisiere ed elaborare i dati inviati dal sensore dht11.