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

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

7694948@bk.ru

Калинин Роман Ш...
Offline
Зарегистрирован: 26.10.2019

tsv_33 пишет:

Калинин Роман Шадиманович пишет:
А как связаться с автором?

А кто и что Вас интересует?


Просто собирать некогда, нужно pid и mqtt от платы в принципе, ну я посмотрел по форуму, она идеально подходит мне, есть только вопрос по установке температуры гвс, моя самоделка так и не научилась выставлять ее, да и собранно все у меня на проводах, если ваша может температуру гвс выставлять будет вообще замечательно! Котел baxi main four 240

Калинин Роман Ш...
Offline
Зарегистрирован: 26.10.2019

СПАСИБО!

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

OldNavi, не замечали одну особенность, а именно подмену переменных? К примеру, ровненько читается температура воды в баке бойлера, потом провал до температуры отопительной воды, температура же отопительной воды падает в "0".

OldNavi
Offline
Зарегистрирован: 22.08.2019

Замечал,  в предыдущих версиях.  Но поправил в своей. Причина тоже понятна - например если послали сообщение а пришел таймаут или ошибка - и соответственно данные сбиваются.  Я у себя поменял логику - на другую - каждый response имеет в заголовки тип запроса - и на основании его я заполняю уже значения в обработчике асинхронного ответа.

  void static HandleReply(unsigned long response) {
    OpenThermMessageID id = ot.getDataID(response);
    uint8_t flags;
    switch (id)
    {
    case OpenThermMessageID::SConfigSMemberIDcode:
        vars.SlaveMemberIDcode.value = response >> 0 & 0xFF; 
        flags = (response & 0xFFFF) >> 8 & 0xFF;
        vars.dhw_present.value = flags & 0x01;
        vars.control_type.value = flags & 0x02;
        vars.cooling_present.value = flags & 0x04;
        vars.dhw_tank_present.value = flags & 0x08;
        vars.pump_control_present.value = flags & 0x10;
        vars.ch2_present.value = flags & 0x20;
        break;
    case OpenThermMessageID::Status:
        // Статус котла получен

.....

void static responseCallback(unsigned long result, OpenThermResponseStatus status)
  {
    DEBUG.println("Status of response " + String(status));
    DEBUG.println("Result of response " + String(result));
    ot_response = result;
    switch (status)
    {
    case OpenThermResponseStatus::INVALID:
      WARN.println("Ошибочный ответ от котла");
      if(debug)
        client.publish((vars.mqttTopicPrefix.value + "/message").c_str(), "Ошибочный ответ от котла");
      break;
    case OpenThermResponseStatus::TIMEOUT:
      WARN.println("Таймаут ответа от котла");
      if(debug)
        client.publish((vars.mqttTopicPrefix.value + "/message").c_str(), "Таймаут ответа от котла");
      break;
      timeout_count++;
      if (timeout_count > TIMEOUT_TRESHOLD)
      {
        vars.online.value = false;
        timeout_count = TIMEOUT_TRESHOLD;
      }
      break;
    case OpenThermResponseStatus::SUCCESS:
      timeout_count = 0;
      vars.online.value = true;
      HandleReply(result);
      break;
    default:
      break;
    }
  }

 

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

Бегло пробежался по вашему проекту, сразу бросилось в глаза:

#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <ESP8266mDNS.h>

Ошибка?

OldNavi
Offline
Зарегистрирован: 22.08.2019

Ага,  очепятка - включил хедер дважды.

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

OldNavi, планировщиком не пользуетесь?

OldNavi
Offline
Зарегистрирован: 22.08.2019

Вопрос не понял, про какой планировщик речь ?  Если про потоки выполнения - то да пользуюсь, есть же в коде

#include <Scheduler.h>


void setup()
{
.....
  Serial.begin(115200);
  setup_wifi();

  // Запускаем основные потоки - в одном обрабатываем сообщения OpenTherm
  // Второй поток отвечает за обслуживание всего остального
  Scheduler.start(&OtHandler);
  Scheduler.start(&MainTask);

  Scheduler.begin();
}

 

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

Планировщиком заданий, скажем уставкой температуры в доме по времени, или ГВС...

OldNavi
Offline
Зарегистрирован: 22.08.2019

tsv_33 пишет:

Планировщиком заданий, скажем уставкой температуры в доме по времени, или ГВС...

А ну, этим у меня Home Assistant занимается на малинке -  крутая штука для написания всяких автоматизаций и сценариев.  Более того у меня там завязано оочень много чего, включая управление рециркуляцией, аварийным переключением котлов, освещением, датчиками безопасности интеграцией с HomeKit, Tasmota Sonoff и т.д.  По сути это система умного дома - которая интегрируется со всем и всея. https://www.home-assistant.io

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

Ага, понял.

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

Добавил  uptime работы esp.

String Uptime()
{
  String Time = "";
  unsigned long dd = 0;
  int hh = 0;
  int mm = 0;
  int ss = 0;
  int Rollover = 0;
  int HighMillis = 0;
  unsigned long secUp = millis() / 1000;
  if (millis() >= 3000000000) 
 {
    HighMillis = 1;
  }
  if (millis() <= 100000 && HighMillis == 1)
 {
    Rollover ++;
    HighMillis = 0;
  }
  ss = secUp % 60;
  mm = (secUp / 60) % 60;                             
  hh = (secUp / (60 * 60)) % 24;                      
  dd = (Rollover * 50) + (secUp / (60 * 60 * 24));    
  if (dd > 0)Time += "0";                             
  Time += (String)dd + " д ";
  if (hh < 10)Time += "0";
  Time += (String)hh + ":";
  if (mm < 10)Time += "0";
  Time += (String)mm + ":";
  if (ss < 10)Time += "0";
  Time += (String)ss;
  return Time;
}

 

RET
Offline
Зарегистрирован: 03.10.2019

Приветствую. Всё пытаюсь ещё доработать свой код для работы с котлом Bosch. Возник вопрос касательно расшифровки ответа котла на запрос c ID-3.

Ответ приходит в HEX со значением 40031108. А вот как его привести в понятный вид, что бы увидеть два параметра HB: Slave configuration и LB: Slave MemberID. Что то совсем запутался в понятии старших и младших битов.

Так же и с ID-6, ответ в HEX c0060303, а вот как его перевести в понятный глазу вид не понимаю.

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

Так в винде калькулятор есть, "программист", а что бы увидеть, вам нужны четыре цифры справа налево, LB-08, HB-11 и т.д., а расшифровку смотрите в протоколе.

RET
Offline
Зарегистрирован: 03.10.2019

tsv_33 пишет:

Так в винде калькулятор есть, "программист", а что бы увидеть, вам нужны четыре цифры справа налево, LB-08, HB-11 и т.д., а расшифровку смотрите в протоколе.

Спасибо, вроде бы понял. То есть, что бы передать в моем случае котлу "нужный" Master MemberID code, запрос на отправку должен выглядеть так?:

 // Записать ID-2; мастер-код MemberID
unsigned int data = 0x1000;
unsigned long request = ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MConfigMMemberIDcode, data);
ot.sendRequest(request);

 

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

Нет. В вашем случае:

// Записать ID-2; мастер-код MemberID
unsigned int data = 0x0008;
unsigned long request = ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MConfigMMemberIDcode, data);
ot.sendRequest(request);

 

