paulsb
Published © GPL3+

Very low power temp and humidity monitor with min and max

A very low power consumption rechargeable temperature and humidity monitor that can run off a rechargeable battery for 12 days or more.

BeginnerFull instructions provided3,831
Very low power temp and humidity monitor with min and max

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
Suggest using a Nano OR....
×1
Arduino Pro Mini
Either will work, or indeed any other Arduino board
×1
LCD 16x2 Backlit Display
For 16x2 project OR....
×1
AZDelivery I2C 0.96-inch OLED Display SSD1306 128x64 Pixels IIC 3.3V 5V White Character Display
For OLED project
×1
Single Turn Potentiometer- 10k ohms
Single Turn Potentiometer- 10k ohms
Any make will do - only required for the LCD 16x2 display version
×1
TP4056 5V 1A TYPE C Micro USB Board Charging Module for 18650 Lithium Battery
×1
3.7V 1000mAh Lithium Rechargable Battery
×1
DHT11 Temperature Humidity Sensor Module
×1
Tactile Switch, Top Actuated
Tactile Switch, Top Actuated
×2
Hook Up wire
×1

Story

Read more

Schematics

Temp/Humid monitor OLED Breadbaord

Temp/Humid monitor OLED Schematic

Temp/Humid monitor LCD Breadboard

Temp/Humid monitor LCD Schematic

Code

Temo/Humid monitor OLED version code

Arduino
/*   Paul Brace April 2021
 *   Very low power consumption temperature and humidity monitor
 *     with minimum and maximum monitoring
 *   Board - Arduino Pro Mini
 *   Display - OLED 128x64 IC2
 *   Aim was to reduce power consumption as much as possible so
 *     can be run on a rechargeable battery for long periods
 *   This is achieved by:
 *         1. Using the rocketscream low power library to put the processor 
 *            to sleep waking every 24 seconds to check the temperature and 
 *            humidity, which takes less than a second.
 *         2. Displaying a small sleep icon to reduce the number of lit pixels  
 *            The sleep icon also moves every 8 seconds to avoid screen burn-in 
 *   The mode button is used to wake the processor for a set period, display   
 *     the readings and switch between the temperature and humidity displays.
 *   The processor remains awake for the period specified in keepAwakeFor.
 *   The reset button is used to reset the minimum and maximum values
 *     when the processor has been woken by the mode button being pressed.
 *   Power consumption with the suggested components:
 *      Asleep:                               24 seconds    3.23mA
 *      Checking temp./humid. between sleeps  <1 second     6.8mA
 *                                            So on average 3.4mA                                            
 *                                          
 *   Using a 1,000 mAh rechargeable battery the system should last for up 
 *     to 12 days between charges – frequently turning on the display will reduce this.
 *   
 *   Note on using the 3.7v rechargeable battery.
 *    The Nano specification is that it requires a 5v supply, however
 *    for this project it runs perfectly well on a 3.7v rechargeable battery
 *    with the supply connected to the 5v pin.
 *    It is also safe to recharge when in use as the voltage will only increase to 4.2v
 */

// Include drivers
#include <DHT.h>            // This is the DHT sensor library by Adafruit
#include "LowPower.h"       // Low-Power library from rocketscream
                            // required for the OLED display 
#include <SPI.h>            // Synchronous serial data protocol library
#include <Wire.h>           // IC2 communications library
#include <Adafruit_GFX.h>   // Adafruit Graphics Core Library
#include <Adafruit_SSD1306.h>  // SSD1306 library for Monochrome 128x64 and 128x32 OLEDs 
#include <Fonts/FreeMonoBold9pt7b.h>  // Include fonts to be used
#include <Fonts/FreeSans9pt7b.h>


#define DATA_PIN 4                    // Pin used to collect data from the DHT
#define DHTTYPE DHT11                 // DHT Type  
//#define DHTTYPE DHT22               // If you have a DHT22 comment the DHT11 and
                                      // uncomment DHT22
// Create the sensor object and assign pin
DHT dht(DATA_PIN, DHTTYPE);


#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// The pins for I2C are defined by the Wire-library. 
// On an arduino UNO/Nano and Pro Mini: A4(SDA), A5(SCL)
// On an arduino MEGA 2560:             20(SDA), 21(SCL)
// On an arduino LEONARDO:              2(SDA),  3(SCL), ...
#define OLED_RESET   -1 // Reset pin # (or -1 if no reset on display)
#define SCREEN_ADDRESS 0x3C // See datasheet for Address (Yours could be 0x3D)

