mirror of
https://github.com/esphome/esphome.git
synced 2024-11-14 10:26:53 +01:00
commit
b976ac54c8
@ -87,7 +87,7 @@ class AddressableLight : public LightOutput, public Component {
|
||||
void mark_shown_() {
|
||||
#ifdef USE_POWER_SUPPLY
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
@ -32,6 +32,9 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase,
|
||||
void mark_(uint32_t on_time, uint32_t off_time, uint32_t usec);
|
||||
|
||||
void space_(uint32_t usec);
|
||||
|
||||
void await_target_time_();
|
||||
uint32_t target_time_;
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
@ -33,56 +33,64 @@ void RemoteTransmitterComponent::calculate_on_off_time_(uint32_t carrier_frequen
|
||||
*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) {
|
||||
if (this->carrier_duty_percent_ == 100 || (on_time == 0 && off_time == 0)) {
|
||||
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);
|
||||
|
||||
delayMicroseconds(std::min(on_time, usec - elapsed));
|
||||
this->pin_->digital_write(false);
|
||||
if (elapsed + on_time >= usec)
|
||||
return;
|
||||
|
||||
delayMicroseconds(std::min(usec - elapsed - on_time, off_time));
|
||||
|
||||
current_time = micros();
|
||||
this->await_target_time_();
|
||||
this->pin_->digital_write(true);
|
||||
|
||||
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->target_time_ += off_time;
|
||||
if (this->target_time_ >= target)
|
||||
break;
|
||||
this->await_target_time_();
|
||||
this->pin_->digital_write(true);
|
||||
}
|
||||
}
|
||||
this->target_time_ = target;
|
||||
}
|
||||
|
||||
void RemoteTransmitterComponent::space_(uint32_t usec) {
|
||||
this->await_target_time_();
|
||||
this->pin_->digital_write(false);
|
||||
delayMicroseconds(usec);
|
||||
this->target_time_ += usec;
|
||||
}
|
||||
|
||||
void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) {
|
||||
ESP_LOGD(TAG, "Sending remote code...");
|
||||
uint32_t 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++) {
|
||||
{
|
||||
InterruptLock lock;
|
||||
for (int32_t item : this->temp_.get_data()) {
|
||||
if (item > 0) {
|
||||
const auto length = uint32_t(item);
|
||||
this->mark_(on_time, off_time, length);
|
||||
} else {
|
||||
const auto length = uint32_t(-item);
|
||||
this->space_(length);
|
||||
}
|
||||
App.feed_wdt();
|
||||
for (int32_t item : this->temp_.get_data()) {
|
||||
if (item > 0) {
|
||||
const auto length = uint32_t(item);
|
||||
this->mark_(on_time, off_time, length);
|
||||
} else {
|
||||
const auto length = uint32_t(-item);
|
||||
this->space_(length);
|
||||
}
|
||||
App.feed_wdt();
|
||||
}
|
||||
this->await_target_time_(); // wait for duration of last pulse
|
||||
this->pin_->digital_write(false);
|
||||
|
||||
if (i + 1 < send_times)
|
||||
delayMicroseconds(send_wait);
|
||||
this->target_time_ += send_wait;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,9 @@ def validate(config):
|
||||
raise cv.Invalid("initial_value cannot be used with lambda")
|
||||
if CONF_RESTORE_VALUE in config:
|
||||
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:
|
||||
raise cv.Invalid(
|
||||
"Either optimistic mode must be enabled, or set_action must be set, to handle the number being set."
|
||||
@ -80,8 +83,7 @@ async def to_code(config):
|
||||
|
||||
else:
|
||||
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:
|
||||
cg.add(var.set_restore_value(config[CONF_RESTORE_VALUE]))
|
||||
|
||||
|
@ -14,6 +14,16 @@ from esphome.const import (
|
||||
CONF_DATA,
|
||||
CONF_RX_BUFFER_SIZE,
|
||||
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
|
||||
|
||||
@ -31,6 +41,8 @@ ESP8266UartComponent = uart_ns.class_(
|
||||
|
||||
UARTDevice = uart_ns.class_("UARTDevice")
|
||||
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
|
||||
|
||||
|
||||
@ -75,6 +87,34 @@ CONF_STOP_BITS = "stop_bits"
|
||||
CONF_DATA_BITS = "data_bits"
|
||||
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(
|
||||
cv.Schema(
|
||||
{
|
||||
@ -91,12 +131,38 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_INVERT): cv.invalid(
|
||||
"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),
|
||||
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):
|
||||
cg.add_global(uart_ns.using)
|
||||
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_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!
|
||||
UART_DEVICE_SCHEMA = cv.Schema(
|
||||
|
@ -45,17 +45,17 @@ class UARTDevice {
|
||||
// Compat APIs
|
||||
int read() {
|
||||
uint8_t data;
|
||||
if (!read_byte(&data))
|
||||
if (!this->read_byte(&data))
|
||||
return -1;
|
||||
return data;
|
||||
}
|
||||
size_t write(uint8_t data) {
|
||||
write_byte(data);
|
||||
this->write_byte(data);
|
||||
return 1;
|
||||
}
|
||||
int peek() {
|
||||
uint8_t data;
|
||||
if (!peek_byte(&data))
|
||||
if (!this->peek_byte(&data))
|
||||
return -1;
|
||||
return data;
|
||||
}
|
||||
|
@ -2,9 +2,13 @@
|
||||
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
#include "esphome/core/automation.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
@ -15,6 +19,14 @@ enum UARTParityOptions {
|
||||
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);
|
||||
|
||||
class UARTComponent {
|
||||
@ -50,6 +62,12 @@ class UARTComponent {
|
||||
void set_baud_rate(uint32_t baud_rate) { baud_rate_ = 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:
|
||||
virtual void check_logger_conflict() = 0;
|
||||
bool check_read_timeout_(size_t len = 1);
|
||||
@ -61,6 +79,9 @@ class UARTComponent {
|
||||
uint8_t stop_bits_;
|
||||
uint8_t data_bits_;
|
||||
UARTParityOptions parity_;
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
CallbackManager<void(UARTDirection, uint8_t)> debug_callback_{};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace uart
|
||||
|
@ -117,26 +117,32 @@ void ESP32ArduinoUARTComponent::dump_config() {
|
||||
|
||||
void ESP32ArduinoUARTComponent::write_array(const uint8_t *data, size_t len) {
|
||||
this->hw_serial_->write(data, len);
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
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) {
|
||||
if (!this->check_read_timeout_())
|
||||
return false;
|
||||
*data = this->hw_serial_->peek();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ESP32ArduinoUARTComponent::read_array(uint8_t *data, size_t len) {
|
||||
if (!this->check_read_timeout_(len))
|
||||
return false;
|
||||
this->hw_serial_->readBytes(data, len);
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
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;
|
||||
}
|
||||
|
||||
int ESP32ArduinoUARTComponent::available() { return this->hw_serial_->available(); }
|
||||
void ESP32ArduinoUARTComponent::flush() {
|
||||
ESP_LOGVV(TAG, " Flushing...");
|
||||
|
@ -130,9 +130,11 @@ void ESP8266UartComponent::write_array(const uint8_t *data, size_t len) {
|
||||
for (size_t i = 0; i < len; i++)
|
||||
this->sw_serial_->write_byte(data[i]);
|
||||
}
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
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) {
|
||||
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++)
|
||||
data[i] = this->sw_serial_->read_byte();
|
||||
}
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
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;
|
||||
}
|
||||
int ESP8266UartComponent::available() {
|
||||
|
@ -130,10 +130,13 @@ 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_);
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
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) {
|
||||
if (!this->check_read_timeout_())
|
||||
return false;
|
||||
@ -152,6 +155,7 @@ bool IDFUARTComponent::peek_byte(uint8_t *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))
|
||||
@ -165,12 +169,12 @@ bool IDFUARTComponent::read_array(uint8_t *data, size_t len) {
|
||||
}
|
||||
if (length_to_read > 0)
|
||||
uart_read_bytes(this->uart_num_, data, length_to_read, 20 / portTICK_RATE_MS);
|
||||
|
||||
xSemaphoreGive(this->lock_);
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
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;
|
||||
}
|
||||
|
||||
|
193
esphome/components/uart/uart_debugger.cpp
Normal file
193
esphome/components/uart/uart_debugger.cpp
Normal 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
|
101
esphome/components/uart/uart_debugger.h
Normal file
101
esphome/components/uart/uart_debugger.h
Normal 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
|
@ -1,6 +1,6 @@
|
||||
"""Constants used by esphome."""
|
||||
|
||||
__version__ = "2021.11.0b1"
|
||||
__version__ = "2021.11.0b2"
|
||||
|
||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||
|
||||
@ -34,6 +34,7 @@ ARDUINO_VERSION_ESP8266 = {
|
||||
SOURCE_FILE_EXTENSIONS = {".cpp", ".hpp", ".h", ".c", ".tcc", ".ino"}
|
||||
HEADER_FILE_EXTENSIONS = {".h", ".hpp", ".tcc"}
|
||||
|
||||
|
||||
CONF_ABOVE = "above"
|
||||
CONF_ACCELERATION = "acceleration"
|
||||
CONF_ACCELERATION_X = "acceleration_x"
|
||||
@ -47,6 +48,7 @@ CONF_ACTIVE_POWER = "active_power"
|
||||
CONF_ADDRESS = "address"
|
||||
CONF_ADDRESSABLE_LIGHT_ID = "addressable_light_id"
|
||||
CONF_ADVANCED = "advanced"
|
||||
CONF_AFTER = "after"
|
||||
CONF_ALPHA = "alpha"
|
||||
CONF_ALTITUDE = "altitude"
|
||||
CONF_AND = "and"
|
||||
@ -93,6 +95,7 @@ CONF_BUFFER_SIZE = "buffer_size"
|
||||
CONF_BUILD_PATH = "build_path"
|
||||
CONF_BUS_VOLTAGE = "bus_voltage"
|
||||
CONF_BUSY_PIN = "busy_pin"
|
||||
CONF_BYTES = "bytes"
|
||||
CONF_CALCULATED_LUX = "calculated_lux"
|
||||
CONF_CALIBRATE_LINEAR = "calibrate_linear"
|
||||
CONF_CALIBRATION = "calibration"
|
||||
@ -164,6 +167,7 @@ CONF_DAYS_OF_WEEK = "days_of_week"
|
||||
CONF_DC_PIN = "dc_pin"
|
||||
CONF_DEASSERT_RTS_DTR = "deassert_rts_dtr"
|
||||
CONF_DEBOUNCE = "debounce"
|
||||
CONF_DEBUG = "debug"
|
||||
CONF_DECAY_MODE = "decay_mode"
|
||||
CONF_DECELERATION = "deceleration"
|
||||
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_TRANSITION_LENGTH = "default_transition_length"
|
||||
CONF_DELAY = "delay"
|
||||
CONF_DELIMITER = "delimiter"
|
||||
CONF_DELTA = "delta"
|
||||
CONF_DEVICE = "device"
|
||||
CONF_DEVICE_CLASS = "device_class"
|
||||
@ -192,6 +197,8 @@ CONF_DNS2 = "dns2"
|
||||
CONF_DOMAIN = "domain"
|
||||
CONF_DRY_ACTION = "dry_action"
|
||||
CONF_DRY_MODE = "dry_mode"
|
||||
CONF_DUMMY_RECEIVER = "dummy_receiver"
|
||||
CONF_DUMMY_RECEIVER_ID = "dummy_receiver_id"
|
||||
CONF_DUMP = "dump"
|
||||
CONF_DURATION = "duration"
|
||||
CONF_EAP = "eap"
|
||||
|
@ -37,6 +37,7 @@
|
||||
#define USE_SWITCH
|
||||
#define USE_TEXT_SENSOR
|
||||
#define USE_TIME
|
||||
#define USE_UART_DEBUGGER
|
||||
#define USE_WEBSERVER
|
||||
#define USE_WIFI
|
||||
|
||||
|
@ -193,6 +193,18 @@ uart:
|
||||
data_bits: 8
|
||||
stop_bits: 1
|
||||
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
|
||||
tx_pin: GPIO25
|
||||
|
Loading…
Reference in New Issue
Block a user