RET
Offline
Зарегистрирован: 03.10.2019

А, т.е. ему обратно так же в HEX'ах надо передавать?

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

Да, в протоколе написано "u8  unsigned 8-bit integer 0 .. 255", но в HEX.

itf1
Offline
Зарегистрирован: 13.11.2019

Добрый день.

Новенький в этой теме - подскажите пожалуйста, а какое оборудование вы используете для коммуникации с котлами по протоколу OpenTherm?

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

Не понял... Протокол так отвечает на ваш вопрос

3.1  Medium Definition- Characteristics of the Transmission Line 
Number of Wires    :  2 
Wiring type    :  untwisted pair * 
Maximum line length    :  50 metres 
Maximum cable resistance    :  2 * 5 Ohms 
Polarity of connections    :  Polarity-free, i.e. interchangeable. 
 
* In electrically noisy environments it may be necessary to use twisted pair or screened cable.
Калинин Роман Ш...
Offline
Зарегистрирован: 26.10.2019

Привет всем! OldNavi а не напишешь тут что в configuration.yaml добавить чтобы такой же интерфейс получить как у тебя? Спасибо!

И еще один вопрос как ему отдать температуру помещения?

Вижу что получает тут:

vars.house_temp.value = json["house_temp"] | 23.0;

А как ему ее отдать не понимаю.....

