ESPHome 2025.5.0
Loading...
Searching...
No Matches
opentherm.h
Go to the documentation of this file.
1/*
2 * OpenTherm protocol implementation. Originally taken from https://github.com/jpraus/arduino-opentherm, but
3 * heavily modified to comply with ESPHome coding standards and provide better logging.
4 * Original code is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
5 * Public License, which is compatible with GPLv3 license, which covers C++ part of ESPHome project.
6 */
7
8#pragma once
9
10#include <string>
11#include "esphome/core/hal.h"
12#include "esphome/core/log.h"
14
15#if defined(ESP32) || defined(USE_ESP_IDF)
16#include "driver/timer.h"
17#endif
18
19namespace esphome {
20namespace opentherm {
21
22template<class T> constexpr T read_bit(T value, uint8_t bit) { return (value >> bit) & 0x01; }
23
24template<class T> constexpr T set_bit(T value, uint8_t bit) { return value |= (1UL << bit); }
25
26template<class T> constexpr T clear_bit(T value, uint8_t bit) { return value &= ~(1UL << bit); }
27
28template<class T> constexpr T write_bit(T value, uint8_t bit, uint8_t bit_value) {
29 return bit_value ? set_bit(value, bit) : clear_bit(value, bit);
30}
31
33 IDLE = 0, // no operation
34
35 LISTEN = 1, // waiting for transmission to start
36 READ = 2, // reading 32-bit data frame
37 RECEIVED = 3, // data frame received with valid start and stop bit
38
39 WRITE = 4, // writing data to output
40 SENT = 5, // all data written to output
41
42 ERROR_PROTOCOL = 8, // protocol error, can happed only during READ
43 ERROR_TIMEOUT = 9, // timeout while waiting for response from device, only during LISTEN
44 ERROR_TIMER = 10 // error operating the ESP32 timer
45};
46
48 NO_ERROR = 0, // No error
49 NO_TRANSITION = 1, // No transition in the middle of the bit
50 INVALID_STOP_BIT = 2, // Stop bit wasn't present when expected
51 PARITY_ERROR = 3, // Parity check didn't pass
52 NO_CHANGE_TOO_LONG = 4, // No level change for too much timer ticks
53};
54
56 NO_TIMER_ERROR = 0, // No error
57 SET_ALARM_VALUE_ERROR = 1, // No transition in the middle of the bit
58 TIMER_START_ERROR = 2, // Stop bit wasn't present when expected
59 TIMER_PAUSE_ERROR = 3, // Parity check didn't pass
60 SET_COUNTER_VALUE_ERROR = 4, // No level change for too much timer ticks
61};
62
72
74 STATUS = 0,
80 REMOTE = 6,
89 MAX_BOILER_CAPACITY = 15, // u8_hb - u8_lb gives min modulation level
95 DATE = 21,
96 YEAR = 22,
118
119 // HVAC Specific Message IDs
139
143
144 // Solar Specific Message IDs
145 SOLAR_MODE_FLAGS = 101, // hb0-2 Controller storage mode
146 // lb0 Device fault
147 // lb1-3 Device mode status
148 // lb4-5 Device status
160
175 VERSION_DEVICE = 127
177
178enum BitPositions { STOP_BIT = 33 };
179
185 uint8_t type;
186 uint8_t id;
187 uint8_t valueHB;
188 uint8_t valueLB;
189
190 OpenthermData() : type(0), id(0), valueHB(0), valueLB(0) {}
191
195 float f88();
196
200 void f88(float value);
201
205 uint16_t u16();
206
210 void u16(uint16_t value);
211
215 int16_t s16();
216
220 void s16(int16_t value);
221};
222
225 uint32_t capture;
226 uint8_t clock;
227 uint32_t data;
228 uint8_t bit_pos;
229};
230
235 public:
236 OpenTherm(InternalGPIOPin *in_pin, InternalGPIOPin *out_pin, int32_t device_timeout = 800);
237
241 bool initialize();
242
250 void listen();
251
257 bool has_message() { return mode_ == OperationMode::RECEIVED; }
258
266 bool get_message(OpenthermData &data);
267
275 void send(OpenthermData &data);
276
281 void stop();
282
289
295 bool is_sent() { return mode_ == OperationMode::SENT; }
296
303 bool is_idle() { return mode_ == OperationMode::IDLE; }
304
311 bool is_error() {
312 return mode_ == OperationMode::ERROR_TIMEOUT || mode_ == OperationMode::ERROR_PROTOCOL || mode_ == ERROR_TIMER;
313 }
314
319 bool is_timeout() { return mode_ == OperationMode::ERROR_TIMEOUT; }
320
326
331 bool is_timer_error() { return mode_ == OperationMode::ERROR_TIMER; }
332
333 bool is_active() { return mode_ == LISTEN || mode_ == READ || mode_ == WRITE; }
334
335 OperationMode get_mode() { return mode_; }
336
337 void debug_data(OpenthermData &data);
338 void debug_error(OpenThermError &error) const;
340
341 const char *protocol_error_to_str(ProtocolErrorType error_type);
342 const char *timer_error_to_str(TimerErrorType error_type);
343 const char *message_type_to_str(MessageType message_type);
345 const char *message_id_to_str(MessageId id);
346
347 static bool timer_isr(OpenTherm *arg);
348
349#ifdef ESP8266
350 static void esp8266_timer_isr();
351#endif
352
353 private:
354 InternalGPIOPin *in_pin_;
355 InternalGPIOPin *out_pin_;
356 ISRInternalGPIOPin isr_in_pin_;
357 ISRInternalGPIOPin isr_out_pin_;
358
359#if defined(ESP32) || defined(USE_ESP_IDF)
360 timer_group_t timer_group_;
361 timer_idx_t timer_idx_;
362#endif
363
364 OperationMode mode_;
365 ProtocolErrorType error_type_;
366 uint32_t capture_;
367 uint8_t clock_;
368 uint32_t data_;
369 uint8_t bit_pos_;
370 int32_t timeout_counter_; // <0 no timeout
371 int32_t device_timeout_;
372
373#if defined(ESP32) || defined(USE_ESP_IDF)
374 esp_err_t timer_error_ = ESP_OK;
376
377 bool init_esp32_timer_();
378 void start_esp32_timer_(uint64_t alarm_value);
379#endif
380
381 void stop_timer_();
382
383 void read_(); // data detected start reading
384 void start_read_timer_(); // reading timer_ to sample at 1/5 of manchester code bit length (at 5kHz)
385 void start_write_timer_(); // writing timer_ to send manchester code (at 2kHz)
386 bool check_parity_(uint32_t val);
387
388 void bit_read_(uint8_t value);
389 ProtocolErrorType verify_stop_bit_(uint8_t value);
390 void write_bit_(uint8_t high, uint8_t clock);
391
392#ifdef ESP8266
393 // ESP8266 timer can accept callback with no parameters, so we have this hack to save a static instance of OpenTherm
394 static OpenTherm *instance; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
395#endif
396};
397
398} // namespace opentherm
399} // namespace esphome
BedjetMode mode
BedJet operating mode.
Copy of GPIOPin that is safe to use from ISRs (with no virtual functions)
Definition gpio.h:73
Opentherm static class that supports either listening or sending Opentherm data packets in the same t...
Definition opentherm.h:234
bool is_timeout()
Indicates whether last listen() or send() operation ends up with a timeout error.
Definition opentherm.h:319
void debug_error(OpenThermError &error) const
const char * message_id_to_str(MessageId id)
void listen()
Start listening for Opentherm data packet comming from line connected to given pin.
Definition opentherm.cpp:67
bool is_idle()
Indicates whether listinig or sending is not in progress.
Definition opentherm.h:303
bool get_message(OpenthermData &data)
Use this to retrive data packed captured by listen() function.
Definition opentherm.cpp:95
static bool timer_isr(OpenTherm *arg)
void send(OpenthermData &data)
Immediately send out Opentherm data packet to line connected on given pin.
Definition opentherm.cpp:78
const char * timer_error_to_str(TimerErrorType error_type)
bool is_timer_error()
Indicates whether start_esp32_timer_() or stop_timer_() had an error.
Definition opentherm.h:331
bool initialize()
Setup pins.
Definition opentherm.cpp:50
bool is_sent()
Use this function to check whether send() function already finished sending data packed to line.
Definition opentherm.h:295
const char * operation_mode_to_str(OperationMode mode)
OpenTherm(InternalGPIOPin *in_pin, InternalGPIOPin *out_pin, int32_t device_timeout=800)
Definition opentherm.cpp:31
void debug_data(OpenthermData &data)
bool has_message()
Use this function to check whether listen() function already captured a valid data packet.
Definition opentherm.h:257
const char * protocol_error_to_str(ProtocolErrorType error_type)
bool is_protocol_error()
Indicates whether last listen() or send() operation ends up with a protocol error.
Definition opentherm.h:325
const char * message_type_to_str(MessageType message_type)
bool get_protocol_error(OpenThermError &error)
Get protocol error details in case a protocol error occured.
void stop()
Stops listening for data packet or sending out data packet and resets internal state of this class.
bool is_error()
Indicates whether last listen() or send() operation ends up with an error.
Definition opentherm.h:311
mopeka_std_values val[4]
constexpr T clear_bit(T value, uint8_t bit)
Definition opentherm.h:26
constexpr T write_bit(T value, uint8_t bit, uint8_t bit_value)
Definition opentherm.h:28
constexpr T set_bit(T value, uint8_t bit)
Definition opentherm.h:24
constexpr T read_bit(T value, uint8_t bit)
Definition opentherm.h:22
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
Structure to hold Opentherm data packet content.
Definition opentherm.h:184