ESPHome 2026.3.0
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 <stdio.h>
13#include <string.h>
14#include <unistd.h>
15#include <fcntl.h>
16#include <errno.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
101static const char *const TAG = "uart.host";
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 };
162 // Set stop bits
163 if (this->stop_bits_ == 2) {
164 options.c_cflag |= CSTOPB;
165 } else {
166 options.c_cflag &= ~CSTOPB;
167 }
168 cfsetispeed(&options, baud);
169 cfsetospeed(&options, baud);
170 tcsetattr(this->file_descriptor_, TCSANOW, &options);
171}
172
174 ESP_LOGCONFIG(TAG, "UART:");
175 ESP_LOGCONFIG(TAG, " Port: %s", this->port_name_.c_str());
176 if (this->file_descriptor_ == -1) {
177 ESP_LOGCONFIG(TAG, " Port status: Not opened");
178 if (!this->first_error_.empty()) {
179 ESP_LOGCONFIG(TAG, " Error: %s", this->first_error_.c_str());
180 }
181 return;
182 }
183 ESP_LOGCONFIG(TAG,
184 " Port status: opened\n"
185 " Baud Rate: %d\n"
186 " Data Bits: %d\n"
187 " Parity: %s\n"
188 " Stop Bits: %d",
189 this->baud_rate_, this->data_bits_,
190 this->parity_ == UART_CONFIG_PARITY_NONE ? "None"
191 : this->parity_ == UART_CONFIG_PARITY_EVEN ? "Even"
192 : "Odd",
193 this->stop_bits_);
194 this->check_logger_conflict();
195}
196
197void HostUartComponent::write_array(const uint8_t *data, size_t len) {
198 if (this->file_descriptor_ == -1) {
199 return;
200 }
201 size_t written = ::write(this->file_descriptor_, data, len);
202 if (written != len) {
203 this->update_error_(strerror(errno));
204 return;
205 }
206#ifdef USE_UART_DEBUGGER
207 for (size_t i = 0; i < len; i++) {
208 this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
209 }
210#endif
211 return;
212}
213
214bool HostUartComponent::peek_byte(uint8_t *data) {
215 if (this->file_descriptor_ == -1) {
216 return false;
217 }
218 if (!this->has_peek_) {
219 if (!this->check_read_timeout_()) {
220 return false;
221 }
222 if (::read(this->file_descriptor_, &this->peek_byte_, 1) != 1) {
223 this->update_error_(strerror(errno));
224 return false;
225 }
226 this->has_peek_ = true;
227 }
228 *data = this->peek_byte_;
229 return true;
230}
231
232bool HostUartComponent::read_array(uint8_t *data, size_t len) {
233 if ((this->file_descriptor_ == -1) || (len == 0)) {
234 return false;
235 }
236 if (!this->check_read_timeout_(len))
237 return false;
238 uint8_t *data_ptr = data;
239 size_t length_to_read = len;
240 if (this->has_peek_) {
241 length_to_read--;
242 *data_ptr = this->peek_byte_;
243 data_ptr++;
244 this->has_peek_ = false;
245 }
246 if (length_to_read > 0) {
247 int sz = ::read(this->file_descriptor_, data_ptr, length_to_read);
248 if (sz == -1) {
249 this->update_error_(strerror(errno));
250 return false;
251 }
252 }
253#ifdef USE_UART_DEBUGGER
254 for (size_t i = 0; i < len; i++) {
255 this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
256 }
257#endif
258 return true;
259}
260
262 if (this->file_descriptor_ == -1) {
263 return 0;
264 }
265 int available;
266 int res = ioctl(this->file_descriptor_, FIONREAD, &available);
267 if (res == -1) {
268 this->update_error_(strerror(errno));
269 return 0;
270 }
271 size_t result = available;
272 if (this->has_peek_)
273 result++;
274 return result;
275};
276
278 if (this->file_descriptor_ == -1) {
280 }
281 tcflush(this->file_descriptor_, TCIOFLUSH);
282 ESP_LOGV(TAG, " Flushing");
284}
285
286void HostUartComponent::update_error_(const std::string &error) {
287 if (this->first_error_.empty()) {
288 this->first_error_ = error;
289 }
290 ESP_LOGE(TAG, "Port error: %s", error.c_str());
291}
292
293} // namespace esphome::uart
294#endif // USE_HOST
void mark_failed()
Mark this component as failed.
bool read_array(uint8_t *data, size_t len) override
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
const char *const TAG
Definition spi.cpp:7
FlushResult
Result of a flush() call.
@ ASSUMED_SUCCESS
Platform cannot report result; success is assumed.
std::string size_t len
Definition helpers.h:892
int written
Definition helpers.h:936