Merge pull request #2693 from esphome/bump-2021.11.0b2

2021.11.0b2
This commit is contained in:
Jesse Hills 2021-11-11 12:52:35 +13:00 committed by GitHub
commit b976ac54c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 482 additions and 52 deletions

View file

@ -87,7 +87,7 @@ class AddressableLight : public LightOutput, public Component {
void mark_shown_() { void mark_shown_() {
#ifdef USE_POWER_SUPPLY #ifdef USE_POWER_SUPPLY
for (const auto &c : *this) { for (const auto &c : *this) {
if (c.get().is_on()) { if (c.get_red_raw() > 0 || c.get_green_raw() > 0 || c.get_blue_raw() > 0 || c.get_white_raw() > 0) {
this->power_.request(); this->power_.request();
return; return;
} }

View file

@ -32,6 +32,9 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase,
void mark_(uint32_t on_time, uint32_t off_time, uint32_t usec); void mark_(uint32_t on_time, uint32_t off_time, uint32_t usec);
void space_(uint32_t usec); void space_(uint32_t usec);
void await_target_time_();
uint32_t target_time_;
#endif #endif
#ifdef USE_ESP32 #ifdef USE_ESP32

View file

@ -33,42 +33,49 @@ void RemoteTransmitterComponent::calculate_on_off_time_(uint32_t carrier_frequen
*off_time_period = period - *on_time_period; *off_time_period = period - *on_time_period;
} }
void RemoteTransmitterComponent::await_target_time_() {
const uint32_t current_time = micros();
if (this->target_time_ == 0)
this->target_time_ = current_time;
else if (this->target_time_ > current_time)
delayMicroseconds(this->target_time_ - current_time);
}
void RemoteTransmitterComponent::mark_(uint32_t on_time, uint32_t off_time, uint32_t usec) { void RemoteTransmitterComponent::mark_(uint32_t on_time, uint32_t off_time, uint32_t usec) {
if (this->carrier_duty_percent_ == 100 || (on_time == 0 && off_time == 0)) { this->await_target_time_();
this->pin_->digital_write(true);
delayMicroseconds(usec);
this->pin_->digital_write(false);
return;
}
const uint32_t start_time = micros();
uint32_t current_time = start_time;
while (current_time - start_time < usec) {
const uint32_t elapsed = current_time - start_time;
this->pin_->digital_write(true); this->pin_->digital_write(true);
delayMicroseconds(std::min(on_time, usec - elapsed)); const uint32_t target = this->target_time_ + usec;
if (this->carrier_duty_percent_ < 100 && (on_time > 0 || off_time > 0)) {
while (true) { // Modulate with carrier frequency
this->target_time_ += on_time;
if (this->target_time_ >= target)
break;
this->await_target_time_();
this->pin_->digital_write(false); this->pin_->digital_write(false);
if (elapsed + on_time >= usec)
return;
delayMicroseconds(std::min(usec - elapsed - on_time, off_time)); this->target_time_ += off_time;
if (this->target_time_ >= target)
current_time = micros(); break;
this->await_target_time_();
this->pin_->digital_write(true);
} }
} }
this->target_time_ = target;
}
void RemoteTransmitterComponent::space_(uint32_t usec) { void RemoteTransmitterComponent::space_(uint32_t usec) {
this->await_target_time_();
this->pin_->digital_write(false); this->pin_->digital_write(false);
delayMicroseconds(usec); this->target_time_ += usec;
} }
void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) { void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) {
ESP_LOGD(TAG, "Sending remote code..."); ESP_LOGD(TAG, "Sending remote code...");
uint32_t on_time, off_time; uint32_t on_time, off_time;
this->calculate_on_off_time_(this->temp_.get_carrier_frequency(), &on_time, &off_time); this->calculate_on_off_time_(this->temp_.get_carrier_frequency(), &on_time, &off_time);
this->target_time_ = 0;
for (uint32_t i = 0; i < send_times; i++) { for (uint32_t i = 0; i < send_times; i++) {
{
InterruptLock lock;
for (int32_t item : this->temp_.get_data()) { for (int32_t item : this->temp_.get_data()) {
if (item > 0) { if (item > 0) {
const auto length = uint32_t(item); const auto length = uint32_t(item);
@ -79,10 +86,11 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen
} }
App.feed_wdt(); App.feed_wdt();
} }
} this->await_target_time_(); // wait for duration of last pulse
this->pin_->digital_write(false);
if (i + 1 < send_times) if (i + 1 < send_times)
delayMicroseconds(send_wait); this->target_time_ += send_wait;
} }
} }

View file

@ -35,6 +35,9 @@ def validate(config):
raise cv.Invalid("initial_value cannot be used with lambda") raise cv.Invalid("initial_value cannot be used with lambda")
if CONF_RESTORE_VALUE in config: if CONF_RESTORE_VALUE in config:
raise cv.Invalid("restore_value cannot be used with lambda") raise cv.Invalid("restore_value cannot be used with lambda")
elif CONF_INITIAL_VALUE not in config:
config[CONF_INITIAL_VALUE] = config[CONF_MIN_VALUE]
if not config[CONF_OPTIMISTIC] and CONF_SET_ACTION not in config: if not config[CONF_OPTIMISTIC] and CONF_SET_ACTION not in config:
raise cv.Invalid( raise cv.Invalid(
"Either optimistic mode must be enabled, or set_action must be set, to handle the number being set." "Either optimistic mode must be enabled, or set_action must be set, to handle the number being set."
@ -80,7 +83,6 @@ async def to_code(config):
else: else:
cg.add(var.set_optimistic(config[CONF_OPTIMISTIC])) cg.add(var.set_optimistic(config[CONF_OPTIMISTIC]))
if CONF_INITIAL_VALUE in config:
cg.add(var.set_initial_value(config[CONF_INITIAL_VALUE])) cg.add(var.set_initial_value(config[CONF_INITIAL_VALUE]))
if CONF_RESTORE_VALUE in config: if CONF_RESTORE_VALUE in config:
cg.add(var.set_restore_value(config[CONF_RESTORE_VALUE])) cg.add(var.set_restore_value(config[CONF_RESTORE_VALUE]))

View file

@ -14,6 +14,16 @@ from esphome.const import (
CONF_DATA, CONF_DATA,
CONF_RX_BUFFER_SIZE, CONF_RX_BUFFER_SIZE,
CONF_INVERT, CONF_INVERT,
CONF_TRIGGER_ID,
CONF_SEQUENCE,
CONF_TIMEOUT,
CONF_DEBUG,
CONF_DIRECTION,
CONF_AFTER,
CONF_BYTES,
CONF_DELIMITER,
CONF_DUMMY_RECEIVER,
CONF_DUMMY_RECEIVER_ID,
) )
from esphome.core import CORE from esphome.core import CORE
@ -31,6 +41,8 @@ ESP8266UartComponent = uart_ns.class_(
UARTDevice = uart_ns.class_("UARTDevice") UARTDevice = uart_ns.class_("UARTDevice")
UARTWriteAction = uart_ns.class_("UARTWriteAction", automation.Action) UARTWriteAction = uart_ns.class_("UARTWriteAction", automation.Action)
UARTDebugger = uart_ns.class_("UARTDebugger", cg.Component, automation.Action)
UARTDummyReceiver = uart_ns.class_("UARTDummyReceiver", cg.Component)
MULTI_CONF = True MULTI_CONF = True
@ -75,6 +87,34 @@ CONF_STOP_BITS = "stop_bits"
CONF_DATA_BITS = "data_bits" CONF_DATA_BITS = "data_bits"
CONF_PARITY = "parity" CONF_PARITY = "parity"
UARTDirection = uart_ns.enum("UARTDirection")
UART_DIRECTIONS = {
"RX": UARTDirection.UART_DIRECTION_RX,
"TX": UARTDirection.UART_DIRECTION_TX,
"BOTH": UARTDirection.UART_DIRECTION_BOTH,
}
DEBUG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UARTDebugger),
cv.Optional(CONF_DIRECTION, default="BOTH"): cv.enum(
UART_DIRECTIONS, upper=True
),
cv.Optional(CONF_AFTER): cv.Schema(
{
cv.Optional(CONF_BYTES, default=256): cv.validate_bytes,
cv.Optional(
CONF_TIMEOUT, default="100ms"
): cv.positive_time_period_milliseconds,
cv.Optional(CONF_DELIMITER): cv.templatable(validate_raw_data),
}
),
cv.Required(CONF_SEQUENCE): automation.validate_automation(),
cv.Optional(CONF_DUMMY_RECEIVER, default=False): cv.boolean,
cv.GenerateID(CONF_DUMMY_RECEIVER_ID): cv.declare_id(UARTDummyReceiver),
}
)
CONFIG_SCHEMA = cv.All( CONFIG_SCHEMA = cv.All(
cv.Schema( cv.Schema(
{ {
@ -91,12 +131,38 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_INVERT): cv.invalid( cv.Optional(CONF_INVERT): cv.invalid(
"This option has been removed. Please instead use invert in the tx/rx pin schemas." "This option has been removed. Please instead use invert in the tx/rx pin schemas."
), ),
cv.Optional(CONF_DEBUG): DEBUG_SCHEMA,
} }
).extend(cv.COMPONENT_SCHEMA), ).extend(cv.COMPONENT_SCHEMA),
cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN), cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN),
) )
async def debug_to_code(config, parent):
trigger = cg.new_Pvariable(config[CONF_TRIGGER_ID], parent)
await cg.register_component(trigger, config)
for action in config[CONF_SEQUENCE]:
await automation.build_automation(
trigger,
[(UARTDirection, "direction"), (cg.std_vector.template(cg.uint8), "bytes")],
action,
)
cg.add(trigger.set_direction(config[CONF_DIRECTION]))
after = config[CONF_AFTER]
cg.add(trigger.set_after_bytes(after[CONF_BYTES]))
cg.add(trigger.set_after_timeout(after[CONF_TIMEOUT]))
if CONF_DELIMITER in after:
data = after[CONF_DELIMITER]
if isinstance(data, bytes):
data = list(data)
for byte in after[CONF_DELIMITER]:
cg.add(trigger.add_delimiter_byte(byte))
if config[CONF_DUMMY_RECEIVER]:
dummy = cg.new_Pvariable(config[CONF_DUMMY_RECEIVER_ID], parent)
await cg.register_component(dummy, {})
cg.add_define("USE_UART_DEBUGGER")
async def to_code(config): async def to_code(config):
cg.add_global(uart_ns.using) cg.add_global(uart_ns.using)
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
@ -115,6 +181,9 @@ async def to_code(config):
cg.add(var.set_data_bits(config[CONF_DATA_BITS])) cg.add(var.set_data_bits(config[CONF_DATA_BITS]))
cg.add(var.set_parity(config[CONF_PARITY])) cg.add(var.set_parity(config[CONF_PARITY]))
if CONF_DEBUG in config:
await debug_to_code(config[CONF_DEBUG], var)
# 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!
UART_DEVICE_SCHEMA = cv.Schema( UART_DEVICE_SCHEMA = cv.Schema(

View file

@ -45,17 +45,17 @@ class UARTDevice {
// Compat APIs // Compat APIs
int read() { int read() {
uint8_t data; uint8_t data;
if (!read_byte(&data)) if (!this->read_byte(&data))
return -1; return -1;
return data; return data;
} }
size_t write(uint8_t data) { size_t write(uint8_t data) {
write_byte(data); this->write_byte(data);
return 1; return 1;
} }
int peek() { int peek() {
uint8_t data; uint8_t data;
if (!peek_byte(&data)) if (!this->peek_byte(&data))
return -1; return -1;
return data; return data;
} }

View file

@ -2,9 +2,13 @@
#include <vector> #include <vector>
#include <cstring> #include <cstring>
#include "esphome/core/defines.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#ifdef USE_UART_DEBUGGER
#include "esphome/core/automation.h"
#endif
namespace esphome { namespace esphome {
namespace uart { namespace uart {
@ -15,6 +19,14 @@ enum UARTParityOptions {
UART_CONFIG_PARITY_ODD, UART_CONFIG_PARITY_ODD,
}; };
#ifdef USE_UART_DEBUGGER
enum UARTDirection {
UART_DIRECTION_RX,
UART_DIRECTION_TX,
UART_DIRECTION_BOTH,
};
#endif
const LogString *parity_to_str(UARTParityOptions parity); const LogString *parity_to_str(UARTParityOptions parity);
class UARTComponent { class UARTComponent {
@ -50,6 +62,12 @@ class UARTComponent {
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_baud_rate() const { return baud_rate_; } uint32_t get_baud_rate() const { return baud_rate_; }
#ifdef USE_UART_DEBUGGER
void add_debug_callback(std::function<void(UARTDirection, uint8_t)> &&callback) {
this->debug_callback_.add(std::move(callback));
}
#endif
protected: protected:
virtual void check_logger_conflict() = 0; virtual void check_logger_conflict() = 0;
bool check_read_timeout_(size_t len = 1); bool check_read_timeout_(size_t len = 1);
@ -61,6 +79,9 @@ class UARTComponent {
uint8_t stop_bits_; uint8_t stop_bits_;
uint8_t data_bits_; uint8_t data_bits_;
UARTParityOptions parity_; UARTParityOptions parity_;
#ifdef USE_UART_DEBUGGER
CallbackManager<void(UARTDirection, uint8_t)> debug_callback_{};
#endif
}; };
} // namespace uart } // namespace uart

View file

@ -117,26 +117,32 @@ void ESP32ArduinoUARTComponent::dump_config() {
void ESP32ArduinoUARTComponent::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); this->hw_serial_->write(data, len);
#ifdef USE_UART_DEBUGGER
for (size_t i = 0; i < len; 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]); this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
} }
#endif
} }
bool ESP32ArduinoUARTComponent::peek_byte(uint8_t *data) { bool ESP32ArduinoUARTComponent::peek_byte(uint8_t *data) {
if (!this->check_read_timeout_()) if (!this->check_read_timeout_())
return false; return false;
*data = this->hw_serial_->peek(); *data = this->hw_serial_->peek();
return true; return true;
} }
bool ESP32ArduinoUARTComponent::read_array(uint8_t *data, size_t len) { bool ESP32ArduinoUARTComponent::read_array(uint8_t *data, size_t len) {
if (!this->check_read_timeout_(len)) if (!this->check_read_timeout_(len))
return false; return false;
this->hw_serial_->readBytes(data, len); this->hw_serial_->readBytes(data, len);
#ifdef USE_UART_DEBUGGER
for (size_t i = 0; i < len; i++) { 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]); this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
} }
#endif
return true; return true;
} }
int ESP32ArduinoUARTComponent::available() { return this->hw_serial_->available(); } int ESP32ArduinoUARTComponent::available() { return this->hw_serial_->available(); }
void ESP32ArduinoUARTComponent::flush() { void ESP32ArduinoUARTComponent::flush() {
ESP_LOGVV(TAG, " Flushing..."); ESP_LOGVV(TAG, " Flushing...");

View file

@ -130,9 +130,11 @@ void ESP8266UartComponent::write_array(const uint8_t *data, size_t len) {
for (size_t i = 0; i < len; i++) for (size_t i = 0; i < len; i++)
this->sw_serial_->write_byte(data[i]); this->sw_serial_->write_byte(data[i]);
} }
#ifdef USE_UART_DEBUGGER
for (size_t i = 0; i < len; 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]); this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
} }
#endif
} }
bool ESP8266UartComponent::peek_byte(uint8_t *data) { bool ESP8266UartComponent::peek_byte(uint8_t *data) {
if (!this->check_read_timeout_()) if (!this->check_read_timeout_())
@ -153,10 +155,11 @@ bool ESP8266UartComponent::read_array(uint8_t *data, size_t len) {
for (size_t i = 0; i < len; i++) for (size_t i = 0; i < len; i++)
data[i] = this->sw_serial_->read_byte(); data[i] = this->sw_serial_->read_byte();
} }
#ifdef USE_UART_DEBUGGER
for (size_t i = 0; i < len; i++) { 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]); this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
} }
#endif
return true; return true;
} }
int ESP8266UartComponent::available() { int ESP8266UartComponent::available() {

View file

@ -130,10 +130,13 @@ void IDFUARTComponent::write_array(const uint8_t *data, size_t len) {
xSemaphoreTake(this->lock_, portMAX_DELAY); xSemaphoreTake(this->lock_, portMAX_DELAY);
uart_write_bytes(this->uart_num_, data, len); uart_write_bytes(this->uart_num_, data, len);
xSemaphoreGive(this->lock_); xSemaphoreGive(this->lock_);
#ifdef USE_UART_DEBUGGER
for (size_t i = 0; i < len; 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]); this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
} }
#endif
} }
bool IDFUARTComponent::peek_byte(uint8_t *data) { bool IDFUARTComponent::peek_byte(uint8_t *data) {
if (!this->check_read_timeout_()) if (!this->check_read_timeout_())
return false; return false;
@ -152,6 +155,7 @@ bool IDFUARTComponent::peek_byte(uint8_t *data) {
xSemaphoreGive(this->lock_); xSemaphoreGive(this->lock_);
return true; return true;
} }
bool IDFUARTComponent::read_array(uint8_t *data, size_t len) { bool IDFUARTComponent::read_array(uint8_t *data, size_t len) {
size_t length_to_read = len; size_t length_to_read = len;
if (!this->check_read_timeout_(len)) if (!this->check_read_timeout_(len))
@ -165,12 +169,12 @@ bool IDFUARTComponent::read_array(uint8_t *data, size_t len) {
} }
if (length_to_read > 0) if (length_to_read > 0)
uart_read_bytes(this->uart_num_, data, length_to_read, 20 / portTICK_RATE_MS); uart_read_bytes(this->uart_num_, data, length_to_read, 20 / portTICK_RATE_MS);
xSemaphoreGive(this->lock_); xSemaphoreGive(this->lock_);
#ifdef USE_UART_DEBUGGER
for (size_t i = 0; i < len; i++) { 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]); this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
} }
#endif
return true; return true;
} }

