Термостат OpenTherm на ESP8266

tsv_33
Offline
Зарегистрирован: 11.04.2019

Приветствую. Делаю термостат для своего газового котла BAXI SLiM, работающий по протоколу OpenTherm. Всё вроде работает. Но веб страница долго грузится. Может есть какие замечения? Как ускорить процесс загрузки? С MQTT проблем нет. 

001#include <ESP8266WiFi.h>
002#include <PubSubClient.h>
003#include <WiFiClient.h>
004#include <ESP8266WebServer.h>
005#include <ESP8266mDNS.h>
006#include <OneWire.h>
007#include <DallasTemperature.h>
008#include <OpenTherm.h>               //<a href="https://github.com/ihormelnyk/opentherm_library/" rel="nofollow">https://github.com/ihormelnyk/opentherm_library/</a>
009 
010//OpenTherm input and output wires connected to 4 and 5 pins on the OpenTherm Shield
011const int inPin = 4; //D2
012const int outPin = 5; //D1
013 
014#define ONE_WIRE_BUS 14 //D5 Data wire is connected to 14 pin on the OpenTherm Shield
015#define BUILTIN_LED 2 //D4 Встроенный LED
016 
017const char* ssid = "*******";
018const char* password = "**********";
019const char* mqtt_server = "***********";
020const int   mqtt_port = 12345;
021const char* mqtt_user = "*********";
022const char* mqtt_password = "********";
023 
024ESP8266WebServer server(80); //Server on port 80
025OneWire oneWire(ONE_WIRE_BUS);
026DallasTemperature sensors(&oneWire);
027OpenTherm ot(inPin, outPin);
028WiFiClient espClient;
029PubSubClient client(espClient);
030char buf[50];
031 
032float sp = 20.00, //set point
033      pv = 0, //current temperature
034      pv_last = 0, //prior temperature
035      ierr = 0, //integral error
036      dt = 0, //time between measurements
037      op = 0; //PID controller output
038unsigned long ts = 0, new_ts = 0; //timestamp
039bool enableCentralHeating = true;
040bool enableHotWater = true;
041bool enableCooling = false;
042 
043int timer_off = 180; //Задержка выключения реле насоса СО 3 мин.
044 
045const char HTTP_HTML[] PROGMEM = "<!DOCTYPE html>\
046<html>\
047<head>\
048<link rel=\"icon\" type=\"image/jpg\" href=\"data:image/jpg;base64,AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAEAAAAAAAAAAAAAAAEAAAAAAAAdTGwAMIKyAAA+ZwBXrtoACh4jAOv6/AA6V4MAMWWJACFQbwAOU5wAj9P/ABVmewAqdZUAHFmBABBAZAAaXYQAC0ZtAC52mABPpckAB1NwAECj+QAdYI0AJmJ4AA1SbQANWXAAIEhhAB5ukwBHm7UAE0ORADVgewADQV8ADDtWAA49UwA3c8UAJ26TAI/R+gBOlccAFD1TABc8UABFodAA2e3yADFxigBQncQAIVpzAB1afAAcYIUAClBoACRdfABInO4AIz9QAAI0SwAseqUAHGWLAA9PbgAvgvsAi6i3AE+JrQCJx+8AFlFrAM3m8AAAHDEAabT/ABVTcQAYV2sAX6zNAKbFzgAqcIgAs9v0ADBoiwBLnMIAPkdEAE+bvwC43+4AOYmuAHOlwQAfWnQAACNPABxfegBcmLYAG1qGAApKaQA5TV8AFERgAKPW5gAqPUIAeXBjAFelwgAgXY8AWKfIAGhfVQAjaIMANT8/AAAdLABttfEAIUpgADKDpgAAQF4As9XsAHCiuQBVpPUAkbW7AA45ZADX8PQAHV11ABxafgA7jK8AUaLDAB5egQBDmP8ADkttAFakyQAiZYQAJ0FPAEuh7QAUcJMAeazXAB1qigAMVHYAElFtAAtFkQA0fKQAATxcAAI2ZQAuY34AXqjMABRWcwBYjqwAUlZXAFGTxABbqu0A2unyAPT//QCNudEAHVKLAE+jxwBirvAAVGNsADR6nwAcSmIAEk9xAOX1+wAoYoUARZa8AAE4YwAAO2MAB2OAAEKcxQAKHSUAJkpiAB1DgwAbVG4AAB1AABc2SwArTF8AEWJ9AE5wpQBJnssAGlt6AE2gvwAza44AMW6OAAFLbwAidqYAJjg/AAk3hABSocIAIlt6ADyQtAAEPpAAUqPIABJcqgBVo8gAkcDVAFehywAeZ4MAJGGDAEeOwABYn+kAAD5kAEqXtwB6t98AMVyPABlVcgA8f6YAGlx1AHvE4gBOnsMACCg1AFCewwAogJgAH0BPABpejQAAL1MAEmWZAA5MagD///8AmL7QABtHZAAxfaEAYLv+AAtZdgAdU2QADk2RADhzuQAob4oAS5m+ACYxNQAdU3YAACFFACdwlgAGS18AIFpwABddggAhWHMAIVp5AA1FaAAJLDkAAytIADVvkwAjQEcAAk99AFajygAjYYUAKGJ/AAY8pwAGOFQAElV0AGqltQCCtckAW6vQABZWbgAVVXQATZe5AEmVxQBWVEwABTxpAAM/aQAYWYAAEjdjAAZBbwAdWXcATaHFABIkKwBJZXYAIFyAAAVVbAAumdIAKY7wAN37/wBYpssAlsPYABFPdQAAAAAAAAAAAAAAAAAAAAAA85kgmDFeH29f1lHbf1Tyo3nZ2Lsy7JDrEHpMXFuIPARSJpS+Zcp3pKiqCZeTzkbqybXgHMuxXTD3FMchle5VWRYkbDYjCoc5YXNjPZsGGXDfC7mAsIFx+MPDtIXF4cAlKeJaQnVQTR03BWTQPvsOirypeNF90vUCTkg4sua2wgiJRJ8u0hiRU8OMT48/uDUADCJpp3QXyPo7g2Lvi9Pnz24SVukROhNynITkoNcrnfSrR+hFt3ZgvSesShVtliyvrc2znkmu48RmKH7caEsvjXwqG6WSGkGCQ8ahNA/wZ43xhmpYjpoewfaiLb/t1aZrQPnd5brM1AEDMw1X3nsH2gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\">\
049<title>Термостат</title>\
050<meta charset=\"utf-8\">\
051    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\
052 <script>\
053   window.setInterval(\"update()\", 2000);\
054    function update(a,b,c,d,e,f,g,h){\
055      var a = new XMLHttpRequest();\
056      a.open(\"GET\", \"/temp\", true);\
057      a.onreadystatechange = function() {\
058        if (a.readyState != XMLHttpRequest.DONE || a.status != 200) return;\
059        document.getElementById('temp').innerHTML = a.responseText;\
060      };\
061      a.send();\
062      var b = new XMLHttpRequest();\
063      b.open(\"GET\", \"/boilertemp\", true);\
064      b.onreadystatechange = function() {\
065        if (b.readyState != XMLHttpRequest.DONE || b.status != 200) return;\
066        document.getElementById('boilertemp').innerHTML = b.responseText;\
067      };\
068      b.send();\
069      var c = new XMLHttpRequest();\
070      c.open(\"GET\", \"/dhwttemp\", true);\
071      c.onreadystatechange = function() {\
072        if (c.readyState != XMLHttpRequest.DONE || c.status != 200) return;\
073        document.getElementById('dhwttemp').innerHTML = c.responseText;\
074      };\
075      c.send();\
076      var d = new XMLHttpRequest();\
077      d.open(\"GET\", \"/outsidetemp\", true);\
078      d.onreadystatechange = function() {\
079        if (d.readyState != XMLHttpRequest.DONE || d.status != 200) return;\
080        document.getElementById('outsidetemp').innerHTML = d.responseText;\
081      };\
082      d.send();\
083      var e = new XMLHttpRequest();\
084      e.open(\"GET\", \"/chon\", true);\
085      e.onreadystatechange = function() {\
086        if (e.readyState != XMLHttpRequest.DONE || e.status != 200) return;\
087        document.getElementById('chon').innerHTML = e.responseText;\
088      };\
089      e.send();\
090      var f = new XMLHttpRequest();\
091      f.open(\"GET\", \"/hwon\", true);\
092      f.onreadystatechange = function() {\
093        if (f.readyState != XMLHttpRequest.DONE || f.status != 200) return;\
094        document.getElementById('hwon').innerHTML = f.responseText;\
095      };\
096      f.send();\
097      var g = new XMLHttpRequest();\
098      g.open(\"GET\", \"/flameon\", true);\
099      g.onreadystatechange = function() {\
100        if (g.readyState != XMLHttpRequest.DONE || g.status != 200) return;\
101        document.getElementById('flameon').innerHTML = g.responseText;\
102      };\
103      g.send();\
104      var h = new XMLHttpRequest();\
105      h.open(\"GET\", \"/getadc\", true);\
106      h.onreadystatechange = function() {\
107        if (h.readyState != XMLHttpRequest.DONE || h.status != 200) return;\
108        document.getElementById('getadc').innerHTML = h.responseText;\
109      };\
110      h.send();\
111    }\
112     </script>\
113</head>\
114<body style=\"text-align:center\">\
115<div style=\"line-height:0.5\">\
116    <h1><font color=\"#1c6b72\"size=\"5\"face=\"Verdana\">Термостат</font></h1>\
117    <img src=\"data:image/png;base64,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\"><p>\
118    <font size=\"3\"face=\"Verdana\">OpenTherm</font><p>\
119    <hr align=\"center\" size=\"3\" width=\"290px\" color=\"#1c6b72\"><br/><br/>\
120    <font color=\"#1c6b72\"size=\"8\"face=\"Verdana\"><span id=\"temp\">{0}</span>°C</font><p><br/><br/>\
121    <font size=\"4\"face=\"Verdana\">Котёл, подача:&nbsp <span id=\"boilertemp\">{2}</span>°C</font><p><br/>\
122    <font size=\"4\"face=\"Verdana\">Горячая вода:&nbsp;&nbsp <span id=\"dhwttemp\">{3}</span>°C</font><p><br/>\
123    <font size=\"4\"face=\"Verdana\">На улице:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp <span id=\"outsidetemp\">{4}</span>°C</font><p><br/>\
124    <font size=\"4\"face=\"Verdana\">Модуляция:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp <span id=\"getadc\">{8}</span>%</font><p>\
125    <hr align=\"center\" size=\"3\" width=\"290px\" color=\"#1c6b72\"><br/>\
126    <form method=\"post\">\
127    <font size=\"4\"face=\"Verdana\">Значение уставки,°C: <b><input type=\"number\" min=\"10\" max=\"30\" step=\"0.1\" name=\"sp\" value=\"{1}\" style=\"font-size:17px; width:65px; background-color:#E1E1E1\"></b></font><br/><br/><br/>\
128        <input type=\"submit\"value=\"Записать\" style=\"font-size:16px; width:100px\">\
129      <form>\
130    </p>\
131    <hr align=\"center\" size=\"3\" width=\"290px\" color=\"#1c6b72\"><br/>\
132    <font size=\"3\"face=\"Verdana\">Статус СО:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp <b><span id=\"chon\">{5}</span></b></font><p><br/>\
133    <font size=\"3\"face=\"Verdana\">Статус ГВС:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp <b><span id=\"hwon\">{6}</span></b></font><p><br/>\
134    <font size=\"3\"face=\"Verdana\">Статус горелки:&nbsp <b><span id=\"flameon\">{7}</span></b></font><p>\
135    </div>\
136</body>\
137</html>";
138 
139void handleInterrupt() {
140  ot.handleInterrupt();
141}
142 
143float getTemp() {
144  return sensors.getTempCByIndex(0);
145}
146float getBoilerTemp() {
147  return ot.getBoilerTemperature();
148}
149float getDHWTemp() {
150  unsigned long request26 = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Tdhw, 0);
151  unsigned long respons26 = ot.sendRequest(request26);
152  uint16_t dataValue26 = respons26 & 0xFFFF;
153  float result26 = dataValue26 / 256;
154  return result26;
155}
156float getOutsideTemp() {
157  unsigned long request27 = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Toutside, 0);
158  unsigned long respons27 = ot.sendRequest(request27);
159  uint16_t dataValue27 = respons27 & 0xFFFF;
160  if (dataValue27 > 32768) {
161    //negative
162    float result27 = -(65536 - dataValue27) / 256;
163    return result27;
164  } else {
165    //positive
166    float result27 = dataValue27 / 256;
167    return result27;
168  }
169}
170int getADC() {
171  delay(100);
172  int ar = analogRead(A0);
173  ar = map(ar, 26, 1023, 0, 100);
174  return ar;
175}
176unsigned long getCentralHeatingEnabled() {
177  unsigned long response = ot.setBoilerStatus(enableCentralHeating, enableHotWater, enableCooling);
178  OpenThermResponseStatus responseStatus = ot.getLastResponseStatus();
179  if (responseStatus = OpenThermResponseStatus::SUCCESS)
180    return ot.isCentralHeatingEnabled(response);
181}
182unsigned long getHotWaterEnabled() {
183  unsigned long response = ot.setBoilerStatus(enableCentralHeating, enableHotWater, enableCooling);
184  OpenThermResponseStatus responseStatus = ot.getLastResponseStatus();
185  if (responseStatus = OpenThermResponseStatus::SUCCESS)
186    return ot.isHotWaterEnabled(response);
187}
188unsigned long getFlameOn() {
189  unsigned long response = ot.setBoilerStatus(enableCentralHeating, enableHotWater, enableCooling);
190  OpenThermResponseStatus responseStatus = ot.getLastResponseStatus();
191  if (responseStatus = OpenThermResponseStatus::SUCCESS)
192    return ot.isFlameOn(response);
193}
194float pid(float sp, float pv, float pv_last, float& ierr, float dt) {
195  float Kc = 10.0; // K / %Heater
196  float tauI = 50.0; // sec
197  float tauD = 1.0;  // sec
198  // PID coefficients
199  float KP = Kc;
200  float KI = Kc / tauI;
201  float KD = Kc * tauD;
202  // upper and lower bounds on heater level
203  float ophi = 100;
204  float oplo = 0;
205  // calculate the error
206  float error = sp - pv;
207  // calculate the integral error
208  ierr = ierr + KI * error * dt;
209  // calculate the measurement derivative
210  float dpv = (pv - pv_last) / dt;
211  // calculate the PID output
212  float P = KP * error; //proportional contribution
213  float I = ierr; //integral contribution
214  float D = -KD * dpv; //derivative contribution
215  float op = P + I + D;
216  // implement anti-reset windup
217  if ((op < oplo) || (op > ophi)) {
218    I = I - KI * error * dt;
219    // clip output
220    op = max(oplo, min(ophi, op));
221  }
222  ierr = I;
223  Serial.println("Заданное значение температуры в помещкнии = " + String(sp) + " °C");
224  Serial.println("Текущее значение температуры в помещкнии = " + String(pv) + " °C");
225  Serial.println("Выхов ПИД регулятора = " + String(op));
226  Serial.println("Время между измерениями = " + String(dt) + "; ПИД коэффициенты: П = " + String(P) + "; И = " + String(I) + "; Д = " + String(D));
227  return op;
228}
229//===============================================================
230// Эта процедура выполняется при открытии IP-адреса в браузере
231//===============================================================
232void handleRoot() {
233  digitalWrite(BUILTIN_LED, 1);
234  if (server.method() == HTTP_POST) {
235    for (uint8_t i = 0; i < server.args(); i++) {
236      if (server.argName(i) == "sp") {
237        sp = server.arg(i).toFloat();
238      }
239    }
240  }
241  String page = FPSTR(HTTP_HTML);
242  page.replace("{0}", String(getTemp()));
243  page.replace("{1}", String((float)sp));
244  page.replace("{2}", String(getBoilerTemp()));
245  page.replace("{3}", String(getDHWTemp()));
246  page.replace("{4}", String(getOutsideTemp()));
247  page.replace("{5}", String(getCentralHeatingEnabled() ? "on" : "off"));
248  page.replace("{6}", String(getHotWaterEnabled() ? "on" : "off"));
249  page.replace("{7}", String(getFlameOn() ? "on" : "off"));
250  page.replace("{8}", String(getADC()));
251  server.send(200, "text/html", page);
252  digitalWrite(BUILTIN_LED, 0);
253}
254void handleGetTemp() {
255  //digitalWrite(BUILTIN_LED, 1);
256  server.send(200, "text/plain", String(getTemp()));
257  //digitalWrite(BUILTIN_LED, 0);
258}
259void handleGetBoilerTemp() {
260  //digitalWrite(BUILTIN_LED, 1);
261  server.send(200, "text/plain", String(getBoilerTemp()));
262  //digitalWrite(BUILTIN_LED, 0);
263}
264void handleGetDHWTemp() {
265  //digitalWrite(BUILTIN_LED, 1);
266  server.send(200, "text/plain", String(getDHWTemp()));
267  //digitalWrite(BUILTIN_LED, 0);
268}
269void handleGetOutsideTemp() {
270  //digitalWrite(BUILTIN_LED, 1);
271  server.send(200, "text/plain", String(getOutsideTemp()));
272  //digitalWrite(BUILTIN_LED, 0);
273}
274void handleGetCentralHeatingEnabled() {
275  //digitalWrite(BUILTIN_LED, 1);
276  server.send(200, "text/plain", String(getCentralHeatingEnabled() ? "on" : "off"));
277  //digitalWrite(BUILTIN_LED, 0);
278}
279void handleGetHotWaterEnabled() {
280  //digitalWrite(BUILTIN_LED, 1);
281  server.send(200, "text/plain", String(getHotWaterEnabled() ? "on" : "off"));
282  //digitalWrite(BUILTIN_LED, 0);
283}
284void handleGetFlameOn() {
285  //digitalWrite(BUILTIN_LED, 1);
286  server.send(200, "text/plain", String(getFlameOn() ? "on" : "off"));
287  //digitalWrite(BUILTIN_LED, 0);
288}
289void handleADC() {
290  //digitalWrite(BUILTIN_LED, 1);
291  server.send(200, "text/plain", String(getADC()));
292  //digitalWrite(BUILTIN_LED, 0);
293}
294//==============================================================
295//                  SETUP
296//==============================================================
297void setup_wifi() {
298  delay(10);
299  //Connect to Wi-Fi Network
300  Serial.println();
301  Serial.print("Connecting to ");
302  Serial.println(ssid);
303  WiFi.mode(WIFI_STA);
304  WiFi.begin(ssid, password); //Connect to your Wi-Fi router
305  // Wait for connection
306  while (WiFi.status() != WL_CONNECTED) {
307    delay(500);
308    Serial.print(".");
309  }
310  //If connection successful show IP address in serial monitor
311  Serial.println("");
312  Serial.println("WiFi connected");
313  Serial.println("IP address: ");
314  Serial.println(WiFi.localIP());
315}
316void setup(void) {
317  pinMode(BUILTIN_LED, OUTPUT);
318  digitalWrite(BUILTIN_LED, 0);
319  Serial.begin(115200);
320  setup_wifi();
321  if (MDNS.begin("thermostat")) {
322    Serial.println("MDNS responder started");
323  }
324  //Initialize Webserver
325  server.on("/", handleRoot); // Ответ сервера на запрос главной страницы
326  server.on("/temp", handleGetTemp); // Ответ сервера на запрос температуры в помещении
327  server.on("/boilertemp", handleGetBoilerTemp); // Ответ сервера на запрос температуры подачи котла
328  server.on("/dhwttemp", handleGetDHWTemp); // Ответ сервера на запрос температуры горячей воды
329  server.on("/outsidetemp", handleGetOutsideTemp); // Ответ сервера на запрос уличной температуры
330  server.on("/chon", handleGetCentralHeatingEnabled); // Ответ сервера на запрос состояния СО
331  server.on("/hwon", handleGetHotWaterEnabled); // Ответ сервера на запрос состояния ГВС
332  server.on("/flameon", handleGetFlameOn); // Ответ сервера на запрос состояния горелки
333  server.on("/getadc", handleADC); //Reads ADC function
334  server.begin();
335  Serial.println("HTTP server started");
336  //==============================================================
337  //                  Init DS18B20 sensor
338  //==============================================================
339  sensors.begin();
340  sensors.requestTemperatures();
341  sensors.setWaitForConversion(false); //switch to async mode
342  pv, pv_last = sensors.getTempCByIndex(0);
343  ts = millis();
344  //==============================================================
345  //                  Init OpenTherm Controller
346  //==============================================================
347  ot.begin(handleInterrupt);
348  //==============================================================
349  //                  Init MQTT Client
350  //==============================================================
351  client.setServer(mqtt_server, mqtt_port);
352  client.setCallback(callback);
353}
354void publish_temperature() {
355  Serial.println("MQTT, Current Room Temperature, °C = " + String(pv));
356  String(pv).toCharArray(buf, 50);
357  client.publish("pv", buf);
358}
359void publish_boilertemp() {
360  Serial.println("MQTT, CH Temperature, °C = " + String(getBoilerTemp()));
361  String(getBoilerTemp()).toCharArray(buf, 50);
362  client.publish("cht", buf);
363}
364void publish_dhwtemp() {
365  Serial.println("MQTT, DHW Temperature, °C = " + String(getDHWTemp()));
366  String(getDHWTemp()).toCharArray(buf, 50);
367  client.publish("dhwt", buf);
368}
369void publish_outtemp() {
370  Serial.println("MQTT, Outside Temperature, °C = " + String(getOutsideTemp()));
371  String(getOutsideTemp()).toCharArray(buf, 50);
372  client.publish("outt", buf);
373}
374void publish_statusCH() {
375  Serial.println("MQTT, Status Central Heating = " + String(getCentralHeatingEnabled()));
376  String(getCentralHeatingEnabled()).toCharArray(buf, 50);
377  client.publish("sch", buf);
378}
379void publish_ADC() {
380  Serial.println("MQTT, Relative Modulation Level, % = " + String(getADC()));
381  String(getADC()).toCharArray(buf, 50);
382  client.publish("adc", buf);
383}
384void publish_statusDWH() {
385  Serial.println("MQTT, Status Hot Water = " + String(getHotWaterEnabled()));
386  String(getHotWaterEnabled()).toCharArray(buf, 50);
387  client.publish("sdwh", buf);
388}
389void publish_statusFlame() {
390  Serial.println("MQTT, Status Central Heating = " + String(getFlameOn()));
391  String(getFlameOn()).toCharArray(buf, 50);
392  client.publish("sfl", buf);
393}
394void publish_setBoilerTemperature() {
395  Serial.println("MQTT, PID Controller Output, % = Set Point CH Temperature, °C = " + String(op));
396  String(op).toCharArray(buf, 50);
397  client.publish("op", buf);
398}
399void callback(char* topic, byte* payload, unsigned int length) {
400  if (strcmp(topic, "sp") != 0) return;
401  String str = String();
402  for (int i = 0; i < length; i++) {
403    str += (char)payload[i];
404  }
405  Serial.println("MQTT, Setpoint Room Temperature = " + str);
406  sp = str.toFloat();
407}
408void reconnect() {
409  while (!client.connected()) {
410    Serial.print("Attempting MQTT connection...");
411    // Attempt to connect
412    if (client.connect("ESP8266Client", mqtt_user, mqtt_password)) {
413      Serial.println("connected");
414      // Once connected, publish an announcement...
415      publish_temperature();
416      publish_boilertemp();
417      publish_dhwtemp();
418      publish_outtemp();
419      publish_ADC();
420      publish_statusCH();
421      publish_statusDWH();
422      publish_statusFlame();
423      publish_setBoilerTemperature();
424      // ... and resubscribe
425      client.subscribe("sp");
426    } else {
427      Serial.print("failed, rc =");
428      Serial.print(client.state());
429      Serial.println(" try again in 5 seconds");
430      // Wait 5 seconds before retrying
431      delay(5000);
432    }
433  }
434}
435//==============================================================
436//                     LOOP
437//==============================================================
438void loop(void) {
439  new_ts = millis();
440  if (new_ts - ts > 1000) {
441    unsigned long response = ot.setBoilerStatus(enableCentralHeating, enableHotWater, enableCooling);
442    OpenThermResponseStatus responseStatus = ot.getLastResponseStatus();
443    if (responseStatus = OpenThermResponseStatus::SUCCESS) {
444      //Serial.println("Error: Invalid boiler response " + String(response, HEX));
445      Serial.println("Текущий статус системы отопления: " + String(ot.isCentralHeatingEnabled(response) ? "on" : "off"));
446      Serial.println("Текущий статус горячей воды: " + String(ot.isHotWaterEnabled(response) ? "on" : "off"));
447      Serial.println("Текущий статус горелки: " + String(ot.isFlameOn(response) ? "on" : "off"));
448      Serial.println("Индикация состояния неисправности: " + String(ot.isFault(response) ? "fault" : "no fault"));
449      Serial.println("Диагностическая индикация: " + String(ot.isDiagnostic(response) ? "diagnostics" : "no diagnostics"));
450    }
451    if (responseStatus == OpenThermResponseStatus::NONE) {
452      Serial.println("Error: OpenTherm is not initialized");
453    }
454    else if (responseStatus == OpenThermResponseStatus::INVALID) {
455      Serial.println("Error: Invalid response " + String(response, HEX));
456    }
457    else if (responseStatus == OpenThermResponseStatus::TIMEOUT) {
458      Serial.println("Error: Response timeout");
459    }
460    pv = sensors.getTempCByIndex(0);
461    dt = (new_ts - ts) / 1000.0;
462    ts = new_ts;
463    if (responseStatus == OpenThermResponseStatus::SUCCESS) {
464      op = pid(sp, pv, pv_last, ierr, dt);
465      //Set CH Temperature
466      ot.setBoilerTemperature(op);
467      pv_last = pv;
468      sensors.requestTemperatures(); //async temperature request
469    }
470    //Print Temperature
471 
472    Serial.println("Текущая температура контура СО = " + String(getBoilerTemp()) + " °C");
473    Serial.println("Текущая температура контура ГВС = " + String(getDHWTemp()) + " °C");
474    Serial.println("Температура на улице = " + String(getOutsideTemp()) + " °C");
475    Serial.println("Текущий уровень модуляции горелки  = " + String (getADC()) + " %");
476    Serial.println("ADC = " + String (analogRead(A0)));
477 
478    publish_temperature();
479    publish_boilertemp();
480    publish_dhwtemp();
481    publish_outtemp();
482    publish_ADC();
483    publish_statusCH();
484    publish_statusDWH();
485    publish_statusFlame();
486    publish_setBoilerTemperature();
487  }
488  //MQTT Loop
489  if (!client.connected()) {
490    reconnect();
491  }
492  client.loop();
493  server.handleClient(); //handle http requests
494}

 

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

