Skip to main content

Temperature Monitoring (indoor and outdoor)

For this project the goal is to relay indoor and outdoor temperature (and optionally humidity) to a server log. This project is fairly simple because we can use off the shelf DHT11 and DHT22 sensors (DHT22 is for below 0c). 

Note to self: avoid non-oem DHT22 modules. You only save a couple of dollars and may more headache than they are worth. They die almost immediately.

As you can see in the diagram below the wiring is pretty basic. The coding is a little more complex but not too much effort. 

temp_humidity_citkit_design.jpg

For the indoor sensor I used this case:

https://www.thingiverse.com/thing:2497711

And for the outdoor sensor I used this case: 

https://www.thingiverse.com/thing:6400888

temperature_sensor_protector.jpg

The end result in my case is a chart showing the temperature over a couple of days. If I see the temperature drop significantly (like pipes freezing zone) I at least can get out there to remedy the situation before is becomes an issue. 

2024-02-19_10-25.png

And the code. It ain't pretty but it works. For the server-side code I am just using free web hosting since the load is amost nothng. For the ESP32 module the code below is about as minimal as I dare go. I do record the server success/fail rates but if everything seems stable after a few weeks I will remove that too.

#include <WiFi.h>
#include <HTTPClient.h>
#include <Wire.h>
#include <Adafruit_ADS1X15.h>
#include <LiquidCrystal_I2C.h>
#include <DHT.h>
#include <DHT_U.h>
#include <EEPROM.h>

//////////////////////////////
// EDIT THIS SECTION
// last updated June 23, 2024

int activeConnection = 1;

// primary wifisilo
const String ssid1 = "xxx"hgf";
const String password1 = "xxxxx"i656dfd1396";

// secondary wifihansen
const String ssid2 = "xxxx"gfds";
const String password2 = "xxxxx";

const String heartbeatUrl = "https://xxx.xxx.ca/silopower/heartbeat.php"9645311355";

const String temperatureUrl = "https://xxx.xxx.ca/homelab.xyz.com/silopower/set_temp_humidity.temp_humidity.php?data=";

// end edit section
////////////////////

#define EEPROM_SIZE 4
int eepromPingsAddress = 0;
float totalServerPings = 0;
int eepromFailedPingsAddress = 1;
float totalServerPingFails = 0;
int eepromActiveConnection = 1;


String prevTemperatureText = "";
String prevHumidifyText = "";
String prevConnectivityText1 = "";
String prevConnectivityText2 = "";

const int indoorTemperaturePinbasementTemperaturePin = 16;
const int upstairsTemperaturePin = 17;
const int outdoorTemperaturePin = 17;23;


const int buttonPin = 15;
int buttonState = 0;
int displayMode = 1;  // 1: temp, humidiy  2: internet

const int pulseRate = 1000;2;  // loop2 runs once per secondseconds

const int serverSendIntervaltemperatureServerSendInterval = 10;1;                                                // 10 minutes between sending a voltagetemperature update to the server
const int samplesPerReadingtemperatureSamplesPerReading = (60 * serverSendInterval;temperatureServerSendInterval) / pulseRate;  // every x minutes send a sample to the server

int temperatureLoopCount = 0;

int lcdBacklightOnCounter = 1;0;


long secondsOfHour = 0;

const int secondsPerHour = 3600;  // usedset to reset60 esp32for everydebugging

24const hoursint hoursPerDay = 24;



float currentIndoorTemperaturecurrentBasementTemperature = 0;
float currentIndoorHumiditycurrentUpstairsTemperature = 0;
float currentOutdoorTemperature = 0;

float currentOutdoorHumiditycurrentBasementHumidity = 0;
Stringfloat payloadcurrentUpstairsHumidity = "";0;
intfloat httpCodecurrentOutdoorHumidity = 0;

bool wifiConnected = false;
bool wifiPaused = false;
int wifiPausedTick = 0;
bool wifiSleeping = false;
bool debug = false;  // when true server does not update
bool serverFailed = false;
int wifiConnectionAttempts = 0;

HTTPClient http;


// TODO; remove anything to do with temperature

