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,
286 #if defined(SOC_TIMER_GROUP_SUPPORT_XTAL) && ESP_IDF_VERSION_MAJOR < 5 287 .clk_src = TIMER_SRC_CLK_APB
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);
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);
307 result = timer_isr_callback_add(this->timer_group_, this->timer_idx_,
reinterpret_cast<bool (*)(
void *)
>(
timer_isr),
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);
318 void IRAM_ATTR OpenTherm::start_esp32_timer_(uint64_t alarm_value) {
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);
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);
337 void IRAM_ATTR OpenTherm::start_read_timer_() {
339 this->start_esp32_timer_(200);
343 void IRAM_ATTR OpenTherm::start_write_timer_() {
345 this->start_esp32_timer_(500);
348 void IRAM_ATTR OpenTherm::stop_timer_() {
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);
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);
372 void OpenTherm::start_read_timer_() {
375 timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP);
380 void OpenTherm::start_write_timer_() {
383 timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP);
387 void OpenTherm::stop_timer_() {
390 timer1_detachInterrupt();
393 #endif // END ESP8266 396 bool OpenTherm::check_parity_(uint32_t
val) {
405 #define TO_STRING_MEMBER(name) \ 411 TO_STRING_MEMBER(
IDLE)
413 TO_STRING_MEMBER(
READ)
415 TO_STRING_MEMBER(
WRITE)
416 TO_STRING_MEMBER(
SENT)
424 switch (error_type) {
435 switch (message_type) {
471 TO_STRING_MEMBER(
DATE)
472 TO_STRING_MEMBER(
YEAR)
550 result << bitset<8>(data.
type) <<
" " << bitset<8>(data.
id) <<
" " << bitset<8>(data.
valueHB) <<
" " 551 << bitset<8>(data.
valueLB) <<
"\n";
566 result <<
"; clock: " <<
to_string(clock_);
567 result <<
"; capture: " << bitset<32>(error.
capture);
578 uint16_t
const value = this->valueHB;
579 return (value << 8) | this->valueLB;
583 this->valueLB = value & 0xFF;
584 this->valueHB = (value >> 8) & 0xFF;
588 int16_t
const value = this->valueHB;
589 return (value << 8) | this->valueLB;
593 this->valueLB = value & 0xFF;
594 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)