mirror of
https://github.com/esphome/esphome.git
synced 2025-09-28 08:02:23 +01:00
🏗 Merge C++ into python codebase (#504)
## Description: Move esphome-core codebase into esphome (and a bunch of other refactors). See https://github.com/esphome/feature-requests/issues/97 Yes this is a shit ton of work and no there's no way to automate it :( But it will be worth it 👍 Progress: - Core support (file copy etc): 80% - Base Abstractions (light, switch): ~50% - Integrations: ~10% - Working? Yes, (but only with ported components). Other refactors: - Moves all codegen related stuff into a single class: `esphome.codegen` (imported as `cg`) - Rework coroutine syntax - Move from `component/platform.py` to `domain/component.py` structure as with HA - Move all defaults out of C++ and into config validation. - Remove `make_...` helpers from Application class. Reason: Merge conflicts with every single new integration. - Pointer Variables are stored globally instead of locally in setup(). Reason: stack size limit. Future work: - Rework const.py - Move all `CONF_...` into a conf class (usage `conf.UPDATE_INTERVAL` vs `CONF_UPDATE_INTERVAL`). Reason: Less convoluted import block - Enable loading from `custom_components` folder. **Related issue (if applicable):** https://github.com/esphome/feature-requests/issues/97 **Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs#<esphome-docs PR number goes here> ## Checklist: - [ ] The code change is tested and works locally. - [ ] Tests have been added to verify that the new code works (under `tests/` folder). If user exposed functionality or configuration variables are added/changed: - [ ] Documentation added/updated in [esphomedocs](https://github.com/OttoWinter/esphomedocs).
This commit is contained in:
46
esphome/components/uart/__init__.py
Normal file
46
esphome/components/uart/__init__.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from esphome import pins
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
from esphome.const import CONF_BAUD_RATE, CONF_ID, CONF_RX_PIN, CONF_TX_PIN, CONF_UART_ID
|
||||
from esphome.core import CORE, coroutine
|
||||
|
||||
uart_ns = cg.esphome_ns.namespace('uart')
|
||||
UARTComponent = uart_ns.class_('UARTComponent', cg.Component)
|
||||
UARTDevice = uart_ns.class_('UARTDevice')
|
||||
MULTI_CONF = True
|
||||
|
||||
|
||||
def validate_rx_pin(value):
|
||||
value = pins.input_pin(value)
|
||||
if CORE.is_esp8266 and value >= 16:
|
||||
raise cv.Invalid("Pins GPIO16 and GPIO17 cannot be used as RX pins on ESP8266.")
|
||||
return value
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(cv.Schema({
|
||||
cv.GenerateID(): cv.declare_variable_id(UARTComponent),
|
||||
cv.Optional(CONF_TX_PIN): pins.output_pin,
|
||||
cv.Optional(CONF_RX_PIN): validate_rx_pin,
|
||||
cv.Required(CONF_BAUD_RATE): cv.positive_int,
|
||||
}).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID], config[CONF_BAUD_RATE])
|
||||
yield cg.register_component(var, config)
|
||||
|
||||
if CONF_TX_PIN in config:
|
||||
cg.add(var.set_tx_pin(config[CONF_TX_PIN]))
|
||||
if CONF_RX_PIN in config:
|
||||
cg.add(var.set_rx_pin(config[CONF_RX_PIN]))
|
||||
|
||||
|
||||
UART_DEVICE_SCHEMA = cv.Schema({
|
||||
cv.GenerateID(CONF_UART_ID): cv.use_variable_id(UARTComponent),
|
||||
})
|
||||
|
||||
|
||||
@coroutine
|
||||
def register_uart_device(var, config):
|
||||
parent = yield cg.get_variable(config[CONF_UART_ID])
|
||||
cg.add(var.set_uart_parent(parent))
|
38
esphome/components/uart/switch/__init__.py
Normal file
38
esphome/components/uart/switch/__init__.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import switch, uart
|
||||
from esphome.const import CONF_DATA, CONF_ID, CONF_INVERTED, CONF_NAME
|
||||
from esphome.core import HexInt
|
||||
from esphome.py_compat import text_type, binary_type, char_to_byte
|
||||
from .. import uart_ns
|
||||
|
||||
DEPENDENCIES = ['uart']
|
||||
|
||||
UARTSwitch = uart_ns.class_('UARTSwitch', switch.Switch, uart.UARTDevice)
|
||||
|
||||
|
||||
def validate_data(value):
|
||||
if isinstance(value, text_type):
|
||||
return value.encode('utf-8')
|
||||
if isinstance(value, str):
|
||||
return value
|
||||
if isinstance(value, list):
|
||||
return cv.Schema([cv.hex_uint8_t])(value)
|
||||
raise cv.Invalid("data must either be a string wrapped in quotes or a list of bytes")
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.nameable(switch.SWITCH_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_variable_id(UARTSwitch),
|
||||
cv.Required(CONF_DATA): validate_data,
|
||||
cv.Optional(CONF_INVERTED): cv.invalid("UART switches do not support inverted mode!"),
|
||||
}).extend(uart.UART_DEVICE_SCHEMA))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
data = config[CONF_DATA]
|
||||
if isinstance(data, binary_type):
|
||||
data = [HexInt(char_to_byte(x)) for x in data]
|
||||
var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], data)
|
||||
yield cg.register_component(var, config)
|
||||
yield switch.register_switch(var, config)
|
||||
yield uart.register_uart_device(var, config)
|
26
esphome/components/uart/switch/uart_switch.cpp
Normal file
26
esphome/components/uart/switch/uart_switch.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "uart_switch.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
|
||||
static const char *TAG = "uart.switch";
|
||||
|
||||
UARTSwitch::UARTSwitch(const std::string &name, const std::vector<uint8_t> &data)
|
||||
: switch_::Switch(name), data_(data) {}
|
||||
|
||||
void UARTSwitch::write_state(bool state) {
|
||||
if (!state) {
|
||||
this->publish_state(false);
|
||||
return;
|
||||
}
|
||||
|
||||
this->publish_state(true);
|
||||
ESP_LOGD(TAG, "'%s': Sending data...", this->get_name().c_str());
|
||||
this->write_array(this->data_.data(), this->data_.size());
|
||||
this->publish_state(false);
|
||||
}
|
||||
void UARTSwitch::dump_config() { LOG_SWITCH("", "UART Switch", this); }
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
22
esphome/components/uart/switch/uart_switch.h
Normal file
22
esphome/components/uart/switch/uart_switch.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
#include "esphome/components/switch/switch.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
|
||||
class UARTSwitch : public switch_::Switch, public UARTDevice, public Component {
|
||||
public:
|
||||
UARTSwitch(const std::string &name, const std::vector<uint8_t> &data);
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
void write_state(bool state) override;
|
||||
std::vector<uint8_t> data_;
|
||||
};
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
350
esphome/components/uart/uart.cpp
Normal file
350
esphome/components/uart/uart.cpp
Normal file
@@ -0,0 +1,350 @@
|
||||
#include "uart.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace 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 if (this->tx_pin_.value_or(9) == 9 && this->rx_pin_.value_or(10) == 10) {
|
||||
this->hw_serial_ = &Serial1;
|
||||
} else if (this->tx_pin_.value_or(16) == 16 && this->rx_pin_.value_or(17) == 17) {
|
||||
this->hw_serial_ = &Serial2;
|
||||
} 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_, SERIAL_8N1, 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_);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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.
|
||||
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_);
|
||||
} 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_);
|
||||
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_);
|
||||
} 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_);
|
||||
}
|
||||
}
|
||||
|
||||
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_);
|
||||
if (this->hw_serial_ != nullptr) {
|
||||
ESP_LOGCONFIG(TAG, " Using hardware serial interface.");
|
||||
} else {
|
||||
ESP_LOGCONFIG(TAG, " Using software serial");
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
disable_interrupts();
|
||||
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);
|
||||
enable_interrupts();
|
||||
}
|
||||
void ESP8266SoftwareSerial::wait_(uint32_t *wait, const uint32_t &start) {
|
||||
while (ESP.getCycleCount() - start < *wait)
|
||||
;
|
||||
*wait += this->bit_time_;
|
||||
}
|
||||
bool ESP8266SoftwareSerial::read_bit_(uint32_t *wait, const uint32_t &start) {
|
||||
this->wait_(wait, start);
|
||||
return this->rx_pin_->digital_read();
|
||||
}
|
||||
void 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() { this->rx_in_pos_ = this->rx_out_pos_ = 0; }
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
121
esphome/components/uart/uart.h
Normal file
121
esphome/components/uart/uart.h
Normal file
@@ -0,0 +1,121 @@
|
||||
#pragma once
|
||||
|
||||
#include <HardwareSerial.h>
|
||||
#include "esphome/core/esphal.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
class ESP8266SoftwareSerial {
|
||||
public:
|
||||
void setup(int8_t tx_pin, int8_t rx_pin, uint32_t baud_rate);
|
||||
|
||||
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);
|
||||
|
||||
inline void wait_(uint32_t *wait, const uint32_t &start);
|
||||
inline bool read_bit_(uint32_t *wait, const uint32_t &start);
|
||||
inline 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_{64};
|
||||
volatile size_t rx_in_pos_{0};
|
||||
size_t rx_out_pos_{0};
|
||||
ISRInternalGPIOPin *tx_pin_{nullptr};
|
||||
ISRInternalGPIOPin *rx_pin_{nullptr};
|
||||
};
|
||||
#endif
|
||||
|
||||
class UARTComponent : public Component, public Stream {
|
||||
public:
|
||||
UARTComponent(uint32_t baud_rate) : baud_rate_(baud_rate) {}
|
||||
|
||||
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_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;
|
||||
|
||||
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; }
|
||||
|
||||
protected:
|
||||
bool check_read_timeout_(size_t len = 1);
|
||||
|
||||
HardwareSerial *hw_serial_{nullptr};
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
ESP8266SoftwareSerial *sw_serial_{nullptr};
|
||||
#endif
|
||||
optional<uint8_t> tx_pin_;
|
||||
optional<uint8_t> rx_pin_;
|
||||
uint32_t baud_rate_;
|
||||
};
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
extern uint8_t next_uart_num;
|
||||
#endif
|
||||
|
||||
class UARTDevice : public Stream {
|
||||
public:
|
||||
UARTDevice() = default;
|
||||
UARTDevice(UARTComponent *parent) : parent_(parent) {}
|
||||
|
||||
void set_uart_parent(UARTComponent *parent) { this->parent_ = parent; }
|
||||
|
||||
void write_byte(uint8_t data) { this->parent_->write_byte(data); }
|
||||
|
||||
void write_array(const uint8_t *data, size_t len) { this->parent_->write_array(data, len); }
|
||||
|
||||
void write_str(const char *str) { this->parent_->write_str(str); }
|
||||
|
||||
bool read_byte(uint8_t *data) { return this->parent_->read_byte(data); }
|
||||
bool peek_byte(uint8_t *data) { return this->parent_->peek_byte(data); }
|
||||
|
||||
bool read_array(uint8_t *data, size_t len) { return this->parent_->read_array(data, len); }
|
||||
|
||||
int available() override { return this->parent_->available(); }
|
||||
|
||||
void flush() override { 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(); }
|
||||
|
||||
protected:
|
||||
UARTComponent *parent_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
Reference in New Issue
Block a user