ESPHome  2024.11.0
uart_component_host.cpp
Go to the documentation of this file.
1 #ifdef USE_HOST
2 #include "uart_component_host.h"
4 #include "esphome/core/defines.h"
5 #include "esphome/core/helpers.h"
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 
24 namespace {
25 
26 speed_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 
99 namespace esphome {
100 namespace uart {
101 
102 static 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, " Port status: opened");
192  ESP_LOGCONFIG(TAG, " Baud Rate: %d", this->baud_rate_);
193  ESP_LOGCONFIG(TAG, " Data Bits: %d", this->data_bits_);
194  ESP_LOGCONFIG(TAG, " Parity: %s",
195  this->parity_ == UART_CONFIG_PARITY_NONE ? "None"
196  : this->parity_ == UART_CONFIG_PARITY_EVEN ? "Even"
197  : "Odd");
198  ESP_LOGCONFIG(TAG, " Stop Bits: %d", this->stop_bits_);
199  this->check_logger_conflict();
200 }
201 
202 void HostUartComponent::write_array(const uint8_t *data, size_t len) {
203  if (this->file_descriptor_ == -1) {
204  return;
205  }
206  size_t written = ::write(this->file_descriptor_, data, len);
207  if (written != len) {
208  this->update_error_(strerror(errno));
209  return;
210  }
211 #ifdef USE_UART_DEBUGGER
212  for (size_t i = 0; i < len; i++) {
213  this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
214  }
215 #endif
216  return;
217 }
218 
219 bool HostUartComponent::peek_byte(uint8_t *data) {
220  if (this->file_descriptor_ == -1) {
221  return false;
222  }
223  if (!this->has_peek_) {
224  if (!this->check_read_timeout_()) {
225  return false;
226  }
227  if (::read(this->file_descriptor_, &this->peek_byte_, 1) != 1) {
228  this->update_error_(strerror(errno));
229  return false;
230  }
231  this->has_peek_ = true;
232  }
233  *data = this->peek_byte_;
234  return true;
235 }
236 
237 bool HostUartComponent::read_array(uint8_t *data, size_t len) {
238  if ((this->file_descriptor_ == -1) || (len == 0)) {
239  return false;
240  }
241  if (!this->check_read_timeout_(len))
242  return false;
243  uint8_t *data_ptr = data;
244  size_t length_to_read = len;
245  if (this->has_peek_) {
246  length_to_read--;
247  *data_ptr = this->peek_byte_;
248  data_ptr++;
249  this->has_peek_ = false;
250  }
251  if (length_to_read > 0) {
252  int sz = ::read(this->file_descriptor_, data_ptr, length_to_read);
253  if (sz == -1) {
254  this->update_error_(strerror(errno));
255  return false;
256  }
257  }
258 #ifdef USE_UART_DEBUGGER
259  for (size_t i = 0; i < len; i++) {
260  this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
261  }
262 #endif
263  return true;
264 }
265 
267  if (this->file_descriptor_ == -1) {
268  return 0;
269  }
270  int available;
271  int res = ioctl(this->file_descriptor_, FIONREAD, &available);
272  if (res == -1) {
273  this->update_error_(strerror(errno));
274  return 0;
275  }
276  if (this->has_peek_)
277  available++;
278  return available;
279 };
280 
282  if (this->file_descriptor_ == -1) {
283  return;
284  }
285  tcflush(this->file_descriptor_, TCIOFLUSH);
286  ESP_LOGV(TAG, " Flushing...");
287 }
288 
289 void HostUartComponent::update_error_(const std::string &error) {
290  if (this->first_error_.empty()) {
291  this->first_error_ = error;
292  }
293  ESP_LOGE(TAG, "Port error: %s", error.c_str());
294 }
295 
296 } // namespace uart
297 } // namespace esphome
298 
299 #endif // USE_HOST
const char *const TAG
Definition: spi.cpp:8
void write_array(const uint8_t *data, size_t len) override
void update_error_(const std::string &error)
std::string size_t len
Definition: helpers.h:293
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:118
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
bool read_array(uint8_t *data, size_t len) override
bool check_read_timeout_(size_t len=1)
CallbackManager< void(UARTDirection, uint8_t)> debug_callback_
bool peek_byte(uint8_t *data) override