Menu
Wi-Fi Термостат для газового котла viessmann vitopend 100

Wi-Fi Термостат для газового котла viessmann vitopend 100

Идея термостата для котлов не нова. Оригинальные допы стоят дорого - поэтому пришло время колхозить.

Для viessmann vitopend 100 требуется найти 4х-контактную клемму X7 рядом с блоком управления, найти контакты 1,2 (они замкнуты перемычкой) и повесить простой выключатель. Всё. Теперь вы можете управлять отоплением вручную и не через режимы управления котла.

Функционал термостата

  • подключение к WiFi
  • WEB -интерфейс для удаленного управления
  • ручной и автоматический режим регулировки

Используется модуль Wemos D1 mini - оптимальный по цене/функционалу.

Схема

Подключение котла

У контактов 1 и 2 на Х7 котла есть полярность, которую можно узнать подключив вольтметр, при разомкнутом состоянии контактов. Коллектор оптрона на клемме J1 модуля надо подключить к "+" выводу котла для термостата, эмиттер - к "-".

Описание функционала

  • При запуске модуль пытается подключиться к одной из 4-х точек доступа
  • Возможно подключение нескольких датчиков, данные которых будут выведены в WEb интерфейсе в HTML,XML,JSON форматах
  • За определенный промежуток времени собирает значения температуры первого по индексу датчика и высчитывает среднее арифметическое. 
  • если среднее значение больше заданного temp_max+temp_delta, оптрон выключается
  • если среднее значение меньше заданного temp_max-temp_delta, оптрон включается
  • кнопка служит для переключения в ручной режим - удерживать 3-6 секунд. Первый раз при удержании 3-6 секунд включается ручной режим и включается оптрон. Следующее удержание 3-6 сек - инвертирует состояние оптрона. Обратное переключение кнопкой не предусмотрено, но возможно через сброс модуля
  • удержание кнопки более 10 секунд - сброс
  • индикация красного светодиода (подогрев) - оптрон включен
  • мигание зеленого светодиода (статус) - 1 раз в 3 сек - АВТО режим; 1 раз в 6 сек - РУЧНОЙ режим
  • Через WEB интерфейс возможно изменить температуру поддержания - temp_max (по умолчанию -20), порог температуры - temp_delta (по умолчанию -1), АВТО/РУЧНОЙ режим, состояние оптрона.
  • Задавать параметры возможно скриптами со сторонних устройств GET и POST запросами.
  •  Считывать параметры возможно в форматах XML и JSON. Ссылки доступны по http://ip-address/

Скетч для Arduino IDE

Раскрыть Код
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Ticker.h>
#include <ESP8266WiFi.h>
// for WEB
#include <WiFiClient.h>
#include <ESP8266WebServer.h>