Посмотрел по диагонали, возможно не прав, у вас получение температуры в самом выводе html странички.

Вообще все плохо, получение статусов котла температуры и прочих данных должно быть неблокирующим кодом по таймерам, а вывод в страничку из переменных а не динамически получать. Кстати зачем страничку в progmem? Неужели места в esp не хватило.

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

ЕвгенийП пишет:

andycat пишет:
Кстати зачем страничку в progmem?

А в строке №241 эта самая страничка целиком закачивается в RAM


И на это тоже тратится драгоценное процессорное время

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

строка 223 и 224 орфографические ошибки, это, что бросилось в глаза

tsv_33
Offline
Зарегистрирован: 11.04.2019

ua6em пишет:

строка 223 и 224 орфографические ошибки, это, что бросилось в глаза

Спасибо, исправил. 

Алексей.
Алексей. аватар
Offline
Зарегистрирован: 02.02.2018

Давно такого не видел, чтоб статику, по определению размещенную в PROGMEM, там ей и место, перегружать в стринг и в этом стринге реплейсить.
Для чего настраивать интервал, чтоб каждые 2 секунды асинхронно выполнялись куча запросов (броузер их параллельно ведь выполняет).
/temp /boilertemp /dhwttemp /outsidetemp /chon /hwon /flameon /getadc
ни ужели нельзя обойтись одним запросом, получать всё необходимое сразу.
 

tsv_33
Offline
Зарегистрирован: 11.04.2019

Алексей. пишет:

Давно такого не видел, чтоб статику, по определению размещенную в PROGMEM, там ей и место, перегружать в стринг и в этом стринге реплейсить.
Для чего настраивать интервал, чтоб каждые 2 секунды асинхронно выполнялись куча запросов (броузер их параллельно ведь выполняет).
/temp /boilertemp /dhwttemp /outsidetemp /chon /hwon /flameon /getadc
ни ужели нельзя обойтись одним запросом, получать всё необходимое сразу.

Алексей., пробовал и одним запросом, как выше написали, но переменные не обновляются без перезагрузки страницы...

Алексей.
Алексей. аватар
Offline
Зарегистрирован: 02.02.2018

Что значит не обновляются? Есп их не отправляет? А по отдельности отправляет? Целую страницу отправляет, а десяток переменных не может? Да быть такого не может. Вы увеличиваете нагрузку, отправляя с броузера сразу несколько запросов, по одному для каждого параметра.

VOVA_iS
Offline
Зарегистрирован: 09.07.2019

tsv_33 пишет:

Приветствую. Делаю термостат для своего газового котла BAXI SLiM, работающий по протоколу OpenTherm. Всё вроде работает. Но веб страница долго грузится. Может есть какие замечения? Как ускорить процесс загрузки? С MQTT проблем нет. 

 

Здравствуйте 

Загрузил ваш скейтч

У меня котел не отключает помпу отопления. У Вас все нормально с этим

И возможно ли дописать скейтч для регулирования температуры горячей воды(можно только через mqtt)

Спасибо

b707
Offline
Зарегистрирован: 26.05.2017

VOVA_iS - не надо этот код брать, он откровенно плохо написан. Не шутите с газом.

VOVA_iS
Offline
Зарегистрирован: 09.07.2019

b707 пишет:

VOVA_iS - не надо этот код брать, он откровенно плохо написан. Не шутите с газом.

Тут криво веб сервер написан

По работе котла код скопирован с примера библиотеки OpenTherm. 

Mqtt работает хорошо

tsv_33
Offline
Зарегистрирован: 11.04.2019

VOVA_iS, да, помпа молотит постоянно, повесил на термостат своё реле для её управления. Горячую воду то же сделал. Вообще много чего добавил и переделал, и продолжаю делать...

P.S. Да, сервер кривой :-(

tsv_33
Offline
Зарегистрирован: 11.04.2019

VOVA_iS, от веб сервера решил отказаться, оставлю только конфигурационную страничку и всё, а для рулёжки достаточно mqtt. 

VOVA_iS
Offline
Зарегистрирован: 09.07.2019

tsv_33 пишет:

VOVA_iS, да, помпа молотит постоянно, повесил на термостат своё реле для её управления. Горячую воду то же сделал. Вообще много чего добавил и переделал, и продолжаю делать...

P.S. Да, сервер кривой :-(

про помпу можно подробней:)?

скетч можно с горячей водой?

VOVA_iS
Offline
Зарегистрирован: 09.07.2019

tsv_33 пишет:

VOVA_iS, от веб сервера решил отказаться, оставлю только конфигурационную страничку и всё, а для рулёжки достаточно mqtt. 

Да я согласен по MQTT управления выше крыши. И умный дом можно добавить полноценно

tsv_33
Offline
Зарегистрирован: 11.04.2019

VOVA_iS пишет:

про помпу можно подробней:)?

скетч можно с горячей водой?

Для помпы использую библиотеку TimingRelay.h:

1#define PIN_RELAY 12                       // D6 На внешнее реле управления насосом СО и внешнюю индикацию работы насоса СО
2TimingRelay relay(PIN_RELAY, 0, 180000);   // Создаем реле времени (назначенный порт, задержка включения, задержка выключения).

В setup:

1pinMode(PIN_RELAY, OUTPUT);
2 relay.autoReset = true;      // Автоматический запуск таймера задержки выключения.

В loop реле работает по условию:

1// обработка индикации и реле насоса СО.
2  if (status_flame == 1 && status_dhw == 0) {    // Если состояния горелки = 1 и статус состояния ГВС = 0 включаем реле.
3    relay.on();
4  } else {
5    relay.off();
6  }
7  relay.loop();                                  // Обрабатываем (обязательно).
8// конец обработки индикации и реле насоса СО.

С ГВС не так всё просто... Можно и по играться уставкой температуры ГВС. :-) При работе от внешнего термостата, а рекомендованный это QAA73, именно он задаёт режим работы ГВС, т.е. когда включить по таймеру (1 раз в неделю) прогрев системы до 65 гр., чтобы убить бацилы. При отсутствии внешнего это делает автоматика котла. Если в термостате этого нет, то ручками и по календарю. У меня пока не получилось автоматом, а может и получилось, полноценно не проверял, здесь, на форуме помогли.

