Kealoha HankinsAndrew Thompson
Published © CC BY-NC-SA

Making our Dorm's Thermostat Smart

Use Particle Argons as the brains for a smart thermostat, without having to replace your thermostat!

IntermediateShowcase (no instructions)588

Things used in this project

Hardware components

DHT11 Temperature & Humidity Sensor (3 pins)
DHT11 Temperature & Humidity Sensor (3 pins)
×1
Gravity: DS18B20 Temperature Sensor (Arduino Compatible)
DFRobot Gravity: DS18B20 Temperature Sensor (Arduino Compatible)
×1
SG90 Micro-servo motor
SG90 Micro-servo motor
×1
Argon
Particle Argon
×2
Breadboard (generic)
Breadboard (generic)
×2
Jumper wires (generic)
Jumper wires (generic)
×10

Software apps and online services

Particle Build Web IDE
Particle Build Web IDE
ThingSpeak API
ThingSpeak API

Hand tools and fabrication machines

Tape, Painters Tape
Tape, Painters Tape
Tape, Duct
Tape, Duct

Story

Read more

Schematics

Argon 1 Circuit Diagram

This argon is attached to a temperature sensor and servo motor, and is used to change the temperature of the dorm thermostat

Argon 2 Circuit Diagram

This argon is attached to a dual temperature and humidity sensor and is located across the dorm from the thermostat.

Code

Code for First Argon

C/C++
This code is for the first Argon device. This Argon is responsible for reading temperature from the attached temperature sensor, publishing data to ThingSpeak, and adjusting the attached servo to change the thermostat temperature.
//=====================
//  Code for 1st Argon Device
//  Written By Andrew Thompson and Kealoha Hankins
//  For UNCC MEGR3171 IoT Project
//=====================



//The two libraries below are needed for the dallas temperature sensor
#include <OneWire.h>
#include <spark-dallas-temperature.h>

//=====================
//SERVO:    (library already built into IDE)
//Servo pin: D8
Servo servo1;
int pos = 90;   //initial position of the servo
const int defaultPos = 90;    //default position of the servo arm.
    //our servo is a 180 degree servo, so 90 degrees is right in the middle.
int setPos(String angle);   //setPos is the variable for the function to set the angle
int angleIncreaseTemp = 35;    //this is the anlge the servo needs to be to press the increase button on the thermostat
int angleDecreaseTemp = 150;     //this is the angle the servo needs to be to press the decrease button on the thermostat
double timeConstant = 2.85*1000; //this is the number of seconds * 1000 that button must be held down in order to change the temp by 1 degree on the thermostat


//=====================
//TEMPERATURE SENSOR:
//Pin 1: GND
//Pin 2: POWER
//Pin 3: D6
#define ONE_WIRE_BUS D6
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
int dsRawTemp = 0;  //this is the temp directly from the temp sensor
int dsTemp = 0; //this is the temp that will be reported

//=====================
//These variables are for getting the time from the internet and time-related actions in the code
int hours = -1; //sets the hour to be -1. If the time value is ever neagtive, this indicates a problem
int mins = -1;  //sets the minute to be -1. If the time value is ever negative, this indicates a problem
String sHour;
String sMin;
String currentTime; //create a string for getting the current time.

//=====================
//These variables control how often the program publishes data
int cycle = 3600000;    //cycle time in milliseconds that the program loops before. Changing cycle will change how often actions are performed.
int interval = 0;   //(check) this is the time in milliseconds for setting when actions are performed.
double cycleMin = cycle/60000;  //this is the cycle time in minutes
double cycleHr = cycleMin/60;   //this is the cycle time in hours
int timeSince = 0;  //this variable will be used to keep track of the time that has passed since the last check

double curTemp = 70;    //this double is for the current temperature the thermostat is set at. This is a double because our thermostat is capable of adjusting temp to the tenths place

//=====================
//these variables are for changing the cycle time. Associated with Particle functions below
int setCycleHr(String newCycle);
int setCycleMin(String newCycle);
int setTemp(String newTemp);

int calibrateTemp(String caliTemp);


int tempDiffCN = 0; //difference between the Current temp and New temp
int morningTemp = 0;
int nightTemp = 0;

//====================================================================================

