Serial (UART)
About
The Serial (UART - Universal Asynchronous Receiver-Transmitter) peripheral provides asynchronous serial communication, allowing the ESP32 to communicate with other devices such as computers, sensors, displays, and other microcontrollers.
UART is a simple, two-wire communication protocol that uses a TX (transmit) and RX (receive) line for full-duplex communication. The ESP32 Arduino implementation provides a HardwareSerial class that is compatible with the standard Arduino Serial API, with additional features for advanced use cases.
Key Features:
Full-duplex communication: Simultaneous transmission and reception.
Configurable baud rates: From 300 to 5,000,000+ baud.
Multiple data formats: Configurable data bits, parity, and stop bits.
Hardware flow control: Support for RTS/CTS signals.
RS485 support: Half-duplex RS485 communication mode.
Low-power UART: Some SoCs support LP (Low-Power) UART for ultra-low power applications.
Baud rate detection: Automatic baud rate detection (ESP32, ESP32-S2 only).
Event callbacks: Receive and error event callbacks.
Configurable buffers: Adjustable RX and TX buffer sizes.
RX internal pull: Automatic pull-up or pull-down on the RX pad (see RX internal pull).
One-wire UART: Automatic open-drain single-GPIO RX+TX when RX and TX resolve to the same pin (see one-wire UART).
Note
In case that both pins, RX and TX are detached from UART, the driver will be stopped.
Detaching may occur when, for instance, starting another peripheral using RX and TX pins, such as Wire.begin(RX0, TX0).
In one-wire mode, RX and TX share a single GPIO registered as UART_RX_TX in the Peripheral Manager;
an external deinit of that pin (for example pinMode() or another peripheral claiming the GPIO) terminates the UART driver.
UART Availability
The number of UART peripherals available varies by ESP32 SoC:
ESP32 SoC |
HP UARTs |
LP UARTs |
|---|---|---|
ESP32 |
3 |
0 |
ESP32-S2 |
2 |
0 |
ESP32-S3 |
3 |
0 |
ESP32-C3 |
2 |
0 |
ESP32-C5 |
2 |
1 |
ESP32-C6 |
2 |
1 |
ESP32-H2 |
2 |
0 |
ESP32-P4 |
5 |
1 |
Note: * HP (High-Performance) UARTs are the standard UART peripherals * LP (Low-Power) UARTs are available on some SoCs for ultra-low power applications * UART0 is typically used for programming and debug output (Serial Monitor) * Additional UARTs (Serial1, Serial2, etc.) are available for general-purpose communication, including LP UARTs when available. The ESP32 Arduino Core automatically creates HardwareSerial objects for all available UARTs:
Serial0(orSerial) - UART0 (HP UART, typically used for programming and debug output)
Serial1,Serial2, etc. - Additional HP UARTs (numbered sequentially)Additional Serial objects - LP UARTs, when available (numbered after HP UARTs)
Example: The ESP32-C6 has 2 HP UARTs and 1 LP UART. The Arduino Core creates
Serial0andSerial1(HP UARTs) plusSerial2(LP UART) HardwareSerial objects.Important: On ESP32-C5, ESP32-C6, and ESP32-C61, LP UARTs use fixed GPIO pins for RX, TX, CTS, and RTS;
setPins()cannot change them. On ESP32-P4, the LP UART supports the GPIO matrix and follows the same pin rules as HP UARTs (including automatic one-wire when RX equals TX).
Arduino-ESP32 Serial API
begin
Initializes the Serial port with the specified baud rate and configuration.
void begin(unsigned long baud, uint32_t config = SERIAL_8N1, int8_t rxPin = -1, int8_t txPin = -1, bool invert = false, unsigned long timeout_ms = 20000UL, uint8_t rxfifo_full_thrhd = 120);
baud- Baud rate (bits per second). Common values: 9600, 115200, 230400, etc.Special value:
0enables baud rate detection (ESP32, ESP32-S2 only). The function will attempt to detect the baud rate for up totimeout_msmilliseconds. See the Baud Rate Detection Example for usage details.config- Serial configuration (data bits, parity, stop bits):SERIAL_8N1- 8 data bits, no parity, 1 stop bit (default)SERIAL_8N2- 8 data bits, no parity, 2 stop bitsSERIAL_8E1- 8 data bits, even parity, 1 stop bitSERIAL_8E2- 8 data bits, even parity, 2 stop bitsSERIAL_8O1- 8 data bits, odd parity, 1 stop bitSERIAL_8O2- 8 data bits, odd parity, 2 stop bitsSERIAL_7N1,SERIAL_7N2,SERIAL_7E1,SERIAL_7E2,SERIAL_7O1,SERIAL_7O2- 7 data bits variantsSERIAL_6N1,SERIAL_6N2,SERIAL_6E1,SERIAL_6E2,SERIAL_6O1,SERIAL_6O2- 6 data bits variantsSERIAL_5N1,SERIAL_5N2,SERIAL_5E1,SERIAL_5E2,SERIAL_5O1,SERIAL_5O2- 5 data bits variants
rxPin- RX pin number. Use-1to keep the default pin or current pin assignment.txPin- TX pin number. Use-1to keep the default pin or current pin assignment.invert- Iftrue, inverts the RX and TX signal polarity. Also sets the RX internal pull to pull-down when enableRxInternalPull() is enabled (see Signal Inversion and RX Internal Pull).timeout_ms- Timeout in milliseconds for baud rate detection (whenbaud = 0). Default: 20000 ms (20 seconds).rxfifo_full_thrhd- RX FIFO full threshold (1-127 bytes). When the FIFO reaches this threshold, data is copied to the RX buffer. Default: 120 bytes.
Example:
// Basic initialization with default pins
Serial.begin(115200);
// Initialize with custom pins
Serial1.begin(9600, SERIAL_8N1, 4, 5);
// Initialize with baud rate detection (ESP32, ESP32-S2 only)
Serial.begin(0, SERIAL_8N1, -1, -1, false, 20000);
end
Stops the Serial port and releases all resources.
void end(void);
This function disables the UART peripheral and frees all associated resources.
available
Returns the number of bytes available for reading from the Serial port.
int available(void);
Returns: The number of bytes available in the RX buffer, or 0 if no data is available.
Example:
if (Serial.available() > 0) {
char data = Serial.read();
}
availableForWrite
Returns the number of bytes that can be written to the Serial port without blocking.
int availableForWrite(void);
Returns: The number of bytes that can be written to the TX buffer without blocking.
read
Reads a single byte from the Serial port.
int read(void);
Returns: The byte read (0-255), or -1 if no data is available.
Example:
int data = Serial.read();
if (data != -1) {
Serial.printf("Received: %c\n", (char)data);
}
read (buffer)
Reads multiple bytes from the Serial port into a buffer.
size_t read(uint8_t *buffer, size_t size);
size_t read(char *buffer, size_t size);
buffer- Pointer to the buffer where data will be storedsize- Maximum number of bytes to read
Returns: The number of bytes actually read.
Example:
uint8_t buffer[64];
size_t bytesRead = Serial.read(buffer, sizeof(buffer));
Serial.printf("Read %lu bytes\n", (unsigned long)bytesRead);
readBytes
Reads multiple bytes from the Serial port, blocking until the specified number of bytes is received or timeout occurs.
size_t readBytes(uint8_t *buffer, size_t length);
size_t readBytes(char *buffer, size_t length);
buffer- Pointer to the buffer where data will be storedlength- Number of bytes to read
Returns: The number of bytes actually read (may be less than length if timeout occurs).
Note: This function overrides Stream::readBytes() for better performance using ESP-IDF functions.
write
Writes data to the Serial port.
size_t write(uint8_t);
size_t write(const uint8_t *buffer, size_t size);
size_t write(const char *buffer, size_t size);
size_t write(const char *s);
size_t write(unsigned long n);
size_t write(long n);
size_t write(unsigned int n);
size_t write(int n);
Single byte:
write(uint8_t)- Writes a single byteBuffer:
write(buffer, size)- Writes multiple bytes from a bufferString:
write(const char *s)- Writes a null-terminated stringNumber:
write(n)- Writes a number as a single byte
Returns: The number of bytes written.
Example:
Serial.write('A');
Serial.write("Hello");
Serial.write(buffer, 10);
Serial.write(65); // Writes byte value 65
peek
Returns the next byte in the RX buffer without removing it.
int peek(void);
Returns: The next byte (0-255), or -1 if no data is available.
Note: Unlike read(), peek() does not remove the byte from the buffer.
flush
Waits for all data in the TX buffer to be transmitted.
void flush(void);
void flush(bool txOnly);
txOnly- Iftrue, only flushes the TX buffer. Iffalse(default), also clears the RX buffer.
Note: This function blocks until all data in the TX buffer has been sent.
baudRate
Returns the current baud rate of the Serial port.
uint32_t baudRate(void);
Returns: The configured baud rate in bits per second.
Note: When using baud rate detection (begin(0)), this function returns the detected baud rate, which may be slightly rounded (e.g., 115200 may return 115201).
updateBaudRate
Updates the baud rate of an already initialized Serial port.
void updateBaudRate(unsigned long baud);
baud- New baud rate
Note: This function can be called after begin() to change the baud rate without reinitializing the port.
setPins
Sets or changes the RX, TX, CTS, and RTS pins for the Serial port.
bool setPins(int8_t rxPin, int8_t txPin, int8_t ctsPin = -1, int8_t rtsPin = -1);
rxPin- RX pin number. Use-1to keep current pin.txPin- TX pin number. Use-1to keep current pin.ctsPin- CTS (Clear To Send) pin for hardware flow control. Use-1to keep current pin or disable.rtsPin- RTS (Request To Send) pin for hardware flow control. Use-1to keep current pin or disable.
Returns: true if pins are set successfully, false otherwise.
Note: This function can be called before or after begin(). When pins are changed, the previous pins are automatically detached.
When the effective RX and TX pins resolve to the same GPIO (explicit setPins(p, p) / begin(..., p, p), or -1 keep-current that makes them equal), the driver automatically enters open-drain one-wire mode and registers a shared UART_RX_TX pin. An external pull-up is required. The UART must be in UART_MODE_UART. RS485 and IrDA reject same-pin configuration, and setMode() rejects switching away from UART_MODE_UART while one-wire is active. See one-wire UART and Mode and Pin Compatibility.
enableRxInternalPull
Enables or disables the internal pull resistor on the RX GPIO. Must be called before begin() to take effect.
bool enableRxInternalPull(bool enable = true);
enable- Iftrue(default), the core applies an internal pull on the RX pad after attach. Iffalse, the RX pad is left floating (no internal pull).
Returns: true if the setting was applied, false if the UART is already running.
When enabled, pull direction follows RX signal inversion (see Signal Inversion and RX Internal Pull):
Normal RX (not inverted): pull-up (idle HIGH)
RX inverted: pull-down (idle LOW)
Internal pull is not applied in one-wire mode (same GPIO for RX and TX).
Related Example:
RxPull_Demo — Floating RX vs default internal pull, and inverted-RX pull direction on split pins.
One-wire UART
One-wire (single-GPIO RX+TX) is enabled automatically when the effective RX and TX pins are the same GPIO. The HAL configures that shared pad as open-drain. No separate opt-in API is required.
Examples:
Serial1.setPins(8, 8);orSerial1.begin(115200, SERIAL_8N1, 8, 8);With RX already
8and TX9,Serial1.setPins(-1, 8);resolves to RX=TX=``8`` and enters one-wire
Warning
Open-drain one-wire requires an external pull-up to 3.3 V using a suitable conventional pull-up resistor. TX actively drives LOW and releases HIGH, so without the resistor the line has no valid idle HIGH. Use a half-duplex protocol and never let multiple peers transmit simultaneously. One-wire mode is not a substitute for RS485 half-duplex (which requires separate TX, RX, and RTS pins to an external transceiver).
Why one-wire UART can receive ghost data
In one-wire mode, RX is always listening on the same open-drain pin that TX uses. The external pull-up establishes idle HIGH whenever all transmitters release the bus. Any brief LOW or noise looks like a UART start bit, so the receiver can produce framing errors, BREAK events, or “ghost” bytes.
Main causes:
Missing external pull-up — open-drain TX releases HIGH and cannot establish idle HIGH by itself.
Startup or mux transients — attaching the shared pin can briefly look like a LOW start bit.
Self-echo — TX is also seen on RX. That is expected for intentional transmission; noise on TX looks the same.
Bus noise or collisions — wiring noise or simultaneous transmitters corrupt frames.
How to prevent it
Add an external pull-up to 3.3 V on the one-wire pad or shared bus. This holds idle HIGH whenever all open-drain outputs release the line.
Drain RX after
begin()orsetPins()usingwhile (Serial1.available()) { Serial1.read(); }to clear glitches generated during pin attachment.Match and discard the expected local self-echo after each write before processing peer data.
Ignore data until the first valid frame, or require a known preamble, to filter startup noise before real protocol traffic.
Use half-duplex only: never let multiple peers transmit simultaneously.
Use suitable wiring and pull-up value to maintain clean signal edges.
The weak internal pull is intentionally disabled in normal one-wire operation. A real one-wire bus requires an external pull-up. Open-drain does not impose a special UART baud-rate restriction.
Related Example:
OneWire_UART_Demo — Single-board one-wire self-echo on one GPIO (RX == TX), using the UART internal loopback so it runs with no external wiring, plus a half-duplex USB bridge.
OneWire_UART_Two_Boards — Open-drain PING/PONG communication between two ESP32-family boards over one signal wire plus common GND. Both peers remain in same-pin RX/TX mode, use an external pull-up, and discard local TX self-echo.
Signal Inversion and RX Internal Pull
RX internal pull (when enableRxInternalPull() is enabled) tracks RX signal inversion only:
Event |
RX internal pull (when enabled) |
|---|---|
|
Pull-up Pull-down Pull-down / pull-up Unchanged Disabled (external pull-up) Disabled (floating) |
Pin Configuration Order
Call these before begin() (same pattern as setClockSource() and setRxBufferSize()):
enableRxInternalPull()(optional, default on)setClockSource(),setRxBufferSize(),setTxBufferSize()(optional)setPins()(optional; can also be called afterbegin(); same-pin auto-enables one-wire)begin()
Mode and Pin Compatibility
Mode |
One-wire |
RX internal pull |
|---|---|---|
|
Auto when RX == TX No No No Auto when RX == TX |
Yes (unless one-wire) Yes (split pins) Yes (split pins) Fixed RX pad only Yes (unless one-wire) |
setRxBufferSize
Sets the size of the RX buffer.
size_t setRxBufferSize(size_t new_size);
new_size- New RX buffer size in bytes
Returns: The actual buffer size set, or 0 on error.
Note: This function must be called before begin() to take effect. Default RX buffer size is 256 bytes.
setTxBufferSize
Sets the size of the TX buffer.
size_t setTxBufferSize(size_t new_size);
new_size- New TX buffer size in bytes
Returns: The actual buffer size set, or 0 on error.
Note: This function must be called before begin() to take effect. Default TX buffer size is 0 (no buffering).
setRxTimeout
Sets the RX timeout threshold in UART symbol periods.
bool setRxTimeout(uint8_t symbols_timeout);
symbols_timeout- Timeout threshold in UART symbol periods. Setting0disables timeout-based callbacks.The timeout is calculated based on the current baud rate and serial configuration. For example:
For
SERIAL_8N1(10 bits per symbol), a timeout of 3 symbols at 9600 baud = 3 / (9600 / 10) = 3.125 msMaximum timeout is calculated automatically by ESP-IDF based on the serial configuration
Returns: true if timeout is set successfully, false otherwise.
Note:
* When RX timeout occurs, the onReceive() callback is triggered
* For ESP32 and ESP32-S2, when using REF_TICK clock source (baud rates ≤ 250000), RX timeout is limited to 1 symbol
* To use higher RX timeout values on ESP32/ESP32-S2, set the clock source to APB using setClockSource(UART_CLK_SRC_APB) before begin()
setRxFIFOFull
Sets the RX FIFO full threshold that triggers data transfer from FIFO to RX buffer.
bool setRxFIFOFull(uint8_t fifoBytes);
fifoBytes- Number of bytes (1-127) that will trigger the FIFO full interruptWhen the UART FIFO reaches this threshold, data is copied to the RX buffer and the
onReceive()callback is triggered.
Returns: true if threshold is set successfully, false otherwise.
Note: * Lower values (e.g., 1) provide byte-by-byte reception but consume more CPU time * Higher values (e.g., 120) provide better performance but introduce latency * Default value depends on baud rate: 1 byte for ≤ 115200 baud, 120 bytes for > 115200 baud
onReceive
Sets a callback function that is called when data is received.
void onReceive(OnReceiveCb function, bool onlyOnTimeout = false);
function- Callback function to call when data is received. UseNULLto disable the callback.onlyOnTimeout- Iftrue, callback is only called on RX timeout. Iffalse(default), callback is called on both FIFO full and RX timeout events.
Callback Signature:
typedef std::function<void(void)> OnReceiveCb;
Note:
* When onlyOnTimeout = false, the callback is triggered when FIFO reaches the threshold (set by setRxFIFOFull()) or on RX timeout
* When onlyOnTimeout = true, the callback is only triggered on RX timeout, ensuring all data in a stream is available at once
* Using onlyOnTimeout = true may cause RX overflow if the RX buffer size is too small for the incoming data stream
* The callback is executed in a separate task, allowing non-blocking data processing
Example:
void onReceiveCallback() {
while (Serial1.available()) {
char c = Serial1.read();
Serial.print(c);
}
}
void setup() {
Serial1.begin(115200);
Serial1.onReceive(onReceiveCallback);
}
onReceiveError
Sets a callback function that is called when a UART error occurs.
void onReceiveError(OnReceiveErrorCb function);
function- Callback function to call when an error occurs. UseNULLto disable the callback.
Callback Signature:
typedef std::function<void(hardwareSerial_error_t)> OnReceiveErrorCb;
Error Types:
UART_NO_ERROR- No errorUART_BREAK_ERROR- Break condition detectedUART_BUFFER_FULL_ERROR- RX buffer is fullUART_FIFO_OVF_ERROR- UART FIFO overflowUART_FRAME_ERROR- Frame error (invalid stop bit)UART_PARITY_ERROR- Parity error
Example:
void onErrorCallback(hardwareSerial_error_t error) {
Serial.printf("UART Error: %d\n", error);
}
void setup() {
Serial1.begin(115200);
Serial1.onReceiveError(onErrorCallback);
}
eventQueueReset
Clears all events in the event queue (events that trigger onReceive() and onReceiveError()).
void eventQueueReset(void);
This function can be useful in some use cases where you want to clear pending events.
setHwFlowCtrlMode
Enables or disables hardware flow control using RTS and/or CTS pins.
bool setHwFlowCtrlMode(SerialHwFlowCtrl mode = UART_HW_FLOWCTRL_CTS_RTS, uint8_t threshold = 64);
mode- Hardware flow control mode:UART_HW_FLOWCTRL_DISABLE(0x0) - Disable hardware flow controlUART_HW_FLOWCTRL_RTS(0x1) - Enable RX hardware flow control (RTS)UART_HW_FLOWCTRL_CTS(0x2) - Enable TX hardware flow control (CTS)UART_HW_FLOWCTRL_CTS_RTS(0x3) - Enable full hardware flow control (default)
threshold- Flow control threshold (default: 64, which is half of the FIFO length)
Returns: true if flow control mode is set successfully, false otherwise.
Note: CTS and RTS pins must be set using setPins() before enabling hardware flow control.
setMode
Sets the UART operating mode.
bool setMode(SerialMode mode);
mode- UART mode:UART_MODE_UART(0x00) - Regular UART mode (default)UART_MODE_RS485_HALF_DUPLEX(0x01) - Half-duplex RS485 mode (RTS pin controls transceiver)UART_MODE_IRDA(0x02) - IrDA UART modeUART_MODE_RS485_COLLISION_DETECT(0x03) - RS485 collision detection mode (for testing)UART_MODE_RS485_APP_CTRL(0x04) - Application-controlled RS485 mode (for testing)
Returns: true if mode is set successfully, false otherwise.
Note: For RS485 half-duplex mode, the RTS pin must be configured using setPins() to control the transceiver. RS485 does not support one-wire (same GPIO for RX and TX). See Mode and Pin Compatibility.
setIrdaDirection
Sets the IrDA transmission direction (TX or RX mode). Can only be used after setMode(UART_MODE_IRDA) is called.
bool setIrdaDirection(esp32_uart_irda_direction_t irdaDirection);
irdaDirection- Direction for IrDA mode:ESP32_UART_IRDA_TX- IrDA TX mode (transmit only). The UART will transmit data in IrDA format. RX is disabled.ESP32_UART_IRDA_RX- IrDA RX mode (receive only). The UART will receive data in IrDA format. TX is disabled.
Returns: true if IrDA mode direction is set successfully, false otherwise.
Note
IrDA mode works in exclusive directions: the UART can either transmit or receive, but not both simultaneously.
IrDA requires separate TX and RX GPIO pins; one-wire mode is not supported.
The
setMode(UART_MODE_IRDA)function must be called before usingsetIrdaDirection().Switching between TX and RX modes can be done by calling
setIrdaDirection()with different parameters.The ESP32 UART hardware automatically handles IrDA pulse timing and encoding/decoding.
Hardware requirements: IR LED (950 nm) + resistor for TX, IR photodiode + amplifier for RX. No external SIR transceiver module needed.
Example:
Serial1.begin(9600, SERIAL_8N1, RX1, TX1);
Serial1.setMode(UART_MODE_IRDA); // Enable IrDA mode
// Switch to TX mode
Serial1.setIrdaDirection(ESP32_UART_IRDA_TX);
Serial1.println("Transmit data");
// Switch to RX mode
Serial1.setIrdaDirection(ESP32_UART_IRDA_RX);
while (Serial1.available()) {
char c = Serial1.read();
Serial.print(c);
}
Related Examples:
IrdaMode_DualUART_Demo - Single-board demonstration using two UARTs with internal loopback. Requires ESP32 with 3+ UARTs (ESP32, ESP32-S3, ESP32-P4). No external hardware needed. Ideal for testing IrDA mode functionality.
IrdaMode_TwoBoard_Demo - Two-board peer-to-peer IrDA communication with user-selectable TX/RX modes via Serial Monitor. Works on any ESP32 variant. Requires IR LED (TX side) and IR receiver (RX side) connected between two boards. Demonstrates real infrared communication.
setClockSource
Sets the UART clock source. Must be called before begin() to take effect.
bool setClockSource(SerialClkSrc clkSrc);
clkSrc- Clock source:UART_CLK_SRC_DEFAULT- Default clock source (varies by SoC)UART_CLK_SRC_APB- APB clock (ESP32, ESP32-S2, ESP32-C3, ESP32-S3)UART_CLK_SRC_PLL- PLL clock (ESP32-C2, ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2, ESP32-P4)UART_CLK_SRC_XTAL- XTAL clock (ESP32-C2, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2, ESP32-S3, ESP32-P4)UART_CLK_SRC_RTC- RTC clock (ESP32-C2, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2, ESP32-S3, ESP32-P4)UART_CLK_SRC_REF_TICK- REF_TICK clock (ESP32, ESP32-S2)
Note: * Clock source availability varies by SoC. * PLL frequency varies by SoC: ESP32-C2 (40 MHz), ESP32-H2 (48 MHz), ESP32-C5/C6/C61/P4 (80 MHz). * ESP32-C5, ESP32-C6, ESP32-C61, and ESP32-P4 have LP UART that uses only RTC_FAST or XTAL/2 as clock source. * For ESP32 and ESP32-S2, REF_TICK is used by default for baud rates ≤ 250000 to avoid baud rate changes when CPU frequency changes, but this limits RX timeout to 1 symbol.
Returns: true if clock source is set successfully, false otherwise.
setRxInvert
Enables or disables RX signal inversion.
bool setRxInvert(bool invert);
invert- Iftrue, inverts the RX signal polarity
Returns: true if inversion is set successfully, false otherwise.
When enableRxInternalPull() is enabled, changing RX inversion also updates the RX pad pull direction (pull-down when inverted, pull-up when normal).
setTxInvert
Enables or disables TX signal inversion.
bool setTxInvert(bool invert);
invert- Iftrue, inverts the TX signal polarity
Returns: true if inversion is set successfully, false otherwise.
setCtsInvert
Enables or disables CTS signal inversion.
bool setCtsInvert(bool invert);
invert- Iftrue, inverts the CTS signal polarity
Returns: true if inversion is set successfully, false otherwise.
setRtsInvert
Enables or disables RTS signal inversion.
bool setRtsInvert(bool invert);
invert- Iftrue, inverts the RTS signal polarity
Returns: true if inversion is set successfully, false otherwise.
setDebugOutput
Enables or disables debug output on this Serial port.
void setDebugOutput(bool enable);
enable- Iftrue, enables debug output (ESP-IDF log messages will be sent to this Serial port)
Note: By default, debug output is sent to UART0 (Serial0).
operator bool
Returns whether the Serial port is initialized and ready.
operator bool() const;
Returns: true if the Serial port is initialized, false otherwise.
Example:
Serial1.begin(115200);
while (!Serial1) {
delay(10); // Wait for Serial1 to be ready
}
Testing and Helper Functions
The following HAL-level functions are available for testing UART functionality without external hardware connections.
uart_internal_loopback
Creates an internal loopback connection from a UART’s TX signal to a specified RX pin, allowing testing without external wires.
void uart_internal_loopback(uint8_t uartNum, int8_t rxPin);
uartNum- UART number (0, 1, 2, etc.)rxPin- GPIO pin number to receive the TX signal
Note:
* When rxPin is the UART’s current RX pad and that pad uses native IOMUX routing (the SoC default pin for that UART’s RX signal), loopback is enabled inside the UART peripheral via uart_set_loop_back().
* Otherwise (GPIO-matrix RX pins, cross-UART routing, or alternate RX GPIOs), the function uses the GPIO matrix to connect the UART TX signal to rxPin.
* LP (Low-Power) UARTs are not supported for loopback.
* This is useful for testing UART communication without physical connections. The CI validation suite tests/validation/uart/uart.ino exercises both paths.
Example:
Serial1.begin(115200);
Serial1.setPins(RX1, TX1);
uart_internal_loopback(1, RX1); // Connect TX1 to RX1 internally
uart_internal_hw_flow_ctrl_loopback
Creates an internal loopback connection from a UART’s RTS signal to a specified CTS pin, allowing hardware flow control testing without external wires.
void uart_internal_hw_flow_ctrl_loopback(uint8_t uartNum, int8_t ctsPin);
uartNum- UART number (0, 1, 2, etc.)ctsPin- GPIO pin number (CTS pin) to receive the RTS signal
Note:
* This function uses the ESP32 GPIO matrix to internally connect the UART’s RTS output to the specified CTS pin.
* LP (Low-Power) UARTs are not supported for hardware flow control loopback.
* This is useful for testing hardware flow control (RTS/CTS) without physical connections.
* Must be used together with uart_internal_loopback() for complete loopback testing with flow control.
Example:
Serial1.begin(115200);
Serial1.setPins(RX1, TX1, CTS_PIN, RTS_PIN);
Serial1.setHwFlowCtrlMode(UART_HW_FLOWCTRL_CTS_RTS);
uart_internal_loopback(1, RX1); // Connect TX1 to RX1 internally
uart_internal_hw_flow_ctrl_loopback(1, CTS_PIN); // Connect RTS to CTS internally
Serial Configuration Constants
The following constants are used for serial configuration in the begin() function:
Data Bits, Parity, Stop Bits
SERIAL_5N1,SERIAL_5N2,SERIAL_5E1,SERIAL_5E2,SERIAL_5O1,SERIAL_5O2- 5 data bitsSERIAL_6N1,SERIAL_6N2,SERIAL_6E1,SERIAL_6E2,SERIAL_6O1,SERIAL_6O2- 6 data bitsSERIAL_7N1,SERIAL_7N2,SERIAL_7E1,SERIAL_7E2,SERIAL_7O1,SERIAL_7O2- 7 data bitsSERIAL_8N1,SERIAL_8N2,SERIAL_8E1,SERIAL_8E2,SERIAL_8O1,SERIAL_8O2- 8 data bits
Where: * First number = data bits (5, 6, 7, or 8) * Letter = parity: N (None), E (Even), O (Odd) * Last number = stop bits (1 or 2)
Example Applications
Baud Rate Detection Example:
OnReceive Callback Example:
RS485 Communication Example:
IrDA Mode Examples:
Dual-UART Example (Single Board with 3+ UARTs):
Two-Board Example (Peer-to-Peer Communication):
Hardware Flow Control Example:
RX Internal Pull Example:
Demonstrates floating RX idle (pull on vs off) and pull direction vs begin(invert) / setRxInvert() on split pins. Leave UART1 RX unconnected; the sketch prints pull state from gpio_get_io_config() on USB Serial at 115200 baud.
One-Wire UART Example:
Demonstrates same-pin open-drain UART on a single board: UART1 transmits and receives on one GPIO (RX == TX) and receives its own transmission back through the UART internal loopback, so the example runs with no external wiring. After the startup self-echo test, typed characters are forwarded onto the one-wire GPIO and echoed back on USB Serial. On a real bus, an external pull-up to 3.3 V replaces the internal loopback.
One-Wire UART Two-Board Example:
Demonstrates same-pin open-drain, half-duplex PING/PONG communication between two ESP32-family boards using one signal wire, common GND, and an external pull-up. The HAL configures open-drain automatically; the example keeps RX and TX on the same pad for every turn and discards expected local TX self-echo. Roles and GPIOs are configured independently on each board, allowing different SoCs.
Complete list of Serial examples.