Вот по порядку:

01#define DAY(x)    ((x) * 24 * MIN(60))
02#define MIN(x)    ((x) * 10000ul)
03#define TIME1      DAY(7)
04#define TIME2      MIN(60)
05#define VAL1       50.
06#define VAL2       60.
07 
08float spdhw = VAL1;  // точка отсчета температуры горячей воды контура ГВС
09unsigned int hex56 = (spdhw * 256 * 16 / 16); // точка отсчета температуры горячей воды контура ГВС, но в (HEX)
10 
11// Обработчик входящих тем и полезных нагрузок MQTT
12void callback(char* topic, byte* payload, unsigned int length) {
13  char buffer[length + 1];
14  memcpy(buffer, payload, length);
15  buffer[length] = '\0';
16  float a = atof((char*)buffer);
17  if (a <= 29) {
18    sp = a;
19  } else {
20    spdhw = a;
21  }
22  //Serial.println("MQTT,topic = " + String(a));
23}

В reconnect добавить строку 

1client.subscribe("sp");  // эта уже есть
2client.subscribe("spdhw");  // добавить

В loop

01//==============================================================================
02// Активации функции Антилегионелла ГВС 65°C на 1 час каждые 7 дней по кругу.
03//==============================================================================
04 
05  static bool state;
06  static uint32_t old_millis = millis();
07  if (state == false) {
08    if (millis() - old_millis >= TIME1) {
09      old_millis = millis();
10      spdhw = VAL1;
11      state = true;
12    }
13  } else if (millis() - old_millis >= TIME2) {
14    old_millis = millis();
15    spdhw = VAL2;
16    state = false;
17  }

 

VOVA_iS
Offline
Зарегистрирован: 09.07.2019

А как параметр spdhw передаётся в котёл? не вижу

tsv_33
Offline
Зарегистрирован: 11.04.2019

VOVA_iS пишет:

А как параметр spdhw передаётся в котёл? не вижу

Извиняюсь!

1float setDHWTemp() {
2  unsigned long request56 = ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::TdhwSet, hex56);
3  unsigned long respons56 = ot.sendRequest(request56);
4  uint16_t dataValue56 = respons56 & 0xFFFF;
5  float result56 = dataValue56 / 256;
6  return result56;
7}

В loop после 466 строки (код в начале) добавить:

1// Заданная температура ГВС
2      hex56 = (spdhw * 256 * 16 / 16);
3      unsigned int setDHWTemp(hex56);

 

VOVA_iS
Offline
Зарегистрирован: 09.07.2019

Ещё вырежу всю веб морду:-)

спасибо завтра попробую скомпилировать скетч

tsv_33
Offline
Зарегистрирован: 11.04.2019

VOVA_iS, есть ещё одна задумка. В слимах по ОТ не передаётся уровень модуляции горелки.  Можно сделать используя у ESP 8266 А0. 

VOVA_iS
Offline
Зарегистрирован: 09.07.2019

Интересная идея:-) только я на сколько помню на А0  может  быть максимум 3.3v

надо сначала замеры сделать у котла:-)

 

tsv_33
Offline
Зарегистрирован: 11.04.2019

VOVA_iS, вполне достаточно. С котла 9,10 контакты А5 с катушки модуляции идёт ШИМ. Надо ставить операционник, преобразовать ШИМ в линейные 0-5В, короче с него через делитель на А0. Программно потребуется ремапинг АЦП.

VOVA_iS
Offline
Зарегистрирован: 09.07.2019

tsv_33 пишет:

VOVA_iS пишет:

про помпу можно подробней:)?

скетч можно с горячей водой?

Для помпы использую библиотеку TimingRelay.h:

1#define PIN_RELAY 12                       // D6 На внешнее реле управления насосом СО и внешнюю индикацию работы насоса СО
2TimingRelay relay(PIN_RELAY, 0, 180000);   // Создаем реле времени (назначенный порт, задержка включения, задержка выключения).

В setup:

1pinMode(PIN_RELAY, OUTPUT);
2 relay.autoReset = true;      // Автоматический запуск таймера задержки выключения.

В loop реле работает по условию:

1// обработка индикации и реле насоса СО.
2  if (status_flame == 1 && status_dhw == 0) {    // Если состояния горелки = 1 и статус состояния ГВС = 0 включаем реле.
3    relay.on();
4  } else {
5    relay.off();
6  }
7  relay.loop();                                  // Обрабатываем (обязательно).
8// конец обработки индикации и реле насоса СО.

 

Не могу скомпилировать идут ошибки

в коде данные переменные status_flame status_dhw не опредлены 

 

tsv_33
Offline
Зарегистрирован: 11.04.2019

Всё верно, статусы не задекларированы, вставьте это в loop выше индикации и состояния насоса:

01// обработка индикации состояния ГВС.
02  bool status_dhw = getHotWaterEnabled();
03  if (status_dhw == 1) {
04    digitalWrite(EXTERNAL_LED, 1);
05  } else {
06    digitalWrite(EXTERNAL_LED, 0);
07  }
08  // обработка индикации состояния горелки.
09  bool status_flame = getFlameOn();
10  if (status_flame == 1) {
11    digitalWrite(EXTERNAL_LED_1, 1);
12  } else {
13    digitalWrite(EXTERNAL_LED_1, 0);
14  }

Условия для индикации можно отключить, а то опять ругаться будет при компиляции...

VOVA_iS
Offline
Зарегистрирован: 09.07.2019

я так понимаю нужно это 

1void callback(char* topic, byte* payload, unsigned int length) {
2  if (strcmp(topic, "sp") != 0) return;
3  String str = String();
4  for (int i = 0; i < length; i++) {
5    str += (char)payload[i];
6  }
7Serial.println("MQTT, Setpoint Room Temperature = " + str);
8sp = str.toFloat();
9}

Заменить на это 

01// Обработчик входящих тем и полезных нагрузок MQTT
02void callback(char* topic, byte* payload, unsigned int length) {
03  char buffer[length + 1];
04  memcpy(buffer, payload, length);
05  buffer[length] = '\0';
06  float a = atof((char*)buffer);
07  if (a <= 29) {
08    sp = a;
09  } else {
10    spdhw = a;
11  }
   
   

 

tsv_33
Offline
Зарегистрирован: 11.04.2019

Да, верно.

VOVA_iS
Offline
Зарегистрирован: 09.07.2019
001#include <ESP8266WiFi.h>
002#include <PubSubClient.h>
003#include <WiFiClient.h>
004#include <TimingRelay.h>
005#include <ESP8266mDNS.h>
006#include <OneWire.h>
007#include <DallasTemperature.h>
008#include <OpenTherm.h>               //<a href="<a href="https://github.com/ihormelnyk/opentherm_library/" rel="nofollow">https://github.com/ihormelnyk/opentherm_library/</a>" rel="nofollow"><a href="https://github.com/ihormelnyk/opentherm_library/" rel="nofollow">https://github.com/ihormelnyk/opentherm_library/</a></a>
009 
010//OpenTherm input and output wires connected to 4 and 5 pins on the OpenTherm Shield
011const int inPin = 4; //D2
012const int outPin = 5; //D1
013 
014#define ONE_WIRE_BUS 14 //D5 Data wire is connected to 14 pin on the OpenTherm Shield
015#define BUILTIN_LED 2 //D4 Встроенный LED
016#define PIN_RELAY 12                       // D6 На внешнее реле управления насосом СО и внешнюю индикацию работы насоса СО
017TimingRelay relay(PIN_RELAY, 0, 180000);   // Создаем реле времени (назначенный порт, задержка включения, задержка выключения).
018 
019 
020const char* ssid = "-------";
021const char* password = "----------";
022const char* mqtt_server = "192.168.10.1";
023const int   mqtt_port = 1883;
024const char* mqtt_user = "";
025const char* mqtt_password = "";
026 
027 
028//ESP8266WebServer server(80); //Server on port 80
029OneWire oneWire(ONE_WIRE_BUS);
030DallasTemperature sensors(&oneWire);
031OpenTherm ot(inPin, outPin);
032WiFiClient espClient;
033PubSubClient client(espClient);
034char buf[50];
035 
036float sp = 20.00, //set point
037      pv = 0, //current temperature
038      pv_last = 0, //prior temperature
039      ierr = 0, //integral error
040      dt = 0, //time between measurements
041      op = 0; //PID controller output
042unsigned long ts = 0, new_ts = 0; //timestamp
043bool enableCentralHeating = true;
044bool enableHotWater = true;
045bool enableCooling = false;
046 
047int timer_off = 180; //Задержка выключения реле насоса СО 3 мин.
048 
049 
050 
051void handleInterrupt() {
052  ot.handleInterrupt();
053}
054 
055float spdhw = 40;  // точка отсчета температуры горячей воды контура ГВС
056unsigned int hex56 = (spdhw * 256 * 16 / 16); // точка отсчета температуры горячей воды контура ГВС, но в (HEX)
057 
058float getTemp() {
059  return sensors.getTempCByIndex(0);
060}
061float getBoilerTemp() {
062  return ot.getBoilerTemperature();
063}
064float setDHWTemp() {
065  unsigned long request56 = ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::TdhwSet, hex56);
066  unsigned long respons56 = ot.sendRequest(request56);
067  uint16_t dataValue56 = respons56 & 0xFFFF;
068  float result56 = dataValue56 / 256;
069  return result56;
070}
071float getDHWTemp() {
072  unsigned long request26 = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Tdhw, 0);
073  unsigned long respons26 = ot.sendRequest(request26);
074  uint16_t dataValue26 = respons26 & 0xFFFF;
075  float result26 = dataValue26 / 256;
076  return result26;
077}
078float getOutsideTemp() {
079  unsigned long request27 = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Toutside, 0);
080  unsigned long respons27 = ot.sendRequest(request27);
081  uint16_t dataValue27 = respons27 & 0xFFFF;
082  if (dataValue27 > 32768) {
083    //negative
084    float result27 = -(65536 - dataValue27) / 256;
085    return result27;
086  } else {
087    //positive
088    float result27 = dataValue27 / 256;
089    return result27;
090  }
091}
092int getADC() {
093  delay(100);
094  int ar = analogRead(A0);
095  ar = map(ar, 26, 1023, 0, 100);
096  return ar;
097}
098unsigned long getCentralHeatingEnabled() {
099  unsigned long response = ot.setBoilerStatus(enableCentralHeating, enableHotWater, enableCooling);
100  OpenThermResponseStatus responseStatus = ot.getLastResponseStatus();
101  if (responseStatus = OpenThermResponseStatus::SUCCESS)
102    return ot.isCentralHeatingActive(response);
103}
104unsigned long getHotWaterEnabled() {
105  unsigned long response = ot.setBoilerStatus(enableCentralHeating, enableHotWater, enableCooling);
106  OpenThermResponseStatus responseStatus = ot.getLastResponseStatus();
107  if (responseStatus = OpenThermResponseStatus::SUCCESS)
108    return ot.isHotWaterActive(response);
109}
110unsigned long getFlameOn() {
111  unsigned long response = ot.setBoilerStatus(enableCentralHeating, enableHotWater, enableCooling);
112  OpenThermResponseStatus responseStatus = ot.getLastResponseStatus();
113  if (responseStatus = OpenThermResponseStatus::SUCCESS)
114    return ot.isFlameOn(response);
115}
116float pid(float sp, float pv, float pv_last, float& ierr, float dt) {
117  float Kc = 10.0; // K / %Heater
118  float tauI = 50.0; // sec
119  float tauD = 1.0;  // sec
120  // PID coefficients
121  float KP = Kc;
122  float KI = Kc / tauI;
123  float KD = Kc * tauD;
124  // upper and lower bounds on heater level
125  float ophi = 85;
126  float oplo = 30;
127  // calculate the error
128  float error = sp - pv;
129  // calculate the integral error
130  ierr = ierr + KI * error * dt;
131  // calculate the measurement derivative
132  float dpv = (pv - pv_last) / dt;
133  // calculate the PID output
134  float P = KP * error; //proportional contribution
135  float I = ierr; //integral contribution
136  float D = -KD * dpv; //derivative contribution
137  float op = P + I + D;
138  // implement anti-reset windup
139  if ((op < oplo) || (op > ophi)) {
140    I = I - KI * error * dt;
141    // clip output
142    op = max(oplo, min(ophi, op));
143  }
144  ierr = I;
145  Serial.println("Заданное значение температуры в помещкнии = " + String(sp) + " °C");
146  Serial.println("Текущее значение температуры в помещкнии = " + String(pv) + " °C");
147  Serial.println("Выхов ПИД регулятора = " + String(op));
148  Serial.println("Время между измерениями = " + String(dt) + "; ПИД коэффициенты: П = " + String(P) + "; И = " + String(I) + "; Д = " + String(D));
149  return op;
150}
151//===============================================================
152// Эта процедура выполняется при открытии IP-адреса в браузере
153//===============================================================
154 
155//==============================================================
156//                  SETUP
157//==============================================================
158  
159void setup_wifi() {
160  delay(10);
161  //Connect to Wi-Fi Network
162  Serial.println();
163  Serial.print("Connecting to ");
164  Serial.println(ssid);
165  WiFi.mode(WIFI_STA);
166  WiFi.begin(ssid, password); //Connect to your Wi-Fi router
167  // Wait for connection
168  while (WiFi.status() != WL_CONNECTED) {
169    delay(500);
170    Serial.print(".");
171  }
172  //If connection successful show IP address in serial monitor
173  Serial.println("");
174  Serial.println("WiFi connected");
175  Serial.println("IP address: ");
176  Serial.println(WiFi.localIP());
177}
178void setup(void) {
179  pinMode(BUILTIN_LED, OUTPUT);
180  digitalWrite(BUILTIN_LED, 0);
181  Serial.begin(115200);
182  setup_wifi();
183  pinMode (12, OUTPUT);
184  relay.autoReset = true;      // Автоматический запуск таймера задержки выключения.
185  if (MDNS.begin("thermostat")) {
186    Serial.println("MDNS responder started");
187  }
188  //Initialize Webserver
189   
190  //==============================================================
191  //                  Init DS18B20 sensor
192  //==============================================================
193  sensors.begin();
194  sensors.requestTemperatures();
195  sensors.setWaitForConversion(false); //switch to async mode
196  pv, pv_last = sensors.getTempCByIndex(0);
197  ts = millis();
198  //==============================================================
199  //                  Init OpenTherm Controller
200  //==============================================================
201  ot.begin(handleInterrupt);
202  //==============================================================
203  //                  Init MQTT Client
204  //==============================================================
205  client.setServer(mqtt_server, mqtt_port);
206  client.setCallback(callback);
207}
208void publish_temperature() {
209  Serial.println("MQTT, Current Room Temperature, °C = " + String(pv));
210  String(pv).toCharArray(buf, 50);
211  client.publish("pv", buf);
212}
213void publish_boilertemp() {
214  Serial.println("MQTT, CH Temperature, °C = " + String(getBoilerTemp()));
215  String(getBoilerTemp()).toCharArray(buf, 50);
216  client.publish("cht", buf);
217}
218void publish_dhwtemp() {
219  Serial.println("MQTT, DHW Temperature, °C = " + String(getDHWTemp()));
220  String(getDHWTemp()).toCharArray(buf, 50);
221  client.publish("dhwt", buf);
222}
223void publish_outtemp() {
224  Serial.println("MQTT, Outside Temperature, °C = " + String(getOutsideTemp()));
225  String(getOutsideTemp()).toCharArray(buf, 50);
226  client.publish("outt", buf);
227}
228void publish_statusCH() {
229  Serial.println("MQTT, Status Central Heating = " + String(getCentralHeatingEnabled()));
230  String(getCentralHeatingEnabled()).toCharArray(buf, 50);
231  client.publish("sch", buf);
232}
233void publish_ADC() {
234  Serial.println("MQTT, Relative Modulation Level, % = " + String(getADC()));
235  String(getADC()).toCharArray(buf, 50);
236  client.publish("adc", buf);
237}
238void publish_statusDWH() {
239  Serial.println("MQTT, Status Hot Water = " + String(getHotWaterEnabled()));
240  String(getHotWaterEnabled()).toCharArray(buf, 50);
241  client.publish("sdwh", buf);
242}
243void publish_statusFlame() {
244  Serial.println("MQTT, Status Central Heating = " + String(getFlameOn()));
245  String(getFlameOn()).toCharArray(buf, 50);
246  client.publish("sfl", buf);
247}
248void publish_setBoilerTemperature() {
249  Serial.println("MQTT, PID Controller Output, % = Set Point CH Temperature, °C = " + String(op));
250  String(op).toCharArray(buf, 50);
251  client.publish("op", buf);
252}
253void callback(char* topic, byte* payload, unsigned int length) {
254  char buffer[length + 1];
255  memcpy(buffer, payload, length);
256  buffer[length] = '\0';
257  float a = atof((char*)buffer);
258  if (a <= 29) {
259    sp = a;
260  } else {
261    spdhw = a;
262  }
263//  Serial.println("MQTT, Setpoint Room Temperature = " + str);
264//  sp = str.toFloat();
265}
266void reconnect() {
267  while (!client.connected()) {
268    Serial.print("Attempting MQTT connection...");
269    // Attempt to connect
270    if (client.connect("ESP8266Client", mqtt_user, mqtt_password)) {
271      Serial.println("connected");
272      // Once connected, publish an announcement...
273      publish_temperature();
274      publish_boilertemp();
275      publish_dhwtemp();
276      publish_outtemp();
277      publish_ADC();
278      publish_statusCH();
279      publish_statusDWH();
280      publish_statusFlame();
281      publish_setBoilerTemperature();
282      // ... and resubscribe
283      client.subscribe("sp");
284      client.subscribe("spdhw");
285    } else {
286      Serial.print("failed, rc =");
287      Serial.print(client.state());
288      Serial.println(" try again in 5 seconds");
289      // Wait 5 seconds before retrying
290      delay(5000);
291    }
292  }
293}
294//==============================================================
295//                     LOOP
296//==============================================================
297void loop(void) {
298  new_ts = millis();
299  if (new_ts - ts > 1000) {
300    unsigned long response = ot.setBoilerStatus(enableCentralHeating, enableHotWater, enableCooling);
301    OpenThermResponseStatus responseStatus = ot.getLastResponseStatus();
302    if (responseStatus = OpenThermResponseStatus::SUCCESS) {
303      //Serial.println("Error: Invalid boiler response " + String(response, HEX));
304      Serial.println("Текущий статус системы отопления: " + String(ot.isCentralHeatingActive(response) ? "on" : "off"));
305      Serial.println("Текущий статус горячей воды: " + String(ot.isHotWaterActive(response) ? "on" : "off"));
306      Serial.println("Текущий статус горелки: " + String(ot.isFlameOn(response) ? "on" : "off"));
307      Serial.println("Индикация состояния неисправности: " + String(ot.isFault(response) ? "fault" : "no fault"));
308      Serial.println("Диагностическая индикация: " + String(ot.isDiagnostic(response) ? "diagnostics" : "no diagnostics"));
309    }
310    if (responseStatus == OpenThermResponseStatus::NONE) {
311      Serial.println("Error: OpenTherm is not initialized");
312    }
313    else if (responseStatus == OpenThermResponseStatus::INVALID) {
314      Serial.println("Error: Invalid response " + String(response, HEX));
315    }
316    else if (responseStatus == OpenThermResponseStatus::TIMEOUT) {
317      Serial.println("Error: Response timeout");
318    }
319    pv = sensors.getTempCByIndex(0);
320    dt = (new_ts - ts) / 1000.0;
321    ts = new_ts;
322    if (responseStatus == OpenThermResponseStatus::SUCCESS) {
323      op = pid(sp, pv, pv_last, ierr, dt);
324      //Set CH Temperature
325      ot.setBoilerTemperature(op);
326      hex56 = (spdhw * 256 * 16 / 16);
327      unsigned int setDHWTemp(hex56);
328      pv_last = pv;
329      sensors.requestTemperatures(); //async temperature request
330    }
331    // обработка индикации состояния ГВС.
332  bool status_dhw = getHotWaterEnabled();
333   // обработка индикации состояния горелки.
334  bool status_flame = getFlameOn();
335  //обработка индикации и реле насоса СО.
336 if (status_flame == 1 && status_dhw == 0) {    // Если состояния горелки = 1 и статус состояния ГВС = 0 включаем реле.
337    relay.on();
338 } else {
339    relay.off();
340  }
341  relay.loop();                                  // Обрабатываем (обязательно).
342 //конец обработки индикации и реле насоса СО.
343   
344    //Print Temperature
345 
346    Serial.println("Текущая температура контура СО = " + String(getBoilerTemp()) + " °C");
347    Serial.println("Текущая температура контура ГВС = " + String(getDHWTemp()) + " °C");
348    Serial.println("Температура на улице = " + String(getOutsideTemp()) + " °C");
349    Serial.println("Текущий уровень модуляции горелки  = " + String (getADC()) + " %");
350    Serial.println("ADC = " + String (analogRead(A0)));
351 
352    publish_temperature();
353    publish_boilertemp();
354    publish_dhwtemp();
355    publish_outtemp();
356    publish_ADC();
357    publish_statusCH();
358    publish_statusDWH();
359    publish_statusFlame();
360    publish_setBoilerTemperature();
361  }
362  //MQTT Loop
363  if (!client.connected()) {
364    reconnect();
365  }
366  client.loop();
367//  server.handleClient(); //handle http requests
368}

