17#ifdef USE_SERIAL_PROXY_USB_IDENTITY
21#ifdef USE_SERIAL_PROXY_HOST_UART
46#ifdef USE_SERIAL_PROXY_USB_IDENTITY
52#ifdef USE_SERIAL_PROXY_TAP
65#ifdef USE_SERIAL_PROXY_TAP
75 ESP_LOGD(TAG,
"Session ended, returning serial proxy [%" PRIu32
"] to RAW mode", this->
instance_index_);
85 ESP_LOGW(TAG,
"Subscriber disconnected");
93#ifdef USE_SERIAL_PROXY_TAP
122#ifdef USE_SERIAL_PROXY_TAP
138#ifdef USE_SERIAL_PROXY_TAP
141 if (this->
tap_ ==
nullptr) {
172 "Serial Proxy [%" PRIu32
"]:\n"
181 : LOG_STR_LITERAL(
"TTL"),
182 this->
rts_pin_ !=
nullptr ? LOG_STR_LITERAL(
"configured") : LOG_STR_LITERAL(
"not configured"),
183 this->
dtr_pin_ !=
nullptr ? LOG_STR_LITERAL(
"configured") : LOG_STR_LITERAL(
"not configured"));
187 uint8_t parity, uint8_t stop_bits, uint8_t data_size) {
190 ESP_LOGW(TAG,
"Ignoring configure request from client without port subscription [%" PRIu32
"]",
196 "Configuring serial proxy [%" PRIu32
"]: baud=%" PRIu32
", flow_ctrl=%s, parity=%" PRIu8
", stop=%" PRIu8
200 auto *uart_comp = this->
parent_;
201 if (uart_comp ==
nullptr) {
202 ESP_LOGE(TAG,
"UART component not available");
208 ESP_LOGW(TAG,
"Invalid baud rate: 0");
212 ESP_LOGW(TAG,
"Invalid stop bits: %u (must be 1 or 2)",
stop_bits);
215 if (data_size < 5 || data_size > 8) {
216 ESP_LOGW(TAG,
"Invalid data bits: %u (must be 5-8)", data_size);
220 ESP_LOGW(TAG,
"Invalid parity: %u (must be 0-2)",
parity);
224 ESP_LOGW(TAG,
"Hardware flow control requested but is not yet supported");
237 if (uart_comp->get_baud_rate() == baudrate && uart_comp->get_stop_bits() ==
stop_bits &&
238 uart_comp->get_data_bits() == data_size && uart_comp->get_parity() == PARITY_MAP[
parity]) {
239 ESP_LOGV(TAG,
"Settings unchanged, skipping reconfigure [%" PRIu32
"]", this->
instance_index_);
246 uart_comp->set_data_bits(data_size);
248 uart_comp->set_parity(PARITY_MAP[
parity]);
251#if defined(USE_ESP8266) || defined(USE_ESP32)
252 uart_comp->load_settings(
true);
262 ESP_LOGW(TAG,
"Ignoring mode request from client without port subscription [%" PRIu32
"]", this->
instance_index_);
268 ESP_LOGW(TAG,
"Invalid mode: %" PRIu32,
static_cast<uint32_t>(
mode));
272#ifdef USE_SERIAL_PROXY_TAP
273 const bool has_tap = this->
tap_ !=
nullptr;
275 const bool has_tap =
false;
278 ESP_LOGW(TAG,
"No tap on serial proxy [%" PRIu32
"]; PROTOCOL mode unavailable", this->
instance_index_);
281 ESP_LOGD(TAG,
"Serial proxy [%" PRIu32
"] mode set to %s", this->
instance_index_,
283#ifdef USE_SERIAL_PROXY_TAP
284 const bool entering_protocol_mode =
286 const bool leaving_protocol_mode =
290 if (entering_protocol_mode && this->
tap_ !=
nullptr) {
297 if (leaving_protocol_mode && this->
tap_ !=
nullptr) {
310 ESP_LOGW(TAG,
"Ignoring write from client that does not hold serial proxy [%" PRIu32
"]", this->
instance_index_);
315 ESP_LOGV(TAG,
"Ignoring write from client without port subscription [%" PRIu32
"]", this->
instance_index_);
320 if (data ==
nullptr ||
len == 0)
327 bool trimmed =
false;
338 "TX buffer full on serial proxy [%" PRIu32
"]: dropping %zu of %zu bytes (would stall %" PRIu32
339 " ms at %" PRIu32
" baud); raise the UART tx_buffer_size or pace writes",
353#ifdef USE_SERIAL_PROXY_TAP
364 return static_cast<uint64_t
>(bytes) * bits_per_byte * 1000 / std::max<uint32_t>(this->
parent_->
get_baud_rate(), 1);
370 ESP_LOGW(TAG,
"Ignoring modem pin request from client without port subscription [%" PRIu32
"]",
379 ESP_LOGW(TAG,
"Requested modem pin not configured on serial proxy [%" PRIu32
"]", this->
instance_index_);
384 ESP_LOGV(TAG,
"Setting modem pins [%" PRIu32
"]: RTS=%s, DTR=%s", this->
instance_index_, ONOFF(rts), ONOFF(dtr));
391 applied = uart->set_modem_control(this->
dtr_pin_ !=
nullptr ? uart->get_dtr() : dtr,
392 this->rts_pin_ !=
nullptr ? uart->get_rts() : rts);
396 ESP_LOGW(TAG,
"Modem pins did not reach the UART on serial proxy [%" PRIu32
"]", this->
instance_index_);
420#ifdef USE_SERIAL_PROXY_USB_IDENTITY
430 if (!client->get_device_info(
scratch)) {
450#ifdef USE_SERIAL_PROXY_USB_IDENTITY
452 ESP_LOGD(TAG,
"USB device %s serial proxy [%" PRIu32
"]",
453 connected ? LOG_STR_LITERAL(
"attached to") : LOG_STR_LITERAL(
"removed from"), this->
instance_index_);
454#ifdef USE_SERIAL_PROXY_TAP
458 if (!connected && this->
tap_ !=
nullptr) {
479 rts = uart->get_rts();
481 dtr = uart->get_dtr();
491 ESP_LOGW(TAG,
"Ignoring flush from client without port subscription [%" PRIu32
"]", this->
instance_index_);
495 ESP_LOGV(TAG,
"Flushing serial proxy [%" PRIu32
"]", this->
instance_index_);
496 switch (this->
flush()) {
515 ESP_LOGV(TAG,
"API connection is already subscribed to serial proxy [%" PRIu32
"]", this->
instance_index_);
523 ESP_LOGE(TAG,
"Only one API subscription is allowed at a time");
526 ESP_LOGW(TAG,
"Previous subscriber disconnected; taking over subscription");
534 ESP_LOGV(TAG,
"API connection subscribed to serial proxy [%" PRIu32
"]", this->
instance_index_);
539 ESP_LOGV(TAG,
"API connection is not subscribed to serial proxy [%" PRIu32
"]", this->
instance_index_);
544#ifdef USE_SERIAL_PROXY_TAP
552 ESP_LOGV(TAG,
"API connection unsubscribed from serial proxy [%" PRIu32
"]", this->
instance_index_);
555 ESP_LOGW(TAG,
"Unknown serial proxy request type: %" PRIu32,
static_cast<uint32_t>(
type));
BedjetMode mode
BedJet operating mode.
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
void enable_loop()
Enable this component's loop.
void disable_loop()
Disable this component's loop.
virtual void digital_write(bool value)=0
T * get_parent() const
Get the parent of this object.
StringRef is a reference to a string owned by something else.
void send_serial_proxy_data(const SerialProxyDataReceived &msg)
bool is_connection_setup()
bool is_marked_for_removal() const
void send_serial_proxy_identity(const SerialProxyIdentity &msg)
Send a port identity to this client.
void send_serial_proxy_identity(const SerialProxyIdentity &msg)
Tell every subscribed client that a serial proxy port's identity changed.
void set_data(const uint8_t *data, size_t len)
enums::SerialProxyIdentitySource source
uint32_t interface_number
void fill_identity_(IdentityScratch &scratch, api::SerialProxyIdentity &msg) const
Fill an identity message for this port.
SerialProxyResult configure(api::APIConnection *api_connection, uint32_t baudrate, bool flow_control, uint8_t parity, uint8_t stop_bits, uint8_t data_size)
Configure UART parameters and apply them.
void read_and_send_(size_t available)
Read from UART, hand the bytes to any tap, and forward them to a subscriber (slow path with a 256-byt...
api::enums::SerialProxyMode mode_
How the bytes passing through are treated; zero is SERIAL_PROXY_MODE_RAW.
SerialProxyResult serial_proxy_request(api::APIConnection *api_connection, api::enums::SerialProxyRequestType type)
Handle a subscribe/unsubscribe request from an API client.
uint32_t get_modem_pins() const
Get current modem pin states as a bitmask of SerialProxyLineStateFlag values.
uint32_t instance_index_
Instance index for identifying this proxy in API messages.
bool trim_warned_
Set while writes are being trimmed, so a client streaming into a slow port warns once.
bool rts_state_
Current modem pin states.
SerialProxyResult flush_port(api::APIConnection *api_connection)
Flush the serial port (block until all TX data is sent)
static uint32_t stall_spent_ms
uint32_t get_configured_modem_pins() const
Get the modem pins this instance can drive as a bitmask of SerialProxyLineStateFlag values.
api::SerialProxyDataReceived outgoing_msg_
Pre-allocated outgoing message; instance field is set once in setup()
const char * name_
Human-readable port name (points to a string literal in flash)
api::enums::SerialProxyPortType port_type_
Port type.
GPIOPin * rts_pin_
Optional GPIO pins for modem control.
usb_uart::USBUartChannel * usb_channel_
The USB UART channel behind this port; nullptr on non-USB ports.
void on_usb_connection_changed_(bool connected)
The USB device behind this port was attached or removed; report the port's new identity to every subs...
bool tap_observing_() const
True when the tap should be shown the traffic passing through this port.
SerialProxyResult set_modem_pins(api::APIConnection *api_connection, uint32_t line_states)
Set modem pin states from a bitmask of SerialProxyLineStateFlag values.
SerialProxyResult set_mode_from_client(api::APIConnection *api_connection, api::enums::SerialProxyMode mode)
Handle a mode change requested by an API client.
void write_from_client(api::APIConnection *api_connection, const uint8_t *data, size_t len)
Write data received from an API client to the serial device.
static uint32_t stall_loop_time
Stall spent by writes in the current loop pass, keyed by the pass's cached start time.
bool is_subscriber_(api::APIConnection *api_connection) const
True when the given connection is the live subscriber.
void dump_config() override
void tap_pump()
Run one read-and-dispatch cycle immediately.
void send_identity(api::APIConnection *api_connection)
Send this port's identity to one client.
void reset_mode_()
Return the port to RAW when a subscriber goes away, so the mode never outlives it.
api::APIConnection * api_connection_
Subscribed API client (only one allowed at a time)
uint32_t wire_time_ms_(size_t bytes) const
Time the wire needs for the given number of bytes at the current framing.
bool with_modem_uart_(F &&f) const
Call f with the UART behind this port if that UART drives the modem lines itself.
virtual void on_device_disconnected()=0
The device behind the port went away – unplugged, or its power was cut.
virtual void on_protocol_enabled()=0
A client turned protocol handling on for this port.
virtual bool tap_needs_port() const =0
True when the port must keep reading even with no subscriber attached, so a tap can do its own protoc...
virtual void on_device_rx(const uint8_t *data, size_t len)=0
Bytes read from the device, before they are forwarded to any subscriber.
virtual void on_client_tx(const uint8_t *data, size_t len)=0
Bytes a subscriber sent towards the device, after they have been written.
virtual void on_protocol_disabled()=0
A client explicitly turned protocol handling off for this port.
UARTParityOptions get_parity() const
void set_baud_rate(uint32_t baud_rate)
uint8_t get_data_bits() const
uint8_t get_stop_bits() const
uint32_t get_baud_rate() const
virtual size_t available_for_write()
optional< std::array< uint8_t, N > > read_array()
void write_array(const uint8_t *data, size_t len)
uint8_t UARTParityOptions parity
uint8_t get_interface_class() const
bInterfaceClass of the interface get_interface_number() names
uint8_t get_port_number() const
Index of this port among the ports on its interface.
uint8_t get_interface_number() const
USB interface number a host driver binds to for this channel: the communication interface of a CDC AC...
bool is_connected() override
struct @66::@67 __attribute__
Wake the main loop task from an ISR. ISR-safe.
@ SERIAL_PROXY_PORT_TYPE_RS232
@ SERIAL_PROXY_PORT_TYPE_RS485
@ SERIAL_PROXY_PORT_TYPE_USB_SERIAL
@ SERIAL_PROXY_REQUEST_TYPE_UNSUBSCRIBE
@ SERIAL_PROXY_REQUEST_TYPE_SUBSCRIBE
@ SERIAL_PROXY_IDENTITY_FLAG_CONNECTED
@ SERIAL_PROXY_IDENTITY_FLAG_ERROR
@ SERIAL_PROXY_IDENTITY_SOURCE_USB
@ SERIAL_PROXY_MODE_PROTOCOL
APIServer * global_api_server
@ SERIAL_PROXY_LINE_STATE_FLAG_RTS
RTS (Request To Send)
@ SERIAL_PROXY_LINE_STATE_FLAG_DTR
DTR (Data Terminal Ready)
SerialProxyResult
Result of a client-initiated operation; mapped to api::enums::SerialProxyStatus by the API layer.
@ SERIAL_PROXY_RESULT_TIMEOUT
Timed out before TX completed.
@ SERIAL_PROXY_RESULT_ERROR
Driver or hardware error.
@ SERIAL_PROXY_RESULT_NOT_SUPPORTED
Requested feature is not available on this instance.
@ SERIAL_PROXY_RESULT_PORT_IN_USE
Denied: another live client holds the port.
@ SERIAL_PROXY_RESULT_OK
Operation completed or request accepted.
@ SERIAL_PROXY_RESULT_INVALID_ARGUMENT
A parameter value is out of range.
@ SERIAL_PROXY_RESULT_ASSUMED_SUCCESS
Platform cannot confirm TX drain; success assumed.
constexpr size_t SERIAL_PROXY_MAX_READ_SIZE
Maximum bytes to read from UART in a single loop iteration.
constexpr uint32_t SERIAL_PROXY_MAX_WRITE_STALL_MS
Longest main-loop stall client writes may cause per loop pass, shared by every instance; bytes the UA...
ESPHOME_LOG_TAG(TAG, "serial_proxy")
@ UART_CONFIG_PARITY_EVEN
@ UART_CONFIG_PARITY_NONE
@ UART_FLUSH_RESULT_ASSUMED_SUCCESS
Platform cannot report result; success is assumed.
@ UART_FLUSH_RESULT_SUCCESS
Confirmed: all bytes left the TX FIFO.
@ UART_FLUSH_RESULT_FAILED
Confirmed: driver or hardware error.
@ UART_FLUSH_RESULT_TIMEOUT
Confirmed: timed out before TX completed.
ESPHOME_ALWAYS_INLINE bool api_is_connected()
Return whether the node has at least one client connected to the native API.
Application App
Global storage of Application pointer - only one Application can exist.