Thermostat Sketch

This sketch is a thermostat for a small gas heater in a garage.  It has an LCD display for current temperature, set point and status.  It has two push buttons to lower and raise the set point, and HCA can also change the set point via the network.  This thermostat reports when it comes on, goes off and the duration to HCA.

// include the library code:
#include <SPI.h>
#include <LiquidCrystal.h>
#include <OneWire.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <stdlib.h>

// initialize the lcd pins, using analogs
LiquidCrystal lcd(A5, A4, A3, A2, A1, A0);
// DS18S20 Temperature chip i/o
OneWire ds(9);  // on dig pin 9
String tempPacHead = “taddr:”;
String ds18addr = “”;
String tempPacket = “”;
String tempStr = “:”;
char fahr[10];
// variables to control heater
int cfhPin = 8;    // heater relay pin
int setpoint = 65;
boolean callforheat = false;
int thermoLead = 1;  // how far above setpoint temp will go
int thermoLag = 1;    //how far below setpoint temp will go
int wsetpoint = 0;  // working setpoint
unsigned long startTime;
unsigned long stopTime;
unsigned long CfhDuration;
unsigned long betweenCfhDuration;
unsigned long minCfhDuration = 60000;  // 60 seconds
unsigned long minbetweenCfhDuration = 60000;  // 60 seconds
// Enter a MAC address and IP address for ethernet
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x75 };
IPAddress ip(192,168,0,75);
unsigned int Port_Remote = 5000;      // remote port
IPAddress IP_Remote(192, 168, 0, 10);    // IP address for smarthome
unsigned int localPort = 5001;      // local port to listen on
char cmdGetData = ‘1’;     // a “1” means read all the data
char cmdGetSetPoint = ‘2’;
char cmdSetPoint = ‘3’;
char cmdStatusCheck = ‘9’;   // a “9” means smarthome is asking for status
char cmdIn = ‘0’;        // used to obtain and compare buffer character
char sp1;    // used to extract new setpoint
char sp2; // used to extract new setpoint
String heatString1 = “ardoffheatdur=”;
String heatString2 = “0000”;
long heatOnDuration;
// can not use dig pins 2, 4, 10, 11, 12, 13
// An EthernetUDP instance to let us receive packets over UDP
EthernetUDP Udp;
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,

void setup(){
//    Serial.begin(9600);
// set up the LCD’s number of columns and rows:
lcd.begin(16, 2);
lcd.print(“hello, world!”);  // just during startup
// set up ethernet, using udp
Ethernet.begin(mac,ip);           // no need for gateway or mask
Udp.begin(localPort);
// set up heater relay pin
digitalWrite(cfhPin, LOW);
pinMode(cfhPin,OUTPUT);
startTime = millis();
stopTime = millis();
CfhDuration = millis();
betweenCfhDuration = millis();
attachInterrupt(0, setpointUp, FALLING);
attachInterrupt(1, setpointDn, FALLING);
}
//end “setup()”

