ESPHome 2026.10.0
Loading...
Searching...
No Matches
tcp_uart.cpp
Go to the documentation of this file.
1#include "tcp_uart.h"
2
4#include "esphome/core/log.h"
5
6#include <cinttypes>
7#include <cstring>
8
10
11static const char *const TAG = "tcp_uart";
12
13static constexpr uint32_t DROP_LOG_INTERVAL_MS = 5000;
14
16 this->link_.begin(TAG);
17#ifdef USE_SOCKET_TCP_LISTENER
18 this->listener_.begin(TAG);
19#endif
20#ifdef USE_BINARY_SENSOR
21 if (this->connected_sensor_ != nullptr) {
22 this->connected_sensor_->publish_state(false);
23 }
24#endif
25#ifdef USE_SENSOR
26 if (this->disconnects_sensor_ != nullptr) {
28 }
29#endif
30}
31
33 ESP_LOGCONFIG(TAG,
34 "TCP UART:\n"
35 " %s: %s:%u\n"
36 " Reconnect Interval: %" PRIu32 "ms",
37 this->server_ ? LOG_STR_LITERAL("Listen") : LOG_STR_LITERAL("Host"),
38 this->server_ ? LOG_STR_LITERAL("*") : this->link_.host(), this->link_.port(),
39 this->link_.reconnect_interval());
40#ifdef USE_SOCKET_TCP_LISTENER
41 this->listener_.dump_config();
42#endif
43#ifdef USE_BINARY_SENSOR
44 LOG_BINARY_SENSOR(" ", "Connected", this->connected_sensor_);
45#endif
46#ifdef USE_SENSOR
47 LOG_SENSOR(" ", "Disconnects", this->disconnects_sensor_);
48#endif
49}
50
52 this->link_.close();
53#ifdef USE_SOCKET_TCP_LISTENER
54 this->listener_.close();
55#endif
56}
57
59 bool up = this->link_.connected();
60 this->link_was_up_ = up;
61 if (up) {
62 // Unread bytes of the last session stay readable while down, never into the next one.
63 this->rx_start_ = this->rx_end_ = 0;
64 }
65#ifdef USE_BINARY_SENSOR
66 if (this->connected_sensor_ != nullptr) {
68 }
69#endif
70#ifdef USE_SENSOR
71 // Only edges get here, so down is the falling edge.
72 if (!up && this->disconnects_sensor_ != nullptr) {
73 this->disconnects_++;
75 }
76#endif
77}
78
80 if (this->rx_start_ != 0) {
81 this->rx_end_ -= this->rx_start_;
82 std::memmove(this->rx_, this->rx_ + this->rx_start_, this->rx_end_);
83 this->rx_start_ = 0;
84 }
85 size_t room = RX_BUFFER_SIZE - this->rx_end_;
86 if (room == 0) {
87 // Only a read that filled all free space gets here, so rx_pending_ is already set.
88 return;
89 }
90 ssize_t count = this->link_.read(this->rx_ + this->rx_end_, room);
91 if (count <= 0) {
92 // A dropped link (-1) is seen by sync_link_() on the next loop.
93 if (count == 0) {
94 this->rx_pending_ = false;
95 }
96 return;
97 }
98 this->rx_end_ += static_cast<uint16_t>(count);
99 this->rx_pending_ = static_cast<size_t>(count) == room;
100}
101
103#ifdef USE_SOCKET_TCP_LISTENER
104 if (this->server_) {
105 // link_was_up_ holds the accept until the previous drop's edge has run,
106 // so the sensor sees the disconnect and the new session's edge clears RX.
107 this->listener_.poll(this->link_, !this->link_was_up_);
108 } else {
109 this->link_.poll();
110 }
111#else
112 this->link_.poll();
113#endif
114 if (this->link_.connected() != this->link_was_up_) {
115 this->sync_link_();
116 }
117 if (!this->link_was_up_) {
118 return;
119 }
120 if (this->rx_pending_ || this->link_.ready()) {
121 this->read_socket_();
122 }
123 this->link_.flush_tx();
124}
125
126void TcpUart::write_array(const uint8_t *data, size_t len) {
127 size_t queued = this->link_.queue(data, len);
128 if (queued < len) {
130 if (this->last_drop_log_ms_ == 0 || now - this->last_drop_log_ms_ >= DROP_LOG_INTERVAL_MS) {
131 ESP_LOGW(TAG, "%s, dropped %u bytes",
132 this->link_.connected() ? LOG_STR_LITERAL("TX buffer full") : LOG_STR_LITERAL("Not connected"),
133 static_cast<unsigned>(len - queued));
134 this->last_drop_log_ms_ = now;
135 }
136 }
137}
138
139bool TcpUart::peek_byte(uint8_t *data) {
140 if (this->rx_start_ == this->rx_end_) {
141 return false;
142 }
143 *data = this->rx_[this->rx_start_];
144 return true;
145}
146
147bool TcpUart::read_array(uint8_t *data, size_t len) {
148 if (this->available() < len) {
149 return false;
150 }
151 std::memcpy(data, this->rx_ + this->rx_start_, len);
152 this->rx_start_ += static_cast<uint16_t>(len);
153 return true;
154}
155
157 bool emptied = this->link_.flush_tx();
158 if (!this->link_.connected()) {
159 // A down link cannot have delivered anything, whether this flush dropped
160 // it or an earlier loop() write did.
162 }
164}
165
166} // namespace esphome::tcp_uart
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 publish_state(bool new_state)
Publish a new state to the front-end.
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:65
void poll(TcpClientLink &link, bool may_accept)
Server state machine; call every loop.
void begin(const char *tag)
Call from setup(); tag names the log lines.
void dump_config() const
One config line per allowed network.
bool read_array(uint8_t *data, size_t len) override
Definition tcp_uart.cpp:147
socket::TcpClientLink link_
Definition tcp_uart.h:68
void write_array(const uint8_t *data, size_t len) override
Definition tcp_uart.cpp:126
size_t available() override
Definition tcp_uart.h:51
binary_sensor::BinarySensor * connected_sensor_
Definition tcp_uart.h:73
void setup() override
Definition tcp_uart.cpp:15
void dump_config() override
Definition tcp_uart.cpp:32
void on_shutdown() override
Definition tcp_uart.cpp:51
static constexpr size_t RX_BUFFER_SIZE
Definition tcp_uart.h:66
uint8_t rx_[RX_BUFFER_SIZE]
Definition tcp_uart.h:88
uart::UARTFlushResult flush() override
Definition tcp_uart.cpp:156
bool peek_byte(uint8_t *data) override
Definition tcp_uart.cpp:139
sensor::Sensor * disconnects_sensor_
Definition tcp_uart.h:76
socket::TcpListener listener_
Definition tcp_uart.h:70
unsigned count
Definition gif.cpp:51
__int64 ssize_t
Definition httplib.h:178
UARTFlushResult
Result of a flush() call.
@ 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.
const void size_t len
Definition hal.h:64
Application App
Global storage of Application pointer - only one Application can exist.
static void uint32_t