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;
58 #if defined(ESP32) || defined(USE_ESP_IDF) 59 return this->init_esp32_timer_();
67 this->timeout_counter_ = this->device_timeout_ * 5;
73 this->start_read_timer_();
78 this->data_ = data.
type;
79 this->data_ = (this->data_ << 12) | data.
id;
80 this->data_ = (this->data_ << 8) | data.
valueHB;
81 this->data_ = (this->data_ << 8) | data.
valueLB;
82 if (!check_parity_(this->data_)) {
83 this->data_ = this->data_ | 0x80000000;
90 this->start_write_timer_();
95 data.
type = (this->data_ >> 28) & 0x7;
96 data.
id = (this->data_ >> 16) & 0xFF;
97 data.
valueHB = (this->data_ >> 8) & 0xFF;
98 data.
valueLB = this->data_ & 0xFF;
110 error.
bit_pos = this->bit_pos_;
111 error.
capture = this->capture_;
112 error.
clock = this->clock_;
113 error.
data = this->data_;
123 void IRAM_ATTR OpenTherm::read_() {
129 this->start_read_timer_();
135 if (arg->timeout_counter_ == 0) {
144 if (arg->timeout_counter_ > 0) {
145 arg->timeout_counter_--;
149 uint8_t
const last = (arg->capture_ & 1);
152 if (arg->clock_ == 1 && arg->capture_ > 0xF) {
158 }
else if (arg->clock_ == 1 || arg->capture_ > 0xF) {
162 auto stop_bit_error = arg->verify_stop_bit_(last);
170 arg->error_type_ = stop_bit_error;
176 arg->bit_read_(last);
184 }
else if (arg->capture_ > 0xFF) {
191 arg->capture_ = (arg->capture_ << 1) | value;
194 if (arg->bit_pos_ == 33 || arg->bit_pos_ == 0) {
195 arg->write_bit_(1, arg->clock_);
197 arg->write_bit_(
read_bit(arg->data_, arg->bit_pos_ - 1), arg->clock_);
199 if (arg->clock_ == 0) {
200 if (arg->bit_pos_ <= 0) {
218 void IRAM_ATTR OpenTherm::bit_read_(uint8_t value) {
219 this->data_ = (this->data_ << 1) | value;
231 void IRAM_ATTR OpenTherm::write_bit_(uint8_t high, uint8_t clock) {
239 #if defined(ESP32) || defined(USE_ESP_IDF) 241 bool OpenTherm::init_esp32_timer_() {
244 timer_group_t timer_group = TIMER_GROUP_0;
245 timer_idx_t timer_idx = TIMER_0;
246 bool timer_found =
false;
248 for (; cur_timer < SOC_TIMER_GROUP_TOTAL_TIMERS; cur_timer++) {
249 timer_config_t temp_config;
250 timer_group = cur_timer < 2 ? TIMER_GROUP_0 : TIMER_GROUP_1;
251 timer_idx = cur_timer < 2 ? (timer_idx_t) cur_timer : (timer_idx_t) (cur_timer - 2);
253 auto err = timer_get_config(timer_group, timer_idx, &temp_config);
254 if (err == ESP_ERR_INVALID_ARG) {
260 ESP_LOGD(TAG,
"Timer %d:%d seems to be occupied, will try another", timer_group, timer_idx);
264 ESP_LOGE(TAG,
"No free timer was found! OpenTherm cannot function without a timer.");
268 ESP_LOGD(TAG,
"Found free timer %d:%d", timer_group, timer_idx);
269 this->timer_group_ = timer_group;
270 this->timer_idx_ = timer_idx;
272 timer_config_t
const config = {
273 .alarm_en = TIMER_ALARM_EN,
274 .counter_en = TIMER_PAUSE,
275 .intr_type = TIMER_INTR_LEVEL,
276 .counter_dir = TIMER_COUNT_UP,
277 .auto_reload = TIMER_AUTORELOAD_EN,
278 #if ESP_IDF_VERSION_MAJOR >= 5 279 .clk_src = TIMER_SRC_CLK_DEFAULT,
282 #if defined(SOC_TIMER_GROUP_SUPPORT_XTAL) && ESP_IDF_VERSION_MAJOR < 5 283 .clk_src = TIMER_SRC_CLK_APB
289 result = timer_init(this->timer_group_, this->timer_idx_, &config);
290 if (result != ESP_OK) {
291 const auto *error = esp_err_to_name(result);
292 ESP_LOGE(TAG,
"Failed to init timer. Error: %s", error);
296 result = timer_set_counter_value(this->timer_group_, this->timer_idx_, 0);
297 if (result != ESP_OK) {
298 const auto *error = esp_err_to_name(result);
299 ESP_LOGE(TAG,
"Failed to set counter value. Error: %s", error);
303 result = timer_isr_callback_add(this->timer_group_, this->timer_idx_,
reinterpret_cast<bool (*)(
void *)
>(
timer_isr),
305 if (result != ESP_OK) {
306 const auto *error = esp_err_to_name(result);
307 ESP_LOGE(TAG,
"Failed to register timer interrupt. Error: %s", error);
314 void IRAM_ATTR OpenTherm::start_esp32_timer_(uint64_t alarm_value) {
317 result = timer_set_alarm_value(this->timer_group_, this->timer_idx_, alarm_value);
318 if (result != ESP_OK) {
319 const auto *error = esp_err_to_name(result);
320 ESP_LOGE(TAG,
"Failed to set alarm value. Error: %s", error);
324 result = timer_start(this->timer_group_, this->timer_idx_);
325 if (result != ESP_OK) {
326 const auto *error = esp_err_to_name(result);
327 ESP_LOGE(TAG,
"Failed to start the timer. Error: %s", error);
333 void IRAM_ATTR OpenTherm::start_read_timer_() {
335 this->start_esp32_timer_(200);
339 void IRAM_ATTR OpenTherm::start_write_timer_() {
341 this->start_esp32_timer_(500);
344 void IRAM_ATTR OpenTherm::stop_timer_() {
349 result = timer_pause(this->timer_group_, this->timer_idx_);
350 if (result != ESP_OK) {
351 const auto *error = esp_err_to_name(result);
352 ESP_LOGE(TAG,
"Failed to pause the timer. Error: %s", error);
356 result = timer_set_counter_value(this->timer_group_, this->timer_idx_, 0);
357 if (result != ESP_OK) {
358 const auto *error = esp_err_to_name(result);
359 ESP_LOGE(TAG,
"Failed to set timer counter to 0 after pausing. Error: %s", error);
368 void IRAM_ATTR OpenTherm::start_read_timer_() {
371 timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP);
376 void IRAM_ATTR OpenTherm::start_write_timer_() {
379 timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP);
383 void IRAM_ATTR OpenTherm::stop_timer_() {
386 timer1_detachInterrupt();
389 #endif // END ESP8266 392 bool IRAM_ATTR OpenTherm::check_parity_(uint32_t
val) {
401 #define TO_STRING_MEMBER(name) \ 407 TO_STRING_MEMBER(
IDLE)
409 TO_STRING_MEMBER(
READ)
411 TO_STRING_MEMBER(
WRITE)
412 TO_STRING_MEMBER(
SENT)
420 switch (error_type) {
431 switch (message_type) {
467 TO_STRING_MEMBER(
DATE)
468 TO_STRING_MEMBER(
YEAR)
547 ESP_LOGD(TAG,
"type: %s; id: %s; HB: %s; LB: %s; uint_16: %s; float: %s",
553 ESP_LOGD(TAG,
"data: %s; clock: %s; capture: %s; bit_pos: %s",
format_hex(error.
data).c_str(),
562 uint16_t
const value = this->valueHB;
563 return (value << 8) | this->valueLB;
567 this->valueLB = value & 0xFF;
568 this->valueHB = (value >> 8) & 0xFF;
572 int16_t
const value = this->valueHB;
573 return (value << 8) | this->valueLB;
577 this->valueLB = value & 0xFF;
578 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.
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.
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)
virtual ISRInternalGPIOPin to_isr() const =0
Helper class to disable interrupts.
std::string to_string(int value)
const char * protocol_error_to_to_str(ProtocolErrorType error_type)
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)