Скомпилировал код выше.

Увы температура горяей воды не передается.

Посмотрите где может быть ошибка?

tsv_33
Offline
Зарегистрирован: 11.04.2019

ОК! Посмотрю. У меня сейчас проблема после обновления менеджера плат до версии 2.5.2. esp-шка ругается в мониторе "ISR not in IRAM!", ребутится постоянно. До обновления была версия 2.5.0. Копать дальше? Или откатиться и забыть? После декодирования стека пишет:

Decoding stack results
0x401001b8: millis() at C:\Users\Stas\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.5.2\cores\esp8266\core_esp8266_wiring.cpp line 186
0x40201038: handleInterrupt() at C:\Users\Stas\Documents\Arduino\OpenTherm_thermostat_v6.01_WEB/OpenTherm_thermostat_v6.01_WEB.ino line 213
0x401004be: __attachInterrupt(uint8_t, voidFuncPtr, int) at C:\Users\Stas\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.5.2\cores\esp8266\core_esp8266_wiring_digital.cpp line 206
0x4020c8f0: OpenTherm::begin(void (*)(), void (*)(unsigned long, OpenThermResponseStatus)) at C:\Users\Stas\Documents\Arduino\libraries\opentherm_library-master\src\OpenTherm.cpp line 28
0x4020c91c: OpenTherm::begin(void (*)()) at C:\Users\Stas\Documents\Arduino\libraries\opentherm_library-master\src\OpenTherm.cpp line 36
0x40203922: setup() at C:\Users\Stas\Documents\Arduino\OpenTherm_thermostat_v6.01_WEB/OpenTherm_thermostat_v6.01_WEB.ino line 642
0x40201018: saveConfigCallback() at C:\Users\Stas\Documents\Arduino\OpenTherm_thermostat_v6.01_WEB/OpenTherm_thermostat_v6.01_WEB.ino line 430
0x402011e0: std::_Function_handler ::_M_invoke(const std::_Any_data &) at c:\users\stas\appdata\local\arduino15\packages\esp8266\tools\xtensa-lx106-elf-gcc\2.5.0-3-20ed2b9\xtensa-lx106-elf\include\c++\4.8.2/functional line 2069
0x402103c4: loop_wrapper() at C:\Users\Stas\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.5.2\cores\esp8266\core_esp8266_main.cpp line 122
 
Ладно, если бы на ошибке в коде..., а то и на библиотеки жалуется...
VOVA_iS
Offline
Зарегистрирован: 09.07.2019

tsv_33 пишет:

ОК! Посмотрю. У меня сейчас проблема после обновления менеджера плат до версии 2.5.2. esp-шка ругается в мониторе "ISR not in IRAM!", ребутится постоянно. До обновления была версия 2.5.0. Копать дальше? Или откатиться и забыть? После декодирования стека пишет:

Откатись и забудь.

У меня тоже после обновления много каких проектов перестало собираться.

Откатился и все норм

tsv_33
Offline
Зарегистрирован: 11.04.2019

Это понятно, но всё же не случайно это, что то с прерываниями не то... Где то надо добавить ICACHE_RAM_ATTR перед функцией ISR, что бы правильно было.

tsv_33
Offline
Зарегистрирован: 11.04.2019

Хотя ругани в сети по этому поводу полно...:-)

tsv_33
Offline
Зарегистрирован: 11.04.2019

Так будет проще, выкладываю один из рабочих кодов без веб страницы, сервер из кода сами удалите и принты в монитор тоже, не нужны они для работы с MQTT.

