Hans ScharlerDouglas Mawrey
Published © MIT

Smart Humidity Sensor - ThingSpeak, MATLAB, and IFTTT

A smart humidity sensor that uses outdoor temperature to determine the ideal indoor humidity and inform the user about the room's comfort.

EasyFull instructions provided4 hours3,738
Smart Humidity Sensor - ThingSpeak, MATLAB, and IFTTT

Things used in this project

Hardware components

DHT11 Temperature & Humidity Sensor
DHT11 Temperature & Humidity Sensor
×1
NodeMCU ESP8266 Breakout Board
NodeMCU ESP8266 Breakout Board
×1

Software apps and online services

ThingSpeak API
ThingSpeak API
Maker service
IFTTT Maker service
MATLAB
MATLAB

Story

Read more

Schematics

NodeMCU and DHT11 - 4 PIN

The schematic for the 4 pin version of the DHT11 with a 10 kΩ pull up resistor.
Nodemcu dht11 4pin bb bo5sbtstea

NodeMCU and DHT11 - 3 PIN

The schematic for the 3 pin version of the DHT11 with built in pull up resistor.
Nodemcu dht11 bb 0fht5toiz4

Code

Monitor

Arduino
The Arduino code used to write temperature and humidity data to ThingSpeak.
#include <ThingSpeak.h>
#include <ESP8266WiFi.h>
#include <DHT.h>

#define DHTPIN 4      // DHT Sensor connected to digital pin 2.
#define DHTTYPE DHT11 // Type of DHT sensor.

char ssid[] = ""; // Change this to your network SSID (name).
char pass[] = ""; // Change this your network password.
long channelID = 0; // Change this to your channel ID.
char writeAPIKey[] = ""; // Change this to your channel Write API Key.

const unsigned long postingInterval = 20L * 1000L; // Post data every 20 seconds.

WiFiClient client;        // Initialize the Wifi client library.
DHT dht(DHTPIN, DHTTYPE); // Initialize DHT sensor.

int connectWifi();
void updateDHT();
unsigned long lastConnectionTime = 0;
float dhtTemp = 0;
float dhtHumidity = 0;

void setup()
{
  Serial.begin(115200);
  Serial.println("Start");

  connectWifi();

  Serial.println("Connected to wifi");
  ThingSpeak.begin(client);
}

void loop()
{
  // If interval time has passed since the last connection, write data to ThingSpeak.
  if (millis() - lastConnectionTime > postingInterval)
  {
    updateDHT();

    ThingSpeak.setField(1, dhtHumidity);
    ThingSpeak.setField(2, dhtTemp);
    ThingSpeak.writeFields(channelID, writeAPIKey);

    lastConnectionTime = millis();
  }
}

// Attempts a connection to WiFi network and repeats until successful.
int connectWifi()
{
  WiFi.begin(ssid, pass);
  while (WiFi.status() != WL_CONNECTED)
  {
    Serial.println("Connecting to WiFi");
    delay(2500);
  }
  Serial.println("Connected");
}

// Get the most recent readings for temperature and humidity.
void updateDHT()
{
  dhtTemp = dht.readTemperature(true);
  dhtHumidity = dht.readHumidity();
  Serial.println(dhtTemp);
  Serial.println(dhtHumidity);
}

Analysis

MATLAB
The ThingSpeak MATLAB analysis used to calculate the ideal indoor humidity with respect to outdoor temperature, determine the distance between the actual and ideal humidity, and assign a comfort level to the difference.
%% Channel Info %%
% Channel to read indoor humidity
indoorChannelID = [];
indoorChannelReadKey = '';

% MathWorks weather station to read outdoor temperature
outdoorChannelID = 12397;
outdoorChannelReadKey = ''; % Not needed for public channel

% Channel to read & post humidity difference
diffChannelID = [];
diffChannelReadKey = '';
diffChannelWriteKey = '';


%% Fetch Data %%
indoorData = thingSpeakRead(indoorChannelID, ...
                            'ReadKey', indoorChannelReadKey, ...
                            'NumMinutes', 5);
outdoorData = thingSpeakRead(outdoorChannelID, ...
                            'ReadKey', outdoorChannelReadKey, ...
                            'NumMinutes', 5, ...
                            'Fields', 4); % Only fetch temperature
diffData = thingSpeakRead(diffChannelID, ...
                            'ReadKey', diffChannelReadKey, ...
                            'NumMinutes', 20, ...
                            'Fields', 1); % Only fetch RH difference

% using webread because thingSpeakRead does not give access to the channel metadata
indoorChannelData = webread(strcat('https://api.thingspeak.com/channels/', ...
                                    num2str(indoorChannelID), ...
                                    '/feeds.json?metadata=true&api_key=', ...
                                    indoorChannelReadKey));
