Code Monkey home page Code Monkey logo

easyopentherm's Introduction

OpenTherm Arduino ESP32/ESP8266 Library

Was already working on an ESP32, now ready for an ESP8266 too!

Create your own thermostat with this library and save money on your energy bills while reducing your carbon footprint! Or use a home automation system like Home Assistant and smart thermostatic valves to heat up each room individually.

The library complies with the OpenTherm specification. Control any (condensing) boiler or air conditioner (HVAC) that also meets the OpenTherm specification.

The library can be easily installed in the Arduino IDE. It has been tested on an ESP32-S2, ESP32-C3, ESP32 NodeMCU and an ESP8266 NodeMCU microcontroller and may also work on other MCU's. To connect the boiler, you will need an OpenTherm controller board or an OpenTherm controller Shield.

Fully functioning Home Assistant Boiler Thermostat in examples

Home Assistant logo MQTT logo\

The Advanced_Thermostat.ino example is specially designed for Home Assistant

  • Setup the MQTT integration in Home Assistant, if not already done
  • Set your WiFi network name and password in Advanced_Thermostat.ino
  • Set your MQTT broker hostname or IP address, MQTT user name and MQTT password in Advanced_Thermostat.ino
  • Compile and flash to an ESP32-S2 or an ESP32-C3 with an OpenTherm controller Shield or any other ESP32 or ESP8266 with an OpenTherm controller board
  • Forward the temperature of a room thermometer to the Thermostat using a Home Assistant Automation The thermostat (Climate) integration is automatically added to Home Assistant, together with several sensors. More information in the examples directory.

Installation

  • Install the EasyOpenTherm library directly using the Arduino IDE library manager (search for 'EasyOpenTherm')
  • Connect the pins marked 'OT' of the OpenTherm controller with two wires to the boiler or use the screw terminals on the OpenTherm controller shield. You can use the existing wires from your current thermostat. The order of the wires is not important, they are interchangeable
  • If using the board connect the pins marked '3v3' and 'GND' to the ESP32 pins '3v3; and 'GND'. For the shield these pins are already connected
  • If using the board connect the pin marked 'RxD' to a pin supporting OUTPUT of the ESP32 and the pin marked 'TxD' to a pin supporting interrupts. For the shield these pins are already connected. The pins used you use should be defined in the program (see below). In Advanced_Thermostat.ino they default to the right pins for the shield and either an ESP32-S2 mini or an ESP32-C3 mini.

Usage

#include <EasyOpenTherm.h>

Select two free GPIO pins, one to send data to the boiler and one to receive data. The pin receiving data must support interrupts. For the pin that sends data, do not use a 'read only' GPIO. Define these pins in the program

#define OT_RX_PIN (35)
#define OT_TX_PIN (33)

In this case GPIO34 is used for receiving and GPIO33 is used for sending data. Note that the Rx pin is connected to the TxD pin of the OpenTherm controller and vice versa!

Create an OpenTherm class instance

OpenTherm thermostat(OT_RX_PIN, OT_TX_PIN);

Make sure that only one instance of this object is alive at a time. So make it global or static like in the examples. Start communicating with the boiler (or HVAC) e.g. request activation of the services of the boiler and request it's status flags

uint8_t primaryFlags = uint8_t(OpenTherm::STATUS_FLAGS::PRIMARY_DHW_ENABLE);  // Enable Domestic Hot Water 
primaryFlags |= uint8_t(OpenTherm::STATUS_FLAGS::PRIMARY_CH_ENABLE);          // Enable Central Heating
primaryFlags |= uint8_t(OpenTherm::STATUS_FLAGS::PRIMARY_COOLING_ENABLE);     // Enable cooling (of your boiler, if available)
primaryFlags |= uint8_t(OpenTherm::STATUS_FLAGS::PRIMARY_OTC_ENABLE);         // Enable Outside Temperature Compensation (ifa available in your boiler)
uint8_t statusFlags;                                                          // Flags returned by the boiler will be stored into this variable
thermostat.status(primaryFlags, statusFlags);

This command will return true on success and false otherwise All other interaction with the boiler is done using the read(), write() or readWrite() functions, e.g.

thermostat.write(OpenTherm::WRITE_DATA_ID::CONTROL_SETPOINT_CH, 40.0); // This will start up the boiler to heat up the boiler water to 40 degrees

