mirror of
https://github.com/esphome/esphome.git
synced 2025-09-12 00:02:21 +01:00
ESP-IDF support and generic target platforms (#2303)
* Socket refactor and SSL * esp-idf temp * Fixes * Echo component and noise * Add noise API transport support * Updates * ESP-IDF * Complete * Fixes * Fixes * Versions update * New i2c APIs * Complete i2c refactor * SPI migration * Revert ESP Preferences migration, too complex for now * OTA support * Remove echo again * Remove ssl again * GPIOFlags updates * Rename esphal and ICACHE_RAM_ATTR * Make ESP32 arduino compilable again * Fix GPIO flags * Complete pin registry refactor and fixes * Fixes to make test1 compile * Remove sdkconfig file * Ignore sdkconfig file * Fixes in reviewing * Make test2 compile * Make test4 compile * Make test5 compile * Run clang-format * Fix lint errors * Use esp-idf APIs instead of btStart * Another round of fixes * Start implementing ESP8266 * Make test3 compile * Guard esp8266 code * Lint * Reformat * Fixes * Fixes v2 * more fixes * ESP-IDF tidy target * Convert ARDUINO_ARCH_ESPxx * Update WiFiSignalSensor * Update time ifdefs * OTA needs millis from hal * RestartSwitch needs delay from hal * ESP-IDF Uart * Fix OTA blank password * Allow setting sdkconfig * Fix idf partitions and allow setting sdkconfig from yaml * Re-add read/write compat APIs and fix esp8266 uart * Fix esp8266 store log strings in flash * Fix ESP32 arduino preferences not initialized * Update ifdefs * Change how sdkconfig change is detected * Add checks to ci-custom and fix them * Run clang-format * Add esp-idf clang-tidy target and fix errors * Fixes from clang-tidy idf round 2 * Fixes from compiling tests with esp-idf * Run clang-format * Switch test5.yaml to esp-idf * Implement ESP8266 Preferences * Lint * Re-do PIO package version selection a bit * Fix arduinoespressif32 package version * Fix unit tests * Lint * Lint fixes * Fix readv/writev not defined * Fix graphing component * Re-add all old options from core/config.py Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
@@ -7,6 +7,7 @@ from esphome import pins, automation
|
||||
from esphome.const import (
|
||||
CONF_BAUD_RATE,
|
||||
CONF_ID,
|
||||
CONF_NUMBER,
|
||||
CONF_RX_PIN,
|
||||
CONF_TX_PIN,
|
||||
CONF_UART_ID,
|
||||
@@ -18,7 +19,16 @@ from esphome.core import CORE
|
||||
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
uart_ns = cg.esphome_ns.namespace("uart")
|
||||
UARTComponent = uart_ns.class_("UARTComponent", cg.Component)
|
||||
UARTComponent = uart_ns.class_("UARTComponent")
|
||||
|
||||
IDFUARTComponent = uart_ns.class_("IDFUARTComponent", UARTComponent, cg.Component)
|
||||
ESP32ArduinoUARTComponent = uart_ns.class_(
|
||||
"ESP32ArduinoUARTComponent", UARTComponent, cg.Component
|
||||
)
|
||||
ESP8266UartComponent = uart_ns.class_(
|
||||
"ESP8266UartComponent", UARTComponent, cg.Component
|
||||
)
|
||||
|
||||
UARTDevice = uart_ns.class_("UARTDevice")
|
||||
UARTWriteAction = uart_ns.class_("UARTWriteAction", automation.Action)
|
||||
MULTI_CONF = True
|
||||
@@ -37,12 +47,23 @@ def validate_raw_data(value):
|
||||
|
||||
|
||||
def validate_rx_pin(value):
|
||||
value = pins.input_pin(value)
|
||||
if CORE.is_esp8266 and value >= 16:
|
||||
value = pins.internal_gpio_input_pin_schema(value)
|
||||
if CORE.is_esp8266 and value[CONF_NUMBER] >= 16:
|
||||
raise cv.Invalid("Pins GPIO16 and GPIO17 cannot be used as RX pins on ESP8266.")
|
||||
return value
|
||||
|
||||
|
||||
def _uart_declare_type(value):
|
||||
if CORE.is_esp8266:
|
||||
return cv.declare_id(ESP8266UartComponent)(value)
|
||||
if CORE.is_esp32:
|
||||
if CORE.using_arduino:
|
||||
return cv.declare_id(ESP32ArduinoUARTComponent)(value)
|
||||
if CORE.using_esp_idf:
|
||||
return cv.declare_id(IDFUARTComponent)(value)
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
UARTParityOptions = uart_ns.enum("UARTParityOptions")
|
||||
UART_PARITY_OPTIONS = {
|
||||
"NONE": UARTParityOptions.UART_CONFIG_PARITY_NONE,
|
||||
@@ -57,19 +78,19 @@ CONF_PARITY = "parity"
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(UARTComponent),
|
||||
cv.GenerateID(): _uart_declare_type,
|
||||
cv.Required(CONF_BAUD_RATE): cv.int_range(min=1),
|
||||
cv.Optional(CONF_TX_PIN): pins.output_pin,
|
||||
cv.Optional(CONF_TX_PIN): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(CONF_RX_PIN): validate_rx_pin,
|
||||
cv.Optional(CONF_RX_BUFFER_SIZE, default=256): cv.validate_bytes,
|
||||
cv.SplitDefault(CONF_INVERT, esp32=False): cv.All(
|
||||
cv.only_on_esp32, cv.boolean
|
||||
),
|
||||
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
|
||||
),
|
||||
cv.Optional(CONF_INVERT): cv.invalid(
|
||||
"This option has been removed. Please instead use invert in the tx/rx pin schemas."
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN),
|
||||
@@ -84,12 +105,12 @@ async def to_code(config):
|
||||
cg.add(var.set_baud_rate(config[CONF_BAUD_RATE]))
|
||||
|
||||
if CONF_TX_PIN in config:
|
||||
cg.add(var.set_tx_pin(config[CONF_TX_PIN]))
|
||||
tx_pin = await cg.gpio_pin_expression(config[CONF_TX_PIN])
|
||||
cg.add(var.set_tx_pin(tx_pin))
|
||||
if CONF_RX_PIN in config:
|
||||
cg.add(var.set_rx_pin(config[CONF_RX_PIN]))
|
||||
rx_pin = await cg.gpio_pin_expression(config[CONF_RX_PIN])
|
||||
cg.add(var.set_rx_pin(rx_pin))
|
||||
cg.add(var.set_rx_buffer_size(config[CONF_RX_BUFFER_SIZE]))
|
||||
if CONF_INVERT in config:
|
||||
cg.add(var.set_invert(config[CONF_INVERT]))
|
||||
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]))
|
||||
|
@@ -4,62 +4,28 @@
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#ifdef USE_LOGGER
|
||||
#include "esphome/components/logger/logger.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
|
||||
static const char *const TAG = "uart";
|
||||
|
||||
size_t UARTComponent::write(uint8_t data) {
|
||||
this->write_byte(data);
|
||||
return 1;
|
||||
}
|
||||
int UARTComponent::read() {
|
||||
uint8_t data;
|
||||
if (!this->read_byte(&data))
|
||||
return -1;
|
||||
return data;
|
||||
}
|
||||
int UARTComponent::peek() {
|
||||
uint8_t data;
|
||||
if (!this->peek_byte(&data))
|
||||
return -1;
|
||||
return data;
|
||||
}
|
||||
|
||||
void UARTComponent::check_logger_conflict_() {
|
||||
#ifdef USE_LOGGER
|
||||
if (this->hw_serial_ == nullptr || logger::global_logger->get_baud_rate() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->hw_serial_ == logger::global_logger->get_hw_serial()) {
|
||||
ESP_LOGW(TAG, " You're using the same serial port for logging and the UART component. Please "
|
||||
"disable logging over the serial port by setting logger->baud_rate to 0.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void UARTDevice::check_uart_settings(uint32_t baud_rate, uint8_t stop_bits, UARTParityOptions parity,
|
||||
uint8_t data_bits) {
|
||||
if (this->parent_->baud_rate_ != baud_rate) {
|
||||
if (this->parent_->get_baud_rate() != 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_->get_baud_rate());
|
||||
}
|
||||
if (this->parent_->stop_bits_ != stop_bits) {
|
||||
if (this->parent_->get_stop_bits() != 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_->get_stop_bits());
|
||||
}
|
||||
if (this->parent_->data_bits_ != data_bits) {
|
||||
if (this->parent_->get_data_bits() != data_bits) {
|
||||
ESP_LOGE(TAG, " Invalid number of data bits: Integration requested %u data bits but you have %u!", data_bits,
|
||||
this->parent_->data_bits_);
|
||||
this->parent_->get_data_bits());
|
||||
}
|
||||
if (this->parent_->parity_ != parity) {
|
||||
if (this->parent_->get_parity() != parity) {
|
||||
ESP_LOGE(TAG, " Invalid parity: Integration requested parity %s but you have %s!",
|
||||
LOG_STR_ARG(parity_to_str(parity)), LOG_STR_ARG(parity_to_str(this->parent_->parity_)));
|
||||
LOG_STR_ARG(parity_to_str(parity)), LOG_STR_ARG(parity_to_str(this->parent_->get_parity())));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,132 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <HardwareSerial.h>
|
||||
#include "esphome/core/esphal.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "uart_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
|
||||
enum UARTParityOptions {
|
||||
UART_CONFIG_PARITY_NONE,
|
||||
UART_CONFIG_PARITY_EVEN,
|
||||
UART_CONFIG_PARITY_ODD,
|
||||
};
|
||||
|
||||
const LogString *parity_to_str(UARTParityOptions parity);
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
class ESP8266SoftwareSerial {
|
||||
public:
|
||||
void setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_rate, uint8_t stop_bits, uint32_t data_bits,
|
||||
UARTParityOptions parity, size_t rx_buffer_size);
|
||||
|
||||
uint8_t read_byte();
|
||||
uint8_t peek_byte();
|
||||
|
||||
void flush();
|
||||
|
||||
void write_byte(uint8_t data);
|
||||
|
||||
int available();
|
||||
|
||||
GPIOPin *gpio_tx_pin_{nullptr};
|
||||
GPIOPin *gpio_rx_pin_{nullptr};
|
||||
|
||||
protected:
|
||||
static void gpio_intr(ESP8266SoftwareSerial *arg);
|
||||
|
||||
void wait_(uint32_t *wait, const uint32_t &start);
|
||||
bool read_bit_(uint32_t *wait, const uint32_t &start);
|
||||
void write_bit_(bool bit, uint32_t *wait, const uint32_t &start);
|
||||
|
||||
uint32_t bit_time_{0};
|
||||
uint8_t *rx_buffer_{nullptr};
|
||||
size_t rx_buffer_size_;
|
||||
volatile size_t rx_in_pos_{0};
|
||||
size_t rx_out_pos_{0};
|
||||
uint8_t stop_bits_;
|
||||
uint8_t data_bits_;
|
||||
UARTParityOptions parity_;
|
||||
ISRInternalGPIOPin *tx_pin_{nullptr};
|
||||
ISRInternalGPIOPin *rx_pin_{nullptr};
|
||||
};
|
||||
#endif
|
||||
|
||||
class UARTComponent : public Component, public Stream {
|
||||
public:
|
||||
void set_baud_rate(uint32_t baud_rate) { baud_rate_ = baud_rate; }
|
||||
uint32_t get_baud_rate() const { return baud_rate_; }
|
||||
|
||||
uint32_t get_config();
|
||||
|
||||
void setup() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void write_byte(uint8_t data);
|
||||
|
||||
void write_array(const uint8_t *data, size_t len);
|
||||
void write_array(const std::vector<uint8_t> &data) { this->write_array(&data[0], data.size()); }
|
||||
|
||||
void write_str(const char *str);
|
||||
|
||||
bool peek_byte(uint8_t *data);
|
||||
|
||||
bool read_byte(uint8_t *data);
|
||||
|
||||
bool read_array(uint8_t *data, size_t len);
|
||||
|
||||
int available() override;
|
||||
|
||||
/// Block until all bytes have been written to the UART bus.
|
||||
void flush() override;
|
||||
|
||||
float get_setup_priority() const override { return setup_priority::BUS; }
|
||||
|
||||
size_t write(uint8_t data) override;
|
||||
int read() override;
|
||||
int peek() override;
|
||||
|
||||
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_buffer_size(size_t rx_buffer_size) { this->rx_buffer_size_ = rx_buffer_size; }
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
void set_invert(bool invert) { this->invert_ = invert; }
|
||||
#endif
|
||||
void set_stop_bits(uint8_t stop_bits) { this->stop_bits_ = stop_bits; }
|
||||
void set_data_bits(uint8_t data_bits) { this->data_bits_ = data_bits; }
|
||||
void set_parity(UARTParityOptions parity) { this->parity_ = parity; }
|
||||
|
||||
protected:
|
||||
void check_logger_conflict_();
|
||||
bool check_read_timeout_(size_t len = 1);
|
||||
friend class UARTDevice;
|
||||
|
||||
HardwareSerial *hw_serial_{nullptr};
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
ESP8266SoftwareSerial *sw_serial_{nullptr};
|
||||
#endif
|
||||
optional<uint8_t> tx_pin_;
|
||||
optional<uint8_t> rx_pin_;
|
||||
size_t rx_buffer_size_;
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
bool invert_;
|
||||
#endif
|
||||
uint32_t baud_rate_;
|
||||
uint8_t stop_bits_;
|
||||
uint8_t data_bits_;
|
||||
UARTParityOptions parity_;
|
||||
|
||||
private:
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
static bool serial0InUse;
|
||||
#endif
|
||||
};
|
||||
|
||||
class UARTDevice : public Stream {
|
||||
class UARTDevice {
|
||||
public:
|
||||
UARTDevice() = default;
|
||||
UARTDevice(UARTComponent *parent) : parent_(parent) {}
|
||||
@@ -155,13 +38,27 @@ class UARTDevice : public Stream {
|
||||
return res;
|
||||
}
|
||||
|
||||
int available() override { return this->parent_->available(); }
|
||||
int available() { return this->parent_->available(); }
|
||||
|
||||
void flush() override { return this->parent_->flush(); }
|
||||
void flush() { return this->parent_->flush(); }
|
||||
|
||||
size_t write(uint8_t data) override { return this->parent_->write(data); }
|
||||
int read() override { return this->parent_->read(); }
|
||||
int peek() override { return this->parent_->peek(); }
|
||||
// Compat APIs
|
||||
int read() {
|
||||
uint8_t data;
|
||||
if (!read_byte(&data))
|
||||
return -1;
|
||||
return data;
|
||||
}
|
||||
size_t write(uint8_t data) {
|
||||
write_byte(data);
|
||||
return 1;
|
||||
}
|
||||
int peek() {
|
||||
uint8_t data;
|
||||
if (!peek_byte(&data))
|
||||
return -1;
|
||||
return data;
|
||||
}
|
||||
|
||||
/// 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,
|
||||
|
24
esphome/components/uart/uart_component.cpp
Normal file
24
esphome/components/uart/uart_component.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "uart_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
|
||||
static const char *const TAG = "uart";
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
67
esphome/components/uart/uart_component.h
Normal file
67
esphome/components/uart/uart_component.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
|
||||
enum UARTParityOptions {
|
||||
UART_CONFIG_PARITY_NONE,
|
||||
UART_CONFIG_PARITY_EVEN,
|
||||
UART_CONFIG_PARITY_ODD,
|
||||
};
|
||||
|
||||
const LogString *parity_to_str(UARTParityOptions parity);
|
||||
|
||||
class UARTComponent {
|
||||
public:
|
||||
void write_array(const std::vector<uint8_t> &data) { this->write_array(&data[0], data.size()); }
|
||||
void write_byte(uint8_t data) { this->write_array(&data, 1); };
|
||||
void write_str(const char *str) {
|
||||
const auto *data = reinterpret_cast<const uint8_t *>(str);
|
||||
this->write_array(data, strlen(str));
|
||||
};
|
||||
|
||||
virtual void write_array(const uint8_t *data, size_t len) = 0;
|
||||
|
||||
bool read_byte(uint8_t *data) { return this->read_array(data, 1); };
|
||||
virtual bool peek_byte(uint8_t *data) = 0;
|
||||
virtual bool read_array(uint8_t *data, size_t len) = 0;
|
||||
|
||||
/// Return available number of bytes.
|
||||
virtual int available() = 0;
|
||||
/// Block until all bytes have been written to the UART bus.
|
||||
virtual void flush() = 0;
|
||||
|
||||
void set_tx_pin(InternalGPIOPin *tx_pin) { this->tx_pin_ = tx_pin; }
|
||||
void set_rx_pin(InternalGPIOPin *rx_pin) { this->rx_pin_ = rx_pin; }
|
||||
void set_rx_buffer_size(size_t rx_buffer_size) { this->rx_buffer_size_ = rx_buffer_size; }
|
||||
|
||||
void set_stop_bits(uint8_t stop_bits) { this->stop_bits_ = stop_bits; }
|
||||
uint8_t get_stop_bits() const { return this->stop_bits_; }
|
||||
void set_data_bits(uint8_t data_bits) { this->data_bits_ = data_bits; }
|
||||
uint8_t get_data_bits() const { return this->data_bits_; }
|
||||
void set_parity(UARTParityOptions parity) { this->parity_ = parity; }
|
||||
UARTParityOptions get_parity() const { return this->parity_; }
|
||||
void set_baud_rate(uint32_t baud_rate) { baud_rate_ = baud_rate; }
|
||||
uint32_t get_baud_rate() const { return baud_rate_; }
|
||||
|
||||
protected:
|
||||
virtual void check_logger_conflict() = 0;
|
||||
bool check_read_timeout_(size_t len = 1);
|
||||
|
||||
InternalGPIOPin *tx_pin_;
|
||||
InternalGPIOPin *rx_pin_;
|
||||
size_t rx_buffer_size_;
|
||||
uint32_t baud_rate_;
|
||||
uint8_t stop_bits_;
|
||||
uint8_t data_bits_;
|
||||
UARTParityOptions parity_;
|
||||
};
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
@@ -1,13 +1,17 @@
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include "uart.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "uart_component_esp32_arduino.h"
|
||||
|
||||
#ifdef USE_LOGGER
|
||||
#include "esphome/components/logger/logger.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
static const char *const TAG = "uart_esp32";
|
||||
static const char *const TAG = "uart.arduino_esp32";
|
||||
|
||||
static const uint32_t UART_PARITY_EVEN = 0 << 0;
|
||||
static const uint32_t UART_PARITY_ODD = 1 << 0;
|
||||
@@ -20,7 +24,7 @@ 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 ESP32ArduinoUARTComponent::get_config() {
|
||||
uint32_t config = 0;
|
||||
|
||||
/*
|
||||
@@ -67,71 +71,63 @@ uint32_t UARTComponent::get_config() {
|
||||
return config;
|
||||
}
|
||||
|
||||
void UARTComponent::setup() {
|
||||
void ESP32ArduinoUARTComponent::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.
|
||||
bool is_default_tx, is_default_rx;
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32C3
|
||||
if (this->tx_pin_.value_or(21) == 21 && this->rx_pin_.value_or(20) == 20) {
|
||||
is_default_tx = tx_pin_ == nullptr || tx_pin_->get_pin() == 21;
|
||||
is_default_rx = rx_pin_ == nullptr || rx_pin_->get_pin() == 20;
|
||||
#else
|
||||
if (this->tx_pin_.value_or(1) == 1 && this->rx_pin_.value_or(3) == 3) {
|
||||
is_default_tx = tx_pin_ == nullptr || tx_pin_->get_pin() == 1;
|
||||
is_default_rx = rx_pin_ == nullptr || rx_pin_->get_pin() == 3;
|
||||
#endif
|
||||
if (is_default_tx && is_default_rx) {
|
||||
this->hw_serial_ = &Serial;
|
||||
} else {
|
||||
static uint8_t next_uart_num = 1;
|
||||
this->hw_serial_ = new HardwareSerial(next_uart_num++); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
}
|
||||
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, this->invert_);
|
||||
int8_t tx = this->tx_pin_ != nullptr ? this->tx_pin_->get_pin() : -1;
|
||||
int8_t rx = this->rx_pin_ != nullptr ? this->rx_pin_->get_pin() : -1;
|
||||
bool invert = false;
|
||||
if (tx_pin_ != nullptr && tx_pin_->is_inverted())
|
||||
invert = true;
|
||||
if (rx_pin_ != nullptr && rx_pin_->is_inverted())
|
||||
invert = true;
|
||||
this->hw_serial_->begin(this->baud_rate_, get_config(), rx, tx, invert);
|
||||
this->hw_serial_->setRxBufferSize(this->rx_buffer_size_);
|
||||
}
|
||||
|
||||
void UARTComponent::dump_config() {
|
||||
void ESP32ArduinoUARTComponent::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_);
|
||||
LOG_PIN(" TX Pin: ", tx_pin_);
|
||||
LOG_PIN(" RX Pin: ", rx_pin_);
|
||||
if (this->rx_pin_ != nullptr) {
|
||||
ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_);
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_);
|
||||
ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_);
|
||||
ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_)));
|
||||
ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_);
|
||||
this->check_logger_conflict_();
|
||||
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) {
|
||||
void ESP32ArduinoUARTComponent::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) {
|
||||
bool ESP32ArduinoUARTComponent::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) {
|
||||
bool ESP32ArduinoUARTComponent::read_array(uint8_t *data, size_t len) {
|
||||
if (!this->check_read_timeout_(len))
|
||||
return false;
|
||||
this->hw_serial_->readBytes(data, len);
|
||||
@@ -141,26 +137,25 @@ bool UARTComponent::read_array(uint8_t *data, size_t len) {
|
||||
|
||||
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() {
|
||||
int ESP32ArduinoUARTComponent::available() { return this->hw_serial_->available(); }
|
||||
void ESP32ArduinoUARTComponent::flush() {
|
||||
ESP_LOGVV(TAG, " Flushing...");
|
||||
this->hw_serial_->flush();
|
||||
}
|
||||
|
||||
void ESP32ArduinoUARTComponent::check_logger_conflict() {
|
||||
#ifdef USE_LOGGER
|
||||
if (this->hw_serial_ == nullptr || logger::global_logger->get_baud_rate() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->hw_serial_ == logger::global_logger->get_hw_serial()) {
|
||||
ESP_LOGW(TAG, " You're using the same serial port for logging and the UART component. Please "
|
||||
"disable logging over the serial port by setting logger->baud_rate to 0.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
#endif // ARDUINO_ARCH_ESP32
|
||||
#endif // USE_ESP32_FRAMEWORK_ARDUINO
|
40
esphome/components/uart/uart_component_esp32_arduino.h
Normal file
40
esphome/components/uart/uart_component_esp32_arduino.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
|
||||
|
||||
#include <HardwareSerial.h>
|
||||
#include <vector>
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "uart_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
|
||||
class ESP32ArduinoUARTComponent : public UARTComponent, public Component {
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::BUS; }
|
||||
|
||||
void write_array(const uint8_t *data, size_t len) override;
|
||||
|
||||
bool peek_byte(uint8_t *data) override;
|
||||
bool read_array(uint8_t *data, size_t len) override;
|
||||
|
||||
int available() override;
|
||||
void flush() override;
|
||||
|
||||
uint32_t get_config();
|
||||
|
||||
protected:
|
||||
void check_logger_conflict() override;
|
||||
|
||||
HardwareSerial *hw_serial_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ESP32_FRAMEWORK_ARDUINO
|
@@ -1,9 +1,9 @@
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
#include "uart.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#ifdef USE_ESP8266
|
||||
#include "uart_component_esp8266.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef USE_LOGGER
|
||||
#include "esphome/components/logger/logger.h"
|
||||
@@ -12,10 +12,10 @@
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
|
||||
static const char *const TAG = "uart_esp8266";
|
||||
bool UARTComponent::serial0InUse = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static const char *const TAG = "uart.arduino_esp8266";
|
||||
bool ESP8266UartComponent::serial0InUse = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
uint32_t UARTComponent::get_config() {
|
||||
uint32_t ESP8266UartComponent::get_config() {
|
||||
uint32_t config = 0;
|
||||
|
||||
if (this->parity_ == UART_CONFIG_PARITY_NONE)
|
||||
@@ -48,15 +48,15 @@ uint32_t UARTComponent::get_config() {
|
||||
return config;
|
||||
}
|
||||
|
||||
void UARTComponent::setup() {
|
||||
void ESP8266UartComponent::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 (!UARTComponent::serial0InUse && this->tx_pin_.value_or(1) == 1 &&
|
||||
this->rx_pin_.value_or(3) == 3
|
||||
if (!ESP8266UartComponent::serial0InUse && (tx_pin_ == nullptr || tx_pin_->get_pin() == 1) &&
|
||||
(rx_pin_ == nullptr || rx_pin_->get_pin() == 3)
|
||||
#ifdef USE_LOGGER
|
||||
// we will use UART0 if logger isn't using it in swapped mode
|
||||
&& (logger::global_logger->get_hw_serial() == nullptr ||
|
||||
@@ -66,9 +66,9 @@ void UARTComponent::setup() {
|
||||
this->hw_serial_ = &Serial;
|
||||
this->hw_serial_->begin(this->baud_rate_, config);
|
||||
this->hw_serial_->setRxBufferSize(this->rx_buffer_size_);
|
||||
UARTComponent::serial0InUse = true;
|
||||
} else if (!UARTComponent::serial0InUse && this->tx_pin_.value_or(15) == 15 &&
|
||||
this->rx_pin_.value_or(13) == 13
|
||||
ESP8266UartComponent::serial0InUse = true;
|
||||
} else if (!ESP8266UartComponent::serial0InUse && (tx_pin_ == nullptr || tx_pin_->get_pin() == 15) &&
|
||||
(rx_pin_ == nullptr || rx_pin_->get_pin() == 13)
|
||||
#ifdef USE_LOGGER
|
||||
// we will use UART0 swapped if logger isn't using it in regular mode
|
||||
&& (logger::global_logger->get_hw_serial() == nullptr ||
|
||||
@@ -79,27 +79,23 @@ void UARTComponent::setup() {
|
||||
this->hw_serial_->begin(this->baud_rate_, config);
|
||||
this->hw_serial_->setRxBufferSize(this->rx_buffer_size_);
|
||||
this->hw_serial_->swap();
|
||||
UARTComponent::serial0InUse = true;
|
||||
} else if (this->tx_pin_.value_or(2) == 2 && this->rx_pin_.value_or(8) == 8) {
|
||||
ESP8266UartComponent::serial0InUse = true;
|
||||
} else if ((tx_pin_ == nullptr || tx_pin_->get_pin() == 2) && (rx_pin_ == nullptr || rx_pin_->get_pin() == 8)) {
|
||||
this->hw_serial_ = &Serial1;
|
||||
this->hw_serial_->begin(this->baud_rate_, config);
|
||||
this->hw_serial_->setRxBufferSize(this->rx_buffer_size_);
|
||||
} else {
|
||||
this->sw_serial_ = new ESP8266SoftwareSerial(); // NOLINT
|
||||
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->data_bits_, this->parity_,
|
||||
this->sw_serial_->setup(tx_pin_, rx_pin_, this->baud_rate_, this->stop_bits_, this->data_bits_, this->parity_,
|
||||
this->rx_buffer_size_);
|
||||
}
|
||||
}
|
||||
|
||||
void UARTComponent::dump_config() {
|
||||
void ESP8266UartComponent::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_);
|
||||
LOG_PIN(" TX Pin: ", tx_pin_);
|
||||
LOG_PIN(" RX Pin: ", rx_pin_);
|
||||
if (this->rx_pin_ != nullptr) {
|
||||
ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); // NOLINT
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_);
|
||||
@@ -111,18 +107,23 @@ void UARTComponent::dump_config() {
|
||||
} else {
|
||||
ESP_LOGCONFIG(TAG, " Using software serial");
|
||||
}
|
||||
this->check_logger_conflict_();
|
||||
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);
|
||||
void ESP8266UartComponent::check_logger_conflict() {
|
||||
#ifdef USE_LOGGER
|
||||
if (this->hw_serial_ == nullptr || logger::global_logger->get_baud_rate() == 0) {
|
||||
return;
|
||||
}
|
||||
ESP_LOGVV(TAG, " Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data), data);
|
||||
|
||||
if (this->hw_serial_ == logger::global_logger->get_hw_serial()) {
|
||||
ESP_LOGW(TAG, " You're using the same serial port for logging and the UART component. Please "
|
||||
"disable logging over the serial port by setting logger->baud_rate to 0.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
void UARTComponent::write_array(const uint8_t *data, size_t len) {
|
||||
|
||||
void ESP8266UartComponent::write_array(const uint8_t *data, size_t len) {
|
||||
if (this->hw_serial_ != nullptr) {
|
||||
this->hw_serial_->write(data, len);
|
||||
} else {
|
||||
@@ -133,28 +134,7 @@ void UARTComponent::write_array(const uint8_t *data, size_t len) {
|
||||
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) {
|
||||
bool ESP8266UartComponent::peek_byte(uint8_t *data) {
|
||||
if (!this->check_read_timeout_())
|
||||
return false;
|
||||
if (this->hw_serial_ != nullptr) {
|
||||
@@ -164,7 +144,7 @@ bool UARTComponent::peek_byte(uint8_t *data) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool UARTComponent::read_array(uint8_t *data, size_t len) {
|
||||
bool ESP8266UartComponent::read_array(uint8_t *data, size_t len) {
|
||||
if (!this->check_read_timeout_(len))
|
||||
return false;
|
||||
if (this->hw_serial_ != nullptr) {
|
||||
@@ -179,28 +159,14 @@ bool UARTComponent::read_array(uint8_t *data, size_t len) {
|
||||
|
||||
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() {
|
||||
int ESP8266UartComponent::available() {
|
||||
if (this->hw_serial_ != nullptr) {
|
||||
return this->hw_serial_->available();
|
||||
} else {
|
||||
return this->sw_serial_->available();
|
||||
}
|
||||
}
|
||||
void UARTComponent::flush() {
|
||||
void ESP8266UartComponent::flush() {
|
||||
ESP_LOGVV(TAG, " Flushing...");
|
||||
if (this->hw_serial_ != nullptr) {
|
||||
this->hw_serial_->flush();
|
||||
@@ -208,32 +174,31 @@ void UARTComponent::flush() {
|
||||
this->sw_serial_->flush();
|
||||
}
|
||||
}
|
||||
void ESP8266SoftwareSerial::setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_rate, uint8_t stop_bits,
|
||||
uint32_t data_bits, UARTParityOptions parity, size_t rx_buffer_size) {
|
||||
void ESP8266SoftwareSerial::setup(InternalGPIOPin *tx_pin, InternalGPIOPin *rx_pin, uint32_t baud_rate,
|
||||
uint8_t stop_bits, uint32_t data_bits, UARTParityOptions parity,
|
||||
size_t rx_buffer_size) {
|
||||
this->bit_time_ = F_CPU / baud_rate;
|
||||
this->rx_buffer_size_ = rx_buffer_size;
|
||||
this->stop_bits_ = stop_bits;
|
||||
this->data_bits_ = data_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 (tx_pin != nullptr) {
|
||||
gpio_tx_pin_ = tx_pin;
|
||||
gpio_tx_pin_->setup();
|
||||
tx_pin_ = gpio_tx_pin_->to_isr();
|
||||
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_]; // NOLINT
|
||||
pin.attach_interrupt(ESP8266SoftwareSerial::gpio_intr, this, FALLING);
|
||||
if (rx_pin != nullptr) {
|
||||
gpio_rx_pin_ = rx_pin;
|
||||
gpio_rx_pin_->setup();
|
||||
rx_pin_ = gpio_rx_pin_->to_isr();
|
||||
rx_buffer_ = new uint8_t[this->rx_buffer_size_]; // NOLINT
|
||||
gpio_rx_pin_->attach_interrupt(ESP8266SoftwareSerial::gpio_intr, this, gpio::INTERRUPT_FALLING_EDGE);
|
||||
}
|
||||
}
|
||||
void ICACHE_RAM_ATTR ESP8266SoftwareSerial::gpio_intr(ESP8266SoftwareSerial *arg) {
|
||||
void IRAM_ATTR ESP8266SoftwareSerial::gpio_intr(ESP8266SoftwareSerial *arg) {
|
||||
uint32_t wait = arg->bit_time_ + arg->bit_time_ / 3 - 500;
|
||||
const uint32_t start = ESP.getCycleCount(); // NOLINT(readability-static-accessed-through-instance)
|
||||
const uint32_t start = arch_get_cpu_cycle_count();
|
||||
uint8_t rec = 0;
|
||||
// Manually unroll the loop
|
||||
for (int i = 0; i < arg->data_bits_; i++)
|
||||
@@ -254,10 +219,10 @@ void ICACHE_RAM_ATTR ESP8266SoftwareSerial::gpio_intr(ESP8266SoftwareSerial *arg
|
||||
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();
|
||||
arg->rx_pin_.clear_interrupt();
|
||||
}
|
||||
void ICACHE_RAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) {
|
||||
if (this->tx_pin_ == nullptr) {
|
||||
void IRAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) {
|
||||
if (this->gpio_tx_pin_ == nullptr) {
|
||||
ESP_LOGE(TAG, "UART doesn't have TX pins set!");
|
||||
return;
|
||||
}
|
||||
@@ -273,7 +238,7 @@ void ICACHE_RAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) {
|
||||
{
|
||||
InterruptLock lock;
|
||||
uint32_t wait = this->bit_time_;
|
||||
const uint32_t start = ESP.getCycleCount(); // NOLINT(readability-static-accessed-through-instance)
|
||||
const uint32_t start = arch_get_cpu_cycle_count();
|
||||
// Start bit
|
||||
this->write_bit_(false, &wait, start);
|
||||
for (int i = 0; i < this->data_bits_; i++) {
|
||||
@@ -290,17 +255,17 @@ void ICACHE_RAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) {
|
||||
this->wait_(&wait, start);
|
||||
}
|
||||
}
|
||||
void ICACHE_RAM_ATTR ESP8266SoftwareSerial::wait_(uint32_t *wait, const uint32_t &start) {
|
||||
while (ESP.getCycleCount() - start < *wait) // NOLINT(readability-static-accessed-through-instance)
|
||||
void IRAM_ATTR ESP8266SoftwareSerial::wait_(uint32_t *wait, const uint32_t &start) {
|
||||
while (arch_get_cpu_cycle_count() - start < *wait)
|
||||
;
|
||||
*wait += this->bit_time_;
|
||||
}
|
||||
bool ICACHE_RAM_ATTR ESP8266SoftwareSerial::read_bit_(uint32_t *wait, const uint32_t &start) {
|
||||
bool IRAM_ATTR ESP8266SoftwareSerial::read_bit_(uint32_t *wait, const uint32_t &start) {
|
||||
this->wait_(wait, start);
|
||||
return this->rx_pin_->digital_read();
|
||||
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);
|
||||
void IRAM_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() {
|
||||
@@ -327,4 +292,4 @@ int ESP8266SoftwareSerial::available() {
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
#endif // ARDUINO_ARCH_ESP8266
|
||||
#endif // USE_ESP8266
|
79
esphome/components/uart/uart_component_esp8266.h
Normal file
79
esphome/components/uart/uart_component_esp8266.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
|
||||
#include <HardwareSerial.h>
|
||||
#include <vector>
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "uart_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
|
||||
class ESP8266SoftwareSerial {
|
||||
public:
|
||||
void setup(InternalGPIOPin *tx_pin, InternalGPIOPin *rx_pin, uint32_t baud_rate, uint8_t stop_bits,
|
||||
uint32_t data_bits, UARTParityOptions parity, size_t rx_buffer_size);
|
||||
|
||||
uint8_t read_byte();
|
||||
uint8_t peek_byte();
|
||||
|
||||
void flush();
|
||||
|
||||
void write_byte(uint8_t data);
|
||||
|
||||
int available();
|
||||
|
||||
protected:
|
||||
static void gpio_intr(ESP8266SoftwareSerial *arg);
|
||||
|
||||
void wait_(uint32_t *wait, const uint32_t &start);
|
||||
bool read_bit_(uint32_t *wait, const uint32_t &start);
|
||||
void write_bit_(bool bit, uint32_t *wait, const uint32_t &start);
|
||||
|
||||
uint32_t bit_time_{0};
|
||||
uint8_t *rx_buffer_{nullptr};
|
||||
size_t rx_buffer_size_;
|
||||
volatile size_t rx_in_pos_{0};
|
||||
size_t rx_out_pos_{0};
|
||||
uint8_t stop_bits_;
|
||||
uint8_t data_bits_;
|
||||
UARTParityOptions parity_;
|
||||
InternalGPIOPin *gpio_tx_pin_{nullptr};
|
||||
ISRInternalGPIOPin tx_pin_;
|
||||
InternalGPIOPin *gpio_rx_pin_{nullptr};
|
||||
ISRInternalGPIOPin rx_pin_;
|
||||
};
|
||||
|
||||
class ESP8266UartComponent : public UARTComponent, public Component {
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::BUS; }
|
||||
|
||||
void write_array(const uint8_t *data, size_t len) override;
|
||||
|
||||
bool peek_byte(uint8_t *data) override;
|
||||
bool read_array(uint8_t *data, size_t len) override;
|
||||
|
||||
int available() override;
|
||||
void flush() override;
|
||||
|
||||
uint32_t get_config();
|
||||
|
||||
protected:
|
||||
void check_logger_conflict() override;
|
||||
|
||||
HardwareSerial *hw_serial_{nullptr};
|
||||
ESP8266SoftwareSerial *sw_serial_{nullptr};
|
||||
|
||||
private:
|
||||
static bool serial0InUse;
|
||||
};
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ESP8266
|
201
esphome/components/uart/uart_component_esp_idf.cpp
Normal file
201
esphome/components/uart/uart_component_esp_idf.cpp
Normal file
@@ -0,0 +1,201 @@
|
||||
#ifdef USE_ESP_IDF
|
||||
|
||||
#include "uart_component_esp_idf.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef USE_LOGGER
|
||||
#include "esphome/components/logger/logger.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
static const char *const TAG = "uart.idf";
|
||||
|
||||
uart_config_t IDFUARTComponent::get_config() {
|
||||
uart_parity_t parity = UART_PARITY_DISABLE;
|
||||
if (this->parity_ == UART_CONFIG_PARITY_EVEN)
|
||||
parity = UART_PARITY_EVEN;
|
||||
else if (this->parity_ == UART_CONFIG_PARITY_ODD)
|
||||
parity = UART_PARITY_ODD;
|
||||
|
||||
uart_word_length_t data_bits;
|
||||
switch (this->data_bits_) {
|
||||
case 5:
|
||||
data_bits = UART_DATA_5_BITS;
|
||||
break;
|
||||
case 6:
|
||||
data_bits = UART_DATA_6_BITS;
|
||||
break;
|
||||
case 7:
|
||||
data_bits = UART_DATA_7_BITS;
|
||||
break;
|
||||
case 8:
|
||||
data_bits = UART_DATA_8_BITS;
|
||||
break;
|
||||
default:
|
||||
data_bits = UART_DATA_BITS_MAX;
|
||||
break;
|
||||
}
|
||||
|
||||
uart_config_t uart_config;
|
||||
uart_config.baud_rate = this->baud_rate_;
|
||||
uart_config.data_bits = data_bits;
|
||||
uart_config.parity = parity;
|
||||
uart_config.stop_bits = this->stop_bits_ == 1 ? UART_STOP_BITS_1 : UART_STOP_BITS_2;
|
||||
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
|
||||
uart_config.source_clk = UART_SCLK_APB;
|
||||
uart_config.rx_flow_ctrl_thresh = 122;
|
||||
|
||||
return uart_config;
|
||||
}
|
||||
|
||||
void IDFUARTComponent::setup() {
|
||||
static uint8_t next_uart_num = 0;
|
||||
#ifdef USE_LOGGER
|
||||
if (logger::global_logger->get_uart_num() == next_uart_num)
|
||||
next_uart_num++;
|
||||
#endif
|
||||
if (next_uart_num >= UART_NUM_MAX) {
|
||||
ESP_LOGW(TAG, "Maximum number of UART components created already.");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
this->uart_num_ = next_uart_num++;
|
||||
ESP_LOGCONFIG(TAG, "Setting up UART %u...", this->uart_num_);
|
||||
|
||||
this->lock_ = xSemaphoreCreateMutex();
|
||||
|
||||
xSemaphoreTake(this->lock_, portMAX_DELAY);
|
||||
|
||||
uart_config_t uart_config = this->get_config();
|
||||
esp_err_t err = uart_param_config(this->uart_num_, &uart_config);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "uart_param_config failed: %s", esp_err_to_name(err));
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
err = uart_driver_install(this->uart_num_, this->rx_buffer_size_, 0, 0, nullptr, 0);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "uart_driver_install failed: %s", esp_err_to_name(err));
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
int8_t tx = this->tx_pin_ != nullptr ? this->tx_pin_->get_pin() : -1;
|
||||
int8_t rx = this->rx_pin_ != nullptr ? this->rx_pin_->get_pin() : -1;
|
||||
|
||||
err = uart_set_pin(this->uart_num_, tx, rx, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "uart_set_pin failed: %s", esp_err_to_name(err));
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t invert = 0;
|
||||
if (this->tx_pin_ != nullptr && this->tx_pin_->is_inverted())
|
||||
invert |= UART_SIGNAL_TXD_INV;
|
||||
if (this->rx_pin_ != nullptr && this->rx_pin_->is_inverted())
|
||||
invert |= UART_SIGNAL_RXD_INV;
|
||||
|
||||
err = uart_set_line_inverse(this->uart_num_, invert);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "uart_set_line_inverse failed: %s", esp_err_to_name(err));
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
xSemaphoreGive(this->lock_);
|
||||
}
|
||||
|
||||
void IDFUARTComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "UART Bus:");
|
||||
ESP_LOGCONFIG(TAG, " Number: %u", this->uart_num_);
|
||||
LOG_PIN(" TX Pin: ", tx_pin_);
|
||||
LOG_PIN(" RX Pin: ", rx_pin_);
|
||||
if (this->rx_pin_ != nullptr) {
|
||||
ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_);
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_);
|
||||
ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_);
|
||||
ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_)));
|
||||
ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_);
|
||||
this->check_logger_conflict();
|
||||
}
|
||||
|
||||
void IDFUARTComponent::write_array(const uint8_t *data, size_t len) {
|
||||
xSemaphoreTake(this->lock_, portMAX_DELAY);
|
||||
uart_write_bytes(this->uart_num_, data, len);
|
||||
xSemaphoreGive(this->lock_);
|
||||
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]);
|
||||
}
|
||||
}
|
||||
bool IDFUARTComponent::peek_byte(uint8_t *data) {
|
||||
if (!this->check_read_timeout_())
|
||||
return false;
|
||||
xSemaphoreTake(this->lock_, portMAX_DELAY);
|
||||
if (this->has_peek_)
|
||||
*data = this->peek_byte_;
|
||||
else {
|
||||
int len = uart_read_bytes(this->uart_num_, data, 1, 20 / portTICK_RATE_MS);
|
||||
if (len == 0) {
|
||||
*data = 0;
|
||||
} else {
|
||||
this->has_peek_ = true;
|
||||
this->peek_byte_ = *data;
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(this->lock_);
|
||||
return true;
|
||||
}
|
||||
bool IDFUARTComponent::read_array(uint8_t *data, size_t len) {
|
||||
size_t length_to_read = len;
|
||||
if (!this->check_read_timeout_(len))
|
||||
return false;
|
||||
xSemaphoreTake(this->lock_, portMAX_DELAY);
|
||||
if (this->has_peek_) {
|
||||
length_to_read--;
|
||||
*data = this->peek_byte_;
|
||||
data++;
|
||||
this->has_peek_ = false;
|
||||
}
|
||||
if (length_to_read > 0)
|
||||
uart_read_bytes(this->uart_num_, data, length_to_read, 20 / portTICK_RATE_MS);
|
||||
|
||||
xSemaphoreGive(this->lock_);
|
||||
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;
|
||||
}
|
||||
|
||||
int IDFUARTComponent::available() {
|
||||
size_t available;
|
||||
|
||||
xSemaphoreTake(this->lock_, portMAX_DELAY);
|
||||
uart_get_buffered_data_len(this->uart_num_, &available);
|
||||
if (this->has_peek_)
|
||||
available++;
|
||||
xSemaphoreGive(this->lock_);
|
||||
|
||||
return available;
|
||||
}
|
||||
|
||||
void IDFUARTComponent::flush() {
|
||||
ESP_LOGVV(TAG, " Flushing...");
|
||||
xSemaphoreTake(this->lock_, portMAX_DELAY);
|
||||
uart_wait_tx_done(this->uart_num_, portMAX_DELAY);
|
||||
xSemaphoreGive(this->lock_);
|
||||
}
|
||||
|
||||
void IDFUARTComponent::check_logger_conflict() {}
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ESP32
|
39
esphome/components/uart/uart_component_esp_idf.h
Normal file
39
esphome/components/uart/uart_component_esp_idf.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ESP_IDF
|
||||
|
||||
#include <driver/uart.h>
|
||||
#include "esphome/core/component.h"
|
||||
#include "uart_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
|
||||
class IDFUARTComponent : public UARTComponent, public Component {
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::BUS; }
|
||||
|
||||
void write_array(const uint8_t *data, size_t len) override;
|
||||
|
||||
bool peek_byte(uint8_t *data) override;
|
||||
bool read_array(uint8_t *data, size_t len) override;
|
||||
|
||||
int available() override;
|
||||
void flush() override;
|
||||
|
||||
protected:
|
||||
void check_logger_conflict() override;
|
||||
uart_port_t uart_num_;
|
||||
uart_config_t get_config();
|
||||
SemaphoreHandle_t lock_;
|
||||
|
||||
bool has_peek_{false};
|
||||
uint8_t peek_byte_;
|
||||
};
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ESP_IDF
|
Reference in New Issue
Block a user