Published © GPL3+

HVAC Smart Zone

Control a HVAC zone by retrofitting a manual duct damper with a damper actuator motor and installing a thermostat to control it.

BeginnerFull instructions provided2 hours3,454
HVAC Smart Zone

Things used in this project

Hardware components

Argon
Particle Argon
×2
Adafruit Power Relay FeatherWing
×1
Adafruit FeatherWing Doubler
×1
National Control Devices Feather Battery I2C Shield For Particle
×1
National Control Devices TMP112 Temperature Sensor
×1
BELIMO LMB24-3-T
×1
DROK 090187 DC-DC Volt Buck Converter Step-down
×1
24V AC to DC Adapter Power Supply
×1
DC Power Connector, Jack, 2.1 mm, Panel Mount
×1
LeMotech ABS Plastic Junction Box 6.2"x3.5"x2.3"
×1
Optional - All assembled Smart HVAC zone DIY kit w/ free Android App
×1

Software apps and online services

HVAC Smart Zone - Android App

Story

Read more

Code

HVACThermostat.ino

C#
// This #include statement was automatically added by the Particle IDE.
#include "TMP112.h"

SYSTEM_THREAD(ENABLED);

TMP112 sensor;
double temperature;
String temperatureString;

bool heat;
float setTemp;
float tempSwing = 1.00;
String setTempString;
String tempSwingString;

String hvacStatus = "";

int setTemperature(String temperature);
int setTemperatureSwing(String temperature);
void setMode(const char *event, const char *data);

int tempSwingEEPROMLocation = 5;
int setTempEEPROMLocation = 1;
int hvacStatusBoolLocation = 0;

void setup() {
    getStoredInfo();
    sensor.setAddress(0,0,0);
    Particle.variable("temperature", temperatureString);
    Particle.variable("HVAC", hvacStatus);
    Particle.variable("SetTemp", setTempString);
    Particle.variable("TempSwing", tempSwingString);
    Particle.function("setTemp", setTemperature);
    Particle.function("setSwing", setTemperatureSwing);
    Particle.subscribe("HVAC_Mode", setMode, MY_DEVICES);
}

void loop() {
    temperature = sensor.temperature();
    temperatureString = String(temperature, 2);
    Serial.printf("Temperature: %f \n", temperature);
    //Running heater
    if(heat){
        if(temperature >= setTemp){
            //Close HVAC DUCT
            Particle.publish("zoneControl", "close", PRIVATE);
            Serial.println("Duct Closed");
        }else{
            if(temperature <= setTemp - tempSwing){
                //Open HVAC DUCT
                Particle.publish("zoneControl", "open", PRIVATE);
                Serial.println("Duct Open");
            }
        }
    }
    //Running AC
    else{
        if(temperature <= setTemp){
            //Close HVAC DUCT
            Particle.publish("zoneControl", "close", PRIVATE);
            Serial.println("Duct Closed");
        }else{
            if(temperature >= setTemp + tempSwing){
                //Open HVAC DUCT
                Particle.publish("zoneControl", "open", PRIVATE);
                Serial.println("Duct Open");
            }
        }
    }
    delay(5000);
}

int setTemperature(String temperature){
    setTemp = temperature.toFloat();
    setTempString = String(setTemp, 2);
    EEPROM.put(setTempEEPROMLocation, setTemp);
    Serial.printf("setTemp: %.2f \n", setTemp);
}

int setTemperatureSwing(String temperature){
    tempSwing = temperature.toFloat();
    tempSwingString = String(tempSwing, 2);
    EEPROM.put(tempSwingEEPROMLocation, tempSwing);
    Serial.printf("tempSwing: %.2f \n", tempSwing);
    
}

void setMode(const char *event, const char*data){
    String command = String(data);
    if(command.equalsIgnoreCase("AC")){
        heat = false;
        hvacStatus = "AC";
    }
    if(command.equalsIgnoreCase("HEAT")){
        heat = true;
        hvacStatus = "HEAT";
    }
    EEPROM.put(hvacStatusBoolLocation, heat);
}