// Creat eobject for the SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define MODE_BUTTON 2             // Activate display and switched between temperature and humidity
#define RESET_BUTTON 3            // If pressed when awake resets min and max settings

float temp;       //Current temperature
float humid;      //Current humidity
float minTemp;    //The minimum temperature recorded
float maxTemp;    //The maximum temperature recorded
float minHumid;   //The minimum humidity recorded
float maxHumid;   //The maximum humidity recorded

enum mode {
  dispTemp,        // Temperature currently displayed
  dispHumid        // Humidity currently displayed
};

mode currentMode;   // Current display setting

bool awake;                        // True if system woken by pressing Mode button
bool justWoken;                    // True on activation of interrupt so system knows to wait for button to be released.
unsigned long timeWoken;           // Time last woken or a button press
int keepAwakeFor = 15;             // Number of seconds before allow to go back to sleep

const int wakeUpPin = MODE_BUTTON; // Set the pin that will wake up from power saving mode

// Icon displayed when system is asleep
static const unsigned char PROGMEM snooze[] =
{ B00000000, B00001110,
  B00000000, B00000100,
  B00000000, B00001110,
  B00000011, B11000000,
  B00000011, B11000000,
  B00000000, B10000000,
  B00000001, B00000000,
  B00000011, B11000000,
  B00000011, B11000000,
  B11111000, B00000000,
  B11111000, B00000000,
  B00010000, B00000000,
  B00100000, B00000000,
  B01000000, B00000000,
  B11111000, B00000000,
  B11111000, B00000000,
};

// Arrows used to indicate maximum and minimum
static const unsigned char PROGMEM upArrow[]{
  B00000000,
  B00011000,
  B00111100,
  B01111110,
  B11111111,
  B00011000,
  B00011000,
  B00011000
};

static const unsigned char PROGMEM downArrow[]{
  B00011000,
  B00011000,
  B00011000,
  B11111111,
  B01111110,
  B00111100,
  B00011000,
  B00000000
};

void setup() {
  minTemp = 99;    //Set to a figure that is going to be too high
  maxTemp = -9;    //Set to a figure that is going to be too low
  minHumid = 99;   //Set to a figure that is going to be too high
  maxHumid = -9;   //Set to a figure that is going to be too low
  pinMode(MODE_BUTTON, INPUT_PULLUP);  // Use INPUT_PULLUP - will go LOW when pressed
  pinMode(RESET_BUTTON, INPUT_PULLUP);
  // Start sensor
  dht.begin();
  // Initialise display
  display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS); 
  display.setTextColor(WHITE);
  currentMode = dispTemp;
  awake = true;
  timeWoken = millis();         // Set so initially remains awake for set period
}

void loop() {
  // Check to see if has been awake for the keepAwakeFor period
  // if so allow to go to sleep
  if ((millis() - timeWoken) > (keepAwakeFor * 1000)) {
    awake = false;
    // Sleep for 24 seconds
    for (int i = 0; i < 3; i++){
      display.clearDisplay(); 
      display.drawBitmap(random(10, 110), random(10, 48), snooze, 16, 16, WHITE);
      display.display();
      // Allow wake up pin to trigger interrupt on low.
      attachInterrupt(0, wakeUp, LOW);
      // Enter power down state with ADC and BOD module disabled.
      // Wake up when wake up pin is low or after 8 seconds.
      // If wake up button pressed then as long as button held down for more
      // than a few milliseconds it will fall through any remaining sleep loops      
      LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
      // Disable external pin interrupt on wake up pin.
      detachInterrupt(0);
    }
  }

  // If woken by Mode button press then default to temperature display
  if (justWoken)
     currentMode = dispTemp; 

  // Repeat read and check buttons 3 times once every 1/10th second to ensure get up to date readings
  // as the system may go back to sleep on next loop
  for (int i = 0; i < 3; i++) {
    delay(100);
    // Get readings, update minimum and maximum values
    // and display if system is awake as a result of Mode button press
    humid = dht.readHumidity();                   // read humidity
    temp = dht.readTemperature();                 // read temperature
    if (temp < minTemp)
      minTemp = temp;
    if (temp > maxTemp)
      maxTemp = temp;
    if (humid < minHumid)
      minHumid = humid;
    if (humid > maxHumid)
      maxHumid = humid;
    // Update display if awake
    if ((currentMode == dispTemp) && (awake))
      ShowTemperature();
    if ((currentMode == dispHumid) && (awake))
      ShowHumidity();

    // If awake due to Mode button press the check if any buttons have been pressed
    if (awake)
      CheckButtons();
  }
}