DHT_Unified dhtIndoor(indoorTemperaturePin,dhtBasement(basementTemperaturePin, DHT11);
DHT_Unified dhtUpstairs(upstairsTemperaturePin, DHT11);
DHT_Unified dhtOutdoor(outdoorTemperaturePin, DHT22);

Adafruit_ADS1115 ads;

LiquidCrystal_I2C lcd_i2c(0x27, 16, 2);

sensors_event_t basementSensorEvent;
sensor_t basementSensor;
sensors_event_t upstairsSensorEvent;
sensor_t upstairsSensor;
sensors_event_t outdoorSensorEvent;
sensor_t outdoorSensor;


void setup() {

  Serial.begin(115200);  // this needs to match the value in the serial monitor

  //Init EEPROM
  EEPROM.begin(EEPROM_SIZE);

  pinMode(buttonPin, INPUT);

  delay(2000);

  dhtIndoor.//dhtBasement.begin();
  dhtUpstairs.begin();
  // Print temperature sensor details.
  sensor_t indoorSensor;
  dhtIndoor.temperature().getSensor( & indoorSensor);
  // Print humidity sensor details.
  dhtIndoor.humidity().getSensor( & indoorSensor);

  dhtOutdoor.begin();

  // Print temperature sensor details.
  sensor_t dhtOutdoor;
  dhtIndoor.dhtBasement.temperature().getSensor( & dhtOutdoor)basementSensor);
  // Print humidity sensor details.
  dhtIndoor.dhtBasement.humidity().getSensor(&basementSensor);

  dhtUpstairs.temperature().getSensor(&upstairsSensor);
  dhtOutdoor)dhtUpstairs.humidity().getSensor(&upstairsSensor);

  //dhtOutdoor.temperature().getSensor(&outdoorSensor);
  //dhtOutdoor.humidity().getSensor(&outdoorSensor);

  getSensorReadings();

  EEPROM.begin(EEPROM_SIZE);

  activeConnection = EEPROM.read(eepromActiveConnection);

  Serial.print("eepromActiveConnection :");
  Serial.println(activeConnection);

  if (isnan(activeConnection)) {
    activeConnection = 1;
  }

  if(if (activeConnection == 0) {
    activeConnection = 1;
  }

  Serial.print("activeConnection: ");
  Serial.println(activeConnection);

  float pingData = EEPROM.readFloat(eepromPingsAddress);
  if (isnan(pingData)) {
    pingData = 0;
  }
  totalServerPings = pingData;
  EEPROM.end();
  EEPROM.begin(EEPROM_SIZE);
  float pingFailData = EEPROM.readFloat(eepromFailedPingsAddress);
  if (isnan(pingFailData)) {
    pingFailData = 0;
  }
  totalServerPingFails = pingFailData;
  EEPROM.end();

  if(if (!connectToWiFi()) {

    delay(2000);
    WiFi.disconnect();
    delay(1000);


    if(if (activeConnection == 1) {
      activeConnection = 2;
    } else {
      activeConnection = 1;
    }
    connectToWiFi();
  }
  
  if (!ads.begin()) {
    Serial.println("Failed to initialize ADS.");
    while (1);
  }

  ads.setGain(GAIN_FOUR);

  lcd_i2c.init();
  lcd_i2c.backlight();
}


void loop() {

  secondsOfHour ++;= pulseRate;

  // after 24 hours reset this integer
  if(if (secondsOfHour > 86400) {
    secondsOfHour = 1;
    // Serial.println("restarting :");
    ESP.restart();
  }

  if(if (lcdBacklightOnCounter == 0) {
    // lcd_i2c.noBacklight();
  } else {
    // if lcb backlight counter is over zero it is active so increment
    lcdBacklightOnCounter +lcdBacklightOnCounter++;
    // turn off backlight after 1800 -300 5 mintes of inactivity
    if(if (lcdBacklightOnCounter >% 1800)300 == 0) {
      //  lcd_i2c.noBacklight();
      lcdBacklightOnCounter = 0;
    }
  }

  buttonState = digitalRead(buttonPin);

  if (buttonState == HIGH) {
    lcd_i2c.clear();
    lcd_i2c.backlight();

    if(if (lcdBacklightOnCounter == 0) {
      displayMode = 1;
    } else {
      displayMode++;
      if (displayMode > 2) {
        displayMode = 1;
      }
    }
    lcdBacklightOnCounter = 1;
  }

  sensors_event_t indoorSensorEvent;basementSensorEvent;

  //////////////////////////////
  // TEMPERATURE AND HUMIDITY

  // no need to read the temperature every second
  if (temperatureLoopCount % 10 == 0) {
    dhtIndoor.temperature().getEvent( & indoorSensorEvent);
    if (isnan(indoorSensorEvent.temperature)) {
      Serial.println(F("Error reading indoor temperature!")getSensorReadings();
    } else {
      currentIndoorTemperature = indoorSensorEvent.temperature;
    }
    dhtIndoor.humidity().getEvent( & indoorSensorEvent);
    if (isnan(indoorSensorEvent.relative_humidity)) {
      Serial.println(F("Error reading indoor humidity!"));
    } else {
      currentIndoorHumidity = indoorSensorEvent.relative_humidity;
    }
  }

  sensors_event_t outdoorSensorEvent;

  // no need to read the temperature every second
  if (temperatureLoopCount % 10 == 0) {
    dhtOutdoor.temperature().getEvent( & outdoorSensorEvent);
    if (isnan(outdoorSensorEvent.temperature)) {
      Serial.println(F("Error reading outdoor temperature!"));
    } else {
      currentOutdoorTemperature = outdoorSensorEvent.temperature;

    }
    dhtOutdoor.humidity().getEvent( & outdoorSensorEvent);
    if (isnan(outdoorSensorEvent.relative_humidity)) {
      Serial.println(F("Error reading outdoor humidity!"));
    } else {
      currentOutdoorHumidity = outdoorSensorEvent.relative_humidity;
    }
  }


  String temperatureText = "IT:T U:" + String(currentIndoorTemperature,currentUpstairsTemperature, 0) + "c OT:B:" + String(currentBasementTemperature, 0) + " E:" + String(currentOutdoorTemperature, 0) + "c";
  String humidifyText = "IH:H U: " + String(currentIndoorHumidity,currentUpstairsHumidity, 0) + " OH:B:" + String(currentBasementHumidity, 0) + " E:" + String(currentOutdoorHumidity, 0) + "";

  if (wifiPaused) {
    if (wifiPausedTick > 60 && wifiConnectionAttempts == 0) {  // wait 1 minute before attempting to reconnect
      wifiPaused = false;
      wifiPausedTick = 0;
      connectToWiFi();
    } else {
      wifiPausedTick++;
    }
  }

  /////////////////////
  // SERVER RELAY


  if (!debug && (temperatureLoopCount >= samplesPerReading)temperatureSamplesPerReading) || serverFailed) {
    temperatureLoopCount = 0;

    setWifiSleepMode(false);

    delay(2000);
    
    // do a heartbeat check to see if we are online...
    http.begin(heartbeatUrl);
    httpCode = http.GET();
    if (!httpCode > 0) {
      // wifi may not be alive yet so wait 3 seconds
      delay(3000);
    }
     
    String recordedIndoorTemperaturerecordedUpstairsTemperature = String(currentIndoorTemperature,currentUpstairsTemperature, 1);
    String recordedIndoorHumidityrecordedUpstairsHumidity = String(currentIndoorHumidity,currentUpstairsHumidity, 1);
    String recordedBasementTemperature = String(currentBasementTemperature, 1);
    String recordedBasementHumidity = String(currentBasementHumidity, 1);
    String recordedOutdoorTemperature = String(currentOutdoorTemperature, 1);
    String recordedOutdoorHumidity = String(currentOutdoorHumidity, 1);

    recordedIndoorTemperature.recordedBasementTemperature.trim();
    recordedIndoorHumidity.recordedBasementHumidity.trim();
    recordedOutdoorTemperature.trim();
    recordedOutdoorHumidity.trim();

    temperatureLoopCount = 0;
    Serial.println("repaying temperature: ");
    Serial.println(temperatureLoopCount)setWifiSleepMode(false);
    http.begin(temperatureUrl + recordedIndoorTemperaturerecordedBasementTemperature + "," + recordedIndoorHumidityrecordedBasementHumidity + "," + recordedUpstairsTemperature + "," + recordedUpstairsHumidity + "," + recordedOutdoorTemperature + "," + recordedOutdoorHumidity);
    int httpCode = http.GET();
    if (httpCode > 0) {
      String payload = http.getString();
      Serial.println("HTTP Response: " + payload);
      recordPingSucces();
    } else {
      recordPingFailure();
    }
    http.end();

    setWifiSleepMode(true);
  }

  temperatureLoopCount++;


  ////////////////////////////
  // LCD DISPLAY

  switch (displayMode) {
    case 1:
      temperatureText.trim();
      humidifyText.trim();

      if(if (prevTemperatureText != temperatureText || prevHumidifyText != humidifyText) {
        lcd_i2c.clear();
      }

      prevTemperatureText = temperatureText;
      prevHumidifyText = humidifyText;

      lcd_i2c.setCursor(0, 0);
      lcd_i2c.print(temperatureText);
      lcd_i2c.setCursor(0, 1);
      lcd_i2c.print(humidifyText);
      break;

    case 2:
      String connectivityText1 = "";
      if(if (wifiConnected) {
        connectivityText1 += "WF ON";
      }
      if(if (wifiConnected && !serverFailed) {
        connectivityText1 += " SERVER ON";
      }

      String serverPings = String(totalServerPings, 0);
      serverPings.trim();

      String serverPingFails = String(totalServerPingFails, 0);
      serverPingFails.trim();

      String connectivityText2 = serverPings + "/" + serverPingFails + " PINGS";

      connectivityText1.trim();
      connectivityText2.trim();

      if(if (prevConnectivityText1 != connectivityText1 || prevConnectivityText2 != connectivityText2) {
        lcd_i2c.clear();
      }

      prevConnectivityText1 = connectivityText1;
      prevConnectivityText2 = connectivityText2;

      lcd_i2c.setCursor(0, 0);
      lcd_i2c.print(connectivityText1);
      lcd_i2c.setCursor(0, 1);
      lcd_i2c.print(connectivityText2);
      break;
  }

  delay(pulseRate)pulseRate * 1000);
}

/**
 * connect to wifi
 */
bool connectToWiFi() {

  String activeSsid = "";
  String activePassword = "";

  if(if (activeConnection == 1) {
    activeSsid = ssid1;
    activePassword=activePassword = password1;
  } else if(if (activeConnection == 2) {
    activeSsid = ssid2;
    activePassword=activePassword = password2;
  }

  Serial.print("Connecting to WiFi: ");
  Serial.println(activeSsid);


  WiFi.begin(activeSsid, activePassword);

  while (WiFi.status() != WL_CONNECTED && wifiConnectionAttempts < 20) {
    delay(500);
    Serial.print(".");
    wifiConnectionAttempts++;
  }

  wifiConnectionAttempts = 0;

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\nConnected to WiFi");
    Serial.print("IP Address: ");
    Serial.println(WiFi.localIP());
    wifiConnected = true;

    EEPROM.begin(EEPROM_SIZE);
    EEPROM.write(eepromActiveConnection, activeConnection);
    EEPROM.commit();
    EEPROM.end();

    Serial.print("set activeConnection to : ");
    Serial.println(activeConnection);


    return true;

  } else {
    Serial.println("Connection failed. Trying alternative");
    return false;
  }
}



/**
 * set wifi sleep mode between data relays to conserve energy
 * @param sleepMode - if true set wifi card to sleep to conserve energy
 */
void setWifiSleepMode(bool sleepMode) {

  wifiSleeping = sleepMode;

  if (sleepMode) {
    WiFi.disconnect();
    WiFi.setSleep(true);
    delay(1000);
    Serial.print("sleep wifi status: ");
    Serial.println(wl_status_to_string(WiFi.status()));
  } else {
    WiFi.setSleep(false);
    WiFi.reconnect();
    delay(1000);
    Serial.print("awaken wifi status: ");
    Serial.println(wl_status_to_string(WiFi.status()));
    // Check if the connection is still active. if not trigger wait for it to come back online
    if (WiFi.status() != WL_CONNECTED && !wifiPaused) {
      Serial.println("Connection lost. Attempting to reconnect in 1 minute ...");
      WiFi.disconnect();
      wifiPaused = true;
      wifiConnected = false;
      connectToWiFi();
    }
  }
}

/**
 * record server ping success in long term memory
 */
void recordPingSucces() {
  totalServerPings++;
  EEPROM.begin(EEPROM_SIZE);
  EEPROM.writeFloat(eepromPingsAddress, totalServerPings);
  EEPROM.commit();
  EEPROM.end();
  wifiConnected = true;
  serverFailed = false;
}

/**
 * record server ping fails in long term memory
 */
void recordPingFailure() {
  totalServerPingFails++;
  EEPROM.begin(EEPROM_SIZE);
  EEPROM.writeFloat(eepromFailedPingsAddress, totalServerPingFails);
  EEPROM.commit();
  EEPROM.end();
  wifiConnected = false;
  serverFailed = true;
}

/**
 * ESP32 wifi card statuses
 * @param status
 * @return string
 */
String wl_status_to_string(wl_status_t status) {

  String response = "";

  switch (status) {
    case WL_NO_SHIELD:
      response = "WL_NO_SHIELD";
      break;
    case WL_IDLE_STATUS:
      response = "WL_IDLE_STATUS";
      break;
    case WL_NO_SSID_AVAIL:
      response = "WL_NO_SSID_AVAIL";
      break;
    case WL_SCAN_COMPLETED:
      response = "WL_SCAN_COMPLETED";
      break;
    case WL_CONNECTED:
      response = "WL_CONNECTED";
      break;
    case WL_CONNECT_FAILED:
      response = "WL_CONNECT_FAILED";
      break;
    case WL_CONNECTION_LOST:
      response = "WL_CONNECTION_LOST";
      break;
    case WL_DISCONNECTED:
      response = "WL_DISCONNECTED";
      break;
  }

  return response;
}









void getSensorReadings() {


  float newTempReading;
  float newHumidityReading;


  // BASEMENT

  /*
  dhtBasement.temperature().getEvent(&basementSensorEvent);
  if (isnan(basementSensorEvent.temperature)) {
    Serial.println(F("Error reading basement temperature!"));
  } else {
    newTempReading = basementSensorEvent.temperature;
    if (newTempReading > -10 && newTempReading < 50) {
      currentBasementTemperature = basementSensorEvent.temperature;
    }
    Serial.print("basement temp: ");
    Serial.println(currentBasementTemperature);
  }


  dhtBasement.humidity().getEvent(&basementSensorEvent);
  if (isnan(basementSensorEvent.relative_humidity)) {
    Serial.println(F("Error reading basement humidity!"));
  } else {

    newHumidityReading = basementSensorEvent.relative_humidity;
    if (newHumidityReading > -10 && newHumidityReading < 100) {
      currentBasementHumidity = basementSensorEvent.relative_humidity;
    }

    Serial.print("basement humidity: ");
    Serial.println(currentBasementHumidity);
  }

*/


  // UPSTAIRS
  dhtUpstairs.temperature().getEvent(&upstairsSensorEvent);
  if (isnan(upstairsSensorEvent.temperature)) {
    Serial.println(F("Error reading upstairs temperature!"));
  } else {
    newTempReading = upstairsSensorEvent.temperature;
    if (newTempReading > -10 && newTempReading < 50) {
      currentUpstairsTemperature = upstairsSensorEvent.temperature;
    }
    Serial.print("upstairs temp: ");
    Serial.println(currentUpstairsTemperature);
  }


  dhtUpstairs.humidity().getEvent(&upstairsSensorEvent);
  if (isnan(upstairsSensorEvent.relative_humidity)) {
    Serial.println(F("Error reading upstairs humidity!"));
  } else {

    newHumidityReading = upstairsSensorEvent.relative_humidity;
    if (newHumidityReading > -10 && newHumidityReading < 100) {
      currentUpstairsHumidity = upstairsSensorEvent.relative_humidity;
    }

    Serial.print("upstairs humidity: ");
    Serial.println(currentUpstairsHumidity);
  }



  /*


  // OUTDOOR

  dhtOutdoor.temperature().getEvent(&outdoorSensorEvent);
  if (isnan(outdoorSensorEvent.temperature)) {
    Serial.println(F("Error reading outdoor temperature!"));
  } else {
    newTempReading = outdoorSensorEvent.temperature;
    if (newTempReading > -10 && newTempReading < 50) {
      currentOutdoorTemperature = outdoorSensorEvent.temperature;
    }
    Serial.print("outdoor temp: ");
    Serial.println(currentOutdoorTemperature);
  }


  dhtOutdoor.humidity().getEvent(&outdoorSensorEvent);
  if (isnan(outdoorSensorEvent.relative_humidity)) {
    Serial.println(F("Error reading outdoor humidity!"));
  } else {

    newHumidityReading = outdoorSensorEvent.relative_humidity;
    if (newHumidityReading > -10 && newHumidityReading < 100) {
      currentOutdoorHumidity = outdoorSensorEvent.relative_humidity;
    }

    Serial.print("outdoor humidity: ");
    Serial.println(currentOutdoorHumidity);
  }

  */
}