Monitoring Room Temperature and Humidity with An Arduino Sensor

By Matt

Since we have point source heating we want to monitor what the temperatures of individuals rooms are that are remote from the heat source. We have a dedicated Mitsubishi ductless minisplit on each level of the house (MUZFH18NAH outdoor unit, MSZFH18NA indoor unit), with the downstairs minisplit mainly used for heating and the upstairs unit used for cooling. Our backup heat is Cadet EnergyPlus 1600-Watt 120/240-Volt In-Wall Electric Wall Heaters in 3 bedrooms, bathroom, laundry, basement (total 6) at 240 V for when the weather is really cold (<0F).

There is the potential that the heated air from the mini-split located in the great room of the main floor will not be sufficient to keep the upstairs bedrooms/bathroom sufficiently warm. For example, on Nov 11, 2018 the temperature outside was 20F and the minisplit is keeping the great room it occupies at 68F, while the upstairs rooms are 65-67F. For a quick glance we can use the Electric Wall Heaters to check the temperatures as they have room temperature displays, but what if we wanted to have a running spreadsheet of temperatures through the heating season to see what temperature we should put the main level minisplit at to maintain comfortable upstairs temperatures. We also want to monitor what temperature the basement maintains (there is a quarter basement which contains our utilities) as it does not have easy access to the airflow (since heat rises) from the main level minisplit.

In addition, humidity can be an issue in a well sealed house especially since we were able to convince the city that we did not want or need exterior venting bathroom or kitchen fans. So we would like to monitor both temperature and humidity.

Rather than purchasing a fancy off the shelve monitor which are expensive when multiplied by many rooms I decided to assemble my own Arduino devices, which permits me to customize the output and buy the parts from American sellers on Amazon or E-bay or if one wants to really reduce cost buy parts from China through e-bay or AliExpress. Since I am not an Arduino expert, I first wanted to buy a pre-assembled kit so that the first device I built was assured to work and then I could start buying individual components later.

I purchased a UCTRONICS ESP8266 Weather Station Starter Kit from Amazon for about 20$. It includes a ESP8266 module (a micro arduino board with wifi), a DHT11 (the temperature and humidity sensor), an OLED display (to display results if not using the Wifi) and a bunch of wires and cords to code, connect and power the components. The starter kit also comes with an excellent manual to walk the user through the assembly and coding of the components.

ESP8266 Weather Station

 

The kit comes with downloadable arduino programs, two of which are relevant to what I want in my temperature sensor. One program contacts a local weather service to get current conditions and forecast and then display them on the OLED display. The other program uses a DHT11 probe to read temperature and humidity and sends the data every few minutes to Thingspeak to be stored and graphed. What I wanted was a measure of temperature and humidity of both the room the sensor is in and also what the local outdoor conditions were, and have both data uploaded to Thingspeak and displayed on the OLED display. So I had to mashup the two programs along with bits and pieces from other internet obtained similar projects. 

Initially I directly attached the pins of the ESP8266 directly to the pins of the DHT11. It looks something like this. 

First Temperature monitor build

This worked fine, but was going to be unwieldy once the OLED display and was attached similarly along with the USB power cord. So I switched to a breadboard so everything was on the same platform and would sit nicely on a flat surface. This is what the final product looks like.

Final Homemade Internet of Things Temp/Humidity Sensor

Here is what the data looks like once uploaded and graphed in Thingspeak.

Two weeks of data for the basement of our NetZero House

Over a two week period the temperature outside varied from a low of 0 degrees to a high of 40 degrees, and the basement maintained a narrow band of 50 to 55 degrees and a relative humidity of 60 to 70%. This should be good conditions for storage of squash and the brewing of lagers. Thingspeak provides iframe code so that their graphs can be displayed on ones own webpage. Our current conditions are displayed here

Here is the arduino code that was uploaded to the device. Wifi Passwords and Thingspeak codes replaced with XXX.

#include <Wire.h>  // Only needed for Arduino 1.6.5 and earlier
#include "SSD1306.h" // alias for `#include "SSD1306Wire.h"
#include "OLEDDisplayUi.h"
#include <ESPWiFi.h>
#include <ESP8266WiFi.h> // For Wifi
//#include <ESPHTTPClient.h> // library so we can make HTTP requests using a very simple interface.
#include <JsonListener.h> // Used to pull data from on line weather
#include "OpenWeatherMapCurrent.h"
#include <time.h>

// initiate the client
OpenWeatherMapCurrent client;

// See https://docs.thingpulse.com/how-tos/openweathermap-key/
String OPEN_WEATHER_MAP_APP_ID = "XXX";
/*
Go to https://openweathermap.org/find?q= and search for a location. Go through the
result set and select the entry closest to the actual location you want to display 
data for. It'll be a URL like https://openweathermap.org/city/2657896. The number
at the end is what you assign to the constant below.
 */