001#include <ESP8266WiFi.h>
002#include <PubSubClient.h>
003#include <WiFiClient.h>
004#include <ESP8266WebServer.h>
005#include <ESP8266mDNS.h>
006#include <OneWire.h>
007#include <DallasTemperature.h>
008#include <OpenTherm.h>
009#include <TimingRelay.h>
010 
011//Входные и выходные контакты OpenTherm, подключены к 4 и 5 контактам платы
012const int inPin = 4;            //D2
013const int outPin = 5;           //D1
014 
015#define ONE_WIRE_BUS 14         // D5 Data wire is connected to 14 pin on the OpenTherm Shield
016#define BUILTIN_LED 2           // D4 Встроенный LED
017#define PIN_RELAY 12            // D6 На внешнее реле управления насосом СО и внешнюю индикацию работы насоса СО
018#define EXTERNAL_LED 13         // D7 На внешний LED индикации работы ГВС
019#define EXTERNAL_LED_1 15       // D8 На внешний LED индикации работы горелки
020#define EXTERNAL_LED_2 0        // D3 На внешний LED индикации работы авария
021 
022//#define DAY(x)            ((x) * 10000ul) // тест
023#define DAY(x)            ((x) * 24 * MIN(60))
024#define MIN(x)            ((x) * 10000ul)
025#define TIME1             DAY(7)
026#define TIME2             MIN(60)
027#define VAL1              50.
028#define VAL2              60.
029 
030const char* ssid = "********";
031const char* password = "*********";
032const char* mqtt_server = "***********";
033const int   mqtt_port = 12345;
034const char* mqtt_user = "***********";
035const char* mqtt_password = "*************";
036 
037ESP8266WebServer server(80);                     // Сервер на порту 80
038OneWire oneWire(ONE_WIRE_BUS);
039DallasTemperature sensors(&oneWire);
040OpenTherm ot(inPin, outPin);
041WiFiClient espClient;
042PubSubClient client(espClient);
043char buf[50];
044 
045float sp = 20,                                   // точка отсчета комнатной температуры
046      pv = 0,                                    // текущая температура в комнате
047      pv_last = 0,                               // предыдущая температура
048      ierr = 0,                                  // интегральная погрешность
049      dt = 0,                                    // время между измерениями
050      op = 0,                                    // выход ПИД контроллера
051      spdhw = VAL1;                              // точка отсчета температуры горячей воды контура ГВС
052unsigned long ts = 0, new_ts = 0;                // отметки времени
053unsigned int hex56 = (spdhw * 256 * 16 / 16);    // точка отсчета температуры горячей воды контура ГВС, но в (HEX)
054unsigned int data126 = 0x013F;                   // тип и версия термостата (HEX)
055bool enableCentralHeating = true;
056bool enableHotWater = true;
057bool enableCooling = false;
058 
059TimingRelay relay(PIN_RELAY, 0, 180000);         // Создаем реле времени (назначенный порт, задержка включения, задержка выключения).
060 
061//===============================================================
062//               Обрабатываем внешние прерывания
063//===============================================================
064void handleInterrupt() {
065  ot.handleInterrupt();
066}
067 
068float getTemp() {
069  return sensors.getTempCByIndex(0);
070}
071float getBoilerTemp() {
072  return ot.getBoilerTemperature();
073}
074float getDHWTemp() {
075  unsigned long request26 = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Tdhw, 0);
076  unsigned long respons26 = ot.sendRequest(request26);
077  uint16_t dataValue26 = respons26 & 0xFFFF;
078  float result26 = dataValue26 / 256;
079  return result26;
080}
081float getOutsideTemp() {
082  unsigned long request27 = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Toutside, 0);
083  unsigned long respons27 = ot.sendRequest(request27);
084  uint16_t dataValue27 = respons27 & 0xFFFF;
085  if (dataValue27 > 32768) {
086    //negative
087    float result27 = -(65536 - dataValue27) / 256;
088    return result27;
089  } else {
090    //positive
091    float result27 = dataValue27 / 256;
092    return result27;
093  }
094}
095int getADC() {
096  delay(100);
097  int ar = analogRead(A0);
098  ar = map(ar, 0, 1023, 0, 100);
099  return ar;
100}
101unsigned long getFault() {
102  unsigned long response = ot.setBoilerStatus(enableCentralHeating, enableHotWater, enableCooling);
103  OpenThermResponseStatus responseStatus = ot.getLastResponseStatus();
104  if (responseStatus = OpenThermResponseStatus::SUCCESS)
105    return ot.isFault(response);
106}
107unsigned long getCentralHeatingEnabled() {
108  unsigned long response = ot.setBoilerStatus(enableCentralHeating, enableHotWater, enableCooling);
109  OpenThermResponseStatus responseStatus = ot.getLastResponseStatus();
110  if (responseStatus = OpenThermResponseStatus::SUCCESS)
111    return ot.isCentralHeatingEnabled(response);
112}
113unsigned long getHotWaterEnabled() {
114  unsigned long response = ot.setBoilerStatus(enableCentralHeating, enableHotWater, enableCooling);
115  OpenThermResponseStatus responseStatus = ot.getLastResponseStatus();
116  if (responseStatus = OpenThermResponseStatus::SUCCESS)
117    return ot.isHotWaterEnabled(response);
118}
119unsigned long getFlameOn() {
120  unsigned long response = ot.setBoilerStatus(enableCentralHeating, enableHotWater, enableCooling);
121  OpenThermResponseStatus responseStatus = ot.getLastResponseStatus();
122  if (responseStatus = OpenThermResponseStatus::SUCCESS)
123    return ot.isFlameOn(response);
124}
125unsigned int getSlaveVersion_type() {
126  unsigned long request127 = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SlaveVersion, 0);
127  unsigned long respons127 = ot.sendRequest(request127);
128  uint16_t dataValue127 = respons127 & 0xFFFF;
129  unsigned result127 = dataValue127 / 256;
130  return result127;
131}
132unsigned int getSlaveVersion_num() {
133  unsigned long request127 = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SlaveVersion, 0);
134  unsigned long respons127 = ot.sendRequest(request127);
135  uint8_t dataValue127_ = respons127 & 0xFF;
136  unsigned result127_ = dataValue127_;
137  return result127_;
138}
139unsigned int setMasterVersion_type() {
140  unsigned long request126 = ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MasterVersion, data126);
141  unsigned long respons126 = ot.sendRequest(request126);
142  uint16_t dataValue126 = respons126 & 0xFFFF;
143  unsigned result126 = dataValue126 / 256;
144  return result126;
145}
146unsigned int setMasterVersion_num() {
147  unsigned long request126 = ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MasterVersion, data126);
148  unsigned long respons126 = ot.sendRequest(request126);
149  uint8_t dataValue126_ = respons126 & 0xFF;
150  unsigned result126_ = dataValue126_;
151  return result126_;
152}
153float setDHWTemp() {
154  unsigned long request56 = ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::TdhwSet, hex56);
155  unsigned long respons56 = ot.sendRequest(request56);
156  uint16_t dataValue56 = respons56 & 0xFFFF;
157  float result56 = dataValue56 / 256;
158  return result56;
159}
160unsigned int getFaultCode() {
161  unsigned long request5 = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::ASFflags, 0);
162  unsigned long respons5 = ot.sendRequest(request5);
163  uint8_t dataValue5 = respons5 & 0xFF;
164  unsigned result5 = dataValue5;
165  return result5;
166}
167//===============================================================
168//              Вычисляем коэффициенты ПИД регулятора
169//===============================================================
170float pid(float sp, float pv, float pv_last, float& ierr, float dt) {
171  float Kc = 10.0; // K / %Heater
172  float tauI = 50.0; // sec
173  float tauD = 1.0;  // sec
174  // ПИД коэффициенты
175  float KP = Kc;
176  float KI = Kc / tauI;
177  float KD = Kc * tauD;
178  // верхняя и нижняя границы уровня нагрева
179  float ophi = 100;
180  float oplo = 0;
181  // вычислить ошибку
182  float error = sp - pv;
183  // calculate the integral error
184  ierr = ierr + KI * error * dt;
185  // вычислить производную измерения
186  float dpv = (pv - pv_last) / dt;
187  // рассчитать выход ПИД регулятора
188  float P = KP * error;                      // пропорциональная составляющая
189  float I = ierr;                            // интегральная составляющая
190  float D = -KD * dpv;                       // дифференциальная составляющая
191  float op = P + I + D;
192  // защита от сброса
193  if ((op < oplo) || (op > ophi)) {
194    I = I - KI * error * dt;
195    // выход регулятора, он же уставка для ID-1 (температура теплоносителя контура СО котла)
196    op = max(oplo, min(ophi, op));
197  }
198  ierr = I;
199  Serial.println("Заданное значение температуры в помещении = " + String(sp) + " °C");
200  Serial.println("Текущее значение температуры в помещении = " + String(pv) + " °C");
201  Serial.println("Выхов ПИД регулятора = " + String(op));
202  Serial.println("Время между измерениями = " + String(dt) + "; ПИД коэффициенты: П = " + String(P) + "; И = " + String(I) + "; Д = " + String(D));
203  return op;
204}
205//===============================================================
206// Эта процедура выполняется при открытии IP-адреса в браузере
207//===============================================================
208void handleRoot() {
209  digitalWrite(BUILTIN_LED, 1);
210  if (server.method() == HTTP_POST) {
211    for (uint8_t i = 0; i < server.args(); i++) {
212      if (server.argName(i) == "sp") {
213        sp = server.arg(i).toFloat();
214      }
215      if (server.argName(i) == "spdhw") {
216        spdhw = server.arg(i).toFloat();
217      }
218    }
219  }
220  String page = FPSTR(HTTP_HTML);
221  page.replace("{0}", String(getTemp()));
222  page.replace("{1}", String((float)sp));
223  page.replace("{2}", String(getBoilerTemp()));
224  page.replace("{3}", String(getDHWTemp()));
225  page.replace("{4}", String(getOutsideTemp()));
226  page.replace("{5}", String(getCentralHeatingEnabled() ? "on" : "off"));
227  page.replace("{6}", String(getHotWaterEnabled() ? "on" : "off"));
228  page.replace("{7}", String(getFlameOn() ? "on" : "off"));
229  page.replace("{8}", String(getADC()));
230  page.replace("{9}", String((float)spdhw));
231  server.send(200, "text/html", page);
232  digitalWrite(BUILTIN_LED, 0);
233}
234void handleGetTemp() {
235  digitalWrite(BUILTIN_LED, 1);
236  server.send(200, "text/plain", String(getTemp()));
237  digitalWrite(BUILTIN_LED, 0);
238}
239void handleGetBoilerTemp() {
240  digitalWrite(BUILTIN_LED, 1);
241  server.send(200, "text/plain", String(getBoilerTemp()));
242  digitalWrite(BUILTIN_LED, 0);
243}
244void handleGetDHWTemp() {
245  digitalWrite(BUILTIN_LED, 1);
246  server.send(200, "text/plain", String(getDHWTemp()));
247  digitalWrite(BUILTIN_LED, 0);
248}
249void handleGetOutsideTemp() {
250  digitalWrite(BUILTIN_LED, 1);
251  server.send(200, "text/plain", String(getOutsideTemp()));
252  digitalWrite(BUILTIN_LED, 0);
253}
254void handleGetCentralHeatingEnabled() {
255  digitalWrite(BUILTIN_LED, 1);
256  server.send(200, "text/plain", String(getCentralHeatingEnabled() ? "on" : "off"));
257  digitalWrite(BUILTIN_LED, 0);
258}
259void handleGetHotWaterEnabled() {
260  digitalWrite(BUILTIN_LED, 1);
261  server.send(200, "text/plain", String(getHotWaterEnabled() ? "on" : "off"));
262  digitalWrite(BUILTIN_LED, 0);
263}
264void handleGetFlameOn() {
265  digitalWrite(BUILTIN_LED, 1);
266  server.send(200, "text/plain", String(getFlameOn() ? "on" : "off"));
267  digitalWrite(BUILTIN_LED, 0);
268}
269void handleADC() {
270  digitalWrite(BUILTIN_LED, 1);
271  server.send(200, "text/plain", String(getADC()));
272  digitalWrite(BUILTIN_LED, 0);
273}
274//==============================================================
275//                  Подключаемся к сети WiFi
276//==============================================================
277void setup_wifi() {
278  delay(10);
279  // подключение к сети Wi-Fi
280  Serial.println();
281  Serial.print("Connecting to ");
282  Serial.println(ssid);
283  WiFi.mode(WIFI_STA);
284  WiFi.begin(ssid, password);                // Подключение к маршрутизатору Wi-Fi
285  // дождаться соединения
286  while (WiFi.status() != WL_CONNECTED) {
287    delay(500);
288    Serial.print(".");
289  }
290  // если соединение успешно, показывает IP-адрес в мониторе
291  Serial.println("");
292  Serial.println("WiFi connected");
293  Serial.println("IP address: ");
294  Serial.println(WiFi.localIP());
295}
296//==============================================================
297//                  Функция SETUP
298//==============================================================
299void setup() {
300  pinMode(BUILTIN_LED, OUTPUT);
301  pinMode(EXTERNAL_LED, OUTPUT);
302  pinMode(EXTERNAL_LED_1, OUTPUT);
303  pinMode(EXTERNAL_LED_2, OUTPUT);
304  pinMode(PIN_RELAY, OUTPUT);
305  digitalWrite(BUILTIN_LED, 0);
306  digitalWrite(EXTERNAL_LED, 0);
307  digitalWrite(EXTERNAL_LED_1, 0);
308  digitalWrite(EXTERNAL_LED_2, 0);
309  relay.autoReset = true;                             // Автоматический запуск таймера задержки выключения.
310  Serial.begin(115200);
311  setup_wifi();
312  if (MDNS.begin("thermostat")) {
313    Serial.println("MDNS responder started");
314  }
315  // инициализация web сервера
316  server.on("/", handleRoot);                         // Ответ сервера на запрос главной страницы
317  server.on("/temp", handleGetTemp);                  // Ответ сервера на запрос температуры в помещении
318  server.on("/boilertemp", handleGetBoilerTemp);      // Ответ сервера на запрос температуры подачи котла
319  server.on("/dhwttemp", handleGetDHWTemp);           // Ответ сервера на запрос температуры горячей воды
320  server.on("/outsidetemp", handleGetOutsideTemp);    // Ответ сервера на запрос уличной температуры
321  server.on("/chon", handleGetCentralHeatingEnabled); // Ответ сервера на запрос состояния СО
322  server.on("/hwon", handleGetHotWaterEnabled);       // Ответ сервера на запрос состояния ГВС
323  server.on("/flameon", handleGetFlameOn);            // Ответ сервера на запрос состояния горелки
324  server.on("/getadc", handleADC);                    // Ответ сервера на запрос уровня модуляции горелки
325  server.begin();
326  Serial.println("HTTP server started");
327 
328  //==============================================================
329  //          Инициализация датчика температуры DS18B20
330  //==============================================================
331  sensors.begin();
332  sensors.requestTemperatures();
333  sensors.setWaitForConversion(false);                // Переключиться в асинхронный режим
334  pv, pv_last = sensors.getTempCByIndex(0);
335  ts = millis();
336  //==============================================================
337  //          Инициализация OpenTherm
338  //==============================================================
339  ot.begin(handleInterrupt);
340  //==============================================================
341  //          Инициализация MQTT клиента
342  //==============================================================
343  client.setServer(mqtt_server, mqtt_port);   // подключаемся к MQTT
344  client.setCallback(callback);               // функция получения топиков с брокера
345}
346void publish_temperature() {
347  Serial.println("MQTT, Current Room Temperature, °C = " + String(pv));
348  String(pv).toCharArray(buf, 50);
349  client.publish("pv", buf);
350}
351void publish_boilertemp() {
352  Serial.println("MQTT, CH Temperature, °C = " + String(getBoilerTemp()));
353  String(getBoilerTemp()).toCharArray(buf, 50);
354  client.publish("cht", buf);
355}
356void publish_dhwtemp() {
357  Serial.println("MQTT, DHW Temperature, °C = " + String(getDHWTemp()));
358  String(getDHWTemp()).toCharArray(buf, 50);
359  client.publish("dhwt", buf);
360}
361void publish_outtemp() {
362  Serial.println("MQTT, Outside Temperature, °C = " + String(getOutsideTemp()));
363  String(getOutsideTemp()).toCharArray(buf, 50);
364  client.publish("outt", buf);
365}
366void publish_statusCH() {
367  Serial.println("MQTT, Status Central Heating = " + String(getCentralHeatingEnabled()));
368  String(getCentralHeatingEnabled()).toCharArray(buf, 50);
369  client.publish("sch", buf);
370}
371void publish_ADC() {
372  Serial.println("MQTT, Relative Modulation Level, % = " + String(getADC()));
373  String(getADC()).toCharArray(buf, 50);
374  client.publish("adc", buf);
375}
376void publish_statusDWH() {
377  Serial.println("MQTT, Status Hot Water = " + String(getHotWaterEnabled()));
378  String(getHotWaterEnabled()).toCharArray(buf, 50);
379  client.publish("sdwh", buf);
380}
381void publish_statusFlame() {
382  Serial.println("MQTT, Status Central Heating = " + String(getFlameOn()));
383  String(getFlameOn()).toCharArray(buf, 50);
384  client.publish("sfl", buf);
385}
386void publish_setBoilerTemperature() {
387  Serial.println("MQTT, PID Controller Output, % = Setpoint CH Temperature, °C = " + String(op));
388  String(op).toCharArray(buf, 50);
389  client.publish("op", buf);
390}
391// функция обратного вызова, чтение топиков
392void callback(char* topic, byte* payload, unsigned int length) {
393  char buffer[length + 1];
394  memcpy(buffer, payload, length);
395  buffer[length] = '\0';
396  float a = atof((char*)buffer);
397  if (a <= 29) {
398    sp = a;
399  } else {
400    spdhw = a;
401  }
402  Serial.println("MQTT,topic = " + String(a));
403}
404void reconnect() {
405  while (!client.connected()) {
406    Serial.print("Attempting MQTT connection...");
407    // Attempt to connect
408    if (client.connect("ESP8266Client", mqtt_user, mqtt_password)) {
409      Serial.println("connected");
410      // после подключения публикуем объявление...
411      publish_temperature();
412      publish_boilertemp();
413      publish_dhwtemp();
414      publish_outtemp();
415      publish_ADC();
416      publish_statusCH();
417      publish_statusDWH();
418      publish_statusFlame();
419      publish_setBoilerTemperature();
420      // ... и перезаписываем
421      client.subscribe("sp");
422      client.subscribe("spdhw");
423    } else {
424      Serial.print("failed, rc =");
425      Serial.print(client.state());
426      Serial.println(" try again in 5 seconds");
427      // Подождать 5 сек. перед повторной попыткой
428      delay(5000);
429    }
430  }
431}
432//==============================================================
433//                     Функция LOOP
434//==============================================================
435void loop() {
436  new_ts = millis();
437  if (new_ts - ts > 1000) {
438    unsigned long response = ot.setBoilerStatus(enableCentralHeating, enableHotWater, enableCooling);
439    OpenThermResponseStatus responseStatus = ot.getLastResponseStatus();
440    if (responseStatus = OpenThermResponseStatus::SUCCESS) {
441      //Serial.println("Error: Invalid boiler response " + String(response, HEX));
442    }
443    if (responseStatus == OpenThermResponseStatus::NONE) {
444      Serial.println("Error: OpenTherm is not initialized");
445    }
446    else if (responseStatus == OpenThermResponseStatus::INVALID) {
447      Serial.println("Error: Invalid response " + String(response, HEX));
448    }
449    else if (responseStatus == OpenThermResponseStatus::TIMEOUT) {
450      Serial.println("Error: Response timeout");
451    }
452    pv = sensors.getTempCByIndex(0);
453    dt = (new_ts - ts) / 1000.0;
454    ts = new_ts;
455    if (responseStatus == OpenThermResponseStatus::SUCCESS) {
456      op = pid(sp, pv, pv_last, ierr, dt);
457      // Заданная температура СО
458      ot.setBoilerTemperature(op);
459      // Заданная температура ГВС
460      hex56 = (spdhw * 256 * 16 / 16);
461      unsigned int setDHWTemp(hex56);
462      pv_last = pv;
463      sensors.requestTemperatures();                     //Асинхронный запрос температуры
464    }
465    // Записать ID-2; мастер-код MemberID
466    unsigned int data = 0x0004;
467    unsigned long request = ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MConfigMMemberIDcode, data);
468    ot.sendRequest(request);  
469   // unsigned long request5 = ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::ASFflags, 0);
470   // unsigned long respons5 = ot.sendRequest(request5);
471   // uint8_t dataValue5 = respons5 & 0xFF;
472   // unsigned result5 = dataValue5;
473    // Выводим в монитор
474    Serial.println("Текущий статус системы отопления: " + String(ot.isCentralHeatingEnabled(response) ? "on" : "off"));
475    Serial.println("Текущий статус горячей воды: " + String(ot.isHotWaterEnabled(response) ? "on" : "off"));
476    Serial.println("Текущий статус горелки: " + String(ot.isFlameOn(response) ? "on" : "off"));
477    Serial.println("Индикация состояния неисправности: " + String(ot.isFault(response) ? "fault" : "no fault"));
478    Serial.println("Диагностическая индикация: " + String(ot.isDiagnostic(response) ? "diagnostics" : "no diagnostics"));
479    Serial.println("Текущая температура контура СО = " + String(getBoilerTemp()) + " °C");
480    Serial.println("Текущая температура контура ГВС = " + String(getDHWTemp()) + " °C");
481    Serial.println("Температура на улице = " + String(getOutsideTemp()) + " °C");
482    Serial.println("Текуший уровень модуляции горелки  = " + String (getADC()) + " %");
483    Serial.println("ADC = " + String (analogRead(A0)));
484    Serial.println("Уставка температуры ГВС = " + String(setDHWTemp()) + " °C");
485    Serial.println("Тип и версия термостата: тип " + String(setMasterVersion_type()) + ", версия " + String(setMasterVersion_num()));
486    Serial.println("Тип и версия котла: тип " + String(getSlaveVersion_type()) + ", версия " + String(getSlaveVersion_num()));
487  //=================================================================================================
488  //    Индикация и реле насоса СО c откл. с задержкой 3 мин., индикация работы ГВС, горелки
489  //=================================================================================================
490  int status_error = getFault();                          // индикация состояния авария.
491  if (status_error == 1) {
492    Serial.println("Код неисправности: E " + String(getFaultCode()));
493    digitalWrite(EXTERNAL_LED_2, 1);
494  } else {
495    digitalWrite(EXTERNAL_LED_2, 0);
496  }
497  int status_dhw = getHotWaterEnabled();                  // индикация состояния ГВС.
498  if (status_dhw == 1) {
499    digitalWrite(EXTERNAL_LED, 1);
500  } else {
501    digitalWrite(EXTERNAL_LED, 0);
502  }
503  int status_flame = getFlameOn();                        // индикация состояния горелки.
504  if (status_flame == 1) {
505    digitalWrite(EXTERNAL_LED_1, 1);
506  } else {
507    digitalWrite(EXTERNAL_LED_1, 0);
508  }
509  if (status_flame == 1 && status_dhw == 0) {             // Если состояния горелки = 1 и статус состояния ГВС = 0 включаем реле.
510    relay.on();
511  } else {
512    relay.off();
513  }
514  relay.loop();                                           // Обрабатываем (обязательно).
515    publish_temperature();
516    publish_boilertemp();
517    publish_dhwtemp();
518    publish_outtemp();
519    publish_ADC();
520    publish_statusCH();
521    publish_statusDWH();
522    publish_statusFlame();
523    publish_setBoilerTemperature();
524    //publish_setDHWTemperature();
525  }
526  // MQTT Loop
527  if (!client.connected()) {
528    reconnect();
529  }
530  client.loop();
531  server.handleClient();     // обработка http-запросов
532  //=================================================================================================
533  //             Активации функции Антилегионелла ГВС 65°C на 1 час каждые 7 дней по кругу.
534  //=================================================================================================
535  static bool state;
536  static uint32_t old_millis = millis();
537  if (state == false) {
538    if (millis() - old_millis >= TIME1) {
539      old_millis = millis();
540      spdhw = VAL1;
541      state = true;
542    }
543  } else if (millis() - old_millis >= TIME2) {
544    old_millis = millis();
545    spdhw = VAL2;
546    state = false;
547  }
548}
tsv_33
Offline
Зарегистрирован: 11.04.2019

