// Libraries
#include <DHT_Async.h> // https://github.com/jcassel/DHT-Sensors-Non-Blocking
// Hardware
#define DHT_SENSOR_TYPE DHT_TYPE_22
static const int DHT_SENSOR_PIN = 10;
DHT_Async dht_sensor(DHT_SENSOR_PIN, DHT_SENSOR_TYPE);
const byte ModePotPin = A1; // Potentiometer for setting OFF/FAN/COOL/HEAT
const byte SetpointPotPin = A2; // Potentiometer for setting OFF/FAN/COOL/HEAT
// OutPins:
const byte G = 23; // Fan call
const byte Y1 = 24; // cooling call stage 1
const byte Y2 = 25; // cooling call stage 2
const byte W1 = 26; // heating call stage 1
const byte W2 = 27; // heating call stage 2
const int ON_LEVEL = HIGH;
const int OFF_LEVEL = LOW;
// Define HVAC capacities
const float W_PER_TON = 3516.8528421;
const float BTUpHR_PER_TON = 12000.0;
const float AC_TON_CAPACITY = 0.33 ; // https://hvacsizecalc.com/ac-unit-size-calculator/
const float AC_WATT_LOAD = AC_TON_CAPACITY * W_PER_TON;
const float AC_BTU_LOAD = AC_TON_CAPACITY * BTUpHR_PER_TON;
// state variables
float heatingSetpoint = 65;
float coolingSetpoint = 72;
float currentTempC = 25;
float currentTempF = 70;
float currentHumidity = 0;
uint32_t nowMs = 0;
float stageHysteresesHC = [[0.5,0.5][2,2]];
enum OpMode {OFF, FAN, COOL, HEAT} opMode = OFF;
void setup() {
Serial.begin(115200);
printCapacities();
printStatus();
pinMode(G, OUTPUT);
pinMode(Y1, OUTPUT);
pinMode(Y2, OUTPUT);
pinMode(W1, OUTPUT);
pinMode(W2, OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
nowMs = millis();
controlHVAC();
if (measure_environment(¤tTempC, ¤tHumidity)) {
currentTempF = degCtoF(currentTempC);
int modeSwitch = analogRead(ModePotPin) / 256;
int setpointVal = map(analogRead(SetpointPotPin), 0, 1023, 40, 90);
switch (modeSwitch) {
case 0: opMode = OFF; break;
case 1: opMode = FAN; break;
case 2: opMode = COOL; coolingSetpoint = setpointVal; break;
case 3: opMode = HEAT; heatingSetpoint = setpointVal; break;
}
printStatus();
}
}
void controlHVAC() {
const uint32_t intervalMs = 1000;
static uint32_t lastControlMs = -intervalMs;
if (nowMs - lastControlMs < intervalMs) return;
lastControlMs = nowMs;
// sanity/safety
if (HEAT == opMode) {
digitalWrite(Y1, OFF_LEVEL);
digitalWrite(Y2, OFF_LEVEL);
}
if (COOL == opMode) {
digitalWrite(W1, OFF_LEVEL);
digitalWrite(W2, OFF_LEVEL);
}
switch (opMode) {
case HEAT:
if (currentTempF < heatingSetpoint ) {
digitalWrite(W1, ON_LEVEL);
}
if (currentTempF < heatingSetpoint - 2 ) {
digitalWrite(W2, ON_LEVEL);
}
if (currentTempF > heatingSetpoint + 0.5 ) {
digitalWrite(W1, OFF_LEVEL);
digitalWrite(W2, OFF_LEVEL);
}
break;
case COOL:
if (currentTempF > coolingSetpoint ) {
digitalWrite(Y1, ON_LEVEL);
}
if (currentTempF > coolingSetpoint + 2 ) {
digitalWrite(Y2, ON_LEVEL);
}
if (currentTempF < coolingSetpoint - 0.5 ) {
digitalWrite(Y1, OFF_LEVEL);
digitalWrite(Y2, OFF_LEVEL);
}
break;
}
}
void printCapacities() {
Serial.print("AC Tons (refrigeration):");
Serial.println(AC_TON_CAPACITY);
Serial.print("AC Watts:");
Serial.println(AC_WATT_LOAD);
Serial.print("AC BTU/hr:");
Serial.println(AC_BTU_LOAD);
}
void printStatus() {
Serial.print("Mode:");
switch (opMode) {
case OFF: Serial.println("OFF"); break;
case FAN: Serial.println("FAN"); break;
case COOL: Serial.println("COOL"); break;
case HEAT: Serial.println("HEAT"); break;
default: Serial.println("unknown mode.");
}
Serial.print("Temperature:");
Serial.print(currentTempF);
switch (opMode) {
case OFF: Serial.println(" -- OFF, no action"); break;
case FAN: Serial.println(" -- FAN only"); break;
case COOL: Serial.print(" / "); Serial.println(coolingSetpoint); break;
case HEAT: Serial.print(" / "); Serial.println(heatingSetpoint); break;
default: Serial.println("unknown mode.");
}
}
static bool measure_environment(float *temperature, float *humidity) {
/*
Poll for a measurement, keeping the state machine alive. Returns
true if a measurement is available.
from https://github.com/jcassel/DHT-Sensors-Non-Blocking/blob/main/examples/DHTAsyncTester/DHTAsyncTester.ino
*/
static unsigned long measurement_timestamp = millis();
/* Measure once every four seconds. */
if (millis() - measurement_timestamp > 4000ul) {
if (dht_sensor.measure(temperature, humidity)) {
measurement_timestamp = millis();
return (true);
}
}
return (false);
}
float degCtoF(float c_deg) {
float f_deg = 32.0 + c_deg * 9.0 / 5;
return f_deg;
}