#pragma once #include #ifdef USE_ESP32 #include "esphome/components/uart/uart_component_esp_idf.h" #include "esphome/core/log.h" #endif #ifdef USE_ESP8266 #include "esphome/components/uart/uart_component_esp8266.h" #endif namespace esphome { namespace xt211 { static const uint32_t TIMEOUT = 20; // default value in uart implementation is 100ms #ifdef USE_ESP8266 class XSoftSerial : public uart::ESP8266SoftwareSerial { public: void set_bit_time(uint32_t bt) { bit_time_ = bt; } }; class XT211Uart final : public uart::ESP8266UartComponent { public: XT211Uart(uart::ESP8266UartComponent const &uart) : uart_(uart), hw_(uart.*(&XT211Uart::hw_serial_)), sw_(uart.*(&XT211Uart::sw_serial_)) {} void update_baudrate(uint32_t baudrate) { if (this->hw_ != nullptr) { this->hw_->updateBaudRate(baudrate); } else if (baudrate > 0) { ((XSoftSerial *) sw_)->set_bit_time(F_CPU / baudrate); } } bool read_one_byte(uint8_t *data) { if (this->hw_ != nullptr) { if (!this->check_read_timeout_quick_(1)) return false; this->hw_->readBytes(data, 1); } else { if (sw_->available() < 1) return false; assert(this->sw_ != nullptr); optional b = this->sw_->read_byte(); if (b) { *data = *b; } else { return false; } } return true; } protected: bool check_read_timeout_quick_(size_t len) { if (this->hw_->available() >= int(len)) return true; uint32_t start_time = millis(); while (this->hw_->available() < int(len)) { if (millis() - start_time > TIMEOUT) { return false; } yield(); } return true; } uart::ESP8266UartComponent const &uart_; HardwareSerial *const hw_; // hardware Serial uart::ESP8266SoftwareSerial *const sw_; // software serial }; #endif #ifdef USE_ESP32 // backward compatibility with old IDF versions #ifndef portTICK_PERIOD_MS #define portTICK_PERIOD_MS portTICK_RATE_MS #endif class XT211Uart final : public uart::IDFUARTComponent { public: XT211Uart(uart::IDFUARTComponent &uart) : uart_(uart), iuart_num_(uart.*(&XT211Uart::uart_num_)) {} // Reconfigure baudrate void update_baudrate(uint32_t baudrate) { auto &lock = uart_.*(&XT211Uart::lock_); if (lock != nullptr) { xSemaphoreTake(lock, portMAX_DELAY); uart_set_baudrate(iuart_num_, baudrate); xSemaphoreGive(lock); } else { // Lock not initialized yet, just set baudrate without locking uart_set_baudrate(iuart_num_, baudrate); } } bool read_one_byte(uint8_t *data) { return read_array_quick_(data, 1); } protected: bool check_read_timeout_quick_(size_t len) { if (uart_.available() >= int(len)) return true; uint32_t start_time = millis(); while (uart_.available() < int(len)) { if (millis() - start_time > TIMEOUT) { return false; } yield(); } return true; } bool read_array_quick_(uint8_t *data, size_t len) { size_t length_to_read = len; if (!this->check_read_timeout_quick_(len)) return false; auto &lock = uart_.*(&XT211Uart::lock_); bool locked = false; if (lock != nullptr) { xSemaphoreTake(lock, portMAX_DELAY); locked = true; } if (this->has_peek_) { length_to_read--; *data = this->peek_byte_; data++; this->has_peek_ = false; } if (length_to_read > 0) { // If no valid hardware UART, fall back to base read_array (e.g., BLE-backed UART) if (this->iuart_num_ < UART_NUM_0 || this->iuart_num_ >= UART_NUM_MAX) { if (!uart_.read_array(data, length_to_read)) { return false; } } else { uart_read_bytes(this->iuart_num_, data, length_to_read, 20 / portTICK_PERIOD_MS); } } if (locked) { xSemaphoreGive(lock); } return true; } uart::IDFUARTComponent &uart_; uart_port_t iuart_num_; }; #endif } // namespace xt211 } // namespace esphome