Еще раз заранее спасибо!

OldNavi
Offline
Зарегистрирован: 22.08.2019

Калинин Роман Шадиманович пишет:

Привет всем! OldNavi а не напишешь тут что в configuration.yaml добавить чтобы такой же интерфейс получить как у тебя? Спасибо!

 

Ну configuration.yaml свой я точно не выложу - там извините приватная информация о других системах по мимо опентерма.  Да и несложно его составлять, здесь просто напишу пару примеров как описать сенсоры

В binary_sensors.yaml пишете бинарные датчики какие вам нужны (те у которых всего 2 состояния)

- platform: mqtt
  name: "Ошибка"
  state_topic: "opentherm/state"
  value_template: '{{ value_json.fault }}'
  availability_topic: "opentherm/status" 
  payload_on: true
  payload_off: false

В sensors.yaml пишете что то типа

- platform: mqtt
  name: "Уставка ГВС"
  device_class: temperature
  state_topic: "opentherm/state"
  value_template: '{{ value_json.dhw_temp_set }}'
  availability_topic: "opentherm/status"
  unit_of_measurement: "°C" 

В input_number.yaml пишете то что будете вводить

ustavka_gvs:
    name: Установка ГВС
    min: 0
    max: 60
    unit_of_measurement: "°C"
    icon: mdi:temperature
ustavka_heat:
    name: Установка Отопления
    min: 0
    max: 85
    step: 0.5
    unit_of_measurement: "°C"
    icon: mdi:temperature    

и в automations.yaml пишете связывающие из автоматизации

- id: 'xxxxxxxx'
  alias: Установка Отопления
  trigger:
  - entity_id: input_number.ustavka_heat
    platform: state
  condition:
  - condition: template
    value_template: '{{ states(''sensor.otoplenie'') not in [''unavailable'', ''unknown'']
      }}'
  action:
  - data:
      payload_template: '{ "heater_temp": {{ states(''input_number.ustavka_heat'')
        }} }'
      topic: opentherm/cmnd
    service: mqtt.publish

Цитата:

И еще один вопрос как ему отдать температуру помещения?

Вижу что получает тут:

vars.house_temp.value = json["house_temp"] | 23.0;

А как ему ее отдать не понимаю.....

Еще раз заранее спасибо!

Все просто - написать в MQTT opentherm/cmnd топик сообщение в виде json c атрибутом house_temp.  

Калинин Роман Ш...
Offline
Зарегистрирован: 26.10.2019

Да, извините это понятно! Спасибо сейчас буду разбираться!

Калинин Роман Ш...
Offline
Зарегистрирован: 26.10.2019

Извините еще раз... А не сможете объяснить примером какими средствами в HA возможно писать в топик сообщения... просто я для три как начал пользоваться HA и непонимаю куда и что....

OldNavi пишет:

Все просто - написать в MQTT opentherm/cmnd топик сообщение в виде json c атрибутом house_temp.  

это понятно что все просто... Я тут с синтаксисом JAML намучался но потихоньку понимаю вроде бы...

OldNavi
Offline
Зарегистрирован: 22.08.2019

Вызвать сервис mqtt.publish - в примерах же видно

      payload_template: '{ "heater_temp": {{ states(''input_number.ustavka_heat'')
        }} }'
      topic: opentherm/cmnd
    service: mqtt.publish

 

Калинин Роман Ш...
Offline
Зарегистрирован: 26.10.2019

Спасибо большое буду дальше разбираться!

Калинин Роман Ш...
Offline
Зарегистрирован: 26.10.2019

OldNavi пишет:

Вызвать сервис mqtt.publish - в примерах же видно

      payload_template: '{ "heater_temp": {{ states(''input_number.ustavka_heat'')
        }} }'
      topic: opentherm/cmnd
    service: mqtt.publish

 

