11#include "driver/timer.h" 
   25static const char *
const TAG = 
"opentherm";
 
   28OpenTherm *OpenTherm::instance = 
nullptr;
 
   35      timer_group_(TIMER_GROUP_0),
 
   45      device_timeout_(device_timeout) {
 
   46  this->isr_in_pin_ = in_pin->
to_isr();
 
   47  this->isr_out_pin_ = out_pin->
to_isr();
 
 
   52  OpenTherm::instance = 
this;
 
   55  this->in_pin_->
setup();
 
   57  this->out_pin_->
setup();
 
   61  return this->init_esp32_timer_();
 
 
   69  this->timeout_counter_ = this->device_timeout_ * 5;  
 
   75  this->start_read_timer_();
 
 
   80  this->data_ = data.
type;
 
   81  this->data_ = (this->data_ << 12) | data.
id;
 
   82  this->data_ = (this->data_ << 8) | data.
valueHB;
 
   83  this->data_ = (this->data_ << 8) | data.
valueLB;
 
   84  if (!check_parity_(this->data_)) {
 
   85    this->data_ = this->data_ | 0x80000000;
 
   92  this->start_write_timer_();
 
 
   97    data.
type = (this->data_ >> 28) & 0x7;
 
   98    data.
id = (this->data_ >> 16) & 0xFF;
 
   99    data.
valueHB = (this->data_ >> 8) & 0xFF;
 
  100    data.
valueLB = this->data_ & 0xFF;
 
 
  112  error.
bit_pos = this->bit_pos_;
 
  113  error.
capture = this->capture_;
 
  114  error.
clock = this->clock_;
 
  115  error.
data = this->data_;
 
 
  125void IRAM_ATTR OpenTherm::read_() {
 
  131  this->start_read_timer_();  
 
  137    if (arg->timeout_counter_ == 0) {
 
  146    if (arg->timeout_counter_ > 0) {
 
  147      arg->timeout_counter_--;
 
  151    uint8_t 
const last = (arg->capture_ & 1);
 
  154      if (arg->clock_ == 1 && arg->capture_ > 0xF) {
 
  160      } 
else if (arg->clock_ == 1 || arg->capture_ > 0xF) {
 
  164          auto stop_bit_error = arg->verify_stop_bit_(last);
 
  172            arg->error_type_ = stop_bit_error;
 
  178          arg->bit_read_(last);
 
  186    } 
else if (arg->capture_ > 0xFF) {
 
  193    arg->capture_ = (arg->capture_ << 1) | value;
 
  196    if (arg->bit_pos_ == 33 || arg->bit_pos_ == 0) {  
 
  197      arg->write_bit_(1, arg->clock_);
 
  199      arg->write_bit_(
read_bit(arg->data_, arg->bit_pos_ - 1), arg->clock_);
 
  201    if (arg->clock_ == 0) {
 
  202      if (arg->bit_pos_ <= 0) {            
 
 
  220void IRAM_ATTR OpenTherm::bit_read_(uint8_t value) {
 
  221  this->data_ = (this->data_ << 1) | value;
 
  233void IRAM_ATTR OpenTherm::write_bit_(uint8_t high, uint8_t clock) {
 
  243bool OpenTherm::init_esp32_timer_() {
 
  246  timer_group_t timer_group = TIMER_GROUP_0;
 
  247  timer_idx_t timer_idx = TIMER_0;
 
  248  bool timer_found = 
false;
 
  250  for (; cur_timer < SOC_TIMER_GROUP_TOTAL_TIMERS; cur_timer++) {
 
  251    timer_config_t temp_config;
 
  252    timer_group = cur_timer < 2 ? TIMER_GROUP_0 : TIMER_GROUP_1;
 
  253    timer_idx = cur_timer < 2 ? (timer_idx_t) cur_timer : (timer_idx_t) (cur_timer - 2);
 
  255    auto err = timer_get_config(timer_group, timer_idx, &temp_config);
 
  256    if (err == ESP_ERR_INVALID_ARG) {
 
  262    ESP_LOGD(TAG, 
"Timer %d:%d seems to be occupied, will try another", timer_group, timer_idx);
 
  266    ESP_LOGE(TAG, 
"No free timer was found! OpenTherm cannot function without a timer.");
 
  270  ESP_LOGD(TAG, 
"Found free timer %d:%d", timer_group, timer_idx);
 
  271  this->timer_group_ = timer_group;
 
  272  this->timer_idx_ = timer_idx;
 
  274  timer_config_t 
const config = {
 
  275      .alarm_en = TIMER_ALARM_EN,
 
  276      .counter_en = TIMER_PAUSE,
 
  277      .intr_type = TIMER_INTR_LEVEL,
 
  278      .counter_dir = TIMER_COUNT_UP,
 
  279      .auto_reload = TIMER_AUTORELOAD_EN,
 
  280      .clk_src = TIMER_SRC_CLK_DEFAULT,
 
  286  result = timer_init(this->timer_group_, this->timer_idx_, &config);
 
  287  if (result != ESP_OK) {
 
  288    const auto *error = esp_err_to_name(result);
 
  289    ESP_LOGE(TAG, 
"Failed to init timer. Error: %s", error);
 
  293  result = timer_set_counter_value(this->timer_group_, this->timer_idx_, 0);
 
  294  if (result != ESP_OK) {
 
  295    const auto *error = esp_err_to_name(result);
 
  296    ESP_LOGE(TAG, 
"Failed to set counter value. Error: %s", error);
 
  300  result = timer_isr_callback_add(this->timer_group_, this->timer_idx_, 
reinterpret_cast<bool (*)(
void *)
>(
timer_isr),
 
  302  if (result != ESP_OK) {
 
  303    const auto *error = esp_err_to_name(result);
 
  304    ESP_LOGE(TAG, 
"Failed to register timer interrupt. Error: %s", error);
 
  311void IRAM_ATTR OpenTherm::start_esp32_timer_(uint64_t alarm_value) {
 
  313  this->timer_error_ = ESP_OK;
 
  316  this->timer_error_ = timer_set_alarm_value(this->timer_group_, this->timer_idx_, alarm_value);
 
  317  if (this->timer_error_ != ESP_OK) {
 
  321  this->timer_error_ = timer_start(this->timer_group_, this->timer_idx_);
 
  322  if (this->timer_error_ != ESP_OK) {
 
  328  if (this->timer_error_ == ESP_OK) {
 
  332  ESP_LOGE(TAG, 
"Error occured while manipulating timer (%s): %s", this->
timer_error_to_str(this->timer_error_type_),
 
  333           esp_err_to_name(this->timer_error_));
 
  335  this->timer_error_ = ESP_OK;
 
 
  340void IRAM_ATTR OpenTherm::start_read_timer_() {
 
  342  this->start_esp32_timer_(200);
 
  346void IRAM_ATTR OpenTherm::start_write_timer_() {
 
  348  this->start_esp32_timer_(500);
 
  351void IRAM_ATTR OpenTherm::stop_timer_() {
 
  352  InterruptLock 
const lock;
 
  354  this->timer_error_ = ESP_OK;
 
  357  this->timer_error_ = timer_pause(this->timer_group_, this->timer_idx_);
 
  358  if (this->timer_error_ != ESP_OK) {
 
  362  this->timer_error_ = timer_set_counter_value(this->timer_group_, this->timer_idx_, 0);
 
  363  if (this->timer_error_ != ESP_OK) {
 
  372void IRAM_ATTR OpenTherm::start_read_timer_() {
 
  373  InterruptLock 
const lock;
 
  375  timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP);  
 
  380void IRAM_ATTR OpenTherm::start_write_timer_() {
 
  381  InterruptLock 
const lock;
 
  383  timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP);  
 
  387void IRAM_ATTR OpenTherm::stop_timer_() {
 
  388  InterruptLock 
const lock;
 
  390  timer1_detachInterrupt();
 
  399bool IRAM_ATTR OpenTherm::check_parity_(uint32_t 
val) {
 
  408#define TO_STRING_MEMBER(name) \ 
  414    TO_STRING_MEMBER(
IDLE)
 
  416    TO_STRING_MEMBER(
READ)
 
  418    TO_STRING_MEMBER(
WRITE)
 
  419    TO_STRING_MEMBER(
SENT)
 
 
  428  switch (error_type) {
 
 
  439  switch (error_type) {
 
 
  450  switch (message_type) {
 
 
  486    TO_STRING_MEMBER(
DATE)
 
  487    TO_STRING_MEMBER(
YEAR)
 
 
  566  ESP_LOGD(TAG, 
"type: %s; id: %s; HB: %s; LB: %s; uint_16: %s; float: %s",
 
  568           to_string(data.
valueHB).c_str(), to_string(data.
valueLB).c_str(), to_string(data.
u16()).c_str(),
 
  569           to_string(data.
f88()).c_str());
 
 
  572  ESP_LOGD(TAG, 
"data: %s; clock: %s; capture: %s; bit_pos: %s", 
format_hex(error.
data).c_str(),
 
 
  581  uint16_t 
const value = this->
valueHB;
 
  582  return (value << 8) | this->
valueLB;
 
 
  587  this->
valueHB = (value >> 8) & 0xFF;
 
 
  591  int16_t 
const value = this->
valueHB;
 
  592  return (value << 8) | this->
valueLB;
 
 
  597  this->
valueHB = (value >> 8) & 0xFF;
 
 
BedjetMode mode
BedJet operating mode.
virtual void pin_mode(gpio::Flags flags)=0
virtual void digital_write(bool value)=0
void digital_write(bool value)
virtual ISRInternalGPIOPin to_isr() const =0
Helper class to disable interrupts.
Opentherm static class that supports either listening or sending Opentherm data packets in the same t...
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.
void report_and_reset_timer_error()
bool get_message(OpenthermData &data)
Use this to retrive data packed captured by listen() function.
static bool timer_isr(OpenTherm *arg)
void send(OpenthermData &data)
Immediately send out Opentherm data packet to line connected on given pin.
const char * timer_error_to_str(TimerErrorType error_type)
bool initialize()
Setup pins.
const char * operation_mode_to_str(OperationMode mode)
OpenTherm(InternalGPIOPin *in_pin, InternalGPIOPin *out_pin, int32_t device_timeout=800)
void debug_data(OpenthermData &data)
const char * protocol_error_to_str(ProtocolErrorType error_type)
static void esp8266_timer_isr()
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.
@ SET_COUNTER_VALUE_ERROR
constexpr T read_bit(T value, uint8_t bit)
@ REMOTE_VENTILATION_PARAM
Providing packet encoding functions for exchanging data with a remote host.
std::string format_hex(const uint8_t *data, size_t length)
Format the byte array data of length len in lowercased hex.
std::string format_bin(const uint8_t *data, size_t length)
Format the byte array data of length len in binary.
ProtocolErrorType error_type
Structure to hold Opentherm data packet content.