Medición de temperatura y humedad con un sensor DHT11

12 febrero, 2012

En muchas ocasiones puede sernos útil la medida de la temperatura y la humedad relativas. Hay muchos sensores en el mercado, pero el que usaremos hoy es el DHT11 de Sensirion, con salidas digitales ya calibradas y a un coste bastante bajo.

Lectura de temperatura y humedad con DHT11

Lectura de temperatura y humedad con DHT11

Para probarlo, usaremos un PIC16F628A y un LCD 16×2.

Acerca del sensor DHT11

El sensor viene en un encapsulado con 4 pines en línea, y se alimenta en un rango entre 3.5V y 5.5V. Puede medir temperautras entre 0º y 50ºC con una precisión de ±2ºC, y una humedad relativa entre el 20% y el 95% con una precisión del 5%.

El sensor se comunica con el microcontrolador a través del protocolo 1-wire, que implementaremos en el firmware del PIC ya que no está integrado a nivel hardware.

Sensor DHT11

Sensor DHT11

Protocolo 1-wire

El microcontrolador inicia la comunicación configurando el pin como salida y enviando la señal de Start. Esta señal consiste en establecer nivel bajo durante 18ms y nivel alto durante 20us-40us. A continuación ponemos el pin como entrada y el sensor responderá estableciendo un nivel bajo de 80us y un nivel alto de 80us.

A continuación el sensor enviará 5 bytes (40 bits) de format continua. El primer bit recibido de cada byte será el más significativo.

Señal Start y respuesta

Señal Start y respuesta

Los 5 bytes recibidos serán los siguientes:

  • Byte1: parte entera de humedad relativa
  • Byte2: parte decimal de humedad relativa
  • Byte3: parte entera de temperatura
  • Byte4: parte decimal de temperatura
  • Byte5: checksum

El sensor DHT11 no utiliza decimales, por lo que podemos ignorar la información de los bytes 2 y 4; nos quedaremos sólo con los bytes 1 y 3.

El checksum se utiliza para confirmar que la información recibida es correcta, y se calcula sumando los 4 bytes anteriores y quedándonos sólo con los 8 bits menos significativos del resultado.

Cada uno de los bits se envía siguiendo esta estructura. Cuando el sensor va a enviar un bit, siempre tira la línea abajo durante 50us, y luego la levanta durante 26-28us para señalizar un “0”, o durante 70us si quiere enviar un “1”.

Envío de bits 0 y 1

Envío de bits 0 y 1

Secuencia completa del protocolo 1-wire

Secuencia completa del protocolo 1-wire

Cuando se han enviado todos los bits, el sensor baja la línea durante 50us y luego la libera.

El protocolo 1-wire requiere de la existencia de una resistencia de pull-up para que cuando está libre se mantenga a nivel alto.

Una vez terminada la transmisión, el sensor pasa al estado de bajo consumo de energía.

El circuito

El esquema es muy simple, ya que sólo tiene el PIC 16F628A, el LCD y nuestro sensor. El PIC está conectado a un resonador de 4MHz, lo que le permite trabajar con un ciclo de reloj de 1us, y esto facilita la lectura de los tiempos de la trama 1-wire.

Esquema circuito

Esquema circuito

El montaje se ha realizado sobre una protoboard, aprovechando el LCD de una placa de prototipos genérica.

Instalación en Protoboard

Instalación en Protoboard

Detalle del PIC y el sensor DHT11

Detalle del PIC y el sensor DHT11

Firmware

El firmware del micro está escrito en MikroC PRO, y como se puede comprobar a continuación, la lectura de las temporizaciones del 1-wire se realiza utilizando el Timer2 del micro. Está configurado para que este TMR2 se incremente en una unidad cada 1us, por lo que los cálculos se simplifican bastante.

// LCD module connections
sbit LCD_RS at RB2_bit;
sbit LCD_EN at RB3_bit;
sbit LCD_D4 at RB4_bit;
sbit LCD_D5 at RB5_bit;
sbit LCD_D6 at RB6_bit;
sbit LCD_D7 at RB7_bit;
sbit LCD_RS_Direction at TRISB2_bit;
sbit LCD_EN_Direction at TRISB3_bit;
sbit LCD_D4_Direction at TRISB4_bit;
sbit LCD_D5_Direction at TRISB5_bit;
sbit LCD_D6_Direction at TRISB6_bit;
sbit LCD_D7_Direction at TRISB7_bit;