All these functions take an OpenTherm DATA-ID as first parameter. The DATA-ID refers to the action requested from the boiler. All known DATA-ID's are defined in EasyOpenTherm.h. The DATA-IDs for reading data from the boiler are defined in enum class READ_DATA_ID, DATA-IDs for writing data to the boiler are defined in enum class WRITE_DATA_ID and DATA-IDs for writing and reading data to and from the boiler are defined in enum class READ_WRITE_DATA_ID. The second parameter and sometimes third parameter defines the value written to the boiler or read from the boiler. The data types are:

  • uint16_t marked as u16 in the comments
  • int16_t marked as s16
  • float marked as f8.8 (because actually it is an int16_t / 256.0)
  • Two times an uint8_t marked as flag8 or u8, or an int8_t as s8. If it is a flag the meaning of bits is defined in an enum class with a name ending in _FLAGS, e.g. enum class STATUS_FLAGS

Error handling

The function error() is used to get information about the last call to one of the functions read(), write() or readWrite(). All these functions return false if an error occurred in the communication between thermostat (primary) and boiler or HVAC (secondary). These functions return true if everything is fine, but also upon an error on application level. You will get this error if e.g. you read out your boiler or HVAC with a DATA-ID that is not supported. Also, if you write data to your boiler or HVAC, the value can be out of range, e.g. a setpoint is too low or too high. In this case error() will return INVALID_DATA.

All error codes:

  • OK: everything is fine!
  • UNKNOWN_DATA_ID: your boiler or HVAC does not support the DATA-ID you requested.
  • INVALID_DATA: the DATA-ID you sent with write() or readWrite() is correct but the value you sent is out of bounds
  • SEND_TIMEOUT: a timeout occurred while sending the request to the boiler or HVAC. Check if the timeout value in the OpenTherm constructor is OK (should be less than 1,000 ms, defaults to 900 ms)
  • RECEIVE_TIMEOUT: a timeout occurred while reading the response from the boiler or HVAC. Most of the time this indicates problems with wiring. If wiring is OK, did you check if your boiler actually does support the OpenTherm protocol?
  • PARITY_ERROR: the parity check failed.

Glossary

  • primary: the device issuing the requests, in this context also called thermostat
  • secondary: the device handling the requests and sending responses, also called boiler or HVAC
  • CH: Central Heating
  • DHW: Domestic Hot Water
  • OTC: Outside Temperature Compensation
  • HVAC: Heating, Ventilation and Air Conditioning
  • setpoint: the desired value of a parameter, e.g. the desired temperature of the temperature of the water of the boiler is called CH (Central Heating) setpoint
  • on/off: a non digital control mode switching the boiler on and off (by shortening the thermostat wires and opening them)
  • modulation: a technique of lowering the flame when less power is needed
  • flow: water leaving the boiler
  • return: water returning to the boiler

License

© 2022 Jeroen Döll, licensed under the GNU General Public License. Enjoy using the library, feedback is welcome!

easyopentherm's People

Contributors

jeroen88 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

bielteknik pgroen

easyopentherm's Issues

FLAGS 2,3,126,127

Hello friend, I didn't see 2, 3, 126, 127 in the exchange code.
For some boilers, the exchange is mandatory, an example unfortunately for another library:

unsigned long request3 = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SConfigSMemberIDcode, 0xFFFF)
unsigned long respons3 = ot.sendRequest(request3);
uint8_t SlaveMemberIDcode = respons3 >> 0 & 0xFF;
unsigned long request2 = ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MConfigMMemberIDcode, SlaveMemberIDcode);
unsigned long respons2 = ot.sendRequest(request2);
nsigned long request127 = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SlaveVersion, 0);
unsigned long respons127 = ot.sendRequest(request127);
uint16_t dataValue127_type = respons127 & 0xFFFF;
uint8_t dataValue127_num = respons127 & 0xFF;
unsigned result127_type = dataValue127_type / 256;
unsigned result127_num = dataValue127_num;
unsigned long request126 = ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MasterVersion, 0x013F);
unsigned long respons126 = ot.sendRequest(request126);
uint16_t dataValue126_type = respons126 & 0xFFFF;
uint8_t dataValue126_num = respons126 & 0xFF;
unsigned result126_type = dataValue126_type / 256;
unsigned result126_num = dataValue126_num;

//=======================================================================================
// This group of data elements defines configuration information on both slaves and
// and on the main sides. Each of them has a group of configuration flags (8 bits)
// and the MemberID code (1 byte). Before transmitting management and status information
// it is recommended to exchange messages about a valid configuration of the slave device
// read and write basic configuration. The zero code MemberID means client
// non-specific device. The product version number/type should be used in combination
// with a "participant ID code" that identifies the device manufacturer.