// To ensure numbers align add a space if required
String FormatNumber(float value){
  if (value < 10.0 && value > 0.0)
    return " " + String(value);
  else
    return String(value);
}

void ShowTemperature() {
  display.clearDisplay();
  display.setTextSize(1);
  display.setFont(&FreeSans9pt7b);  // Font used for text
  display.setCursor(0, 15);
  display.print("Temp:");
  display.drawBitmap(20, 35-9, upArrow, 8, 8, WHITE);
  display.drawBitmap(20, 55-9, downArrow, 8, 8, WHITE);
  display.setFont(&FreeMonoBold9pt7b);  // Font used for numbers
  display.setCursor(60, 15);
  display.print(FormatNumber(temp) + "c");
  display.setCursor(60, 35);
  display.print(FormatNumber(maxTemp) + "c");
  display.setCursor(60, 55);
  display.print(FormatNumber(minTemp) + "c");
  display.display(); 
}

void ShowHumidity() {
  display.clearDisplay();
  display.setTextSize(1);
  display.setFont(&FreeSans9pt7b);  // Font used for text
  display.setCursor(0, 15);
  display.print("Humid:");
  display.drawBitmap(20, 35-9, upArrow, 8, 8, WHITE);
  display.drawBitmap(20, 55-9, downArrow, 8, 8, WHITE);
  display.setFont(&FreeMonoBold9pt7b);    // Font used for numbers
  display.setCursor(60, 15);
  display.print(FormatNumber(humid) + "%");
  display.setCursor(60, 35);
  display.print(FormatNumber(maxHumid) + "%");
  display.setCursor(60, 55);
  display.print(FormatNumber(minHumid) + "%");
  display.display(); 
}

void CheckButtons() {
  // If just woken up then pause for 1/2 second to allow button to be released
  if (justWoken){
    delay(500);
    justWoken = false;
  }
  // If the mode or reset button has been pressed pin will go LOW
  // Check if mode button pressed if so switch mode between displays
  if (digitalRead(MODE_BUTTON) == LOW) {
    timeWoken = millis();           // Reset so remains awake for set period after switching mode
    //awake = true;
    if (currentMode == dispTemp) {
      currentMode = dispHumid;
      ShowHumidity();
      delay(500);                     // Give time to release button
    }
    else {
      currentMode = dispTemp;
      ShowTemperature();
      delay(500);                     // Give time to release button
    }
  }

  // Check if reset button pressed
  // Is the system awake?
  // If so the reset min and max values
  if (digitalRead(RESET_BUTTON) == LOW) {
    if (awake) {
      maxTemp = -9;
      minTemp = 99;
      maxHumid = -9;
      minHumid = 99;
      delay(500);                     // Give time to release button
    }
  }
}

void wakeUp()
{
  // Handler for the pin interrupt.
  timeWoken = millis();               // Reset so remains awake for awake period
  awake = true;
  justWoken = true;
}

Temp/Humid monitor LCD version code

Arduino
/*   Paul Brace April 2021
 *   Very low power consumption temperature and humidity monitor
 *     with minimum and maximum monitoring
 *   Board - Arduino Nano 3
 *   Display - LCD 16x2 
 *   
 *   Aim was to reduce power consumption as much as possible so
 *     can be run on a rechargeable battery for long periods
 *   This is achieved by:
 *         1. Turning the backlight off after a set period 
 *         2. Using the rocketscream low power library to put the processor 
 *            to sleep waking every 24 seconds to check the temperature and 
 *            humidity, which takes less than a second.
 *   The mode button is used to wake the processor for a set period , turn 
 *     the backlight on and switch between the temperature and humidity displays.
 *   The processor remains awake for the period specified in keepAwakeFor.
 *   The backlight remains on for the period specified in timeLightTurnedOn
 *       (Note: this should be less than keepAwakeFor or the light will not be 
 *        turned off until the next awake cycle). 
 *   The reset button is used to reset the minimum and maximum values
 *     when the backlight is on.
 *   Power consumption with the suggested components:
 *      Asleep:                               24 seconds    5.6mA
 *      Checking temp./humid. between sleeps  <1 second     14mA
 *                                            So on average 6.0mA                                            
 *                                          
 *      With backlight on                                   28.5mA
 *      
 *   Using a 1,000 mAh rechargeable battery the system should last for up 
 *     to 7 days between charges – frequently turning on the backlight will obviously 
 *     reduce this.
 *   
 *   Note on using the 3.7v rechargeable battery.
 *    The Nano specification is that it requires a 5v supply, however
 *    for this project it runs perfectly well on a 3.7v rechargeable battery
 *    with the supply connected to the 5v pin.
 *    It is also safe to recharge when in use as the voltage will only increase to 4.2v
 */

