Temperatura y humedad, con Arduino y DHT11/DHT22

Hoy os traigo un tutorial sobre cómo montar un sensor de temperatura y humedad con Arduino, usando el sensor de temperatura DHT11 o DHT22, y la pantalla de Nokia 5110.

A la hora de hacer el proyecto, me centré en que consumiera lo menos posible, ya que a nadie le gusta tener que estar cambiando las baterías de los sensores a diario. Por esta razón, utilizo un chip ATMega328p underclockeado, el cual reduce su consumo y permite prescindir del oscilador externo. Además la luz de la pantalla está programada para estar encendida sólo unos segundos tras pulsar un botón.

Si no sabes cómo undeclockear tu ATMega328p te recomiendo estar atento al blog, ya que en próximas entradas enseñaré cómo cambiar la frecuencia del microcontrolador.

Materiales necesarios

  • Potoboard
  • Jumpers o cables de conexión
  • ATMega328p
  • Pantalla Nokia 5110
  • Oscilador de cristal de cuarzo de 16Mhz
  • Dos condensadores cerámicos de 22pf
  • Transistor 2n2222
  • Sensor DHT11 o DHT22
  • Dos resistencias de 47k
  • Una resistencia de 1k
  • Dos pulsadores

Funcionalidades

El circuito es simple por lo que no tiene demasiadas funcionalidades, pero entre ellas puedo destacar:

  • Mide la temperatura y la humedad con una resolución de 1ºC y 1% para el DHT11, y 0.1ºC y 0.1% para el DHT22
  • El rango de temperatura es de 0ºC a 50ºC para el DHT11 y -40ºC y 80ºC para el DHT22
  • El rango de humedad es de 20% a 90% para el DHT11 y 0% a 100% para el DHT22
  • Tiene un consumo medio de 0,31ma con el bootloader de 4Mhz, por lo que puede aguantar unos 363 días con una batería de litio de 3.7v y 3Ah.
  • Actualiza los datos una vez cada minuto y el resto del tiempo está en modo sleep consumiendo 0,25ma.

Para conseguir el bajo consumo la luz de la pantalla permanecerá apagada hasta que se pulse el botón de encendido. Además, el microcontrolador permanecerá en modo Sleep entre mediciones.

El tiempo de refresco por defecto es un minuto (25 ciclos de 4 segundos), y el número de ciclos de espera se puede modificar con la macro SLEEP_LOOPS. Con esto se puede conseguir un consumo aún menor, por ejemplo, si se cambia a 75 ciclos (5 minutos) la duración de la batería pasará a ser de 443 días.

Esquema del circuito

El circuito que os traigo es bastante simple. En él también incluyo el circuito oscilador para los que no hayan podido prescindir de él (el cristal de cuarzo y los dos condensadores cerámicos), pero si conseguiste underclockear tu Arduino, entonces puedes obviarlo.

El sensor lo dejaremos conectado debido a que sino las mediciones no serán correctas, y a que su consumo es despreciable. La luz de la pantalla por su parte, la mantendremos apagada hasta que se pulse el botón de la derecha, tras lo cual se mantendrá encendida 4 segundos.

Código

El código que he utilizado para este proyecto es el siguiente:

#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
#include <LowPower.h>
#include <DHT.h>

// flag to turn on/off debugging
#define DEBUG true
#define Serial if(DEBUG)Serial

// Software SPI (slower updates, more flexible pin options):
// pin 7 - Serial clock out (SCLK)
// pin 6 - Serial data out (DIN)
// pin 5 - Data/Command select (D/C)
// pin 4 - LCD chip select (CS)
// pin 3 - LCD reset (RST)
Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 5, 4, 3);

// Every loop the Arduino sleeps for 4 seconds
#define SLEEP_LOOPS 25
#define L1_POS 6
#define L2_POS 24
#define DHTPIN 9
#define DHTTYPE DHT22
#define LEDPIN 8
#define LIGHTSON 4000
#define POS_TEXT 4
#define POS_NUM 38

