Temperature and humidity: Arduino & DHT11/DHT22

Today I bring you a tutorial on how to mount a temperature and humidity sensor with Arduino, using the DHT11 or DHT22 temperature sensor, and the Nokia 5110 display.

When doing the project, I focused on consuming as little as possible, since nobody likes having to change the sensor batteries on a daily basis. For this reason, I use an underclocked ATMega328p chip, which reduces its consumption and makes it possible to do without the external oscillator. In addition, the display light is programmed to be on for only a few seconds after pressing a button.

If you do not know how to underclock your ATMega328p I recommend you stay tuned to the blog, since in future posts I will teach you how to change the frequency of the microcontroller.

Necessary materials

Potoboard
Jumpers or connecting cables
ATMega328p
Nokia 5110 display
16Mhz Quartz Crystal Oscillator
Two 22pf ceramic capacitors
2n2222 transistor
DHT11 or DHT22 sensor
Two 47k resistors
1k resistor
Two push buttons

Features

The circuit is simple so it does not have many functionalities, but among them I can highlight:

  • Measures temperature and humidity with a resolution of 1ºC and 1% for DHT11, and 0.1ºC and 0.1% for DHT22
  • The temperature range is 0ºC to 50ºC for DHT11 and -40ºC and 80ºC for DHT22
  • The humidity range is 20% to 90% for DHT11 and 0% to 100% for DHT22
  • It has an average consumption of 0.31ma with the 4Mhz bootloader, so it can last 363 days with a 3.7v and 3Ah lithium battery.
  • It updates the data once every minute and the rest of the time it is in sleep mode consuming 0.25ma.

To achieve low power consumption, the display light will remain off until the power button is pressed. Also, the microcontroller will remain in Sleep mode between measurements.

The default refresh time is one minute (25 cycles of 4 seconds), and the number of wait cycles can be modified with the SLEEP_LOOPS macro. With this, even lower consumption can be achieved, for example, if it is changed to 75 cycles (5 minutes), the battery life will be 443 days.

Circuit diagram

The circuit that I bring you is quite simple. In it I also include the oscillator circuit for those who could not do without it (the quartz crystal and the two ceramic capacitors), but if you managed to underclock your Arduino, then you can skip it.

We will leave the sensor connected because otherwise the measurements will not be correct, and because its consumption is negligible. On the other hand, we will keep the display light off until the button on the right is pressed, after which it will stay on for 4 seconds.

Code

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();
}

Greetings!, and don’t forget to comment if you have any suggestion or just want to say something 😉

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.