Sensor de temperatura y humedad DHT11

Este es un sensor de temperatura y humedad muy popular dentro de los sensores de bajo costo y de fácil utilización, lo van a encontrar en proyectos con Arduino en un montón de sitios y también con otros microcontroladores.
En este caso lo estaré realizando en base al PIC, ya saben que los vengo usando desde hace mucho aunque también utilizo otras marcas, pero aqui en Argentina los PIC se consiguen casi en cualquier lado y a precios accesibles.
El sensor DHT11 es un sensor que nos proporciona temperatura y humedad relativa, en este sensor vamos a encontrar un sensor de temperatura del tipo resistivo NTC y un sensor de humedad del tipo resistivo similar al EMD4000, es decir, tanto para el sensor de humedad como el de temperatura requieren de una electrónica analógica para acondicionar la señal y luego una electrónica microcontrolada para leer los valores e integrarlos a la trama de datos serial, pero esto ya esta resuelto dentro del DHT11 y solo tendremos que conectarlo a un pin del microcontrolador mediante un pull-up.



Parametros electricos del DHT11
Es un sensor que posee solo 1% de resolución para la humedad y la temperatura, es decir, tendremos solo valores enteros, pero el protocolo que nos provee el DHT11 es enviando el valor entero de las unidades y decenas (0 a 255) y el valor de los decimales (0 a 255), tendremos entonces 2 byte para dada valor (para el de humedad y para el de temperatura), quedando así 4 bytes de datos y un quinto byte mas que sera de Checksum (se un proceso dato que se envía para comprobar la existencia de errores en la comunicación (sin entrar en muchos detalles)), este checksum sera de 1 byte, conformando así una trama completa de 5 bytes de datos que detallaremos mas adelante.

Este sensor nos proporciona una humedad que va entre el 20 y el 90 % de humedad relativa con una presicion de +/-5%, luego el sensor de temperatura va entre 0 y 50 °C con una precision de +/-2%, esta bien, no son los mejores valores ni los mejores rangos, pero este sensor esta pensado para el uso domestico donde podemos utilizarlo sin problemas y a un bajo costo, para valores mas amplios o mayor exactitud debemos buscar otro tipo de sensores como los de Bosch.

El sensor posee 4 pines de conexión aunque solo utilizaremos 3 pines, dos de alimentación y uno de datos, es un sensor que utiliza un protocolo de datos de 1 hilo, o One-Wire.
La tensión de alimentación va 3 a 5.5V lo que nos ayuda a usarlo en casi cualquier microcontrolador ya sea de 3V3 (500uA) o de 5V (2.5mA).
Los tiempo de propagación del mismo son algo lentos, es decir actualiza los datos cada unos 10s lo cual es lento, pero recordemos que los sensores atmosféricos no requieren de una alta velocidad de muestreo ya que los parámetros atmosféricos no suelen variar con tanta velocidad.

El cableado del sensor debe ser de un máximo de 20m según datasheet, para que no se pierdan datos y cada envió de datos por el puerto se realizara cada 4ms.

Protocolo de datos
El protocolo como mencionamos antes es de un solo hilo, es un protocolo sencillo pero al tener el clock junto con el dato requiere de algún trato extra que podría complicar un poco el proceso aunque es sencillo.

Como mencionamos antes serán 5 bytes de datos o 40 bits, estos bytes se componen por 1 byte de unidad/decena de temperatura, 1 byte de decimales de temperatura, 1 byte de unidad/decena de humedad, 1 byte de decimales de humedad y por ultimo 1 byte de checksum.
En este caso se conforma de esta manera:
8bit integral RH data + 8bit decimal RH data + 8bit integral T data + 8bit decimal T data + 8bit checksum.
De esta manera es como nos enviara los datos el sensor DHT11.
El checksum como mencionamos antes es un byte (en este caso es un byte) que lo que hace es sumar los valores de los demás bytes enviados, es decir, suma los valores de "8bit integral RH data + 8bit decimal RH data + 8bit integral T data + 8bit decimal T data", pero si sumamos 4 números de 8bits estaremos en un numero de 255+255+255+255 que nos dará 1024 y nos pasamos de los 8bit del checksum, entonces lo que hace es tomar los últimos 8bit de la suma y eso es lo que controla.

Ejemplo de Checksum.
el sensor nos envía 30.90° y 70.90%, esto si lo pasamos a bytes sera:
30, 90, 70, 90, que en binario sera: 00011110, 01011010, 01000110, 01011010, esto corresponde a los 4 bytes que envía el sensor DHT11, ahora si sumamos estos valores sera, 30+90+70+90 = 280, en este caso 280 es mayor a 255 por lo que nos pasamos de los 8 bits por ello solo tomaremos los ultimos 8 bits del valor, en este caso 280 es 100011000 como podremos ver son 9 bits y el DHT11 lo que nos enviara son los últimos 8 bits que seran 00011000 correspondiente al valor 24, entonces en este caso el DHT11 nos enviara: 00011110, 01011010, 01000110, 01011010, 00011000. Luego en nuestro programa debemos realizar el mismo proceso, recibimos los 4 bytes, los sumamos entre si y ese valor si es mayor a 255 lo movemos 1 vez a la izquierda, si es mayor a 511 lo moveremos dos veces para que siempre nos muestre el valor y lo compararemos contra el ultimo byte enviado por DHT11 si estos son iguales (osea el valor sumado por nosotros es igual al checksum que ha enviado el sensor) entonces el dato es correcto y podremos mostrarlo.

