The WiFi_Thermostat and WiFi_Thermostat_mDNS firmware is available in English language for the ESP32 and ESP8266 microcontrollers (Espressif Systems). Experimental version of basic Thermostat firmware with manual output control is only available in the Slovak language.
Support the WiFi thermostat project via PayPal. Support will allow you to add new features in the future and open the source code of the application: PayPal donate
ESP8266 ESP32 WiFi DS18B20 OneWire Dallas HTML Webserver WebSocket JSON mDNS UART

WiFi thermostat - ESP8266 / ESP32


The software implementation is meticulously crafted for microcontroller platforms provided by Espressif Systems, featuring robust WiFi connectivity. Specifically, it seamlessly supports ESP8266 microcontrollers in versions 12E to F, commonly found in Wemos D1 Mini and NodeMCU v2/v3 platforms. Please note that ESP8266-01/ESP8266-01S microcontrollers and similar standalone chips predating the 12E generation are not compatible with the project's program recording requirements. They do not utilize the DIO flash method, essential for ESPTOOL calls in the project. Uploading firmware to these devices is not supported. For the ESP32 microcontroller, standalone chips like ESP32-WROOM-32 from Espressif and the equivalent ESP-32S from AI-Thinker can be utilized. All DevKits associated with these chips, including DevKit v1/DevKitC-v4, are fully compatible. The WiFi thermostat is seamlessly accessible within the LAN network where it is situated, equipped with an intuitive web interface for configuring all thermostat elements. Operating independently of the web application, the thermostat efficiently manages the boiler based on the measured and target temperature, along with hysteresis. To enhance accessibility, the thermostat can be complemented with an mDNS record, generating a local domain (hostname.local) accessible exclusively within the LAN network via multicast service. Connectivity configuration for the thermostat on the home WiFi LAN network is facilitated by WiFiManager, securely storing WiFi network data (SSID and password) in the microcontroller's flash memory. The one-time storage ensures persistent configuration memory, allowing full utilization of the thermostat upon gaining connectivity. Running on the ESP8266/ESP32 microcontroller, the HTTP web server supports multiple independent HTML pages, offering both informative and functional content.