P.S.

Так и есть, атрибут ICACHE_RAM_ATTR теперь обязателен в коде, функциях, вызываемых по прерываниям. Проблема работы с 2.5.2 разрешилась.

VOVA_iS
Offline
Зарегистрирован: 09.07.2019

Проблему с помпой решил немного по другому

в секцию loop добавил 

1if (pv >= sp) {
2   enableCentralHeating = false;
3   } else {
4   enableCentralHeating = true;  }

После добавления этого кода котёл по достижению заданной температуры выключает режим отопления и соответсвенно насос, через заданное время в настройке котла

Проблема с горячей водой так и осталось. Регулирование нету

даже в логах пишется что заданная температура 0

 

но если изменить код вот так

1float setDHWTemp() {
2 unsigned long request56 = ot.buildRequest(OpenThermRequestType::WRITE_ACK, OpenThermMessageID::TdhwSet, hex56);
3 unsigned long respons56 = ot.sendRequest(request56);
4 uint16_t dataValue56 = respons56 & 0xFFFF;
5 float result56 = dataValue56 / 256;
6 return result56;

То заданная температура отображается(температура гвс установлена до подключение платы), но не устанавливается

tsv_33
Offline
Зарегистрирован: 11.04.2019

VOVA_iS, с помпой понятно, а я то начал городить огород с доп. реле, простенько, сам бы не догадался...

Пишет 0, но только когда котёл не подключен к термостату, когда же котёл представится термостату и тот ответит ему, то всё ОК.

P.S.

Вообще библиотека ОТ неплохая, но попытка скрестить ужа и ежа провалилась, я про web и mqtt в одном флаконе. Жуткие тормоза при совместной работе двух библиотек, причём только web. Приглядываюсь к такому решению https://www.youtube.com/watch?v=QQ7apgKBgNM

VOVA_iS
Offline
Зарегистрирован: 09.07.2019

tsv_33 пишет:

VOVA_iS, с помпой понятно, а я то начал городить огород с доп. реле, простенько, сам бы не догадался...

Пишет 0, но только когда котёл не подключен к термостату, когда же котёл представится термостату и тот ответит ему, то всё ОК.

Котёл с термостатом представились друг другу.

А проблема что все данные с котла приходят кроме установленной температуры ГВС 

да не нужен этот web

mqtt более чем достаточно

VOVA_iS
Offline
Зарегистрирован: 09.07.2019

tsv_33 пишет:

VOVA_iS, с помпой понятно, а я то начал городить огород с доп. реле, простенько, сам бы не догадался...

Пишет 0, но только когда котёл не подключен к термостату, когда же котёл представится термостату и тот ответит ему, то всё ОК.

Котёл с термостатом представились друг другу.

А проблема что все данные с котла приходят кроме установленной температуры ГВС 

да не нужен этот web

mqtt более чем достаточно

tsv_33
Offline
Зарегистрирован: 11.04.2019

А в mqtt брокере переменная spdhw видна?

VOVA_iS
Offline
Зарегистрирован: 09.07.2019

но если изменить код вот так

1 float setDHWTemp() {
2  unsigned long request56 = ot.buildRequest(OpenThermRequestType::WRITE_ACK, OpenThermMessageID::TdhwSet, hex56);
3  unsigned long respons56 = ot.sendRequest(request56);
4  uint16_t dataValue56 = respons56 & 0xFFFF;
5  float result56 = dataValue56 / 256;
6

 return result56;

 

Тогда видна

tsv_33
Offline
Зарегистрирован: 11.04.2019

В протоколе ОТ ID Msg 56 имеет | R  W | DHW setpoint | f8.8 | 0..127 | Domestic hot water temperature setpoint (°C) 

Можно прочитать, что записалось и вывести в монитор, отдельно я этого не делал, просто проверял ранее и всё было ОК. Точно не помню, как переменную обзывал...

1float getDHWTemp() {
2  unsigned long request56 = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::TdhwSet, hex56);
3  unsigned long respons56 = ot.sendRequest(request56);
4  uint16_t dataValue56 = respons56 & 0xFFFF;
5  float result56 = dataValue56 / 256;
6  return result56;
7}

 

tsv_33
Offline
Зарегистрирован: 11.04.2019

VOVA_iS, а как определили, что в котёл не ушла уставка?

tsv_33
Offline
Зарегистрирован: 11.04.2019

 

1if (pv >= sp) {
2   enableCentralHeating = false;
3   } else {
4   enableCentralHeating = true;  }

С этим кодом работы помпы ОТ отключается...т.е. интерфейс активируется только при этом условии, а если ОТ не активен, то целевую температуру контур ГВС не примет, стало быть, котёл работает в этом промежутке, в обычном режиме, ON/OFF (перемычка). Хотя, могу и ошибаться, погонять надо...

VOVA_iS
Offline
Зарегистрирован: 09.07.2019

tsv_33 пишет:

VOVA_iS, а как определили, что в котёл не ушла уставка?

 

Банально включаю воду и смотрю на сколько нагрел котел. Было 38 ставлю 45. Все равно греет до 38. 

VOVA_iS
Offline
Зарегистрирован: 09.07.2019

tsv_33 пишет:

 

1if (pv >= sp) {
2   enableCentralHeating = false;
3   } else {
4   enableCentralHeating = true;  }

С этим кодом работы помпы ОТ отключается...т.е. интерфейс активируется только при этом условии, а если ОТ не активен, то целевую температуру контур ГВС не примет, стало быть, котёл работает в этом промежутке, в обычном режиме, ON/OFF (перемычка). Хотя, могу и ошибаться, погонять надо...

Это решение было найдено после проблемы с регулировкой температуры.

С параметром READ и WRITE_ACK установленную температуру читает.

 

Вообщем добавил свою переменную в MQTT

1void publish_setDHWTemp() {
2  Serial.println("MQTT, DHW Temperature Set, °C = " + String(setDHWTemp()));
3  String(setDHWTemp()).toCharArray(buf, 50);
4  client.publish("dhws", buf);

Она почему-то в консоли появляется один раз.

 

Лог с WRITE

Connecting to HomeN16
....
WiFi connected
IP address: 
192.168.10.110
MDNS responder started
Attempting MQTT connection...connected
MQTT, DHW Temperature Set, °C = 0.00
MQTT, Current Room Temperature, °C = 0.00
MQTT, CH Temperature, °C = 28.00
MQTT, DHW Temperature, °C = 28.00
MQTT, Outside Temperature, °C = 0.00
MQTT, Relative Modulation Level, % = 0
MQTT, Status Central Heating = 1
MQTT, Status Hot Water = 0
MQTT, Status Central Heating = 0
MQTT, PID Controller Output, % = Setpoint CH Temperature, °C = 0.00
Заданное значение температуры в помещении = 24.50 °C
Текущее значение температуры в помещении = 28.44 °C
Выхов ПИД регулятора = 45.00
Время между измерениями = 3.88; ПИД коэффициенты: П = -39.38; И = 0.00; Д = 0.00
Текущий статус системы отопления: on
Текущий статус горячей воды: off
Текущий статус горелки: off
Индикация состояния неисправности: no fault
Диагностическая индикация: no diagnostics
Текущая температура контура СО = 28.00 °C
Текущая температура контура ГВС = 28.00 °C
Температура на улице = 0.00 °C
Текуший уровень модуляции горелки  = 0 %
ADC = 3
Установка температуры ГВС = 0.00 °C
Тип и версия термостата: тип 0, версия 0
Тип и версия котла: тип 0, версия 0
MQTT, Current Room Temperature, °C = 28.44
MQTT, CH Temperature, °C = 28.00
MQTT, DHW Temperature, °C = 28.00
MQTT, Outside Temperature, °C = 0.00
MQTT, Relative Modulation Level, % = 0
MQTT, Status Central Heating = 0
MQTT, Status Hot Water = 0
MQTT, Status Central Heating = 0
MQTT, PID Controller Output, % = Setpoint CH Temperature, °C = 45.00
Заданное значение температуры в помещении = 24.50 °C
Текущее значение температуры в помещении = 28.44 °C
Выхов ПИД регулятора = 45.00
Время между измерениями = 5.32; ПИД коэффициенты: П = -39.38; И = 0.00; Д = 0.00
Текущий статус системы отопления: off
Текущий статус горячей воды: off
Текущий статус горелки: off
Индикация состояния неисправности: no fault
Диагностическая индикация: no diagnostics
Текущая температура контура СО = 28.00 °C
Текущая температура контура ГВС = 28.00 °C
Температура на улице = 0.00 °C
Текуший уровень модуляции горелки  = 0 %
ADC = 10
Установка температуры ГВС = 0.00 °C
Тип и версия термостата: тип 0, версия 0
Тип и версия котла: тип 0, версия 0
MQTT, Current Room Temperature, °C = 28.44
MQTT, CH Temperature, °C = 28.00
MQTT, DHW Temperature, °C = 28.00
MQTT, Outside Temperature, °C = 0.00
MQTT, Relative Modulation Level, % = 0
MQTT, Status Central Heating = 0
MQTT, Status Hot Water = 0
MQTT, Status Central Heating = 0
MQTT, PID Controller Output, % = Setpoint CH Temperature, °C = 45.00
MQTT,topic = 45.80
 
 
Лог с READ
 
Connecting to HomeN16
....
WiFi connected
IP address: 
192.168.10.110
MDNS responder started
Attempting MQTT connection...connected
MQTT, DHW Temperature Set, °C = 40.00
MQTT, Current Room Temperature, °C = 0.00
MQTT, CH Temperature, °C = 29.00
MQTT, DHW Temperature, °C = 28.00
MQTT, Outside Temperature, °C = 0.00
MQTT, Relative Modulation Level, % = 0
MQTT, Status Central Heating = 1
MQTT, Status Hot Water = 0
MQTT, Status Central Heating = 0
MQTT, PID Controller Output, % = Setpoint CH Temperature, °C = 0.00
Заданное значение температуры в помещении = 24.50 °C
Текущее значение температуры в помещении = 28.38 °C
Выхов ПИД регулятора = 45.00
Время между измерениями = 3.88; ПИД коэффициенты: П = -38.75; И = 0.00; Д = 0.00
Текущий статус системы отопления: on
Текущий статус горячей воды: off
Текущий статус горелки: off
Индикация состояния неисправности: no fault
Диагностическая индикация: no diagnostics
Текущая температура контура СО = 28.00 °C
Текущая температура контура ГВС = 29.00 °C
Температура на улице = 0.00 °C
Текуший уровень модуляции горелки  = 0 %
ADC = 3
Установка температуры ГВС = 40.00 °C
Тип и версия термостата: тип 0, версия 0
Тип и версия котла: тип 0, версия 0
MQTT, Current Room Temperature, °C = 28.38
MQTT, CH Temperature, °C = 29.00
MQTT, DHW Temperature, °C = 29.00
MQTT, Outside Temperature, °C = 0.00
MQTT, Relative Modulation Level, % = 0
MQTT, Status Central Heating = 0
MQTT, Status Hot Water = 0
MQTT, Status Central Heating = 0
MQTT, PID Controller Output, % = Setpoint CH Temperature, °C = 45.00
Заданное значение температуры в помещении = 24.50 °C
Текущее значение температуры в помещении = 28.38 °C
Выхов ПИД регулятора = 45.00
Время между измерениями = 5.32; ПИД коэффициенты: П = -38.75; И = 0.00; Д = 0.00
Текущий статус системы отопления: off
Текущий статус горячей воды: off
Текущий статус горелки: off
Индикация состояния неисправности: no fault
Диагностическая индикация: no diagnostics
Текущая температура контура СО = 29.00 °C
Текущая температура контура ГВС = 29.00 °C
Температура на улице = 0.00 °C
Текуший уровень модуляции горелки  = 0 %
ADC = 3
Установка температуры ГВС = 40.00 °C
Тип и версия термостата: тип 0, версия 0
Тип и версия котла: тип 0, версия 0
MQTT, Current Room Temperature, °C = 28.38
MQTT, CH Temperature, °C = 29.00
MQTT, DHW Temperature, °C = 28.00
MQTT, Outside Temperature, °C = 0.00
MQTT, Relative Modulation Level, % = 0
MQTT, Status Central Heating = 0
MQTT, Status Hot Water = 0
MQTT, Status Central Heating = 0
MQTT, PID Controller Output, % = Setpoint CH Temperature, °C = 45.00
MQTT,topic = 45.80
Заданное значение температуры в помещении = 24.50 °C
Текущее значение температуры в помещении = 28.38 °C
Выхов ПИД регулятора = 45.00
Время между измерениями = 5.32; ПИД коэффициенты: П = -38.75; И = 0.00; Д = 0.00
Текущий статус системы отопления: off
Текущий статус горячей воды: off
Текущий статус горелки: off
Индикация состояния неисправности: no fault
Диагностическая индикация: no diagnostics
Текущая температура контура СО = 29.00 °C
Текущая температура контура ГВС = 29.00 °C
Температура на улице = 0.00 °C
Текуший уровень модуляции горелки  = 0 %
ADC = 3
Установка температуры ГВС = 40.00 °C
Тип и версия термостата: тип 0, версия 0
Тип и версия котла: тип 0, версия 0
MQTT, Current Room Temperature, °C = 28.38
MQTT, CH Temperature, °C = 28.00
MQTT, DHW Temperature, °C = 29.00
MQTT, Outside Temperature, °C = 0.00
MQTT, Relative Modulation Level, % = 0
MQTT, Status Central Heating = 0
MQTT, Status Hot Water = 0
MQTT, Status Central Heating = 0
MQTT, PID Controller Output, % = Setpoint CH Temperature, °C = 45.00
 
 
tsv_33
Offline
Зарегистрирован: 11.04.2019

VOVA_iS пишет:

tsv_33 пишет:

VOVA_iS, а как определили, что в котёл не ушла уставка?

 

Банально включаю воду и смотрю на сколько нагрел котел. Было 38 ставлю 45. Все равно греет до 38. 

Гистерезис большой, не зацикливаюсь. Я сейчас откопал свой рабочий код без WEB, внёс в него вашу правку помпы, проверил работает. Только обновите библиотеку ОТ. Там слово Enable заменено на Active (код правленный).

001/*
002  OpenThermMessageID:
003 
004  Status, // flag8 / flag8  Master and Slave Status flags.
005  TSet, // f8.8  Control setpoint  ie CH  water temperature setpoint (°C)
006  MConfigMMemberIDcode, // flag8 / u8  Master Configuration Flags /  Master MemberID Code
007  SConfigSMemberIDcode, // flag8 / u8  Slave Configuration Flags /  Slave MemberID Code
008  Command, // u8 / u8  Remote Command
009  ASFflags, // / OEM-fault-code  flag8 / u8  Application-specific fault flags and OEM fault code
010  RBPflags, // flag8 / flag8  Remote boiler parameter transfer-enable & read/write flags
011  CoolingControl, // f8.8  Cooling control signal (%)
012  TsetCH2, // f8.8  Control setpoint for 2e CH circuit (°C)
013  TrOverride, // f8.8  Remote override room setpoint
014  TSP, // u8 / u8  Number of Transparent-Slave-Parameters supported by slave
015  TSPindexTSPvalue, // u8 / u8  Index number / Value of referred-to transparent slave parameter.
016  FHBsize, // u8 / u8  Size of Fault-History-Buffer supported by slave
017  FHBindexFHBvalue, // u8 / u8  Index number / Value of referred-to fault-history buffer entry.
018  MaxRelModLevelSetting, // f8.8  Maximum relative modulation level setting (%)
019  MaxCapacityMinModLevel, // u8 / u8  Maximum boiler capacity (kW) / Minimum boiler modulation level(%)
020  TrSet, // f8.8  Room Setpoint (°C)
021  RelModLevel, // f8.8  Relative Modulation Level (%)
022  CHPressure, // f8.8  Water pressure in CH circuit  (bar)
023  DHWFlowRate, // f8.8  Water flow rate in DHW circuit. (litres/minute)
024  DayTime, // special / u8  Day of Week and Time of Day
025  Date, // u8 / u8  Calendar date
026  Year, // u16  Calendar year
027  TrSetCH2, // f8.8  Room Setpoint for 2nd CH circuit (°C)
028  Tr, // f8.8  Room temperature (°C)
029  Tboiler, // f8.8  Boiler flow water temperature (°C)
030  Tdhw, // f8.8  DHW temperature (°C)
031  Toutside, // f8.8  Outside temperature (°C)
032  Tret, // f8.8  Return water temperature (°C)
033  Tstorage, // f8.8  Solar storage temperature (°C)
034  Tcollector, // f8.8  Solar collector temperature (°C)
035  TflowCH2, // f8.8  Flow water temperature CH2 circuit (°C)
036  Tdhw2, // f8.8  Domestic hot water temperature 2 (°C)
037  Texhaust, // s16  Boiler exhaust temperature (°C)
038  TdhwSetUBTdhwSetLB = 48, // s8 / s8  DHW setpoint upper & lower bounds for adjustment  (°C)
039  MaxTSetUBMaxTSetLB, // s8 / s8  Max CH water setpoint upper & lower bounds for adjustment  (°C)
040  HcratioUBHcratioLB, // s8 / s8  OTC heat curve ratio upper & lower bounds for adjustment
041  TdhwSet = 56, // f8.8  DHW setpoint (°C)    (Remote parameter 1)
042  MaxTSet, // f8.8  Max CH water setpoint (°C)  (Remote parameters 2)
043  Hcratio, // f8.8  OTC heat curve ratio (°C)  (Remote parameter 3)
044  RemoteOverrideFunction = 100, // flag8 / -  Function of manual and program changes in master and remote room setpoint.
045  OEMDiagnosticCode = 115, // u16  OEM-specific diagnostic/service code
046  BurnerStarts, // u16  Number of starts burner
047  CHPumpStarts, // u16  Number of starts CH pump
048  DHWPumpValveStarts, // u16  Number of starts DHW pump/valve
049  DHWBurnerStarts, // u16  Number of starts burner during DHW mode
050  BurnerOperationHours, // u16  Number of hours that burner is in operation (i.e. flame on)
051  CHPumpOperationHours, // u16  Number of hours that CH pump has been running
052  DHWPumpValveOperationHours, // u16  Number of hours that DHW pump has been running or DHW valve has been opened
053  DHWBurnerOperationHours, // u16  Number of hours that burner is in operation during DHW mode
054  OpenThermVersionMaster, // f8.8  The implemented version of the OpenTherm Protocol Specification in the master.
055  OpenThermVersionSlave, // f8.8  The implemented version of the OpenTherm Protocol Specification in the slave.
056  MasterVersion, // u8 / u8  Master product version number and type
057  SlaveVersion, // u8 / u8  Slave product version number and type
058*/
059 
060#include <ESP8266WiFi.h>
061#include <PubSubClient.h>
062#include <WiFiClient.h>
063#include <OneWire.h>
064#include <DallasTemperature.h>
065#include <OpenTherm.h>
066 
067//Входные и выходные контакты OpenTherm, подключены к 4 и 5 контактам платы
068const int inPin = 4;            //D2
069const int outPin = 5;           //D1
070 
071#define ONE_WIRE_BUS 14         // D5 Data wire is connected to 14 pin on the OpenTherm Shield
072#define BUILTIN_LED 2           // D4 Встроенный LED
073#define EXTERNAL_LED_3 12       // D6 На внешний LED индикации работы СО
074#define EXTERNAL_LED 13         // D7 На внешний LED индикации работы ГВС
075#define EXTERNAL_LED_1 15       // D8 На внешний LED индикации работы горелки
076#define EXTERNAL_LED_2 0        // D3 На внешний LED индикации работы авария
077 
078//#define DAY(x)            ((x) * 10000ul) // тест
079#define DAY(x)            ((x) * 24 * MIN(60))
080#define MIN(x)            ((x) * 10000ul)
081#define TIME1             DAY(7)
082#define TIME2             MIN(60)
083#define VAL1              50.
084#define VAL2              60.
085 
086const char* ssid = "";
087const char* password = "";
088const char* mqtt_server = "";
089const int   mqtt_port = 12345;
090const char* mqtt_user = "";
091const char* mqtt_password = "";
092 
093OneWire oneWire(ONE_WIRE_BUS);
094DallasTemperature sensors(&oneWire);
095OpenTherm ot(inPin, outPin);
096WiFiClient espClient;
097PubSubClient client(espClient);
098char buf[50];
099 
100float sp = 20,                                   // точка отсчета комнатной температуры
101      pv = 0,                                    // текущая температура в комнате
102      pv_last = 0,                               // предыдущая температура
103      ierr = 0,                                  // интегральная погрешность
104      dt = 0,                                    // время между измерениями
105      op = 0,                                    // выход ПИД контроллера
106      spdhw = VAL1;                              // точка отсчета температуры горячей воды контура ГВС
107unsigned long ts = 0, new_ts = 0;                // отметки времени
108unsigned int hex56 = (spdhw * 256 * 16 / 16);    // точка отсчета температуры горячей воды контура ГВС, но в (HEX)
109unsigned int data126 = 0x013F;                   // тип и версия термостата (HEX)
110bool enableCentralHeating = true;
111bool enableHotWater = true;
112bool enableCooling = false;
113 
114//===============================================================
115//               Обрабатываем внешние прерывания
116//===============================================================
117void handleInterrupt() {
118  ot.handleInterrupt();
119}
120 
121float getTemp() {
122  return sensors.getTempCByIndex(0);
123}
124float getBoilerTemp() {
125  return ot.getBoilerTemperature();
126}
127float getDHWTemp() {
128  unsigned long request26 = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Tdhw, 0);
129  unsigned long respons26 = ot.sendRequest(request26);
130  uint16_t dataValue26 = respons26 & 0xFFFF;
131  float result26 = dataValue26 / 256;
132  return result26;
133}
134float getOutsideTemp() {
135  unsigned long request27 = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Toutside, 0);
136  unsigned long respons27 = ot.sendRequest(request27);
137  uint16_t dataValue27 = respons27 & 0xFFFF;
138  if (dataValue27 > 32768) {
139    //negative
140    float result27 = -(65536 - dataValue27) / 256;
141    return result27;
142  } else {
143    //positive
144    float result27 = dataValue27 / 256;
145    return result27;
146  }
147}
148int getADC() {
149  delay(100);
150  int ar = analogRead(A0);
151  ar = map(ar, 0, 1023, 0, 100);
152  return ar;
153}
154unsigned long getFault() {
155  unsigned long response = ot.setBoilerStatus(enableCentralHeating, enableHotWater, enableCooling);
156  OpenThermResponseStatus responseStatus = ot.getLastResponseStatus();
157  if (responseStatus = OpenThermResponseStatus::SUCCESS)
158    return ot.isFault(response);
159}
160unsigned long getCentralHeatingEnabled() {
161  unsigned long response = ot.setBoilerStatus(enableCentralHeating, enableHotWater, enableCooling);
162  OpenThermResponseStatus responseStatus = ot.getLastResponseStatus();
163  if (responseStatus = OpenThermResponseStatus::SUCCESS)
164    return ot.isCentralHeatingActive(response);
165}
166unsigned long getHotWaterEnabled() {
167  unsigned long response = ot.setBoilerStatus(enableCentralHeating, enableHotWater, enableCooling);
168  OpenThermResponseStatus responseStatus = ot.getLastResponseStatus();
169  if (responseStatus = OpenThermResponseStatus::SUCCESS)
170    return ot.isHotWaterActive(response);
171}
172unsigned long getFlameOn() {
173  unsigned long response = ot.setBoilerStatus(enableCentralHeating, enableHotWater, enableCooling);
174  OpenThermResponseStatus responseStatus = ot.getLastResponseStatus();
175  if (responseStatus = OpenThermResponseStatus::SUCCESS)
176    return ot.isFlameOn(response);
177}
178unsigned int getSlaveVersion_type() {
179  unsigned long request127 = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SlaveVersion, 0);
180  unsigned long respons127 = ot.sendRequest(request127);
181  uint16_t dataValue127 = respons127 & 0xFFFF;
182  unsigned result127 = dataValue127 / 256;
183  return result127;
184}
185unsigned int getSlaveVersion_num() {
186  unsigned long request127 = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SlaveVersion, 0);
187  unsigned long respons127 = ot.sendRequest(request127);
188  uint8_t dataValue127_ = respons127 & 0xFF;
189  unsigned result127_ = dataValue127_;
190  return result127_;
191}
192unsigned int setMasterVersion_type() {
193  unsigned long request126 = ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MasterVersion, data126);
194  unsigned long respons126 = ot.sendRequest(request126);
195  uint16_t dataValue126 = respons126 & 0xFFFF;
196  unsigned result126 = dataValue126 / 256;
197  return result126;
198}
199unsigned int setMasterVersion_num() {
200  unsigned long request126 = ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MasterVersion, data126);
201  unsigned long respons126 = ot.sendRequest(request126);
202  uint8_t dataValue126_ = respons126 & 0xFF;
203  unsigned result126_ = dataValue126_;
204  return result126_;
205}
206float setDHWTemp() {
207  unsigned long request56 = ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::TdhwSet, hex56);
208  unsigned long respons56 = ot.sendRequest(request56);
209  uint16_t dataValue56 = respons56 & 0xFFFF;
210  float result56 = dataValue56 / 256;
211  return result56;
212}
213unsigned int getFaultCode() {
214  unsigned long request5 = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::ASFflags, 0);
215  unsigned long respons5 = ot.sendRequest(request5);
216  uint8_t dataValue5 = respons5 & 0xFF;
217  unsigned result5 = dataValue5;
218  return result5;
219}
220//===============================================================
221//              Вычисляем коэффициенты ПИД регулятора
222//===============================================================
223float pid(float sp, float pv, float pv_last, float& ierr, float dt) {
224  float Kc = 10.0; // K / %Heater
225  float tauI = 50.0; // sec
226  float tauD = 1.0;  // sec
227  // ПИД коэффициенты
228  float KP = Kc;
229  float KI = Kc / tauI;
230  float KD = Kc * tauD;
231  // верхняя и нижняя границы уровня нагрева
232  float ophi = 100;
233  float oplo = 0;
234  // вычислить ошибку
235  float error = sp - pv;
236  // calculate the integral error
237  ierr = ierr + KI * error * dt;
238  // вычислить производную измерения
239  float dpv = (pv - pv_last) / dt;
240  // рассчитать выход ПИД регулятора
241  float P = KP * error;                      // пропорциональная составляющая
242  float I = ierr;                            // интегральная составляющая
243  float D = -KD * dpv;                       // дифференциальная составляющая
244  float op = P + I + D;
245  // защита от сброса
246  if ((op < oplo) || (op > ophi)) {
247    I = I - KI * error * dt;
248    // выход регулятора, он же уставка для ID-1 (температура теплоносителя контура СО котла)
249    op = max(oplo, min(ophi, op));
250  }
251  ierr = I;
252  Serial.println("Заданное значение температуры в помещении = " + String(sp) + " °C");
253  Serial.println("Текущее значение температуры в помещении = " + String(pv) + " °C");
254  Serial.println("Выхов ПИД регулятора = " + String(op));
255  Serial.println("Время между измерениями = " + String(dt) + "; ПИД коэффициенты: П = " + String(P) + "; И = " + String(I) + "; Д = " + String(D));
256  return op;
257}
258//==============================================================
259//                  Подключаемся к сети WiFi
260//==============================================================
261void setup_wifi() {
262  delay(10);
263  // подключение к сети Wi-Fi
264  Serial.println();
265  Serial.print("Connecting to ");
266  Serial.println(ssid);
267  WiFi.mode(WIFI_STA);
268  WiFi.begin(ssid, password);                // Подключение к маршрутизатору Wi-Fi
269  // дождаться соединения
270  while (WiFi.status() != WL_CONNECTED) {
271    delay(500);
272    Serial.print(".");
273  }
274  // если соединение успешно, показывает IP-адрес в мониторе
275  Serial.println("");
276  Serial.println("WiFi connected");
277  Serial.println("IP address: ");
278  Serial.println(WiFi.localIP());
279}
280//==============================================================
281//                  Функция SETUP
282//==============================================================
283 
284void setup() {
285  pinMode(BUILTIN_LED, OUTPUT);
286  pinMode(EXTERNAL_LED, OUTPUT);
287  pinMode(EXTERNAL_LED_1, OUTPUT);
288  pinMode(EXTERNAL_LED_2, OUTPUT);
289  pinMode(EXTERNAL_LED_3, OUTPUT);
290  digitalWrite(BUILTIN_LED, LOW);
291  digitalWrite(EXTERNAL_LED, LOW);
292  digitalWrite(EXTERNAL_LED_1, LOW);
293  digitalWrite(EXTERNAL_LED_2, LOW);
294  digitalWrite(EXTERNAL_LED_3, LOW);
295  Serial.begin(115200);
296  setup_wifi();
297  //==============================================================
298  //          Инициализация датчика температуры DS18B20
299  //==============================================================
300  sensors.begin();
301  sensors.requestTemperatures();
302  sensors.setWaitForConversion(false);                // Переключиться в асинхронный режим
303  pv, pv_last = sensors.getTempCByIndex(0);
304  ts = millis();
305  //==============================================================
306  //          Инициализация OpenTherm
307  //==============================================================
308  ot.begin(handleInterrupt);
309  //==============================================================
310  //          Инициализация MQTT клиента
311  //==============================================================
312  client.setServer(mqtt_server, mqtt_port);   // подключаемся к MQTT
313  client.setCallback(callback);               // функция получения топиков с брокера
314}
315void publish_temperature() {
316  Serial.println("MQTT, Current Room Temperature, °C = " + String(pv));
317  String(pv).toCharArray(buf, 50);
318  client.publish("pv", buf);
319}
320void publish_boilertemp() {
321  Serial.println("MQTT, CH Temperature, °C = " + String(getBoilerTemp()));
322  String(getBoilerTemp()).toCharArray(buf, 50);
323  client.publish("cht", buf);
324}
325void publish_dhwtemp() {
326  Serial.println("MQTT, DHW Temperature, °C = " + String(getDHWTemp()));
327  String(getDHWTemp()).toCharArray(buf, 50);
328  client.publish("dhwt", buf);
329}
330void publish_outtemp() {
331  Serial.println("MQTT, Outside Temperature, °C = " + String(getOutsideTemp()));
332  String(getOutsideTemp()).toCharArray(buf, 50);
333  client.publish("outt", buf);
334}
335void publish_statusCH() {
336  Serial.println("MQTT, Status Central Heating = " + String(getCentralHeatingEnabled()));
337  String(getCentralHeatingEnabled()).toCharArray(buf, 50);
338  client.publish("sch", buf);
339}
340void publish_ADC() {
341  Serial.println("MQTT, Relative Modulation Level, % = " + String(getADC()));
342  String(getADC()).toCharArray(buf, 50);
343  client.publish("adc", buf);
344}
345void publish_statusDWH() {
346  Serial.println("MQTT, Status Hot Water = " + String(getHotWaterEnabled()));
347  String(getHotWaterEnabled()).toCharArray(buf, 50);
348  client.publish("sdwh", buf);
349}
350void publish_statusFlame() {
351  Serial.println("MQTT, Status Central Heating = " + String(getFlameOn()));
352  String(getFlameOn()).toCharArray(buf, 50);
353  client.publish("sfl", buf);
354}
355void publish_setBoilerTemperature() {
356  Serial.println("MQTT, PID Controller Output, % = Setpoint CH Temperature, °C = " + String(op));
357  String(op).toCharArray(buf, 50);
358  client.publish("op", buf);
359}
360// функция обратного вызова, чтение топиков
361 
362void callback(char* topic, byte* payload, unsigned int length) {
363  char buffer[length + 1];
364  memcpy(buffer, payload, length);
365  buffer[length] = '\0';
366  float a = atof((char*)buffer);
367  if (a <= 31) {
368    sp = a;
369  } else {
370    spdhw = a;
371  }
372  Serial.println("MQTT,topic = " + String(a));
373}
374 
375void reconnect() {
376  while (!client.connected()) {
377    Serial.print("Attempting MQTT connection...");
378    // Attempt to connect
379    if (client.connect("ESP8266Client", mqtt_user, mqtt_password)) {
380      Serial.println("connected");
381 
382      // после подключения публикуем объявление...
383 
384      publish_temperature();
385      publish_boilertemp();
386      publish_dhwtemp();
387      publish_outtemp();
388      publish_ADC();
389      publish_statusCH();
390      publish_statusDWH();
391      publish_statusFlame();
392      publish_setBoilerTemperature();
393 
394      // ... и перезаписываем
395 
396      client.subscribe("sp");
397      client.subscribe("spdhw");
398    } else {
399      Serial.print("failed, rc =");
400      Serial.print(client.state());
401      Serial.println(" try again in 5 seconds");
402 
403      // Подождать 5 сек. перед повторной попыткой
404 
405      delay(5000);
406    }
407  }
408}
409//==============================================================
410//                     Функция LOOP
411//==============================================================
412void loop() {
413  new_ts = millis();
414  if (new_ts - ts > 1000) {
415    
416    unsigned long response = ot.setBoilerStatus(enableCentralHeating, enableHotWater, enableCooling);
417    OpenThermResponseStatus responseStatus = ot.getLastResponseStatus();
418    if (responseStatus = OpenThermResponseStatus::SUCCESS) {
419      //Serial.println("Error: Invalid boiler response " + String(response, HEX));
420    }
421    if (responseStatus == OpenThermResponseStatus::NONE) {
422      Serial.println("Error: OpenTherm is not initialized");
423    }
424    else if (responseStatus == OpenThermResponseStatus::INVALID) {
425      Serial.println("Error: Invalid response " + String(response, HEX));
426    }
427    else if (responseStatus == OpenThermResponseStatus::TIMEOUT) {
428      Serial.println("Error: Response timeout");
429    }
430 
431    pv = sensors.getTempCByIndex(0);
432    dt = (new_ts - ts) / 1000.0;
433    ts = new_ts;
434    if (responseStatus == OpenThermResponseStatus::SUCCESS) {
435      op = pid(sp, pv, pv_last, ierr, dt);
436 
437      // Заданная температура СО
438      ot.setBoilerTemperature(op);
439 
440      // Заданная температура ГВС
441      hex56 = (spdhw * 256 * 16 / 16);
442      unsigned int setDHWTemp(hex56);
443 
444      pv_last = pv;
445      sensors.requestTemperatures();                     //Асинхронный запрос температуры
446    }
447 
448    // Записать ID-2; мастер-код MemberID
449    unsigned int data = 0x0004;
450    unsigned long request = ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MConfigMMemberIDcode, data);
451    ot.sendRequest(request);
452     
453   // unsigned long request5 = ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::ASFflags, 0);
454   // unsigned long respons5 = ot.sendRequest(request5);
455   // uint8_t dataValue5 = respons5 & 0xFF;
456  //  unsigned result5 = dataValue5;
457 
458    // Выводим в монитор
459    Serial.println("Текущий статус системы отопления: " + String(ot.isCentralHeatingActive(response) ? "on" : "off"));
460    Serial.println("Текущий статус горячей воды: " + String(ot.isHotWaterActive(response) ? "on" : "off"));
461    Serial.println("Текущий статус горелки: " + String(ot.isFlameOn(response) ? "on" : "off"));
462    Serial.println("Индикация состояния неисправности: " + String(ot.isFault(response) ? "fault" : "no fault"));
463    Serial.println("Диагностическая индикация: " + String(ot.isDiagnostic(response) ? "diagnostics" : "no diagnostics"));
464    Serial.println("Текущая температура контура СО = " + String(getBoilerTemp()) + " °C");
465    Serial.println("Текущая температура контура ГВС = " + String(getDHWTemp()) + " °C");
466    Serial.println("Температура на улице = " + String(getOutsideTemp()) + " °C");
467    Serial.println("Текуший уровень модуляции горелки  = " + String (getADC()) + " %");
468    Serial.println("ADC = " + String (analogRead(A0)));
469    Serial.println("Уставка температуры ГВС = " + String(setDHWTemp()) + " °C");
470    Serial.println("Тип и версия термостата: тип " + String(setMasterVersion_type()) + ", версия " + String(setMasterVersion_num()));
471    Serial.println("Тип и версия котла: тип " + String(getSlaveVersion_type()) + ", версия " + String(getSlaveVersion_num()));
472 
473  //=================================================================================================
474  //    Индикация и реле насоса СО c откл. с задержкой 3 мин., индикация работы ГВС, горелки
475  //=================================================================================================
476  int status_error = getFault();                          // индикация состояния авария.
477  if (status_error == 1) {
478    Serial.println("Код неисправности: E " + String(getFaultCode()));
479    digitalWrite(EXTERNAL_LED_2, HIGH);
480  } else {
481    digitalWrite(EXTERNAL_LED_2, LOW);
482  }
483  int status_dhw = getHotWaterEnabled();                  // индикация состояния ГВС.
484  if (status_dhw == 1) {
485    digitalWrite(EXTERNAL_LED, HIGH);
486  } else {
487    digitalWrite(EXTERNAL_LED, LOW);
488  }
489  int status_flame = getFlameOn();                        // индикация состояния горелки.
490  if (status_flame == 1) {
491    digitalWrite(EXTERNAL_LED_1, HIGH);
492  } else {
493    digitalWrite(EXTERNAL_LED_1, LOW);
494  }
495   
496  if (pv >= sp) {                                    // индикация состояния СО.
497   enableCentralHeating = false;
498   digitalWrite(EXTERNAL_LED_3, LOW);
499   } else {
500   enableCentralHeating = true;
501   digitalWrite(EXTERNAL_LED_3, HIGH); 
502   }
503     
504    publish_temperature();
505    publish_boilertemp();
506    publish_dhwtemp();
507    publish_outtemp();
508    publish_ADC();
509    publish_statusCH();
510    publish_statusDWH();
511    publish_statusFlame();
512    publish_setBoilerTemperature();
513  }
514  // MQTT Loop
515  if (!client.connected()) {
516    reconnect();
517  }
518  client.loop();
519  //=================================================================================================
520  //             Активации функции Антилегионелла ГВС 65°C на 1 час каждые 7 дней по кругу.
521  //=================================================================================================
522 
523  static bool state;
524  static uint32_t old_millis = millis();
525 
526  if (state == false) {
527    if (millis() - old_millis >= TIME1) {
528      old_millis = millis();
529      spdhw = VAL1;
530      state = true;
531    }
532  } else if (millis() - old_millis >= TIME2) {
533    old_millis = millis();
534    spdhw = VAL2;
535    state = false;
536  }
537}

 