View file

@ -0,0 +1,193 @@
#include "esphome/core/defines.h"
#ifdef USE_UART_DEBUGGER
#include <vector>
#include "uart_debugger.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace uart {
static const char *const TAG = "uart_debug";
UARTDebugger::UARTDebugger(UARTComponent *parent) {
parent->add_debug_callback([this](UARTDirection direction, uint8_t byte) {
if (!this->is_my_direction_(direction) || this->is_recursive_()) {
return;
}
this->trigger_after_direction_change_(direction);
this->store_byte_(direction, byte);
this->trigger_after_delimiter_(byte);
this->trigger_after_bytes_();
});
}
void UARTDebugger::loop() { this->trigger_after_timeout_(); }
bool UARTDebugger::is_my_direction_(UARTDirection direction) {
return this->for_direction_ == UART_DIRECTION_BOTH || this->for_direction_ == direction;
}
bool UARTDebugger::is_recursive_() { return this->is_triggering_; }
void UARTDebugger::trigger_after_direction_change_(UARTDirection direction) {
if (this->has_buffered_bytes_() && this->for_direction_ == UART_DIRECTION_BOTH &&
this->last_direction_ != direction) {
this->fire_trigger_();
}
}
void UARTDebugger::store_byte_(UARTDirection direction, uint8_t byte) {
this->bytes_.push_back(byte);
this->last_direction_ = direction;
this->last_time_ = millis();
}
void UARTDebugger::trigger_after_delimiter_(uint8_t byte) {
if (this->after_delimiter_.empty() || !this->has_buffered_bytes_()) {
return;
}
if (this->after_delimiter_[this->after_delimiter_pos_] != byte) {
this->after_delimiter_pos_ = 0;
return;
}
this->after_delimiter_pos_++;
if (this->after_delimiter_pos_ == this->after_delimiter_.size()) {
this->fire_trigger_();
this->after_delimiter_pos_ = 0;
}
}
void UARTDebugger::trigger_after_bytes_() {
if (this->has_buffered_bytes_() && this->after_bytes_ > 0 && this->bytes_.size() >= this->after_bytes_) {
this->fire_trigger_();
}
}
void UARTDebugger::trigger_after_timeout_() {
if (this->has_buffered_bytes_() && this->after_timeout_ > 0 && millis() - this->last_time_ >= this->after_timeout_) {
this->fire_trigger_();
}
}
bool UARTDebugger::has_buffered_bytes_() { return !this->bytes_.empty(); }
void UARTDebugger::fire_trigger_() {
this->is_triggering_ = true;
trigger(this->last_direction_, this->bytes_);
this->bytes_.clear();
this->is_triggering_ = false;
}
void UARTDummyReceiver::loop() {
// Reading up to a limited number of bytes, to make sure that this loop()
// won't lock up the system on a continuous incoming stream of bytes.
uint8_t data;
int count = 50;
while (this->available() && count--) {
this->read_byte(&data);
}
}
void UARTDebug::log_hex(UARTDirection direction, std::vector<uint8_t> bytes, uint8_t separator) {
std::string res;
if (direction == UART_DIRECTION_RX) {
res += "<<< ";
} else {
res += ">>> ";
}
size_t len = bytes.size();
char buf[5];
for (size_t i = 0; i < len; i++) {
if (i > 0) {
res += separator;
}
sprintf(buf, "%02X", bytes[i]);
res += buf;
}
ESP_LOGD(TAG, "%s", res.c_str());
}
void UARTDebug::log_string(UARTDirection direction, std::vector<uint8_t> bytes) {
std::string res;
if (direction == UART_DIRECTION_RX) {
res += "<<< \"";
} else {
res += ">>> \"";
}
size_t len = bytes.size();
char buf[5];
for (size_t i = 0; i < len; i++) {
if (bytes[i] == 7) {
res += "\\a";
} else if (bytes[i] == 8) {
res += "\\b";
} else if (bytes[i] == 9) {
res += "\\t";
} else if (bytes[i] == 10) {
res += "\\n";
} else if (bytes[i] == 11) {
res += "\\v";
} else if (bytes[i] == 12) {
res += "\\f";
} else if (bytes[i] == 13) {
res += "\\r";
} else if (bytes[i] == 27) {
res += "\\e";
} else if (bytes[i] == 34) {
res += "\\\"";
} else if (bytes[i] == 39) {
res += "\\'";
} else if (bytes[i] == 92) {
res += "\\\\";
} else if (bytes[i] < 32 || bytes[i] > 127) {
sprintf(buf, "\\x%02X", bytes[i]);
res += buf;
} else {
res += bytes[i];
}
}
res += '"';
ESP_LOGD(TAG, "%s", res.c_str());
}
void UARTDebug::log_int(UARTDirection direction, std::vector<uint8_t> bytes, uint8_t separator) {
std::string res;
size_t len = bytes.size();
if (direction == UART_DIRECTION_RX) {
res += "<<< ";
} else {
res += ">>> ";
}
for (size_t i = 0; i < len; i++) {
if (i > 0) {
res += separator;
}
res += to_string(bytes[i]);
}
ESP_LOGD(TAG, "%s", res.c_str());
}
void UARTDebug::log_binary(UARTDirection direction, std::vector<uint8_t> bytes, uint8_t separator) {
std::string res;
size_t len = bytes.size();
if (direction == UART_DIRECTION_RX) {
res += "<<< ";
} else {
res += ">>> ";
}
char buf[20];
for (size_t i = 0; i < len; i++) {
if (i > 0) {
res += separator;
}
sprintf(buf, "0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(bytes[i]), bytes[i]);
res += buf;
}
ESP_LOGD(TAG, "%s", res.c_str());
}
} // namespace uart
} // namespace esphome
#endif

