ESPHome 2025.12.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 {
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 if (this->rx_pin_) {
60 this->rx_pin_->setup();
61 }
62 if (this->tx_pin_ && this->rx_pin_ != this->tx_pin_) {
63 this->tx_pin_->setup();
64 }
65
66 // Use Arduino HardwareSerial UARTs if all used pins match the ones
67 // preconfigured by the platform. For example if RX disabled but TX pin
68 // is 1 we still want to use Serial.
69 SerialConfig config = static_cast<SerialConfig>(get_config());
70
71 if (!ESP8266UartComponent::serial0_in_use && (tx_pin_ == nullptr || tx_pin_->get_pin() == 1) &&
72 (rx_pin_ == nullptr || rx_pin_->get_pin() == 3)
73#ifdef USE_LOGGER
74 // we will use UART0 if logger isn't using it in swapped mode
75 && (logger::global_logger->get_hw_serial() == nullptr ||
77#endif
78 ) {
79 this->hw_serial_ = &Serial;
80 this->hw_serial_->begin(this->baud_rate_, config);
81 this->hw_serial_->setRxBufferSize(this->rx_buffer_size_);
82 ESP8266UartComponent::serial0_in_use = true;
83 } else if (!ESP8266UartComponent::serial0_in_use && (tx_pin_ == nullptr || tx_pin_->get_pin() == 15) &&
84 (rx_pin_ == nullptr || rx_pin_->get_pin() == 13)
85#ifdef USE_LOGGER
86 // we will use UART0 swapped if logger isn't using it in regular mode
87 && (logger::global_logger->get_hw_serial() == nullptr ||
89#endif
90 ) {
91 this->hw_serial_ = &Serial;
92 this->hw_serial_->begin(this->baud_rate_, config);
93 this->hw_serial_->setRxBufferSize(this->rx_buffer_size_);
94 this->hw_serial_->swap();
95 ESP8266UartComponent::serial0_in_use = true;
96 } else if ((tx_pin_ == nullptr || tx_pin_->get_pin() == 2) && (rx_pin_ == nullptr || rx_pin_->get_pin() == 8)) {
97 this->hw_serial_ = &Serial1;
98 this->hw_serial_->begin(this->baud_rate_, config);
99 this->hw_serial_->setRxBufferSize(this->rx_buffer_size_);
100 } else {
101 this->sw_serial_ = new ESP8266SoftwareSerial(); // NOLINT
102 this->sw_serial_->setup(tx_pin_, rx_pin_, this->baud_rate_, this->stop_bits_, this->data_bits_, this->parity_,
103 this->rx_buffer_size_);
104 }
105}
106
108 ESP_LOGCONFIG(TAG, "Loading UART bus settings");
109 if (this->hw_serial_ != nullptr) {
110 SerialConfig config = static_cast<SerialConfig>(get_config());
111 this->hw_serial_->begin(this->baud_rate_, config);
112 this->hw_serial_->setRxBufferSize(this->rx_buffer_size_);
113 } else {
114 this->sw_serial_->setup(this->tx_pin_, this->rx_pin_, this->baud_rate_, this->stop_bits_, this->data_bits_,
115 this->parity_, this->rx_buffer_size_);
116 }
117 if (dump_config) {
118 ESP_LOGCONFIG(TAG, "UART bus was reloaded.");
119 this->dump_config();
120 }
121}
122
124 ESP_LOGCONFIG(TAG, "UART Bus:");
125 LOG_PIN(" TX Pin: ", this->tx_pin_);
126 LOG_PIN(" RX Pin: ", this->rx_pin_);
127 if (this->rx_pin_ != nullptr) {
128 ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); // NOLINT
129 }
130 ESP_LOGCONFIG(TAG,
131 " Baud Rate: %u baud\n"
132 " Data Bits: %u\n"
133 " Parity: %s\n"
134 " Stop bits: %u",
135 this->baud_rate_, this->data_bits_, LOG_STR_ARG(parity_to_str(this->parity_)), this->stop_bits_);
136 if (this->hw_serial_ != nullptr) {
137 ESP_LOGCONFIG(TAG, " Using hardware serial interface.");
138 } else {
139 ESP_LOGCONFIG(TAG, " Using software serial");
140 }
141 this->check_logger_conflict();
142}
143
145#ifdef USE_LOGGER
146 if (this->hw_serial_ == nullptr || logger::global_logger->get_baud_rate() == 0) {
147 return;
148 }
149
150 if (this->hw_serial_ == logger::global_logger->get_hw_serial()) {
151 ESP_LOGW(TAG, " You're using the same serial port for logging and the UART component. Please "
152 "disable logging over the serial port by setting logger->baud_rate to 0.");
153 }
154#endif
155}
156
157void ESP8266UartComponent::write_array(const uint8_t *data, size_t len) {
158 if (this->hw_serial_ != nullptr) {
159 this->hw_serial_->write(data, len);
160 } else {
161 for (size_t i = 0; i < len; i++)
162 this->sw_serial_->write_byte(data[i]);
163 }
164#ifdef USE_UART_DEBUGGER
165 for (size_t i = 0; i < len; i++) {
166 this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
167 }
168#endif
169}
171 if (!this->check_read_timeout_())
172 return false;
173 if (this->hw_serial_ != nullptr) {
174 *data = this->hw_serial_->peek();
175 } else {
176 *data = this->sw_serial_->peek_byte();
177 }
178 return true;
179}
180bool ESP8266UartComponent::read_array(uint8_t *data, size_t len) {
181 if (!this->check_read_timeout_(len))
182 return false;
183 if (this->hw_serial_ != nullptr) {
184 this->hw_serial_->readBytes(data, len);
185 } else {
186 for (size_t i = 0; i < len; i++)
187 data[i] = this->sw_serial_->read_byte();
188 }
189#ifdef USE_UART_DEBUGGER
190 for (size_t i = 0; i < len; i++) {
191 this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
192 }
193#endif
194 return true;
195}
197 if (this->hw_serial_ != nullptr) {
198 return this->hw_serial_->available();
199 } else {
200 return this->sw_serial_->available();
201 }
202}
204 ESP_LOGVV(TAG, " Flushing");
205 if (this->hw_serial_ != nullptr) {
206 this->hw_serial_->flush();
207 } else {
208 this->sw_serial_->flush();
209 }
210}
211void ESP8266SoftwareSerial::setup(InternalGPIOPin *tx_pin, InternalGPIOPin *rx_pin, uint32_t baud_rate,
212 uint8_t stop_bits, uint32_t data_bits, UARTParityOptions parity,
213 size_t rx_buffer_size) {
214 this->bit_time_ = F_CPU / baud_rate;
215 this->rx_buffer_size_ = rx_buffer_size;
216 this->stop_bits_ = stop_bits;
217 this->data_bits_ = data_bits;
218 this->parity_ = parity;
219 if (tx_pin != nullptr) {
220 gpio_tx_pin_ = tx_pin;
224 }
225 if (rx_pin != nullptr) {
226 gpio_rx_pin_ = rx_pin;
229 rx_buffer_ = new uint8_t[this->rx_buffer_size_]; // NOLINT
231 }
232}
234 uint32_t wait = arg->bit_time_ + arg->bit_time_ / 3 - 500;
235 const uint32_t start = arch_get_cpu_cycle_count();
236 uint8_t rec = 0;
237 // Manually unroll the loop
238 for (int i = 0; i < arg->data_bits_; i++)
239 rec |= arg->read_bit_(&wait, start) << i;
240
241 /* If parity is enabled, just read it and ignore it. */
242 /* TODO: Should we check parity? Or is it too slow for nothing added..*/
244 arg->read_bit_(&wait, start);
245
246 // Stop bit
247 arg->wait_(&wait, start);
248 if (arg->stop_bits_ == 2)
249 arg->wait_(&wait, start);
250
251 arg->rx_buffer_[arg->rx_in_pos_] = rec;
252 arg->rx_in_pos_ = (arg->rx_in_pos_ + 1) % arg->rx_buffer_size_;
253 // Clear RX pin so that the interrupt doesn't re-trigger right away again.
255}
256void IRAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) {
257 if (this->gpio_tx_pin_ == nullptr) {
258 ESP_LOGE(TAG, "UART doesn't have TX pins set!");
259 return;
260 }
261 bool parity_bit = false;
262 bool need_parity_bit = true;
263 if (this->parity_ == UART_CONFIG_PARITY_EVEN) {
264 parity_bit = false;
265 } else if (this->parity_ == UART_CONFIG_PARITY_ODD) {
266 parity_bit = true;
267 } else {
268 need_parity_bit = false;
269 }
270
271 {
272 InterruptLock lock;
273 uint32_t wait = this->bit_time_;
274 const uint32_t start = arch_get_cpu_cycle_count();
275 // Start bit
276 this->write_bit_(false, &wait, start);
277 for (int i = 0; i < this->data_bits_; i++) {
278 bool bit = data & (1 << i);
279 this->write_bit_(bit, &wait, start);
280 if (need_parity_bit)
281 parity_bit ^= bit;
282 }
283 if (need_parity_bit)
284 this->write_bit_(parity_bit, &wait, start);
285 // Stop bit
286 this->write_bit_(true, &wait, start);
287 if (this->stop_bits_ == 2)
288 this->wait_(&wait, start);
289 }
290}
291void IRAM_ATTR ESP8266SoftwareSerial::wait_(uint32_t *wait, const uint32_t &start) {
292 while (arch_get_cpu_cycle_count() - start < *wait)
293 ;
294 *wait += this->bit_time_;
295}
296bool IRAM_ATTR ESP8266SoftwareSerial::read_bit_(uint32_t *wait, const uint32_t &start) {
297 this->wait_(wait, start);
298 return this->rx_pin_.digital_read();
299}
300void IRAM_ATTR ESP8266SoftwareSerial::write_bit_(bool bit, uint32_t *wait, const uint32_t &start) {
301 this->tx_pin_.digital_write(bit);
302 this->wait_(wait, start);
303}
305 if (this->rx_in_pos_ == this->rx_out_pos_)
306 return 0;
307 uint8_t data = this->rx_buffer_[this->rx_out_pos_];
308 this->rx_out_pos_ = (this->rx_out_pos_ + 1) % this->rx_buffer_size_;
309 return data;
310}
312 if (this->rx_in_pos_ == this->rx_out_pos_)
313 return 0;
314 return this->rx_buffer_[this->rx_out_pos_];
315}
317 // Flush is a NO-OP with software serial, all bytes are written immediately.
318}
320 int avail = int(this->rx_in_pos_) - int(this->rx_out_pos_);
321 if (avail < 0)
322 return avail + this->rx_buffer_size_;
323 return avail;
324}
325
326} // namespace uart
327} // namespace esphome
328#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:994
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
@ 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:500