tsv_33
Offline
Зарегистрирован: 11.04.2019

Ещё, в приложении (не знаю каким пользуетесь) целевую уставку температуры в помещении делайте не более 30 гр., а целевую уставку ГВС не менее 32 гр. Условие описано в этом куске кода, строка 367:

1if (a <= 31) {
2    sp = a;
3  } else {
4    spdhw = a;
5  }

P.S. Кстати, у вас BAXI SLiM? В интерфейсной плате к LMU33 котла назначение реле какое? Есть предположения? Я интерфейсную плату сам делал, вместо реле повесил светодиод, для индикации, зажигается только при работе контура СО, на ГВС не активируется. При отключении СО, сразу гаснет.

VOVA_iS
Offline
Зарегистрирован: 09.07.2019

Вообщем такая же фигня... Не проходит установка температуры.

Может проблема с версией термостата.

 

1Тип и версия термостата: тип 0, версия 0
2Тип и версия котла: тип 0, версия 0

У вас что выдает в логах по этим параметрам. Думаю может он представиться нормально не может. Либо представился без регулировки ГВС.

И откуда можно узнать какие варианты есть представления для котла?

VOVA_iS
Offline
Зарегистрирован: 09.07.2019

tsv_33 пишет:

Ещё, в приложении (не знаю каким пользуетесь) целевую уставку температуры в помещении делайте не более 30 гр., а целевую уставку ГВС не менее 32 гр. Условие описано в этом куске кода, строка 367:

