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

Arduino UNO collegamenti 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:

invio sequenza di start al dht11

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:

dht11 start sequence

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.

dht11 send data

Quindi riepilogando avremmo una sequenza di trasmissione di questo tipo:

dht11 signal diagram

I 40 bit trasmessi sono suddivisi in questo modo:

DHT11 Struttura dati

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.