UART is the bedrock of embedded communication. It requires only two wires, needs no shared clock, and is supported by virtually every microcontroller, sensor, and communication module ever made. On the ESP32, UART is the first tool you reach for when you need to debug, communicate with a PC, or talk to a GPS or GSM module.
What is UART?
UART (Universal Asynchronous Receiver/Transmitter) is a serial communication protocol that transmits data one bit at a time over a single wire in each direction, without a shared clock signal.
UART Frame (8N1 — 8 data bits, no parity, 1 stop bit)
------------------------------------------------------
Idle START D0 D1 D2 D3 D4 D5 D6 D7 STOP Idle
HIGH LOW b b b b b b b b HIGH HIGH
Key characteristics:
| Feature | UART |
|---|---|
| Wires | 2 (TX and RX) |
| Clock | None (asynchronous — both sides agree on baud rate) |
| Direction | Full-duplex (simultaneous TX and RX) |
| Max speed | Typically 115200 baud for standard use; up to 5 Mbaud on ESP32 |
| Topology | Point-to-point (one TX to one RX) |
What is Baud Rate?
Baud rate is the number of symbol changes per second. For UART, 1 baud = 1 bit per second. Common baud rates:
| Baud Rate | Bits/sec | Use Case |
|---|---|---|
| 9600 | 9600 bps | Older devices, GPS modules |
| 115200 | 115200 bps | Standard debug output |
| 921600 | 921600 bps | ESP32 firmware upload |
Both sides of a UART connection must be configured to the same baud rate for communication to work.
ESP32 UART Interfaces
The ESP32 provides three hardware UART controllers:
| Interface | TX Pin (default) | RX Pin (default) | Notes |
|---|---|---|---|
| UART0 | GPIO1 | GPIO3 | Used for USB-to-serial, programming, and Serial in Arduino |
| UART1 | GPIO17 | GPIO16 | General-purpose serial |
| UART2 | GPIO17 | GPIO16 | General-purpose serial (configurable pins) |
All UART pins on the ESP32 are software-remappable — any GPIO can be assigned as TX or RX for UART1 and UART2.
Using UART with the Arduino Framework
In the Arduino framework, UART is accessed through Serial, Serial1, and Serial2 objects.
UART0: Debug Output (Serial)
UART0 is the default serial port, connected to the USB-to-serial converter on most ESP32 development boards.
#include <Arduino.h>
void setup() {
Serial.begin(115200); // Initialize UART0 at 115200 baud
Serial.println("ESP32 UART initialized");
}
void loop() {
Serial.println("Hello from UART0");
delay(1000);
}
UART1: General Purpose Serial
Use Serial1.begin() to configure UART1 with custom TX/RX pins:
#include <Arduino.h>
#define UART1_TX 17
#define UART1_RX 16
void setup() {
Serial.begin(115200); // Debug output on UART0
Serial1.begin(9600, SERIAL_8N1, UART1_RX, UART1_TX); // UART1 for device communication
Serial.println("UART1 initialized");
}
void loop() {
// Send data over UART1
Serial1.println("AT"); // Example: send AT command to GSM module
// Read response from UART1
if (Serial1.available()) {
String response = Serial1.readString();
Serial.print("Response: ");
Serial.println(response);
}
delay(1000);
}
Serial Configuration Options
Serial.begin(baud_rate, config, rx_pin, tx_pin);
| Config | Data Bits | Parity | Stop Bits |
|---|---|---|---|
SERIAL_8N1 | 8 | None | 1 |
SERIAL_8N2 | 8 | None | 2 |
SERIAL_8E1 | 8 | Even | 1 |
SERIAL_8O1 | 8 | Odd | 1 |
8N1 is the most common configuration and the default when only baud rate is specified.
Reading UART Data
Several methods are available for reading incoming data:
// Check if data is available
if (Serial.available()) {
// Read one byte
char c = Serial.read();
// Read all available bytes as String
String line = Serial.readString();
// Read until newline character
String line2 = Serial.readStringUntil('\n');
// Read raw bytes into buffer
uint8_t buf[64];
size_t len = Serial.readBytes(buf, 64);
}
Complete UART Example: Echo Server
This example echoes back any data received over UART, a classic test for verifying bidirectional communication:
#include <Arduino.h>
void setup() {
Serial.begin(115200);
Serial.println("UART Echo Server Ready");
Serial.println("Type something and press Enter...");
}
void loop() {
if (Serial.available()) {
String received = Serial.readStringUntil('\n');
received.trim(); // Remove whitespace and newline characters
Serial.print("Received: '");
Serial.print(received);
Serial.println("'");
// Echo back with confirmation
Serial.print("Echo: ");
Serial.println(received);
}
}
Open the Serial Monitor at 115200 baud and type a message. The ESP32 will echo it back.
UART Between Two Microcontrollers
When connecting two devices via UART:
Device A Device B
-------- --------
TX (GPIO17) -----------------> RX (pin)
RX (GPIO16) <----------------- TX (pin)
GND -----------------> GND
NOTE: Always connect GND between the two devices.
Cross-connect TX to RX and RX to TX.
Critical: Always connect GND between the two devices — a shared reference ground is required for UART to function correctly.
UART for GPS Module (Example Application)
GPS modules like the NEO-6M communicate at 9600 baud using NMEA sentences:
#include <Arduino.h>
#define GPS_TX 17 // ESP32 TX to GPS RX
#define GPS_RX 16 // ESP32 RX to GPS TX
void setup() {
Serial.begin(115200); // Debug output
Serial1.begin(9600, SERIAL_8N1, GPS_RX, GPS_TX); // GPS module
Serial.println("GPS UART initialized");
}
void loop() {
while (Serial1.available()) {
char c = Serial1.read();
Serial.write(c); // Print raw NMEA sentences to debug terminal
}
}
UART Summary
| Operation | Code |
|---|---|
| Initialize UART0 | Serial.begin(115200) |
| Initialize UART1 with custom pins | Serial1.begin(9600, SERIAL_8N1, RX_PIN, TX_PIN) |
| Send string | Serial.println("text") |
| Send bytes | Serial.write(buffer, length) |
| Check for data | Serial.available() |
| Read one byte | Serial.read() |
| Read string until newline | Serial.readStringUntil('\n') |
Final Thoughts
UART is the simplest and most universally supported serial protocol. On the ESP32, it serves as the debug output channel, the firmware upload interface, and the communication bridge to external modules like GPS, GSM, and Bluetooth adapters.
Mastering UART usage — especially configuring custom pins for UART1/UART2 — enables the ESP32 to communicate with virtually any serial device in existence.
UART is how the ESP32 talks to the world one bit at a time.