diffChannelData = webread(strcat('https://api.thingspeak.com/channels/', ...
                                    num2str(diffChannelID), ...
                                    '/feeds.json?metadata=true&api_key=', ...
                                    diffChannelReadKey));


%% Prepare Data %%
humidityLookup = cell2mat(textscan(indoorChannelData.channel.metadata, '%f, %f'));
stateLookup = textscan(diffChannelData.channel.metadata, '%f %q');


%% Use Data %%
curHumidity = mean(indoorData(:,1));
curTempIn = mean(indoorData(:,2));
curTempOut = mean(outdoorData(:,1));

% Determine the target humidity using a polynomial fit over the lookup data
lookupFit = polyfit(humidityLookup(:, 1), humidityLookup(:, 2), length(humidityLookup) - 1);
optimalHumidity = polyval(lookupFit, curTempOut);

humidityDiff = curHumidity - optimalHumidity;

% Add the most recent diff in the data
diffData = [diffData ; humidityDiff];
avgDiff = mean(diffData);


%% Determine Comfort Level %%
comfortTier = 0;

% loop through the lookup table and find which threshold the humidity falls under
for i = 1:length(stateLookup{1})
    comfortTier = i;
    if abs(avgDiff) < stateLookup{1}(i)
        break
    end
end

% Create a message describing the current comfort level
comfortMsg = stateLookup{2}(comfortTier);

% Append a further description if not within the lowest threshold
if comfortTier > 1
    if avgDiff > 0
        comfortMsg = strcat(comfortMsg, ' -- too humid');
    else
        comfortMsg = strcat(comfortMsg, ' -- too dry');
    end
end

% Give the comfortTier a sign to denote if it is too dry/humid
% sign of avgDiff is subtracted to make 0 the base (okay/comfortable) value
comfortTier = comfortTier * sign(avgDiff) - sign(avgDiff);

%% Publish Data %%
% Using webread because thingSpeakWrite does not allow the status field
webread(cell2mat(strcat('https://api.thingspeak.com/update?', ...
            'api_key=', diffChannelWriteKey, ...
            '&status=', comfortMsg, ...
            '&field1=', num2str(humidityDiff), ...
            '&field2=', num2str(optimalHumidity), ...
            '&field3=', num2str(comfortTier))));

Alert

MATLAB
The ThingSpeak MATLAB analysis used to trigger an IFTTT Applet at defined intervals if the humidity falls out of the desired range.
%% Configuration %%
alertIntervals = [hours(0.25) hours(1) hours(3) hours(6) hours(12) hours(24)];
%% Channel Info %%
% Channel to read humidity difference
channelID = [];
channelReadKey = '';
% Event name and key for the IFTTT WebHooks service
makerEvent = 'humidity_alert';
makerKey = '';


%% Read Data %%
comfortData = thingSpeakRead(channelID, 'ReadKey', channelReadKey, ...
                                'NumMinutes', minutes(alertIntervals(end)), ...
                                'Fields', 3, ...
                                'OutputFormat', 'table');

currState = comfortData.ComfortTier(end);
lastStateChange = [];


%% Use Data %%
% Determine when the last change in state occurred
for i = height(comfortData):-1:1
   comfortTier = comfortData.ComfortTier(i);
   lastStateChange = i;
   
    if (sign(comfortTier) ~= sign(currState) || comfortTier == 0)
       break
   end
end
lastChangeTime = comfortData.Timestamps(lastStateChange);
timeSinceChange = datetime('now') - lastChangeTime;

% Create a message for the state report
stateMsg = '';
if sign(currState) > 0
    stateMsg = 'humid';
elseif sign(currState) < 0
    stateMsg = 'dry';
end


%% Send Alert %%
% Determine if we are close enough to any of the alert intervals to receive an update
alertCountdowns = alertIntervals - timeSinceChange;
% Send notification if we are within 5 minutes following an alert interval
if sum(alertCountdowns <= 0 & alertCountdowns > -1 * minutes(5)) > 0
    webwrite(strcat('https://maker.ifttt.com/trigger/', makerEvent, ...
                '/with/key/', makerKey), ...
                'value1', stateMsg, ...
                'value2', char(timeSinceChange, 'hh:mm'));
end

GitHub Repository

Credits

Hans Scharler

Hans Scharler

11 projects • 38 followers
IoT Engineer, Maker - I have a toaster that has been tweeting since 2008.
Contact
Douglas Mawrey

Douglas Mawrey

1 project • 6 followers
Programmer and student at Northeastern University.
Contact

Comments