Repozitár projektu Ethernet termostat - strojové kódy: Github
Článok k projektu na Chiptron.cz: Izbový termostat - Arduino + Ethernet
Firmvér pre Ethernet termostat je plné dostupný v slovenskej jazykovej mutácii.
Podporte projekt Ethernet termostat cez PayPal. Podpora umožní pridať nové funkcionality v budúcnosti a otvorenie zdrojového kódu aplikácie: PayPal donate
Arduino Ethernet Wiznet W5100 / W5500 DS18B20 OneWire Dallas HTML Webserver WebSocket

Kompatibilný riadiaci hardvér pre Ethernet termostat:



Kompatibilné Ethernet moduly a shieldy pre Ethernet termostat:


Ethernet modul Wiznet USR-ES1
Ethernet modul Wiznet USR-ES1 (W5500 V2)
Kúpiť Techfun.sk

Izbový termostat - Arduino + Ethernet


Arduino je šikovná embeeded platforma, ktorú je možné využiť napríklad na stavbu izbového termostatu s Ethernet konektivitou a webserverom implementovaným na Arduine. Implementácia využíva Arduino Uno / Nano s čipom AVR ATmega328P v kombinácii s Ethernet shieldom Wiznet W5100 / W5500 s ktorým komunikuje cez SPI zbernicu, resp. ICSP header. Programovo funguje termostat v režime webservera, kde dokáže prijímať požiadavky (requesty) od klientov v sieti po HTTP protokole a odosielať na ne odpoveď (response) - HTML / JSON kód a vykonávať pri ich spustení funkcie backendu (vykonanie scriptu, ovládanie GPIO, zápis do pamäte, funkčnosť...). Termostat je prístupný z LAN siete v ktorej sa nachádza, pričom je vybavený webovým rozhraním ktoré slúži na konfiguráciu všetkých prvkov termostatu, t.j. cieľovej (referenčnej) teploty a hysterézy. Webserver umožňuje beh niekoľkých na sebe nezávislých HTML stránok, ktoré môžu mať informatívny (textový výstup), alebo aj funkcionálny charakter (implementovaný backend s funkciami termostatu). Webserver beží na štandardnom HTTP porte - 80.


Termostat dokáže automatizovane prostredníctvom GPIO výstupu ovládať signalizačné relé pre zapnutie / vypnutie kotla. Dokáže tak nahradiť existujúci izbový termostat a sprístupniť ho v sieti klientom. Termostat dokáže obsluhovať akékoľvek zariadenie s prehliadačom - počítač / smartfón / tablet / Smart TV a podobne. Ako rozhodovací algoritmus sa využíva cieľová teplota s hysterézou, ktorá sa porovnáva s nameranou teplotou z digitálneho senzora teploty Dallas DS18B20. Cieľová teplota a hysteréza je načítaná z EEPROM pamäte, kde je uchovaná aj v prípade výpadku napájania permanentne a pri zápise nových dát sa prepíše. Rozlíšenie senzora DS18B20 pri meraní je 12-bitov, čomu rozpovedá rozlíšenie na teploty 0.0625 °C, čo je minimálny rozlišovací krok medzi rozlišnými meraniami. Dáta po OneWire zbernici môžu prísť do mikrokontroléru po vyžiadaní za 500 až 1000 ms v závislosti od počtu senzorov na OneWire zbernici, dĺžke zbernice a podobne... Rozhodovacia logika termostatu sa vykonáva každých 10 sekúnd nezávisle na webaplikácii, nevyžaduje sa keep-alive spojenie pre vykonanie logiky, systém tak funguje autonómne a nevyžaduje pozornosť používateľa.

