ESPHome  2024.11.1
opentherm.cpp
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 #include "opentherm.h"
9 #include "esphome/core/helpers.h"
10 #if defined(ESP32) || defined(USE_ESP_IDF)
11 #include "driver/timer.h"
12 #include "esp_err.h"
13 #endif
14 #ifdef ESP8266
15 #include "Arduino.h"
16 #endif
17 #include <string>
18 #include <sstream>
19 #include <bitset>
20 
21 namespace esphome {
22 namespace opentherm {
23 
24 using std::string;
25 using std::bitset;
26 using std::stringstream;
27 using std::to_string;
28 
29 static const char *const TAG = "opentherm";
30 
31 #ifdef ESP8266
32 OpenTherm *OpenTherm::instance_ = nullptr;
33 #endif
34 
35 OpenTherm::OpenTherm(InternalGPIOPin *in_pin, InternalGPIOPin *out_pin, int32_t device_timeout)
36  : in_pin_(in_pin),
37  out_pin_(out_pin),
38 #if defined(ESP32) || defined(USE_ESP_IDF)
39  timer_group_(TIMER_GROUP_0),
40  timer_idx_(TIMER_0),
41 #endif
42  mode_(OperationMode::IDLE),
43  error_type_(ProtocolErrorType::NO_ERROR),
44  capture_(0),
45  clock_(0),
46  data_(0),
47  bit_pos_(0),
48  timeout_counter_(-1),
49  device_timeout_(device_timeout) {
50  this->isr_in_pin_ = in_pin->to_isr();
51  this->isr_out_pin_ = out_pin->to_isr();
52 }
53 
55 #ifdef ESP8266
56  OpenTherm::instance_ = this;
57 #endif
58  this->in_pin_->pin_mode(gpio::FLAG_INPUT);
59  this->out_pin_->pin_mode(gpio::FLAG_OUTPUT);
60  this->out_pin_->digital_write(true);
61 
62 #if defined(ESP32) || defined(USE_ESP_IDF)
63  return this->init_esp32_timer_();
64 #else
65  return true;
66 #endif
67 }
68 
70  this->stop_timer_();
71  this->timeout_counter_ = this->device_timeout_ * 5; // timer_ ticks at 5 ticks/ms
72 
73  this->mode_ = OperationMode::LISTEN;
74  this->data_ = 0;
75  this->bit_pos_ = 0;
76 
77  this->start_read_timer_();
78 }
79 
81  this->stop_timer_();
82  this->data_ = data.type;
83  this->data_ = (this->data_ << 12) | data.id;
84  this->data_ = (this->data_ << 8) | data.valueHB;
85  this->data_ = (this->data_ << 8) | data.valueLB;
86  if (!check_parity_(this->data_)) {
87  this->data_ = this->data_ | 0x80000000;
88  }
89 
90  this->clock_ = 1; // clock starts at HIGH
91  this->bit_pos_ = 33; // count down (33 == start bit, 32-1 data, 0 == stop bit)
92  this->mode_ = OperationMode::WRITE;
93 
94  this->start_write_timer_();
95 }
96 
98  if (this->mode_ == OperationMode::RECEIVED) {
99  data.type = (this->data_ >> 28) & 0x7;
100  data.id = (this->data_ >> 16) & 0xFF;
101  data.valueHB = (this->data_ >> 8) & 0xFF;
102  data.valueLB = this->data_ & 0xFF;
103  return true;
104  }
105  return false;
106 }
107 
109  if (this->mode_ != OperationMode::ERROR_PROTOCOL) {
110  return false;
111  }
112 
113  error.error_type = this->error_type_;
114  error.bit_pos = this->bit_pos_;
115  error.capture = this->capture_;
116  error.clock = this->clock_;
117  error.data = this->data_;
118 
119  return true;
120 }
121 
123  this->stop_timer_();
124  this->mode_ = OperationMode::IDLE;
125 }
126 
127 void IRAM_ATTR OpenTherm::read_() {
128  this->data_ = 0;
129  this->bit_pos_ = 0;
130  this->mode_ = OperationMode::READ;
131  this->capture_ = 1; // reset counter and add as if read start bit
132  this->clock_ = 1; // clock is high at the start of comm
133  this->start_read_timer_(); // get us into 1/4 of manchester code. 5 timer ticks constitute 1 ms, which is 1 bit
134  // period in OpenTherm.
135 }
136 
137 bool IRAM_ATTR OpenTherm::timer_isr(OpenTherm *arg) {
138  if (arg->mode_ == OperationMode::LISTEN) {
139  if (arg->timeout_counter_ == 0) {
140  arg->mode_ = OperationMode::ERROR_TIMEOUT;
141  arg->stop_timer_();
142  return false;
143  }
144  bool const value = arg->isr_in_pin_.digital_read();
145  if (value) { // incoming data (rising signal)
146  arg->read_();
147  }
148  if (arg->timeout_counter_ > 0) {
149  arg->timeout_counter_--;
150  }
151  } else if (arg->mode_ == OperationMode::READ) {
152  bool const value = arg->isr_in_pin_.digital_read();
153  uint8_t const last = (arg->capture_ & 1);
154  if (value != last) {
155  // transition of signal from last sampling
156  if (arg->clock_ == 1 && arg->capture_ > 0xF) {
157  // no transition in the middle of the bit
158  arg->mode_ = OperationMode::ERROR_PROTOCOL;
159  arg->error_type_ = ProtocolErrorType::NO_TRANSITION;
160  arg->stop_timer_();
161  return false;
162  } else if (arg->clock_ == 1 || arg->capture_ > 0xF) {
163  // transition in the middle of the bit OR no transition between two bit, both are valid data points
164  if (arg->bit_pos_ == BitPositions::STOP_BIT) {
165  // expecting stop bit
166  auto stop_bit_error = arg->verify_stop_bit_(last);
167  if (stop_bit_error == ProtocolErrorType::NO_ERROR) {
168  arg->mode_ = OperationMode::RECEIVED;
169  arg->stop_timer_();
170  return false;
171  } else {
172  // end of data not verified, invalid data
173  arg->mode_ = OperationMode::ERROR_PROTOCOL;
174  arg->error_type_ = stop_bit_error;
175  arg->stop_timer_();
176  return false;
177  }
178  } else {
179  // normal data point at clock high
180  arg->bit_read_(last);
181  arg->clock_ = 0;
182  }
183  } else {
184  // clock low, not a data point, switch clock
185  arg->clock_ = 1;
186  }
187  arg->capture_ = 1; // reset counter
188  } else if (arg->capture_ > 0xFF) {
189  // no change for too long, invalid mancheter encoding
190  arg->mode_ = OperationMode::ERROR_PROTOCOL;
191  arg->error_type_ = ProtocolErrorType::NO_CHANGE_TOO_LONG;
192  arg->stop_timer_();
193  return false;
194  }
195  arg->capture_ = (arg->capture_ << 1) | value;
196  } else if (arg->mode_ == OperationMode::WRITE) {
197  // write data to pin
198  if (arg->bit_pos_ == 33 || arg->bit_pos_ == 0) { // start bit
199  arg->write_bit_(1, arg->clock_);
200  } else { // data bits
201  arg->write_bit_(read_bit(arg->data_, arg->bit_pos_ - 1), arg->clock_);
202  }
203  if (arg->clock_ == 0) {
204  if (arg->bit_pos_ <= 0) { // check termination
205  arg->mode_ = OperationMode::SENT; // all data written
206  arg->stop_timer_();
207  }
208  arg->bit_pos_--;
209  arg->clock_ = 1;
210  } else {
211  arg->clock_ = 0;
212  }
213  }
214 
215  return false;
216 }
217 
218 #ifdef ESP8266
219 void IRAM_ATTR OpenTherm::esp8266_timer_isr() { OpenTherm::timer_isr(OpenTherm::instance_); }
220 #endif
221 
222 void IRAM_ATTR OpenTherm::bit_read_(uint8_t value) {
223  this->data_ = (this->data_ << 1) | value;
224  this->bit_pos_++;
225 }
226 
227 ProtocolErrorType OpenTherm::verify_stop_bit_(uint8_t value) {
228  if (value) { // stop bit detected
229  return check_parity_(this->data_) ? ProtocolErrorType::NO_ERROR : ProtocolErrorType::PARITY_ERROR;
230  } else { // no stop bit detected, error
232  }
233 }
234 
235 void IRAM_ATTR OpenTherm::write_bit_(uint8_t high, uint8_t clock) {
236  if (clock == 1) { // left part of manchester encoding
237  this->isr_out_pin_.digital_write(!high); // low means logical 1 to protocol
238  } else { // right part of manchester encoding
239  this->isr_out_pin_.digital_write(high); // high means logical 0 to protocol
240  }
241 }
242 
243 #if defined(ESP32) || defined(USE_ESP_IDF)
244 
245 bool OpenTherm::init_esp32_timer_() {
246  // Search for a free timer. Maybe unstable, we'll see.
247  int cur_timer = 0;
248  timer_group_t timer_group = TIMER_GROUP_0;
249  timer_idx_t timer_idx = TIMER_0;
250  bool timer_found = false;
251 
252  for (; cur_timer < SOC_TIMER_GROUP_TOTAL_TIMERS; cur_timer++) {
253  timer_config_t temp_config;
254  timer_group = cur_timer < 2 ? TIMER_GROUP_0 : TIMER_GROUP_1;
255  timer_idx = cur_timer < 2 ? (timer_idx_t) cur_timer : (timer_idx_t) (cur_timer - 2);
256 
257  auto err = timer_get_config(timer_group, timer_idx, &temp_config);
258  if (err == ESP_ERR_INVALID_ARG) {
259  // Error means timer was not initialized (or other things, but we are careful with our args)
260  timer_found = true;
261  break;
262  }
263 
264  ESP_LOGD(TAG, "Timer %d:%d seems to be occupied, will try another", timer_group, timer_idx);
265  }
266 
267  if (!timer_found) {
268  ESP_LOGE(TAG, "No free timer was found! OpenTherm cannot function without a timer.");
269  return false;
270  }
271 
272  ESP_LOGD(TAG, "Found free timer %d:%d", timer_group, timer_idx);
273  this->timer_group_ = timer_group;
274  this->timer_idx_ = timer_idx;
275 
276  timer_config_t const config = {
277  .alarm_en = TIMER_ALARM_EN,
278  .counter_en = TIMER_PAUSE,
279  .intr_type = TIMER_INTR_LEVEL,
280  .counter_dir = TIMER_COUNT_UP,
281  .auto_reload = TIMER_AUTORELOAD_EN,
282 #if ESP_IDF_VERSION_MAJOR >= 5
283  .clk_src = TIMER_SRC_CLK_DEFAULT,
284 #endif
285  .divider = 80,
286 #if defined(SOC_TIMER_GROUP_SUPPORT_XTAL) && ESP_IDF_VERSION_MAJOR < 5
287  .clk_src = TIMER_SRC_CLK_APB
288 #endif
289  };
290 
291  esp_err_t result;
292 
293  result = timer_init(this->timer_group_, this->timer_idx_, &config);
294  if (result != ESP_OK) {
295  const auto *error = esp_err_to_name(result);
296  ESP_LOGE(TAG, "Failed to init timer. Error: %s", error);
297  return false;
298  }
299 
300  result = timer_set_counter_value(this->timer_group_, this->timer_idx_, 0);
301  if (result != ESP_OK) {
302  const auto *error = esp_err_to_name(result);
303  ESP_LOGE(TAG, "Failed to set counter value. Error: %s", error);
304  return false;
305  }
306 
307  result = timer_isr_callback_add(this->timer_group_, this->timer_idx_, reinterpret_cast<bool (*)(void *)>(timer_isr),
308  this, 0);
309  if (result != ESP_OK) {
310  const auto *error = esp_err_to_name(result);
311  ESP_LOGE(TAG, "Failed to register timer interrupt. Error: %s", error);
312  return false;
313  }
314 
315  return true;
316 }
317 
318 void IRAM_ATTR OpenTherm::start_esp32_timer_(uint64_t alarm_value) {
319  esp_err_t result;
320 
321  result = timer_set_alarm_value(this->timer_group_, this->timer_idx_, alarm_value);
322  if (result != ESP_OK) {
323  const auto *error = esp_err_to_name(result);
324  ESP_LOGE(TAG, "Failed to set alarm value. Error: %s", error);
325  return;
326  }
327 
328  result = timer_start(this->timer_group_, this->timer_idx_);
329  if (result != ESP_OK) {
330  const auto *error = esp_err_to_name(result);
331  ESP_LOGE(TAG, "Failed to start the timer. Error: %s", error);
332  return;
333  }
334 }
335 
336 // 5 kHz timer_
337 void IRAM_ATTR OpenTherm::start_read_timer_() {
338  InterruptLock const lock;
339  this->start_esp32_timer_(200);
340 }
341 
342 // 2 kHz timer_
343 void IRAM_ATTR OpenTherm::start_write_timer_() {
344  InterruptLock const lock;
345  this->start_esp32_timer_(500);
346 }
347 
348 void IRAM_ATTR OpenTherm::stop_timer_() {
349  InterruptLock const lock;
350 
351  esp_err_t result;
352 
353  result = timer_pause(this->timer_group_, this->timer_idx_);
354  if (result != ESP_OK) {
355  const auto *error = esp_err_to_name(result);
356  ESP_LOGE(TAG, "Failed to pause the timer. Error: %s", error);
357  return;
358  }
359 
360  result = timer_set_counter_value(this->timer_group_, this->timer_idx_, 0);
361  if (result != ESP_OK) {
362  const auto *error = esp_err_to_name(result);
363  ESP_LOGE(TAG, "Failed to set timer counter to 0 after pausing. Error: %s", error);
364  return;
365  }
366 }
367 
368 #endif // END ESP32
369 
370 #ifdef ESP8266
371 // 5 kHz timer_
372 void OpenTherm::start_read_timer_() {
373  InterruptLock const lock;
374  timer1_attachInterrupt(OpenTherm::esp8266_timer_isr);
375  timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP); // 5MHz (5 ticks/us - 1677721.4 us max)
376  timer1_write(1000); // 5kHz
377 }
378 
379 // 2 kHz timer_
380 void OpenTherm::start_write_timer_() {
381  InterruptLock const lock;
382  timer1_attachInterrupt(OpenTherm::esp8266_timer_isr);
383  timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP); // 5MHz (5 ticks/us - 1677721.4 us max)
384  timer1_write(2500); // 2kHz
385 }
386 
387 void OpenTherm::stop_timer_() {
388  InterruptLock const lock;
389  timer1_disable();
390  timer1_detachInterrupt();
391 }
392 
393 #endif // END ESP8266
394 
395 // https://stackoverflow.com/questions/21617970/how-to-check-if-value-has-even-parity-of-bits-or-odd
396 bool OpenTherm::check_parity_(uint32_t val) {
397  val ^= val >> 16;
398  val ^= val >> 8;
399  val ^= val >> 4;
400  val ^= val >> 2;
401  val ^= val >> 1;
402  return (~val) & 1;
403 }
404 
405 #define TO_STRING_MEMBER(name) \
406  case name: \
407  return #name;
408 
410  switch (mode) {
411  TO_STRING_MEMBER(IDLE)
412  TO_STRING_MEMBER(LISTEN)
413  TO_STRING_MEMBER(READ)
414  TO_STRING_MEMBER(RECEIVED)
415  TO_STRING_MEMBER(WRITE)
416  TO_STRING_MEMBER(SENT)
417  TO_STRING_MEMBER(ERROR_PROTOCOL)
418  TO_STRING_MEMBER(ERROR_TIMEOUT)
419  default:
420  return "<INVALID>";
421  }
422 }
424  switch (error_type) {
425  TO_STRING_MEMBER(NO_ERROR)
426  TO_STRING_MEMBER(NO_TRANSITION)
427  TO_STRING_MEMBER(INVALID_STOP_BIT)
428  TO_STRING_MEMBER(PARITY_ERROR)
429  TO_STRING_MEMBER(NO_CHANGE_TOO_LONG)
430  default:
431  return "<INVALID>";
432  }
433 }
434 const char *OpenTherm::message_type_to_str(MessageType message_type) {
435  switch (message_type) {
436  TO_STRING_MEMBER(READ_DATA)
437  TO_STRING_MEMBER(READ_ACK)
438  TO_STRING_MEMBER(WRITE_DATA)
439  TO_STRING_MEMBER(WRITE_ACK)
440  TO_STRING_MEMBER(INVALID_DATA)
441  TO_STRING_MEMBER(DATA_INVALID)
442  TO_STRING_MEMBER(UNKNOWN_DATAID)
443  default:
444  return "<INVALID>";
445  }
446 }
447 
449  switch (id) {
450  TO_STRING_MEMBER(STATUS)
451  TO_STRING_MEMBER(CH_SETPOINT)
452  TO_STRING_MEMBER(CONTROLLER_CONFIG)
453  TO_STRING_MEMBER(DEVICE_CONFIG)
454  TO_STRING_MEMBER(COMMAND_CODE)
455  TO_STRING_MEMBER(FAULT_FLAGS)
456  TO_STRING_MEMBER(REMOTE)
457  TO_STRING_MEMBER(COOLING_CONTROL)
458  TO_STRING_MEMBER(CH2_SETPOINT)
459  TO_STRING_MEMBER(CH_SETPOINT_OVERRIDE)
460  TO_STRING_MEMBER(TSP_COUNT)
461  TO_STRING_MEMBER(TSP_COMMAND)
462  TO_STRING_MEMBER(FHB_SIZE)
463  TO_STRING_MEMBER(FHB_COMMAND)
464  TO_STRING_MEMBER(MAX_MODULATION_LEVEL)
465  TO_STRING_MEMBER(MAX_BOILER_CAPACITY)
466  TO_STRING_MEMBER(ROOM_SETPOINT)
467  TO_STRING_MEMBER(MODULATION_LEVEL)
468  TO_STRING_MEMBER(CH_WATER_PRESSURE)
469  TO_STRING_MEMBER(DHW_FLOW_RATE)
470  TO_STRING_MEMBER(DAY_TIME)
471  TO_STRING_MEMBER(DATE)
472  TO_STRING_MEMBER(YEAR)
473  TO_STRING_MEMBER(ROOM_SETPOINT_CH2)
474  TO_STRING_MEMBER(ROOM_TEMP)
475  TO_STRING_MEMBER(FEED_TEMP)
476  TO_STRING_MEMBER(DHW_TEMP)
477  TO_STRING_MEMBER(OUTSIDE_TEMP)
478  TO_STRING_MEMBER(RETURN_WATER_TEMP)
479  TO_STRING_MEMBER(SOLAR_STORE_TEMP)
480  TO_STRING_MEMBER(SOLAR_COLLECT_TEMP)
481  TO_STRING_MEMBER(FEED_TEMP_CH2)
482  TO_STRING_MEMBER(DHW2_TEMP)
483  TO_STRING_MEMBER(EXHAUST_TEMP)
484  TO_STRING_MEMBER(FAN_SPEED)
485  TO_STRING_MEMBER(FLAME_CURRENT)
486  TO_STRING_MEMBER(ROOM_TEMP_CH2)
487  TO_STRING_MEMBER(REL_HUMIDITY)
488  TO_STRING_MEMBER(DHW_BOUNDS)
489  TO_STRING_MEMBER(CH_BOUNDS)
490  TO_STRING_MEMBER(OTC_CURVE_BOUNDS)
491  TO_STRING_MEMBER(DHW_SETPOINT)
492  TO_STRING_MEMBER(MAX_CH_SETPOINT)
493  TO_STRING_MEMBER(OTC_CURVE_RATIO)
494  TO_STRING_MEMBER(HVAC_STATUS)
495  TO_STRING_MEMBER(REL_VENT_SETPOINT)
496  TO_STRING_MEMBER(DEVICE_VENT)
497  TO_STRING_MEMBER(HVAC_VER_ID)
498  TO_STRING_MEMBER(REL_VENTILATION)
499  TO_STRING_MEMBER(REL_HUMID_EXHAUST)
500  TO_STRING_MEMBER(EXHAUST_CO2)
501  TO_STRING_MEMBER(SUPPLY_INLET_TEMP)
502  TO_STRING_MEMBER(SUPPLY_OUTLET_TEMP)
503  TO_STRING_MEMBER(EXHAUST_INLET_TEMP)
504  TO_STRING_MEMBER(EXHAUST_OUTLET_TEMP)
505  TO_STRING_MEMBER(EXHAUST_FAN_SPEED)
506  TO_STRING_MEMBER(SUPPLY_FAN_SPEED)
507  TO_STRING_MEMBER(REMOTE_VENTILATION_PARAM)
508  TO_STRING_MEMBER(NOM_REL_VENTILATION)
509  TO_STRING_MEMBER(HVAC_NUM_TSP)
510  TO_STRING_MEMBER(HVAC_IDX_TSP)
511  TO_STRING_MEMBER(HVAC_FHB_SIZE)
512  TO_STRING_MEMBER(HVAC_FHB_IDX)
513  TO_STRING_MEMBER(RF_SIGNAL)
514  TO_STRING_MEMBER(DHW_MODE)
515  TO_STRING_MEMBER(OVERRIDE_FUNC)
516  TO_STRING_MEMBER(SOLAR_MODE_FLAGS)
517  TO_STRING_MEMBER(SOLAR_ASF)
518  TO_STRING_MEMBER(SOLAR_VERSION_ID)
519  TO_STRING_MEMBER(SOLAR_PRODUCT_ID)
520  TO_STRING_MEMBER(SOLAR_NUM_TSP)
521  TO_STRING_MEMBER(SOLAR_IDX_TSP)
522  TO_STRING_MEMBER(SOLAR_FHB_SIZE)
523  TO_STRING_MEMBER(SOLAR_FHB_IDX)
524  TO_STRING_MEMBER(SOLAR_STARTS)
525  TO_STRING_MEMBER(SOLAR_HOURS)
526  TO_STRING_MEMBER(SOLAR_ENERGY)
527  TO_STRING_MEMBER(SOLAR_TOTAL_ENERGY)
528  TO_STRING_MEMBER(FAILED_BURNER_STARTS)
529  TO_STRING_MEMBER(BURNER_FLAME_LOW)
530  TO_STRING_MEMBER(OEM_DIAGNOSTIC)
531  TO_STRING_MEMBER(BURNER_STARTS)
532  TO_STRING_MEMBER(CH_PUMP_STARTS)
533  TO_STRING_MEMBER(DHW_PUMP_STARTS)
534  TO_STRING_MEMBER(DHW_BURNER_STARTS)
535  TO_STRING_MEMBER(BURNER_HOURS)
536  TO_STRING_MEMBER(CH_PUMP_HOURS)
537  TO_STRING_MEMBER(DHW_PUMP_HOURS)
538  TO_STRING_MEMBER(DHW_BURNER_HOURS)
539  TO_STRING_MEMBER(OT_VERSION_CONTROLLER)
540  TO_STRING_MEMBER(OT_VERSION_DEVICE)
541  TO_STRING_MEMBER(VERSION_CONTROLLER)
542  TO_STRING_MEMBER(VERSION_DEVICE)
543  default:
544  return "<INVALID>";
545  }
546 }
547 
549  stringstream result;
550  result << bitset<8>(data.type) << " " << bitset<8>(data.id) << " " << bitset<8>(data.valueHB) << " "
551  << bitset<8>(data.valueLB) << "\n";
552  result << "type: " << this->message_type_to_str((MessageType) data.type) << "; ";
553  result << "id: " << to_string(data.id) << "; ";
554  result << "HB: " << to_string(data.valueHB) << "; ";
555  result << "LB: " << to_string(data.valueLB) << "; ";
556  result << "uint_16: " << to_string(data.u16()) << "; ";
557  result << "float: " << to_string(data.f88());
558 
559  return result.str();
560 }
562  stringstream result;
563  result << "type: " << this->protocol_error_to_to_str(error.error_type) << "; ";
564  result << "data: ";
565  result << format_hex(error.data);
566  result << "; clock: " << to_string(clock_);
567  result << "; capture: " << bitset<32>(error.capture);
568  result << "; bit_pos: " << to_string(error.bit_pos);
569 
570  return result.str();
571 }
572 
573 float OpenthermData::f88() { return ((float) this->s16()) / 256.0; }
574 
575 void OpenthermData::f88(float value) { this->s16((int16_t) (value * 256)); }
576 
577 uint16_t OpenthermData::u16() {
578  uint16_t const value = this->valueHB;
579  return (value << 8) | this->valueLB;
580 }
581 
582 void OpenthermData::u16(uint16_t value) {
583  this->valueLB = value & 0xFF;
584  this->valueHB = (value >> 8) & 0xFF;
585 }
586 
588  int16_t const value = this->valueHB;
589  return (value << 8) | this->valueLB;
590 }
591 
592 void OpenthermData::s16(int16_t value) {
593  this->valueLB = value & 0xFF;
594  this->valueHB = (value >> 8) & 0xFF;
595 }
596 
597 } // namespace opentherm
598 } // namespace esphome
void listen()
Start listening for Opentherm data packet comming from line connected to given pin.
Definition: opentherm.cpp:69
virtual void digital_write(bool value)=0
constexpr T read_bit(T value, uint8_t bit)
Definition: opentherm.h:23
std::string format_hex(const uint8_t *data, size_t length)
Format the byte array data of length len in lowercased hex.
Definition: helpers.cpp:352
void send(OpenthermData &data)
Immediately send out Opentherm data packet to line connected on given pin.
Definition: opentherm.cpp:80
const char * to_string(SHTCXType type)
Definition: shtcx.cpp:16
virtual void pin_mode(gpio::Flags flags)=0
void stop()
Stops listening for data packet or sending out data packet and resets internal state of this class...
Definition: opentherm.cpp:122
const char * operation_mode_to_str(OperationMode mode)
Definition: opentherm.cpp:409
mopeka_std_values val[4]
bool get_message(OpenthermData &data)
Use this to retrive data packed captured by listen() function.
Definition: opentherm.cpp:97
timeout while waiting to receive bytes
Definition: i2c_bus.h:16
No error found during execution of method.
Definition: i2c_bus.h:12
static bool timer_isr(OpenTherm *arg)
Definition: opentherm.cpp:137
const char * message_id_to_str(MessageId id)
Definition: opentherm.cpp:448
Opentherm static class that supports either listening or sending Opentherm data packets in the same t...
Definition: opentherm.h:226
BedjetMode mode
BedJet operating mode.
Definition: bedjet_codec.h:183
OpenTherm(InternalGPIOPin *in_pin, InternalGPIOPin *out_pin, int32_t device_timeout=800)
Definition: opentherm.cpp:35
std::string debug_data(OpenthermData &data)
Definition: opentherm.cpp:548
virtual ISRInternalGPIOPin to_isr() const =0
Helper class to disable interrupts.
Definition: helpers.h:597
std::string to_string(int value)
Definition: helpers.cpp:81
const char * protocol_error_to_to_str(ProtocolErrorType error_type)
Definition: opentherm.cpp:423
bool initialize()
Setup pins.
Definition: opentherm.cpp:54
const char * message_type_to_str(MessageType message_type)
Definition: opentherm.cpp:434
bool get_protocol_error(OpenThermError &error)
Get protocol error details in case a protocol error occured.
Definition: opentherm.cpp:108
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
Structure to hold Opentherm data packet content.
Definition: opentherm.h:176
std::string debug_error(OpenThermError &error)
Definition: opentherm.cpp:561
void digital_write(bool value)
Definition: gpio.cpp:121