void setup() 
{
servo1.attach(D8);  //tells the argon the servo is connected to pin D8
sensors.begin();    //tells the temp sensor to be ready

Time.zone(-5);  //the line sets the time zone to EST, which is 5 hours behind GMT

Particle.variable("Temperature",dsTemp);    //particle variable for the temperature
Particle.variable("Angle", pos);    //particle variable for the servo angle
Particle.variable("Time_Interval_millis", cycle);
Particle.variable("Time_Interval_mins",cycleMin);
Particle.variable("Time_Interval_hour",cycleHr);
Particle.variable("Thermostat_Temperature", curTemp);
Particle.variable("Current_Time", currentTime);

Particle.function("Set_Servo_Angle", setPos); //particle function for adjusting the servo angle over the web
Particle.function("Change_Time_Interval (hours)", setCycleHr);
Particle.function("Change_Time_Interval (minutes)", setCycleMin);
Particle.function("Change_Temperature", setTemp);
Particle.function("Calibrate_Temperature", calibrateTemp);
Particle.function("Change_Temperature", setTemp);

Particle.subscribe("Day_Temp", morningRoutine, MY_DEVICES);
Particle.subscribe("Night_Temp", nightRoutine, MY_DEVICES);
}

//====================================================================================

void loop() 
{
sensors.requestTemperatures();  //requests that the temp sensor to take the temperature
dsRawTemp = sensors.getTempFByIndex(0); //sets dsrawTemp equal to the temperature (in F)

hours = Time.hour();
sHour = String(hours);
if (hours >= 1 && hours <= 9)
{
    sHour = "0"+String(hours);
}

mins = Time.minute();
sMin = String(mins);
if (mins <=1 <= 9 )
{
    sMin = "0"+String(mins);
}
currentTime = sHour + sMin;

//the following if statmenet is used to suppress random spikes in data.
//The environement where the sensor is located will never realistically be above 100F or below 20F
if (dsRawTemp < 100 && dsRawTemp > 20)  //if dsRawTemp is less than 100 AND dsRawTemp is greater than 20...
{
    dsTemp = dsRawTemp; //sets dstemp to the value of dsRawTemp
}

if (millis() - interval > cycle)    //if the number of miliseconds since the program began minus the interval period is GEATER than the cycle period
{
    Particle.publish("Temperature_internal", String(dsTemp));
    interval = millis();    //sets interval to the number of milliseconds that have passed since the program began
}
timeSince = millis();   //sets timeSince to the number of milliseconds that have passed since the program began

cycleMin = cycle/60000; //converts the cycle time to minutes
cycleHr = cycleMin/60;  //converts the cycle time to hours

delay (500);    //waits .5 seconds
}


//====================================================================================

int setPos(String angle)    //function code for changing the servo's angle
{
    if(angle.toInt() >= 0 && angle.toInt() <= 180)
    {
        pos = angle.toInt();
        servo1.write(pos);
        return pos; //returns the angle given and end the function
    }
    return -1;  //returns -1. This implies there was a problem (such as an angle above 180 or a string that wasn't a number)
}

//====================================================================================

int setCycleHr(String newCycle)   //function for changing the cycle time (in hours) from the web
{
    if (newCycle.toInt() > 0)   //checks to see if the passed string is an INT greater than 0
    {
        cycle = newCycle.toInt()*60000*60; //since the expected time is in hours, newCycle is multiplied by 60000, and then 60, to convert back to milliseconds.
        return newCycle.toInt();    //returns the time given and ends the function.
    }
    return -1;  //if an error occured, -1 will be returned
}

//====================================================================================

int setCycleMin(String newCycle)    //function for changing the cycle time (in minutes) from the web
{
    if (newCycle.toInt() > 0)
    {
        cycle = newCycle.toInt()*60000;
        return newCycle.toInt();
    }
    return -1;  //if an error occured, -1 will be returned
}

//====================================================================================

int setTemp(String newTemp) //this function is in charge of changing the temperature in the program AND physically. Later code will be added to adjust the servo to push the buttons.
{
    if (newTemp.toFloat() >= 60 && newTemp.toFloat() <= 80)
    {
        if(newTemp.toFloat() > curTemp)
        {
            tempDiffCN = curTemp-newTemp.toFloat();
            tempDiffCN = abs(tempDiffCN);
            //finds the absolute value of the difference between the new temperature and the old temperature
            
            servo1.write(angleIncreaseTemp);
            delay(timeConstant*tempDiffCN);
            servo1.write(defaultPos);
            //sets the servo to hold down the INCREASE button long enough to raise the temperature by tempDiffCN degrees
            
            curTemp = newTemp.toFloat();    //updates the current temperature within the program
            return newTemp.toFloat();       //returns the value it was given
        }
        else if (newTemp.toFloat() < curTemp)
        {
            tempDiffCN = curTemp-newTemp.toFloat();
            tempDiffCN = abs(tempDiffCN);
            //finds the absolute value of the difference between the new temperature and the old temperature
            
            servo1.write(angleDecreaseTemp);
            delay(timeConstant*tempDiffCN);
            servo1.write(defaultPos);
            //sets the servo to hold down the DECREASE button long enough to raise the temperature by tempDiffCN degrees
            
            curTemp = newTemp.toFloat();    //updates the current temperature within the program
            return newTemp.toFloat();   //returns the value it was given
        }
        
    }
    return -1;  //if an error occured, -1 will be returned
}

