mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	Uart improvments (#1024)
* uart: Add support for specifying the number of bits and parity. ESP8266SwSerial doesn't really check parity but just read the parity bit and ignore it when receiving data. Signed-off-by: 0hax <0hax@protonmail.com> * uart: support begin and end methods. A component may need to reset uart buffer/status by using begin() and end() methods. This is useful for example when a component needs to be sure it is not reading garbage from previously received data over uart. For end() methods with software serial, disabling interrupt is currently impossible because of a bug in esp8266 Core: https://github.com/esp8266/Arduino/issues/6049 Signed-off-by: 0hax <0hax@protonmail.com> * esphal: add support for detaching an interrupt. That's needed when a component needs to enable/disable interrupt on a gpio. Signed-off-by: 0hax <0hax@protonmail.com> * uart: rename CONF_NR_BITS to CONF_DATA_BITS_NUMBER. Signed-off-by: 0hax <0hax@protonmail.com> * uart: use static const uint32_t instead of #define. Signed-off-by: 0hax <0hax@protonmail.com> * uart: use an enum to handle parity. Signed-off-by: 0hax <0hax@protonmail.com> * uart: split between esp32 and esp8266. Signed-off-by: 0hax <0hax@protonmail.com> * uart: check_uart_settings for parity and number of data bits. Signed-off-by: 0hax <0hax@protonmail.com> * name param data_bits * add new params to test Co-authored-by: Guillermo Ruffino <glm.net@gmail.com>
This commit is contained in:
		| @@ -28,13 +28,25 @@ def validate_rx_pin(value): | |||||||
|     return value |     return value | ||||||
|  |  | ||||||
|  |  | ||||||
|  | UARTParityOptions = uart_ns.enum('UARTParityOptions') | ||||||
|  | UART_PARITY_OPTIONS = { | ||||||
|  |     'NONE': UARTParityOptions.UART_CONFIG_PARITY_NONE, | ||||||
|  |     'EVEN': UARTParityOptions.UART_CONFIG_PARITY_EVEN, | ||||||
|  |     'ODD': UARTParityOptions.UART_CONFIG_PARITY_ODD, | ||||||
|  | } | ||||||
|  |  | ||||||
| CONF_STOP_BITS = 'stop_bits' | CONF_STOP_BITS = 'stop_bits' | ||||||
|  | CONF_DATA_BITS = 'data_bits' | ||||||
|  | CONF_PARITY = 'parity' | ||||||
|  |  | ||||||
| CONFIG_SCHEMA = cv.All(cv.Schema({ | CONFIG_SCHEMA = cv.All(cv.Schema({ | ||||||
|     cv.GenerateID(): cv.declare_id(UARTComponent), |     cv.GenerateID(): cv.declare_id(UARTComponent), | ||||||
|     cv.Required(CONF_BAUD_RATE): cv.int_range(min=1), |     cv.Required(CONF_BAUD_RATE): cv.int_range(min=1), | ||||||
|     cv.Optional(CONF_TX_PIN): pins.output_pin, |     cv.Optional(CONF_TX_PIN): pins.output_pin, | ||||||
|     cv.Optional(CONF_RX_PIN): validate_rx_pin, |     cv.Optional(CONF_RX_PIN): validate_rx_pin, | ||||||
|     cv.Optional(CONF_STOP_BITS, default=1): cv.one_of(1, 2, int=True), |     cv.Optional(CONF_STOP_BITS, default=1): cv.one_of(1, 2, int=True), | ||||||
|  |     cv.Optional(CONF_DATA_BITS, default=8): cv.int_range(min=5, max=8), | ||||||
|  |     cv.Optional(CONF_PARITY, default="NONE"): cv.enum(UART_PARITY_OPTIONS, upper=True) | ||||||
| }).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN)) | }).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN)) | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -50,6 +62,8 @@ def to_code(config): | |||||||
|     if CONF_RX_PIN in config: |     if CONF_RX_PIN in config: | ||||||
|         cg.add(var.set_rx_pin(config[CONF_RX_PIN])) |         cg.add(var.set_rx_pin(config[CONF_RX_PIN])) | ||||||
|     cg.add(var.set_stop_bits(config[CONF_STOP_BITS])) |     cg.add(var.set_stop_bits(config[CONF_STOP_BITS])) | ||||||
|  |     cg.add(var.set_data_bits(config[CONF_DATA_BITS])) | ||||||
|  |     cg.add(var.set_parity(config[CONF_PARITY])) | ||||||
|  |  | ||||||
|  |  | ||||||
| # A schema to use for all UART devices, all UART integrations must extend this! | # A schema to use for all UART devices, all UART integrations must extend this! | ||||||
|   | |||||||
| @@ -13,345 +13,6 @@ namespace uart { | |||||||
|  |  | ||||||
| static const char *TAG = "uart"; | static const char *TAG = "uart"; | ||||||
|  |  | ||||||
| #ifdef ARDUINO_ARCH_ESP32 |  | ||||||
| uint8_t next_uart_num = 1; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifdef ARDUINO_ARCH_ESP32 |  | ||||||
| void UARTComponent::setup() { |  | ||||||
|   ESP_LOGCONFIG(TAG, "Setting up UART..."); |  | ||||||
|   // Use Arduino HardwareSerial UARTs if all used pins match the ones |  | ||||||
|   // preconfigured by the platform. For example if RX disabled but TX pin |  | ||||||
|   // is 1 we still want to use Serial. |  | ||||||
|   if (this->tx_pin_.value_or(1) == 1 && this->rx_pin_.value_or(3) == 3) { |  | ||||||
|     this->hw_serial_ = &Serial; |  | ||||||
|   } else { |  | ||||||
|     this->hw_serial_ = new HardwareSerial(next_uart_num++); |  | ||||||
|   } |  | ||||||
|   int8_t tx = this->tx_pin_.has_value() ? *this->tx_pin_ : -1; |  | ||||||
|   int8_t rx = this->rx_pin_.has_value() ? *this->rx_pin_ : -1; |  | ||||||
|   uint32_t config = SERIAL_8N1; |  | ||||||
|   if (this->stop_bits_ == 2) |  | ||||||
|     config = SERIAL_8N2; |  | ||||||
|   this->hw_serial_->begin(this->baud_rate_, config, rx, tx); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void UARTComponent::dump_config() { |  | ||||||
|   ESP_LOGCONFIG(TAG, "UART Bus:"); |  | ||||||
|   if (this->tx_pin_.has_value()) { |  | ||||||
|     ESP_LOGCONFIG(TAG, "  TX Pin: GPIO%d", *this->tx_pin_); |  | ||||||
|   } |  | ||||||
|   if (this->rx_pin_.has_value()) { |  | ||||||
|     ESP_LOGCONFIG(TAG, "  RX Pin: GPIO%d", *this->rx_pin_); |  | ||||||
|   } |  | ||||||
|   ESP_LOGCONFIG(TAG, "  Baud Rate: %u baud", this->baud_rate_); |  | ||||||
|   ESP_LOGCONFIG(TAG, "  Stop bits: %u", this->stop_bits_); |  | ||||||
|   this->check_logger_conflict_(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void UARTComponent::write_byte(uint8_t data) { |  | ||||||
|   this->hw_serial_->write(data); |  | ||||||
|   ESP_LOGVV(TAG, "    Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data), data); |  | ||||||
| } |  | ||||||
| void UARTComponent::write_array(const uint8_t *data, size_t len) { |  | ||||||
|   this->hw_serial_->write(data, len); |  | ||||||
|   for (size_t i = 0; i < len; i++) { |  | ||||||
|     ESP_LOGVV(TAG, "    Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| void UARTComponent::write_str(const char *str) { |  | ||||||
|   this->hw_serial_->write(str); |  | ||||||
|   ESP_LOGVV(TAG, "    Wrote \"%s\"", str); |  | ||||||
| } |  | ||||||
| bool UARTComponent::read_byte(uint8_t *data) { |  | ||||||
|   if (!this->check_read_timeout_()) |  | ||||||
|     return false; |  | ||||||
|   *data = this->hw_serial_->read(); |  | ||||||
|   ESP_LOGVV(TAG, "    Read 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(*data), *data); |  | ||||||
|   return true; |  | ||||||
| } |  | ||||||
| bool UARTComponent::peek_byte(uint8_t *data) { |  | ||||||
|   if (!this->check_read_timeout_()) |  | ||||||
|     return false; |  | ||||||
|   *data = this->hw_serial_->peek(); |  | ||||||
|   return true; |  | ||||||
| } |  | ||||||
| bool UARTComponent::read_array(uint8_t *data, size_t len) { |  | ||||||
|   if (!this->check_read_timeout_(len)) |  | ||||||
|     return false; |  | ||||||
|   this->hw_serial_->readBytes(data, len); |  | ||||||
|   for (size_t i = 0; i < len; i++) { |  | ||||||
|     ESP_LOGVV(TAG, "    Read 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return true; |  | ||||||
| } |  | ||||||
| bool UARTComponent::check_read_timeout_(size_t len) { |  | ||||||
|   if (this->available() >= len) |  | ||||||
|     return true; |  | ||||||
|  |  | ||||||
|   uint32_t start_time = millis(); |  | ||||||
|   while (this->available() < len) { |  | ||||||
|     if (millis() - start_time > 1000) { |  | ||||||
|       ESP_LOGE(TAG, "Reading from UART timed out at byte %u!", this->available()); |  | ||||||
|       return false; |  | ||||||
|     } |  | ||||||
|     yield(); |  | ||||||
|   } |  | ||||||
|   return true; |  | ||||||
| } |  | ||||||
| int UARTComponent::available() { return this->hw_serial_->available(); } |  | ||||||
| void UARTComponent::flush() { |  | ||||||
|   ESP_LOGVV(TAG, "    Flushing..."); |  | ||||||
|   this->hw_serial_->flush(); |  | ||||||
| } |  | ||||||
| #endif  // ESP32 |  | ||||||
|  |  | ||||||
| #ifdef ARDUINO_ARCH_ESP8266 |  | ||||||
| void UARTComponent::setup() { |  | ||||||
|   ESP_LOGCONFIG(TAG, "Setting up UART bus..."); |  | ||||||
|   // Use Arduino HardwareSerial UARTs if all used pins match the ones |  | ||||||
|   // preconfigured by the platform. For example if RX disabled but TX pin |  | ||||||
|   // is 1 we still want to use Serial. |  | ||||||
|   uint32_t mode = UART_NB_BIT_8 | UART_PARITY_NONE; |  | ||||||
|   if (this->stop_bits_ == 1) |  | ||||||
|     mode |= UART_NB_STOP_BIT_1; |  | ||||||
|   else |  | ||||||
|     mode |= UART_NB_STOP_BIT_2; |  | ||||||
|   SerialConfig config = static_cast<SerialConfig>(mode); |  | ||||||
|   if (this->tx_pin_.value_or(1) == 1 && this->rx_pin_.value_or(3) == 3) { |  | ||||||
|     this->hw_serial_ = &Serial; |  | ||||||
|     this->hw_serial_->begin(this->baud_rate_, config); |  | ||||||
|   } else if (this->tx_pin_.value_or(15) == 15 && this->rx_pin_.value_or(13) == 13) { |  | ||||||
|     this->hw_serial_ = &Serial; |  | ||||||
|     this->hw_serial_->begin(this->baud_rate_, config); |  | ||||||
|     this->hw_serial_->swap(); |  | ||||||
|   } else if (this->tx_pin_.value_or(2) == 2 && this->rx_pin_.value_or(8) == 8) { |  | ||||||
|     this->hw_serial_ = &Serial1; |  | ||||||
|     this->hw_serial_->begin(this->baud_rate_, config); |  | ||||||
|   } else { |  | ||||||
|     this->sw_serial_ = new ESP8266SoftwareSerial(); |  | ||||||
|     int8_t tx = this->tx_pin_.has_value() ? *this->tx_pin_ : -1; |  | ||||||
|     int8_t rx = this->rx_pin_.has_value() ? *this->rx_pin_ : -1; |  | ||||||
|     this->sw_serial_->setup(tx, rx, this->baud_rate_, this->stop_bits_); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void UARTComponent::dump_config() { |  | ||||||
|   ESP_LOGCONFIG(TAG, "UART Bus:"); |  | ||||||
|   if (this->tx_pin_.has_value()) { |  | ||||||
|     ESP_LOGCONFIG(TAG, "  TX Pin: GPIO%d", *this->tx_pin_); |  | ||||||
|   } |  | ||||||
|   if (this->rx_pin_.has_value()) { |  | ||||||
|     ESP_LOGCONFIG(TAG, "  RX Pin: GPIO%d", *this->rx_pin_); |  | ||||||
|   } |  | ||||||
|   ESP_LOGCONFIG(TAG, "  Baud Rate: %u baud", this->baud_rate_); |  | ||||||
|   ESP_LOGCONFIG(TAG, "  Stop bits: %u", this->stop_bits_); |  | ||||||
|   if (this->hw_serial_ != nullptr) { |  | ||||||
|     ESP_LOGCONFIG(TAG, "  Using hardware serial interface."); |  | ||||||
|   } else { |  | ||||||
|     ESP_LOGCONFIG(TAG, "  Using software serial"); |  | ||||||
|   } |  | ||||||
|   this->check_logger_conflict_(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void UARTComponent::write_byte(uint8_t data) { |  | ||||||
|   if (this->hw_serial_ != nullptr) { |  | ||||||
|     this->hw_serial_->write(data); |  | ||||||
|   } else { |  | ||||||
|     this->sw_serial_->write_byte(data); |  | ||||||
|   } |  | ||||||
|   ESP_LOGVV(TAG, "    Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data), data); |  | ||||||
| } |  | ||||||
| void UARTComponent::write_array(const uint8_t *data, size_t len) { |  | ||||||
|   if (this->hw_serial_ != nullptr) { |  | ||||||
|     this->hw_serial_->write(data, len); |  | ||||||
|   } else { |  | ||||||
|     for (size_t i = 0; i < len; i++) |  | ||||||
|       this->sw_serial_->write_byte(data[i]); |  | ||||||
|   } |  | ||||||
|   for (size_t i = 0; i < len; i++) { |  | ||||||
|     ESP_LOGVV(TAG, "    Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| void UARTComponent::write_str(const char *str) { |  | ||||||
|   if (this->hw_serial_ != nullptr) { |  | ||||||
|     this->hw_serial_->write(str); |  | ||||||
|   } else { |  | ||||||
|     const auto *data = reinterpret_cast<const uint8_t *>(str); |  | ||||||
|     for (size_t i = 0; data[i] != 0; i++) |  | ||||||
|       this->sw_serial_->write_byte(data[i]); |  | ||||||
|   } |  | ||||||
|   ESP_LOGVV(TAG, "    Wrote \"%s\"", str); |  | ||||||
| } |  | ||||||
| bool UARTComponent::read_byte(uint8_t *data) { |  | ||||||
|   if (!this->check_read_timeout_()) |  | ||||||
|     return false; |  | ||||||
|   if (this->hw_serial_ != nullptr) { |  | ||||||
|     *data = this->hw_serial_->read(); |  | ||||||
|   } else { |  | ||||||
|     *data = this->sw_serial_->read_byte(); |  | ||||||
|   } |  | ||||||
|   ESP_LOGVV(TAG, "    Read 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(*data), *data); |  | ||||||
|   return true; |  | ||||||
| } |  | ||||||
| bool UARTComponent::peek_byte(uint8_t *data) { |  | ||||||
|   if (!this->check_read_timeout_()) |  | ||||||
|     return false; |  | ||||||
|   if (this->hw_serial_ != nullptr) { |  | ||||||
|     *data = this->hw_serial_->peek(); |  | ||||||
|   } else { |  | ||||||
|     *data = this->sw_serial_->peek_byte(); |  | ||||||
|   } |  | ||||||
|   return true; |  | ||||||
| } |  | ||||||
| bool UARTComponent::read_array(uint8_t *data, size_t len) { |  | ||||||
|   if (!this->check_read_timeout_(len)) |  | ||||||
|     return false; |  | ||||||
|   if (this->hw_serial_ != nullptr) { |  | ||||||
|     this->hw_serial_->readBytes(data, len); |  | ||||||
|   } else { |  | ||||||
|     for (size_t i = 0; i < len; i++) |  | ||||||
|       data[i] = this->sw_serial_->read_byte(); |  | ||||||
|   } |  | ||||||
|   for (size_t i = 0; i < len; i++) { |  | ||||||
|     ESP_LOGVV(TAG, "    Read 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return true; |  | ||||||
| } |  | ||||||
| bool UARTComponent::check_read_timeout_(size_t len) { |  | ||||||
|   if (this->available() >= int(len)) |  | ||||||
|     return true; |  | ||||||
|  |  | ||||||
|   uint32_t start_time = millis(); |  | ||||||
|   while (this->available() < int(len)) { |  | ||||||
|     if (millis() - start_time > 100) { |  | ||||||
|       ESP_LOGE(TAG, "Reading from UART timed out at byte %u!", this->available()); |  | ||||||
|       return false; |  | ||||||
|     } |  | ||||||
|     yield(); |  | ||||||
|   } |  | ||||||
|   return true; |  | ||||||
| } |  | ||||||
| int UARTComponent::available() { |  | ||||||
|   if (this->hw_serial_ != nullptr) { |  | ||||||
|     return this->hw_serial_->available(); |  | ||||||
|   } else { |  | ||||||
|     return this->sw_serial_->available(); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| void UARTComponent::flush() { |  | ||||||
|   ESP_LOGVV(TAG, "    Flushing..."); |  | ||||||
|   if (this->hw_serial_ != nullptr) { |  | ||||||
|     this->hw_serial_->flush(); |  | ||||||
|   } else { |  | ||||||
|     this->sw_serial_->flush(); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ESP8266SoftwareSerial::setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_rate, uint8_t stop_bits) { |  | ||||||
|   this->bit_time_ = F_CPU / baud_rate; |  | ||||||
|   if (tx_pin != -1) { |  | ||||||
|     auto pin = GPIOPin(tx_pin, OUTPUT); |  | ||||||
|     pin.setup(); |  | ||||||
|     this->tx_pin_ = pin.to_isr(); |  | ||||||
|     this->tx_pin_->digital_write(true); |  | ||||||
|   } |  | ||||||
|   if (rx_pin != -1) { |  | ||||||
|     auto pin = GPIOPin(rx_pin, INPUT); |  | ||||||
|     pin.setup(); |  | ||||||
|     this->rx_pin_ = pin.to_isr(); |  | ||||||
|     this->rx_buffer_ = new uint8_t[this->rx_buffer_size_]; |  | ||||||
|     pin.attach_interrupt(ESP8266SoftwareSerial::gpio_intr, this, FALLING); |  | ||||||
|   } |  | ||||||
|   this->stop_bits_ = stop_bits; |  | ||||||
| } |  | ||||||
| void ICACHE_RAM_ATTR ESP8266SoftwareSerial::gpio_intr(ESP8266SoftwareSerial *arg) { |  | ||||||
|   uint32_t wait = arg->bit_time_ + arg->bit_time_ / 3 - 500; |  | ||||||
|   const uint32_t start = ESP.getCycleCount(); |  | ||||||
|   uint8_t rec = 0; |  | ||||||
|   // Manually unroll the loop |  | ||||||
|   rec |= arg->read_bit_(&wait, start) << 0; |  | ||||||
|   rec |= arg->read_bit_(&wait, start) << 1; |  | ||||||
|   rec |= arg->read_bit_(&wait, start) << 2; |  | ||||||
|   rec |= arg->read_bit_(&wait, start) << 3; |  | ||||||
|   rec |= arg->read_bit_(&wait, start) << 4; |  | ||||||
|   rec |= arg->read_bit_(&wait, start) << 5; |  | ||||||
|   rec |= arg->read_bit_(&wait, start) << 6; |  | ||||||
|   rec |= arg->read_bit_(&wait, start) << 7; |  | ||||||
|   // Stop bit |  | ||||||
|   arg->wait_(&wait, start); |  | ||||||
|   if (arg->stop_bits_ == 2) |  | ||||||
|     arg->wait_(&wait, start); |  | ||||||
|  |  | ||||||
|   arg->rx_buffer_[arg->rx_in_pos_] = rec; |  | ||||||
|   arg->rx_in_pos_ = (arg->rx_in_pos_ + 1) % arg->rx_buffer_size_; |  | ||||||
|   // Clear RX pin so that the interrupt doesn't re-trigger right away again. |  | ||||||
|   arg->rx_pin_->clear_interrupt(); |  | ||||||
| } |  | ||||||
| void ICACHE_RAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) { |  | ||||||
|   if (this->tx_pin_ == nullptr) { |  | ||||||
|     ESP_LOGE(TAG, "UART doesn't have TX pins set!"); |  | ||||||
|     return; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   { |  | ||||||
|     InterruptLock lock; |  | ||||||
|     uint32_t wait = this->bit_time_; |  | ||||||
|     const uint32_t start = ESP.getCycleCount(); |  | ||||||
|     // Start bit |  | ||||||
|     this->write_bit_(false, &wait, start); |  | ||||||
|     this->write_bit_(data & (1 << 0), &wait, start); |  | ||||||
|     this->write_bit_(data & (1 << 1), &wait, start); |  | ||||||
|     this->write_bit_(data & (1 << 2), &wait, start); |  | ||||||
|     this->write_bit_(data & (1 << 3), &wait, start); |  | ||||||
|     this->write_bit_(data & (1 << 4), &wait, start); |  | ||||||
|     this->write_bit_(data & (1 << 5), &wait, start); |  | ||||||
|     this->write_bit_(data & (1 << 6), &wait, start); |  | ||||||
|     this->write_bit_(data & (1 << 7), &wait, start); |  | ||||||
|     // Stop bit |  | ||||||
|     this->write_bit_(true, &wait, start); |  | ||||||
|     if (this->stop_bits_ == 2) |  | ||||||
|       this->wait_(&wait, start); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| void ICACHE_RAM_ATTR ESP8266SoftwareSerial::wait_(uint32_t *wait, const uint32_t &start) { |  | ||||||
|   while (ESP.getCycleCount() - start < *wait) |  | ||||||
|     ; |  | ||||||
|   *wait += this->bit_time_; |  | ||||||
| } |  | ||||||
| bool ICACHE_RAM_ATTR ESP8266SoftwareSerial::read_bit_(uint32_t *wait, const uint32_t &start) { |  | ||||||
|   this->wait_(wait, start); |  | ||||||
|   return this->rx_pin_->digital_read(); |  | ||||||
| } |  | ||||||
| void ICACHE_RAM_ATTR ESP8266SoftwareSerial::write_bit_(bool bit, uint32_t *wait, const uint32_t &start) { |  | ||||||
|   this->tx_pin_->digital_write(bit); |  | ||||||
|   this->wait_(wait, start); |  | ||||||
| } |  | ||||||
| uint8_t ESP8266SoftwareSerial::read_byte() { |  | ||||||
|   if (this->rx_in_pos_ == this->rx_out_pos_) |  | ||||||
|     return 0; |  | ||||||
|   uint8_t data = this->rx_buffer_[this->rx_out_pos_]; |  | ||||||
|   this->rx_out_pos_ = (this->rx_out_pos_ + 1) % this->rx_buffer_size_; |  | ||||||
|   return data; |  | ||||||
| } |  | ||||||
| uint8_t ESP8266SoftwareSerial::peek_byte() { |  | ||||||
|   if (this->rx_in_pos_ == this->rx_out_pos_) |  | ||||||
|     return 0; |  | ||||||
|   return this->rx_buffer_[this->rx_out_pos_]; |  | ||||||
| } |  | ||||||
| void ESP8266SoftwareSerial::flush() { |  | ||||||
|   // Flush is a NO-OP with software serial, all bytes are written immediately. |  | ||||||
| } |  | ||||||
| int ESP8266SoftwareSerial::available() { |  | ||||||
|   int avail = int(this->rx_in_pos_) - int(this->rx_out_pos_); |  | ||||||
|   if (avail < 0) |  | ||||||
|     return avail + this->rx_buffer_size_; |  | ||||||
|   return avail; |  | ||||||
| } |  | ||||||
| #endif  // ESP8266 |  | ||||||
|  |  | ||||||
| size_t UARTComponent::write(uint8_t data) { | size_t UARTComponent::write(uint8_t data) { | ||||||
|   this->write_byte(data); |   this->write_byte(data); | ||||||
|   return 1; |   return 1; | ||||||
| @@ -382,7 +43,7 @@ void UARTComponent::check_logger_conflict_() { | |||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| void UARTDevice::check_uart_settings(uint32_t baud_rate, uint8_t stop_bits) { | void UARTDevice::check_uart_settings(uint32_t baud_rate, uint8_t stop_bits, UARTParityOptions parity, uint8_t nr_bits) { | ||||||
|   if (this->parent_->baud_rate_ != baud_rate) { |   if (this->parent_->baud_rate_ != baud_rate) { | ||||||
|     ESP_LOGE(TAG, "  Invalid baud_rate: Integration requested baud_rate %u but you have %u!", baud_rate, |     ESP_LOGE(TAG, "  Invalid baud_rate: Integration requested baud_rate %u but you have %u!", baud_rate, | ||||||
|              this->parent_->baud_rate_); |              this->parent_->baud_rate_); | ||||||
| @@ -391,6 +52,27 @@ void UARTDevice::check_uart_settings(uint32_t baud_rate, uint8_t stop_bits) { | |||||||
|     ESP_LOGE(TAG, "  Invalid stop bits: Integration requested stop_bits %u but you have %u!", stop_bits, |     ESP_LOGE(TAG, "  Invalid stop bits: Integration requested stop_bits %u but you have %u!", stop_bits, | ||||||
|              this->parent_->stop_bits_); |              this->parent_->stop_bits_); | ||||||
|   } |   } | ||||||
|  |   if (this->parent_->nr_bits_ != nr_bits) { | ||||||
|  |     ESP_LOGE(TAG, "  Invalid number of data bits: Integration requested %u data bits but you have %u!", nr_bits, | ||||||
|  |              this->parent_->nr_bits_); | ||||||
|  |   } | ||||||
|  |   if (this->parent_->parity_ != parity) { | ||||||
|  |     ESP_LOGE(TAG, "  Invalid parity: Integration requested parity %s but you have %s!", parity_to_str(parity), | ||||||
|  |              parity_to_str(this->parent_->parity_)); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const char *parity_to_str(UARTParityOptions parity) { | ||||||
|  |   switch (parity) { | ||||||
|  |     case UART_CONFIG_PARITY_NONE: | ||||||
|  |       return "NONE"; | ||||||
|  |     case UART_CONFIG_PARITY_EVEN: | ||||||
|  |       return "EVEN"; | ||||||
|  |     case UART_CONFIG_PARITY_ODD: | ||||||
|  |       return "ODD"; | ||||||
|  |     default: | ||||||
|  |       return "UNKNOWN"; | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| }  // namespace uart | }  // namespace uart | ||||||
|   | |||||||
| @@ -7,10 +7,19 @@ | |||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace uart { | namespace uart { | ||||||
|  |  | ||||||
|  | enum UARTParityOptions { | ||||||
|  |   UART_CONFIG_PARITY_NONE, | ||||||
|  |   UART_CONFIG_PARITY_EVEN, | ||||||
|  |   UART_CONFIG_PARITY_ODD, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const char *parity_to_str(UARTParityOptions parity); | ||||||
|  |  | ||||||
| #ifdef ARDUINO_ARCH_ESP8266 | #ifdef ARDUINO_ARCH_ESP8266 | ||||||
| class ESP8266SoftwareSerial { | class ESP8266SoftwareSerial { | ||||||
|  public: |  public: | ||||||
|   void setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_rate, uint8_t stop_bits); |   void setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_rate, uint8_t stop_bits, uint32_t nr_bits, | ||||||
|  |              UARTParityOptions parity); | ||||||
|  |  | ||||||
|   uint8_t read_byte(); |   uint8_t read_byte(); | ||||||
|   uint8_t peek_byte(); |   uint8_t peek_byte(); | ||||||
| @@ -21,6 +30,11 @@ class ESP8266SoftwareSerial { | |||||||
|  |  | ||||||
|   int available(); |   int available(); | ||||||
|  |  | ||||||
|  |   void begin(); | ||||||
|  |   void end(); | ||||||
|  |   GPIOPin *gpio_tx_pin_{nullptr}; | ||||||
|  |   GPIOPin *gpio_rx_pin_{nullptr}; | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   static void gpio_intr(ESP8266SoftwareSerial *arg); |   static void gpio_intr(ESP8266SoftwareSerial *arg); | ||||||
|  |  | ||||||
| @@ -34,6 +48,8 @@ class ESP8266SoftwareSerial { | |||||||
|   volatile size_t rx_in_pos_{0}; |   volatile size_t rx_in_pos_{0}; | ||||||
|   size_t rx_out_pos_{0}; |   size_t rx_out_pos_{0}; | ||||||
|   uint8_t stop_bits_; |   uint8_t stop_bits_; | ||||||
|  |   uint8_t nr_bits_; | ||||||
|  |   UARTParityOptions parity_; | ||||||
|   ISRInternalGPIOPin *tx_pin_{nullptr}; |   ISRInternalGPIOPin *tx_pin_{nullptr}; | ||||||
|   ISRInternalGPIOPin *rx_pin_{nullptr}; |   ISRInternalGPIOPin *rx_pin_{nullptr}; | ||||||
| }; | }; | ||||||
| @@ -43,6 +59,8 @@ class UARTComponent : public Component, public Stream { | |||||||
|  public: |  public: | ||||||
|   void set_baud_rate(uint32_t baud_rate) { baud_rate_ = baud_rate; } |   void set_baud_rate(uint32_t baud_rate) { baud_rate_ = baud_rate; } | ||||||
|  |  | ||||||
|  |   uint32_t get_config(); | ||||||
|  |  | ||||||
|   void setup() override; |   void setup() override; | ||||||
|  |  | ||||||
|   void dump_config() override; |   void dump_config() override; | ||||||
| @@ -53,6 +71,8 @@ class UARTComponent : public Component, public Stream { | |||||||
|   void write_array(const std::vector<uint8_t> &data) { this->write_array(&data[0], data.size()); } |   void write_array(const std::vector<uint8_t> &data) { this->write_array(&data[0], data.size()); } | ||||||
|  |  | ||||||
|   void write_str(const char *str); |   void write_str(const char *str); | ||||||
|  |   void end(); | ||||||
|  |   void begin(); | ||||||
|  |  | ||||||
|   bool peek_byte(uint8_t *data); |   bool peek_byte(uint8_t *data); | ||||||
|  |  | ||||||
| @@ -74,6 +94,8 @@ class UARTComponent : public Component, public Stream { | |||||||
|   void set_tx_pin(uint8_t tx_pin) { this->tx_pin_ = tx_pin; } |   void set_tx_pin(uint8_t tx_pin) { this->tx_pin_ = tx_pin; } | ||||||
|   void set_rx_pin(uint8_t rx_pin) { this->rx_pin_ = rx_pin; } |   void set_rx_pin(uint8_t rx_pin) { this->rx_pin_ = rx_pin; } | ||||||
|   void set_stop_bits(uint8_t stop_bits) { this->stop_bits_ = stop_bits; } |   void set_stop_bits(uint8_t stop_bits) { this->stop_bits_ = stop_bits; } | ||||||
|  |   void set_data_bits(uint8_t nr_bits) { this->nr_bits_ = nr_bits; } | ||||||
|  |   void set_parity(UARTParityOptions parity) { this->parity_ = parity; } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   void check_logger_conflict_(); |   void check_logger_conflict_(); | ||||||
| @@ -88,6 +110,8 @@ class UARTComponent : public Component, public Stream { | |||||||
|   optional<uint8_t> rx_pin_; |   optional<uint8_t> rx_pin_; | ||||||
|   uint32_t baud_rate_; |   uint32_t baud_rate_; | ||||||
|   uint8_t stop_bits_; |   uint8_t stop_bits_; | ||||||
|  |   uint8_t nr_bits_; | ||||||
|  |   UARTParityOptions parity_; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #ifdef ARDUINO_ARCH_ESP32 | #ifdef ARDUINO_ARCH_ESP32 | ||||||
| @@ -130,9 +154,12 @@ class UARTDevice : public Stream { | |||||||
|   size_t write(uint8_t data) override { return this->parent_->write(data); } |   size_t write(uint8_t data) override { return this->parent_->write(data); } | ||||||
|   int read() override { return this->parent_->read(); } |   int read() override { return this->parent_->read(); } | ||||||
|   int peek() override { return this->parent_->peek(); } |   int peek() override { return this->parent_->peek(); } | ||||||
|  |   void end() { this->parent_->end(); } | ||||||
|  |   void begin() { this->parent_->begin(); } | ||||||
|  |  | ||||||
|   /// Check that the configuration of the UART bus matches the provided values and otherwise print a warning |   /// Check that the configuration of the UART bus matches the provided values and otherwise print a warning | ||||||
|   void check_uart_settings(uint32_t baud_rate, uint8_t stop_bits = 1); |   void check_uart_settings(uint32_t baud_rate, uint8_t stop_bits = 1, | ||||||
|  |                            UARTParityOptions parity = UART_CONFIG_PARITY_NONE, uint8_t nr_bits = 8); | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   UARTComponent *parent_{nullptr}; |   UARTComponent *parent_{nullptr}; | ||||||
|   | |||||||
							
								
								
									
										162
									
								
								esphome/components/uart/uart_esp32.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								esphome/components/uart/uart_esp32.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,162 @@ | |||||||
|  | #ifdef ARDUINO_ARCH_ESP32 | ||||||
|  | #include "uart.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  | #include "esphome/core/application.h" | ||||||
|  | #include "esphome/core/defines.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace uart { | ||||||
|  | static const char *TAG = "uart_esp32"; | ||||||
|  | uint8_t next_uart_num = 1; | ||||||
|  |  | ||||||
|  | static const uint32_t UART_PARITY_EVEN = 0 << 0; | ||||||
|  | static const uint32_t UART_PARITY_ODD = 1 << 0; | ||||||
|  | static const uint32_t UART_PARITY_EN = 1 << 1; | ||||||
|  | static const uint32_t UART_NB_BIT_5 = 0 << 2; | ||||||
|  | static const uint32_t UART_NB_BIT_6 = 1 << 2; | ||||||
|  | static const uint32_t UART_NB_BIT_7 = 2 << 2; | ||||||
|  | static const uint32_t UART_NB_BIT_8 = 3 << 2; | ||||||
|  | static const uint32_t UART_NB_STOP_BIT_1 = 1 << 4; | ||||||
|  | static const uint32_t UART_NB_STOP_BIT_2 = 3 << 4; | ||||||
|  | static const uint32_t UART_TICK_APB_CLOCK = 1 << 27; | ||||||
|  |  | ||||||
|  | uint32_t UARTComponent::get_config() { | ||||||
|  |   uint32_t config = 0; | ||||||
|  |  | ||||||
|  |   /* | ||||||
|  |    * All bits numbers below come from | ||||||
|  |    * framework-arduinoespressif32/cores/esp32/esp32-hal-uart.h | ||||||
|  |    * And more specifically conf0 union in uart_dev_t. | ||||||
|  |    * | ||||||
|  |    * Below is bit used from conf0 union. | ||||||
|  |    * <name>:<bits position>  <values> | ||||||
|  |    * parity:0                0:even 1:odd | ||||||
|  |    * parity_en:1             Set this bit to enable uart parity check. | ||||||
|  |    * bit_num:2-4             0:5bits 1:6bits 2:7bits 3:8bits | ||||||
|  |    * stop_bit_num:4-6        stop bit. 1:1bit  2:1.5bits  3:2bits | ||||||
|  |    * tick_ref_always_on:27   select the clock.1:apb clock:ref_tick | ||||||
|  |    */ | ||||||
|  |  | ||||||
|  |   if (this->parity_ == UART_CONFIG_PARITY_EVEN) | ||||||
|  |     config |= UART_PARITY_EVEN | UART_PARITY_EN; | ||||||
|  |   else if (this->parity_ == UART_CONFIG_PARITY_ODD) | ||||||
|  |     config |= UART_PARITY_ODD | UART_PARITY_EN; | ||||||
|  |  | ||||||
|  |   switch (this->nr_bits_) { | ||||||
|  |     case 5: | ||||||
|  |       config |= UART_NB_BIT_5; | ||||||
|  |       break; | ||||||
|  |     case 6: | ||||||
|  |       config |= UART_NB_BIT_6; | ||||||
|  |       break; | ||||||
|  |     case 7: | ||||||
|  |       config |= UART_NB_BIT_7; | ||||||
|  |       break; | ||||||
|  |     case 8: | ||||||
|  |       config |= UART_NB_BIT_8; | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (this->stop_bits_ == 1) | ||||||
|  |     config |= UART_NB_STOP_BIT_1; | ||||||
|  |   else | ||||||
|  |     config |= UART_NB_STOP_BIT_2; | ||||||
|  |  | ||||||
|  |   config |= UART_TICK_APB_CLOCK; | ||||||
|  |  | ||||||
|  |   return config; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void UARTComponent::setup() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "Setting up UART..."); | ||||||
|  |   // Use Arduino HardwareSerial UARTs if all used pins match the ones | ||||||
|  |   // preconfigured by the platform. For example if RX disabled but TX pin | ||||||
|  |   // is 1 we still want to use Serial. | ||||||
|  |   if (this->tx_pin_.value_or(1) == 1 && this->rx_pin_.value_or(3) == 3) { | ||||||
|  |     this->hw_serial_ = &Serial; | ||||||
|  |   } else { | ||||||
|  |     this->hw_serial_ = new HardwareSerial(next_uart_num++); | ||||||
|  |   } | ||||||
|  |   int8_t tx = this->tx_pin_.has_value() ? *this->tx_pin_ : -1; | ||||||
|  |   int8_t rx = this->rx_pin_.has_value() ? *this->rx_pin_ : -1; | ||||||
|  |   this->hw_serial_->begin(this->baud_rate_, get_config(), rx, tx); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void UARTComponent::dump_config() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "UART Bus:"); | ||||||
|  |   if (this->tx_pin_.has_value()) { | ||||||
|  |     ESP_LOGCONFIG(TAG, "  TX Pin: GPIO%d", *this->tx_pin_); | ||||||
|  |   } | ||||||
|  |   if (this->rx_pin_.has_value()) { | ||||||
|  |     ESP_LOGCONFIG(TAG, "  RX Pin: GPIO%d", *this->rx_pin_); | ||||||
|  |   } | ||||||
|  |   ESP_LOGCONFIG(TAG, "  Baud Rate: %u baud", this->baud_rate_); | ||||||
|  |   ESP_LOGCONFIG(TAG, "  Bits: %u", this->nr_bits_); | ||||||
|  |   ESP_LOGCONFIG(TAG, "  Parity: %s", parity_to_str(this->parity_)); | ||||||
|  |   ESP_LOGCONFIG(TAG, "  Stop bits: %u", this->stop_bits_); | ||||||
|  |   this->check_logger_conflict_(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void UARTComponent::write_byte(uint8_t data) { | ||||||
|  |   this->hw_serial_->write(data); | ||||||
|  |   ESP_LOGVV(TAG, "    Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data), data); | ||||||
|  | } | ||||||
|  | void UARTComponent::write_array(const uint8_t *data, size_t len) { | ||||||
|  |   this->hw_serial_->write(data, len); | ||||||
|  |   for (size_t i = 0; i < len; i++) { | ||||||
|  |     ESP_LOGVV(TAG, "    Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | void UARTComponent::write_str(const char *str) { | ||||||
|  |   this->hw_serial_->write(str); | ||||||
|  |   ESP_LOGVV(TAG, "    Wrote \"%s\"", str); | ||||||
|  | } | ||||||
|  | void UARTComponent::end() { this->hw_serial_->end(); } | ||||||
|  | void UARTComponent::begin() { this->hw_serial_->begin(this->baud_rate_, get_config()); } | ||||||
|  | bool UARTComponent::read_byte(uint8_t *data) { | ||||||
|  |   if (!this->check_read_timeout_()) | ||||||
|  |     return false; | ||||||
|  |   *data = this->hw_serial_->read(); | ||||||
|  |   ESP_LOGVV(TAG, "    Read 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(*data), *data); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | bool UARTComponent::peek_byte(uint8_t *data) { | ||||||
|  |   if (!this->check_read_timeout_()) | ||||||
|  |     return false; | ||||||
|  |   *data = this->hw_serial_->peek(); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | bool UARTComponent::read_array(uint8_t *data, size_t len) { | ||||||
|  |   if (!this->check_read_timeout_(len)) | ||||||
|  |     return false; | ||||||
|  |   this->hw_serial_->readBytes(data, len); | ||||||
|  |   for (size_t i = 0; i < len; i++) { | ||||||
|  |     ESP_LOGVV(TAG, "    Read 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | bool UARTComponent::check_read_timeout_(size_t len) { | ||||||
|  |   if (this->available() >= len) | ||||||
|  |     return true; | ||||||
|  |  | ||||||
|  |   uint32_t start_time = millis(); | ||||||
|  |   while (this->available() < len) { | ||||||
|  |     if (millis() - start_time > 1000) { | ||||||
|  |       ESP_LOGE(TAG, "Reading from UART timed out at byte %u!", this->available()); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     yield(); | ||||||
|  |   } | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | int UARTComponent::available() { return this->hw_serial_->available(); } | ||||||
|  | void UARTComponent::flush() { | ||||||
|  |   ESP_LOGVV(TAG, "    Flushing..."); | ||||||
|  |   this->hw_serial_->flush(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace uart | ||||||
|  | }  // namespace esphome | ||||||
|  | #endif  // ESP32 | ||||||
							
								
								
									
										330
									
								
								esphome/components/uart/uart_esp8266.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										330
									
								
								esphome/components/uart/uart_esp8266.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,330 @@ | |||||||
|  | #ifdef ARDUINO_ARCH_ESP8266 | ||||||
|  | #include "uart.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  | #include "esphome/core/application.h" | ||||||
|  | #include "esphome/core/defines.h" | ||||||
|  | # | ||||||
|  | namespace esphome { | ||||||
|  | namespace uart { | ||||||
|  |  | ||||||
|  | static const char *TAG = "uart_esp8266"; | ||||||
|  | uint32_t UARTComponent::get_config() { | ||||||
|  |   uint32_t config = 0; | ||||||
|  |  | ||||||
|  |   if (this->parity_ == UART_CONFIG_PARITY_NONE) | ||||||
|  |     config |= UART_PARITY_NONE; | ||||||
|  |   else if (this->parity_ == UART_CONFIG_PARITY_EVEN) | ||||||
|  |     config |= UART_PARITY_EVEN; | ||||||
|  |   else if (this->parity_ == UART_CONFIG_PARITY_ODD) | ||||||
|  |     config |= UART_PARITY_ODD; | ||||||
|  |  | ||||||
|  |   switch (this->nr_bits_) { | ||||||
|  |     case 5: | ||||||
|  |       config |= UART_NB_BIT_5; | ||||||
|  |       break; | ||||||
|  |     case 6: | ||||||
|  |       config |= UART_NB_BIT_6; | ||||||
|  |       break; | ||||||
|  |     case 7: | ||||||
|  |       config |= UART_NB_BIT_7; | ||||||
|  |       break; | ||||||
|  |     case 8: | ||||||
|  |       config |= UART_NB_BIT_8; | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (this->stop_bits_ == 1) | ||||||
|  |     config |= UART_NB_STOP_BIT_1; | ||||||
|  |   else | ||||||
|  |     config |= UART_NB_STOP_BIT_2; | ||||||
|  |  | ||||||
|  |   return config; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void UARTComponent::setup() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "Setting up UART bus..."); | ||||||
|  |   // Use Arduino HardwareSerial UARTs if all used pins match the ones | ||||||
|  |   // preconfigured by the platform. For example if RX disabled but TX pin | ||||||
|  |   // is 1 we still want to use Serial. | ||||||
|  |   SerialConfig config = static_cast<SerialConfig>(get_config()); | ||||||
|  |  | ||||||
|  |   if (this->tx_pin_.value_or(1) == 1 && this->rx_pin_.value_or(3) == 3) { | ||||||
|  |     this->hw_serial_ = &Serial; | ||||||
|  |     this->hw_serial_->begin(this->baud_rate_, config); | ||||||
|  |   } else if (this->tx_pin_.value_or(15) == 15 && this->rx_pin_.value_or(13) == 13) { | ||||||
|  |     this->hw_serial_ = &Serial; | ||||||
|  |     this->hw_serial_->begin(this->baud_rate_, config); | ||||||
|  |     this->hw_serial_->swap(); | ||||||
|  |   } else if (this->tx_pin_.value_or(2) == 2 && this->rx_pin_.value_or(8) == 8) { | ||||||
|  |     this->hw_serial_ = &Serial1; | ||||||
|  |     this->hw_serial_->begin(this->baud_rate_, config); | ||||||
|  |   } else { | ||||||
|  |     this->sw_serial_ = new ESP8266SoftwareSerial(); | ||||||
|  |     int8_t tx = this->tx_pin_.has_value() ? *this->tx_pin_ : -1; | ||||||
|  |     int8_t rx = this->rx_pin_.has_value() ? *this->rx_pin_ : -1; | ||||||
|  |     this->sw_serial_->setup(tx, rx, this->baud_rate_, this->stop_bits_, this->nr_bits_, this->parity_); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void UARTComponent::dump_config() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "UART Bus:"); | ||||||
|  |   if (this->tx_pin_.has_value()) { | ||||||
|  |     ESP_LOGCONFIG(TAG, "  TX Pin: GPIO%d", *this->tx_pin_); | ||||||
|  |   } | ||||||
|  |   if (this->rx_pin_.has_value()) { | ||||||
|  |     ESP_LOGCONFIG(TAG, "  RX Pin: GPIO%d", *this->rx_pin_); | ||||||
|  |   } | ||||||
|  |   ESP_LOGCONFIG(TAG, "  Baud Rate: %u baud", this->baud_rate_); | ||||||
|  |   ESP_LOGCONFIG(TAG, "  Bits: %u", this->nr_bits_); | ||||||
|  |   ESP_LOGCONFIG(TAG, "  Parity: %s", parity_to_str(this->parity_)); | ||||||
|  |   ESP_LOGCONFIG(TAG, "  Stop bits: %u", this->stop_bits_); | ||||||
|  |   if (this->hw_serial_ != nullptr) { | ||||||
|  |     ESP_LOGCONFIG(TAG, "  Using hardware serial interface."); | ||||||
|  |   } else { | ||||||
|  |     ESP_LOGCONFIG(TAG, "  Using software serial"); | ||||||
|  |   } | ||||||
|  |   this->check_logger_conflict_(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void UARTComponent::write_byte(uint8_t data) { | ||||||
|  |   if (this->hw_serial_ != nullptr) { | ||||||
|  |     this->hw_serial_->write(data); | ||||||
|  |   } else { | ||||||
|  |     this->sw_serial_->write_byte(data); | ||||||
|  |   } | ||||||
|  |   ESP_LOGVV(TAG, "    Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data), data); | ||||||
|  | } | ||||||
|  | void UARTComponent::write_array(const uint8_t *data, size_t len) { | ||||||
|  |   if (this->hw_serial_ != nullptr) { | ||||||
|  |     this->hw_serial_->write(data, len); | ||||||
|  |   } else { | ||||||
|  |     for (size_t i = 0; i < len; i++) | ||||||
|  |       this->sw_serial_->write_byte(data[i]); | ||||||
|  |   } | ||||||
|  |   for (size_t i = 0; i < len; i++) { | ||||||
|  |     ESP_LOGVV(TAG, "    Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | void UARTComponent::write_str(const char *str) { | ||||||
|  |   if (this->hw_serial_ != nullptr) { | ||||||
|  |     this->hw_serial_->write(str); | ||||||
|  |   } else { | ||||||
|  |     const auto *data = reinterpret_cast<const uint8_t *>(str); | ||||||
|  |     for (size_t i = 0; data[i] != 0; i++) | ||||||
|  |       this->sw_serial_->write_byte(data[i]); | ||||||
|  |   } | ||||||
|  |   ESP_LOGVV(TAG, "    Wrote \"%s\"", str); | ||||||
|  | } | ||||||
|  | void UARTComponent::end() { | ||||||
|  |   if (this->hw_serial_ != nullptr) | ||||||
|  |     this->hw_serial_->end(); | ||||||
|  |   else if (this->sw_serial_ != nullptr) | ||||||
|  |     this->sw_serial_->end(); | ||||||
|  | } | ||||||
|  | void UARTComponent::begin() { | ||||||
|  |   if (this->hw_serial_ != nullptr) | ||||||
|  |     this->hw_serial_->begin(this->baud_rate_, static_cast<SerialConfig>(get_config())); | ||||||
|  |   else if (this->sw_serial_ != nullptr) | ||||||
|  |     this->sw_serial_->begin(); | ||||||
|  | } | ||||||
|  | bool UARTComponent::read_byte(uint8_t *data) { | ||||||
|  |   if (!this->check_read_timeout_()) | ||||||
|  |     return false; | ||||||
|  |   if (this->hw_serial_ != nullptr) { | ||||||
|  |     *data = this->hw_serial_->read(); | ||||||
|  |   } else { | ||||||
|  |     *data = this->sw_serial_->read_byte(); | ||||||
|  |   } | ||||||
|  |   ESP_LOGVV(TAG, "    Read 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(*data), *data); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | bool UARTComponent::peek_byte(uint8_t *data) { | ||||||
|  |   if (!this->check_read_timeout_()) | ||||||
|  |     return false; | ||||||
|  |   if (this->hw_serial_ != nullptr) { | ||||||
|  |     *data = this->hw_serial_->peek(); | ||||||
|  |   } else { | ||||||
|  |     *data = this->sw_serial_->peek_byte(); | ||||||
|  |   } | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | bool UARTComponent::read_array(uint8_t *data, size_t len) { | ||||||
|  |   if (!this->check_read_timeout_(len)) | ||||||
|  |     return false; | ||||||
|  |   if (this->hw_serial_ != nullptr) { | ||||||
|  |     this->hw_serial_->readBytes(data, len); | ||||||
|  |   } else { | ||||||
|  |     for (size_t i = 0; i < len; i++) | ||||||
|  |       data[i] = this->sw_serial_->read_byte(); | ||||||
|  |   } | ||||||
|  |   for (size_t i = 0; i < len; i++) { | ||||||
|  |     ESP_LOGVV(TAG, "    Read 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | bool UARTComponent::check_read_timeout_(size_t len) { | ||||||
|  |   if (this->available() >= int(len)) | ||||||
|  |     return true; | ||||||
|  |  | ||||||
|  |   uint32_t start_time = millis(); | ||||||
|  |   while (this->available() < int(len)) { | ||||||
|  |     if (millis() - start_time > 100) { | ||||||
|  |       ESP_LOGE(TAG, "Reading from UART timed out at byte %u!", this->available()); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     yield(); | ||||||
|  |   } | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | int UARTComponent::available() { | ||||||
|  |   if (this->hw_serial_ != nullptr) { | ||||||
|  |     return this->hw_serial_->available(); | ||||||
|  |   } else { | ||||||
|  |     return this->sw_serial_->available(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | void UARTComponent::flush() { | ||||||
|  |   ESP_LOGVV(TAG, "    Flushing..."); | ||||||
|  |   if (this->hw_serial_ != nullptr) { | ||||||
|  |     this->hw_serial_->flush(); | ||||||
|  |   } else { | ||||||
|  |     this->sw_serial_->flush(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | void ESP8266SoftwareSerial::end() { | ||||||
|  |   /* Because of this bug: https://github.com/esp8266/Arduino/issues/6049 | ||||||
|  |    * detach_interrupt can't called. | ||||||
|  |    * So simply reset rx_in_pos and rx_out_pos even if it's totally racy with | ||||||
|  |    * the interrupt. | ||||||
|  |    */ | ||||||
|  |   // this->gpio_rx_pin_->detach_interrupt(); | ||||||
|  |   this->rx_in_pos_ = 0; | ||||||
|  |   this->rx_out_pos_ = 0; | ||||||
|  | } | ||||||
|  | void ESP8266SoftwareSerial::begin() { | ||||||
|  |   /* attach_interrupt() is also not safe because gpio_intr() may | ||||||
|  |    * endup with arg == nullptr. | ||||||
|  |    */ | ||||||
|  |   // this->gpio_rx_pin_->attach_interrupt(ESP8266SoftwareSerial::gpio_intr, this, FALLING); | ||||||
|  | } | ||||||
|  | void ESP8266SoftwareSerial::setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_rate, uint8_t stop_bits, uint32_t nr_bits, | ||||||
|  |                                   UARTParityOptions parity) { | ||||||
|  |   this->bit_time_ = F_CPU / baud_rate; | ||||||
|  |   this->stop_bits_ = stop_bits; | ||||||
|  |   this->nr_bits_ = nr_bits; | ||||||
|  |   this->parity_ = parity; | ||||||
|  |   if (tx_pin != -1) { | ||||||
|  |     auto pin = GPIOPin(tx_pin, OUTPUT); | ||||||
|  |     this->gpio_tx_pin_ = &pin; | ||||||
|  |     pin.setup(); | ||||||
|  |     this->tx_pin_ = pin.to_isr(); | ||||||
|  |     this->tx_pin_->digital_write(true); | ||||||
|  |   } | ||||||
|  |   if (rx_pin != -1) { | ||||||
|  |     auto pin = GPIOPin(rx_pin, INPUT); | ||||||
|  |     pin.setup(); | ||||||
|  |     this->gpio_rx_pin_ = &pin; | ||||||
|  |     this->rx_pin_ = pin.to_isr(); | ||||||
|  |     this->rx_buffer_ = new uint8_t[this->rx_buffer_size_]; | ||||||
|  |     pin.attach_interrupt(ESP8266SoftwareSerial::gpio_intr, this, FALLING); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | void ICACHE_RAM_ATTR ESP8266SoftwareSerial::gpio_intr(ESP8266SoftwareSerial *arg) { | ||||||
|  |   uint32_t wait = arg->bit_time_ + arg->bit_time_ / 3 - 500; | ||||||
|  |   const uint32_t start = ESP.getCycleCount(); | ||||||
|  |   uint8_t rec = 0; | ||||||
|  |   // Manually unroll the loop | ||||||
|  |   for (int i = 0; i < arg->nr_bits_; i++) | ||||||
|  |     rec |= arg->read_bit_(&wait, start) << i; | ||||||
|  |  | ||||||
|  |   /* If parity is enabled, just read it and ignore it. */ | ||||||
|  |   /* TODO: Should we check parity? Or is it too slow for nothing added..*/ | ||||||
|  |   if (arg->parity_ == UART_CONFIG_PARITY_EVEN) | ||||||
|  |     arg->read_bit_(&wait, start); | ||||||
|  |   else if (arg->parity_ == UART_CONFIG_PARITY_ODD) | ||||||
|  |     arg->read_bit_(&wait, start); | ||||||
|  |  | ||||||
|  |   // Stop bit | ||||||
|  |   arg->wait_(&wait, start); | ||||||
|  |   if (arg->stop_bits_ == 2) | ||||||
|  |     arg->wait_(&wait, start); | ||||||
|  |  | ||||||
|  |   arg->rx_buffer_[arg->rx_in_pos_] = rec; | ||||||
|  |   arg->rx_in_pos_ = (arg->rx_in_pos_ + 1) % arg->rx_buffer_size_; | ||||||
|  |   // Clear RX pin so that the interrupt doesn't re-trigger right away again. | ||||||
|  |   arg->rx_pin_->clear_interrupt(); | ||||||
|  | } | ||||||
|  | void ICACHE_RAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) { | ||||||
|  |   if (this->tx_pin_ == nullptr) { | ||||||
|  |     ESP_LOGE(TAG, "UART doesn't have TX pins set!"); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   bool parity_bit = false; | ||||||
|  |   bool need_parity_bit = true; | ||||||
|  |   if (this->parity_ == UART_CONFIG_PARITY_EVEN) | ||||||
|  |     parity_bit = true; | ||||||
|  |   else if (this->parity_ == UART_CONFIG_PARITY_ODD) | ||||||
|  |     parity_bit = false; | ||||||
|  |   else | ||||||
|  |     need_parity_bit = false; | ||||||
|  |  | ||||||
|  |   { | ||||||
|  |     InterruptLock lock; | ||||||
|  |     uint32_t wait = this->bit_time_; | ||||||
|  |     const uint32_t start = ESP.getCycleCount(); | ||||||
|  |     // Start bit | ||||||
|  |     this->write_bit_(false, &wait, start); | ||||||
|  |     for (int i = 0; i < this->nr_bits_; i++) { | ||||||
|  |       bool bit = data & (1 << i); | ||||||
|  |       this->write_bit_(bit, &wait, start); | ||||||
|  |       if (need_parity_bit) | ||||||
|  |         parity_bit ^= bit; | ||||||
|  |     } | ||||||
|  |     if (need_parity_bit) | ||||||
|  |       this->write_bit_(parity_bit, &wait, start); | ||||||
|  |     // Stop bit | ||||||
|  |     this->write_bit_(true, &wait, start); | ||||||
|  |     if (this->stop_bits_ == 2) | ||||||
|  |       this->wait_(&wait, start); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | void ICACHE_RAM_ATTR ESP8266SoftwareSerial::wait_(uint32_t *wait, const uint32_t &start) { | ||||||
|  |   while (ESP.getCycleCount() - start < *wait) | ||||||
|  |     ; | ||||||
|  |   *wait += this->bit_time_; | ||||||
|  | } | ||||||
|  | bool ICACHE_RAM_ATTR ESP8266SoftwareSerial::read_bit_(uint32_t *wait, const uint32_t &start) { | ||||||
|  |   this->wait_(wait, start); | ||||||
|  |   return this->rx_pin_->digital_read(); | ||||||
|  | } | ||||||
|  | void ICACHE_RAM_ATTR ESP8266SoftwareSerial::write_bit_(bool bit, uint32_t *wait, const uint32_t &start) { | ||||||
|  |   this->tx_pin_->digital_write(bit); | ||||||
|  |   this->wait_(wait, start); | ||||||
|  | } | ||||||
|  | uint8_t ESP8266SoftwareSerial::read_byte() { | ||||||
|  |   if (this->rx_in_pos_ == this->rx_out_pos_) | ||||||
|  |     return 0; | ||||||
|  |   uint8_t data = this->rx_buffer_[this->rx_out_pos_]; | ||||||
|  |   this->rx_out_pos_ = (this->rx_out_pos_ + 1) % this->rx_buffer_size_; | ||||||
|  |   return data; | ||||||
|  | } | ||||||
|  | uint8_t ESP8266SoftwareSerial::peek_byte() { | ||||||
|  |   if (this->rx_in_pos_ == this->rx_out_pos_) | ||||||
|  |     return 0; | ||||||
|  |   return this->rx_buffer_[this->rx_out_pos_]; | ||||||
|  | } | ||||||
|  | void ESP8266SoftwareSerial::flush() { | ||||||
|  |   // Flush is a NO-OP with software serial, all bytes are written immediately. | ||||||
|  | } | ||||||
|  | int ESP8266SoftwareSerial::available() { | ||||||
|  |   int avail = int(this->rx_in_pos_) - int(this->rx_out_pos_); | ||||||
|  |   if (avail < 0) | ||||||
|  |     return avail + this->rx_buffer_size_; | ||||||
|  |   return avail; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace uart | ||||||
|  | }  // namespace esphome | ||||||
|  | #endif  // ESP8266 | ||||||
| @@ -12,6 +12,7 @@ typedef struct {        // NOLINT | |||||||
|  |  | ||||||
| void ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, void (*)(void *), void *fp,  // NOLINT | void ICACHE_RAM_ATTR __attachInterruptArg(uint8_t pin, void (*)(void *), void *fp,  // NOLINT | ||||||
|                                           int mode); |                                           int mode); | ||||||
|  | void ICACHE_RAM_ATTR __detachInterrupt(uint8_t pin);  // NOLINT | ||||||
| }; | }; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -226,6 +227,15 @@ void ICACHE_RAM_ATTR interrupt_handler(void *arg) { | |||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | void GPIOPin::detach_interrupt() const { this->detach_interrupt_(); } | ||||||
|  | void GPIOPin::detach_interrupt_() const { | ||||||
|  | #ifdef ARDUINO_ARCH_ESP8266 | ||||||
|  |   __detachInterrupt(get_pin()); | ||||||
|  | #endif | ||||||
|  | #ifdef ARDUINO_ARCH_ESP32 | ||||||
|  |   detachInterrupt(get_pin()); | ||||||
|  | #endif | ||||||
|  | } | ||||||
| void GPIOPin::attach_interrupt_(void (*func)(void *), void *arg, int mode) const { | void GPIOPin::attach_interrupt_(void (*func)(void *), void *arg, int mode) const { | ||||||
|   if (this->inverted_) { |   if (this->inverted_) { | ||||||
|     if (mode == RISING) { |     if (mode == RISING) { | ||||||
|   | |||||||
| @@ -94,11 +94,13 @@ class GPIOPin { | |||||||
|   bool is_inverted() const; |   bool is_inverted() const; | ||||||
|  |  | ||||||
|   template<typename T> void attach_interrupt(void (*func)(T *), T *arg, int mode) const; |   template<typename T> void attach_interrupt(void (*func)(T *), T *arg, int mode) const; | ||||||
|  |   void detach_interrupt() const; | ||||||
|  |  | ||||||
|   ISRInternalGPIOPin *to_isr() const; |   ISRInternalGPIOPin *to_isr() const; | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   void attach_interrupt_(void (*func)(void *), void *arg, int mode) const; |   void attach_interrupt_(void (*func)(void *), void *arg, int mode) const; | ||||||
|  |   void detach_interrupt_() const; | ||||||
|  |  | ||||||
|   const uint8_t pin_; |   const uint8_t pin_; | ||||||
|   const uint8_t mode_; |   const uint8_t mode_; | ||||||
| @@ -114,5 +116,4 @@ class GPIOPin { | |||||||
| template<typename T> void GPIOPin::attach_interrupt(void (*func)(T *), T *arg, int mode) const { | template<typename T> void GPIOPin::attach_interrupt(void (*func)(T *), T *arg, int mode) const { | ||||||
|   this->attach_interrupt_(reinterpret_cast<void (*)(void *)>(func), arg, mode); |   this->attach_interrupt_(reinterpret_cast<void (*)(void *)>(func), arg, mode); | ||||||
| } | } | ||||||
|  |  | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|   | |||||||
| @@ -132,6 +132,9 @@ uart: | |||||||
|   rx_pin: GPIO23 |   rx_pin: GPIO23 | ||||||
|   baud_rate: 115200 |   baud_rate: 115200 | ||||||
|   id: uart0 |   id: uart0 | ||||||
|  |   parity: NONE | ||||||
|  |   data_bits: 8 | ||||||
|  |   stop_bits: 1 | ||||||
|  |  | ||||||
| ota: | ota: | ||||||
|   safe_mode: True |   safe_mode: True | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user