Repozitár projektu WiFi termostat so strojovými kódmi pre cieľovú platformu ESP8266 a ESP32: Firmvér
Článok k projektu WiFi termostat na ESP8266 - deadawp.blog.sector.sk: WiFi termostat - ESP8266 - 1.0.2.1 - JSON clients
Článok k projektu WiFi termostat na ESP32 - deadawp.blog.sector.sk: Podpora ESP32 do projektu WiFi termostat
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 termostat - ESP8266 / ESP32


Programová implementácia je navrhnutá pre mikrokontrólerové platformy od výrobcu Espressif Systems s WiFi konektivitou. Podporované WiFi (2,4 GHz) mikrokontroléry ESP8266 a ESP32. WiFi termostat je prístupný z LAN siete v ktorej sa nachádza, pričom je vybavený webovým rozhraním ktoré slúži na konfiguráciu všetkých prvkov termostatu a zároveň pre vizualizáciu aktuálnych stavov. Termostat riadi kotol na základe nameranej, cieľovej teploty a hysterézy nezávisle na webaplikácii, ktorá slúži na konfiguráciu a rozhodovací prah termostatu. Okrem dostupnosti termostatu na konkrétnej IP adrese je možné termostat doplniť o mDNS záznam, ktorý generuje lokálnu doménu (hostname.local) prístupnú iba v LAN sieti v rámci tejto multicastovej služby. Konfigurácia termostatu (z pohľadu konektivity) na domácu WiFi LAN sieť je zabezpečená prostredníctvom WiFiManagera, ktorý údaje o WiFi sieti (SSID a heslo) ukladá do flash pamäte mikrokontroléru. Uloženie je jednorázové a zariadenie si konfiguráciu pamätá permanentne. Po nadobudnutí konektivity (priradení IP adresy od routra vašej domácej WiFi siete) je možné termostat plne používať. HTTP webserver bežiaci na mikrokontorléri ESP8266 / ESP32 umožňuje beh niekoľkých na sebe nezávislých HTML stránok, ktoré môžu mať informatívny, alebo aj funkcionálny charakter.