View file

@ -0,0 +1,101 @@
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_UART_DEBUGGER
#include <vector>
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "uart.h"
#include "uart_component.h"
namespace esphome {
namespace uart {
/// The UARTDebugger class adds debugging support to a UART bus.
///
/// It accumulates bytes that travel over the UART bus and triggers one or
/// more actions that can log the data at an appropriate time. What
/// 'appropriate time' means exactly, is determined by a number of
/// configurable constraints. E.g. when a given number of bytes is gathered
/// and/or when no more data has been seen for a given time interval.
class UARTDebugger : public Component, public Trigger<UARTDirection, std::vector<uint8_t>> {
public:
explicit UARTDebugger(UARTComponent *parent);
void loop() override;
/// Set the direction in which to inspect the bytes: incoming, outgoing
/// or both. When debugging in both directions, logging will be triggered
/// when the direction of the data stream changes.
void set_direction(UARTDirection direction) { this->for_direction_ = direction; }
/// Set the maximum number of bytes to accumulate. When the number of bytes
/// is reached, logging will be triggered.
void set_after_bytes(size_t size) { this->after_bytes_ = size; }
/// Set a timeout for the data stream. When no new bytes are seen during
/// this timeout, logging will be triggered.
void set_after_timeout(uint32_t timeout) { this->after_timeout_ = timeout; }
/// Add a delimiter byte. This can be called multiple times to setup a
/// multi-byte delimiter (a typical example would be '\r\n').
/// When the constructued byte sequence is found in the data stream,
/// logging will be triggered.
void add_delimiter_byte(uint8_t byte) { this->after_delimiter_.push_back(byte); }
protected:
UARTDirection for_direction_;
UARTDirection last_direction_{};
std::vector<uint8_t> bytes_{};
size_t after_bytes_;
uint32_t after_timeout_;
uint32_t last_time_{};
std::vector<uint8_t> after_delimiter_{};
size_t after_delimiter_pos_{};
bool is_triggering_{false};
bool is_my_direction_(UARTDirection direction);
bool is_recursive_();
void store_byte_(UARTDirection direction, uint8_t byte);
void trigger_after_direction_change_(UARTDirection direction);
void trigger_after_delimiter_(uint8_t byte);
void trigger_after_bytes_();
void trigger_after_timeout_();
bool has_buffered_bytes_();
void fire_trigger_();
};
/// This UARTDevice is used by the serial debugger to read data from a
/// serial interface when the 'dummy_receiver' option is enabled.
/// The data are not stored, nor processed. This is most useful when the
/// debugger is used to reverse engineer a serial protocol, for which no
/// specific UARTDevice implementation exists (yet), but for which the
/// incoming bytes must be read to drive the debugger.
class UARTDummyReceiver : public Component, public UARTDevice {
public:
UARTDummyReceiver(UARTComponent *parent) : UARTDevice(parent) {}
void loop() override;
};
/// This class contains some static methods, that can be used to easily
/// create a logging action for the debugger.
class UARTDebug {
public:
/// Log the bytes as hex values, separated by the provided separator
/// character.
static void log_hex(UARTDirection direction, std::vector<uint8_t> bytes, uint8_t separator);
/// Log the bytes as string values, escaping unprintable characters.
static void log_string(UARTDirection direction, std::vector<uint8_t> bytes);
/// Log the bytes as integer values, separated by the provided separator
/// character.
static void log_int(UARTDirection direction, std::vector<uint8_t> bytes, uint8_t separator);
/// Log the bytes as '<binary> (<hex>)' values, separated by the provided
/// separator.
static void log_binary(UARTDirection direction, std::vector<uint8_t> bytes, uint8_t separator);
};
} // namespace uart
} // namespace esphome
#endif

