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