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