localRequest = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SConfigSMemberIDcode, 0xFFFF);
  sendRequest(localRequest, localResponse);

  if(!vars.monitor_only.value && ot.getLastResponseStatus() == OpenThermResponseStatus::SUCCESS && ot.getDataID(localResponse) == OpenThermMessageID::SConfigSMemberIDcode) 
  {
  localRequest = ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MConfigMMemberIDcode, vars.SlaveMemberIDcode.value);
  sendRequest(localRequest, localResponse);
  }
  
  localRequest = ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::SlaveVersion, 0);
  sendRequest(localRequest, localResponse);

  localRequest = ot.buildRequest(OpenThermRequestType::WRITE, OpenThermMessageID::MasterVersion, 0x013F);
  sendRequest(localRequest, localResponse);

Boiler monitoring?

Hi,

Saw this passing by on Hackster.io, looks super interesting!
I was wondering, when I hook up my boiler with this, can I use it to monitor rather than control it? With my current thermostat still connected and in control?

Thanks!
Erwin

Electrical isolation of opentherm-controller board is faulty

Hi,

I've stumbled upon your project because of a google news recommendation. I noticed that the hardware referenced from the project's readme uses a dual photocoupler which creates a safety risk regarding electrical isolation.

Isolation is provided by separation on each side of the photocoupler. But on said board, there are tracks going around on each side of the photocoupler, which means that you have less than 1mm of separation between the boiler and the microcontroller (distance between pins [2:3] and [6:7]). I don't have the number from the top of my head, but it should be much more! The fix is to use two single photocouplers and rotate one of them. It will also make routing much more straightforward.

You could assume that there is isolation on the boiler side, that the microcontroller is powered by an isolated source, that no one puts their hands where they shouldn't. But are these assumptions always true?

Mqtt

Nice project, order the otg controller.
Any update on mqtt?
Thanks!!

Installation guide for IDE novices

Hi @Jeroen88,

I saw your comment on one of the HA Community forums and your code looks amazing. I would love to try it out.
The problem is, like in #8, I'm more of an ESPhome kinda guy and I never worked with Arduino IDE.

I'm learning the very basics right now but cannot get a solid grip on things.

Would you be open to describing in a bit more detail how to actually set all the variables needed (pins, WiFi, etc.), compile the code and upload it to an ESP board? I'm sure a lot of people will thank you for that, if they come across your repo :)

Thanks and keep up the great work!

Compile error advanced thermostat ino

Hi Jeroen,

First the connection INO works, i have contact with my boiler (yeah!)
Getting the data id's is also working
I have a ESP32-Wroom-32 (or other esp) and I get some errors during compiling.
Im not so very advanced with arduino ide etc....

I want to use the advanced thermostat.ino

The first is this one
I cannot find the declare for below error. if i remove i get other errors

C:\Users\kn\Documents\Arduino\Advanced_Thermostat\Advanced_Thermostat.ino: In function 'void setup()':
C:\Users\kns\Documents\Arduino\Advanced_Thermostat\Advanced_Thermostat.ino:425:18: error: 'LED_BUILTIN' was not declared in this scope
digitalWrite(LED_BUILTIN, digitalRead(LED_BUILTIN) == LOW ? HIGH : LOW);
^~~~~~~~~~~
C:\Users\kns\Documents\Arduino\Advanced_Thermostat\Advanced_Thermostat.ino:434:16: error: 'LED_BUILTIN' was not declared in this scope
digitalWrite(LED_BUILTIN, LOW);
^~~~~~~~~~~

exit status 1

Compilation error: 'LED_BUILTIN' was not declared in this scope
I can remove these but get diff error, will show later :-)

also not sure what to do with this: do i need to fill this? guess not
#if !defined(ESP32) && !defined(ESP_LOGE)
#define ESP_LOGE(...)
#define ESP_LOGI(...)
#define ESP_LOGV(...)
#endif

thanks very much

Will this work on my analog boiler?

Hi,

We have an old boiler that does not support the OpenTherm protocol, but is just an on/off one. Can we still use this on a Wemos D1 mini and connect the 2 boiler wires to the rx/tx on the shield?

In the basic thermostat example, there is this piece of formation. But it does not recognize the current temperature and keeps the heating on indefinitely, even if the setpoint is reached by the onboard temperature sensor.

 *    To keep an OpenTherm compatible boiler in OpenTherm mode a message should be send to the boiler by the thermostat at least every second according to the OpenTherm specification.
 *    If the boiler does not receive a message every second it falls back to on/off mode.
 *    This is implemented in this example by running the main loop every second.

Would love some help here to make our analog boiler smart. :)

ESPHome support

Hi, are you considering support for an ESPHome module?

This would increase your potential market by a lot...

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.