mirror of
https://github.com/esphome/esphome.git
synced 2024-11-23 15:38:11 +01:00
Uart debugging support (#2478)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net> Co-authored-by: Maurice Makaay <account-github@makaay.nl> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
21c896d8f8
commit
7552893311
11 changed files with 430 additions and 13 deletions
|
@ -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(
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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...");
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
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
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue