Files
esphome-wake-up-light-ii/components/uart/uart_component_libretiny.cpp
T
mangavalkandClaude Opus 5.5 a8f6bb3ca5 Fit this lamp: 4 alarms, full data model, knock snooze, server use
Decode DP127 as 48 bytes (4 alarms), plus DP128, DP107 and DP126, from Tuya's
data model. Add day switches, alarm and sunset controls, a knock sensor on PA18,
display schedule, bedtime fade, radio timer and grouped web UI. Pin LibreTiny
v1.14.0 for the OTA restart fix. Ship the patched UART as an external component
so a server needs only a short YAML.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 12:27:54 +01:00

209 lines
6.2 KiB
C++

#ifdef USE_LIBRETINY
#include "esphome/core/application.h"
#include "esphome/core/defines.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "uart_component_libretiny.h"
#include "esphome/components/libretiny/core.h"
#ifdef USE_LOGGER
#include "esphome/components/logger/logger.h"
#endif
#if LT_ARD_HAS_SOFTSERIAL
#include <SoftwareSerial.h>
#endif
namespace esphome::uart {
static const char *const TAG = "uart";
static const char *const UART_TYPE[] = {
"hardware",
"software",
};
uint16_t LibreTinyUARTComponent::get_config() {
uint16_t config = 0;
switch (this->parity_) {
case UART_CONFIG_PARITY_NONE:
config |= SERIAL_PARITY_NONE;
break;
case UART_CONFIG_PARITY_EVEN:
config |= SERIAL_PARITY_EVEN;
break;
case UART_CONFIG_PARITY_ODD:
config |= SERIAL_PARITY_ODD;
break;
default:
break;
}
config |= (this->data_bits_ - 4) << 8;
config |= 0x10 + (this->stop_bits_ - 1) * 0x20;
return config;
}
void LibreTinyUARTComponent::setup() {
int16_t tx_pin = tx_pin_ == nullptr ? -1 : tx_pin_->get_pin();
int16_t rx_pin = rx_pin_ == nullptr ? -1 : rx_pin_->get_pin();
auto should_fallback_to_software_serial = [&]() -> bool {
auto has_flags = [](InternalGPIOPin *pin, const gpio::Flags mask) -> bool {
return pin && (pin->get_flags() & mask) != gpio::Flags::FLAG_NONE;
};
if (has_flags(this->tx_pin_,
gpio::Flags::FLAG_OPEN_DRAIN | gpio::Flags::FLAG_PULLUP | gpio::Flags::FLAG_PULLDOWN) ||
has_flags(this->rx_pin_,
gpio::Flags::FLAG_OPEN_DRAIN | gpio::Flags::FLAG_PULLUP | gpio::Flags::FLAG_PULLDOWN)) {
#if LT_ARD_HAS_SOFTSERIAL
ESP_LOGI(TAG, "Pins have flags set. Using Software Serial");
return true;
#else
ESP_LOGW(TAG, "Pin flags are set but not supported for hardware serial. Ignoring");
#endif
}
return false;
};
if (false) { // NOLINT(readability-simplify-boolean-expr)
return;
}
#if LT_HW_UART0
else if ((((tx_pin == -1 || tx_pin == PIN_SERIAL0_TX) &&
(rx_pin == -1 || rx_pin == PIN_SERIAL0_RX)) ||
(tx_pin == 14 && rx_pin == 13)) &&
!should_fallback_to_software_serial()) {
this->serial_ = &Serial0;
this->hardware_idx_ = 0;
}
#endif
#if LT_HW_UART1
else if ((tx_pin == -1 || tx_pin == PIN_SERIAL1_TX) && (rx_pin == -1 || rx_pin == PIN_SERIAL1_RX) &&
!should_fallback_to_software_serial()) {
this->serial_ = &Serial1;
this->hardware_idx_ = 1;
}
#endif
#if LT_HW_UART2
else if ((tx_pin == -1 || tx_pin == PIN_SERIAL2_TX) && (rx_pin == -1 || rx_pin == PIN_SERIAL2_RX) &&
!should_fallback_to_software_serial()) {
this->serial_ = &Serial2;
this->hardware_idx_ = 2;
}
#endif
else {
#if LT_ARD_HAS_SOFTSERIAL
if (this->rx_pin_) {
this->rx_pin_->setup();
}
if (this->tx_pin_ && this->rx_pin_ != this->tx_pin_) {
this->tx_pin_->setup();
}
bool tx_inverted = tx_pin_ != nullptr && tx_pin_->is_inverted();
bool rx_inverted = rx_pin_ != nullptr && rx_pin_->is_inverted();
this->serial_ = new SoftwareSerial(rx_pin, tx_pin, rx_inverted || tx_inverted);
#else
this->serial_ = &Serial;
ESP_LOGE(TAG, " SoftwareSerial is not implemented for this chip. Only hardware pins are supported:");
#if LT_HW_UART0
ESP_LOGE(TAG, " TX=%u, RX=%u", PIN_SERIAL0_TX, PIN_SERIAL0_RX);
#endif
#if LT_HW_UART1
ESP_LOGE(TAG, " TX=%u, RX=%u", PIN_SERIAL1_TX, PIN_SERIAL1_RX);
#endif
#if LT_HW_UART2
ESP_LOGE(TAG, " TX=%u, RX=%u", PIN_SERIAL2_TX, PIN_SERIAL2_RX);
#endif
this->mark_failed(LOG_STR("SoftwareSerial is not implemented for this chip."));
return;
#endif
}
if (this->hardware_idx_ >= 0)
libretiny::ensure_serial_handle(this->hardware_idx_);
if (this->hardware_idx_ == 0 && tx_pin == 14 && rx_pin == 13) {
ESP_LOGI(TAG, "Using alternate UART0 pins RX=PA13 TX=PA14");
Serial0.setPins(rx_pin, tx_pin);
}
this->serial_->begin(this->baud_rate_, get_config());
}
void LibreTinyUARTComponent::dump_config() {
bool is_software = this->hardware_idx_ == -1;
ESP_LOGCONFIG(TAG,
"UART Bus:\n"
" Type: %s",
UART_TYPE[is_software]);
if (!is_software) {
ESP_LOGCONFIG(TAG, " Port number: %d", this->hardware_idx_);
}
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: %" PRIu32 " baud\n"
" Data Bits: %u\n"
" Parity: %s\n"
" Stop bits: %u",
this->baud_rate_, this->data_bits_, LOG_STR_ARG(parity_to_str(this->parity_)), this->stop_bits_);
this->check_logger_conflict();
}
void LibreTinyUARTComponent::write_array(const uint8_t *data, size_t len) {
this->serial_->write(data, len);
#ifdef USE_UART_DEBUGGER
for (size_t i = 0; i < len; i++) {
this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
}
#endif
}
bool LibreTinyUARTComponent::peek_byte(uint8_t *data) {
if (!this->check_read_timeout_())
return false;
*data = this->serial_->peek();
return true;
}
bool LibreTinyUARTComponent::read_array(uint8_t *data, size_t len) {
if (!this->check_read_timeout_(len))
return false;
this->serial_->readBytes(data, len);
#ifdef USE_UART_DEBUGGER
for (size_t i = 0; i < len; i++) {
this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
}
#endif
return true;
}
size_t LibreTinyUARTComponent::available() { return this->serial_->available(); }
UARTFlushResult LibreTinyUARTComponent::flush() {
ESP_LOGVV(TAG, " Flushing");
this->serial_->flush();
return UARTFlushResult::UART_FLUSH_RESULT_ASSUMED_SUCCESS;
}
void LibreTinyUARTComponent::check_logger_conflict() {
#ifdef USE_LOGGER
if (this->hardware_idx_ == -1 || logger::global_logger->get_baud_rate() == 0) {
return;
}
if (this->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 esphome::uart
#endif // USE_LIBRETINY