хорошо а как его вызвать... не пойму... я вижу это так я беру данные с датчиков и скриптом отправляю их через сервис mqtt 

Если можно примером покажите:

забираем данные с датчика так-то

Отправляем их так- то 

все это делается тут -то..

Еще раз извините за тупые вопросы, да еще и не по теме топика.... И огромное спасибо Вам!

OldNavi
Offline
Зарегистрирован: 22.08.2019

Вам все равно прийдется погрузится в документацию HA и почитать как работает автоматизация и что прописывается в automations.yaml.

И я же привел пример

- id: 'xxxxxxxx'
  alias: Установка Отопления
  trigger:
  - entity_id: input_number.ustavka_heat
    platform: state
  condition:
  - condition: template
    value_template: '{{ states(''sensor.otoplenie'') not in [''unavailable'', ''unknown'']
      }}'
  action:
  - data:
      payload_template: '{ "heater_temp": {{ states(''input_number.ustavka_heat'')
        }} }'
      topic: opentherm/cmnd
    service: mqtt.publish

Который делает следующее - 

если изменилось состояние entity_id - в данном случае ползунка ввода уставки отопления и выполнены условия из секции condition - то по action вызвать service mqtt.publish  что бы он отправил строку в виде json описанную в параметре payload.

Засим предлагаю закрыть вопрос по обучению конфигурирования HA - это оффтопик.

Калинин Роман Ш...
Offline
Зарегистрирован: 26.10.2019

Спасибо большое все получилось!

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

OldNavi, как работает устройство? Оправдало ожидание? Решил я то же брать усреднённую температуру по дому для котла, но не с MQTT, а с, имеющегося у меня, modbas сервера. Все же, склоняюсь к тому, что работа котла с ПИД регулятором более ровная и экономичная, подобрал оптимальным Кс.

OldNavi
Offline
Зарегистрирован: 22.08.2019

Работает изумительно :-). Не лазил в него больше 

itf1
Offline
Зарегистрирован: 13.11.2019

Добрый день. OldNavi, подскажите пожалуйста в вашем коде используется библиотека OpenTherm.h. 

Правильно ли я понимаю, что это библиотека OpenThermLibrary by Ohor Melnyk?

OldNavi
Offline
Зарегистрирован: 22.08.2019

Да это его,  там были мои правки - он их вставил в проект.

s_lemesh
Offline
Зарегистрирован: 27.12.2019

RET
Доброго времени.
Не могли бы вы поделиться своим скетчем для котла Bosh.
Заранее спасибо

RET
Offline
Зарегистрирован: 03.10.2019

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

s_lemesh
Offline
Зарегистрирован: 27.12.2019

Доброго времени RET.
Интересует управление котлом тоесть автоматическая установка температуры в зависимости от температуры на улице и в доме.
Заранее спасибо.

leonzone
Offline
Зарегистрирован: 31.12.2019
Приветствую! хочу выразить благодарность tsv_33 за предоставленную плата ОТ_адаптера - качество на высоте, работает без нареканий и OldNavi за полный код на гитхабе, который стал основой для моего - я новичок в ардуинах, без него результата мне было бы куда сложнее добиться... +описание интеграции с Home Assistant
 
для расчета температуры взял вот этот код: https://mxjournal.ru/blog/1472, просто и доступно :)
 
котёл немного не радует - Unical IDEA CS 24 - для 65кв.м. слишком мощный, тактует, плюс у него минимально возможная температура СО 45 градусов, при задании ниже - просто игнорирует и использует предыдущее значение, горелку не отключает, т.е. получить просто рабочий насос без горелки не могу. при таких условиях держать температуру в доме в 22,5 - легко, а я хочу 21,8... получается всё равно работа похожа на обычный термостат вкл-выкл, но в целом оч доволен, дома стало значительно комфортнее
miks69
Offline
Зарегистрирован: 16.02.2020

Задержки работы скетча при использовании библиотеки OpenThermLibrary by Ihor Melnyk могут происходить из-за логики работы самой библиотеки, где после отправки очередного запроса происходит непосредственное ожидание ответа от котла, которое может занимать от 0,1 сек (при хорошем раскладе) до 1 сек (при таймауте). В случае, если в скетче несколько запросов отправляются подряд, такое ожидание ответов вполне может давать ощутимую задержку.

