ESPHome 2026.10.0
Loading...
Searching...
No Matches
uart_tcp.cpp
Go to the documentation of this file.
1#include "uart_tcp.h"
2
4#include "esphome/core/log.h"
5
6#include <algorithm>
7#include <cerrno>
8#include <cinttypes>
9
11
12static const char *const TAG = "uart_tcp";
13
14// Keeps the pacing product in 32 bits up to about 10 Mbaud.
15static constexpr uint32_t MAX_PACE_SPAN_MS = 4000;
16
18 this->link_.begin(TAG);
19#ifdef USE_SOCKET_TCP_LISTENER
20 this->listener_.begin(TAG);
21#endif
22#ifdef USE_BINARY_SENSOR
23 if (this->connected_sensor_ != nullptr) {
24 this->connected_sensor_->publish_state(false);
25 }
26#endif
27#ifdef USE_SENSOR
28 if (this->disconnects_sensor_ != nullptr) {
30 }
31#endif
32}
33
35 ESP_LOGCONFIG(TAG,
36 "UART TCP:\n"
37 " %s: %s:%u\n"
38 " Reconnect Interval: %" PRIu32 "ms",
39 this->server_ ? LOG_STR_LITERAL("Listen") : LOG_STR_LITERAL("Host"),
40 this->server_ ? LOG_STR_LITERAL("*") : this->link_.host(), this->link_.port(),
41 this->link_.reconnect_interval());
42#ifdef USE_SOCKET_TCP_LISTENER
43 this->listener_.dump_config();
44#endif
45#ifdef USE_BINARY_SENSOR
46 LOG_BINARY_SENSOR(" ", "Connected", this->connected_sensor_);
47#endif
48#ifdef USE_SENSOR
49 LOG_SENSOR(" ", "Disconnects", this->disconnects_sensor_);
50#endif
51}
52
54 this->link_.close();
55#ifdef USE_SOCKET_TCP_LISTENER
56 this->listener_.close();
57#endif
58}
59
61 bool up = this->link_.connected();
62 this->link_was_up_ = up;
63 if (up) {
64 // The driver kept whatever arrived while the link was down.
65 this->discard_uart_();
66 }
67#ifdef USE_BINARY_SENSOR
68 if (this->connected_sensor_ != nullptr) {
70 }
71#endif
72#ifdef USE_SENSOR
73 // Only edges get here, so down is the falling edge.
74 if (!up && this->disconnects_sensor_ != nullptr) {
75 this->disconnects_++;
77 }
78#endif
79}
80
82 // A hardware write blocks until the driver takes every byte. Leave what does
83 // not fit in the socket, so TCP flow control throttles the peer.
84 size_t room = this->parent_->available_for_write();
85 if (room == SIZE_MAX) {
86 // Capacity unknown on this platform; pace to the UART time since the last write,
87 // at most one loop interval and 4 s, so a pass woken early by the socket writes little.
88 uint32_t span = std::min(
90 // 10 bits per byte on the line.
91 uint32_t paced = this->parent_->get_baud_rate() / 10 * span / 1000;
92 room = std::max<size_t>(1, paced);
93 }
94 if (room == 0) {
95 this->rx_pending_ = true;
96 return;
97 }
98 uint8_t tmp[READ_CHUNK];
99 size_t want = std::min(room, sizeof(tmp));
100 ssize_t count = this->link_.read(tmp, want);
101 if (count <= 0) {
102 // A dropped link (-1) is cleaned up by sync_link_() on the next loop.
103 if (count == 0) {
104 this->rx_pending_ = false;
105 }
106 return;
107 }
108 this->rx_pending_ = static_cast<size_t>(count) == want;
109 this->write_array(tmp, static_cast<size_t>(count));
111}
112
114 // Drain exactly what was buffered while the link was down; later bytes are live.
115 uint8_t dump[DISCARD_CHUNK];
116 size_t left = this->available();
117 while (left != 0) {
118 size_t n = std::min(left, sizeof(dump));
119 if (!this->read_array(dump, n)) {
120 return;
121 }
122 left -= n;
123 }
124}
125
127 size_t want = std::min<size_t>(this->available(), this->link_.tx_free());
128 if (want != 0 && this->read_array(this->link_.tx_tail(), want)) {
129 this->link_.tx_commit(want);
130 }
131}
132
134#ifdef USE_SOCKET_TCP_LISTENER
135 if (this->server_) {
136 // link_was_up_ holds the accept until the previous drop's edge has run,
137 // so the sensor and the stale UART discard always see the disconnect.
138 this->listener_.poll(this->link_, !this->link_was_up_);
139 } else {
140 this->link_.poll();
141 }
142#else
143 this->link_.poll();
144#endif
145 if (this->link_.connected() != this->link_was_up_) {
146 this->sync_link_();
147 }
148 if (!this->link_was_up_) {
149 return;
150 }
151 if (this->rx_pending_ || this->link_.ready()) {
152 this->read_socket_();
153 }
154 // UART bytes picked up here go out in the same pass.
155 this->read_uart_();
156 this->link_.flush_tx();
157}
158
159} // namespace esphome::uart_tcp
uint32_t get_loop_interval() const
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.
virtual size_t available_for_write()
optional< std::array< uint8_t, N > > read_array()
Definition uart.h:39
UARTComponent * parent_
Definition uart.h:76
void write_array(const uint8_t *data, size_t len)
Definition uart.h:27
static constexpr size_t DISCARD_CHUNK
Definition uart_tcp.h:55
socket::TcpClientLink link_
Definition uart_tcp.h:57
sensor::Sensor * disconnects_sensor_
Definition uart_tcp.h:67
void on_shutdown() override
Definition uart_tcp.cpp:53
static constexpr size_t READ_CHUNK
Definition uart_tcp.h:53
socket::TcpListener listener_
Definition uart_tcp.h:59
binary_sensor::BinarySensor * connected_sensor_
Definition uart_tcp.h:62
void dump_config() override
Definition uart_tcp.cpp:34
void setup() override
Definition uart_tcp.cpp:17
unsigned count
Definition gif.cpp:51
__int64 ssize_t
Definition httplib.h:178
Application App
Global storage of Application pointer - only one Application can exist.
static void uint32_t