String OPEN_WEATHER_MAP_LOCATION_ID = "5043473";
/*
Arabic - ar, Bulgarian - bg, Catalan - ca, Czech - cz, German - de, Greek - el,
English - en, Persian (Farsi) - fa, Finnish - fi, French - fr, Galician - gl,
Croatian - hr, Hungarian - hu, Italian - it, Japanese - ja, Korean - kr,
Latvian - la, Lithuanian - lt, Macedonian - mk, Dutch - nl, Polish - pl,
Portuguese - pt, Romanian - ro, Russian - ru, Swedish - se, Slovak - sk,
Slovenian - sl, Spanish - es, Turkish - tr, Ukrainian - ua, Vietnamese - vi,
Chinese Simplified - zh_cn, Chinese Traditional - zh_tw.
*/
String OPEN_WEATHER_MAP_LANGUAGE = "en";
boolean IS_METRIC = false;

// Update every 600 seconds = 10 minutes. Min with Thingspeak is ~20 seconds
const int UPDATE_INTERVAL_SECONDS = 600;

// Display Settings
const int I2C_DISPLAY_ADDRESS = 0x3c;
#if defined(ESP8266)
const int SDA_PIN = D1;
const int SDC_PIN = D2;
#else
const int SDA_PIN = 1; //D1;
const int SDC_PIN = 2; //D2;
#endif

// Initialize the oled display for address 0x3c
 SSD1306Wire     display(I2C_DISPLAY_ADDRESS, SDA_PIN, SDC_PIN);
 OLEDDisplayUi   ui( &display );


/* DHT22 */
#include "DHT.h"
#define DHTPIN D7  
#define DHTTYPE DHT22 
DHT dht(DHTPIN, DHTTYPE);
int INTEMP = 0;
int INHUM = 0;

//const boolean IS_METRIC = false;

const char* ssid     = "XXX"; // Name of Wifi
const char* password = "XXX"; // Password of Wifi
const char* host = "api.thingspeak.com"; // Thingspeak API address

// Thinkspeak API Keys, Uncomment Key for particular Temp Logger
// Upstairs Bedroom
const char* THINGSPEAK_API_KEY = "XXX";
// Basement
//const char* THINGSPEAK_API_KEY = "XXX";
// Main Room
//const char* THINGSPEAK_API_KEY = "XXX";

// Correct Temperature Monitor by integer
const int CORRECTION = 0;



void setup() 
{
  Serial.begin(115200);

  // initialize display
  display.init();
  display.clear();
  display.display();
  display.flipScreenVertically();
  
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
}

  Serial.println("");
  Serial.println("WiFi connected");  
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());


}

void loop() 
{
  display.clear();
  drawDHT(); 
  delay (2000);
}

/***************************************************
* Get indoor Temp/Hum data
****************************************************/


/*****************************************
 * Get Data from Openweathermap
 */