С учетом этого разумнее в коде скетча разделить отправку/получение данных от котла и формирование/отправку веб-страницы клиенту.

OldNavi
Offline
Зарегистрирован: 22.08.2019

Стоило бы прочесть ветку то :-). И уже бы знали ответ,  ибо все уже написано и все грабли уже пройдены.  OpenThermLibrary поддерживает в том числе и  асинхронную работу и в моем коде это уже реализовано :-)

miks69
Offline
Зарегистрирован: 16.02.2020

OldNavi пишет:

Стоило бы прочесть ветку то :-). И уже бы знали ответ,  ибо все уже написано и все грабли уже пройдены.  OpenThermLibrary поддерживает в том числе и  асинхронную работу и в моем коде это уже реализовано :-)

Ну я рад за Вас, а читать всю Вашу переписку нет времени, извините. Просто хотел помочь.

Если не сложно приведите часть Вашего кода по работе с OpenTherm.

OldNavi
Offline
Зарегистрирован: 22.08.2019

А стоило бы,  там бы и нашли ссылку на репозиторий на Гитхабе

miks69
Offline
Зарегистрирован: 16.02.2020

OldNavi пишет:

А стоило бы,  там бы и нашли ссылку на репозиторий на Гитхабе

Посмотрел Ваш код, немного не понял, зачем после выполнения асинхронного запроса сделано явное ожидание ответа, если по факту получения ответа у Вас отрабатывает ResponseCallback:

  bool sendRequest(unsigned long request, unsigned long& response) {
	  
    send_newts = millis();
    if (send_newts - send_ts < 200) {
      // Преждем чем слать что то - надо подождать 100ms согласно специфиикации протокола ОТ
      delay(200 - (send_newts - send_ts));
    }
    
	bool result = ot.sendRequestAync(request);
    if(!result) {
      DEBUG.println("Не могу отправить запрос");
      DEBUG.println("Шина "+ ot.isReady() ? "готова" : "неготова");
      return false; 
      }
    
	while (!ot.isReady())
    {
      ot.process();
      yield(); // This is local Task yield() call which allow us to switch to another task in scheduler
    }
	
    send_ts = millis();
    response = ot_response;
    return true; // Response is global variable
	
  }

 

OldNavi
Offline
Зарегистрирован: 22.08.2019

Где именно сделано явное ожидание ответа ?

miks69
Offline
Зарегистрирован: 16.02.2020

OldNavi пишет:

Где именно сделано явное ожидание ответа ?

Цикл WHILE, строки 16-20 в опубликованной части кода

OldNavi
Offline
Зарегистрирован: 22.08.2019

Это не ожидание ответа,  это процессинг библиотеки OpenThermLibrary.... ResponseCallback невыполнится пока из пришедших прерываний не соберется сам Response и ot.process() не вызовет ResponseCallback. И да его надо крутить в цикле ( а если нечего делать - отдаем процессор другому таску) - смотрите реализацию в библиотеке.

unsigned long OpenTherm::sendRequest(unsigned long request)

miks69
Offline
Зарегистрирован: 16.02.2020

Похоже Вы правы, но в таком случае я не вижу никакой разницы между синхронным и асинхронным запросом, т.к. при выполнении синхронного запроса методом ot.sendRequest там стоит аналогичная конструкция

OldNavi
Offline
Зарегистрирован: 22.08.2019

Похожая, но не такая....

Вся разница здесь

    yield(); // This is local Task yield() call which allow us to switch to another task in scheduler

Подсказка - это несистемный yield()

miks69
Offline
Зарегистрирован: 16.02.2020

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

OldNavi
Offline
Зарегистрирован: 22.08.2019

Именно.  Он отдает управление потоку в котором крутится логика и осуществляется коммуникация (MQTT, HTTP и так далее).   Если просто выполнить синхронный SendRequest() из библиотеки OpenThermLibrary - код и будет там крутится и не даст ни одного кванта времени для исполнения кода в потоке MainTask.

И этот yield() - не системный yield(), а метод класса Task  из библиотеки ESPScheduler https://github.com/nrwiersma/ESP8266Scheduler