View file

@ -1,6 +1,6 @@
"""Constants used by esphome.""" """Constants used by esphome."""
__version__ = "2021.11.0b1" __version__ = "2021.11.0b2"
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
@ -34,6 +34,7 @@ ARDUINO_VERSION_ESP8266 = {
SOURCE_FILE_EXTENSIONS = {".cpp", ".hpp", ".h", ".c", ".tcc", ".ino"} SOURCE_FILE_EXTENSIONS = {".cpp", ".hpp", ".h", ".c", ".tcc", ".ino"}
HEADER_FILE_EXTENSIONS = {".h", ".hpp", ".tcc"} HEADER_FILE_EXTENSIONS = {".h", ".hpp", ".tcc"}
CONF_ABOVE = "above" CONF_ABOVE = "above"
CONF_ACCELERATION = "acceleration" CONF_ACCELERATION = "acceleration"
CONF_ACCELERATION_X = "acceleration_x" CONF_ACCELERATION_X = "acceleration_x"
@ -47,6 +48,7 @@ CONF_ACTIVE_POWER = "active_power"
CONF_ADDRESS = "address" CONF_ADDRESS = "address"
CONF_ADDRESSABLE_LIGHT_ID = "addressable_light_id" CONF_ADDRESSABLE_LIGHT_ID = "addressable_light_id"
CONF_ADVANCED = "advanced" CONF_ADVANCED = "advanced"
CONF_AFTER = "after"
CONF_ALPHA = "alpha" CONF_ALPHA = "alpha"
CONF_ALTITUDE = "altitude" CONF_ALTITUDE = "altitude"
CONF_AND = "and" CONF_AND = "and"
@ -93,6 +95,7 @@ CONF_BUFFER_SIZE = "buffer_size"
CONF_BUILD_PATH = "build_path" CONF_BUILD_PATH = "build_path"
CONF_BUS_VOLTAGE = "bus_voltage" CONF_BUS_VOLTAGE = "bus_voltage"
CONF_BUSY_PIN = "busy_pin" CONF_BUSY_PIN = "busy_pin"
CONF_BYTES = "bytes"
CONF_CALCULATED_LUX = "calculated_lux" CONF_CALCULATED_LUX = "calculated_lux"
CONF_CALIBRATE_LINEAR = "calibrate_linear" CONF_CALIBRATE_LINEAR = "calibrate_linear"
CONF_CALIBRATION = "calibration" CONF_CALIBRATION = "calibration"
@ -164,6 +167,7 @@ CONF_DAYS_OF_WEEK = "days_of_week"
CONF_DC_PIN = "dc_pin" CONF_DC_PIN = "dc_pin"
CONF_DEASSERT_RTS_DTR = "deassert_rts_dtr" CONF_DEASSERT_RTS_DTR = "deassert_rts_dtr"
CONF_DEBOUNCE = "debounce" CONF_DEBOUNCE = "debounce"
CONF_DEBUG = "debug"
CONF_DECAY_MODE = "decay_mode" CONF_DECAY_MODE = "decay_mode"
CONF_DECELERATION = "deceleration" CONF_DECELERATION = "deceleration"
CONF_DEFAULT_MODE = "default_mode" CONF_DEFAULT_MODE = "default_mode"
@ -171,6 +175,7 @@ CONF_DEFAULT_TARGET_TEMPERATURE_HIGH = "default_target_temperature_high"
CONF_DEFAULT_TARGET_TEMPERATURE_LOW = "default_target_temperature_low" CONF_DEFAULT_TARGET_TEMPERATURE_LOW = "default_target_temperature_low"
CONF_DEFAULT_TRANSITION_LENGTH = "default_transition_length" CONF_DEFAULT_TRANSITION_LENGTH = "default_transition_length"
CONF_DELAY = "delay" CONF_DELAY = "delay"
CONF_DELIMITER = "delimiter"
CONF_DELTA = "delta" CONF_DELTA = "delta"
CONF_DEVICE = "device" CONF_DEVICE = "device"
CONF_DEVICE_CLASS = "device_class" CONF_DEVICE_CLASS = "device_class"
@ -192,6 +197,8 @@ CONF_DNS2 = "dns2"
CONF_DOMAIN = "domain" CONF_DOMAIN = "domain"
CONF_DRY_ACTION = "dry_action" CONF_DRY_ACTION = "dry_action"
CONF_DRY_MODE = "dry_mode" CONF_DRY_MODE = "dry_mode"
CONF_DUMMY_RECEIVER = "dummy_receiver"
CONF_DUMMY_RECEIVER_ID = "dummy_receiver_id"
CONF_DUMP = "dump" CONF_DUMP = "dump"
CONF_DURATION = "duration" CONF_DURATION = "duration"
CONF_EAP = "eap" CONF_EAP = "eap"

View file

@ -37,6 +37,7 @@
#define USE_SWITCH #define USE_SWITCH
#define USE_TEXT_SENSOR #define USE_TEXT_SENSOR
#define USE_TIME #define USE_TIME
#define USE_UART_DEBUGGER
#define USE_WEBSERVER #define USE_WEBSERVER
#define USE_WIFI #define USE_WIFI

View file

@ -193,6 +193,18 @@ uart:
data_bits: 8 data_bits: 8
stop_bits: 1 stop_bits: 1
rx_buffer_size: 512 rx_buffer_size: 512
debug:
dummy_receiver: true
direction: both
after:
bytes: 50
timeout: 500ms
delimiter: "\r\n"
sequence:
- lambda: UARTDebug::log_hex(direction, bytes, ':');
- lambda: UARTDebug::log_string(direction, bytes);
- lambda: UARTDebug::log_int(direction, bytes, ',');
- lambda: UARTDebug::log_binary(direction, bytes, ';');
- id: adalight_uart - id: adalight_uart
tx_pin: GPIO25 tx_pin: GPIO25