10 #if defined(ESP32) || defined(USE_ESP_IDF) 11 #include "driver/timer.h" 26 using std::stringstream;
29 static const char *
const TAG =
"opentherm";
32 OpenTherm *OpenTherm::instance_ =
nullptr;
38 #if defined(ESP32) || defined(USE_ESP_IDF)
39 timer_group_(TIMER_GROUP_0),
49 device_timeout_(device_timeout) {
50 this->isr_in_pin_ = in_pin->
to_isr();
51 this->isr_out_pin_ = out_pin->
to_isr();
56 OpenTherm::instance_ =
this;
62 #if defined(ESP32) || defined(USE_ESP_IDF) 63 return this->init_esp32_timer_();
71 this->timeout_counter_ = this->device_timeout_ * 5;
77 this->start_read_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;
94 this->start_write_timer_();
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;
114 error.
bit_pos = this->bit_pos_;
115 error.
capture = this->capture_;
116 error.
clock = this->clock_;
117 error.
data = this->data_;
127 void IRAM_ATTR OpenTherm::read_() {
133 this->start_read_timer_();
139 if (arg->timeout_counter_ == 0) {
148 if (arg->timeout_counter_ > 0) {
149 arg->timeout_counter_--;
153 uint8_t
const last = (arg->capture_ & 1);
156 if (arg->clock_ == 1 && arg->capture_ > 0xF) {
162 }
else if (arg->clock_ == 1 || arg->capture_ > 0xF) {
166 auto stop_bit_error = arg->verify_stop_bit_(last);
174 arg->error_type_ = stop_bit_error;
180 arg->bit_read_(last);
188 }
else if (arg->capture_ > 0xFF) {
195 arg->capture_ = (arg->capture_ << 1) | value;
198 if (arg->bit_pos_ == 33 || arg->bit_pos_ == 0) {
199 arg->write_bit_(1, arg->clock_);
201 arg->write_bit_(
read_bit(arg->data_, arg->bit_pos_ - 1), arg->clock_);
203 if (arg->clock_ == 0) {
204 if (arg->bit_pos_ <= 0) {
222 void IRAM_ATTR OpenTherm::bit_read_(uint8_t value) {
223 this->data_ = (this->data_ << 1) | value;
235 void IRAM_ATTR OpenTherm::write_bit_(uint8_t high, uint8_t clock) {
243 #if defined(ESP32) || defined(USE_ESP_IDF) 245 bool OpenTherm::init_esp32_timer_() {
248 timer_group_t timer_group = TIMER_GROUP_0;
249 timer_idx_t timer_idx = TIMER_0;
250 bool timer_found =
false;
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);
257 auto err = timer_get_config(timer_group, timer_idx, &temp_config);
258 if (err == ESP_ERR_INVALID_ARG) {
264 ESP_LOGD(TAG,
"Timer %d:%d seems to be occupied, will try another", timer_group, timer_idx);
268 ESP_LOGE(TAG,
"No free timer was found! OpenTherm cannot function without a timer.");
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;
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,
290 result = timer_init(this->timer_group_, this->timer_idx_, &config);
291 if (result != ESP_OK) {
292 const auto *error = esp_err_to_name(result);
293 ESP_LOGE(TAG,
"Failed to init timer. Error: %s", error);
297 result = timer_set_counter_value(this->timer_group_, this->timer_idx_, 0);
298 if (result != ESP_OK) {
299 const auto *error = esp_err_to_name(result);
300 ESP_LOGE(TAG,
"Failed to set counter value. Error: %s", error);
304 result = timer_isr_callback_add(this->timer_group_, this->timer_idx_,
reinterpret_cast<bool (*)(
void *)
>(
timer_isr),
306 if (result != ESP_OK) {
307 const auto *error = esp_err_to_name(result);
308 ESP_LOGE(TAG,
"Failed to register timer interrupt. Error: %s", error);
315 void IRAM_ATTR OpenTherm::start_esp32_timer_(uint64_t alarm_value) {
318 result = timer_set_alarm_value(this->timer_group_, this->timer_idx_, alarm_value);
319 if (result != ESP_OK) {
320 const auto *error = esp_err_to_name(result);
321 ESP_LOGE(TAG,
"Failed to set alarm value. Error: %s", error);
325 result = timer_start(this->timer_group_, this->timer_idx_);
326 if (result != ESP_OK) {
327 const auto *error = esp_err_to_name(result);
328 ESP_LOGE(TAG,
"Failed to start the timer. Error: %s", error);
334 void IRAM_ATTR OpenTherm::start_read_timer_() {
336 this->start_esp32_timer_(200);
340 void IRAM_ATTR OpenTherm::start_write_timer_() {
342 this->start_esp32_timer_(500);
345 void IRAM_ATTR OpenTherm::stop_timer_() {
350 result = timer_pause(this->timer_group_, this->timer_idx_);
351 if (result != ESP_OK) {
352 const auto *error = esp_err_to_name(result);
353 ESP_LOGE(TAG,
"Failed to pause the timer. Error: %s", error);
357 result = timer_set_counter_value(this->timer_group_, this->timer_idx_, 0);
358 if (result != ESP_OK) {
359 const auto *error = esp_err_to_name(result);
360 ESP_LOGE(TAG,
"Failed to set timer counter to 0 after pausing. Error: %s", error);
369 void OpenTherm::start_read_timer_() {
372 timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP);
377 void OpenTherm::start_write_timer_() {
380 timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP);
384 void OpenTherm::stop_timer_() {
387 timer1_detachInterrupt();
390 #endif // END ESP8266 393 bool OpenTherm::check_parity_(uint32_t
val) {
402 #define TO_STRING_MEMBER(name) \ 408 TO_STRING_MEMBER(
IDLE)
410 TO_STRING_MEMBER(
READ)
412 TO_STRING_MEMBER(
WRITE)
413 TO_STRING_MEMBER(
SENT)
421 switch (error_type) {
432 switch (message_type) {
468 TO_STRING_MEMBER(
DATE)
469 TO_STRING_MEMBER(
YEAR)
520 result << bitset<8>(data.
type) <<
" " << bitset<8>(data.
id) <<
" " << bitset<8>(data.
valueHB) <<
" " 521 << bitset<8>(data.
valueLB) <<
"\n";
536 result <<
"; clock: " <<
to_string(clock_);
537 result <<
"; capture: " << bitset<32>(error.
capture);
548 uint16_t
const value = this->valueHB;
549 return (value << 8) | this->valueLB;
553 this->valueLB = value & 0xFF;
554 this->valueHB = (value >> 8) & 0xFF;
558 int16_t
const value = this->valueHB;
559 return (value << 8) | this->valueLB;
563 this->valueLB = value & 0xFF;
564 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
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)
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)
std::string debug_data(OpenthermData &data)
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.
std::string debug_error(OpenThermError &error)
void digital_write(bool value)