//====================================================================================

int calibrateTemp(String caliTemp)  //this function is responsible for changing ONLY the temp in the program. This is useful in case the thermostat  temperature and curTemp differ.
{
    if(caliTemp.toFloat() >= 60 && caliTemp.toFloat() <= 80)
    {
        curTemp = caliTemp.toFloat();
        return caliTemp.toFloat();  //if the program runs correctly, the number entered will be returned
    }
    return -1;  //if an error occured, -1 will be returned
}

//====================================================================================

void morningRoutine(const char *topic, const char *data)    //this function is for chaing the temperature based on the daily morning routine. Does not return any value
{
    morningTemp = String(data).toInt(); //sets morning temp to the value from the Day_Temp event
    
    if(morningTemp > curTemp)   //if the morning temp is warmer than the current temp, the temp needs to be INCREASED
    {
        tempDiffCN = curTemp-morningTemp;
        tempDiffCN = abs(tempDiffCN);
            //finds the absolute value of the difference between the new temperature and the old temperature
        
        servo1.write(angleIncreaseTemp);
        delay(timeConstant*tempDiffCN);
        servo1.write(defaultPos);
            //sets the servo to hold down the INCREASE button long enough to raise the temperature by tempDiffCN degrees
        
        curTemp = morningTemp;   //updates the curret temperature within the program
    }
    
    if(morningTemp < curTemp)   //if the morning temp is cooler than the current temp, the temp needs to be DECREASED
    {
        tempDiffCN = curTemp-morningTemp;
        tempDiffCN = abs(tempDiffCN);
        //finds the absolute value of the difference between the new temperature and the old temperature
        
        servo1.write(angleDecreaseTemp);
        delay(timeConstant*tempDiffCN);
        servo1.write(defaultPos);
            //sets the servo to hold down the DECREASE button long enough to lower the temp by tempDiffCN degrees
        
        curTemp = morningTemp;   //updates the current temperature within the program
    }
}

//====================================================================================

void nightRoutine(const char *topic, const char *data)    //this function is for chaing the temperature based on the daily night routine. Does not return any value
{
    nightTemp = String(data).toInt(); //sets night temp to the value from the Night_temp event
    
    if(nightTemp > curTemp)   //if the night temp is warmer than the current temp, the temp needs to be INCREASED
    {
        tempDiffCN = curTemp-nightTemp;
        tempDiffCN = abs(tempDiffCN);
            //finds the absolute value of the difference between the new temperature and the old temperature
        
        servo1.write(angleIncreaseTemp);
        delay(timeConstant*tempDiffCN);
        servo1.write(defaultPos);
            //sets the servo to hold down the INCREASE button long enough to raise the temperature by tempDiffCN degrees
        
        curTemp = nightTemp;   //updates the curret temperature within the program
    }
    
    if(nightTemp < curTemp)   //if the night temp is cooler than the current temp, the temp needs to be DECREASED
    {
        tempDiffCN = curTemp-nightTemp;
        tempDiffCN = abs(tempDiffCN);
        //finds the absolute value of the difference between the new temperature and the old temperature
        
        servo1.write(angleDecreaseTemp);
        delay(timeConstant*tempDiffCN);
        servo1.write(defaultPos);
            //sets the servo to hold down the DECREASE button long enough to lower the temp by tempDiffCN degrees
        
        curTemp = nightTemp;   //updates the current temperature within the program
    }
}

Code for Second Argon

C/C++
This is the code for the second argon device. This argon is responsible for reading temperature and humidity data. It is also responsible for telling the other argon to adjust the thermostat at certain times throughout the day.
//=====================
//  Code for 2nd Argon Device
//  Written By Andrew Thompson and Kealoha Hankins
//  For UNCC MEGR3171 IoT Project
//=====================


#include <Adafruit_DHT.h>   //library for the DHT sensor
#define DHTPIN D6   //declares pin D6 as the DHT sensor pin
#define DHTTYPE DHT11   //delcares the DHT sensors as a type 11 sensor

DHT dhtSensor(DHTPIN,DHTTYPE);  //declares a DHT sensor, the pin its connected to, and what type of sensor it is

