ESPHome 2025.12.2
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 options.c_cflag &= ~CRTSCTS;
128 options.c_cflag |= CREAD | CLOCAL;
129 options.c_lflag &= ~ICANON;
130 options.c_lflag &= ~ECHO;
131 options.c_lflag &= ~ECHOE;
132 options.c_lflag &= ~ECHONL;
133 options.c_lflag &= ~ISIG;
134 options.c_iflag &= ~(IXON | IXOFF | IXANY);
135 options.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL);
136 options.c_oflag &= ~OPOST;
137 options.c_oflag &= ~ONLCR;
138 // Set data bits
139 options.c_cflag &= ~CSIZE; // Mask the character size bits
140 switch (this->data_bits_) {
141 case 5:
142 options.c_cflag |= CS5;
143 break;
144 case 6:
145 options.c_cflag |= CS6;
146 break;
147 case 7:
148 options.c_cflag |= CS7;
149 break;
150 case 8:
151 default:
152 options.c_cflag |= CS8;
153 break;
154 }
155 // Set parity
156 switch (this->parity_) {
158 options.c_cflag &= ~PARENB;
159 break;
161 options.c_cflag |= PARENB;
162 options.c_cflag &= ~PARODD;
163 break;
165 options.c_cflag |= PARENB;
166 options.c_cflag |= PARODD;
167 break;
168 };
169 // Set stop bits
170 if (this->stop_bits_ == 2) {
171 options.c_cflag |= CSTOPB;
172 } else {
173 options.c_cflag &= ~CSTOPB;
174 }
175 cfsetispeed(&options, baud);
176 cfsetospeed(&options, baud);
177 tcsetattr(this->file_descriptor_, TCSANOW, &options);
178}
179
181 ESP_LOGCONFIG(TAG, "UART:");
182 ESP_LOGCONFIG(TAG, " Port: %s", this->port_name_.c_str());
183 if (this->file_descriptor_ == -1) {
184 ESP_LOGCONFIG(TAG, " Port status: Not opened");
185 if (!this->first_error_.empty()) {
186 ESP_LOGCONFIG(TAG, " Error: %s", this->first_error_.c_str());
187 }
188 return;
189 }
190 ESP_LOGCONFIG(TAG,
191 " Port status: opened\n"
192 " Baud Rate: %d\n"
193 " Data Bits: %d\n"
194 " Parity: %s\n"
195 " Stop Bits: %d",
196 this->baud_rate_, this->data_bits_,
197 this->parity_ == UART_CONFIG_PARITY_NONE ? "None"
198 : this->parity_ == UART_CONFIG_PARITY_EVEN ? "Even"
199 : "Odd",
200 this->stop_bits_);
201 this->check_logger_conflict();
202}
203
204void HostUartComponent::write_array(const uint8_t *data, size_t len) {
205 if (this->file_descriptor_ == -1) {
206 return;
207 }
208 size_t written = ::write(this->file_descriptor_, data, len);
209 if (written != len) {
210 this->update_error_(strerror(errno));
211 return;
212 }
213#ifdef USE_UART_DEBUGGER
214 for (size_t i = 0; i < len; i++) {
215 this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
216 }
217#endif
218 return;
219}
220
221bool HostUartComponent::peek_byte(uint8_t *data) {
222 if (this->file_descriptor_ == -1) {
223 return false;
224 }
225 if (!this->has_peek_) {
226 if (!this->check_read_timeout_()) {
227 return false;
228 }
229 if (::read(this->file_descriptor_, &this->peek_byte_, 1) != 1) {
230 this->update_error_(strerror(errno));
231 return false;
232 }
233 this->has_peek_ = true;
234 }
235 *data = this->peek_byte_;
236 return true;
237}
238
239bool HostUartComponent::read_array(uint8_t *data, size_t len) {
240 if ((this->file_descriptor_ == -1) || (len == 0)) {
241 return false;
242 }
243 if (!this->check_read_timeout_(len))
244 return false;
245 uint8_t *data_ptr = data;
246 size_t length_to_read = len;
247 if (this->has_peek_) {
248 length_to_read--;
249 *data_ptr = this->peek_byte_;
250 data_ptr++;
251 this->has_peek_ = false;
252 }
253 if (length_to_read > 0) {
254 int sz = ::read(this->file_descriptor_, data_ptr, length_to_read);
255 if (sz == -1) {
256 this->update_error_(strerror(errno));
257 return false;
258 }
259 }
260#ifdef USE_UART_DEBUGGER
261 for (size_t i = 0; i < len; i++) {
262 this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
263 }
264#endif
265 return true;
266}
267
269 if (this->file_descriptor_ == -1) {
270 return 0;
271 }
272 int available;
273 int res = ioctl(this->file_descriptor_, FIONREAD, &available);
274 if (res == -1) {
275 this->update_error_(strerror(errno));
276 return 0;
277 }
278 if (this->has_peek_)
279 available++;
280 return available;
281};
282
284 if (this->file_descriptor_ == -1) {
285 return;
286 }
287 tcflush(this->file_descriptor_, TCIOFLUSH);
288 ESP_LOGV(TAG, " Flushing");
289}
290
291void HostUartComponent::update_error_(const std::string &error) {
292 if (this->first_error_.empty()) {
293 this->first_error_ = error;
294 }
295 ESP_LOGE(TAG, "Port error: %s", error.c_str());
296}
297
298} // namespace esphome::uart
299#endif // USE_HOST
virtual 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:8
std::string size_t len
Definition helpers.h:503