ESPHome 2026.1.0-dev
Loading...
Searching...
No Matches
uart_component_esp8266.cpp
Go to the documentation of this file.
1#ifdef USE_ESP8266
6#include "esphome/core/log.h"
7
8#ifdef USE_LOGGER
10#endif
11
12namespace esphome::uart {
13
14static const char *const TAG = "uart.arduino_esp8266";
15bool ESP8266UartComponent::serial0_in_use = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
16
18 uint32_t config = 0;
19
20 if (this->parity_ == UART_CONFIG_PARITY_NONE) {
21 config |= UART_PARITY_NONE;
22 } else if (this->parity_ == UART_CONFIG_PARITY_EVEN) {
23 config |= UART_PARITY_EVEN;
24 } else if (this->parity_ == UART_CONFIG_PARITY_ODD) {
25 config |= UART_PARITY_ODD;
26 }
27
28 switch (this->data_bits_) {
29 case 5:
30 config |= UART_NB_BIT_5;
31 break;
32 case 6:
33 config |= UART_NB_BIT_6;
34 break;
35 case 7:
36 config |= UART_NB_BIT_7;
37 break;
38 case 8:
39 config |= UART_NB_BIT_8;
40 break;
41 }
42
43 if (this->stop_bits_ == 1) {
44 config |= UART_NB_STOP_BIT_1;
45 } else {
46 config |= UART_NB_STOP_BIT_2;
47 }
48
49 if (this->tx_pin_ != nullptr && this->tx_pin_->is_inverted())
50 config |= BIT(22);
51 if (this->rx_pin_ != nullptr && this->rx_pin_->is_inverted())
52 config |= BIT(19);
53
54 return config;
55}
56
58 auto setup_pin_if_needed = [](InternalGPIOPin *pin) {
59 if (!pin) {
60 return;
61 }
63 if ((pin->get_flags() & mask) != gpio::Flags::FLAG_NONE) {
64 pin->setup();
65 }
66 };
67
68 setup_pin_if_needed(this->rx_pin_);
69 if (this->rx_pin_ != this->tx_pin_) {
70 setup_pin_if_needed(this->tx_pin_);
71 }
72
73 // Use Arduino HardwareSerial UARTs if all used pins match the ones
74 // preconfigured by the platform. For example if RX disabled but TX pin
75 // is 1 we still want to use Serial.
76 SerialConfig config = static_cast<SerialConfig>(get_config());
77
78 if (!ESP8266UartComponent::serial0_in_use && (tx_pin_ == nullptr || tx_pin_->get_pin() == 1) &&
79 (rx_pin_ == nullptr || rx_pin_->get_pin() == 3)
80#ifdef USE_LOGGER
81 // we will use UART0 if logger isn't using it in swapped mode
82 && (logger::global_logger->get_hw_serial() == nullptr ||
84#endif
85 ) {
86 this->hw_serial_ = &Serial;
87 this->hw_serial_->begin(this->baud_rate_, config);
88 this->hw_serial_->setRxBufferSize(this->rx_buffer_size_);
89 ESP8266UartComponent::serial0_in_use = true;
90 } else if (!ESP8266UartComponent::serial0_in_use && (tx_pin_ == nullptr || tx_pin_->get_pin() == 15) &&
91 (rx_pin_ == nullptr || rx_pin_->get_pin() == 13)
92#ifdef USE_LOGGER
93 // we will use UART0 swapped if logger isn't using it in regular mode
94 && (logger::global_logger->get_hw_serial() == nullptr ||
96#endif
97 ) {
98 this->hw_serial_ = &Serial;
99 this->hw_serial_->begin(this->baud_rate_, config);
100 this->hw_serial_->setRxBufferSize(this->rx_buffer_size_);
101 this->hw_serial_->swap();
102 ESP8266UartComponent::serial0_in_use = true;
103 } else if ((tx_pin_ == nullptr || tx_pin_->get_pin() == 2) && (rx_pin_ == nullptr || rx_pin_->get_pin() == 8)) {
104 this->hw_serial_ = &Serial1;
105 this->hw_serial_->begin(this->baud_rate_, config);
106 this->hw_serial_->setRxBufferSize(this->rx_buffer_size_);
107 } else {
108 this->sw_serial_ = new ESP8266SoftwareSerial(); // NOLINT
109 this->sw_serial_->setup(tx_pin_, rx_pin_, this->baud_rate_, this->stop_bits_, this->data_bits_, this->parity_,
110 this->rx_buffer_size_);
111 }
112}
113
115 ESP_LOGCONFIG(TAG, "Loading UART bus settings");
116 if (this->hw_serial_ != nullptr) {
117 SerialConfig config = static_cast<SerialConfig>(get_config());
118 this->hw_serial_->begin(this->baud_rate_, config);
119 this->hw_serial_->setRxBufferSize(this->rx_buffer_size_);
120 } else {
121 this->sw_serial_->setup(this->tx_pin_, this->rx_pin_, this->baud_rate_, this->stop_bits_, this->data_bits_,
122 this->parity_, this->rx_buffer_size_);
123 }
124 if (dump_config) {
125 ESP_LOGCONFIG(TAG, "UART bus was reloaded.");
126 this->dump_config();
127 }
128}
129
131 ESP_LOGCONFIG(TAG, "UART Bus:");
132 LOG_PIN(" TX Pin: ", this->tx_pin_);
133 LOG_PIN(" RX Pin: ", this->rx_pin_);
134 if (this->rx_pin_ != nullptr) {
135 ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); // NOLINT
136 }
137 ESP_LOGCONFIG(TAG,
138 " Baud Rate: %u baud\n"
139 " Data Bits: %u\n"
140 " Parity: %s\n"
141 " Stop bits: %u",
142 this->baud_rate_, this->data_bits_, LOG_STR_ARG(parity_to_str(this->parity_)), this->stop_bits_);
143 if (this->hw_serial_ != nullptr) {
144 ESP_LOGCONFIG(TAG, " Using hardware serial interface.");
145 } else {
146 ESP_LOGCONFIG(TAG, " Using software serial");
147 }
148 this->check_logger_conflict();
149}
150
152#ifdef USE_LOGGER
153 if (this->hw_serial_ == nullptr || logger::global_logger->get_baud_rate() == 0) {
154 return;
155 }
156
157 if (this->hw_serial_ == logger::global_logger->get_hw_serial()) {
158 ESP_LOGW(TAG, " You're using the same serial port for logging and the UART component. Please "
159 "disable logging over the serial port by setting logger->baud_rate to 0.");
160 }
161#endif
162}
163
164void ESP8266UartComponent::write_array(const uint8_t *data, size_t len) {
165 if (this->hw_serial_ != nullptr) {
166 this->hw_serial_->write(data, len);
167 } else {
168 for (size_t i = 0; i < len; i++)
169 this->sw_serial_->write_byte(data[i]);
170 }
171#ifdef USE_UART_DEBUGGER
172 for (size_t i = 0; i < len; i++) {
173 this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
174 }
175#endif
176}
178 if (!this->check_read_timeout_())
179 return false;
180 if (this->hw_serial_ != nullptr) {
181 *data = this->hw_serial_->peek();
182 } else {
183 *data = this->sw_serial_->peek_byte();
184 }
185 return true;
186}
187bool ESP8266UartComponent::read_array(uint8_t *data, size_t len) {
188 if (!this->check_read_timeout_(len))
189 return false;
190 if (this->hw_serial_ != nullptr) {
191 this->hw_serial_->readBytes(data, len);
192 } else {
193 for (size_t i = 0; i < len; i++)
194 data[i] = this->sw_serial_->read_byte();
195 }
196#ifdef USE_UART_DEBUGGER
197 for (size_t i = 0; i < len; i++) {
198 this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
199 }
200#endif
201 return true;
202}
204 if (this->hw_serial_ != nullptr) {
205 return this->hw_serial_->available();
206 } else {
207 return this->sw_serial_->available();
208 }
209}
211 ESP_LOGVV(TAG, " Flushing");
212 if (this->hw_serial_ != nullptr) {
213 this->hw_serial_->flush();
214 } else {
215 this->sw_serial_->flush();
216 }
217}
218void ESP8266SoftwareSerial::setup(InternalGPIOPin *tx_pin, InternalGPIOPin *rx_pin, uint32_t baud_rate,
219 uint8_t stop_bits, uint32_t data_bits, UARTParityOptions parity,
220 size_t rx_buffer_size) {
221 this->bit_time_ = F_CPU / baud_rate;
222 this->rx_buffer_size_ = rx_buffer_size;
223 this->stop_bits_ = stop_bits;
224 this->data_bits_ = data_bits;
225 this->parity_ = parity;
226 if (tx_pin != nullptr) {
227 gpio_tx_pin_ = tx_pin;
231 }
232 if (rx_pin != nullptr) {
233 gpio_rx_pin_ = rx_pin;
236 rx_buffer_ = new uint8_t[this->rx_buffer_size_]; // NOLINT
238 }
239}
241 uint32_t wait = arg->bit_time_ + arg->bit_time_ / 3 - 500;
242 const uint32_t start = arch_get_cpu_cycle_count();
243 uint8_t rec = 0;
244 // Manually unroll the loop
245 for (int i = 0; i < arg->data_bits_; i++)
246 rec |= arg->read_bit_(&wait, start) << i;
247
248 /* If parity is enabled, just read it and ignore it. */
249 /* TODO: Should we check parity? Or is it too slow for nothing added..*/
251 arg->read_bit_(&wait, start);
252
253 // Stop bit
254 arg->wait_(&wait, start);
255 if (arg->stop_bits_ == 2)
256 arg->wait_(&wait, start);
257
258 arg->rx_buffer_[arg->rx_in_pos_] = rec;
259 arg->rx_in_pos_ = (arg->rx_in_pos_ + 1) % arg->rx_buffer_size_;
260 // Clear RX pin so that the interrupt doesn't re-trigger right away again.
262}
263void IRAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) {
264 if (this->gpio_tx_pin_ == nullptr) {
265 ESP_LOGE(TAG, "UART doesn't have TX pins set!");
266 return;
267 }
268 bool parity_bit = false;
269 bool need_parity_bit = true;
270 if (this->parity_ == UART_CONFIG_PARITY_EVEN) {
271 parity_bit = false;
272 } else if (this->parity_ == UART_CONFIG_PARITY_ODD) {
273 parity_bit = true;
274 } else {
275 need_parity_bit = false;
276 }
277
278 {
279 InterruptLock lock;
280 uint32_t wait = this->bit_time_;
281 const uint32_t start = arch_get_cpu_cycle_count();
282 // Start bit
283 this->write_bit_(false, &wait, start);
284 for (int i = 0; i < this->data_bits_; i++) {
285 bool bit = data & (1 << i);
286 this->write_bit_(bit, &wait, start);
287 if (need_parity_bit)
288 parity_bit ^= bit;
289 }
290 if (need_parity_bit)
291 this->write_bit_(parity_bit, &wait, start);
292 // Stop bit
293 this->write_bit_(true, &wait, start);
294 if (this->stop_bits_ == 2)
295 this->wait_(&wait, start);
296 }
297}
298void IRAM_ATTR ESP8266SoftwareSerial::wait_(uint32_t *wait, const uint32_t &start) {
299 while (arch_get_cpu_cycle_count() - start < *wait)
300 ;
301 *wait += this->bit_time_;
302}
303bool IRAM_ATTR ESP8266SoftwareSerial::read_bit_(uint32_t *wait, const uint32_t &start) {
304 this->wait_(wait, start);
305 return this->rx_pin_.digital_read();
306}
307void IRAM_ATTR ESP8266SoftwareSerial::write_bit_(bool bit, uint32_t *wait, const uint32_t &start) {
308 this->tx_pin_.digital_write(bit);
309 this->wait_(wait, start);
310}
312 if (this->rx_in_pos_ == this->rx_out_pos_)
313 return 0;
314 uint8_t data = this->rx_buffer_[this->rx_out_pos_];
315 this->rx_out_pos_ = (this->rx_out_pos_ + 1) % this->rx_buffer_size_;
316 return data;
317}
319 if (this->rx_in_pos_ == this->rx_out_pos_)
320 return 0;
321 return this->rx_buffer_[this->rx_out_pos_];
322}
324 // Flush is a NO-OP with software serial, all bytes are written immediately.
325}
327 int avail = int(this->rx_in_pos_) - int(this->rx_out_pos_);
328 if (avail < 0)
329 return avail + this->rx_buffer_size_;
330 return avail;
331}
332
333} // namespace esphome::uart
334#endif // USE_ESP8266
virtual void setup()=0
void digital_write(bool value)
Definition gpio.cpp:150
virtual uint8_t get_pin() const =0
void attach_interrupt(void(*func)(T *), T *arg, gpio::InterruptType type) const
Definition gpio.h:88
virtual bool is_inverted() const =0
virtual ISRInternalGPIOPin to_isr() const =0
Helper class to disable interrupts.
Definition helpers.h:1178
Stream * get_hw_serial() const
Definition logger.h:178
UARTSelection get_uart() const
Get the UART used by the logger.
Definition logger.cpp:233
void wait_(uint32_t *wait, const uint32_t &start)
void setup(InternalGPIOPin *tx_pin, InternalGPIOPin *rx_pin, uint32_t baud_rate, uint8_t stop_bits, uint32_t data_bits, UARTParityOptions parity, size_t rx_buffer_size)
void write_bit_(bool bit, uint32_t *wait, const uint32_t &start)
bool read_bit_(uint32_t *wait, const uint32_t &start)
static void gpio_intr(ESP8266SoftwareSerial *arg)
bool read_array(uint8_t *data, size_t len) override
void write_array(const uint8_t *data, size_t len) override
bool check_read_timeout_(size_t len=1)
CallbackManager< void(UARTDirection, uint8_t)> debug_callback_
@ INTERRUPT_FALLING_EDGE
Definition gpio.h:42
@ FLAG_OPEN_DRAIN
Definition gpio.h:20
@ FLAG_NONE
Definition gpio.h:17
@ FLAG_PULLUP
Definition gpio.h:21
@ FLAG_PULLDOWN
Definition gpio.h:22
@ UART_SELECTION_UART0_SWAP
Definition logger.h:143
@ UART_SELECTION_UART0
Definition logger.h:128
Logger * global_logger
Definition logger.cpp:297
const char *const TAG
Definition spi.cpp:7
const LogString * parity_to_str(UARTParityOptions parity)
Definition uart.cpp:32
uint32_t arch_get_cpu_cycle_count()
Definition core.cpp:50
std::string size_t len
Definition helpers.h:533