Por otro lado la trama es de un solo hilo, entonces tanto el microcontrolador como el sensor deben escuchar y hablar, no al mismo tiempo, pero si por turnos, cuando el microcontrolador le pide datos al sensor luego tiene que escuchar al sensor y recoger los datos, para esto el protocolo tiene tiempos establecidos en los bits del protocolo serial. Esto esta en el datasheet del sensor.


Aquí podremos ver las señales del microcontrolador (en negro) y del sensor (en gris), compartiendo el mismo bus de datos pero en tiempos distintos.
En el primer tramo podemos ver el MCU a nivel bajo como start y luego se pone a nivel alto en espera del sensor, entonces en este momento el MCU se comporta primero como salida y luego como entrada (esto tenemos que tenerlo en cuenta al momento de configurar el pin que usamos en el puerto).
Luego de que el MCU se pone a nivel alto como lectura, el sensor se pone a nivel bajo y el MCU lo lee, luego se pone a nivel alto como señal de que se reconoció la trama y comienza a enviar los datos unos y ceros, como podremos ver el cero o el uno se diferencia por el tiempo en que esta en High o Low.




Como podremos ver en estos diagramas de tiempo tenemos que respetar un determinado delay de tiempo entre ceros y unos, al momento de escuchar los bits del DHT como también al enviar la señal de start desde el MCU. Se podar ver mejor en el programa.

  1. #include <16F883.h>
  2. #FUSES NOWDT
  3. #FUSES HS
  4. #FUSES MCLR
  5. #use delay(clock=4000000)
  6. #use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
  7. #define DATA PIN_B0                    //Pin del bus de un hilo para el DHT11
  8. unsigned int trama[5];                 //Vector donde se alojan los datos
  9. unsigned int recibeByte()              //Funcion que recibe un Byte
  10. {
  11.    unsigned int8 valorLeido = 0;       //Valor de retorno de la funcion
  12.    int8 i=0;
  13.    for(i=0; i<8; i++)                  //Iteracion para recepcion de bits
  14.    {
  15.       valorLeido <<= 1;                //Registro de desplazamiento de bits
  16.       while(!input(DATA));             //Espera a DATA = 0
  17.       delay_us(30);                    //Demora de 30us (Del Datasheet)
  18.       if(input(DATA))                  //Pregunta si DATA = 1
  19.       {
  20.           valorLeido |= 1;             //Realiza toggle del valor leido
  21.       }
  22.       while(input(DATA));              //Espera a DATA = 1
  23.    }
  24.    return valorLeido;                  //Retorna el valor leido
  25. }
  26. unsigned int recibeDato()              //Funcion que recibe el Dato
  27. {
  28.    int validacion = 0;                 //Variable de Validacion
  29.    int checksum = 0;                   //Variable de deteccion de cambios de secuencia
  30.    int8 j=0;                           //Variable para el lazo for
  31.    output_high(DATA);                  //Set DATA = 1  
  32.    output_low(DATA);                   //Set DATA = 0
  33.    delay_ms(18);                       //Demora de 18ms (Del Datasheet)
  34.    output_high(DATA);                  //Set DATA = 1
  35.    delay_us(25);                       //Demora de 25ms (Del Datasheet)
  36.    validacion = input(DATA);           //Mueve valor de DATA a Validacion
  37.    if(validacion)                      //Si Validacion = 1, Sensor no encontrado
  38.    {
  39.       printf( "Sensor no encontrado. \r");                      
  40.    }
  41.    delay_us(80);                       //Espera 80us (Del Datasheet)
  42.    validacion = input(DATA);           //Mueve valor de DATA a Validacion
  43.    if(!validacion)                     //Si Validacion = 0, Error de secuencia
  44.    {
  45.       printf( "Error en secuencia (Checksum) \r");  
  46.    }
  47.    delay_us(80);                       //Espera 80us (Del Datasheet)
  48.    for(j=0; j<5; j++)                  //Lazo de carga de bytes de datos
  49.    {
  50.        trama[j] = recibeByte();        //Carga del vector de datos
  51.    }
  52.    output_high(DATA);                  //Set DATA = 1
  53.    for(j=0; j<4; j++)                  //Lazo de carga de bytes de verificacion
  54.    {
  55.        checksum += trama[j];           //Carga de bytes de verificacion
  56.    }
  57.    if(checksum == trama[4])            //Si la secuencia de verificacion es correcta
  58.    {
  59.       return 0;                        //Se retorna 0 y se realiza la lectura
  60.    }
  61. }
  62. void main()
  63. {
  64.    setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
  65.    setup_timer_1(T1_DISABLED);
  66.    setup_timer_2(T2_DISABLED,0,1);
  67.    while(TRUE)
  68.    {
  69.       if(recibeDato()==0)              //Si el retorno es 0, se imprime en terminal
  70.          printf( "Temp: %2u - R.H: %2u \r", trama[2], trama[0]);
  71.       delay_ms(500);
  72.    }
  73. }