Hardware components | ||||||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
![]() |
|
Tired of having to use old chrono-thermostats with reduced and incomplete functionality, I decided to build one myself. There are a lot of projects like this on the NET but none of them satisfied me because they don't have the advanced features that I wanted. That's what I started from. An old device with basic functions and with little information displayed in the graphic part which is also very poor.
Here is the old device now dated and with functions and knobs difficult to set. Let's start by describing my home heating system. The heating system is a condominium system with a boiler shared between several apartments. In each apartment there are sectioning solenoid valves that open and close parts of the system connected to the individual timer-thermostats. In my case I have two plant sections. One on the first floor and one on the attic. In order not to make too many mistakes, I started designing a single chrono thermostat for the apartment on the first floor. Then if the device goes well I can think of installing another one in the attic. When I started with Arduino, my first project was that of a power strip to control an aquarium and its devices. Over time, however, these features no longer served me and therefore I reused the Arduino components for new projects.
Let's now see the components and how the chrono-thermostat project was composed. The components reused were the arduino uno and a set of relays to drive the loads in AC 220V (Italian voltage). To this I added a realtime clock RTC to keep the time when the power shutdown, and a very precise temperature sensor with 0.065 ° C resolution (MCP9808). In addition to all this I added a LCD Keypad, a black PLASTIC box to enclose all and a 5V power supply recovered from one of the USB chargers that haunt our homes with the advent of mobile phones.
Once all the components have been assembled and the program loaded, here is what appears in the various screens of the programmer.
here in brief the various functions provided by the program:
- Idle display temperature and state of the plant
- Force state plant ON or OFF 1-4h, 1-Nday
- Set current date and time
- Switch plant ON/OFF
- Preset of temperature offset
- 4 different day program
- 7day week 4program assignment
/*
* programma CronoTermostato_FA
* by Fabrizio Allevi
* fabrizio . allevi @ tiscali . it
*/
#include <LiquidCrystal.h>
#include <Wire.h>
#include "Adafruit_MCP9808.h"
#include "Sodaq_DS3231.h"
//#define DEBUG true
#define DEBUG false
// initialize the library with the numbers of the interface pins (4-5-6-7-8-9-10dimmer)
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
Adafruit_MCP9808 tempsensor = Adafruit_MCP9808();
// menu principali
#define MAX_MENU 7
String sMenu[MAX_MENU] = {" ",
"Programs ",
"Days ",
"Date & Time ",
"OfsTemp ",
"Force ",
"SwitchPlant "};
#define LEVEL_ZERO 0
#define LEVEL_PROGRAMS 1
#define LEVEL_DAYS 2
#define LEVEL_DATETIME 3
#define LEVEL_OFSTEMP 4
#define LEVEL_FORCE 5
#define LEVEL_OFF 6
int mLevel = LEVEL_ZERO;
// sottomenu programmi
#define MAX_PROGS 4
String sPrograms[MAX_PROGS] = {"AllDay ",
"Morning+Evening ",
"Early+Evening ",
"maiNtenance " };
char cProgram[MAX_PROGS] = { 'A', 'M', 'E', 'N' };
int smLevel = 0;
// giorni settimana
#define MAX_DAYS 7
String sDays[MAX_DAYS] = { "Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab" };
String sMonth[12] = { "Gen", "Feb", "Mar", "Apr", "Mag", "Giu", "Lug", "Ago", "Set", "Ott", "Nov", "Dic" };
#define MAX_SEQ 6
////////////////////////////////////////////////////////////////////////////////
#define EEPROM_OFSP 0
// crono programmi
struct stProgram {
int T[MAX_SEQ]; // in decimi di grado
int HM[MAX_SEQ]; // hhmm nei 16bit hh=partealta mm=partebassa
} progs[MAX_PROGS] = {{{ 210, 195, 210, 195, 210, 195 }, { 0x071E, 0x091E, 0x0B00, 0x0D00, 0x0F00, 0x1700 }},
{{ 210, 195, 210, 195, 195, 195 }, { 0x071E, 0x091E, 0x0F00, 0x1700, 0x0000, 0x0000 }},
{{ 210, 195, 210, 195, 195, 195 }, { 0x051E, 0x0800, 0x0F00, 0x1700, 0x0000, 0x0000 }},
{{ 170, 170, 170, 170, 170, 170 }, { 0x0000, 0x173B, 0x0000, 0x0000, 0x0000, 0x0000 }}};
////////////////////////////////////////////////////////////////////////////////
#define EEPROM_OFSD (sizeof(progs))
// assegnazione programmi/giorni
int dayProg[MAX_DAYS] = { 0, 2, 2, 2, 2, 2, 1 };
////////////////////////////////////////////////////////////////////////////////
#define EEPROM_OFSF (sizeof(progs)+sizeof(dayProg))
// forzatura programmi a tempo e offset temperatura
bool bForce=false;
struct stForce {
int hForce;
unsigned long tFin;
int ofsTemp;
bool bOFF;
} forceData = { 0, 0, 0, false};
////////////////////////////////////////////////////////////////////////////////
int smValue = 0;
bool bProgram = false;
bool bFire = false;
int nProg = 0;
int arrayDT[5];
void showDateTimePage(bool bGet = false);
int smCursor = 0;
// define the button
#define btnRIGHT 0
#define btnUP 1
#define btnDOWN 2
#define btnLEFT 3
#define btnSELECT 4
#define btnNONE 5
int lastBtn = btnNONE;
//read the button value
int read_buttons() {
int adc_key_in = analogRead(0); // read analog A0 value
if (adc_key_in > 1000)
return btnNONE;
if (adc_key_in < 50)
return btnRIGHT;
if (adc_key_in < 250)
return btnUP;
if (adc_key_in < 450)
return btnDOWN;
if (adc_key_in < 650)
return btnLEFT;
if (adc_key_in < 850)
return btnSELECT;
return btnNONE;
}
byte cFire = 1;
byte fire[8] = { B00100, B00100, B01000, B10010, B10101, B10001, B01110, };
byte cNoFire = 2;
byte noFire[8] = { B00000, B00000, B00000, B00000, B00000, B10101, B11111, };
#define CALDAIA 11 // digital pin out 11
#define DIMMER 10 // pin 10 dimmer lcd
#define D_LOW 50 //50
#define D_MIDDLE 150 //100
#define D_HIGH 250 // 200
void setup() {
if (DEBUG) {
Serial.begin(57600);
Serial.println("Cronotermostato Debug:");
}
// wire
Wire.begin();
pinMode(CALDAIA, OUTPUT);
digitalWrite(CALDAIA, HIGH); // spengo rele
// RTC DS3231
rtc.begin();
// MCP9808
if (!tempsensor.begin(0x18) && DEBUG)
Serial.println("Couldn't find MCP9808! Check your connections and verify the address is correct.");
tempsensor.setResolution(3); // sets the resolution 0-0.5C-30ms | 1-0.25C-65ms | 2-0.125C-130ms | 3-0.0625C-250ms
// LCD
lcd.begin(16, 2); // set up the LCD's number of columns and rows:
lcd.createChar(cFire, fire);
lcd.createChar(cNoFire, noFire);
// force data for update init progs in flash
//saveData((byte*) progs, sizeof(progs), EEPROM_OFSP);
//saveData((byte*) dayProg, sizeof(dayProg), EEPROM_OFSD);
//saveData((byte*) &forceData, sizeof(forceData), EEPROM_OFSF);
// load from eeprom data
loadData((byte*) progs, sizeof(progs), EEPROM_OFSP);
loadData((byte*) dayProg, sizeof(dayProg), EEPROM_OFSD);
loadData((byte*) &forceData, sizeof(forceData), EEPROM_OFSF);
//showMainPage(readTemp());
analogWrite(DIMMER, D_LOW);
idlePage(readTemp(), true);
}
unsigned long msIdle = millis();
unsigned long msTemp = millis();
unsigned long msScheduler = millis();
unsigned long msLast = millis();
unsigned long msDimmer = millis();
bool bIdleState = true;
float lastTemp;
/**************************************************************/
void loop() {
unsigned long msNow = millis();
if (msNow < msLast) { // gestione overflow
msLast = msNow;
if (msDimmer)
msDimmer = msNow;
if (msScheduler > msNow)
msScheduler = msNow;
if (msIdle > msNow)
msIdle = msNow;
if (msTemp > msNow)
msTemp = msNow;
}
int newBtn = read_buttons();
if (newBtn != btnNONE) {
if (lastBtn == btnNONE) {
if (DEBUG)
Serial.println(newBtn);
lastBtn = newBtn;
//lcd.display();
analogWrite(DIMMER, D_HIGH);
handleMenus(); // gestione menus
msDimmer = millis() + 5000;
bIdleState = false;
}
} else {
lastBtn = btnNONE;
if (msDimmer) {
if (msNow >= msDimmer + 5000) {
analogWrite(DIMMER, D_LOW);
if (!bProgram) {
//showMainPage(readTemp());
mLevel = LEVEL_ZERO;
smLevel = 0;
smValue = 0;
msDimmer = 0;
bIdleState = true;
idlePage(readTemp(), true);
}
} else if (msNow >= msDimmer)
analogWrite(DIMMER, D_MIDDLE);
}
}
if (bForce || (msNow > msScheduler + 60000)) { // ogni 60sec
float t = readTemp();
scheduler(t);
handleCaldaia();
msScheduler = msNow;
bForce=false;
}
if (msNow > msTemp + 12000) { // ogni 12sec
lastTemp = readTemp();
msTemp = msNow;
}
if (bIdleState) {
if (msNow > msIdle + 1000) { // ogni 1sec
idlePage(lastTemp, false);
msIdle = msNow;
}
}
msLast = msNow;
}
/*
* Buttons:
* #up/page
* #sx/- #dx/+ #enter
* #down/page
*/
/*
* gestione salvataggio parametri per programma CronoTermostato_FA
* by Fabrizio Allevi
* fabrizio . allevi @ tiscali . it
*/
// functions imported
void i2c_eeprom_write_byte( int deviceaddress, unsigned int eeaddress, byte data ) {
int rdata = data;
Wire.beginTransmission(deviceaddress);
Wire.write((int)(eeaddress >> 8)); // MSB
Wire.write((int)(eeaddress & 0xFF)); // LSB
Wire.write(rdata);
Wire.endTransmission();
}
// WARNING: address is a page address, 6-bit end will wrap around
// also, data can be maximum of about 30 bytes, because the Wire library has a buffer of 32 bytes
void i2c_eeprom_write_page( int deviceaddress, unsigned int eeaddresspage, byte* data, byte length ) {
Wire.beginTransmission(deviceaddress);
Wire.write((int)(eeaddresspage >> 8)); // MSB
Wire.write((int)(eeaddresspage & 0xFF)); // LSB
byte c;
for ( c = 0; c < length; c++)
Wire.write(data[c]);
Wire.endTransmission();
}
byte i2c_eeprom_read_byte( int deviceaddress, unsigned int eeaddress ) {
byte rdata = 0xFF;
Wire.beginTransmission(deviceaddress);
Wire.write((int)(eeaddress >> 8)); // MSB
Wire.write((int)(eeaddress & 0xFF)); // LSB
Wire.endTransmission();
Wire.requestFrom(deviceaddress,1);
if (Wire.available()) rdata = Wire.read();
return rdata;
}
// maybe let's not read more than 30 or 32 bytes at a time!
void i2c_eeprom_read_buffer( int deviceaddress, unsigned int eeaddress, byte *buffer, int length ) {
Wire.beginTransmission(deviceaddress);
Wire.write((int)(eeaddress >> 8)); // MSB
Wire.write((int)(eeaddress & 0xFF)); // LSB
Wire.endTransmission();
Wire.requestFrom(deviceaddress,length);
int c = 0;
for ( c = 0; c < length; c++ )
if (Wire.available()) buffer[c] = Wire.read();
}
void saveData(byte *pb, int sizeTot, int addr) {
// save data array ////////////////////////////////
if(DEBUG) {
Serial.print("sizeTot=");
Serial.println(sizeTot);
}
int size=(sizeTot<16)?sizeTot:16;
while(size) {
i2c_eeprom_write_page(0x57, addr, pb, size);
delay(100); //add a small delay
if(DEBUG) {
byte tmp[16];
i2c_eeprom_read_buffer(0x57, addr, tmp, size);
for(int c=0;c<size;c++) {
Serial.print(tmp[c]);
Serial.print('-');
}
Serial.print('\n');
}
pb+=size; addr+=size;
sizeTot-=size;
if(sizeTot<size)
size=sizeTot;
}
}
void loadData(byte *pb, int sizeTot, int addr) {
// save data array ////////////////////////////////
if(DEBUG) {
Serial.print("sizeTot=");
Serial.println(sizeTot);
}
int size=(sizeTot<16)?sizeTot:16;
while(size) {
i2c_eeprom_read_buffer(0x57, addr, pb, size);
delay(100); //add a small delay
if(DEBUG) {
for(int c=0;c<size;c++) {
Serial.print(pb[c]);
Serial.print('-');
}
Serial.print('\n');
}
pb+=size; addr+=size;
sizeTot-=size;
if(sizeTot<size)
size=sizeTot;
}
}
/*
* gestione menus per programma CronoTermostato_FA
* by Fabrizio Allevi
* fabrizio . allevi @ tiscali . it
*/
void changeText(int step, int max, int col, int row, int *idx, String *arr) {
if(step>0) {
if(*idx==max-1)
*idx=-1;
}
else {
if(*idx==0)
*idx=max;
}
*idx+=step;
lcd.setCursor(col,row);
lcd.print(arr[*idx]);
if(DEBUG) {
Serial.print("*idx=");
Serial.println(*idx);
}
}
void handleMenus() {
int dir=1;
if(bIdleState) {
showInfo();
return;
}
if(lastBtn==btnLEFT || lastBtn==btnRIGHT) {
if(lastBtn==btnLEFT)
dir=-1;
if(bProgram) {
if(mLevel==LEVEL_DATETIME) {
if(smLevel==4 && dir==1)
smLevel=-1;
else if(smLevel==0 && dir==-1)
smLevel=5;
smLevel+=dir;
if(smLevel<3)
lcd.setCursor(smLevel*3,1);
else
lcd.setCursor(2+smLevel*3,1);
}
else if(mLevel==LEVEL_PROGRAMS) {
if(smCursor==2 && dir==1) {
smCursor=-1;
if(smValue==5)
smValue=-1;
smValue+=dir;
printProgEvent();
}
else if(smCursor==0 && dir==-1) {
smCursor=3;
if(smValue==0)
smValue=MAX_SEQ;
smValue+=dir;
printProgEvent();
}
smCursor+=dir;
if(smCursor<2)
lcd.setCursor(smCursor*3,1);
else
lcd.setCursor(11,1);
}
}
else {
if(mLevel==LEVEL_PROGRAMS) {
changeText(dir,MAX_PROGS,0,1, &smLevel, sPrograms);
}
else if(mLevel==LEVEL_DAYS) {
changeText(dir,MAX_DAYS,12,0, &smLevel, sDays);
lcd.setCursor(0,1);
lcd.print(sPrograms[dayProg[smLevel]]);
}
}
}
else if(lastBtn==btnUP || lastBtn==btnDOWN) {
if(bProgram) {
if(mLevel==LEVEL_OFF) {
forceData.bOFF=!forceData.bOFF;
updateOFF();
}
else if(mLevel==LEVEL_FORCE) {
if(lastBtn==btnDOWN)
dir=-1;
if((forceData.hForce==4 && dir==1) || (forceData.hForce==-4 && dir==-1))
forceData.hForce=dir*24;
else if((forceData.hForce==-24 && dir==1) || (forceData.hForce==24 && dir==-1))
forceData.hForce=dir*4*-1;
else if(forceData.hForce>4 || forceData.hForce<-4)
forceData.hForce+=dir*24;
else
forceData.hForce+=dir;
updateForce();
}
else if(mLevel==LEVEL_OFSTEMP) {
if(lastBtn==btnDOWN)
dir=-1;
forceData.ofsTemp+=dir;
updateOfs();
}
else if(mLevel==LEVEL_DATETIME) {
if(lastBtn==btnDOWN)
dir=-1;
arrayDT[smLevel]+=dir;
showDateTimePage();
}
else if(mLevel==LEVEL_DAYS) {
if(lastBtn==btnUP)
dir=-1;
changeText(dir,MAX_PROGS,0,1, &smValue, sPrograms);
}
else if(mLevel==LEVEL_PROGRAMS) {
if(lastBtn==btnDOWN)
dir=-1;
int hour=progs[smLevel].HM[smValue]>>8;
int min=progs[smLevel].HM[smValue]&0xFF;
if(smCursor==2) { //temp
progs[smLevel].T[smValue]+=5*dir;
}
else {
if(smCursor==0) { //hour
hour+=dir;
if(hour<0)
hour=23;
hour%=24;
}
else if(smCursor==1) { //min
min+=dir*15;
if(min<0)
min=45;
min%=60;
}
progs[smLevel].HM[smValue]=(hour<<8)|(min&0xFF);
}
printProgEvent();
}
}
else {
lcd.clear();
if(lastBtn==btnUP)
dir=-1;
changeText(dir,MAX_MENU,0,0, &mLevel, sMenu);
lcd.setCursor(0,1);
if(mLevel==LEVEL_OFF)
updateOFF();
else if(mLevel==LEVEL_FORCE)
updateForce();
else if(mLevel==LEVEL_OFSTEMP)
updateOfs();
else if(mLevel==LEVEL_DATETIME)
showDateTimePage(true);
else if(mLevel==LEVEL_DAYS) {
lcd.print(sPrograms[dayProg[0]]);
lcd.setCursor(12,0);
lcd.print(sDays[0]);
}
else if(mLevel==LEVEL_PROGRAMS)
lcd.print(sPrograms[0]);
else if(mLevel==LEVEL_ZERO)
showInfo();
smLevel=0;
}
}
else if(lastBtn==btnSELECT) {
if(mLevel!=LEVEL_ZERO) {
if(!bProgram) {
if(mLevel==LEVEL_PROGRAMS) {
smValue=0;
smCursor=0;
printProgEvent();
}
else if(mLevel==LEVEL_DAYS) {
//...
}
else if(mLevel==LEVEL_DATETIME) {
smLevel=0;
}
else if(mLevel==LEVEL_OFSTEMP) {
//...
}
else if(mLevel==LEVEL_FORCE) {
//...
}
else if(mLevel==LEVEL_OFF) {
//...
}
lcd.setCursor(0,1);
lcd.blink();
bProgram=true;
}
else {
if(mLevel==LEVEL_PROGRAMS) {
smValue=smCursor=0;
mLevel=LEVEL_ZERO;
changeText(1,MAX_MENU,0,0, &mLevel, sMenu);
lcd.setCursor(0,1);
lcd.print(sPrograms[smLevel]);
saveData((byte*)progs, sizeof(progs), EEPROM_OFSP);
}
else if(mLevel==LEVEL_DAYS) {
dayProg[smLevel]=smValue;
saveData((byte*)dayProg, sizeof(dayProg), EEPROM_OFSD);
}
else if(mLevel==LEVEL_DATETIME)
adjustDateTime();
else if(mLevel==LEVEL_OFSTEMP) {
saveData((byte*)&forceData, sizeof(forceData), EEPROM_OFSF);
}
else if(mLevel==LEVEL_FORCE) {
// calcolo dell'endPoint
if(DEBUG)
Serial.println("setForce");
int hf=forceData.hForce;
if(hf==0) {
forceData.tFin=0;
}
else {
if(hf<0)
hf*=-1; // date always in the future :)
DateTime now = rtc.now(); //get the current date-time
unsigned long nt=now.get();
nt+=((unsigned long)hf*3600);
if(DEBUG)
Serial.println(nt);
DateTime tFin(nt);
if(DEBUG) {
char txt[17];
sprintf(txt, "%02d/%02d/%4d %02d:%02d", tFin.date(), tFin.month(), tFin.year(), tFin.hour(), tFin.minute());
Serial.println(txt);
Serial.println("endSetForce");
}
forceData.tFin=tFin.get();
}
saveData((byte*)&forceData, sizeof(forceData), EEPROM_OFSF);
}
else if(mLevel==LEVEL_OFF) {
saveData((byte*)&forceData, sizeof(forceData), EEPROM_OFSF);
}
bProgram=false;
bForce=true;
lcd.noBlink();
}
}
}
}
void printProgEvent() {
lcd.setCursor(0,0);
lcd.print(sPrograms[smLevel]);
lcd.setCursor(13,0);
lcd.print("["+String(smValue)+"]");
lcd.setCursor(0,1);
char sTemp[5];
float fTemp=progs[smLevel].T[smValue];
fTemp/=10.0;
dtostrf(fTemp, 2, 1, sTemp);
char txt[17];
sprintf(txt, "%02d:%02d -> %s\xdf", progs[smLevel].HM[smValue]>>8, progs[smLevel].HM[smValue]&0xFF, sTemp);
lcd.print(txt);
}
void updateForce() {
lcd.setCursor(0,1);
if(forceData.hForce>23 || forceData.hForce<-23) {
lcd.print(forceData.hForce/24);
lcd.print(" day ");
}
else {
lcd.print(forceData.hForce);
lcd.print(" hour ");
}
lcd.setCursor(0,1);
}
void updateOFF() {
lcd.setCursor(0,1);
if(forceData.bOFF) {
lcd.print(" OFF ");
}
else {
lcd.print(" ON ");
}
lcd.setCursor(0,1);
}
void updateOfs() {
lcd.setCursor(0,1);
char sTemp[5];
float fTemp=forceData.ofsTemp;
fTemp/=10.0;
dtostrf(fTemp, 2, 1, sTemp);
lcd.print(sTemp);
lcd.print("\xdf ");
lcd.setCursor(0,1);
}
void showOnlyTemp(float t) {
lcd.clear();
char sTemp[6];
dtostrf(t, 2, 1, sTemp);
lcd.setCursor(6, 1);
sTemp[4]='\xdf';
sTemp[5]='\x0';
lcd.print(sTemp);
lcd.setCursor(14, 1);
if(bFire)
lcd.write(byte(cFire));
else
lcd.write(byte(cNoFire));
}
void showInfo() {
char txt[17];
DateTime now = rtc.now(); //get the current date-time
lcd.clear();
lcd.setCursor(0, 0);
sprintf(txt, "%s %02d %s %02d:%02d", sDays[now.dayOfWeek()].c_str(), now.date(), sMonth[now.month()-1].c_str(), now.hour(), now.minute());
lcd.print(txt);
lcd.setCursor(0, 1);
char sTemp[6];
dtostrf(readTemp(), 2, 1, sTemp);
sTemp[5]='\x0';
char c=cNoFire; // no flame
if(bFire)
c=cFire;
sprintf(txt, "[%c] %s\xdf %c%c%c", cProgram[dayProg[now.dayOfWeek()]], sTemp, c,c,c);
lcd.print(txt);
}
void showDateTimePage(bool bGet/*=false*/) {
char txt[17];
if(bGet) {
DateTime now=rtc.now(); //get the current date-time
arrayDT[0]=now.date();
arrayDT[1]=now.month();
arrayDT[2]=now.year();
arrayDT[3]=now.hour();
arrayDT[4]=now.minute();
}
lcd.setCursor(0, 1);
sprintf(txt, "%02d/%02d/%4d %02d:%02d", arrayDT[0], arrayDT[1], arrayDT[2], arrayDT[3], arrayDT[4]);
lcd.print(txt);
if(DEBUG)
Serial.println(txt);
}
void adjustDateTime() {
if(DEBUG) {
Serial.print(arrayDT[0]);
Serial.print(arrayDT[1]);
Serial.print(arrayDT[2]);
Serial.print(arrayDT[3]);
Serial.println(arrayDT[4]);
}
DateTime dtAdjust(arrayDT[2], arrayDT[1], arrayDT[0], arrayDT[3], arrayDT[4], 0);
rtc.setDateTime(dtAdjust);
}
int pointLCD=0;
int lineLCD=0;
int ds=1;
void idlePage(float t, bool bReset) {
if(pointLCD==12 && lineLCD==0 && ds==1) {
pointLCD=11;
lineLCD=1;
ds=-1;
lcd.clear();
}
else if(bReset || (pointLCD==-1 && lineLCD==1 && ds==-1)) {
pointLCD=0;
lineLCD=0;
ds=1;
lcd.clear();
}
else if(ds==1) {
lcd.scrollDisplayRight();
pointLCD+=ds;
return;
}
else if(ds==-1) {
lcd.scrollDisplayLeft();
pointLCD+=ds;
return;
}
// temp
char sTemp[6];
dtostrf(t, 2, 1, sTemp);
sTemp[4]='\xdf';
sTemp[5]='\x0';
lcd.setCursor(pointLCD, lineLCD);
lcd.print(sTemp);
// flame
char sFlame[5];
if(bFire) {
sFlame[0]=sFlame[1]=sFlame[2]=sFlame[3]=cFire;
sFlame[4]='\x0';
}
else {
sFlame[0]=sFlame[1]=sFlame[2]=sFlame[3]=cNoFire;
sFlame[4]='\x0';
}
lcd.setCursor(pointLCD, (lineLCD+1)%2);
lcd.print(sFlame);
//lcd.write(byte(cNoFire));
pointLCD+=ds;
}
/*
* scheduler per programma CronoTermostato_FA
* by Fabrizio Allevi
* fabrizio . allevi @ tiscali . it
*/
void handleCaldaia() {
if(bFire && !forceData.bOFF)
digitalWrite(CALDAIA, LOW);
else
digitalWrite(CALDAIA, HIGH);
}
float readTemp() {
tempsensor.wake(); // wake up, ready to read!
float c=tempsensor.readTempC();
float o=forceData.ofsTemp;
o/=10;
o+=c;
if(DEBUG) {
Serial.print("TempRead: ");
Serial.print(c, 4);
Serial.print("C - TempOfs: ");
Serial.print(o, 4);
Serial.println("C");
}
tempsensor.shutdown_wake(1); // shutdown MSP9808 - power consumption ~0.1 mikro Ampere, stops temperature sampling
return o;
}
void scheduler(float tempNow) {
DateTime now = rtc.now(); //get the current date-time
int hh=now.hour();
int minuteNow=hh*60+now.minute();
int idProg=dayProg[now.dayOfWeek()];
if(DEBUG) {
Serial.print("minuteNow=");
Serial.print(minuteNow);
Serial.print(" idProg=");
Serial.println(idProg);
}
// controllo force functionality
if(forceData.hForce) {
if(forceData.tFin>now.get()) {
if(forceData.hForce>0 && hh>7 && hh<23) // forza ma non di notte
bFire=true;
else
bFire=false;
if(DEBUG) {
Serial.print("forceFor=");
Serial.print(forceData.hForce);
Serial.println(" hour");
}
return;
}
else {
forceData.hForce=0;
forceData.tFin=0;
saveData((byte*)&forceData, sizeof(forceData), EEPROM_OFSF);
}
}
// check sequences
for(int seq=0; seq<MAX_SEQ-1; seq++) {
int minI=(progs[idProg].HM[seq]>>8)*60+(progs[idProg].HM[seq]&0xFF);
int minF=(progs[idProg].HM[seq+1]>>8)*60+(progs[idProg].HM[seq+1]&0xFF);
if(minI<=minuteNow && (minuteNow<minF || minF==0)) { // trovata la seq in corso
float setPtemp=progs[idProg].T[seq]; // setpoint di temperatura da tenere in questa sequenza
setPtemp/=10.0;
if(DEBUG) {
Serial.print("seq=");
Serial.print(seq);
Serial.print(" minI=");
Serial.print(minI);
Serial.print(" minF=");
Serial.print(minF);
Serial.print(" temp=");
Serial.println(setPtemp);
}
if((!bFire && tempNow<setPtemp) || (bFire && tempNow<(setPtemp+0.15f)))
bFire=true;
else
bFire=false;
break;
}
}
}
Comments
Please log in or sign up to comment.