Po hardvérovej stránke projekt využíva:
  • Arduino Uno
  • Ethernet shield Wiznet W5100 / Ethernet modul Wiznet W5200-W5500
  • Teplotný senzor DS18B20 na OneWire zbernici v puzdre TO-92, alebo vo vodotesnom vyhotovení v hliníkovej rúrke
  • Elektromagnetické relé SRD-5VDC-SL-C / SSR relé OMRON G3MB-202P slúžiace na spínanie kotla (Active-LOW signál)
  • HTML stránky bežiace na Arduine:

  • / - root stránka obsahujúca formulár, aktuálny výpis logického výstupu pre relé, teplotu
  • /action.html - spracúvava hodnoty z formulára, zapisuje ich do EEPROM pamäte, presmeruje používateľa späť na root stránku
  • /get_data.json - distribuuje dáta o aktuálnej teplote, referenčnej teplote a hysteréza tretej strane (počítač, mikrokontróler, iný klient...) v JSON formáte

  • Elektromagnetické relé SRD-5VDC-SL-C, ktoré je v projekte použité umožňuje spínať až 10A pri 230V - maximálny výkon 2300W. V prípade spínania jednosmerného obvodu (záťaže) je možné spínať 300W (10A pri 30V DC max). Pre schému zapojenia je plne kompatibilné aj SSR relé OMRON G3MB-202P, ktoré je vhodné iba pre neindukčnú záťaž a výhradne pre záťaž so striedavým napätím (DC obvod nedokáže po zopnutí rozopnúť). Maximálny spínaný výkon 460W (230V, 2A). Termostat je možné používať celoročne. V prípade nepotrebného riadenia je možné výstup fyzicky odpojiť a termostat využívať ako Ethernet teplomer pre zisk dát z miestnosti, kde sa nachádza.

    Webové rozhranie pre Ethernet termostat umožňuje:
  • Prehliadať v reálnom čase teplotu zo senzora DS18B20 na OneWire zbernici, uptime zariadenia, stav výstupu s dynamickou zmenou, aktuálne nastavené konfiguračné údaje pre termostat t.j. cieľovú teplotu a hysterézu z EEPROM
  • Modifikovať cieľovú (referenčnú) teplotu v rozsahu 5 až 50 °C s 0,25 °C krokom
  • Modifikovať hysterézu v rozsahu 0 až 10 °C s 0,25 °C krokom
  • ZAP/VYP regulácia kotla:
  • Príklad ZAP/VYP regulácie vykurovania - VIZUALIZÁCIA NIE JE SÚČASŤOU PROJEKTU
  • Kotol je aktívny po dobu dostiahnutia cieľovej teploty + hysterézy
  • Na vizualizácii teplôt vody je patrný tzv. dobeh vykurovania a následné chladnutie vody až do opätovnej aktivity vykurovania, kedy je nameraná teplota pod nastavenú cieľovú teplotu - hystérzu
  • ZAP/VYP regulácia kotla s hysterézou

    Webové rozhranie je navrhnuté pre prispôsobenie sa väčším i menším obrazovkám. Je reponzívne, podporuje širokouhlé obrazovky s vysokým rozlíšením, ale aj mobilné zariadenia. Rozhranie využíva importované CSS štýly Bootstrap frameworku z externého CDN servera, ktorý načíta client-side zariadenie pri otvorení stránky bežiacej na Arduine. Nakoľko je Arduino Uno limitované pamäťou, dokáže spustiť iba stránky s veľkosťou pár kB. Importovaním CSS štýlov z externého servera umožní žnížiť výkonové a pamäťové zaťaženie Arduina. Programová implementácia (pri Arduine Uno) využíva cca 70% flash pamäte (32kB - 4kB Bootloader) a 44% RAM pamäte (2kB). Statické časti webovej stránky (hlavička a pätička HTML dokumentu, linkovanie Bootstrap CSS, meta tagy, HTTP response hlavička, Content Type, formulár a ďalšie) sú uložené priamo vo flash pamäti Arduina, čo dokáže výrazne redukovať veľkosť používanej RAM pamäte pre obsah generovaný používateľovi. Webserver je tak stabilnejší a zvláda aj multi-pripojenie viacerých zariadení v sieti súčasne. Spotreba celého termostatu je na úrovni do 200 mA pri 5V napájaní - pod 1W.


    Aby ostali nastavené hodnoty zachované aj po výpadku napájania, sú uložené do EEPROM pamäte Arduina (celkom k dispozícii 512 B). Referenčná teplota je zapísaná na offset 10, hysteréza na offset 100. Každá z hodnôt zaberá maximálne 5B v EEPROM pamäti + ukončovací znak - terminátor. Limit prepisov EEPROM je na úrovni 100-tisíc prepisov. Dáta sa prepisujú iba pri odoslaní HTML formulára. Prevádzka termostatu je tak maximálne šetrná k EEPROM pamäti s cieľom maximalizovať jej životnosť. V prípade, že zariadenie pri prvom spustení nemá nič uložené na spomenutých EEPROM offsetoch, vykoná sa automatický zápis s predvolenými hodnotami - referencia: 20.25 °C, hysteréza 0.25 °C, tzv. fail-safe riešenie, aby bol termostat ihneď funkčný a pripravený k prevádzke.


    Prostredníctvom meta tagu Refresh vykonáva webserver obnovu celej stránky každých 30 sekúnd a prostredníctvom Javascriptu sa vypisuje do HTML stránky aj orientačný čas do refreshu. Do tohto času je potrebné stihnúť zapísať zmenu pre termostat, inak sa input okná pre číselné vstupy do formulára resetujú pri obnovení stránky. Na základe spätnej väzby od používateľov Android zariadení bol čas pre Refresh predĺžený z 10 na 30 sekúnd. Nakoľko built-in knižnica Ethernet neobsahuje využitie asynchrónneho webservera (ktorý je možné využiť napríklad u mikrokontrolérov Espressif ESP8266 / ESP32), je nutné prepisovať celú stránku. Dynamický údaj, ktorý sa predovšetkým mení je aktuálna hodnota výstupu - Zapnutý / Vypnutý, ktorý informuje prevádzkovateľa o skutočnom stave výstupu spoločne aj s farebným označením. Nakoľko sa logika systému vykonáva nezávisle na webserveri, do refreshu môže už byť výstup v inom stave, ako aktuálne vypísanom vo webaplikácii. Zmena výstupu je ihneď vypísaná napríklad na UART monitor (115200 baud/s). Na webovej stránke termostatu nájde používateľ aj informácie o uptime zariadenia (ako dlho beží), t.j. čas v dňoch, hodinách, minútach a sekundách.

    Autor Ethernet termostatu nezodpovedá za funkčnosť termostatu, poruchu kotla, úraz elektrickým prúdom pri neodbornej montáži termostatu do siete. Termostat je šírený pod MIT licenciou.
    Hlavná stránka pre modifikáciu cieľovej teploty a hysterézy - ukážka zapnutého:
    Ukážkové dáta
  • Cieľová teplota: 22.75 °C
  • Hysteréza: 0.25 °C
  • Namerané dáta: 22.49 °C
  • Výstup: Zapnutý

  • Termostat vykuruje od nameranej teploty 22.49 °C a nižšej. V prípade dosiahnutia teploty 23.01 °C a vyššej sa výstup vypne, signalizačné relé sa rozpojí a plynový kotol prestáva kúriť. Nastáva dobeh vykurovania a chladnutie miestnosti v ktorej sa vykonávajú merania. Termostat sa opäť aktivuje až pri dosiahnutí teploty 22.49 °C, alebo nižšej.


    Plnefunkčný Ethernet termostat s možnosťou nastavenia riadiach dát:


    Refresh webového rozhrania automaticky každých 30 sekúnd
    Ethernet termostat - Hlavný prehľad s modifikáciou cieľovej teploty a hysterézy - Vypnutý

    Validácia používateľského vstupu - zápis do EEPROM pri validných dátach


    Ethernet termostat - spracovanie údajov z HTML formulára, zápis do EEPROM Ethernet termostat - nesprávny dátový typ používateľského vstupu

    JSON štruktúra s dátami - aktuálna, cieľová teplota a hysteréza


    Ethernet termostat - JSON output

    UART Monitor výstup - logika systému, nastavenie Ethernet adaptéra


    Výstup cyklicky každých 10 sekúnd
    Ethernet termostat - UART výstup - IP adresa termostatu, logika, stav výstupu

    Dostupné knižnice pre mikrokontroléry (Arduino)


    Archív knižnice (.zip) rozbaliť do C:/Používatelia/[Používateľ]/Dokumenty/Arduino/libraries
    Názov knižnice Funkcia knižnice Stiahnuť
    Dallas

    Knižnica pre AVR mikrokontroléry (ATmega) Arduino Uno / Nano / Mega. Umožňuje komunikáciu so senzorom Dallas DS18B20 na OneWire zbernici. Možnosť komunikácie po normálnom, alebo parazitnom zapojení.

    Stiahnuť

    Zdrojový kód - Ethernet termostat - automatický režim

    /*|----------------------------------------------------------|*/
    /*|HTTP webserver - FORM - HTML - PROCESSING - EEPROM        |*/
    /*|AUTHOR: Martin Chlebovec                                  |*/
    /*|EMAIL: martinius96@gmail.com                              |*/
    /*|----------------------------------------------------------|*/
    #include <avr\wdt.h>
    #include <SPI.h>
    #include <Ethernet.h>
    #include <EEPROM.h>
    #include <OneWire.h>
    #include <DallasTemperature.h>
    
    #define ONE_WIRE_BUS 5
    OneWire oneWire(ONE_WIRE_BUS);
    DallasTemperature sensorsA(&oneWire);
    const int rele = 6;
    byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
    byte ip[] = { 192, 168, 4, 1 };
    EthernetServer server(80); //server port
    const char terminator1[2] = " ";
    const char terminator3[2] = "?";
    const char terminator4[2] = "&";
    const char terminator5[2] = "=";
    unsigned long cas = 0;
    
    long day = 86400000; // 86400000 milliseconds in a day
    long hour = 3600000; // 3600000 milliseconds in an hour
    long minute = 60000; // 60000 milliseconds in a minute
    long second =  1000; // 1000 milliseconds in a second
    
    float teplota;
    String stav;
    
    boolean isFloat(String tString) {
      String tBuf;
      boolean decPt = false;
    
      if (tString.charAt(0) == '+' || tString.charAt(0) == '-') tBuf = &tString[1];
      else tBuf = tString;
    
      for (int x = 0; x < tBuf.length(); x++)
      {
        if (tBuf.charAt(x) == '.') {
          if (decPt) return false;
          else decPt = true;
        }
        else if (tBuf.charAt(x) < '0' || tBuf.charAt(x) > '9') return false;
      }
      return true;
    }
    
    
    void writeString(char add, String data)
    {
      int _size = data.length();
      int i;
      for (i = 0; i < _size; i++)
      {
        EEPROM.write(add + i, data[i]);
      }
      EEPROM.write(add + _size, '\0'); //Add termination null character for String Data
    }
    
    
    String read_String(char add)
    {
      int i;
      char data[100]; //Max 100 Bytes
      int len = 0;
      unsigned char k;
      k = EEPROM.read(add);
      while (k != '\0' && len < 500) //Read until null character
      {
        k = EEPROM.read(add + len);
        data[len] = k;
        len++;
      }
      data[len] = '\0';
      return String(data);
    }
    
    void setup() {
      String a = read_String(10);
      String b = read_String(100);
      if (a == "" || a == NULL) {
        writeString(100, "0.25");
      }
      if (b == "" || b == NULL) {
        writeString(10, "20.25");
      }
      sensorsA.begin();
      pinMode(rele, OUTPUT);
      digitalWrite(rele, HIGH);
      Serial.begin(115200);
      sensorsA.requestTemperatures();
      delay(2000);
      //Ethernet.begin(mac); //DHCP
      Ethernet.begin(mac, ip); //STATIC IP
      server.begin();
      Serial.println(F("Webaplikaciu vytvoril: Martin Chlebovec"));
      Serial.println(F("Build 1.0.4 z 18. Sep 2021"));
      Serial.println(F("Ethernet shield na IP:"));
      Serial.println(Ethernet.localIP());
      wdt_enable(WDTO_8S);
    }
    
    void loop() {
      wdt_reset();
      if ((millis() - cas) >= 10000 || cas == 0) {
        //Ethernet.maintain(); //odkomentovat iba ak sa pouziva Ethernet.begin(mac);
        cas = millis();
        Serial.println(F("Ethernet shield na IP:"));
        Serial.println(Ethernet.localIP());
        teplota = sensorsA.getTempCByIndex(0);
        String referencia = read_String(10);
        String hystereza = read_String(100);
        float referencia_teplota = referencia.toFloat();
        float hystereza_teplota = hystereza.toFloat();
        float minus_hystereza_teplota = (-1 * hystereza_teplota);
        float rozdiel = referencia_teplota - teplota;
        if (rozdiel > hystereza_teplota) {
          Serial.println(F("Kotol zapnuty"));
          stav = "ZAP";
          digitalWrite(rele, LOW);
        } else if (rozdiel < minus_hystereza_teplota) {
          Serial.println(F("Kotol vypnuty"));
          stav = "VYP";
          digitalWrite(rele, HIGH);
        } else {
          Serial.println(F("Rozdiel cielovej a aktuálnej teploty nie je nad, ani pod hysterezou. Stav vystupu sa nemeni."));
          Serial.print(F("Aktualny stav vystupu pre kotol: "));
          Serial.println(stav);
        }
        sensorsA.requestTemperatures();
      }
      EthernetClient client = server.available();
      if (client) {
        while (client.connected()) {
          if (client.available()) {
            String line = client.readStringUntil('\n');
            char str[line.length() + 1];
            line.toCharArray(str, line.length());
            char *method;
            char *request;
            method = strtok(str, terminator1);
            request = strtok(NULL, terminator1);
            if (String(request) == "/") {
              int days = millis() / day ;                                //number of days
              unsigned int hours = (millis() % day) / hour;                       //the remainder from days division (in milliseconds) divided by hours, this gives the full hours
              unsigned int minutes = ((millis() % day) % hour) / minute ;         //and so on...
              unsigned int seconds = (((millis() % day) % hour) % minute) / second;
              //HLAVNA ROOT HTTP STRANKA
              client.println(F("HTTP/1.1 200 OK"));
              client.println(F("Content-Type: text/html"));
              client.println();
              client.println(F("<!DOCTYPE html>"));
              client.println(F("<html>"));
              client.println(F("<head>"));
              client.println(F("<script type='text/javascript'>"));
              client.println(F("window.smartlook||(function(d) {"));
              client.println(F("var o=smartlook=function(){ o.api.push(arguments)},h=d.getElementsByTagName('head')[0];"));
              client.println(F("var c=d.createElement('script');o.api=new Array();c.async=true;c.type='text/javascript';"));
              client.println(F("c.charset='utf-8';c.src='https://rec.smartlook.com/recorder.js';h.appendChild(c);"));
              client.println(F("})(document);"));
              client.println(F("smartlook('init', '6beae97f98b9844b761672af23f38fc60b962338');"));
              client.println(F("</script>"));
              client.println(F("<meta charset='utf-8'>"));
              client.println(F("<meta name='author' content='Martin Chlebovec'>"));
              client.println(F("<meta http-equiv='Refresh' content='30'; />"));
              client.println(F("<meta name='viewport' content='width=device-width, initial-scale=1'>"));
              client.println(F("<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css'>"));
              client.println(F("<script type='text/javascript'>"));
              client.println(F("var timeleft = 30;"));
              client.println(F("var downloadTimer = setInterval(function(){"));
              client.println(F("if(timeleft <= 0){"));
              client.println(F("clearInterval(downloadTimer);"));
              client.println(F("document.getElementById(\"countdown\").innerHTML = \"Refresh...\";"));
              client.println(F("} else {"));
              client.println(F("document.getElementById(\"countdown\").innerHTML = timeleft + \" sekúnd do refreshu\";"));
              client.println(F("}"));
              client.println(F("timeleft -= 1;"));
              client.println(F("}, 1000);"));
              client.println(F("</script>"));
              client.println(F("<title>HTTP webserver - Arduino + Ethernet</title>"));
              client.println(F("</head>"));
              client.println(F("<body>"));
              client.println(F("<center><h3>Zadajte dáta pre webserver (budú uložené do EEPROM):</h3>"));
              client.println(F("<form action='/action.html' method='get'>"));
              client.println("<b>Referenčná teplota:</b><br><input type='text' id='fname' name='fname' value=" + read_String(10) + "><br>");
              client.println("<b>Hysteréza:</b><br><input type='text' id='fname2' name='fname2' value=" + read_String(100) + "><br>");
              client.println(F("<input type='submit' class='btn btn-success' value='Zapísať'>"));
              client.println(F("</form><hr>"));
              if (stav == "ZAP") {
                client.println(F("<b><font color='green'>Výstup: Zapnutý</font></b>"));
              }
              if (stav == "VYP") {
                client.println(F("<b><font color='red'>Výstup: Vypnutý</font></b>"));
              }
              client.println(F("<div id=\"countdown\"></div>"));
              client.print(F("<b>Aktuálna teplota senzora DS18B20:</b> "));
              client.print(teplota);
              client.println(F(" °C"));
              client.print(F("<hr>"));
              client.print(F("<b>Uptime: </b>"));
              client.print(days);
              client.print(F("d"));
              client.print(F(" "));
              client.print(hours);
              client.print(F("h"));
              client.print(F(" "));
              client.print(minutes);
              client.print(F("m"));
              client.print(F(" "));
              client.print(seconds);
              client.print(F("s"));
              client.println(F("<h3>Autor: Martin Chlebovec - martinius96@gmail.com - https://martinius96.github.io/termostat-ethernet/</h3>"));
              client.println(F("<h4>Verzia free - 1.0.4 build: 18. Sep. 2021</h4>"));
              client.println(F("</center>"));
              client.println(F("</body>"));
              client.println(F("</html>"));
              delay(1);
              client.stop();
              client.flush();
            } else if (String(request) == "/get_data.json") {
              //PODSTRANKA PRE VYCITANIE DAT (INYM MIKROKONTROLEROM)
              client.println(F("HTTP/1.1 200 OK"));
              client.println(F("Content-Type: application/json"));
              client.println();
              client.println(F("{"));
              client.print(F("\"Hysteresis\":"));
              client.print(read_String(100));
              client.println(F(","));
              client.print(F("\"Target_Temperature\":"));
              client.print(read_String(10));
              client.println(F(","));
              client.print(F("\"Actual_Temperature\":"));
              client.println(String(teplota));
              client.println(F("}"));
              delay(1);
              client.stop();
              client.flush();
            } else if (String(request) == "/favicon.ico") { //fix chybajuceho faviconu
              client.stop();
            } else {
              String myString = String(request);
              if (myString.startsWith("/action.html")) {
                char* parameter;
                char* value;
                char* hodnota1;
                char* hodnota2;
                parameter = strtok(request, terminator3);
                Serial.println(parameter);
                value = strtok(NULL, terminator3);
                hodnota1 = strtok(value, terminator4);
                hodnota2 = strtok(NULL, terminator4);
                char* H_1;
                char* H_2;
                strtok(hodnota1, terminator5);
                H_1 = strtok(NULL, terminator5);
                strtok(hodnota2, terminator5);
                H_2 = strtok(NULL, terminator5);
                String first_param = String(H_1);
                String second_param = String(H_2);
                if (isFloat(first_param)) {
                  writeString(10, String(H_1));
                } else {
                  Serial.println(F("Pouzivatelsky vstup pre kluc fname (Cielova teplota) nie je cislo!"));
                }
                if (isFloat(second_param)) {
                  writeString(100, String(H_2));
                } else {
                  Serial.println(F("Pouzivatelsky vstup pre kluc fname2 (Hystereza) nie je cislo!"));
                }
                client.println(F("HTTP/1.1 200 OK"));
                client.println(F("Content-Type: text/html"));
                client.println();
                client.println(F("<!DOCTYPE html>"));
                client.println(F("<html>"));
                client.println(F("<head>"));
                client.println(F("<meta charset='utf-8'>"));
                client.println(F("<meta http-equiv='Refresh' content='5; url=/' />"));
                client.println(F("<title>HTTP webserver - Arduino + Ethernet</title>"));
                client.println(F("</head>"));
                client.println(F("<body>"));
                client.println(F("<center><h3>Server prijal data z formulára:</h3>"));
                if (!isFloat(second_param) || !isFloat(second_param)) {
                  client.println(F("<h3><font color='red'>Zadane udaje nie su cisla!!! Opakujte pokus po presmerovaní.</font></h3>"));
                } else {
                  client.println("<li><b>Referenčná teplota: </b>" + String(H_1) + "</li>");
                  client.println("<li><b>Hysteréza: </b>" + String(H_2) + "</li>");
                }
                client.println(F("<b>Presmerovanie... Prosim cakajte</b></center>"));
                client.println(F("</body>"));
                client.println(F("</html>"));
                delay(1);
                client.stop();
                client.flush();
              } else {
                client.println(F("HTTP/1.1 404 Not Found"));
                client.println();
                delay(1);
                client.stop();
                client.flush();
              }
            }
          }
        }
      }
    }
    
    

    Implementácia obsahuje programy pre statickú / dynamickú IPv4 adresu priradenú k Ethernet shieldu. Termostat je určený iba pre interiérové teploty! (nad 0°C), čomu je prispôsobená aj logika systému! Termostatom je možné nahradiť už existujúci izbový termostat, možno dočasne nahradiť ohrievač v akváriu / teráriu pre udržiavanie stálej teploty.