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 designed for microcontroller platforms from the manufacturer Espressif Systems with WiFi connectivity. Specifically, the ESP8266 microcontroller in versions 12E to F used in the Wemos D1 Mini, NodeMCU v2, v3 platforms. ESP8266-01 / ESP8266-01S microcontrollers and similar standalone chips before the 12E generation are not compatible for the project in terms of program recording, as they do not use the DIO flash method, for which ESPTOOL calls are designed in the project; upload firmware to these devices as well. For the ESP32 microcontroller, it is possible to use standalone chips ESP32-WROOM-32 from Espressif and the equivalent ESP-32S from AI-Thinker. All DevKits included with these chips are also compatible, they are fully compatible (for example DevKit v1 / DevKiTC-v4). WiFi thermostat is accessible from the LAN network in which it is located, while it is equipped with a web interface that is used to configure all elements of the thermostat. The thermostat controls the boiler based on the measured, target temperature and hysteresis independently of the web application, which serves for the configuration and decision threshold of the thermostat. In addition to the availability of the thermostat at a specific IP address, the thermostat can be supplemented with an mDNS record, which generates a local domain (hostname.local) accessible only in the LAN network within this multicast service. The configuration of the thermostat (from the point of view of connectivity) on the home WiFi LAN network is secured by WiFiManager, which stores data about the WiFi network (SSID and password) in the flash memory of the microcontroller. The storage is one-time and the device remembers the configuration permanently. After gaining connectivity (assigning an IP address from the router of your home WiFi network), the thermostat can be fully used. The HTTP web server running on the ESP8266 / ESP32 microcontroller allows the running of several independent HTML pages, which can be informative or even functional.

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 resolution of the DS18B20 sensor during measurement is 12-bits, which is indicated by the resolution at 0.0625 ° C. Data via the OneWire bus can be returned to the microcontroller on demand in 500 to 1000 ms. The electromagnetic relay SRD-5VDC-SL-C, which is used in the project, allows switching up to 10A at 230V - power 2300W. In case of switching a DC circuit (load) it is possible to switch 300W (10A at 30V DC). Alternatively, the OMRON G3MB-202P SSR relay is fully compatible for the wiring diagram, which is only suitable for non-inductive loads and exclusively for AC circuits. Maximum switching power 460W (230V, 2A). The thermostat can be used all year round. In case of unnecessary control, it is possible to physically disconnect the output and use the thermostat as a WiFi thermometer to obtain data from the room where it is located. The thermostat logic is executed every 10 seconds independently of the web server and connected clients, no keep-alive connection is required to execute it. Each time the logic is executed, the thermostat also writes information about the current IP address or mDNS record (if used with mDNS firmware) on the UART thermostat and thus gives the user information on where the thermostat is accessible with its web interface in the LAN network. In addition, it also lists the dynamic free memory - HEAP, which is at the level of 40 to 44kB, as well as the current state of the output with notification of the change if it occurs -> (if the decision threshold flows to + or to -). The 3V3 GPIO operating logic of the ESP8266 and ESP32 microcontrollers is sufficient for a digital change signal, but the relay must be powered to 5V from the 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

    A manual control mode (hard ON / OFF) has been implemented in the basic version of the WiFi thermostat (without mDNS recording) with the possibility of switching between manual and automatic mode. The web interface is designed to adapt to larger and smaller screens. It is responsive, supporting widescreen high-definition screens as well as mobile devices. The interface uses imported CSS styles of the Bootstrap framework from an external CDN server, which loads the client-side device when opening a page running on ESP. By importing CSS styles from an external server, it will reduce the power and memory load of the microcontroller.


    In order to keep the set values of the thermostat even after a power failure, they are stored in the EEPROM memory ESP, which is emulated in the flash memory, as the platform does not have a physical EEPROM chip (memory). Reference temperature to offset 10, hysteresis to offset 100. Each of the values occupies a maximum of 5B in the EEPROM memory + the end character. The data is overwritten only when the HTML form is sent, so the operation of the thermostat is as gentle as possible to the EEPROM memory for its maximum durability. The output state exists only in RAM memory, it is not stored in flash memory. If the device does not have anything stored on the mentioned EEPROM offsets at the first start-up, automatic writing will be performed with default values - reference: 20.25 °C, hysteresis 0.00 °C, so-called fail-safe solution. ESP8266 and ESP32 use the EEPROM.put() function in conjunction with EEPROM.commit() to work with EEPROM memory and EEPROM.get() to read data from EEPROM, which supports any data type, in our case float().

    Using the Refresh meta tag, the web server refreshes the entire page every 30 seconds, and the approximate time to refresh is also written to the HTML page via Javascript. By this time it is necessary to write the change for the thermostat, otherwise the input windows for numerical inputs to the form will be reset when the page is refreshed. As the built-in Ethernet library does not include the use of an asynchronous web server (which can be used, for example, with the Espressif ESP8266 / ESP32 microcontrollers), it is necessary to rewrite the entire page.


    The dynamic data that is mainly changing is the current value of the output - Turned on / Turned off, which informs the operator about the actual state of the output together with the color coding. Since the system logic is executed independently of the web server, the output may already be in a different state than currently currently listed in the web application. The change of output is immediately written to the UART monitor, for example. On the thermostat's website, the user will also find information about the device's uptime (how long it has been running), i. time in days, hours, minutes and seconds. The thermostat is only intended for indoor temperatures! (above 0 ° C), to which the system logic is adapted! The thermostat can replace an existing room thermostat, it can temporarily replace the heater in the aquarium / terrarium to maintain a constant 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 heats from a measured temperature of 22.49 °C and below. If the temperature reaches 23.01 °C, the output is switched off, the signaling relay opens and the gas boiler stops heating. The heating and cooling of the room in which the measurements are performed takes place. Thermostat is not reactivated until the temperature reaches 22.49 °C or lower.

    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)


    Library archive (.zip) expand to C:/Users/[User]/Documents/Arduino/libraries
    Library name Library function Download
    Dallas

    Library for ESP8266 and ESP32 microcontrollers. It allows communication with the Dallas DS18B20 sensor on the OneWire bus. Possibility of communication after normal or parasitic connection.

    Download
    WiFiManager

    Library for ESP8266 and ESP32 microcontrollers. Creates an access point (AP) and Captive portal for configuring a WiFi thermostat on a home WiFi network.

    Download
    DNS Server

    ESP32 microcontroller library. Required for WiFiManager library to work properly.

    Download

    WiFi thermostat - source code - ESP8266

    #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; s
    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'>");
      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

    #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();
    }