1if (a <= 31) {
2    sp = a;
3  } else {
4    spdhw = a;
5  }

P.S. Кстати, у вас BAXI SLiM? В интерфейсной плате к LMU33 котла назначение реле какое? Есть предположения? Я интерфейсную плату сам делал, вместо реле повесил светодиод, для индикации, зажигается только при работе контура СО, на ГВС не активируется. При отключении СО, сразу гаснет.

 

У меня KOREASTAR Premium. В этом вопросе не помогу.

tsv_33
Offline
Зарегистрирован: 11.04.2019

Загрузите мой последний код, посмотрим, что у вас. У меня котел представляется 1.51. если не ошибаюсь. просто я принты отключил.

tsv_33
Offline
Зарегистрирован: 11.04.2019

VOVA_iS пишет:

tsv_33 пишет:

Ещё, в приложении (не знаю каким пользуетесь) целевую уставку температуры в помещении делайте не более 30 гр., а целевую уставку ГВС не менее 32 гр. Условие описано в этом куске кода, строка 367:

1if (a <= 31) {
2    sp = a;
3  } else {
4    spdhw = a;
5  }

P.S. Кстати, у вас BAXI SLiM? В интерфейсной плате к LMU33 котла назначение реле какое? Есть предположения? Я интерфейсную плату сам делал, вместо реле повесил светодиод, для индикации, зажигается только при работе контура СО, на ГВС не активируется. При отключении СО, сразу гаснет.

У меня KOREASTAR Premium. В этом вопросе не помогу.

Понял, тогда и с уровнем модуляции вам разбираться надо. Мой котёл по ОТ этот ID Msg "RelModLevel" не отдаёт, потому сам вычисляю, а не читаю его из котла.

VOVA_iS
Offline
Зарегистрирован: 09.07.2019
Заданное значение температуры в помещении = 20.00 °C
Текущее значение температуры в помещении = 28.56 °C
Выхов ПИД регулятора = 0.00
Время между измерениями = 5.32; ПИД коэффициенты: П = -85.62; И = 0.00; Д = 0.00
Текущий статус системы отопления: off
Текущий статус горячей воды: off
Текущий статус горелки: off
Индикация состояния неисправности: no fault
Диагностическая индикация: no diagnostics
Текущая температура контура СО = 31.00 °C
Текущая температура контура ГВС = 36.00 °C
Температура на улице = 0.00 °C
Текуший уровень модуляции горелки  = 0 %
ADC = 10
Уставка температуры ГВС = 0.00 °C
Тип и версия термостата: тип 0, версия 0
Тип и версия котла: тип 0, версия 0
MQTT, Current Room Temperature, °C = 28.56
MQTT, CH Temperature, °C = 31.00
MQTT, DHW Temperature, °C = 36.00
MQTT, Outside Temperature, °C = 0.00
MQTT, Relative Modulation Level, % = 0
MQTT, Status Central Heating = 0
MQTT, Status Hot Water = 0
MQTT, Status Central Heating = 0
MQTT, PID Controller Output, % = Setpoint CH Temperature, °C = 0.00
Заданное значение температуры в помещении = 20.00 °C
Текущее значение температуры в помещении = 28.56 °C
Выхов ПИД регулятора = 0.00
Время между измерениями = 5.32; ПИД коэффициенты: П = -85.62; И = 0.00; Д = 0.00
Текущий статус системы отопления: off
Текущий статус горячей воды: off
Текущий статус горелки: off
Индикация состояния неисправности: no fault
Диагностическая индикация: no diagnostics
Текущая температура контура СО = 31.00 °C
Текущая температура контура ГВС = 36.00 °C
Температура на улице = 0.00 °C
Текуший уровень модуляции горелки  = 0 %
ADC = 3
Уставка температуры ГВС = 0.00 °C
Тип и версия термостата: тип 0, версия 0
Тип и версия котла: тип 0, версия 0
MQTT, Current Room Temperature, °C = 28.56
MQTT, CH Temperature, °C = 31.00
MQTT, DHW Temperature, °C = 36.00
MQTT, Outside Temperature, °C = 0.00
MQTT, Relative Modulation Level, % = 0
MQTT, Status Central Heating = 0
MQTT, Status Hot Water = 0
MQTT, Status Central Heating = 0
MQTT, PID Controller Output, % = Setpoint CH Temperature, °C = 0.00
Заданное значение температуры в помещении = 20.00 °C
Текущее значение температуры в помещении = 28.56 °C
Выхов ПИД регулятора = 0.00
Время между измерениями = 5.32; ПИД коэффициенты: П = -85.62; И = 0.00; Д = 0.00
Текущий статус системы отопления: off
Текущий статус горячей воды: off
Текущий статус горелки: off
Индикация состояния неисправности: no fault
Диагностическая индикация: no diagnostics
Текущая температура контура СО = 31.00 °C
Текущая температура контура ГВС = 36.00 °C
Температура на улице = 0.00 °C
Текуший уровень модуляции горелки  = 0 %
ADC = 10
Уставка температуры ГВС = 0.00 °C
Тип и версия термостата: тип 0, версия 0
Тип и версия котла: тип 0, версия 0
MQTT, Current Room Temperature, °C = 28.56
MQTT, CH Temperature, °C = 31.00
MQTT, DHW Temperature, °C = 36.00
MQTT, Outside Temperature, °C = 0.00
MQTT, Relative Modulation Level, % = 0
MQTT, Status Central Heating = 0
MQTT, Status Hot Water = 0
MQTT, Status Central Heating = 0
MQTT, PID Controller Output, % = Setpoint CH Temperature, °C = 0.00
Заданное значение температуры в помещении = 20.00 °C
Текущее значение температуры в помещении = 28.56 °C
Выхов ПИД регулятора = 0.00
Время между измерениями = 5.32; ПИД коэффициенты: П = -85.62; И = 0.00; Д = 0.00
Текущий статус системы отопления: off
Текущий статус горячей воды: off
Текущий статус горелки: off
Индикация состояния неисправности: no fault
Диагностическая индикация: no diagnostics
Текущая температура контура СО = 31.00 °C
Текущая температура контура ГВС = 36.00 °C
Температура на улице = 0.00 °C
Текуший уровень модуляции горелки  = 0 %
ADC = 3
Уставка температуры ГВС = 0.00 °C
Тип и версия термостата: тип 0, версия 0
Тип и версия котла: тип 0, версия 0
MQTT, Current Room Temperature, °C = 28.56
MQTT, CH Temperature, °C = 31.00
MQTT, DHW Temperature, °C = 36.00
MQTT, Outside Temperature, °C = 0.00
MQTT, Relative Modulation Level, % = 0
MQTT, Status Central Heating = 0
MQTT, Status Hot Water = 0
MQTT, Status Central Heating = 0
MQTT, PID Controller Output, % = Setpoint CH Temperature, °C = 0.00
Заданное значение температуры в помещении = 20.00 °C
Текущее значение температуры в помещении = 28.56 °C
Выхов ПИД регулятора = 0.00
Время между измерениями = 5.32; ПИД коэффициенты: П = -85.62; И = 0.00; Д = 0.00
Текущий статус системы отопления: off
Текущий статус горячей воды: off
Текущий статус горелки: off
Индикация состояния неисправности: no fault
Диагностическая индикация: no diagnostics
Текущая температура контура СО = 31.00 °C
Текущая температура контура ГВС = 36.00 °C
Температура на улице = 0.00 °C
Текуший уровень модуляции горелки  = 0 %
ADC = 10
Уставка температуры ГВС = 0.00 °C
Тип и версия термостата: тип 0, версия 0
Тип и версия котла: тип 0, версия 0
MQTT, Current Room Temperature, °C = 28.56
MQTT, CH Temperature, °C = 31.00
MQTT, DHW Temperature, °C = 36.00
MQTT, Outside Temperature, °C = 0.00
MQTT, Relative Modulation Level, % = 0
MQTT, Status Central Heating = 0
MQTT, Status Hot Water = 0
MQTT, Status Central Heating = 0
MQTT, PID Controller Output, % = Setpoint CH Temperature, °C = 0.00
Заданное значение температуры в помещении = 20.00 °C
Текущее значение температуры в помещении = 28.56 °C
Выхов ПИД регулятора = 0.00
Время между измерениями = 5.32; ПИД коэффициенты: П = -85.62; И = 0.00; Д = 0.00
Текущий статус системы отопления: off
Текущий статус горячей воды: off
Текущий статус горелки: off
Индикация состояния неисправности: no fault
Диагностическая индикация: no diagnostics
Текущая температура контура СО = 30.00 °C
Текущая температура контура ГВС = 36.00 °C
Температура на улице = 0.00 °C
Текуший уровень модуляции горелки  = 0 %
ADC = 10
Уставка температуры ГВС = 0.00 °C
Тип и версия термостата: тип 0, версия 0
Тип и версия котла: тип 0, версия 0