void getStoredInfo(){
    EEPROM.get(setTempEEPROMLocation, setTemp);
    setTempString = String(setTemp, 2);
    EEPROM.get(tempSwingEEPROMLocation, tempSwing);
    tempSwingString = String(tempSwing, 2);
    EEPROM.get(hvacStatusBoolLocation, heat);
    if(heat){
        hvacStatus = "HEAT";
    }else{
        hvacStatus = "AC";
    }
    Serial.printf("setTemp: %.2f \n", setTemp);
    Serial.printf("tempSwing: %.2f \n", tempSwing);
    Serial.printf("heat: %d \n", heat);
}

TMP112.cpp

C#
#include "TMP112.h"
#include "math.h"

bool TMP112::setAddress(int a0, int a1, int a2){
    
    if(a0 == 1){
        address = address | 1;
    }
    if(a1 == 1){
        address = address | 2;
    }
    if(a2 == 1){
        address = address | 4;
    }
    
    Wire.begin();
    Wire.beginTransmission(address);
    byte status = Wire.endTransmission();
    if(status != 0){
        Serial.println("Serial init failed 1");
        return false;
    }
    Wire.beginTransmission(address);
    Wire.write(0x01);
    Wire.write(96);
    status = Wire.endTransmission();
    if(status != 0){
        Serial.println("Serial init failed 1");
        return false;
    }
    Serial.println("sensor initialized");
    return true;
}

float TMP112::temperature(){
    Wire.beginTransmission(address);
    Wire.write(0x00);
    byte status = Wire.endTransmission();
    if(status != 0){
        return 999.99;
    }
    Wire.requestFrom(address, 2);
    byte a = Wire.read();
    byte b = Wire.read();
    float temp = a;
    temp +=(float)b/256.00;
    
    //Convert to Farenheit
    float tempF = temp*1.8+32;
    return tempF;
}

TMP112.h

C#
#include "spark_wiring_i2c.h"
#include "spark_wiring_usbserial.h"
#include "spark_wiring_constants.h"

class TMP112{
public:
    bool setAddress(int a0, int a1, int a2);
    float temperature();
    
private:
    int address = 0x48;
};

hvaczone.ino

C#
/* Includes ------------------------------------------------------------------*/

SYSTEM_MODE(AUTOMATIC);

SYSTEM_THREAD(ENABLED);

int triggerRelay(String command);

int relayStatus; 

String ventStatus;

String commandEntered;

String SSID;

String pass;

void ductHandler(const char *event, const char *data);

/* This function is called once at start up ----------------------------------*/
void setup()
{
	Particle.function("controlRelay", triggerRelay);
	Particle.subscribe("zoneControl", ductHandler, MY_DEVICES);
//	Particle.variable("BedroomVents", ventStatus);
	Particle.variable("LastCommand", commandEntered);
	Particle.variable("RelayStatus", relayStatus);
	Particle.variable("SSID", SSID);
	pinMode(D6, OUTPUT);

	
}

/* This function loops forever --------------------------------------------*/
void loop()
{
		SSID = WiFi.SSID(); 
		
		relayStatus = digitalRead(D6); 


	if (relayStatus == 0) {
	    ventStatus = "Open";
	}
	else {
	    ventStatus = "Closed";
	}
 
	
}



void ductHandler(const char *event, const char *data){
    String command = String(data);
    if(command.equalsIgnoreCase("close")){
        Particle.publish("HVAC_Zone", "Closed");
        digitalWrite(D6, HIGH);
        commandEntered = "Close";
    }
    if(command.equalsIgnoreCase("open")){
        Particle.publish("HVAC_Zone", "Open");
        digitalWrite(D6, LOW);
        commandEntered = "Open";
    }
}


int triggerRelay(String command){
	if(command.equalsIgnoreCase("on")){
		digitalWrite(D6, HIGH);
		Particle.publish("HVAC_Zone", "Closed");
		commandEntered = "Close";
		return 1;
	}
	if(command.equalsIgnoreCase("off")){
		digitalWrite(D6, LOW);
		Particle.publish("HVAC_Zone", "Open");
		commandEntered = "Open";
		return 1;
	}
	return 0;
}

Credits

Comments