ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
uart_component_host.cpp
Go to the documentation of this file.
1#ifdef USE_HOST
6#include "esphome/core/log.h"
7
8#if !(defined(__linux__) || defined(__APPLE__))
9#error This HostUartComponent implementation is not supported on this host OS
10#endif
11
12#include <cerrno>
13#include <cstdio>
14#include <cstring>
15#include <unistd.h>
16#include <fcntl.h>
17#include <termios.h>
18#include <sys/ioctl.h>
19
20#ifdef USE_LOGGER
22#endif
23
24namespace {
25
26speed_t get_baud(int baud) {
27#ifdef __APPLE__
28 return baud;
29#else
30 switch (baud) {
31 case 50:
32 return B50;
33 case 75:
34 return B75;
35 case 110:
36 return B110;
37 case 134:
38 return B134;
39 case 150:
40 return B150;
41 case 200:
42 return B200;
43 case 300:
44 return B300;
45 case 600:
46 return B600;
47 case 1200:
48 return B1200;
49 case 1800:
50 return B1800;
51 case 2400:
52 return B2400;
53 case 4800:
54 return B4800;
55 case 9600:
56 return B9600;
57 case 19200:
58 return B19200;
59 case 38400:
60 return B38400;
61 case 57600:
62 return B57600;
63 case 115200:
64 return B115200;
65 case 230400:
66 return B230400;
67 case 460800:
68 return B460800;
69 case 500000:
70 return B500000;
71 case 576000:
72 return B576000;
73 case 921600:
74 return B921600;
75 case 1000000:
76 return B1000000;
77 case 1152000:
78 return B1152000;
79 case 1500000:
80 return B1500000;
81 case 2000000:
82 return B2000000;
83 case 2500000:
84 return B2500000;
85 case 3000000:
86 return B3000000;
87 case 3500000:
88 return B3500000;
89 case 4000000:
90 return B4000000;
91 default:
92 return B0;
93 }
94#endif
95}
96
97} // namespace
98
99namespace esphome::uart {
100
101ESPHOME_LOG_TAG(TAG, "uart");
102
104 if (this->file_descriptor_ != -1) {
105 close(this->file_descriptor_);
106 this->file_descriptor_ = -1;
107 }
108}
109
111 ESP_LOGCONFIG(TAG, "Opening UART port");
112 speed_t baud = get_baud(this->baud_rate_);
113 if (baud == B0) {
114 ESP_LOGE(TAG, "Unsupported baud rate: %d", this->baud_rate_);
115 this->mark_failed();
116 return;
117 }
118 this->file_descriptor_ = ::open(this->port_name_.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
119 if (this->file_descriptor_ == -1) {
120 this->update_error_(strerror(errno));
121 this->mark_failed();
122 return;
123 }
124 fcntl(this->file_descriptor_, F_SETFL, 0);
125 struct termios options;
126 tcgetattr(this->file_descriptor_, &options);
127 cfmakeraw(&options);
128 options.c_cflag &= ~CRTSCTS;
129 options.c_cflag |= CREAD | CLOCAL;
130 options.c_iflag &= ~(IXOFF | IXANY);
131 // Set data bits
132 options.c_cflag &= ~CSIZE; // Mask the character size bits
133 switch (this->data_bits_) {
134 case 5:
135 options.c_cflag |= CS5;
136 break;
137 case 6:
138 options.c_cflag |= CS6;
139 break;
140 case 7:
141 options.c_cflag |= CS7;
142 break;
143 case 8:
144 default:
145 options.c_cflag |= CS8;
146 break;
147 }
148 // Set parity
149 switch (this->parity_) {
151 options.c_cflag &= ~PARENB;
152 break;
154 options.c_cflag |= PARENB;
155 options.c_cflag &= ~PARODD;
156 break;
158 options.c_cflag |= PARENB;
159 options.c_cflag |= PARODD;
160 break;
161 default:
162 break;
163 };
164 // Set stop bits
165 if (this->stop_bits_ == 2) {
166 options.c_cflag |= CSTOPB;
167 } else {
168 options.c_cflag &= ~CSTOPB;
169 }
170 cfsetispeed(&options, baud);
171 cfsetospeed(&options, baud);
172 tcsetattr(this->file_descriptor_, TCSANOW, &options);
173}
174
176 ESP_LOGCONFIG(TAG, "UART:");
177 ESP_LOGCONFIG(TAG, " Port: %s", this->port_name_.c_str());
178 if (this->file_descriptor_ == -1) {
179 ESP_LOGCONFIG(TAG, " Port status: Not opened");
180 if (!this->first_error_.empty()) {
181 ESP_LOGCONFIG(TAG, " Error: %s", this->first_error_.c_str());
182 }
183 return;
184 }
185 ESP_LOGCONFIG(TAG,
186 " Port status: opened\n"
187 " Baud Rate: %d\n"
188 " Data Bits: %d\n"
189 " Parity: %s\n"
190 " Stop Bits: %d",
191 this->baud_rate_, this->data_bits_,
192 this->parity_ == UART_CONFIG_PARITY_NONE ? "None"
193 : this->parity_ == UART_CONFIG_PARITY_EVEN ? "Even"
194 : "Odd",
195 this->stop_bits_);
196 this->check_logger_conflict();
197}
198
199void HostUartComponent::write_array(const uint8_t *data, size_t len) {
200 if (this->file_descriptor_ == -1) {
201 return;
202 }
203 size_t written = ::write(this->file_descriptor_, data, len);
204 if (written != len) {
205 this->update_error_(strerror(errno));
206 return;
207 }
208#ifdef USE_UART_DEBUGGER
209 for (size_t i = 0; i < len; i++) {
210 this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
211 }
212#endif
213}
214
215bool HostUartComponent::peek_byte(uint8_t *data) {
216 if (this->file_descriptor_ == -1) {
217 return false;
218 }
219 if (!this->has_peek_) {
220 if (!this->check_read_timeout_()) {
221 return false;
222 }
223 if (::read(this->file_descriptor_, &this->peek_byte_, 1) != 1) {
224 this->update_error_(strerror(errno));
225 return false;
226 }
227 this->has_peek_ = true;
228 }
229 *data = this->peek_byte_;
230 return true;
231}
232
233bool HostUartComponent::read_array(uint8_t *data, size_t len) {
234 if ((this->file_descriptor_ == -1) || (len == 0)) {
235 return false;
236 }
237 if (!this->check_read_timeout_(len))
238 return false;
239 size_t length_to_read = len;
240 if (this->has_peek_) {
241 length_to_read--;
242 *data = this->peek_byte_;
243 this->has_peek_ = false;
244 }
245 if (length_to_read > 0) {
246 int sz = ::read(this->file_descriptor_, data + (len - length_to_read), length_to_read);
247 if (sz == -1) {
248 this->update_error_(strerror(errno));
249 return false;
250 }
251 }
252#ifdef USE_UART_DEBUGGER
253 for (size_t i = 0; i < len; i++) {
254 this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
255 }
256#endif
257 return true;
258}
259
261 if (this->file_descriptor_ == -1) {
262 return 0;
263 }
264 int available;
265 int res = ioctl(this->file_descriptor_, FIONREAD, &available);
266 if (res == -1) {
267 this->update_error_(strerror(errno));
268 return 0;
269 }
270 size_t result = available;
271 if (this->has_peek_)
272 result++;
273 return result;
274};
275
277 if (this->file_descriptor_ == -1) {
279 }
280 ESP_LOGV(TAG, " Flushing");
281 if (tcdrain(this->file_descriptor_) == -1) {
282 this->update_error_(strerror(errno));
284 }
286}
287
288bool HostUartComponent::set_modem_control(bool dtr, bool rts) {
289 if (this->file_descriptor_ == -1) {
290 return false;
291 }
292 // One TIOCMSET so the device never sees a state in between the old and the new one
293 int bits;
294 if (ioctl(this->file_descriptor_, TIOCMGET, &bits) == -1) {
295 this->update_error_(strerror(errno));
296 return false;
297 }
298 bits = (bits & ~(TIOCM_DTR | TIOCM_RTS)) | (dtr ? TIOCM_DTR : 0) | (rts ? TIOCM_RTS : 0);
299 if (ioctl(this->file_descriptor_, TIOCMSET, &bits) == -1) {
300 this->update_error_(strerror(errno));
301 return false;
302 }
303 return true;
304}
305
306bool HostUartComponent::get_dtr() const { return (this->get_modem_bits_() & TIOCM_DTR) != 0; }
307
308bool HostUartComponent::get_rts() const { return (this->get_modem_bits_() & TIOCM_RTS) != 0; }
309
311 int bits = 0;
312 if (this->file_descriptor_ != -1) {
313 ioctl(this->file_descriptor_, TIOCMGET, &bits);
314 }
315 return bits;
316}
317
318void HostUartComponent::update_error_(const std::string &error) {
319 if (this->first_error_.empty()) {
320 this->first_error_ = error;
321 }
322 ESP_LOGE(TAG, "Port error: %s", error.c_str());
323}
324
325} // namespace esphome::uart
326#endif // USE_HOST
void mark_failed()
Mark this component as failed.
bool read_array(uint8_t *data, size_t len) override
bool set_modem_control(bool dtr, bool rts)
Drive DTR/RTS. Returns false when the port is closed or the ioctl fails.
void write_array(const uint8_t *data, size_t len) override
void update_error_(const std::string &error)
bool peek_byte(uint8_t *data) override
bool check_read_timeout_(size_t len=1)
CallbackManager< void(UARTDirection, uint8_t)> debug_callback_
uint8_t options
ESPHOME_LOG_TAG(TAG, "uart.binary_sensor")
UARTFlushResult
Result of a flush() call.
@ 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.
const void size_t len
Definition hal.h:68
int written
Definition helpers.h:1262