mirror of
https://github.com/esphome/esphome.git
synced 2024-11-21 22:48:10 +01: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:
parent
4de44a99eb
commit
fb2b7ade41
8 changed files with 572 additions and 343 deletions
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue