// konstanty #define RTC_MODUL 1 #define LCD_MODUL 1 #define PLUS 50 // 50 pin rele, ktere na servu pridava #define MINUS 52 // 52 pin rele, ktere na servu ubira #define LED 13 // stavova LED dioda v rezimech blika (vse v poradku), sviti (chyba cidla nebo zaseknuty procesor), nesviti (zaseknuty procesor) #define DS 40 // 40 sbernice cidel Dallas #define TEMP_RESOLUTION 11 // bitove rozliseni presnosti Dallas cidel: 9 - 0.5 stupne, 10 - 0.25 stupne, 11 - 0.125 stupne, 12 - 0.06 stupne #define RELE_ON 0 // rele spina na GND #define RELE_OFF 1 #define NECITLIVOST 2 // pocet stupnu necitlivosti od pozadovane teploty, kdy nebude servo reagovat #define TASKER_MAX_TASKS 10 //maximalni pocet tasku // knihovny #include // http://www.pjrc.com/teensy/td_libs_OneWire.html #include // https://github.com/milesburton/Arduino-Temperature-Control-Library #include #include // watchdog ta je už v arduino od instalace. Slouzi k autoresetu v pripade zaseknuti procesoru #include #include #include #if RTC_MODUL #include #endif #if LCD_MODUL #include #endif // promenne static byte dsKotelVratka[8] = {0x28, 0x5C, 0x87, 0x28, 0x00, 0x00, 0x80, 0x37}; // adresy Dallas cidel static byte dsKotelTopna[8] = {0x28, 0xFF, 0xB6, 0x68, 0x84, 0x16, 0x04, 0xCC}; static byte dsNadrzNahore[8] = {0x28, 0x89, 0x9E, 0x28, 0x00, 0x00, 0x80, 0x97}; static byte dsNadrzDole[8] = {0x28, 0x92, 0x93, 0x28, 0x00, 0x00, 0x80, 0xD8}; int tempKotelZadana = 60; // pozadovana teplota kotle float tempKotelVratka = 0; float tempKotelTopna = 0; float tempNadrzNahore = 0; float tempNadrzDole = 0; float teplotaDocasna; unsigned long krokServa = 120; // doba chodu serva v sekundach byte Servo1 = 0; // procento otevreni serva #if RTC_MODUL DS1302RTC RTC(27, 29, 31); #endif #if LCD_MODUL LiquidCrystal lcd(8, 9, 4, 5, 6, 7); #endif boolean servoJede = true; // priznak, ze je servo v chodu byte vynechCyklu = 0; // promenna pro vynechani cyklu regulace pri pohybu serva boolean heart = true; // funkce blikani // PID konstanty float Kp = .5; // Initial Proportional Gain float Ki = .07; // Initial Integral Gain float Kd = .1; // Initial Differential Gain double Input, Output; //These are just variables for storing values double Setpoint = tempKotelZadana; PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, REVERSE); // This sets up our PDID Loop OneWire oneWire(DS); // instance 1-wire komunikace DallasTemperature sensors(&oneWire); // pripojeni na knihovnu Dallas pro prepocet dat na teplotu Tasker tasker; void setup() { #if RTC_MODUL #define DS1302_GND_PIN 33 #define DS1302_VCC_PIN 35 digitalWrite(DS1302_GND_PIN, LOW); pinMode(DS1302_GND_PIN, OUTPUT); digitalWrite(DS1302_VCC_PIN, HIGH); pinMode(DS1302_VCC_PIN, OUTPUT); RTC.writeEN(true); setSyncProvider(RTC.get); #endif #if LCD_MODUL lcd.begin(16, 2); lcd.setCursor(1, 0); lcd.print("Regulace v 1.0"); lcd.setCursor(3, 1); lcd.print("Startuji..."); #endif pinMode(PLUS, OUTPUT); // nastav pin na vystup pinMode(MINUS, OUTPUT); // nastav pin na vystup pinMode(LED_BUILTIN, OUTPUT); // nastav pin na vystup blikejLED(); // rozsvit LED sensors.begin(); // spusteni komuniakce 1-wire sensors.setWaitForConversion(false); sensors.setResolution(TEMP_RESOLUTION); Serial.begin(9600); // start serioveho portu #if RTC_MODUL Servo1 = RTC.readRTC(DS1302_RAM_START+(0x00 << 1)); Serial.print("Poloha serva nactena z NVRAM: "); Serial.print(Servo1); Serial.println("%"); #else digitalWrite(PLUS, RELE_OFF); //vypnout rele pridavani digitalWrite(MINUS, RELE_ON); //nastaveni serva do nuly Serial.print("Nastavuji servo do vychozi polohy - prosim cekejte"); // delay(krokServa*1000); // delay(60000); Serial.println("....HOTOVO"); #endif vypniServo(); Serial.println("Cas;Output;Servo;KotelVratka;KotelTopna;NadrzNahore;NadrzDole"); myPID.SetMode(AUTOMATIC); //Turn on the PID loop myPID.SetSampleTime(10000); myPID.SetOutputLimits(0,100); cmdInit(&Serial); cmdAdd("reg", cmdRegulace); cmdAdd("ser", cmdServo); cmdAdd("pid", cmdPIDParams); cmdAdd("tep", cmdSetTemp); tasker.setTimeout(vyzadejTeploty, 9500); tasker.setInterval(regulaceKotle, 10000); tasker.setInterval(blikejLED, 1000); tasker.setInterval(cmdPoll, 500); #if LCD_MODUL tasker.setInterval(zobrazLCD, 5000); #endif tasker.run(); } void loop() { } void blikejLED() { digitalWrite(LED_BUILTIN, heart); heart = !heart; } void vyzadejTeploty() { //vyzada si teploty od vsech Dallas cidel sensors.requestTemperatures(); tasker.setTimeout(nactiTeploty, 400); } void nactiTeploty() { tempKotelVratka = sensors.getTempC(dsKotelVratka); tempKotelTopna = sensors.getTempC(dsKotelTopna); tempNadrzNahore = sensors.getTempC(dsNadrzNahore); tempNadrzDole = sensors.getTempC(dsNadrzDole); } void regulaceKotle() { if (teplotaDocasna > 0) tempKotelVratka = teplotaDocasna; if (vynechCyklu == 0) { Input = tempKotelVratka; if (myPID.GetMode() && servoJede == false && (Input <= Setpoint - NECITLIVOST || Input >= Setpoint + NECITLIVOST)) { myPID.Compute(); nastavServo(Output); } else Output = 0; } else { vynechCyklu--; // jedno kolo vynechano, snizime vynechavku Output = 0; } print2(hour()); Serial.print(":"); print2(minute()); Serial.print(":"); print2(second()); Serial.print(";"); Serial.print(Output); Serial.print(";"); Serial.print(Servo1); Serial.print(";"); Serial.print(tempKotelVratka, 1); Serial.print(";"); Serial.print(tempKotelTopna, 1); Serial.print(";"); Serial.print(tempNadrzNahore, 1); Serial.print(";"); Serial.print(tempNadrzDole, 1); Serial.println(); tasker.setTimeout(vyzadejTeploty, 9500); } void vypniServo() { digitalWrite(PLUS, RELE_OFF); // blokuj rele pridavani digitalWrite(MINUS, RELE_OFF); // blokuj rele ubirani servoJede = false; } void nastavServo(int procent) { int zmena = procent - Servo1; if (zmena <= NECITLIVOST && zmena >= -NECITLIVOST) return; //zadna, nebo jen mala zmena pozadovana, servem se tocit nebude if (zmena > 0) { digitalWrite(PLUS, RELE_ON); // sepni rele pridavani digitalWrite(MINUS, RELE_OFF); // blokuj rele ubirani } else { zmena = abs(zmena); digitalWrite(PLUS, RELE_OFF); // blokuj rele pridavani digitalWrite(MINUS, RELE_ON); // sepni rele ubirani } Servo1 = procent; #if RTC_MODUL RTC.writeRTC(DS1302_RAM_START+(0x00 << 1), Servo1); // zapisem polohu serva do RTC RAM #endif servoJede = true; tasker.setTimeout(vypniServo, zmena*krokServa*10); if (zmena >= 5) vynechCyklu = 1; // pri vetsim zasahu do ventilu vynech jedno kolo regulace, kvuli zpozdeni soustavy if (zmena >= 10) vynechCyklu = 2; if (zmena >= 15) vynechCyklu = 3; if (zmena >= 20) vynechCyklu = 4; if (zmena >= 25) vynechCyklu = 5; } #if LCD_MODUL void zobrazLCD() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(tempKotelTopna, 1); lcd.setCursor(0, 1); lcd.print(tempKotelVratka, 1); lcd.setCursor(12, 0); lcd.print(tempNadrzNahore, 1); lcd.setCursor(12, 1); lcd.print(tempNadrzDole, 1); lcd.setCursor(7, 0); lcd.print(Servo1); } #endif void cmdRegulace(int arg_cnt, char **args) { if (arg_cnt > 1) { switch (atoi(args[1])) { case 0: myPID.SetMode(MANUAL); Serial.print("Regulace vypnuta"); break; case 1: myPID.SetMode(AUTOMATIC); Serial.print("Regulace zapnuta"); break; default: Serial.print("Nespravny parametr"); } } else Serial.print("Chybi parametr"); } void print2(int number) { if (number >= 0 && number < 10) { Serial.write('0'); } Serial.print(number); } void cmdServo(int arg_cnt, char **args) { if (arg_cnt > 1) { int proc = atoi(args[1]); if (proc > 100 || proc < 0) Serial.print("Nespravny parametr"); else { nastavServo(proc); Serial.print("Servo: "); Serial.print(proc); Serial.print("%"); } } else Serial.print("Chybi parametr"); } void cmdPIDParams(int arg_cnt, char **args) { if (arg_cnt > 3) { float P = atof(args[1]); float I = atof(args[2]); float D = atof(args[3]); myPID.SetTunings(P, I, D); Serial.print("Kp="); Serial.print(myPID.GetKp()); Serial.print(" Ki="); Serial.print(myPID.GetKi()); Serial.print(" Kd="); Serial.print(myPID.GetKd()); } else Serial.print("Chybi parametr"); } void cmdSetTemp(int arg_cnt, char **args) { if (arg_cnt > 1) { int temp = atoi(args[1]); teplotaDocasna = temp; Serial.print(temp); } else teplotaDocasna = 0; }