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 WiFi thermostat is a highly efficient system designed for microcontrollers from Espressif Systems, featuring seamless integration with ESP8266 (12E, 12F, Wemos D1 Mini, NodeMCU v2/v3) and ESP32 platforms. This system enables reliable WiFi connectivity, enabling easy access to configuration via an intuitive web interface. Note: ESP8266-01/ESP8266-01S and earlier microcontrollers are not compatible with the project due to limitations in firmware upload methods. ESP32 microcontrollers, including ESP32-WROOM-32, ESP-32S, and associated DevKits, are fully supported.

Hardware Requirements

  • ESP8266 / ESP32 microcontroller
  • DS18B20 temperature sensor (OneWire interface)
  • Relay: SRD-5VDC-SL-C / OMRON G3MB-202P for boiler switching (Active-LOW signal)

Web Interface Pages

  • / - Root page displaying form, current relay output, target and current temperature, and hysteresis settings.
  • /action.html - Processes form data, updates EEPROM, and redirects to the root page.
  • /get_data.json - Shares temperature data in JSON format, suitable for third-party systems (e.g., MQTT brokers, home automation).
  • Other system pages for control and configuration adjustments.

Sensor and Relay Details

DS18B20 Sensor: This temperature sensor provides 12-bit resolution, offering accuracy in 0.0625 °C increments. The OneWire bus allows for fast data transmission (500-1000 ms).

Relay Options: The SRD-5VDC-SL-C relay can handle up to 10A at 230V AC (2300W). For DC circuits, it supports up to 300W. Alternatively, the OMRON G3MB-202P SSR is designed for AC circuits with non-inductive loads (up to 460W).

Features and Functionality

  • Automatic boiler control with real-time temperature adjustment and hysteresis
  • Configurable target temperature and hysteresis
  • Boiler ON/OFF control with automatic mode
Important: The automatic mode implementation is experimental and may have some limitations.

The thermostat can also serve as a WiFi thermometer when not controlling the boiler, providing temperature data without active heating control. It operates independently of the web interface every 10 seconds.

Boiler control with hysteresis

Web Interface and User Experience

The thermostat's web interface adapts for both desktop and mobile devices, ensuring a responsive and user-friendly experience. It uses Bootstrap CSS for optimal performance, reducing load on the microcontroller. The configuration is stored in EEPROM memory to persist settings after power outages, ensuring reliable operation.

Important Notes

To prevent EEPROM wear, configuration data (reference temperature and hysteresis) are only updated when the form is submitted. The web page refreshes every 30 seconds to update the status, with a warning for users to save their changes before the page refresh.

Disclaimer

Warning: The author of the WiFi thermostat is not responsible for any damage caused by improper installation or operation. The thermostat is provided under the MIT License.

Main Page for Modifying Target Temperature and Hysteresis

Example of Switched On Output:

Sample Data:
  • Target Temperature: 22.75 °C
  • Hysteresis: 0.25 °C
  • Measured Temperature: 22.49 °C
  • Output Status: ON

The thermostat activates the heating process when the measured temperature drops to 22.49 °C or lower. Once the temperature reaches 23.01 °C, the output is turned off, the signaling relay opens, and the gas boiler stops heating, initiating the cooling phase in the room. The thermostat remains inactive until the temperature falls to 22.49 °C or below, at which point a new heating cycle begins. This precise temperature regulation helps maintain the desired room temperature while optimizing energy efficiency and ensuring comfort.

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