Automatické řízení bazénu pomocí Arduino vzniklo postupem času, kdy jsem začínal pouze s automatickým dávkováním chloru (chlornan sodný) pomocí programovatelného relé z Číny.
Nedávno jsem narazil na „výukovou“ sadu Arduino a řekl si, že by tam šel automatizovat celý bazén. Tak jsem si na zkoušku objednal pár komponent a začal zkoušet.
Plán byl takový:
1/ Automatický provoz - měl by mít za úkol ranní a večerní filtrování bazénu, přičemž přitom přidá požadovanou dávku chloru. Mimo tyto časové úseky bude hlídat teplotu bazénu a solárního ohřevu a dle podmínek bude ohřívat vodu v bazénu.
2/ Ruční provoz – po zmáčknutí tlačítka, přeruší automatický provoz a zapne pouze čerpadlo filtru. Tato funkce je zejména pro čištění bazénu, nebo pro promíchání přidaných chemikálií.
3/ Ruční dávka chloru – tato funkce je pro přidání chloru mimo automatický režim a je využívána víceméně po čištění bazénu, kdy se dopouští nová voda a je nutné vyrovnat hladinu chloru
4/ vypnutí – toto tlačítko vypne veškeré běžící funkce do doby, než se některá opět ručně spustí.
Vedlejší funkce:
Na LCD displeji se bude zobrazovat aktuální čas, teplota vody bazénu, aktuální zapnutá funkce a teplota solárního panelu (pouze při automatu).
Použité vybavení:
1/ Arduino NANO
2/ DS3231
3/ LCD 16x2 s I2C
4/ relé modul se 4 relé (stačily by tři)
5/ motorový kulový ventil 1“
6/ teplotní dvě teplotní čidla DS18B20
7/ peristaltické čerpadlo
8/ nezbytné součástky jako jsou 4 tlačítka, 4 LED, 5 rezistorů
Celé zapojení je velmi jednoduché. Tlačítka jsou pomocí funkce pinMode nastaveny jako INPUT_PULLUP na pinech 9,10,11,12,. Výstupy jsou pak na pinech A0-A3 a 8 (power LED). Obě tepelné čidla sdílí přes rezistor pin číslo 2. A nakonec LCD a RCT sdílí piny A4 a A5 (SCL,SDA). Na výstupech A0-A3 je zapojen relé modul a zároveň stavové LED, které dle aktuální činnosti svítí nebo nesvítí. Ty pak spínají hlavní filtrační čerpadlo, čerpadlo chloru a otevírají nebo zavírají ventil solárního ohřevu.
Rezistory k LED jsou 330Ω, k teplotním čidlům je pak rezistor 4k7Ω. LCD 1602 a RTC (DS3231) jsou připojeny pomocí SLC a SDA pinů (LCD na schématu nemá I2C řadič).
Program:
#include "Wire.h" #include <OneWire.h> OneWire oneWire(2); // ----------- Display ------------ #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcd(0x3F,16,2); // ----------- Display end ------------ // -------- Nastavení čidel ------------- #include <DallasTemperature.h> DallasTemperature sensors(&oneWire); DeviceAddress BAZEN = { 0x28, 0xFF, 0x99, 0x70, 0x01, 0x17, 0x03, 0x6F }; DeviceAddress PANEL = { 0x28, 0xFF, 0xF1, 0x88, 0x01, 0x17, 0x03, 0x2D }; // -------- Nastavení čidel konec------------- // ------- Nastavení vstupů a výstupů-------- int releFiltr = A0; // hlavní čerpadlo int releChlor = A1; // čerpadlo chloru int releVentil = A2; // ventil ohřevu int ledOn = A3; // Zelená (dvoubarevná LED) int ledOff = 8; // červená (dvoubarevná LED) int buttonOn = 12; // zapnutí automatu int buttonOff = 11; // vypnutí všeho int buttonFiltr = 10; // zapnutí filtrace int buttonChlor = 9; // Dávka chloru // nastavení proměnných pro teplotu a funkce float Tbazen; float Tpanel; boolean filtraceAuto, cisteni, chlor; // nastavení časování long previousMillis = 0; long previousMillisTemp = 0; long previousMillisTempP = 0; long interval = 1000; // čas snímání teploty (ms) long previousButton = 0; long intervalButton = 50; // čas omezení prokliku tlačítka // ------------ Nastavení hodin ------------ #define DS3231_I2C_ADDRESS 0x68 byte decToBcd(byte val){ return( (val/10*16) + (val%10) ); } byte bcdToDec(byte val){ return( (val/16*10) + (val%16) ); } // ------------ Nastavení hodin konec------------ void setup(){ Wire.begin(); Serial.begin(9600); // -------- nastavení pinů ------------ pinMode(releFiltr, OUTPUT); pinMode(releChlor, OUTPUT); pinMode(releVentil, OUTPUT); pinMode(ledOn, OUTPUT); pinMode(ledOff, OUTPUT); pinMode(buttonOn, INPUT_PULLUP); pinMode(buttonOff, INPUT_PULLUP); pinMode(buttonFiltr, INPUT_PULLUP); pinMode(buttonChlor, INPUT_PULLUP); lcd.init(); lcd.backlight(); lcd.setCursor(0,0); lcd.print("Automat. bazen"); lcd.setCursor(0,1); lcd.print("Dušan Vala"); delay(2000); lcd.setCursor(0,1); lcd.print(" "); lcd.setCursor(0,1); lcd.print("Vypnuto"); digitalWrite(ledOff, HIGH); // ---------- POUZE PRO NASTAVENÍ ČASU! --------------------- // sekundy, minuty, hodiny, den v týdnu (1- neděle), datum, měsíc, rok //setDS3231time(0,8,19,7,16,12,17); // ---------- POUZE PRO NASTAVENÍ ČASU! KONEC --------------- //nastavení rozlišení snímání čidel (9 - 0,5°C 10 - 0,25°C..) sensors.begin(); sensors.setResolution(9); } // ---------- POUZE PRO NASTAVENÍ ČASU! --------------------- /*void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year){ // sets time and date data to DS3231 Wire.beginTransmission(DS3231_I2C_ADDRESS); Wire.write(0); // set next input to start at the seconds register Wire.write(decToBcd(second)); // set seconds Wire.write(decToBcd(minute)); // set minutes Wire.write(decToBcd(hour)); // set hours Wire.write(decToBcd(dayOfWeek)); // set day of week (1=Sunday, 7=Saturday) Wire.write(decToBcd(dayOfMonth)); // set date (1 to 31) Wire.write(decToBcd(month)); // set month Wire.write(decToBcd(year)); // set year (0 to 99) Wire.endTransmission(); }*/ void readDS3231time(byte *second, byte *minute, byte *hour){ Wire.beginTransmission(DS3231_I2C_ADDRESS); Wire.write(0); Wire.endTransmission(); Wire.requestFrom(DS3231_I2C_ADDRESS, 7); *second = bcdToDec(Wire.read() & 0x7f); *minute = bcdToDec(Wire.read()); *hour = bcdToDec(Wire.read() & 0x3f); } void displayTime(){ // ------- Hodiny --------- byte second, minute, hour; readDS3231time(&second, &minute, &hour); lcd.setCursor(0,0); lcd.print(hour, DEC); lcd.print(":"); if (minute<10){ lcd.print("0"); } lcd.print(minute, DEC); lcd.print(":"); if (second<10){ lcd.print("0"); } lcd.print(second, DEC); //--------- Hodiny konec ---------- } //-------------------- LOOP -------------- void loop(){ // ------------- ZAPNUTÍ AUTOMATIKY -------- if (digitalRead(buttonOn) == LOW) { unsigned long currentMillis = millis(); if(currentMillis - previousButton > intervalButton) { previousButton = currentMillis; sensors.requestTemperatures(); off(); digitalWrite(ledOn, HIGH); digitalWrite(ledOff, LOW); lcd.setCursor(0,1); lcd.print(" "); lcd.setCursor(0,1); lcd.print("Automat"); filtraceAuto = true; } } // ----------- POUZE FILTRACE / ČÍŠTĚNÍ ------------ if (digitalRead(buttonFiltr) == LOW) { unsigned long currentMillis = millis(); if(currentMillis - previousButton > intervalButton) { previousButton = currentMillis; sensors.requestTemperatures(); off(); digitalWrite(ledOn, HIGH); digitalWrite(ledOff, LOW); lcd.setCursor(0,1); lcd.print(" "); lcd.setCursor(0,1); lcd.print("Cisteni/Filtrace"); cisteni = true; } } // -------- DÁVKOVÁNÍ CHLORU -------- if (digitalRead(buttonChlor) == LOW) { unsigned long currentMillis = millis(); if(currentMillis - previousButton > intervalButton) { previousButton = currentMillis; sensors.requestTemperatures(); off(); digitalWrite(ledOn, HIGH); digitalWrite(ledOff, LOW); digitalWrite(releChlor, HIGH); lcd.setCursor(0,1); lcd.print(" "); lcd.setCursor(0,1); lcd.print("Davka chloru"); chlor = true; } } // ------------- VYPNUTO ----------- if (digitalRead(buttonOff) == LOW) { unsigned long currentMillis = millis(); if(currentMillis - previousButton > intervalButton) { previousButton = currentMillis; sensors.requestTemperatures(); off(); } } filtraceChlor(); // načtení funkce denního programu filtrace(); // funkce pro filtraci nebo čištění displayTime(); // zobrazení hodin bazenTemp(); // zobrazení teploty bazénu if(filtraceAuto) { // pokud je aktivní automatický režim, panelTemp(); // je zobrazena teplota soláního panelu } } //-------------------- LOOP END ---------------- // ---------- VYPNUTÍ VŠECH FUNKCÍ -------------- void off(){ digitalWrite(releFiltr, LOW); digitalWrite(releChlor, LOW); digitalWrite(releVentil, LOW); digitalWrite(ledOn, LOW); digitalWrite(ledOff, HIGH); cisteni = false; filtraceAuto = false; chlor = false; lcd.setCursor(0,1); lcd.print(" "); lcd.setCursor(0,1); lcd.print("Vypnuto"); } // zapnutí pouze filtrace nebo dávky chloru void filtrace(){ if(cisteni || (chlor)){ digitalWrite(releFiltr, HIGH); //zapni čerpadlo digitalWrite(releVentil, LOW); //a vypni ventil pokud by byl otevřený if(chlor) { byte sec, minuty, hod; readDS3231time(&sec, &minuty, &hod); if((sec >= 5 & (sec < 45))) { digitalWrite(releChlor, HIGH); } else { if(sec == 59) { lcd.setCursor(0,1); lcd.print(" "); lcd.setCursor(0,1); lcd.print("Automat"); digitalWrite(releFiltr, LOW); filtraceAuto = true; chlor = false; } digitalWrite(releChlor, LOW); } } } else { if(!filtraceAuto){ digitalWrite(releFiltr, LOW); } } } /* ------------ AUTOMATICKÁ FILTRACE ---------- * Ráno a večer zapne filtraci a nadávkuje chlor * Mimo časovou filtraci zapíná denní program ohřevu bazénu */ void filtraceChlor(){ byte sec, minuty, hod; readDS3231time(&sec, &minuty, &hod); if(filtraceAuto & (!cisteni & (!chlor))) { // --------------- VEČER / RÁNO------------ if((hod >= 21 & (hod < 22) || (hod >= 5 & (hod < 6)))) { // filtrace pojede mezi 21-22hod večer a mezi 5-6hod ráno digitalWrite(releFiltr, HIGH); //zapni čerpadlo if((minuty == 0) & (sec >=5) & (sec <=45)) { // u každé filtrace bude přidán chlor po dobu 40s digitalWrite(releChlor, HIGH); } else { digitalWrite(releChlor, LOW); } } else { teplota(); // mimo daný čas zapne denní režim ohřevu bazénu } } } // ------------- DENNÍ PROGRAM OHŘEVU ------------- void teplota(){ unsigned long currentMillis = millis(); if(currentMillis - previousMillis > interval) { previousMillis = currentMillis; sensors.requestTemperatures(); Tbazen = sensors.getTempC(BAZEN); //teplota bazenu Tpanel = sensors.getTempC(PANEL); //teplota panelu float hystereze = 2; //rozdíl teplot pro zapnutí a vypnutí if(Tbazen == -127 || (Tpanel == -127)){ // pokud není čidlo připojeno hlásí teplotu -127 lcd.setCursor(9, 0); lcd.print("Chyba cidla!"); } else { if(Tbazen <= 24) {//pokud je teplota bazénu menší if((Tpanel - 28) > hystereze) { //a teplota panelu větší digitalWrite(releFiltr, HIGH); // zapni čerpadlo digitalWrite(releVentil, HIGH); // a uzavři ventil } else { if((28 - Tpanel) > hystereze) { // pokud teplota panelu klesne digitalWrite(releFiltr, LOW); // vypni čerpadlo } } } else { digitalWrite(releFiltr, LOW); // vypni čerpadlo digitalWrite(releVentil, LOW); // otevři ventil } } } } // ---------- TEPLOTA BAZÉNU ----------- void bazenTemp(){ unsigned long currentMillis = millis(); if(currentMillis - previousMillisTemp > interval) { previousMillisTemp = currentMillis; //načtení teploty z čidla sensors.requestTemperatures(); Tbazen = sensors.getTempC(BAZEN); if(Tbazen == -127){ lcd.setCursor(9, 0); lcd.print("Error"); } else { lcd.setCursor(9, 0); lcd.print(Tbazen); lcd.print((char)223); lcd.print("C"); } } } // ---------- TEPLOTA SOLÁRNÍHO PANELU ----------- void panelTemp(){ unsigned long currentMillis = millis(); if(currentMillis - previousMillisTempP > interval) { previousMillisTempP = currentMillis; //načtení teploty z čidla sensors.requestTemperatures(); Tpanel = sensors.getTempC(PANEL); if(Tpanel == -127){ lcd.setCursor(9, 1); lcd.print("Error"); } else { lcd.setCursor(9, 1); lcd.print(Tpanel); lcd.print((char)223); lcd.print("C"); } } }
Systém krom dávkování chloru je myslím celkem jednoduchý a jasný. Ta dávka chloru mimo program chce ještě doladit. Takhle vím, že ji mám spustit mezi nultou a pátou sekundou aby prošla celá. Zatím nevím jak líp to udělat. Jsem naprostý začátečník a tohle je můj naprosto první program co jsem zkoušel. Určitě by tam šlo udělat něco líp nebo jinak, ale pro mé potřeby to zatím stačí a myslím, že tím nepohrdne ani někdo další kdo nad něčím podobným přemýšlí. :)
Další vývoj projektu je aktualizován na webu https://github.com/Rellik12/BazenBot