// Include driver for the DHT
// This is the DHT sensor library by Adafruit
#include <DHT.h>
// Include the library driver for display:
#include <LiquidCrystal.h>
// Include Low-Power library from rocketscream
#include "LowPower.h"

// Pin used to collect data from the DHT
#define DATA_PIN 4

// DHT Type. If you have a DHT22 comment the DHT11 and
// uncomment DHT22
#define DHTTYPE DHT11
//#define DHTTYPE DHT22

// Create the sensor object and assign pin
DHT dht(DATA_PIN, DHTTYPE);

// Create an lcd object and assign the pins
// LiquidCrystal lcd( RS, EN, D4,D5, D6, D7)
LiquidCrystal lcd(12, 13, 6, 7, 8, 9);

#define MODE_BUTTON 2             // Wake processor/turn backlight on and switch display between temperature and humidity
#define RESET_BUTTON 3            // If pressed when light on then resets min and max settings
#define BACKLIGHT_PIN 5           // Connected to backlight on LCD

float temp;       //Current temperature
float humid;      //Current humidity
float minTemp;    //The minimum temperature recorded
float maxTemp;    //The maximum temperature recorded
float minHumid;   //The minimum humidity recorded
float maxHumid;   //The maximum humidity recorded

enum mode {
  dispTemp,        // Temperature currently displayed
  dispHumid        // Humidity currently displayed
};

mode currentMode;   // Current display setting

bool awake;                        // True if system woken by pressing Mode button
bool justWoken;                    // True on activation of interrupt so system knows to wait for button to be released. 
bool lightOn;                      // True if backlight on
unsigned long timeLightTurnedOn;   // Time backlight was last turned on
int lightTimeOut = 10;             // Number of seconds light remains on (should be less than keepAwakeFor)
unsigned long timeWoken;           // Time last woken or a button press
int keepAwakeFor = 15;             // Number of seconds before allow to go back to sleep

const int wakeUpPin = MODE_BUTTON; // Set the pin that will wake up from power saving mode

// Arrows used to indicate maximum and minimum
byte upArrow[8] = {
  B00000,
  B00100,
  B01110,
  B10101,
  B00100,
  B00100,
  B00000,
};

byte downArrow[8] = {
  B00000,
  B00100,
  B00100,
  B10101,
  B01110,
  B00100,
  B00000,
};

void setup() {
  minTemp = 99;    //Set to a figure that is going to be too high
  maxTemp = -9;    //Set to a figure that is going to be too low
  minHumid = 99;   //Set to a figure that is going to be too high
  maxHumid = -9;   //Set to a figure that is going to be too low
  pinMode(MODE_BUTTON, INPUT_PULLUP);  // Use INPUT_PULLUP - will go LOW when pressed
  pinMode(RESET_BUTTON, INPUT_PULLUP);
  pinMode(BACKLIGHT_PIN, OUTPUT);
  // Start sensor
  dht.begin();
  // Initalise diplay
  lcd.begin(16, 2);
  lcd.noAutoscroll();
  lcd.display();
  lcd.clear();
  // Create up and down arrow characters
  lcd.createChar(0, upArrow);
  lcd.createChar(1, downArrow);
  
  TurnBacklightOn();
  currentMode = dispTemp;
  awake = true;
  timeWoken = millis();         // Set so initially remains awake for set period
}