int rawTemp = 0;    //temp read from sensor
int temp = 0;       //temp that has been checked for abnormal readings
int rawHum = 0;     //humidity read from sensor
int hum = 0;        //humidity that has been checked for abnormal readings
int tempDiff = 0;   //the difference in the two temps from each temp sensor
int otherTemp = 0;  //the temp from the other temp sensor
//String data1;
//String data2;

//=====================
//These variables are for getting the time from the internet and time-related actions in the code
int hours = -1; //sets the hour to be -1
int mins = -1;  //sets the minute to be -1
String sHour;
String sMin;
String currentTime; //create a string for getting the current time.

//=====================
//these are for the times that the morning and night routines occur. The time is a 4 digit number. The first two digits are the hour (24hr format), and the second two numbers are the min
String morningRoutine = "0730";  //0700 refers to 7am
int dayTemp = 70;   //this is the default temp for the thermostat during the day
//int nightRoutine = 2230;    //2230 refers to 10:30pm
String nightRoutine = "1030";
int nightTemp = 68;     //this is the default temp for the thermostat during the night

//=====================
//these booleans are used to control loops below and prevent the program from spamming publish events
bool doMorningRoutine = true;
bool doNightRoutine = true;


//====================================================================================

void setup() {
    //Creates numerous particle variables
Particle.variable("Temperature_2", temp);
Particle.variable("Humidity", hum);
Particle.variable("Difference", tempDiff);
Particle.variable("CurTime", currentTime);
Particle.variable("Hr", hours);
Particle.variable("Mins", mins);
//Particle.variable("Data", data1);
//Particle.variable("Data2",data2);

    //Subscribes to a particle.publish a different Argon
Particle.subscribe("Temperature_internal", altTemp, MY_DEVICES);
}


//====================================================================================

void loop() {
        //these two lines get data from the sensor
    int rawTemp = int(dhtSensor.getTempFarenheit());
    int rawHum = int(dhtSensor.getHumidity());
    
    //gets the current time
    hours = Time.hour();
    sHour = String(hours);
    if (hours >= 1 && hours <= 9)
    {
        sHour = "0"+String(hours);
    }
    
    mins = Time.minute();
    sMin = String(mins);
    if (mins <=1 <= 9 )
    {
        sMin = "0"+String(mins);
    }
    
    currentTime = String(hours) + String(mins);

    Time.zone(-5);  //the line sets the time zone to EST, which is 5 hours behind GMT

        //these if statements check the data incase there is an abnormal reading (such as 20 for the temp)
    if (rawTemp < 100 && rawTemp > 20)
    {
        temp = rawTemp;
    }
    if (rawHum < 100 && rawHum > 0)
    {
        hum = rawHum;
    }
    
        //These if statments check to see if the current time matches any of the routine times.
        //Since the loop could run multiple times during the current minute, the booleans are used to make the loops run once
        //when the clock first changes to the routine time.
    if(currentTime == morningRoutine && doMorningRoutine == true)
    {
        Particle.publish("Day_Temp", String(dayTemp));
        doMorningRoutine = false;
    }
    if(currentTime == nightRoutine && doNightRoutine == true)
    {
        Particle.publish("Night_Temp", String(nightTemp));
        doNightRoutine = false;
    }
    
        //These if statements are designed to turn the routine booleans back to once the set routine time has passed
    if(currentTime == (String(morningRoutine.toInt() +1)) && doMorningRoutine == false)
    {
        doMorningRoutine = true;
    }
    if(currentTime == (String(nightRoutine.toInt() +1)) && doNightRoutine == false)
    {
        doNightRoutine = false;
    }
    
    delay(500); //waits .5 seconds
}


//====================================================================================

    //code for Particle.subscribe. Void type because it doesn't return anything
void altTemp(const char *topic, const char *data)   //*data is the value, *topic is the variable name
{
    //data1 = String(data);
    //data2 = String(topic);
    otherTemp = String(data).toInt();   //in order for the data to be an int, it first has to be cast as a String, and then as an int
    tempDiff = otherTemp-temp;  //if tempDiff is POSITIVE, then it is cooler where this device is located. If tempDiff is NEGATIVE, it is warmer where this device is located
    Particle.publish("Difference", String(tempDiff));
    Particle.publish("Temperature",String(otherTemp));
    Particle.publish("Temperature2", String(temp));
    Particle.publish("Humidity",String(hum));
    //Particle.publish("Temperature", String(otherTemp),String(temp),String(tempDiff));
}

Credits

Kealoha Hankins

Kealoha Hankins

1 project • 3 followers
Andrew Thompson

Andrew Thompson

1 project • 3 followers

Comments