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. }









10 comentarios:

  1. cuales son los cambios para hacerlo en un pic18f4550?

    ResponderEliminar
    Respuestas
    1. Hola, si vas a programar en CCS, el programa es el mismo, lo que cambia es el setup del micro que es diferente, los fuses, puertos, etc... pero si estas usando el CCS con algún IDE como el PCWHD o PICC, entonces solo tendrías que hacer un nuevo programa desde el ayudante "wizard" elegir tu pic18F y listo, con ese ayudante ya tenes el encabezado para tu programa que luego es copiar y pegar el codigo este en ese.

      Eliminar
    2. #include <18F4550.h>
      #FUSES NOWDT //No Watch Dog Timer
      #FUSES WDT128 //Watch Dog Timer uses 1:128 Postscale
      #FUSES NOBROWNOUT //No brownout reset
      #FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
      #FUSES NOXINST //Extended set extension and Indexed Addressing mode disabled (Legacy mode)
      #use delay(crystal=20000000)
      #use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
      #define DATA PIN_B0 //Pin del bus de un hilo para el DHT11
      unsigned int trama[5]; //Vector donde se alojan los datos
      unsigned int recibeByte() //Funcion que recibe un Byte
      {
      unsigned int8 valorLeido = 0; //Valor de retorno de la funcion
      int8 i=0;
      for(i=0; i<8; i++) //Iteracion para recepcion de bits
      {
      valorLeido <<= 1; //Registro de desplazamiento de bits
      while(!input(DATA)); //Espera a DATA = 0
      delay_us(30); //Demora de 30us (Del Datasheet)
      if(input(DATA)) //Pregunta si DATA = 1
      {
      valorLeido |= 1; //Realiza toggle del valor leido
      }
      while(input(DATA)); //Espera a DATA = 1
      }
      return valorLeido; //Retorna el valor leido
      }
      unsigned int recibeDato() //Funcion que recibe el Dato
      {
      int validacion = 0; //Variable de Validacion
      int checksum = 0; //Variable de deteccion de cambios de secuencia
      int8 j=0; //Variable para el lazo for
      output_high(DATA); //Set DATA = 1
      output_low(DATA); //Set DATA = 0
      delay_ms(18); //Demora de 18ms (Del Datasheet)
      output_high(DATA); //Set DATA = 1
      delay_us(25); //Demora de 25ms (Del Datasheet)
      validacion = input(DATA); //Mueve valor de DATA a Validacion
      if(validacion) //Si Validacion = 1, Sensor no encontrado
      {
      printf( "Sensor no encontrado. \r");
      }
      delay_us(80); //Espera 80us (Del Datasheet)
      validacion = input(DATA); //Mueve valor de DATA a Validacion
      if(!validacion) //Si Validacion = 0, Error de secuencia
      {
      printf( "Error en secuencia (Checksum) \r");
      }
      delay_us(80); //Espera 80us (Del Datasheet)
      for(j=0; j<5; j++) //Lazo de carga de bytes de datos
      {
      trama[j] = recibeByte(); //Carga del vector de datos
      }
      output_high(DATA); //Set DATA = 1
      for(j=0; j<4; j++) //Lazo de carga de bytes de verificacion
      {
      checksum += trama[j]; //Carga de bytes de verificacion
      }
      if(checksum == trama[4]) //Si la secuencia de verificacion es correcta
      {
      return 0; //Se retorna 0 y se realiza la lectura
      }
      }
      void main()
      {
      setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
      setup_timer_1(T1_DISABLED);
      setup_timer_2(T2_DISABLED,0,1);
      while(TRUE)
      {
      if(recibeDato()==0) //Si el retorno es 0, se imprime en terminal
      printf( "Temp: %2u - R.H: %2u \r", trama[2], trama[0]);
      delay_ms(500);
      }
      }

      Eliminar
    3. Solamente cambie la parte de arriba:

      #include <18F4550.h>
      #FUSES NOWDT //No Watch Dog Timer
      #FUSES WDT128 //Watch Dog Timer uses 1:128 Postscale
      #FUSES NOBROWNOUT //No brownout reset
      #FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
      #FUSES NOXINST //Extended set extension and Indexed Addressing mode disabled (Legacy mode)
      #use delay(crystal=20000000)

      Eliminar
  2. Muy buenas tardes.
    Depronto me podrías compartir un esquemático del circuito, en el momento estoy tratando de simularlo en proteus, pero siempre dice "Sensor no encontrado".
    Muchas gracias por su ayuda.

    ResponderEliminar
    Respuestas
    1. Que raro, fijate aca: https://github.com/electgpl/Utn-labMicros-Est/tree/master/Simulaci%C3%B3n%20y%20Pruebas%20de%20Laboratorio
      es un proyecto de una estacion meteorologica con PIC o 8051, pero en ese link tenes el archivo del Proteus para la simulacion del DHT11. Podes dejar ese que anda bien y cambiar el micro por el arduino.
      Saludos.

      Eliminar
    2. Muy buenas tardes amigo. Te agradezco mucho la ayuda. Ya estuve revisando, pero aun continuo con problemas. Por el momento quite la parte donde verifica la validación para poder visualizar los valores que me da de temperatura y humedad. Ahora me arroja datos pero ellos no son los que yo intento simular, por ejemplo yo le coloco una temperatura de 50 y me esta mostrando 24, igualmente con la humedad. En si, aun no he logrado encontrar el error y tengo el código como lo tienes tu, pero estoy usando un pic16F1937

      Eliminar
  3. Muy buenas tardes amigo. Te agradezco mucho la ayuda. Ya estuve revisando, pero aun continuo con problemas. Por el momento quite la parte donde verifica la validación para poder visualizar los valores que me da de temperatura y humedad. Ahora me arroja datos pero ellos no son los que yo intento simular, por ejemplo yo le coloco una temperatura de 50 y me esta mostrando 24, igualmente con la humedad. En si, aun no he logrado encontrar el error y tengo el código como lo tienes tu, pero estoy usando un pic16F1937

    ResponderEliminar
    Respuestas
    1. Hola como estas? Es raro que si seteas un valor te muestre otro. porque el protocolo del DHT11 usa un checksum, es decir, si no es correcta la suma de valores entonces se desecha el valor. A menos que tengas un error de conversión, que estés trabajando en fahrenheit y muestres grados celcius.
      Saludos.

      Eliminar
  4. Para los compañeros que les esta dando el error de sensor no encontrado es debido a que en la simulacion deben colocar el la linea de la demora de 25us a 30us y con eso queda solucionado. a mi tambien me pasaba lo mismo y lleve la simulacion paso a paso y con ese tiempo de 25us en la simulacion no alcanza para que el sensor coloque la linea en bajo.

    //delay_us(25); //Demora de 25ms (Del Datasheet)
    delay_us(30); //cambiar por 30us
    validacion = input(DATA); //Mueve valor de DATA a Validacion
    if(validacion) //Si Validacion = 1, Sensor no encontrado
    {
    printf( "Sensor no encontrado. \r");
    }

    ResponderEliminar