static const unsigned char PROGMEM LogoESC [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xEF, 0x73,
0xC0, 0x00, 0x00, 0x01, 0x09, 0x81, 0x00, 0x00, 0x00, 0x01, 0x09, 0x81, 0x00, 0x00, 0x3F, 0x00,
0xE9, 0xF1, 0x00, 0x00, 0x20, 0x80, 0x29, 0x81, 0x00, 0x00, 0x20, 0x80, 0x29, 0x81, 0x00, 0x00,
0x3F, 0x81, 0xEE, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF8, 0x3C, 0x80, 0x00, 0x00,
0x01, 0x04, 0x25, 0x81, 0x2A, 0x54, 0x81, 0x04, 0x23, 0x07, 0xFF, 0xFF, 0xE1, 0x04, 0x3F, 0x87,
0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF1, 0xFC, 0x20, 0x1F, 0xFF, 0xFF, 0xF9, 0x00,
0x20, 0x0F, 0xFF, 0xFF, 0xF1, 0x00, 0x3F, 0x8F, 0xFF, 0xFF, 0xF1, 0x00, 0x20, 0x1F, 0xFF, 0xFF,
0xF8, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xF9, 0xFC, 0x20, 0x8F,
0xFF, 0xFF, 0xF1, 0x04, 0x20, 0x9F, 0xFF, 0xFF, 0xF9, 0x04, 0x20, 0x8F, 0xFF, 0xFF, 0xF0, 0xFC,
0x1F, 0x8F, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xF9, 0xFC, 0x24, 0x8F, 0xFF, 0xFF,
0xF1, 0x00, 0x24, 0x9F, 0xFF, 0xFF, 0xF9, 0x00, 0x20, 0x8F, 0xFF, 0xFF, 0xF1, 0xFC, 0x1F, 0x9F,
0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x8F, 0xFF, 0xFF, 0xF1, 0xFC,
0x00, 0x9F, 0xFF, 0xFF, 0xF9, 0x04, 0x00, 0x8F, 0xFF, 0xFF, 0xF1, 0x04, 0x3F, 0x87, 0xFF, 0xFF,
0xE1, 0xFC, 0x00, 0x03, 0xFF, 0xFF, 0xE0, 0x20, 0x24, 0x81, 0x2A, 0x54, 0x80, 0x70, 0x24, 0x80,
0x00, 0x00, 0x00, 0x70, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

void data_update();
void clean_box(int x, int y);
void clean_box(int x, int y, int ch, int cv);
void ligths_on();

bool led_state = true;
unsigned int led_timer = 0;

DHT dht(DHTPIN, DHTTYPE);

void setup()   {
  Serial.begin(9600);
  display.begin();
  dht.begin();
  // init done
  display.clearDisplay();
  display.drawBitmap(18, 0, LogoESC, 48, 48, BLACK);
  display.setContrast(50);
  display.display();
  digitalWrite(LEDPIN, HIGH);
  digitalWrite(10, HIGH);
  delay(3000);
  led_timer = millis();
  display.clearDisplay();
  display.setTextSize(2);
  display.setCursor(POS_TEXT, L1_POS);
  display.print(F("T"));
  display.setCursor(POS_TEXT, L2_POS);
  display.print(F("H"));
  display.setCursor(display.getCursorX()-4, L2_POS);
  display.setTextSize(1);
  display.setCursor(POS_TEXT+11, L1_POS+7);
  display.print(F("EMP"));
  display.setCursor(POS_TEXT+11, L2_POS+7);
  display.print(F("UME"));
  display.drawRoundRect(POS_NUM+34, L1_POS-2, 3, 3, 1, BLACK);
  display.setCursor(POS_NUM+38, L1_POS-1);
  display.print(F("C"));
  display.setCursor(POS_NUM+38, L2_POS-1);
  display.print(F("%"));
  display.display();
  
  // Led Interruption
  attachInterrupt(digitalPinToInterrupt(2), ligths_on, RISING);
}

void loop() {
  digitalWrite(13, HIGH);
  delay(100);
  digitalWrite(13, LOW);
  data_update();
  
  if (led_state) {
    while (((unsigned int)millis() - led_timer) < LIGHTSON) {
      data_update();
    }
    led_state = false;
    digitalWrite(LEDPIN, LOW);
  }
  for (int i = 0; i < SLEEP_LOOPS; i++) {  
    LowPower.powerDown(SLEEP_4S, ADC_OFF, BOD_ON);
    if (led_state) {
      break;
    }
  }
}

void data_update(){
  float h = dht.readHumidity();;
  float t = dht.readTemperature();
  // Check if any reads failed and exit early (to try again).
  if (isnan(h) || isnan(t)) {
    Serial.println(F("Failed to read from DHT sensor!"));
    return;
  }

  Serial.print(F("Humidity: "));
  Serial.print(h);
  Serial.print(F("%  Temperature: "));
  Serial.print(t);
  Serial.print(F("°C "));

  // Temperature
  char temp_e[3] ;
  byte temp_d = 0;
  display.setTextSize(2);
  clean_box(POS_NUM, L1_POS, 6, 4);
  sprintf(temp_e, "%3d", (int)t);
  display.setCursor(POS_NUM, L1_POS);
  display.print(temp_e);
  display.setCursor(POS_NUM+38, L1_POS);
  display.setTextSize(1);
  display.setCursor(POS_NUM+33, L1_POS+7);
  display.print(".");
  display.setCursor(POS_NUM+38, L1_POS+7);
  clean_box(POS_NUM+38, L1_POS+7);
  temp_d = round((t - (int)t) * 10);
  display.print(temp_d);

  // Humidity
  char hum_e[3] ;
  byte hum_d = 0;
  display.setTextSize(2);
  clean_box(POS_NUM, L2_POS, 6, 4);
  sprintf(hum_e, "%3d", (int)h);
  display.setCursor(POS_NUM, L2_POS);
  display.print(hum_e);
  display.setCursor(POS_NUM+38, L2_POS);
  display.setTextSize(1);
  display.setCursor(POS_NUM+33, L2_POS+7);
  display.print(".");
  display.setCursor(POS_NUM+38, L2_POS+7);
  clean_box(POS_NUM+38, L2_POS+7);
  // We use round because byte conversion is wrong
  hum_d = round(double(h - (int)h) * 10);
  display.print(hum_d);
  
  display.display();
}

void clean_box(int x, int y) {
  clean_box(x, y, 1, 1);
}

void clean_box(int x, int y, int ch, int cv) {
  int width = 6 * ch - 1;
  int height = 8 * cv - 1;
  for (int i = 0; i < width; i++){
    for (int j = 0; j < height; j++){
      display.drawPixel(x + i, y + j, WHITE);
    }
  }
}

void ligths_on() {
   digitalWrite(LEDPIN, HIGH);
   led_state = true;
   led_timer = millis();
}

¡Un saludo!, y no olvides comentar si tienes alguna sugerencia o simplemente quieres decir algo 😉

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.