/* Boiler Control Box for Central Heating (BCB)
The BCB design is fail safe. If the power is switched off, the relays are released, switching the boiler installation in normal operating conditions.
The boiler is controlled by room thermostat and the floor heating valve is open. Obviously ValveProtectionProgram is inactive
*/
String version = "1.3";
/* Version 1.3 solves an error of the 24 hours VPP timer; the 15 minutes the VPP takes was not compensated for in the total 24 hours timer.
*/
#include <LiquidCrystal.h> // include library code
#include <avr/wdt.h> // include watchdog timer library code
/*
In1...In6 are the corresponding pin numbers of the Arduino outputs for controlling relais K1...K6.
The relais are activated if the pins are LOW.
LED is the corresponding pin number of the front panel LED, if the pin is HIGH the LED is on
PermOverridePin is the corresponding pin number of the front panel tumbler switch. If the pin is LOW, permanent override is activated
TempOverridePin is the corresponding pin number of the front panel pushbutton. If the pin is LOW, temporary override is activated
TsetPin is the corresponding pin number of the front panel potmeter for adjusting the maximum boiler water temperature
TmeasuredPin is the corresponding pin number of the NTC, measuring the boiler water temperature.
rs, en, d4, d5, d6, d7 are the control and data pins of the LCD
*/
const int In1 = 0, In2 = 1, In3 = 8, In4 = 9, In5 = 10, In6 = 11, LED=16, PermOverridePin = 6, TemporaryOverridePin = 7,
TsetPin = 0, TmeasuredPin = 1, R1 = 10000, rs = 12, en = 13, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
/*
Tset is the adjusted temperature and Tmeasured the actual temperature in °C, R2 is NTC resistance in ohm
Steinhart-Hart coefficients A, B and C are based on the FCB hot water working area of 45...70 °C.
Used NTC (resistance, temperature) points:
(4912 ohm, 45°C), (3216 ohm, 58 °C), (2229 ohm, 70 °C)
see http://www.thinksrs.com/downloads/programs/Therm%20Calc/NTCCalibrator/NTCcalculator.htm
*/
float Tset, Tmeasured, R2, A = 0.8604937964e-03, B = 2.562827478e-04, C = 1.700622851e-07;
/* Vmeasured is voltage on analog input representing actual temperature (integer from 0 to 1023), h is hysteresis in °C
Override is de status of the installation.
Override == 0 means normal operation (boiler is controlled by room thermostat
and the floor heating valve is open. The floor heating valve is closed every 24 hours during 15 minutes
to avoid getting stuck (Valve Protection Program)
Override == 1 means the FCB controls the Boiler and keeps the Boiler output water temperature between Tset-h and Tset.
The room thermostat controls the valve.
min is the number of minutes the Valve Protection Program runs and closes the electric valve
*/
int Vmeasured, h = 10, Override = 0, VPnumber = -1, min = 15;
unsigned int counter = 0;
unsigned long ValveProtectionCounter = 0;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7); // initialize the library by associating any needed LCD interface pin with the arduino pin number it is connected to
byte degree[8] = { // defines degree character
B00010,
B00101,
B00010,
B00000,
B00000,
B00000,
B00000,
};
//Function declarations
float SteinHart(float R) {
float logR = log(R);
float result;
result = (1.0 / (A + B*logR + C*logR*logR*logR)); // Calculation of current temperature in Kelvin with Steinhart - Hart approximation of NTC R-T curve
result = result - 273.15; // T in degrees Celcius
return result;
}
void setup() {
lcd.createChar(0, degree); // creates custom character (gylph) number 0 following definition "byte degree[8]"
lcd.begin(20,4);
// set pinmode for pins 0 to 11 to OUTPUT and set pins HIGH:
int PinMin = 0; // Lowest pin
int PinMax = 11; // Highest pin
for(int i=PinMin; i<=PinMax; i++)
{
pinMode(i, OUTPUT);
digitalWrite(i,HIGH);
}
// set pinmode for PermOverridePin (tumbler switch) and TemporaryOverridePin to input. Set pinmode for LED to output:
pinMode(PermOverridePin, INPUT);
pinMode(TemporaryOverridePin, INPUT);
pinMode(LED, OUTPUT);
/*
Print "REBOOT IN PROGRESS" to LCD during 5 seconds. Although during initial startup a reboot is not
really the case, during normal operation this warning indicates the watchdog timer has timed out and reboot the
Arduino, probably after a program hang.
*/
lcd.clear();
lcd.setCursor(0,1);
lcd.print(" REBOOT IN PROGRESS");
lcd.setCursor(0,2);
lcd.print(" VERSION ");
lcd.print(version);
delay(5000);
}
void loop() {
wdt_enable(WDTO_8S); // activates watchdog timer which will fire after 8 seconds if not reset.
if(counter>0) {
counter--;
}
if(digitalRead(TemporaryOverridePin)==LOW){
counter = counter+3602; // loop program execution takes approx 1 second so 3602 seconds adds approx 1 hour to temporary override time
if(counter>30000){ // maximum temporary override time is 8 hours, because 9 hours exceeds 30000 seconds
counter = 0; // temporary override time can be set to 1-2-3-4-5-6-7-8-0-1-2-etc hours
}
}
if(digitalRead(PermOverridePin)==LOW){ // if tumbler switch is set to "override", temporary override timer is reset
counter = 0;
}
if(digitalRead(TemporaryOverridePin) == LOW && digitalRead(PermOverridePin) == LOW){ //If tumbler switch is set to "override" and temporary override button is pushed
VPnumber = 0; //ValveProtection number VPnumber is reset to 0 (display: #VP=0)
}
if(digitalRead(PermOverridePin)==LOW || counter>0){ // If either permanent or temporary override condition is met, override status is set to 1
Override = 1;
}
else {
Override = 0;
}
Tset = (int)((25.0*(analogRead(TsetPin))/1023)+45); // Tset is adjustable from 45 to 70°C as AnalogRead(TsetPin) varies from 0 to 1023
Vmeasured = analogRead(TmeasuredPin); // integer from 1 to 1023 representing measured temperature
R2 = R1 * (1023.0 / (float)Vmeasured - 1.0); // calculation of R2 (resistance of NTC at current temperature);
// (float) converts the integer to a floating value
Tmeasured = SteinHart(R2); // calculation of measured temperature from current NTC resistance
if(Override == 0){ // Relays are set to normal operation(Boiler is controlled by room thermostat
// and the floor heating valve is open. The floor heating valve is closed every 24 hours during 15 minutes
// to avoid getting stuck (Valve Protection Program)
digitalWrite(In1,HIGH);
digitalWrite(In2,HIGH);
digitalWrite(In3,HIGH);
digitalWrite(In4,HIGH);
digitalWrite(In5,HIGH);
digitalWrite(In6,HIGH);
digitalWrite(LED,LOW);
}
if(Override == 1){ // Override == 1 means the FCB controls the Boiler and keeps the Boiler output water temperature between Tset-h and Tset.
// The room thermostat controls the valve.
digitalWrite(In5,LOW);
digitalWrite(In1,LOW);
digitalWrite(In2,LOW);
digitalWrite(In3,LOW);
digitalWrite(In4,LOW);
digitalWrite(LED,HIGH);
ValveProtectionCounter = 0; // If override has been activated the valve has moved (room thermostat will close the valve if
// room temperature gets too high) and protection is not necessary, ValveProtectionCounter is reset
}
if(Tmeasured > Tset && Override == 1){ // K6 switches the Boiler off
digitalWrite(In6,HIGH);
}
else if(Tmeasured < (Tset - h) && Override == 1){ // K6 switches the Boiler on if water temperature comes below set temperature minus hysteresis h
digitalWrite(In6,LOW);
}
else if(Override == 0){
digitalWrite(In6,HIGH);
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Temperature: "); // print measured Boiler output water temperature
lcd.print(Tmeasured, 0);
lcd.write(byte(0)); // write degree character
lcd.print("C");
lcd.setCursor(0, 1);
lcd.print(" Setpoint:"); // print adjusted Boiler output water temperature
lcd.setCursor(12, 1);
lcd.print(Tset, 0);
lcd.write(byte(0)); // write degree character
lcd.print("C");
lcd.setCursor(0, 2);
if(digitalRead(PermOverridePin)==LOW){
lcd.print(" Permanent override");
}
else if(counter>0){ // print remaining temporary override time
lcd.print("Override: ");
lcd.print(counter/3600); // hours
lcd.print(" h ");
lcd.print((counter % 3600)/60); // minutes
lcd.print(" min ");
}
else{
lcd.print(" Override: OFF");
}
lcd.setCursor(0,3); // print Boiler control during override status
if (digitalRead(In6) == HIGH && Override == 1){
lcd.print("Boiler off");
}
if (digitalRead(In6) == LOW && Override == 1){
lcd.print("Boiler on");
}
lcd.setCursor(13,3);
if (VPnumber == -1){
lcd.print("BOOTED"); //after a reboot print "BOOTED" to LCD untill VPnumber is reset to 0 by putting tumbler switch to "permanent override" and
//simultaneously pushing the temporary override button
}
else{
lcd.print("#VP=");
lcd.print(VPnumber);
}
delay(1000); // delay 1 second
if(ValveProtectionCounter >= (86400 - 60 * min) && Override == 0){ //Valve protection program: Valve closes and opens to avoid getting stuck once every 24 hours (86400 seconds)
digitalWrite(In5, LOW);
for(min; min>0; min--) // min is duration of valve protection program in minutes
{
for(int sec=60; sec>0; sec--)
{
lcd.clear();
lcd.setCursor(2,0);
lcd.print("VALVE PROTECTION");
lcd.setCursor(6,1);
lcd.print("PROGRAM");
lcd.setCursor(2,2);
lcd.print("TIME REMAINING:");
lcd.setCursor(3,3);
lcd.print(min-1);
lcd.print(" MIN ");
lcd.print(sec);
lcd.print(" SEC");
delay(1000);
wdt_reset();
}
}
ValveProtectionCounter = 0;
if (VPnumber >= 0){
VPnumber++;
}
if (VPnumber >= 100){
VPnumber = 0;
}
}
ValveProtectionCounter++;
wdt_reset(); //reset watchdog timer
}