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).

Automatické řízení bazénu

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.

Schéma zapojení Automatického řízení bazénu

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

FB tw

Další podobné články