Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improv Serial support via USB CDC and JTAG #5559

Merged
merged 3 commits into from Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 10 additions & 3 deletions esphome/components/improv_serial/__init__.py
@@ -1,10 +1,14 @@
from esphome.components.logger import USB_CDC, USB_SERIAL_JTAG
from esphome.components import improv_base
from esphome.components.esp32 import get_esp32_variant
from esphome.components.esp32.const import (
VARIANT_ESP32S3,
)
from esphome.components.logger import USB_CDC
from esphome.const import CONF_BAUD_RATE, CONF_HARDWARE_UART, CONF_ID, CONF_LOGGER
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.core import CORE
import esphome.final_validate as fv
from esphome.components import improv_base

AUTO_LOAD = ["improv_base"]
CODEOWNERS = ["@esphome/core"]
Expand All @@ -30,7 +34,10 @@ def validate_logger(config):
if logger_conf[CONF_BAUD_RATE] == 0:
raise cv.Invalid("improv_serial requires the logger baud_rate to be not 0")
if CORE.using_esp_idf:
if logger_conf[CONF_HARDWARE_UART] in [USB_SERIAL_JTAG, USB_CDC]:
if (
logger_conf[CONF_HARDWARE_UART] == USB_CDC
and get_esp32_variant() == VARIANT_ESP32S3
):
raise cv.Invalid(
"improv_serial does not support the selected logger hardware_uart"
)
Expand Down
102 changes: 79 additions & 23 deletions esphome/components/improv_serial/improv_serial_component.cpp
Expand Up @@ -31,26 +31,57 @@ void ImprovSerialComponent::setup() {

void ImprovSerialComponent::dump_config() { ESP_LOGCONFIG(TAG, "Improv Serial:"); }

int ImprovSerialComponent::available_() {
optional<uint8_t> ImprovSerialComponent::read_byte_() {
optional<uint8_t> byte;
uint8_t data = 0;
#ifdef USE_ARDUINO
return this->hw_serial_->available();
if (this->hw_serial_->available()) {
this->hw_serial_->readBytes(&data, 1);
byte = data;
}
#endif
#ifdef USE_ESP_IDF
size_t available;
uart_get_buffered_data_len(this->uart_num_, &available);
return available;
#endif
}

uint8_t ImprovSerialComponent::read_byte_() {
uint8_t data;
#ifdef USE_ARDUINO
this->hw_serial_->readBytes(&data, 1);
switch (logger::global_logger->get_uart()) {
case logger::UART_SELECTION_UART0:
case logger::UART_SELECTION_UART1:
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \
!defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3)
case logger::UART_SELECTION_UART2:
#endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3
if (this->uart_num_ >= 0) {
size_t available;
uart_get_buffered_data_len(this->uart_num_, &available);
if (available) {
uart_read_bytes(this->uart_num_, &data, 1, 20 / portTICK_PERIOD_MS);
byte = data;
}
}
break;
#if defined(CONFIG_ESP_CONSOLE_USB_CDC) && (defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3))
case logger::UART_SELECTION_USB_CDC:
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
if (esp_usb_console_available_for_read()) {
#else
if (esp_usb_console_read_available()) {
#endif
#ifdef USE_ESP_IDF
uart_read_bytes(this->uart_num_, &data, 1, 20 / portTICK_PERIOD_MS);
esp_usb_console_read_buf((char *) &data, 1);
byte = data;
}
break;
#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3)
case logger::UART_SELECTION_USB_SERIAL_JTAG: {
if (usb_serial_jtag_read_bytes((char *) &data, 1, 20 / portTICK_PERIOD_MS)) {
byte = data;
}
break;
}
#endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3
default:
break;
}
#endif
return data;
return byte;
}

void ImprovSerialComponent::write_data_(std::vector<uint8_t> &data) {
Expand All @@ -59,24 +90,49 @@ void ImprovSerialComponent::write_data_(std::vector<uint8_t> &data) {
this->hw_serial_->write(data.data(), data.size());
#endif
#ifdef USE_ESP_IDF
uart_write_bytes(this->uart_num_, data.data(), data.size());
switch (logger::global_logger->get_uart()) {
case logger::UART_SELECTION_UART0:
case logger::UART_SELECTION_UART1:
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \
!defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3)
case logger::UART_SELECTION_UART2:
#endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3
uart_write_bytes(this->uart_num_, data.data(), data.size());
break;
#if defined(CONFIG_ESP_CONSOLE_USB_CDC) && (defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3))
case logger::UART_SELECTION_USB_CDC: {
const char *msg = (char *) data.data();
esp_usb_console_write_buf(msg, data.size());
break;
}
#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3)
case logger::UART_SELECTION_USB_SERIAL_JTAG:
usb_serial_jtag_write_bytes((char *) data.data(), data.size(), 20 / portTICK_PERIOD_MS);
break;
#endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32S3
default:
break;
}
#endif
}

void ImprovSerialComponent::loop() {
const uint32_t now = millis();
if (now - this->last_read_byte_ > 50) {
if (this->last_read_byte_ && (millis() - this->last_read_byte_ > IMPROV_SERIAL_TIMEOUT)) {
this->last_read_byte_ = 0;
this->rx_buffer_.clear();
this->last_read_byte_ = now;
ESP_LOGV(TAG, "Improv Serial timeout");
}

while (this->available_()) {
uint8_t byte = this->read_byte_();
if (this->parse_improv_serial_byte_(byte)) {
this->last_read_byte_ = now;
auto byte = this->read_byte_();
while (byte.has_value()) {
if (this->parse_improv_serial_byte_(byte.value())) {
this->last_read_byte_ = millis();
} else {
this->last_read_byte_ = 0;
this->rx_buffer_.clear();
}
byte = this->read_byte_();
}

if (this->state_ == improv::STATE_PROVISIONING) {
Expand Down
11 changes: 9 additions & 2 deletions esphome/components/improv_serial/improv_serial_component.h
Expand Up @@ -14,6 +14,13 @@
#endif
#ifdef USE_ESP_IDF
#include <driver/uart.h>
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) || \
defined(USE_ESP32_VARIANT_ESP32H2)
#include <driver/usb_serial_jtag.h>
#endif
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
#include <esp_private/usb_console.h>
#endif
#endif

namespace esphome {
Expand All @@ -26,6 +33,7 @@ enum ImprovSerialType : uint8_t {
TYPE_RPC_RESPONSE = 0x04
};

static const uint16_t IMPROV_SERIAL_TIMEOUT = 100;
static const uint8_t IMPROV_SERIAL_VERSION = 1;

class ImprovSerialComponent : public Component, public improv_base::ImprovBase {
Expand All @@ -48,8 +56,7 @@ class ImprovSerialComponent : public Component, public improv_base::ImprovBase {
std::vector<uint8_t> build_rpc_settings_response_(improv::Command command);
std::vector<uint8_t> build_version_info_();

int available_();
uint8_t read_byte_();
optional<uint8_t> read_byte_();
void write_data_(std::vector<uint8_t> &data);

#ifdef USE_ARDUINO
Expand Down
107 changes: 81 additions & 26 deletions esphome/components/logger/logger.cpp
Expand Up @@ -3,8 +3,21 @@

#ifdef USE_ESP_IDF
#include <driver/uart.h>

#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) || \
defined(USE_ESP32_VARIANT_ESP32H2)
#include <driver/usb_serial_jtag.h>
#include <esp_vfs_dev.h>
#include <esp_vfs_usb_serial_jtag.h>
#endif

#include "freertos/FreeRTOS.h"
#include "esp_idf_version.h"

#include <cstdint>
#include <cstdio>
#include <fcntl.h>

#endif // USE_ESP_IDF

#if defined(USE_ESP32_FRAMEWORK_ARDUINO) || defined(USE_ESP_IDF)
Expand Down Expand Up @@ -93,6 +106,58 @@ void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStr
}
#endif

#ifdef USE_ESP_IDF
void Logger::init_uart_() {
uart_config_t uart_config{};
uart_config.baud_rate = (int) baud_rate_;
uart_config.data_bits = UART_DATA_8_BITS;
uart_config.parity = UART_PARITY_DISABLE;
uart_config.stop_bits = UART_STOP_BITS_1;
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
uart_config.source_clk = UART_SCLK_DEFAULT;
#endif
uart_param_config(this->uart_num_, &uart_config);
const int uart_buffer_size = tx_buffer_size_;
// Install UART driver using an event queue here
uart_driver_install(this->uart_num_, uart_buffer_size, uart_buffer_size, 10, nullptr, 0);
}

#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
void Logger::init_usb_cdc_() {}
#endif

#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) || \
defined(USE_ESP32_VARIANT_ESP32H2)
void Logger::init_usb_serial_jtag_() {
setvbuf(stdin, NULL, _IONBF, 0); // Disable buffering on stdin

// Minicom, screen, idf_monitor send CR when ENTER key is pressed
esp_vfs_dev_usb_serial_jtag_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
// Move the caret to the beginning of the next line on '\n'
esp_vfs_dev_usb_serial_jtag_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);

// Enable non-blocking mode on stdin and stdout
fcntl(fileno(stdout), F_SETFL, 0);
fcntl(fileno(stdin), F_SETFL, 0);

usb_serial_jtag_driver_config_t usb_serial_jtag_config{};
usb_serial_jtag_config.rx_buffer_size = 512;
usb_serial_jtag_config.tx_buffer_size = 512;

esp_err_t ret = ESP_OK;
// Install USB-SERIAL-JTAG driver for interrupt-driven reads and writes
ret = usb_serial_jtag_driver_install(&usb_serial_jtag_config);
if (ret != ESP_OK) {
return;
}

// Tell vfs to use usb-serial-jtag driver
esp_vfs_usb_serial_jtag_use_driver();
}
#endif
#endif

int HOT Logger::level_for(const char *tag) {
// Uses std::vector<> for low memory footprint, though the vector
// could be sorted to minimize lookup times. This feature isn't used that
Expand Down Expand Up @@ -120,19 +185,19 @@ void HOT Logger::log_message_(int level, const char *tag, int offset) {
#ifdef USE_ESP_IDF
if (
#if defined(USE_ESP32_VARIANT_ESP32S2)
uart_ == UART_SELECTION_USB_CDC
this->uart_ == UART_SELECTION_USB_CDC
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32H2)
uart_ == UART_SELECTION_USB_SERIAL_JTAG
this->uart_ == UART_SELECTION_USB_SERIAL_JTAG
#elif defined(USE_ESP32_VARIANT_ESP32S3)
uart_ == UART_SELECTION_USB_CDC || uart_ == UART_SELECTION_USB_SERIAL_JTAG
this->uart_ == UART_SELECTION_USB_CDC || this->uart_ == UART_SELECTION_USB_SERIAL_JTAG
#else
/* DISABLES CODE */ (false) // NOLINT
#endif
) {
puts(msg);
kbx81 marked this conversation as resolved.
Show resolved Hide resolved
} else {
uart_write_bytes(uart_num_, msg, strlen(msg));
uart_write_bytes(uart_num_, "\n", 1);
uart_write_bytes(this->uart_num_, msg, strlen(msg));
uart_write_bytes(this->uart_num_, "\n", 1);
}
#endif
}
Expand Down Expand Up @@ -209,48 +274,38 @@ void Logger::pre_setup() {
}
#endif // USE_ARDUINO
#ifdef USE_ESP_IDF
uart_num_ = UART_NUM_0;
switch (uart_) {
this->uart_num_ = UART_NUM_0;
switch (this->uart_) {
case UART_SELECTION_UART0:
uart_num_ = UART_NUM_0;
this->uart_num_ = UART_NUM_0;
break;
case UART_SELECTION_UART1:
uart_num_ = UART_NUM_1;
this->uart_num_ = UART_NUM_1;
break;
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \
!defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) && !defined(USE_ESP32_VARIANT_ESP32H2)
case UART_SELECTION_UART2:
uart_num_ = UART_NUM_2;
this->uart_num_ = UART_NUM_2;
break;
#endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3 &&
// !USE_ESP32_VARIANT_ESP32H2
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
case UART_SELECTION_USB_CDC:
uart_num_ = -1;
this->uart_num_ = -1;
this->init_usb_cdc_();
break;
#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) || \
defined(USE_ESP32_VARIANT_ESP32H2)
case UART_SELECTION_USB_SERIAL_JTAG:
uart_num_ = -1;
this->uart_num_ = -1;
this->init_usb_serial_jtag_();
break;
#endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 ||
// USE_ESP32_VARIANT_ESP32H2
}
if (uart_num_ >= 0) {
uart_config_t uart_config{};
uart_config.baud_rate = (int) baud_rate_;
uart_config.data_bits = UART_DATA_8_BITS;
uart_config.parity = UART_PARITY_DISABLE;
uart_config.stop_bits = UART_STOP_BITS_1;
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
uart_config.source_clk = UART_SCLK_DEFAULT;
#endif
uart_param_config(uart_num_, &uart_config);
const int uart_buffer_size = tx_buffer_size_;
// Install UART driver using an event queue here
uart_driver_install(uart_num_, uart_buffer_size, uart_buffer_size, 10, nullptr, 0);
if (this->uart_num_ >= 0) {
this->init_uart_();
}
#endif // USE_ESP_IDF
}
Expand Down
10 changes: 10 additions & 0 deletions esphome/components/logger/logger.h
Expand Up @@ -107,6 +107,16 @@ class Logger : public Component {
#endif

protected:
#ifdef USE_ESP_IDF
void init_uart_();
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
void init_usb_cdc_();
#endif
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) || \
defined(USE_ESP32_VARIANT_ESP32H2)
void init_usb_serial_jtag_();
#endif
#endif
void write_header_(int level, const char *tag, int line);
void write_footer_();
void log_message_(int level, const char *tag, int offset = 0);
Expand Down