#include // pro DS18S20 #include // pro DHT22 #include // SD karta #include // realny cas #include // I2C komunikace (SDA, SCL, CLK) pro display #include #define hystpokoj 0.2 //hystereze regulace pro pokojovou teplotu #define hystkotel 15.1 //hystereze regulace pro kotlovou teplotu #define teplotakotelmax 47.1 // vypne kotel pri : 57°C je rosny bod spalin => kondenzace v kotli : zpatecka do 57-5=52°C #define kotelzapcas 10 // maximalni cas souvisleho topeni [min] #define kotelvypcas 8 // minimalni cas nasledneho netopeni [min] #define pocetcasu 16 // casovac[0][pocetcasu] : 8.0 = hodnota z webu vypnuto, 8.1 = hodnota z webu zapnuto float casovac[4][pocetcasu] = { // predtapeni 40min, konec -10min { 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8}, // den v tydnu : 0=vse, 1=Po, 2=Ut, 3=St, 4=Ct, 5=Pa, 6=So, 7=Ne, 8=web { 0.00, 6.05, 16.30, 5.20, 16.30, 5.20, 16.30, 5.20, 16.30, 6.05, 16.30, 7.00, 9.00, 7.00, 9.00, 20.00}, // cas OD {33.00, 6.40, 22.00, 6.00, 22.00, 6.00, 22.00, 6.00, 22.00, 6.40, 22.00, 9.00, 22.00, 9.00, 22.00, 21.00}, // cas DO {19.50, 19.80, 21.00, 19.80, 21.00, 19.80, 21.00, 19.80, 21.00, 19.80, 21.00, 20.00, 21.00, 20.00, 21.00, 19.30} // teplota pokoj }; #define cetnost 20000 //cetnost logovani teplot v ms //#define cidlo1_address "40.120.209.1.5.0.0.14." //#define cidlo2_address "40.137.134.1.5.0.0.143." //#define cidlo3_address "40.251.63.180.1.0.0.75." #define cidlo1_address 14 #define cidlo2_address 143 #define cidlo3_address 75 #define rele1 5 // kotel #define rele2 6 // cerpadlo #define soubornazev "data.txt" #define soubornazeverr "dataerr.txt" OneWire ds(2); // DS18S20 Temperature chip on pin 2 LiquidCrystal_I2C lcd(0x27, 20, 4); // zapojeni SD ctecky // SCK pin 13 // MOSI pin 11 // MISO pin 12 const uint8_t CHIP_SELECT = 4; // SD card chip select pin. SdCard card; Fat16 file; // DHT22 - humidity sensor // DHT22 minimalni krok pro vlhkost i teplotu je 0,1 // DS18B20 minimalni krok pro teplotu je 0,06 // Connect a 4.7K resistor between VCC and the data pin (strong pullup) dht DHT; #define DHT22_PIN 7 // millis() je unsigned longs = 2^32 = 4294967296 unsigned long predchoziMillis, predchoziMillis2; //cas poseldni akce float cidloh1, cidloh1t, cidlot1, cidlot2, cidlot3 = 0; float tepkotelmax, teppokojmin = 0; float tempfloat; boolean stavkotle, stavcerpadla = 0; // zapnuto / vypnuto char stavchyb = 'A'; char stavregulace = 'A'; int rtc[7]; // aktualni datum a cas (0-6) : SEC, MIN, HR, DOW, DATE, MTH, YR int kdyzavyp[2][3]; // cas posledniho zapnuti a vypnuti kotle void setup(void) { #if defined(DEVMODE) Serial.begin(9600); #endif lcd.init();// inicializuje displej lcd.backlight(); // zapne podsvětlení lcd.print("Regulace kotle"); lcd.setCursor(0, 1); lcd.print("Made by OJ"); pinMode(53, OUTPUT); // SS pin pro SD kartu pinMode(rele1, OUTPUT); // kotel pinMode(rele2, OUTPUT); // cerpadlo digitalWrite(rele1, HIGH); // rele vypnuto digitalWrite(rele2, HIGH); tepkotelmax = teplotakotelmax; teppokojmin = casovac[3][0]; predchoziMillis = millis(); predchoziMillis2 = predchoziMillis; RTC.get(rtc, true); kdyzavyp[0][0] = rtc[2]; //hod kdyzavyp[0][1] = rtc[1]; //min kdyzavyp[0][2] = rtc[0]; //sec kdyzavyp[1][0] = rtc[2]; //hod kdyzavyp[1][1] = rtc[1]; //min kdyzavyp[1][2] = rtc[0]; //sec if (!card.begin(CHIP_SELECT)) SDerror("card.begin"); if (!Fat16::init(&card)) SDerror("Fat16::init"); // initialize a FAT16 volume } void loop(void) { if (millis() - predchoziMillis > cetnost) { // kazde 20sec se vyctou hodnoty cidel a zapisi na SD predchoziMillis = millis(); RTC.get(rtc, true); cticidla(); if (cidlot3 < 85.00) regulace(); sdzapisdata(); lcdzobraz(); } } void cticidla(void) { byte addr[8], data[8]; int chkdht = 0; chkdht = DHT.read22(DHT22_PIN); if (chkdht == DHTLIB_OK) { cidloh1 = DHT.humidity; cidloh1t = DHT.temperature; } else sdzapiserr("err DHT " + String(chkdht)); while ( ds.search(addr)) { // vycteni hodnot teploty 3 cidel DS18B20 if ( addr[0] != 0x28) { //Serial.print("Device is a DS18B20\n"); //Serial.print("Device family is not recognized: 0x"); //Serial.println(addr[0], HEX); sdzapiserr("err unknown DS18B20 " + String(addr[0])); return; } //byte cidlo = addr[7]; // for (int i = 0; i < 8; i++) cidlo = cidlo + addr[i] + "."; if ( OneWire::crc8( addr, 7) != addr[7]) { //Serial.println("CRC is not valid! Cidlo " + cidlo); sdzapiserr("err DS CRC " + String(addr[7])); return; } ds.reset(); ds.select(addr); ds.write(0x44); // start conversion, with parasite power on at the end delay(1000); // maybe 750ms is enough, maybe not byte present = ds.reset(); ds.select(addr); ds.write(0xBE); // Read Scratchpad for (byte i = 0; i < 9; i++) data[i] = ds.read(); // we need 10 bytes //Serial.print(" CRC="); //Serial.print( OneWire::crc8( data, 8), HEX); tempfloat = ((data[1] << 8) + data[0] ) * 0.0625; if (addr[7] == cidlo1_address) cidlot1 = tempfloat; else if (addr[7] == cidlo2_address) cidlot2 = tempfloat; else if (addr[7] == cidlo3_address) cidlot3 = tempfloat; else sdzapiserr("err nezname DS18B20 " + String(addr[7])); } } void regulace(void) { // casove rele nastavi pozadovanou teplotu podle hodnot v poli CASOVAC tempfloat = rtc[2] + (float(rtc[1]) / 100.00); // aktualni cas [hod.min] // rtc[3] je DOW for (byte i = 0; i < pocetcasu; i++) { if ((tempfloat >= casovac[1][i]) && (tempfloat < casovac[2][i]) && ((casovac[0][i] == 0.00) || (casovac[0][i] == rtc[3]))) { teppokojmin = casovac[3][i]; } } // stav reregulace tempfloat = 0; //promenna pro opravu casu kdy kotel topi pres pulnoc if (kdyzavyp[0][0] > rtc[2]) tempfloat = 1440; if (60 * kdyzavyp[1][0] + kdyzavyp[1][1] + kotelvypcas < 60 * rtc[2] + rtc[1] + tempfloat) stavregulace = 'A'; // ukonceni stavu prekroceni maximalniho casu souvisleho topeni if (stavkotle && (60 * kdyzavyp[0][0] + kdyzavyp[0][1] + 5 < 60 * rtc[2] + rtc[1] + tempfloat) && (teppokojmin - 0.1 < cidloh1t)) stavregulace = 'P'; // prekroceni maximalniho casu souvisleho topeni // regulace // zatop max do tepkotelmax if (((cidlot1 < tepkotelmax) || !stavcerpadla) && !stavkotle) if ((cidloh1t < teppokojmin)) if (stavregulace < 'P') { stavkotle = 1; // topi stavcerpadla = 1; // cerpadlo jede digitalWrite(rele2, LOW); // cerpadlo zapnout delay(1000); digitalWrite(rele1, LOW); // kotel zapnout delay(2000); kdyzavyp[0][0] = rtc[2]; //hod kdyzavyp[0][1] = rtc[1]; //min kdyzavyp[0][2] = rtc[0]; //sec } // vypnout kotel v tepkotelmax if (stavkotle && ((cidlot1 > (tepkotelmax + float(hystkotel))) || (((cidloh1t > (teppokojmin + float(hystpokoj))) || (stavregulace >= 'P'))))) { if (cidlot1 > (tepkotelmax + float(hystkotel))) stavregulace = 'B'; // prekroceni max teploty kotle stavkotle = 0; // netopi digitalWrite(rele1, HIGH); // kotel vypnout delay(2000); kdyzavyp[1][0] = rtc[2]; //hod kdyzavyp[1][1] = rtc[1]; //min kdyzavyp[1][2] = rtc[0]; //sec } // vypne cerpadlo kdyz rozdil na kotli < 5°C if (!stavkotle && stavcerpadla && ((cidlot1 - cidlot2) < 5.0)) if (((cidloh1t > teppokojmin))) { digitalWrite(rele2, HIGH); // cerpadlo vypnout delay(2000); stavcerpadla = 0; } if (stavkotle && !stavcerpadla) { // bezpecnostni kontrola zapnuti cerpadla digitalWrite(rele2, LOW); // cerpadlo zapnout stavcerpadla = 1; // cerpadlo jede sdzapiserr("err nezapnute cerpadlo"); } } void sdzapisdata(void) { file.writeError = false; if (!file.open(soubornazev, O_CREAT | O_APPEND | O_WRITE)) SDerror("open "); delay(300); file.println(String(rtc[4]) + "." + rtc[5] + "." + rtc[6] + " " + rtc[2] + ":" + rtc[1] + ":" + rtc[0] + ";" + cidloh1 + ";" + cidloh1t + ";" + cidlot1 + ";" + cidlot2 + ";" + cidlot3 + ";" + stavkotle + ";" + stavcerpadla + ";" + teppokojmin); if (file.writeError) SDerror("write "); if (!file.close()) SDerror("close "); } void sdzapiserr(const String radka) { file.writeError = false; if (!file.open(soubornazeverr, O_CREAT | O_APPEND | O_WRITE)) SDerror("open "); delay(300); stavchyb = 'B'; // vyskytla se chyba file.println(String(rtc[4]) + "." + rtc[5] + "." + rtc[6] + " " + rtc[2] + ":" + rtc[1] + ":" + rtc[0] + ";" + radka); if (file.writeError) SDerror("write "); if (!file.close()) SDerror("close "); } void SDerror(const char * str) { stavchyb = 'C'; // chyba zapisu na SD } void lcdzobraz(void) { lcd.clear(); lcd.setCursor(0, 0); // nastaví kurzor lcd.print(String(cidlot1, 2) + " " + cidlot2 + " " + cidlot3 + " " + stavchyb); // zobrazí text lcd.setCursor(0, 1); lcd.print(String(cidloh1, 2) + " " + cidloh1t + " " + kdyzavyp[0][0] + ":" + kdyzavyp[0][1] + ":" + kdyzavyp[0][2]); // zobrazí text lcd.setCursor(0, 2); lcd.print(String(rtc[3]) + rtc[2] + ":" + rtc[1] + ":" + rtc[0] + " " + teppokojmin + " " + stavkotle + stavcerpadla); // zobrazí text }