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.
ESP8266 ESP32 WiFi DS18B20 OneWire Dallas HTML Webserver WebSocket JSON mDNS UART
Riadiaci mikrokontróler NodeMCU v2 / v3 Lolin - ESP8266-12E / ESP8266-12F
NodeMCU (ESP8266)
Riadiaci mikrokontróler Wemos D1 Mini - ESP8266-12E / ESP8266-12F
Wemos D1 Mini (ESP8266)
Riadiaci mikrokontróler ESP32 Devkit V1 - ESP-WROOM-32 / ESP32-S
ESP32 DevKit

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
    An extended version of this thermostat includes an extra - NOT INCLUDED IN THE GITHUB REPOSITORY WITH A COMPILED FIRMWARE:
    If you are interested in purchasing the full version of the project with the source code (.ino) -
  • Source code (.ino)
  • Watchdog timer
  • Available sensors SHT21, SHT31, DHT22, BME280, BMP280 and others
  • Cooling mode
  • Basic OTA, OTA Web Updater, Elegant remote OTA
  • Possibility of control via UDP datagrams - callbacks, via UDP clients (Packet Sender).
  • Interact with Amazon Alexa Echo Dot with thermostat voice control
  • Possibility to Publish data to MQTT Broker / via HTTP / HTTPS request to a remote web server with data storage in a MySQL database (without JSON client)

  • Sample statement - JSON output for other clients, processing


    Thermostat - source code

  • Thermostat logic - minimal implementation (without web server, WiFi, emulated EEPROM) - general implementation
  • Pattern used for the WiFi thermostat project
  • //Projekt: Minimum implementation - thermostat
    //Autor: Martin Chlebovec
    //Hardver: Arduino Uno + DS18B20 (1x OneWire bus)
    //Revizia: 23. Mar. 2020
    #include <OneWire.h>
    #include <DallasTemperature.h>
    #define ONE_WIRE_BUS 5 //datovy vovod OneWire zbernica na pin D5
    OneWire oneWire(ONE_WIRE_BUS);
    DallasTemperature sensorsA(&oneWire);
    float nastavena_teplota = 24.50;
    float hystereza = 0.50;
    //float hystereza2 = 0.50;
    const int rele = 6;
    //const int rele2 = 7;
    unsigned long timer = 0;
    unsigned long interval = 15000; //ako casto chceme logiku spustit --> v ms (milisekundy)
    void setup() {
      Serial.println("UART ready");
      pinMode(rele, OUTPUT);
      // pinMode(rele2, OUTPUT);
    void loop() {
      if ((millis() - timer) >= interval || timer == 0) {
        timer = millis();
        delay(2000); //cakame na teploty
        float  teplota1 = sensorsA.getTempCByIndex(0);
        //float  teplota2 = sensorsA.getTempCByIndex(1);
        float rozdiel = nastavena_teplota - teplota1;
        if (rozdiel > hystereza) {
          digitalWrite(rele, HIGH); //zopnutie rele
          Serial.println("Rele bolo zopnute - Vystup aktivny");
        } else if (rozdiel < (-1 * hystereza)) {
          digitalWrite(rele, LOW); //rozopnutie rele
          Serial.println("Rele bolo rozopnute - Vystup neaktivny");
          float rozdiel2 = nastavena_teplota2 - teplota2;
          if (rozdiel2 > hystereza2) {
          digitalWrite(rele2, HIGH); //zopnutie rele
          } else if (rozdiel2 < (-1 * hystereza2)) {
          digitalWrite(rele2, LOW); //rozopnutie rele

    Source code snippet for the General Report page - without processing the data from the HTML form

              Client handling  
              HTTP header
      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=''>");
      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>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 - -</h3>");
      stranka += F("<h4>Free version - 1.0.4 build: 06. Oct. 2021</h4>");
      stranka += F("</center>");
      stranka += F("</body>");
      stranka += F("</html>");

    Fragment of the source code with a listing of the set control data after submitting the HTML form

  • With the subsequent redirection of the user back to the main page after 5 seconds from processing - the so-called lazy loading ... (Processing not included in the fragment)
  • 	    backend_processing of data from HTML form (NOT AVAILABLE FOR FREE)
                HTTP header
      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 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>");
    	    STOP CLIENT