In terms of hardware, the project uses:
  • ESP8266 / ESP32
  • DS18B20 temperature sensor on the OneWire bus
  • Relay SRD-5VDC-SL-C / OMRON G3MB-202P used for boiler switching (Active-LOW signal)
  • HTML pages running on ESP8266 / ESP32 platform:
  • / - root page containing form, current logic output for relay, current and target temperature, hysteresis
  • /action.html - processes values from the form, writes them to the emulated EEPROM memory, redirects the user back to the root page
  • /get_data.json - distributes data on current temperature, reference temperature and hysteresis to a third party (computer, microcontroller, other client ...) in JSON format - can be used with an example JSON client that can send data to MQTT Broker, for example to home automation
  • and other system subpages to control the output, change the mode

  • The DS18B20 sensor boasts a 12-bit measurement resolution, providing precision at 0.0625 °C increments. The OneWire bus efficiently returns data to the microcontroller within 500 to 1000 ms upon request. The SRD-5VDC-SL-C electromagnetic relay, a key component in the project, facilitates switching up to 10A at 230V, translating to a power capacity of 2300W. When handling a DC circuit (load), it supports up to 300W (10A at 30V DC). Alternatively, the OMRON G3MB-202P SSR relay is fully compatible with the wiring diagram, designed exclusively for non-inductive loads and AC circuits, with a maximum switching power of 460W (230V, 2A). The thermostat is designed for year-round use. If control is unnecessary, the output can be physically disconnected, transforming the thermostat into a WiFi thermometer for data retrieval from its location. The thermostat's logic operates independently every 10 seconds, unrelated to the web server or connected clients, eliminating the need for a keep-alive connection. With each logic execution, the thermostat logs information about the current IP address or mDNS record (if applicable) on the UART thermostat, providing users with accessibility details within the LAN network. Furthermore, it displays dynamic free memory (HEAP), ranging from 40 to 44kB, and the current output state with notifications of changes, such as when the decision threshold exceeds + or -. The 3V3 GPIO operating logic of ESP8266 and ESP32 microcontrollers suffices for digital signal changes, but it's crucial to power the relay with 5V from VUSB and VIN, respectively.

    Web interface for WiFi thermostat allows:
  • View in real time the temperature from the DS18B20 sensor, the device uptime, the output value with dynamic change, the currently set configuration data for the thermostat, i. target temperature and hysteresis
  • Modify the target (reference) temperature in the range of 5 to 50 °C with a 0.25 °C step
  • Modify hysteresis in the range of 0 to 10 °C with a 0.25 °C step
  • Program implementation of a thermostat with automatic mode is experimental!
    Boiler ON / OFF control - automatic mode:
  • Example ON / OFF of heating control - VISUALIZATION IS NOT PART OF THE PROJECT
  • The boiler is active until the target temperature + hysteresis is reached
  • The visualization of water temperatures shows the so-called heating run-up and subsequent cooling of the water until the heating activity is repeated, when the measured temperature is below the set target temperature - hysteresis
  • ZAP/VYP regulácia kotla s hysterézou

    In the basic version of the WiFi thermostat (without mDNS record), a manual control mode (hard ON / OFF) has been seamlessly integrated, offering the flexibility to switch between manual and automatic modes. The web interface is meticulously crafted to adapt seamlessly to both larger and smaller screens, ensuring a responsive design that caters to widescreen high-definition displays and mobile devices alike. To enhance performance and efficiency, the interface leverages imported CSS styles from the Bootstrap framework, sourced from an external CDN server. This approach optimally loads client-side resources when accessing pages running on the ESP microcontroller. By importing CSS styles from an external server, the WiFi thermostat efficiently mitigates power and memory load on the microcontroller, ensuring a smooth and responsive user experience.


    To ensure the persistence of thermostat settings even after a power outage, they are stored in the ESP's EEPROM memory, emulated within the flash memory due to the absence of a physical EEPROM chip on the platform. The reference temperature is assigned to offset 10, while the hysteresis is stored at offset 100. Each value utilizes a maximum of 5 bytes in the EEPROM memory, including the end character. Data overwriting occurs exclusively when the HTML form is submitted, ensuring a gentle operation that prolongs the EEPROM memory's durability. The output state exclusively resides in RAM memory, with no storage in the flash memory. Upon the device's initial startup, if there's no existing data on the mentioned EEPROM offsets, an automatic writing process initiates with default values—reference temperature: 20.25 °C, hysteresis: 0.00 °C, providing a fail-safe solution. ESP8266 and ESP32 utilize the EEPROM.put() function coupled with EEPROM.commit() for EEPROM memory read and write operations, respectively. Additionally, EEPROM.get() is employed to retrieve data from EEPROM, supporting any data type, including float() in our specific case. This comprehensive approach ensures efficient and reliable EEPROM memory management for the thermostat.

    Utilizing the Refresh meta tag, the web server systematically refreshes the entire page every 30 seconds. To enhance user experience, an approximate time for the upcoming refresh is dynamically inscribed into the HTML page using Javascript. It is imperative to apply any changes for the thermostat within this timeframe; otherwise, numerical input windows in the form may reset upon page refresh. Due to the limitations of the built-in Ethernet library, which lacks support for an asynchronous web server (commonly used with microcontrollers such as Espressif ESP8266 / ESP32), a page rewrite becomes necessary to accommodate the constraints of the current implementation. This ensures a seamless and uninterrupted operation of the thermostat interface.


    The dynamic data that undergoes frequent changes primarily includes the current output status - Turned on / Turned off, providing operators with real-time information about the output state, visually indicated through color coding. Due to the system logic operating independently of the web server, the output status may differ from the information presented in the web application. Immediate updates reflecting any change in the output are promptly communicated on the UART monitor. Additionally, the thermostat's website offers users insights into the device's uptime, showcasing the duration it has been operational, expressed in days, hours, minutes, and seconds. The thermostat is exclusively designed for indoor temperatures! (above 0 °C), with the system logic finely tuned to accommodate these conditions. Versatile in application, the thermostat can seamlessly replace an existing room thermostat or serve as a temporary substitute for an aquarium/terrarium heater, ensuring a consistent and controlled temperature.

    The author of the WiFi thermostat is not responsible for the functionality of the thermostat, boiler failure, electric shock due to improper installation of the thermostat in the network. The thermostat is distributed under the MIT license.
    Main page for modification of target temperature and hysteresis - example of switched on output:
    Sample data
  • Target temperature: 22.75 °C
  • Hysteresis: 0.25 °C
  • Measured data: 22.49 °C
  • Output: ON

  • The thermostat initiates heating when the measured temperature falls to 22.49 °C and below. Upon reaching 23.01 °C, the output is disengaged, the signaling relay opens, and the gas boiler ceases heating, marking the onset of a cooling phase for the room where measurements are conducted. The thermostat remains inactive until the temperature drops to 22.49 °C or lower, thereby triggering a new heating cycle. This precise temperature control ensures optimal comfort and energy efficiency in maintaining the desired room temperature.

    Main page for target temperature and hysteresis modification: WiFi thermostat - Main overview with modification of target temperature and hysteresis Processing of datas that user entered by HTML form: WiFi thermostat - data processing from HTML form JSON web server output in browser / client via websocket:
    WiFi termostat - JSON output
    Output to UART monitor - system logic + set IP address, mDNS record:
    WiFi termostat - UART output with mDNS record, IP address of thermostat

    Available libraries for microcontrollers (ESP)



    WiFi thermostat - source code - ESP8266

    /*|----------------------------------------------------------|*/
    /*|HTTP webserver - WiFi thermostat - ESP8266 + DS18B20      |*/
    /*|AUTHOR: Martin Chlebovec                                  |*/
    /*|EMAIL: martinius96@gmail.com                              |*/
    /*|DONATE: paypal.me/chlebovec                               |*/
    /*|----------------------------------------------------------|*/
    
    #include <ESP8266WiFi.h>
    #include <ESP8266WebServer.h>
    #include <WiFiManager.h>
    ESP8266WebServer server(80);
    #include <DNSServer.h>
    #include <EEPROM.h>
    #include <OneWire.h>
    #include <DallasTemperature.h>
    
    #define ONE_WIRE_BUS 5 //D1
    OneWire oneWire(ONE_WIRE_BUS);
    DallasTemperature sensorsA(&oneWire);
    const int rele = 4; //D2
    unsigned long cas = 0;
    String stav = "OFF";
    float teplota;
    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 rezim;
    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) == '.' || 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, float data)
    {
      EEPROM.put(add, (data * 1000));
      EEPROM.commit();
    }
    
    
    float read_String(char add)
    {
      float payload = 0;
      float data = EEPROM.get(add, payload);
      return (data / 1000);
    }
    
    void handleRoot() {
      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;
      String stranka = F("<!DOCTYPE html>");
      stranka += F("<html>");
      stranka += F("<head>");
      stranka += F("<meta charset='utf-8'>");
      stranka += F("<meta name='author' content='Martin Chlebovec'>");
      stranka += F("<meta http-equiv='Refresh' content='30'; />");
      stranka += F("<meta name='viewport' content='width=device-width, initial-scale=1'>");
      stranka += F("<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css'>");
      stranka += F("<script type='text/javascript'> window.smartlook||(function(d) {"); 
      stranka += F("var o=smartlook=function(){ o.api.push(arguments)},h=d.getElementsByTagName('head')[0];");
      stranka += F("var c=d.createElement('script');o.api=new Array();c.async=true;c.type='text/javascript';");
      stranka += F("c.charset='utf-8';c.src='https://rec.smartlook.com/recorder.js';h.appendChild(c); })(document);"); 
      stranka += F("smartlook('init', '63ff3bf4db2f85029b19856425a4f2086533c9e6');"); 
      stranka += F("</script>");  
      stranka += F("<script type='text/javascript'>");
      stranka += F("var timeleft = 30;");
      stranka += F("var downloadTimer = setInterval(function(){");
      stranka += F("if(timeleft <= 0){");
      stranka += F("clearInterval(downloadTimer);");
      stranka += F("document.getElementById(\"countdown\").innerHTML = \"Refreshing...\";");
      stranka += F("} else {");
      stranka += F("document.getElementById(\"countdown\").innerHTML = timeleft + \" seconds to refresh\";");
      stranka += F("}");
      stranka += F("timeleft -= 1;");
      stranka += F("}, 1000);");
      stranka += F("</script>");
      stranka += F("<title>WiFi thermostat - ESP8266</title>");
      stranka += F("</head>");
      stranka += F("<body>");
      stranka += F("<center><h3>WiFi thermostat - ESP8266:</h3>");
      if (rezim == 0.00) {
        stranka += F("<form action='/action.html' method='post'>");
        stranka += "<b>Target temperature:</b><br><input type='text' id='fname' name='fname' min='5' max='50' step='0.25' value=" + String(read_String(10)) + "><br>";
        stranka += "<b>Hysteresis:</b><br><input type='text' id='fname2' name='fname2' min='0' max='10' step='0.25' value=" + String(read_String(100)) + "><br>";
        stranka += F("<input type='submit' class='btn btn-success' value='Send'>");
        stranka += F("</form>");
        stranka += F("<a href='manual.html' class='btn btn-primary' role='button'>Manual mode</a><hr>");
      } else if (rezim == 1.00) {
        if (stav == "ON") {
          stranka += F("<a href='vyp.html' class='btn btn-danger' role='button'>Turn off</a><br>");
        }
        if (stav == "OFF") {
          stranka += F("<a href='zap.html' class='btn btn-success' role='button'>Turn on</a><br>");
        }
        stranka += F("<a href='automat.html' class='btn btn-primary' role='button'>Automatic mode</a><hr>");
      }
      if (stav == "ON") {
        stranka += F("<b><font color='green'>Output: ON</font></b>");
      }
      if (stav == "OFF") {
        stranka += F("<b><font color='red'>Output: OFF</font></b>");
      }
      stranka += F("<div id=\"countdown\"></div>");
      stranka += F("<b>Current sensor DS18B20 temperature:</b> ");
      stranka += String(teplota);
      stranka += F(" °C");
      stranka += F("<hr>");
      stranka += F("<b>Uptime: </b>");
      stranka += String(days);
      stranka += F("d");
      stranka += F(" ");
      stranka += String(hours);
      stranka += F("h");
      stranka += F(" ");
      stranka += String(minutes);
      stranka += F("m");
      stranka += F(" ");
      stranka += String(seconds);
      stranka += F("s");
      stranka += F("<h3>Author: Martin Chlebovec - martinius96@gmail.com - https://martinius96.github.io/WiFi-termostat/en/</h3>");
      stranka += F("<h4>Free version - 1.0.4 build: 06. Oct. 2021</h4>");
      stranka += F("</center>");
      stranka += F("</body>");
      stranka += F("</html>");
      server.send(200, "text/html", stranka);
    }
    
    void handleBody() {
      if (server.hasArg("fname")) {
        String target_temp = server.arg("fname");
        // float cielova_teplota = target_temp.toFloat();
    
        if (isFloat(target_temp)) {
          float cielova_teplota = target_temp.toFloat();
          writeString(10, cielova_teplota);
        } else {
          Serial.println(F("No number was entered in the input according to the target temperature!"));
          Serial.println(F("Writing into EEPROM prohibited!"));
        }
      }
      if (server.hasArg("fname2")) {
        String hysteresis = server.arg("fname2");
        if (isFloat(hysteresis)) {
          float hystereza = hysteresis.toFloat();
          writeString(100, hystereza);
        } else {
          Serial.println(F("No number was entered in the input according to the hysteresis!"));
          Serial.println(F("Writing into EEPROM prohibited!"));
        }
      }
      String stranka = F("<!DOCTYPE html>");
      stranka += F("<html>");
      stranka += F("<head>");
      stranka += F("<meta charset='utf-8'>");
      stranka += F("<meta http-equiv='Refresh' content='5; url=/' />");
      stranka += F("<title>WiFi thermostat - ESP8266 - data processing</title>");
      stranka += F("</head>");
      stranka += F("<body>");
      stranka += F("<center><h3>Server received data from HTML form:</h3>");
      stranka += "<li><b>Target temperature: </b>" + String(read_String(10)) + " °C</li>";
      stranka += "<li><b>Hysteresis: </b>" + String(read_String(100)) + " °C</li>";
      stranka += F("<b>Redirecting... Please wait</b></center>");
      stranka += F("</body>");
      stranka += F("</html>");
      server.send(200, "text/html", stranka);
    }
    
    void handleGet() {
      String stranka = "{\n";
      stranka += F("\"Hysteresis\":");
      stranka += String(read_String(100));
      stranka += F(",\n");
      stranka += F("\"Target_Temperature\":");
      stranka += String(read_String(10));
      stranka += F(",\n");
      stranka += F("\"Actual_Temperature\":");
      stranka += String(teplota) + "\n";
      stranka += F("}\n");
      server.send(200, "application/json", stranka);
    }
    
    void handleZAP() {
      stav = "ON";
      digitalWrite(rele, LOW);
      String stranka = F("<!DOCTYPE html>");
      stranka += F("<html>");
      stranka += F("<head>");
      stranka += F("<meta charset='utf-8'>");
      stranka += F("<meta http-equiv='Refresh' content='0; url=/' />");
      stranka += F("</head>");
      stranka += F("</html>");
      server.send(200, "text/html", stranka);
    }
    
    void handleAuto() {
      writeString(150, 0.00);
      rezim = read_String(150);
      String stranka = F("<!DOCTYPE html>");
      stranka += F("<html>");
      stranka += F("<head>");
      stranka += F("<meta charset='utf-8'>");
      stranka += F("<meta http-equiv='Refresh' content='0; url=/' />");
      stranka += F("</head>");
      stranka += F("</html>");
      server.send(200, "text/html", stranka);
    }
    void handleManual() {
      writeString(150, 1.00);
      rezim = read_String(150);
      String stranka = F("<!DOCTYPE html>");
      stranka += F("<html>");
      stranka += F("<head>");
      stranka += F("<meta charset='utf-8'>");
      stranka += F("<meta http-equiv='Refresh' content='0; url=/' />");
      stranka += F("</head>");
      stranka += F("</html>");
      server.send(200, "text/html", stranka);
    }
    void handleVYP() {
      stav = "OFF";
      digitalWrite(rele, HIGH);
      String stranka = F("<!DOCTYPE html>");
      stranka += F("<html>");
      stranka += F("<head>");
      stranka += F("<meta charset='utf-8'>");
      stranka += F("<meta http-equiv='Refresh' content='0; url=/' />");
      stranka += F("</head>");
      stranka += F("</html>");
      server.send(200, "text/html", stranka);
    }
    void setup() {
      Serial.begin(115200);
      WiFiManager wifiManager;
      wifiManager.autoConnect("WiFi_TERMOSTAT_AP");
      EEPROM.begin(512);  //Initialize EEPROM
      float a = read_String(10);
      float b = read_String(100);
      float c = read_String(150);
      if (isnan(a)) {
        writeString(10, 20.25);
      }
      if (isnan(b)) {
        writeString(100, 0.25);
      }
      if (isnan(c)) {
        writeString(150, 0.00);
      }
      sensorsA.begin();
      pinMode(rele, OUTPUT);
      digitalWrite(rele, HIGH);
      sensorsA.requestTemperatures();
      delay(750);
      Serial.println(F("WiFi thermostat - Author: Martin Chlebovec"));
      Serial.println("");
      Serial.println(F("WiFi connected."));
      Serial.println(F("IP address: "));
      Serial.println(WiFi.localIP());
      server.on("/", handleRoot);
      server.on("/get_data.json", handleGet);
      server.on("/automat.html", handleAuto);
      server.on("/manual.html", handleManual);
      server.on("/zap.html", handleZAP);
      server.on("/vyp.html", handleVYP);
      server.on("/action.html", HTTP_POST, handleBody);
      server.begin();
    }
    
    void loop() {
      if ((millis() - cas) >= 10000 || cas == 0) {
        cas = millis();
        teplota = sensorsA.getTempCByIndex(0);
        Serial.println();
        Serial.println(F("----------------------------------------------"));
        Serial.print(F("IP address of ESP8266 thermostat: "));
        Serial.print(WiFi.localIP());
        Serial.print(F(", for access via mDNS use http://"));
        Serial.print(WiFi.localIP());
        Serial.println(F("/"));
        Serial.print(F("Free HEAP: "));
        Serial.print(ESP.getFreeHeap());
        Serial.println(F(" B"));
        Serial.print(F("Actual DS18B20 temperature: "));
        Serial.print(String(teplota));
        Serial.println(F(" °C"));
        sensorsA.requestTemperatures();
        rezim = read_String(150);
        if (rezim == 0.00) {
          float cielova_teplota = read_String(10);
          float  hystereza = read_String(100);
          float minus_hystereza_teplota = (-1 * hystereza);
          float rozdiel = cielova_teplota - teplota; //21 - 20
          if (rozdiel > hystereza) {
            Serial.println(F("Output ON"));
            stav = "ON";
            digitalWrite(rele, LOW);
          } else if (rozdiel < minus_hystereza_teplota) {
            Serial.println(F("Output OFF"));
            stav = "OFF";
            digitalWrite(rele, HIGH);
          } else {
            Serial.println(F("Difference between the target and actual temperature is not above or below the hysteresis. The output status does not change."));
            Serial.print(F("Actual output state: "));
            Serial.println(stav);
          }
        } else {
          Serial.print(F("Manual operation mode is used, output status: "));
          Serial.println(stav);
        }
      }
      server.handleClient();
      yield();
    }
    

    WiFi thermostat - source code - ESP32

    /*|----------------------------------------------------------|*/
    /*|HTTP webserver - WiFi thermostat - ESP32 + DS18B20        |*/
    /*|AUTHOR: Martin Chlebovec                                  |*/
    /*|EMAIL: martinius96@gmail.com                              |*/
    /*|DONATE: paypal.me/chlebovec                               |*/
    /*|----------------------------------------------------------|*/
    
    #include <WiFi.h>
    #include <WebServer.h>
    #include <WiFiManager.h>
    #include <ESPmDNS.h>
    WebServer server(80);
    #include <EEPROM.h>
    #include <OneWire.h>
    #include <DallasTemperature.h>
    
    #define ONE_WIRE_BUS 23
    OneWire oneWire(ONE_WIRE_BUS);
    DallasTemperature sensorsA(&oneWire);
    const int rele = 22; //D2
    unsigned long cas = 0;
    String stav;
    float teplota;
    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
    
    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) == '.' || 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, float data)
    {
      EEPROM.put(add, (data * 1000));
      EEPROM.commit();
    }
    
    
    float read_String(char add)
    {
      float payload = 0;
      float data = EEPROM.get(add, payload);
      return (data / 1000);
    }
    
    void handleRoot() {
      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;
      String stranka = F("<!DOCTYPE html>");
      stranka += F("<html>");
      stranka += F("<head>");
      stranka += F("<meta charset='utf-8'>");
      stranka += F("<meta name='author' content='Martin Chlebovec'>");
      stranka += F("<meta http-equiv='Refresh' content='30'; />");
      stranka += F("<meta name='viewport' content='width=device-width, initial-scale=1'>");
      stranka += F("<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css'>");
      stranka += F("<script type='text/javascript'> window.smartlook||(function(d) {"); 
      stranka += F("var o=smartlook=function(){ o.api.push(arguments)},h=d.getElementsByTagName('head')[0];");
      stranka += F("var c=d.createElement('script');o.api=new Array();c.async=true;c.type='text/javascript';");
      stranka += F("c.charset='utf-8';c.src='https://rec.smartlook.com/recorder.js';h.appendChild(c); })(document);"); 
      stranka += F("smartlook('init', '63ff3bf4db2f85029b19856425a4f2086533c9e6');"); 
      stranka += F("</script>");
      stranka += F("<script type='text/javascript'>");
      stranka += F("var timeleft = 30;");
      stranka += F("var downloadTimer = setInterval(function(){");
      stranka += F("if(timeleft <= 0){");
      stranka += F("clearInterval(downloadTimer);");
      stranka += F("document.getElementById(\"countdown\").innerHTML = \"Refreshing...\";");
      stranka += F("} else {");
      stranka += F("document.getElementById(\"countdown\").innerHTML = timeleft + \" seconds to refresh\";");
      stranka += F("}");
      stranka += F("timeleft -= 1;");
      stranka += F("}, 1000);");
      stranka += F("</script>");
      stranka += F("<title>WiFi thermostat - ESP32</title>");
      stranka += F("</head>");
      stranka += F("<body>");
      stranka += F("<center><h3>Enter datas for web server:</h3>");
      stranka += F("<form action='/action.html' method='post'>");
      stranka += "<b>Target temperature:</b><br><input type='text' id='fname' name='fname' min='5' max='50' step='0.25' value=" + String(read_String(10)) + "><br>";
      stranka += "<b>Hysteresis:</b><br><input type='text' id='fname2' name='fname2' min='0' max='10' step='0.25' value=" + String(read_String(100)) + "><br>";
      stranka += F("<input type='submit' class='btn btn-success' value='Send'>");
      stranka += F("</form><hr>");
      if (stav == "ON") {
        stranka += F("<b><font color='green'>Output: ON</font></b>");
      }
      if (stav == "OFF") {
        stranka += F("<b><font color='red'>Output: OFF</font></b>");
      }
      stranka += F("<div id=\"countdown\"></div>");
      stranka += F("<b>Current sensor DS18B20 temperature:</b> ");
      stranka += String(teplota);
      stranka += F(" °C");
      stranka += F("<hr>");
      stranka += F("<b>Uptime: </b>");
      stranka += String(days);
      stranka += F("d");
      stranka += F(" ");
      stranka += String(hours);
      stranka += F("h");
      stranka += F(" ");
      stranka += String(minutes);
      stranka += F("m");
      stranka += F(" ");
      stranka += String(seconds);
      stranka += F("s");
      stranka += F("<h3>Author: Martin Chlebovec - martinius96@gmail.com - https://martinius96.github.io/WiFi-termostat/en/</h3>");
      stranka += F("<h4>Free version - 1.0.4 build: 10. Oct. 2021</h4>");
      stranka += F("</center>");
      stranka += F("</body>");
      stranka += F("</html>");
      server.send(200, "text/html", stranka);
    }
    
    void handleBody() {
      if (server.hasArg("fname")) {
        String target_temp = server.arg("fname");
        // float cielova_teplota = target_temp.toFloat();
    
        if (isFloat(target_temp)) {
          float cielova_teplota = target_temp.toFloat();
          writeString(10, cielova_teplota);
        } else {
          Serial.println(F("No number was entered in the target temperature input field!"));
          Serial.println(F("Datas will be not stored in EEPROM!"));
        }
      }
      if (server.hasArg("fname2")) {
        String hysteresis = server.arg("fname2");
        if (isFloat(hysteresis)) {
          float hystereza = hysteresis.toFloat();
          writeString(100, hystereza);
        } else {
          Serial.println(F("No number was entered in the hysteresis input field!"));
          Serial.println(F("Datas will be not stored in EEPROM!"));
        }
      }
      String stranka = F("<!DOCTYPE html>");
      stranka += F("<html>");
      stranka += F("<head>");
      stranka += F("<meta charset='utf-8'>");
      stranka += F("<meta http-equiv='Refresh' content='5; url=/' />");
      stranka += F("<title>WiFi thermostat - ESP32 - data processing</title>");
      stranka += F("</head>");
      stranka += F("<body>");
      stranka += F("<center><h3>Server received datas from HTML form:</h3>");
      stranka += "<li><b>Target temperature: </b>" + String(read_String(10)) + " °C</li>";
      stranka += "<li><b>Hysteresis: </b>" + String(read_String(100)) + " °C</li>";
      stranka += F("<b>Redirecting... Please wait</b></center>");
      stranka += F("</body>");
      stranka += F("</html>");
      server.send(200, "text/html", stranka);
    }
    
    void handleGet() {
      String stranka = "{\n";
      stranka += F("\"Hysteresis\":");
      stranka += String(read_String(100));
      stranka += F(",\n");
      stranka += F("\"Target_Temperature\":");
      stranka += String(read_String(10));
      stranka += F(",\n");
      stranka += F("\"Actual_Temperature\":");
      stranka += String(teplota) + "\n";
      stranka += F("}\n");
      server.send(200, "application/json", stranka);
    }
    
    void setup() {
      Serial.begin(115200);
      WiFiManager wifiManager;
      wifiManager.autoConnect("WiFi_TERMOSTAT_AP");
      EEPROM.begin(512);  //Initialize EEPROM
      float a = read_String(10);
      float b = read_String(100);
      if (isnan(a)) {
        writeString(10, 20.25);
      }
      if (isnan(b)) {
        writeString(100, 0.25);
      }
      sensorsA.begin();
      pinMode(rele, OUTPUT);
      digitalWrite(rele, HIGH);
      sensorsA.requestTemperatures();
      delay(750);
      Serial.println(F("Webapp created by: Martin Chlebovec"));
      Serial.println(F("Build 1.0.4 from 10. Oct 2021"));
      Serial.println(F("WiFi connected."));
      Serial.println(F("IP address of WiFi thermostat: "));
      Serial.println(WiFi.localIP());
      if (!MDNS.begin("wifi-termostat")) {
        Serial.println("Error setting up MDNS responder!");
        while (1) {
          delay(1000);
        }
      }
      Serial.println("mDNS responder running");
      Serial.println(F("Thermostat available at: http://wifi-termostat.local"));
      server.on("/", handleRoot);
      server.on("/get_data.json", handleGet);
      server.on("/action.html", HTTP_POST, handleBody);
      server.begin();
      MDNS.addService("http", "tcp", 80);
    }
    
    void loop() {
      if ((millis() - cas) >= 10000 || cas == 0) {
        cas = millis();
        teplota = sensorsA.getTempCByIndex(0);
        Serial.println();
        Serial.println(F("----------------------------------------------"));
        Serial.print(F("IP address of WiFi Thermostat: "));
        Serial.print(WiFi.localIP());
        Serial.print(F(", to access WiFi thermostat, visit http://"));
        Serial.print(WiFi.localIP());
        Serial.println(F("/"));
        Serial.println(F("Thermostat also available at: http://wifi-termostat.local via mDNS record"));
        Serial.print(F("Free HEAP: "));
        Serial.print(ESP.getFreeHeap());
        Serial.println(F(" B"));
        Serial.print(F("Current temperature: "));
        Serial.print(String(teplota));
        Serial.println(F(" °C"));
        sensorsA.requestTemperatures();
        float cielova_teplota = read_String(10);
        float  hystereza = read_String(100);
        float minus_hystereza_teplota = (-1 * hystereza);
        float rozdiel = cielova_teplota - teplota; //21 - 20
        if (rozdiel > hystereza) {
          Serial.println(F("Output on"));
          stav = "ON";
          digitalWrite(rele, LOW);
        } else if (rozdiel < minus_hystereza_teplota) {
          Serial.println(F("Output off"));
          stav = "OFF";
          digitalWrite(rele, HIGH);
        } else {
          Serial.println(F("Difference between target and actual temp is not above or below the hysteresis. The output state does not change."));
          Serial.print(F("Actual state of output: "));
          Serial.println(stav);
        }
      }
      server.handleClient();
      yield();
    }