// wifi nets
const char* ssids[4] = { "wifi1","wifi2","wifi3","wifi4" };
const char* passwds[4] = { "12341234","12341234","12341234","12341234" };
//IPAddress ip(192, 168, 1, xx); // where xx is the desired IP Address //IPAddress gateway(192, 168, 1, 254); // set gateway to match your wifi network //IPAddress subnet(255, 255, 255, 0); // set subnet mask to match your wifi network // remote host const char* host = "devices.domain.ru"; // remote page for sync time String url = "/gpio/themp.php"; // GET password String url_get_password = "12345"; String last_url = ""; float h_max_comfort=60; char* relays[4] = { "-","-","-","-" }; //WiFiServer server(80); ESP8266WebServer server ( 80 ); Ticker dht_get; Ticker tim; Ticker key_r; #define ONE_WIRE_BUS 13 #define button12 12 #define relay5 5 float h = 0; String chip_id = ""; int temp_max = 20; int temp_delta = 1; float temp_real = 0; bool temp_auto = true; float temp_arr[4]; bool temp_arr_full = false; int temp_index = 0; int fail_connect =0; int time_update =10; int analogval =0; int hour=0; int minu=0; int secu=0; int clnts=0; int point=0; bool but_state = false; bool but_last_state = false; int but_count = 0; int led_delay = 1; int str2_count = 0; int str2_state = 0; float refr_cnt=883; String datas="Init"; OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(&oneWire); DeviceAddress deviceAddress; String owstat = ""; String owaddr[8]; float owtemp[8]; bool dht_get_flag = false; bool tm_flag = false; bool key_flag = false; bool updated = false; String tm_capt = ""; void wifi_connect() { Serial.println(""); Serial.print("Trying ");Serial.print(ssids[point]); // save flash if (WiFi.getPersistent() == true) WiFi.persistent(false); //disable saving wifi config into SDK flash area // WiFi.softAPdisconnect(false); //setting ssid/pass to null WiFi.persistent(true); //enable saving wifi config into SDK flash area // WiFi.softAPdisconnect(); WiFi.disconnect(); WiFi.begin(ssids[point], passwds[point]); // if(WiFi.status() != WL_CONNECTED) { fail_connect++; if(fail_connect>20) { fail_connect=0; //ESP.reset(); } } else fail_connect=0; Serial.print("WiFi connects: 1-"); Serial.print(fail_connect); int wfcnt=0; while (WiFi.status() != WL_CONNECTED) { // dokud nenГ­ pripojeno if(wfcnt>15) { if(point<3) point++; else point=0; return; } delay(500); wfcnt++; // cekej 500ms Serial.print("."); // vypis tecku } chip_id = WiFi.macAddress(); Serial.println(tm_capt); Serial.println("WiFi connected"); // zapnni sever server.begin(); // Serial.println("Server started"); Serial.print("NodeMCU IP: "); Serial.println(WiFi.localIP()); delay(2000); } void http_get_finc( int operation, String sens_id, float value) { fail_connect= millis(); if((WiFi.status() == WL_CONNECTED)) { // Use WiFiClient class to create TCP connections WiFiClient client; String line = ""; if(operation==1) { if (client.connect(host, 80)) { // This will send the request to the server line = url + "?act=Hi" + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n"; last_url = line; client.print(String("GET ") + line); // Read all the lines of the reply from server and print them to Serial line = ""; while (client.connected()) { if (client.available()) { line = client.readStringUntil('\n'); if(line.indexOf('now')>0) { String st= line.substring(3,5); hour = st.toInt(); st= line.substring(5,7); minu = st.toInt(); st= line.substring(7,9); secu = st.toInt(); Serial.println(line); } } if(millis()-fail_connect > 5000) client.stop(); } client.stop(); last_url += line; // Serial.println("disconnect"); } else { Serial.println("connection failed!]"); client.stop(); } } if(operation==2) // { if (client.connect(host, 80)) { // "/gpio/themp.php?p=12345&sens_id=4&act=save&val="; line = url + "?p=" + url_get_password + "&act=save" + "&sens_id=" + sens_id + "&val=" + value + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n"; last_url = line; client.print(String("GET ") + line); line = ""; while (client.connected()) { if (client.available()) { line = client.readStringUntil('\n'); if(line.indexOf('saved')>0) Serial.println(line); } if(millis()-fail_connect > 5000) client.stop(); } client.stop(); last_url += line; } else { Serial.println("connection failed!]"); client.stop(); } } if(operation==3) // { if (client.connect(host, 80)) { // This will send the request to the server line = url + "?act=simple&sens_id=" + sens_id + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n"; last_url = line; client.print(String("GET ") + line); // Read all the lines of the reply from server and print them to Serial line = ""; while (client.connected()) { if (client.available()) { line = client.readStringUntil('\n'); // bool variables String st= line.substring(0,1); bool state = false; if(st=="0" || st=="") state = false; if(st=="1" || st=="5") state = true; } if(millis()-fail_connect > 5000) client.stop(); } client.stop(); last_url += line; } else { Serial.println("connection failed!]"); client.stop(); } } /////////WiFi.disconnect(); } Serial.println(last_url); dht_get_flag = false; } void time_finc() { secu=secu+30; if(secu>59) { secu=0; minu++; } if(minu>59) { minu=0; hour++; } if(hour>23) hour=0; if(secu==0) { Serial.print(hour); Serial.print(":"); Serial.print(minu); Serial.print(":"); Serial.print(secu); Serial.print(": IP "); Serial.println(WiFi.localIP().toString() ); } if(updated==false && WiFi.status() == WL_CONNECTED) { http_get_finc( 1, "", 0); // sync time updated=true; } tm_flag=false; } void key_reader() { bool but = digitalRead(button12); if(but==but_last_state && but==false) { but_count++; digitalWrite(2, HIGH ); delay(60); digitalWrite(2, LOW ); delay(60); digitalWrite(2, HIGH ); delay(60); digitalWrite(2, LOW ); delay(60); led_delay=0; Serial.println(but_count); } if(but!=but_last_state) { Serial.print("But: "); Serial.println(but); if(but==true) // { if(but_count>2 && but_count<8) { Serial.println("Manual mode. Invert control"); temp_auto=false; digitalWrite(relay5, !digitalRead(relay5)); } if(but_count>9) { Serial.println("Reset"); ESP.reset(); } but_count=0; } } but_last_state = but; digitalWrite(2, LOW ); if(temp_auto==false && led_delay>6) { digitalWrite(2, HIGH ); led_delay=0; } if(temp_auto==true && led_delay>3){ digitalWrite(2, HIGH ); led_delay=0; } led_delay++; key_flag = false; } void handleRoot() { char temp[550]; snprintf ( temp, 550, "<html>\ <head>\ <meta http-equiv='refresh' content='5'/>\ <meta charset=\"utf-8\">\ <title>ESP d1mini module</title>\ <style>\ body { background-color: #cfc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\ </style>\ <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"> \ </head>\ <body>\ <h1>ESP d1mini module </h1>\ <a href=\"index.php\">main page</a> <br /> \n \ <a href=index.xml>XML page</a> <br />\n \ <a href=index.json>JSON page</a> <br />\n \ </body></html>" ); server.send ( 200, "text/html", temp ); } void handleNotFound() { String s = " "; char footer[400]; snprintf ( footer, 400, "<html> <head> \ <meta charset=\"utf-8\">\ <title>ESP d1mini module</title>\ <style> \ body { background-color: #cfc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\ </style>\ <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"> \ </head><body>\ <h1>ESP d1mini module</h1>"); char button[30]; snprintf ( button, 30, "</body> </html>" ); // Serial.println(s); if(server.uri()=="/index.php" && server.args()>0) { String temp_max1; String temp_delta1; String temp_auto1; for ( uint8_t i = 0; i < server.args(); i++ ) { // settings if(server.argName ( i ) == "temp_max" ) { String temp_max1 = server.arg ( i ); temp_max = temp_max1.toInt(); Serial.println("Settings> temp_max:" + temp_max1 ); } if(server.argName ( i ) == "temp_delta" ) { String temp_delta1 = server.arg ( i ); temp_delta = temp_delta1.toInt(); Serial.println("Settings> temp_delta: " + temp_delta1); } if(server.argName(i)=="temp_auto") { if(server.arg(i)=="on") temp_auto = true; else temp_auto = false; } // reset if(server.argName(i) == "reset" && server.arg(i) == "run") ESP.reset(); //ESP.restart() } // arg for } // index String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><params>"; String json = "{"; String s_tmp = ""; s += "<form action=\"/index.php\" method=\"post\"> \ temp_max <input type=\"text\" name=\"temp_max\" value=\""+String(temp_max)+"\" size=2> C \ delta <input type=\"text\" name=\"temp_delta\" value=\""+String(temp_delta)+"\" size=2> C \ <input type=\"submit\" value=\"Set\"> \ </form>\n"; if(temp_auto) s_tmp="off"; else s_tmp="on"; s += "<form action=\"/index.php\" method=\"post\">temp_auto <input type=\"hidden\" name=\"temp_auto\" value=\""+s_tmp+"\"><input type=\"submit\" value=\""+s_tmp+"\"> </form>\n"; xml += "<temp_max>" + String(temp_max) + "</temp_max>"; json += "\"temp_max\": " + String(temp_max) + ","; xml += "<temp_delta>" + String(temp_delta) + "</temp_delta>"; json += "\"temp_delta\": " + String(temp_delta) + ","; xml += "<temp_auto>" + String(temp_auto) + "</temp_auto>"; json += "\"temp_auto\": " + String(temp_auto) + ","; /* if(digitalRead(0)==1) s_tmp="off"; else s_tmp="on"; s += "<form action=\"/index.php\" method=\"post\">Relay 0<input type=\"hidden\" name=\"r0\" value=\""+s_tmp+"\"><input type=\"submit\" value=\""+s_tmp+"\"> </form>\n"; xml += "<out0>"; xml +=digitalRead(0); xml +="</out0>"; */ if(digitalRead(relay5)==1) s_tmp="off"; else s_tmp="on"; s += "<form action=\"/index.php\" method=\"post\">Relay 5<input type=\"hidden\" name=\"r5\" value=\""+s_tmp+"\"><input type=\"submit\" value=\""+s_tmp+"\"> </form>\n"; // s += "<br />Relay 5: <a href=index.php?r5="+s_tmp+" >"+s_tmp+"</a><br />\n"; xml += "<out5>"; xml +=digitalRead(relay5); xml +="</out5>"; json += "\"out5\": \""; json +=digitalRead(relay5); json +="\","; // s += "Serial output: <a href=\"/index.php?tx=off\">TX off</a> <a href=\"/index.php?tx=on\">TX on</a><br />\n"; for (int i = 0; i < 8; i++) { if(owaddr[i]!="") { s += "Temperature "; s += i; s += " ["; s += owaddr[i]; s += "] : "; s += owtemp[i]; s += "c<br />\n"; xml += "<temp"; xml += i; xml +=">"; xml += owtemp[i]; xml += "</temp"; xml += i; xml +=">"; json += "\""; json += owaddr[i]; json += "\": "; json += "\""; json += owtemp[i]; json += "\","; } } s += "FreeMem = "; s += round(ESP.getFreeHeap()/1024); s += " kB<br /> \n"; s += "Uptime = "; s += round(millis() / (1000 * 60 * 60)); s += " hours <br />\n"; xml += "<Uptime>"; xml+=round(millis() / (1000 * 60 * 60)); xml+="</Uptime>"; json += "\"Uptime\": \""; json +=round(millis() / (1000 * 60 * 60)); json +="\","; s += "MAC: "; s += chip_id; s += " <br /> \n"; xml += "<MAC>"; xml+=chip_id; xml+="</MAC>"; json += "\"MAC\": \""; json +=chip_id; json +="\""; s += "Available input Params: temp_max[int], temp_delta[int], temp_auto[on/off], r5[on/off] <br />\n"; s += "URI: "; s += server.uri(); s += "<br />\n"; s += " Method: "; s += ( server.method() == HTTP_GET ) ? "GET" : "POST"; s += "\n"; for ( uint8_t i = 0; i < server.args(); i++ ) { s += " " + server.argName ( i ) + ": " + server.arg ( i ) + "<br />\n"; } s += " Arguments: "; s += server.args(); s += "<br />\n"; s += "<a href=index.php>Index page</a> <br /> \n \ <a href=index.xml>XML page</a> <br />\n \ <a href=index.json>JSON page</a> <br />\n"; s += owstat; xml += "</params>"; json += "}"; if(server.uri()=="/index.php" ) server.send ( 200, "text/html", footer + s + button ); if(server.uri()=="/index.xml") server.send ( 200, "text/xml", xml ); if(server.uri()=="/index.json") server.send ( 200, "application/json", json ); } void key_event() { if(!tm_flag && !dht_get_flag) key_flag = true; } void dht_get_event() { if(!tm_flag ) dht_get_flag = true; } void tm_event() { tm_flag=true; } void setup() { Serial.begin(115200); Serial.println("System inits"); pinMode(5, OUTPUT); digitalWrite(relay5, LOW); pinMode(2, OUTPUT); digitalWrite(relay5, LOW); // BUILTLED pinMode(12, INPUT); // timers in sec: dht_get.attach(302, dht_get_event); // 937 tim.attach(30.0, tm_event); key_r.attach(1.0, key_event); //0.3 //chip_id = ESP.getChipId(); chip_id = WiFi.macAddress(); // Start the server server.on ( "/", handleRoot ); //server.on ( "/test.svg", drawGraph ); //server.on ( "/inline", []() { server.send ( 200, "text/plain", "this works as well" ); } ); server.onNotFound ( handleNotFound ); server.begin(); Serial.println(chip_id +"Server started"); // Print the IP address Serial.println(WiFi.localIP()); sensors.begin(); // Start up the library owstat = ""; owstat += sensors.getDeviceCount() ; owstat += " Devices"; sensors.requestTemperatures(); for (int i = 0; i < sensors.getDeviceCount(); i++) { sensors.getAddress(deviceAddress, i); String addr = ""; for (uint8_t u = 0; u < 8; u++) addr += deviceAddress[u], HEX; owaddr[i] = addr; owtemp[i] = sensors.getTempCByIndex(i); } for (int i = 0; i < 8; i++) { owstat += "Sensor "; owstat += i; owstat += "-"; owstat += owaddr[i]; owstat += "-"; owstat += owtemp[i]; owstat += "; "; } } void loop() { if(tm_flag) time_finc(); if(key_flag && !dht_get_flag && !tm_flag) key_reader(); if(!key_flag && !dht_get_flag && !tm_flag) {// ArduinoOTA.handle(); server.handleClient();// http_server(); } if (!key_flag && !tm_flag && dht_get_flag) { Serial.println("dht_get_flag"); // check wifi if(WiFi.status() != WL_CONNECTED) { Serial.println("Not connected"); wifi_connect(); } else { time_update++; if(time_update>10) { http_get_finc( 1, "", 0); // sync time time_update=0; } } for (int i = 0; i < 8; i++) { owaddr[i]=""; owtemp[i]=0; } owstat = ""; owstat += sensors.getDeviceCount() ; owstat += " Devices"; sensors.requestTemperatures(); for (int i = 0; i < sensors.getDeviceCount(); i++) { sensors.getAddress(deviceAddress, i); String addr = ""; for (uint8_t u = 0; u < 8; u++) addr += deviceAddress[u], HEX; owaddr[i] = addr; owtemp[i] = sensors.getTempCByIndex(i); String id = "4"; id += i; // if(owtemp[i]<40 && owtemp[i]>-40) // http_get_finc( 2, id, owtemp[i]); Serial.print(i); Serial.print(owtemp[i]); Serial.println(" c"); if(temp_auto && i==0 && owtemp[i]<40 && owtemp[i]>-40) // auto temp control { temp_arr[temp_index] = owtemp[i]; float temp_summ = 0; Serial.print("Auto temp. arr:"); for (int k = 0; k < 3; k++) { temp_summ = temp_summ + temp_arr[k]; Serial.print(temp_arr[k]); Serial.print("-"); } Serial.print(" approx:"); temp_real = temp_summ / 3; Serial.print(temp_real); if(temp_index==2) { temp_index = 0; temp_arr_full = true; } else temp_index++; if(temp_arr_full) { if(temp_real>=(temp_max+temp_delta)) { Serial.println(" io 5 disable"); digitalWrite(relay5, LOW); } if(temp_real<=(temp_max-temp_delta)) { Serial.println(" io 5 enable"); digitalWrite(relay5, HIGH); } } } } dht_get_flag = false; } }

В коде много "фарша"- дописывайте под себя/

Можно переделать статически заданные сети на WIFImanager

У DS18B20 есть эффект самонагрева при комнатных температурах.

Датчик лучше не впаивать - мой первый впаянный датчик показывал к реальной температуре +6 градусов - приходилось вводить корректировку.

Lisolog / 2022