/***************************************************
* Draw Indoor Page and Print Conditions to Come Out
****************************************************/
void drawDHT() 
{

  // read values from the sensor
    
    float tempIni = INTEMP;
    float humIni = INHUM;
    float INTEMP = dht.readTemperature(!IS_METRIC);
    float INHUM = dht.readHumidity();
    float CORINTEMP = INTEMP + CORRECTION;
    
    
    if (isnan(INTEMP) || isnan(INHUM))   // Check if any reads failed and exit early (to try again).
  {
    Serial.println("Failed to read from DHT sensor!");
    INTEMP = tempIni;
    INHUM = humIni;
    return;
  }
    
    
    Serial.print("Temperature: ");
    Serial.print(INTEMP);
    Serial.print("Corrected Temperature: ");
    Serial.print(CORINTEMP);
    Serial.print(" Humidity: ");
    Serial.println(INHUM);
  
  
  
  
  Serial.println();
  Serial.println("\n\nNext Loop-Step: " + String(millis()) + ":");

  OpenWeatherMapCurrentData data;
  client.setLanguage(OPEN_WEATHER_MAP_LANGUAGE);
  client.setMetric(IS_METRIC);
  client.updateCurrentById(&data, OPEN_WEATHER_MAP_APP_ID, OPEN_WEATHER_MAP_LOCATION_ID);

  Serial.println("------------------------------------");

  // "lon": 8.54, float lon;
  Serial.printf("lon: %f\n", data.lon);
  // "lat": 47.37 float lat;
  Serial.printf("lat: %f\n", data.lat);
  // "id": 521, weatherId weatherId;
  Serial.printf("weatherId: %d\n", data.weatherId);
  // "main": "Rain", String main;
  Serial.printf("main: %s\n", data.main.c_str());
  // "description": "shower rain", String description;
  Serial.printf("description: %s\n", data.description.c_str());
  // "icon": "09d" String icon; String iconMeteoCon;
  Serial.printf("icon: %s\n", data.icon.c_str());
  Serial.printf("iconMeteoCon: %s\n", data.iconMeteoCon.c_str());
  // "temp": 290.56, float temp;
  Serial.printf("temp: %f\n", data.temp);
  // "pressure": 1013, uint16_t pressure;
  Serial.printf("pressure: %d\n", data.pressure);
  // "humidity": 87, uint8_t humidity;
  Serial.printf("humidity: %d\n", data.humidity);
  // "temp_min": 289.15, float tempMin;
  Serial.printf("tempMin: %f\n", data.tempMin);
  // "temp_max": 292.15 float tempMax;
  Serial.printf("tempMax: %f\n", data.tempMax);
  // "wind": {"speed": 1.5}, float windSpeed;
  Serial.printf("windSpeed: %f\n", data.windSpeed);
  // "wind": {"deg": 1.5}, float windDeg;
  Serial.printf("windDeg: %f\n", data.windDeg);
  // "clouds": {"all": 90}, uint8_t clouds;
  Serial.printf("clouds: %d\n", data.clouds);
  // "dt": 1527015000, uint64_t observationTime;
  time_t time = data.observationTime;
  Serial.printf("observationTime: %d, full date: %s", data.observationTime, ctime(&time));
  // "country": "CH", String country;
  Serial.printf("country: %s\n", data.country.c_str());
  // "sunrise": 1526960448, uint32_t sunrise;
  time = data.sunrise;
  Serial.printf("sunrise: %d, full date: %s", data.sunrise, ctime(&time));
  // "sunset": 1527015901 uint32_t sunset;
  time = data.sunset;
  Serial.printf("sunset: %d, full date: %s", data.sunset, ctime(&time));

  // "name": "Zurich", String cityName;
  Serial.printf("cityName: %s\n", data.cityName.c_str());
  Serial.println();
  Serial.println("---------------------------------------------------/\n");

  int x=0;
  int y=0;

  display.setFont(ArialMT_Plain_10);
  display.setTextAlignment(TEXT_ALIGN_LEFT);
  display.drawString(0 + x, 4 + y, "Hum");
  
  display.setFont(ArialMT_Plain_10);
  display.setTextAlignment(TEXT_ALIGN_LEFT);
  display.drawString(48 + x,15+y, "IN");

  display.setFont(ArialMT_Plain_16);
  int INHUM2 = round(INHUM);
  String hum = String(INHUM2) + "%";
  display.drawString(0 + x, 15 + y, hum);
  int humWidth = display.getStringWidth(hum);

  display.setFont(ArialMT_Plain_10);
  display.setTextAlignment(TEXT_ALIGN_LEFT);
  display.drawString(95 + x, 4 + y, "Temp");

  display.setFont(ArialMT_Plain_16);
  String intemp2 = String(INTEMP) + "°F";
  display.drawString(75 + x, 15 + y, intemp2);
  int tempWidth = display.getStringWidth(intemp2);

  display.setFont(ArialMT_Plain_10);
  display.setTextAlignment(TEXT_ALIGN_LEFT);
  display.drawString(48 + x, 32+y, "OUT");

  display.setFont(ArialMT_Plain_16);
  String hum2 = String(data.humidity) + "%";
  display.drawString(0 + x, 42 + y, hum2);
  int humWidth2 = display.getStringWidth(hum2);

  display.setFont(ArialMT_Plain_16);
  int OUTTEMP = round(data.temp);
  String OUTTEMP2 = String(OUTTEMP) + "°F";
  display.drawString(75 + x, 42 + y, OUTTEMP2);
  int tempWidth2 = display.getStringWidth(OUTTEMP2);

    Serial.print("connecting to ");
    Serial.println(host);
    
    // Use WiFiClient class to create TCP connections
    WiFiClient client;
    const int httpPort = 80;
    if (!client.connect(host, httpPort)) {
      Serial.println("connection failed");
      return;
    }

    
    // We now create a URI for the request
    String url = "/update?api_key=";
    url += THINGSPEAK_API_KEY;
    url += "&field1=";
    url += String(INTEMP);
    url += "&field2=";
    url += String(INHUM);
    url += "&field3=";
    url += String(data.temp);
    
    Serial.print("Requesting URL: ");
    Serial.println(url);
    
    // This will send the request to the server
    client.print(String("GET ") + url + " HTTP/1.1\r\n" +
                 "Host: " + host + "\r\n" + 
                 "Connection: close\r\n\r\n");
    delay(10);
    while(!client.available()){
      delay(100);
      Serial.print(".");
    }
    // Read all the lines of the reply from server and print them to Serial
    while(client.available()){
      String line = client.readStringUntil('\r');
      Serial.print(line);
    }
    
    Serial.println();
    Serial.println("closing connection");
 
    display.display();


  // Go back to sleep. If your sensor is battery powered you might
  // want to use deep sleep here
  delay(1000 * UPDATE_INTERVAL_SECONDS);

Leave a Reply

Your email address will not be published. Required fields are marked *