10 #if defined(ESP32) || defined(USE_ESP_IDF) 11 #include "driver/timer.h" 25 static const char *
const TAG =
"opentherm";
28 OpenTherm *OpenTherm::instance =
nullptr;
34 #if defined(ESP32) || defined(USE_ESP_IDF)
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();
60 #if defined(ESP32) || defined(USE_ESP_IDF) 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_;
125 void 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) {
220 void IRAM_ATTR OpenTherm::bit_read_(uint8_t value) {
221 this->data_ = (this->data_ << 1) | value;
233 void IRAM_ATTR OpenTherm::write_bit_(uint8_t high, uint8_t clock) {
241 #if defined(ESP32) || defined(USE_ESP_IDF) 243 bool 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 #if ESP_IDF_VERSION_MAJOR >= 5 281 .clk_src = TIMER_SRC_CLK_DEFAULT,
284 #if defined(SOC_TIMER_GROUP_SUPPORT_XTAL) && ESP_IDF_VERSION_MAJOR < 5 285 .clk_src = TIMER_SRC_CLK_APB
291 result = timer_init(this->timer_group_, this->timer_idx_, &config);
292 if (result != ESP_OK) {
293 const auto *error = esp_err_to_name(result);
294 ESP_LOGE(TAG,
"Failed to init timer. Error: %s", error);
298 result = timer_set_counter_value(this->timer_group_, this->timer_idx_, 0);
299 if (result != ESP_OK) {
300 const auto *error = esp_err_to_name(result);
301 ESP_LOGE(TAG,
"Failed to set counter value. Error: %s", error);
305 result = timer_isr_callback_add(this->timer_group_, this->timer_idx_,
reinterpret_cast<bool (*)(
void *)
>(
timer_isr),
307 if (result != ESP_OK) {
308 const auto *error = esp_err_to_name(result);
309 ESP_LOGE(TAG,
"Failed to register timer interrupt. Error: %s", error);
316 void IRAM_ATTR OpenTherm::start_esp32_timer_(uint64_t alarm_value) {
318 this->timer_error_ = ESP_OK;
321 this->timer_error_ = timer_set_alarm_value(this->timer_group_, this->timer_idx_, alarm_value);
322 if (this->timer_error_ != ESP_OK) {
326 this->timer_error_ = timer_start(this->timer_group_, this->timer_idx_);
327 if (this->timer_error_ != ESP_OK) {
333 if (this->timer_error_ == ESP_OK) {
337 ESP_LOGE(TAG,
"Error occured while manipulating timer (%s): %s", this->
timer_error_to_str(this->timer_error_type_),
338 esp_err_to_name(this->timer_error_));
340 this->timer_error_ = ESP_OK;
345 void IRAM_ATTR OpenTherm::start_read_timer_() {
347 this->start_esp32_timer_(200);
351 void IRAM_ATTR OpenTherm::start_write_timer_() {
353 this->start_esp32_timer_(500);
356 void IRAM_ATTR OpenTherm::stop_timer_() {
359 this->timer_error_ = ESP_OK;
362 this->timer_error_ = timer_pause(this->timer_group_, this->timer_idx_);
363 if (this->timer_error_ != ESP_OK) {
367 this->timer_error_ = timer_set_counter_value(this->timer_group_, this->timer_idx_, 0);
368 if (this->timer_error_ != ESP_OK) {
377 void IRAM_ATTR OpenTherm::start_read_timer_() {
380 timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP);
385 void IRAM_ATTR OpenTherm::start_write_timer_() {
388 timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP);
392 void IRAM_ATTR OpenTherm::stop_timer_() {
395 timer1_detachInterrupt();
401 #endif // END ESP8266 404 bool IRAM_ATTR OpenTherm::check_parity_(uint32_t
val) {
413 #define TO_STRING_MEMBER(name) \ 419 TO_STRING_MEMBER(
IDLE)
421 TO_STRING_MEMBER(
READ)
423 TO_STRING_MEMBER(
WRITE)
424 TO_STRING_MEMBER(
SENT)
433 switch (error_type) {
444 switch (error_type) {
455 switch (message_type) {
491 TO_STRING_MEMBER(
DATE)
492 TO_STRING_MEMBER(
YEAR)
571 ESP_LOGD(TAG,
"type: %s; id: %s; HB: %s; LB: %s; uint_16: %s; float: %s",
577 ESP_LOGD(TAG,
"data: %s; clock: %s; capture: %s; bit_pos: %s",
format_hex(error.
data).c_str(),
586 uint16_t
const value = this->valueHB;
587 return (value << 8) | this->valueLB;
591 this->valueLB = value & 0xFF;
592 this->valueHB = (value >> 8) & 0xFF;
596 int16_t
const value = this->valueHB;
597 return (value << 8) | this->valueLB;
601 this->valueLB = value & 0xFF;
602 this->valueHB = (value >> 8) & 0xFF;
void listen()
Start listening for Opentherm data packet comming from line connected to given pin.
virtual void digital_write(bool value)=0
void debug_error(OpenThermError &error) const
constexpr T read_bit(T value, uint8_t bit)
std::string format_hex(const uint8_t *data, size_t length)
Format the byte array data of length len in lowercased hex.
void send(OpenthermData &data)
Immediately send out Opentherm data packet to line connected on given pin.
void report_and_reset_timer_error()
const char * to_string(SHTCXType type)
std::string format_bin(const uint8_t *data, size_t length)
Format the byte array data of length len in binary.
const char * timer_error_to_str(TimerErrorType error_type)
void debug_data(OpenthermData &data)
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...
ProtocolErrorType error_type
const char * operation_mode_to_str(OperationMode mode)
static void esp8266_timer_isr()
bool get_message(OpenthermData &data)
Use this to retrive data packed captured by listen() function.
timeout while waiting to receive bytes
No error found during execution of method.
static bool timer_isr(OpenTherm *arg)
const char * message_id_to_str(MessageId id)
Opentherm static class that supports either listening or sending Opentherm data packets in the same t...
BedjetMode mode
BedJet operating mode.
OpenTherm(InternalGPIOPin *in_pin, InternalGPIOPin *out_pin, int32_t device_timeout=800)
const char * protocol_error_to_str(ProtocolErrorType error_type)
virtual ISRInternalGPIOPin to_isr() const =0
Helper class to disable interrupts.
std::string to_string(int value)
bool initialize()
Setup pins.
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.
Implementation of SPI Controller mode.
Structure to hold Opentherm data packet content.
void digital_write(bool value)