void loop() {
  // Check to see if has been awake for the keepAwakeFor period
  // if so allow to go to sleep
  if ((millis() - timeWoken) > (keepAwakeFor * 1000)) {
    awake = false;
    // Sleep for 24 seconds
    for (int i = 0; i < 3; i++){
      // Allow wake up pin to trigger interrupt on low.
      attachInterrupt(0, wakeUp, LOW);
      // Enter power down state with ADC and BOD disabled.
      // Wake up when wake up pin goes low or after 8 seconds.
      // If wake up button pressed then as long as button held down for more
      // than a few milliseconds it will fall through any remaining sleep loops
      LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
      // Disable external pin interrupt on wake up pin.
      detachInterrupt(0);
    }
  }

  // If woken by Mode button press then default to temperature display
  if (justWoken)
     currentMode = dispTemp; 
  
  // Repeat read 3 times, once every 1/10th second, to ensure get up to date readings
  // as the system may go back to sleep on next loop
  for (int i = 0; i < 3; i++) {
    delay(100);

    // Check if need to turn backlight off
    if (lightOn && ((millis() - timeLightTurnedOn) > (lightTimeOut * 1000)))
      TurnBacklightOff();
    // get readings, update minimum and maximum values and
    // refresh display
    humid = dht.readHumidity();                   // read humidity
    temp = dht.readTemperature();                 // read temperature
    // Set min and max values if required
    if (temp < minTemp)
      minTemp = temp;
    if (temp > maxTemp)
      maxTemp = temp;
    if (humid < minHumid)
      minHumid = humid;
    if (humid > maxHumid)
      maxHumid = humid;
    if (currentMode == dispTemp)
      ShowTemperature();
    else
      ShowHumidity();
    // If awake due to Mode button press the check if any buttons have been pressed
    if (awake)
      CheckButtons();
  }
}

// To ensure numbers align add a space if required
String formatNumber(float value){
  if (value < 10.0 && value > 0.0)
    return " " + String(value);
  else
    return String(value);
}

void ShowTemperature() {
  lcd.setCursor(0, 0);
  lcd.print("Temp: " + String(temp) + "c   ");
  lcd.setCursor(0, 1);
  lcd.write(byte(1));
  lcd.print(String(minTemp) + "c  ");
  lcd.setCursor(9, 1);
  lcd.write(byte(0));
  lcd.print(String(maxTemp) + "c  ");
}

void ShowHumidity() {
  // Display current temp on line 0
  lcd.setCursor(0, 0);
  lcd.print("Humid: " + String(humid) + "%   ");
  lcd.setCursor(0, 1);
  lcd.write(byte(1));
  lcd.print(String(minHumid) + "%  ");
  lcd.setCursor(9, 1);
  lcd.write(byte(0));
  lcd.print(String(maxHumid) + "%  ");
}

void CheckButtons() {
  // If just woken up then pause for 1/2 second to allow button to be released
  if (justWoken){
    delay(500);
    justWoken = false;
  }
  // If the mode or reset button has been pressed the associated pin will go LOW
  // Check if mode button pressed if so switch mode between displays
  if (digitalRead(MODE_BUTTON) == LOW) {
    timeWoken = millis();           // Reset so remains awake for the awake period after switching mode
    TurnBacklightOn();              // In case off and so also remains on for set period
    // Switch displays
    if (currentMode == dispTemp) {
      currentMode = dispHumid;
      ShowHumidity();
      delay(500);                   // Give time to release button
    }
    else {
      currentMode = dispTemp;
      ShowTemperature();
      delay(500);                  // Give time to release button
    }

  }

  // Check if reset button pressed
  // Is light currently on?
  // If so the reset min and max values
  if (digitalRead(RESET_BUTTON) == LOW) {
    if (lightOn) {
      maxTemp = -9;
      minTemp = 99;
      maxHumid = -9;
      minHumid = 99;
      delay(350);                 // Give time to release button
    }
  }
}

void TurnBacklightOn() {
  // Turn backlight on
  lightOn = true;
  timeLightTurnedOn = millis();
  digitalWrite(BACKLIGHT_PIN, HIGH);
}

void TurnBacklightOff() {
  // Turn backlight off
  lightOn = false;
  digitalWrite(BACKLIGHT_PIN, LOW);
}

void wakeUp()
{
  // Handler for the pin interrupt.
  timeWoken = millis();         // Reset so remains awake for awake period
  TurnBacklightOn();
  awake = true;
  justWoken = true;
}

Credits

paulsb

paulsb

4 projects • 28 followers

Comments