sbit Data at RA0_bit;
sbit DataDir at TRISA0_bit;
char message1[] = "Temp = 00.0 C";
char message2[] = "RH   = 00.0 %";
unsigned short TOUT = 0, CheckSum, i;
unsigned short T_Byte1, T_Byte2, RH_Byte1, RH_Byte2;

void StartSignal(){
  DataDir = 0;     // Data port is output
  Data    = 0;
  Delay_ms(25);    // Low for at least 18us
  Data    = 1;
  Delay_us(30);    // High for 20-40 us
  DataDir = 1;     // Data port is input

}

unsigned short CheckResponse(){
  TOUT = 0;
  TMR2 = 0;
  T2CON.TMR2ON = 1;      // Start TMR2 while waiting for sensor response
  while(!Data && !TOUT); // If there's no response within 256us, the Timer2 overflows
  if (TOUT) return 0;    // and exit
  else {
   TMR2 = 0;
   while(Data && !TOUT);
   if (TOUT) return 0;
   else {
    T2CON.TMR2ON = 0;
    return 1;
   }
  }
}

unsigned short ReadByte(){
  unsigned short num = 0, t;
  DataDir = 1;
  for (i=0; i<8; i++){
   while(!Data);
   TMR2 = 0;
   T2CON.TMR2ON = 1;  // Start TMR2 from 0 when a low to high data pulse
   while(Data);       // is detected, and wait until it falls low again.
   T2CON.TMR2ON = 0;  // Stop the TMR2 when the data pulse falls low.
   if(TMR2 > 40) num |= 1<<(7-i);  // If time > 40us, Data is 1
  }
  return num;
}

void interrupt(){
  if(PIR1.TMR2IF){
   TOUT = 1;
   T2CON.TMR2ON = 0; // stop timer
   PIR1.TMR2IF  = 0; // Clear TMR0 interrupt flag
  }
}

void main() {
  unsigned short check;
  TRISB = 0b00000000;
  PORTB = 0;
  TRISA = 0b00100001;
  CMCON = 7;
  INTCON.GIE = 1;    //Enable global interrupt
  INTCON.PEIE = 1;   //Enable peripheral interrupt
  // Configure Timer2 module
  PIE1.TMR2IE = 1;  // Enable Timer2 interrupt
  T2CON = 0;        // Prescaler 1:1, and Timer2 is off initially
  PIR1.TMR2IF =0;   // Clear TMR INT Flag bit
  TMR2 = 0;
  Lcd_Init();
  Lcd_Cmd(_Lcd_Clear);
  Lcd_Cmd(_LCD_CURSOR_OFF);

  do {
    Delay_ms(1000);
    StartSignal();
    check = CheckResponse();
    if (!check) {
     Lcd_Cmd(_Lcd_Clear);
     Lcd_Out(1, 1, "No response");
     Lcd_Out(2, 1, "from the sensor");
    }
    else{

     RH_Byte1 = ReadByte();
     RH_Byte2 = ReadByte();
     T_Byte1 = ReadByte();
     T_Byte2 = ReadByte();
     CheckSum = ReadByte();
     // Check for error in Data reception
     if (CheckSum == ((RH_Byte1 + RH_Byte2 + T_Byte1 + T_Byte2) & 0xFF))
     {
      message1[7]  = T_Byte1/10 + 48;
      message1[8]  = T_Byte1%10 + 48;
      message1[10] = T_Byte2/10 + 48;
      message2[7]  = RH_Byte1/10 + 48;
      message2[8]  = RH_Byte1%10 + 48;
      message2[10] = RH_Byte2/10 + 48;
      message1[11] = 223;     // Degree symbol
      Lcd_Cmd(_Lcd_Clear);
      Lcd_Out(1, 1, message1);
      Lcd_Out(2, 1, message2);
      }

      else{
       Lcd_Cmd(_Lcd_Clear);
       Lcd_Out(1, 1, "Checksum Error!");
       Lcd_Out(2, 1, "Trying Again ...");
      }
    }

  }while(1);
}

Es posible simplificar la rutina ReadByte si no queremos usar el módulo Timer2. Esta siguiente versión de dicha rutina funciona también. Cuando se lee el pin a nivel alto, el PIC espera 40μs y chequea la línea de nuevo. Si está en estado alto, es un “1”; en caso contrario es un “0”.

unsigned short ReadByte(){
  unsigned short num = 0, t;
  DataDir = 1;
  for (i=0; i<8; i++){
   while(!Data);
   Delay_us(40);
   if(Data) num |= 1<<(7-i);
   while(Data);
   }
  return num;
}

Puedes descargar el código fuente completo y el fichero HEX en nuestra zona de Descargas.

Visto en embedded-lab

Share

Etiquetas: , , ,