Po hardvérovej stránke projekt využíva:
  • ESP8266 / ESP32
  • Teplotný senzor DS18B20 na OneWire zbernici
  • Relé SRD-5VDC-SL-C / OMRON G3MB-202P slúžiace na spínanie kotla (Active-LOW signál)

  • HTML stránky bežiace na platforme ESP8266 / ESP32:
  • / - root stránka obsahujúca formulár, aktuálny výpis logického výstupu pre relé, aktuálnu a cieľovú teplotu teplotu, hysterézu
  • /action.html - spracúvava hodnoty z formulára, zapisuje ich do emulovanej EEPROM pamäte, presmeruje používateľa späť na root stránku
  • /get_data.json - distribuuje dáta o aktuálnej teplote, referenčnej teplote a hysteréza tretej strane (počítač, mikrokontróler, iný klient...) v JSON formáte - možno využiť s príkladom JSON klient, ktorý dáta vie odoslať na MQTT Broker, napríklad do domácej automatizácie

  • Rozšírené o HTML stránky v prípadne experimentálnej verzie s manuálnym režimom:
  • /zap.html - permanentné zapnutie výstupu v manuálnom režime
  • /vyp.html - permanentné vypnutie výstupu v manuálnom režime
  • /automat.html - zmena režimu na automatický (používa hysterézu a cieľovú teplotu)
  • /manual.html - zmena režimu na manuálny (permanentné ovládanie výstupu ZAP / VYP natvrdo)

  • Rozlíšenie senzora DS18B20 pri meraní je 12-bitov, čomu rozpovedá rozlíšenie teploty na 0.0625 °C, čo je zároveň aj minimálny krok medzi rôznými nameranými hodnotami. Dáta po OneWire zbernici môžu vrátiť do mikrokontroléru po vyžiadaní za 500 až 1000 ms v závislosti od počtu senzorov, dĺžky zbernice. Elektromagnetické relé SRD-5VDC-SL-C, ktoré je v projekte použité umožňuje spínať až 10A pri 230V - výkon 2300W. V prípade spínania jednosmerného obvodu (záťaže) je možné spínať 300W (10A pri 30V DC). Prípadne je pre schému zapojenia plne kompatibilné aj SSR relé OMRON G3MB-202P, ktoré je vhodné iba pre neindukčnú záťaž a výhradne pre obvod so striedavým napätím. Maximálny spínaný výkon 460W (230V, 2A). Termostat je možné používať celoročne. V prípade nepotrebného riadenia je možné výstup fyzicky odpojiť a termostat využívať ako WiFi teplomer pre zisk dát z miestnosti, kde sa nachádza.


    V prípade firmvéru s podporou manuálneho ovládania GPIO výstupu mikrokontroléru ESP je možné termostat fyzicky vypnúť bez nutnosti jeho odpojenia zo svorkovnice relé. Logika termostatu sa vykonáva každých 10 sekúnd nezávisle na webserveri a pripojených klientoch, nevyžaduje sa keep-alive spojenie pre jej vykonanie. Vždy pri vykonaní logiky vypíše na UART termostat aj informáciu o aktuálnej IP adrese, respektíve aj mDNS zázname (ak sa s mDNS firmvér používa) a dá tak používateľovi informáciu, kde v LAN sieti je termostat dosiahnuteľný so svojim webovým rozhraním. Okrem toho vypíše aj dynamickú voľnú pamäť - HEAP, ktorá sa pohybuje na úrovni 40 až 44kB a taktiež aj aktuálny stav výstupu s oznámením zmeny ak k nej dôjde --> (ak pretečie rozhodovací threshold do + respektíve do -). 3V3 operačná logika GPIO mikrokontrolérov ESP8266 a ESP32 postačuje pre digitálny signál zmeny, relé však musí byť napájané na 5V z VUSB, respektíve VIN.

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

    Do základnej verzie WiFi termostatu (bez mDNS záznamu) bol implementovaný manuálny režim ovládania (natvrdo ZAP/VYP) s možnosťou prepínania medzi manuálnym a automatickým režimom. Webové rozhranie je navrhnuté pre prispôsobenie sa väčším i menším obrazovkám. Je responzívne, podporuje širokouhlé obrazovky s vysokým rozlíšením, ale aj mobilné zariadenia. Rozhranie využíva importované CSS štýly Bootstrap frameworku z externého CDN servera, ktorý načíta client-side zariadenie pri otvorení stránky bežiacej na ESP. Importovaním CSS štýlov z externého servera umožní znížiť výkonové a pamäťové zaťaženie mikrokontroléru.

    WiFi termostat - webové rozhranie vizualizované v systéme Android - Chrome

    Aby ostali nastavené hodnoty termostatu zachované aj po výpadku napájania, sú uložené do EEPROM pamäte ESP, ktorá je emulovaná vo flash pamäti, nakoľko platforma nemá fyzicky EEPROM čip (pamäť). Referenčná teplota na offset 10, hysteréza na offset 100. Každá z hodnôt zaberá maximálne 5B v EEPROM pamäti + ukončovací znak. Dáta sa prepisujú iba pri odoslaní HTML formulára, prevádzka termostatu je tak maximálne šetrná k EEPROM pamäti pre jej maximálnu trvácnosť. Stav výstupu existuje iba v RAM pamäti, kde sa pri zmene prepisuje. Hodnota sa neukladá do emulovanej EEPROM pamäte vo flash pamäti.


    V prípade, že zariadenie pri prvom spustení nemá nič uložené na spomenutých EEPROM offsetoch, vykoná sa automatický zápis s predvolenými hodnotami - referencia: 20.25 °C, hysteréza 0.00 °C. Tým je zabezpečené, že termostat dokáže fungovať aj na mikrokontroléroch, ktoré nemajú nič zapísané v EEPROM pamäti - fail-safe riešenie. ESP8266 a ESP32 využíva pre zápis do EEPROM pamäte funkciu EEPROM.put(), ktorá podporuje akýkoľvek dátový typ a EEPROM.commit() pre potvrdenie zápisu na cieľový ofset. Implementácia používa dátový typ float() pre 32-bitové číslo, ktoré je uložené do EEPROM a prislúcha referenčnej (cieľovej) teplote a taktiež aj hysteréze. Prostredníctvom meta tagu Refresh vykonáva webserver obnovu celej HTML stránky každých 30 sekúnd a prostredníctvom Javascriptu sa vypisuje do HTML stránky aj orientačný čas do refreshu. Do tohto času je potrebné stihnúť zapísať zmenu pre termostat, inak sa input okná pre číselné vstupy do formulára resetujú pri obnovení stránky. Nakoľko built-in knižnica Ethernet neobsahuje využitie asynchrónneho webservera (ktorý je možné využiť napríklad u mikrokontrolérov Espressif ESP8266 / ESP32), je nutné prepisovať celú stránku z dôvodu, že je táto implementácia 1:1 s pôvodným Ethernet termostatom.


    Dynamický údaj, ktorý sa predovšetkým mení je aktuálna hodnota výstupu - Zapnutý / Vypnutý, ktorý informuje prevádzkovateľa o skutočnom stave výstupu spoločne aj s farebným označením. Nakoľko sa logika systému vykonáva nezávisle na webserveri, do refreshu môže už byť výstup v inom stave, ako aktuálne vypísanom vo webaplikácii. Zmena výstupu je ihneď vypísaná napríklad na UART monitor. Na webovej stránke termostatu nájde používateľ aj informácie o uptime zariadenia (ako dlho beží), t.j. čas v dňoch, hodinách, minútach a sekundách. Termostat je určený iba pre interiérové teploty! (nad 0°C), čomu je prispôsobená aj logika systému! Termostatom je možné nahradiť už existujúci izbový termostat, možno dočasne nahradiť ohrievač v akváriu / teráriu pre udržiavanie stálej teploty.

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

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

    Hlavná stránka pre modifikáciu cieľovej teploty a hysterézy: WiFi termostat - Hlavný prehľad s modifikáciou cieľovej teploty a hysterézy - Vypnutý Priebeh spracovania zadaných údajov (presmerovanie používateľa): WiFi termostat - spracovanie údajov z HTML formulára JSON výstup webservera v prehliadači / klientovi cez websocket:
    WiFi termostat - JSON output
    Výstup do UART monitoru - logika systému + nastavená IP adresa, mDNS záznam:
    WiFi termostat - UART - ESP8266 - mDNS záznam WiFi termostat - UART - ESP32 - výstup - ovládanie kotla
    Rozšírená verzia WiFi termostatu obsahuje:
  • Zdrojový kód (.ino) s možnosťou editácie
  • Dostupné senzory SHT21, SHT31, DHT22, BME280, BMP280 pre riadenie kotla
  • Režim chladenia (inverzná logika k termostatu vykurovania)
  • Basic OTA, OTA Web Updater, Elegant remote OTA
  • Možnosť ovládania výstupu cez callbacky volané UDP datagramami prostredníctvom UDP clientov (Packet Sender).
  • Interakcia s Amazon Alexa Echo Dot s možnosťou hlasového ovládania termostatu (UDP callbacky)
  • Možnosť Publishu dát na MQTT Broker / cez HTTP / HTTPS request na vzdialený webserver s uložením dát do MySQL databázy (bez JSON clienta)

  • Ukážkový výpis vo formáte JSON

  • Možnosť spracovať JSON klientom (s kompatibilným firmvérom pre Arduino + Ethernet / ESP8266 / ESP32 - dostupný v repozitári)
  • Možnosť odoslať dáta na MQTT Broker a distribuovať ich iným klientom, ktorí môžu cieľový topic odoberať
  • Možnosť čítania JSON formátu inými klientami - JSON Parser, Editor, Viewer a iné...
  • {
    "Hysteresis":0.25,
    "Target_Temperature":21.75,
    "Actual_Temperature":21.43
    }

    Termostat - zdrojový kód

  • Logika termostatu v minimálnej implementácii, bez Web servera, WiFi konektivity a emulovanej EEPROM pamäte - všeobecná implementácia
  • Pattern použitý pre Ethernet i WiFi termostat pre všetky mikrokontroléry - Arduino, ESP8266, ESP32 s totožným zdrojovým kódom
  • //Projekt: Minimálna implementácia - Termostat
    //Autor: Martin Chlebovec
    //Hardvér: Arduino / ESP8266 / ESP32 + DS18B20 (1x OneWire bus)
    //Revizia: 23. Mar. 2020
    
    #include <OneWire.h>
    #include <DallasTemperature.h>
    
    #define ONE_WIRE_BUS 23 //datovy vovod OneWire zbernice na pin D23
    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() {
      sensorsA.begin();
      Serial.begin(115200);
      Serial.println("UART ready");
      pinMode(rele, OUTPUT);
      // pinMode(rele2, OUTPUT);
    }
    
    void loop() {
      if ((millis() - timer) >= interval || timer == 0) {
        timer = millis();
        sensorsA.requestTemperatures();
        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
          }
        */
      }
    }
    

    Zdrojový kód pre výpis aktuálne nameraných údajov - HTML kód - bez backendu

              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='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 = \"Reštart...\";");
      stranka += F("} else {");
      stranka += F("document.getElementById(\"countdown\").innerHTML = timeleft + \" sekúnd do reštartu\";");
      stranka += F("}");
      stranka += F("timeleft -= 1;");
      stranka += F("}, 1000);");
      stranka += F("</script>");
      stranka += F("<title>WiFi termostat - ESP8266</title>");
      stranka += F("</head>");
      stranka += F("<body>");
      stranka += F("<center><h3>Zadajte dáta pre webserver:</h3>");
      stranka += F("<form action='/action.html' method='post'>");
      stranka += "<b>Referenčná teplota:</b><br><input type='text' id='fname' name='fname' min='5' max='50' step='0.25' value=" + String(read_String(10)) + "><br>";
      stranka += "<b>Hysteréza:</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='Zapísať'>");
      stranka += F("</form><hr>");
      if (stav == "ZAP") { //Glob premenna drziaca stav vystupu na zaklade prepoctu mimo HTML stranky
        stranka += F("<b><font color='green'>Výstup: Zapnutý</font></b>");
      }
      if (stav == "VYP") {
        stranka += F("<b><font color='red'>Výstup: Vypnutý</font></b>");
      }
      stranka += F("<div id=\"countdown\"></div>");
      stranka += F("<b>Aktuálna teplota senzora DS18B20:</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>Autor: Martin Chlebovec - martinius96@gmail.com - https://martinius96.github.io/WiFi-termostat/</h3>");
      stranka += F("<h4>Verzia free - 1.0.3 build: 22. Jan. 2021</h4>");
      stranka += F("</center>");
      stranka += F("<div class='alert alert-info'>");
      stranka += F("Finálny build projektu WiFi termostat. Ďakujem za vyskúšanie webaplikácie. Rozšírené verzie projektu sú platené.<br><strong>Obsah platených verzií:</strong><li>Async Webserver - AJAX update</li><li>Manuálny režim</li><li>OTA aktualizácie</li><li>Ovládanie hlasom cez Amazon Alexa</li><li>Ovládanie cez UDP callbacky</li><li>Možnosť publikácie dát na MQTT Broker (Loxone, IoT Industries Slovakia, Blynk...),</li><li>Dostupné senzory Bosch, Sensirion, DHT</li><li>Watchdog Timer</li><li>Zdrojový kód (.ino) pre aplikáciu.</li><li>Auto-test periférii, fail-safe riešenie</li><li>JSON output rozšírený o systémové dáta (WiFi sieť, RSSI, uptime, napájacie napätie...)</li>");
      stranka += F("</div>");
      stranka += F("</body>");
      stranka += F("</html>");
    	  SEND TO CLIENT
    	  FLUSH CLIENT
    	  STOP CLIENT
    

    Fragment zdrojového kódu HTML stránky po spracovaní údajov z HTML formuláru

  • Použitý redirect používateľa späť na hlavnú stránku prehľadu 5 sekúnd po spracovaní údajov zadaných do formuláru.
  • Typ lazy loadingu. Fragment neobsahuje backend pre spracovanie dát, ani funkčné časti webstránky.
  • 	    backend_processing dát z HTML formuláru
    	    .
    	    .
    	    .
                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 termostat - ESP8266 - spracovanie riadiach dát</title>");
      stranka += F("</head>");
      stranka += F("<body>");
      stranka += F("<center><h3>Server prijal data z formulára:</h3>");
      stranka += "<li><b>Referenčná teplota: </b>" + String(read_String(10)) + " °C</li>";
      stranka += "<li><b>Hysteréza: </b>" + String(read_String(100)) + " °C</li>";
      stranka += F("<b>Presmerovanie... Prosím čakajte</b></center>");
      stranka += F("</body>");
      stranka += F("</html>");
    	    SEND TO CLIENT
    	    FLUSH CLIENT
    	    STOP CLIENT