void loop(){
// initialize variables
delay(5000);
byte i;
byte present = 0;
byte data[12];
byte addr[8];
float tFahr;
float cent;
String stringOne;
String tsetpoint;

// start of temp sensor code
while (ds.search(addr))
{
ds18addr = “”;
for( i = 0; i < 8; i++)
{
stringOne = String(addr[i], HEX);
if (stringOne.length() < 2){stringOne = “0” + stringOne; }
stringOne.toUpperCase();
ds18addr = ds18addr + stringOne;
}
if ( OneWire::crc8( addr, 7) == addr[7])
{
ds.reset();
ds.select(addr);
ds.write(0x44,1);
delay(1000);
present = ds.reset();
ds.select(addr);
ds.write(0xBE);         // Read Scratchpad

for ( i = 0; i < 9; i++) {data[i] = ds.read();}

// calc temp value
cent = ( (data[1] << 8) + data[0] )*0.0625;
tFahr = (cent * 9 / 5) + 32;
dtostrf(tFahr,4,1,fahr);
tempPacket = tempPacHead + ds18addr + tempStr + fahr;
}  // end of crc ok branch
}    // end of while loop and temp sensor code

lcd.setCursor(0, 0);
lcd.print(“temp =       “);
lcd.setCursor(7, 0);
lcd.print(fahr);

lcd.setCursor(0, 1);
lcd.print(“setpoint = “);
lcd.setCursor(11, 1);
lcd.print( setpoint );
// compare/control operation of heater relay
// there are 3 factors used to make the decision
// setpoint vs temp, current status, timer expirations
// so 8 different possibilities
// first, adjust setpoint for lead/lag
lcd.setCursor(14, 0);  // set the lcd cursor
if (callforheat)
{ wsetpoint = setpoint + thermoLead;    }
else { wsetpoint = setpoint – thermoLag;    }
// second, set up timers
if (callforheat)
{ CfhDuration = millis()- startTime;    }
else { betweenCfhDuration = millis()- stopTime;    }
//first handle 4 conditions if call for heat is off
if (!callforheat)
{
if ((wsetpoint >= tFahr) && (betweenCfhDuration >= minbetweenCfhDuration))
{ digitalWrite(cfhPin, HIGH);
callforheat = TRUE;
lcd.print(“TN”);  // Turn On cfh
startTime = millis();
Udp.beginPacket(IP_Remote, Port_Remote);
Udp.write(“ardoffheatrun=yes”);        // now send packet to reset trigger
Udp.endPacket();
}
else if ((wsetpoint < tFahr) && (betweenCfhDuration >= minbetweenCfhDuration))
{
lcd.print(“LF”);  // Leave Off, timer inactive
}
else if ((wsetpoint >= tFahr) && (betweenCfhDuration < minbetweenCfhDuration))
{
lcd.print(“FN”);  // Leave off for now
}
else if((wsetpoint < tFahr) && (betweenCfhDuration < minbetweenCfhDuration))
{
lcd.print(“Lf”);  // Leave off and timer still active
}
else {
lcd.print(“E1”);  // error, should never get to here
}
}
//now handle 4 conditions if call for heat is on
if (callforheat)
{
if ((wsetpoint >= tFahr) && (CfhDuration >= minCfhDuration))
{
lcd.print(“LN”);  // Leave on, timer inactive
}
else if ((wsetpoint < tFahr) && (CfhDuration >= minCfhDuration))
{ digitalWrite(cfhPin, LOW);
callforheat = FALSE;
lcd.print(“TF”);  // Turn Off
stopTime = millis();
heatOnDuration = (millis() – startTime)/1000;        //calc dur of run
heatString2 = heatString1 + heatOnDuration;        // build string
Udp.beginPacket(IP_Remote, Port_Remote);
Udp.print(heatString2);        // send packet with dur
Udp.endPacket();
delay(1000);
Udp.beginPacket(IP_Remote, Port_Remote);
Udp.write(“ardoffheatrun=no”);        // now send packet with trigger
Udp.endPacket();
}
else if ((wsetpoint >= tFahr) && (CfhDuration < minCfhDuration))
{
lcd.print(“Ln”);  // Leave on and timer still active
}
else if((wsetpoint < tFahr) && (CfhDuration < minCfhDuration))
{
lcd.print(“NN”);  // Leave on for now
}
else {
lcd.print(“E2”);  // error, should never get to here
}
}
// end of compare/control section
// listen for incoming packets
int packetSize = Udp.parsePacket();
if(packetSize)
{
Udp.read(packetBuffer,3);
cmdIn = (packetBuffer[0]);
// respond to current temp request
if (cmdIn == cmdGetData)
{
Udp.beginPacket(IP_Remote, Port_Remote);
Udp.print(tempPacket);
Udp.endPacket();
} // end of cmdGetData
// respond to inquiry of current setpoint
if (cmdIn == cmdGetSetPoint)
{
tsetpoint = String(setpoint);
tempPacket = “ard75setp=” + tsetpoint;
Udp.beginPacket(IP_Remote, Port_Remote);
Udp.print(tempPacket);
Udp.endPacket();
} // end of cmdGetSetPoint
// handle set setpoint request
if (cmdIn == cmdSetPoint)
{
sp1 = (packetBuffer[1]);  // retrieve 2 digits
sp2 = (packetBuffer[2]);  // of new setpoint
int tsp1 = sp1 – ‘0’;  // convert char to int
int tsp2 = sp2 – ‘0’;  // convert char to int
setpoint = (tsp1*10) + tsp2;
} // end of cmdSetPoint
// respond to status check
if (cmdIn == cmdStatusCheck)
{
Udp.beginPacket(IP_Remote, Port_Remote);
Udp.write(“ard75ok=yes”);        // send packet good status
Udp.endPacket();
}  // end of status check
} // end of if packetsize
} // end loop()
// subrouting to handle int 0, pin 3, setpointUp
void setpointUp()
{
setpoint = setpoint + 1;
lcd.setCursor(11, 1);
lcd.print( setpoint );
}
// subrouting to handle int 1, pin 2, setpointDn
void setpointDn()
{
setpoint = setpoint – 1;
lcd.setCursor(11, 1);
lcd.print( setpoint );

Arduino, Venstar, Wemos, HCA, etc.