UART Protocol Reference

ESP32 UART reference — baud rates, data formats, TX/RX wiring, UART port assignments, and troubleshooting

UART (Universal Asynchronous Receiver-Transmitter) is the simplest serial protocol and the one you already use every time you open the Serial Monitor. It is a point-to-point, asynchronous protocol — no clock signal, no addresses, just two devices agreeing on a speed and exchanging bytes.

🔗How UART Works

UART sends data one byte at a time as a sequence of voltage-level transitions on a single wire. Because there is no shared clock, both sides must agree on the timing in advance. That agreed timing is the baud rate.

A single UART frame looks like this:

sequenceDiagram
    participant Idle as Line Idle (HIGH)
    participant Start as Start Bit (LOW)
    participant D0 as Bit 0
    participant D1 as Bit 1
    participant D2 as Bit 2
    participant D3 as Bit 3
    participant D4 as Bit 4
    participant D5 as Bit 5
    participant D6 as Bit 6
    participant D7 as Bit 7
    participant Stop as Stop Bit (HIGH)

    Note over Idle,Stop: One UART frame (8N1): 1 start + 8 data + 1 stop = 10 bits per byte
PartBitsPurpose
Start bit1Pulled LOW to signal the beginning of a byte
Data bits8 (usually)The actual data, LSB first
Parity bit0 or 1Optional error check (usually omitted)
Stop bit1 (usually)Pulled HIGH to signal the end of the byte

The most common format is 8N1: 8 data bits, No parity, 1 stop bit. This is the default on virtually every device you will encounter.

🔗TX/RX Crossover

UART uses two data lines:

  • TX (Transmit) — sends data out
  • RX (Receive) — takes data in

The critical wiring rule: TX connects to RX, and RX connects to TX. You cross the wires because one device's output must feed the other device's input.

graph LR
    subgraph ESP32
        TX1["TX"]
        RX1["RX"]
    end

    subgraph Peripheral
        TX2["TX"]
        RX2["RX"]
    end

    TX1 -- "data →" --> RX2
    TX2 -- "data →" --> RX1

Common mistake: Connecting TX-to-TX and RX-to-RX. If you get no data at all, try swapping the two wires.

🔗Baud Rate

The baud rate is the number of signal transitions (bits) per second. Both devices must use the exact same baud rate, or the data will be garbled.

Common baud rates:

Baud RateBits/secEffective Byte RateTypical Use
96009,600~960 B/sGPS modules, some sensors
1920019,200~1,920 B/sSome industrial devices
3840038,400~3,840 B/sBluetooth modules (AT mode)
5760057,600~5,760 B/sFaster peripherals
115200115,200~11,520 B/sSerial Monitor default, most common
921600921,600~92,160 B/sFast data logging

The effective byte rate is approximately $\frac{\text{baud rate}}{10}$ because each byte requires 10 bits on the wire (1 start + 8 data + 1 stop in 8N1 format):

$$\text{Byte rate} = \frac{\text{Baud rate}}{1 + 8 + 1} = \frac{\text{Baud rate}}{10}$$

At $115200\,\text{baud}$, you can transfer about $11{,}520$ bytes per second — roughly $11\,\text{KB/s}$.

🔗ESP32 UART Ports

The ESP32 has three hardware UART ports:

PortArduino NameDefault TXDefault RXStatus
UART0SerialGPIO 1GPIO 3Used by USB serial — do not reassign
UART1Serial1GPIO 10GPIO 9Connected to flash on most boards — remap before using
UART2Serial2GPIO 17GPIO 16Free to use

🔗UART0 — USB Serial

UART0 is connected to the USB-to-serial converter (CP2102 or CH340) on your dev board. This is the port that Serial.begin(115200) opens and that the Serial Monitor reads from. Using GPIO 1 or GPIO 3 for anything else will break serial communication and may prevent uploading code.

🔗UART1 — Remap Required

The default UART1 pins (GPIO 9 and GPIO 10) are connected to the internal SPI flash on most ESP32 boards. Using them will crash the chip. Always remap UART1 to safe pins:

Serial1.begin(9600, SERIAL_8N1, 26, 27);  // RX=26, TX=27

🔗UART2 — Free to Use

UART2 on GPIO 16 (RX) and GPIO 17 (TX) is free and the easiest choice for connecting a peripheral:

Serial2.begin(9600);  // Uses default pins: RX=16, TX=17

🔗Data Format: 8N1

The SERIAL_8N1 constant passed to begin() sets the data format:

CharacterMeaningValue
8Data bits8 bits per byte
NParityNone (no error-check bit)
1Stop bits1 stop bit

Other formats exist (7E1, 8E2, etc.) but are rare in hobbyist electronics. If your device's datasheet does not mention a format, assume 8N1.

🔗Code Example

Connecting a peripheral device (such as a GPS module or sensor with UART output) on Serial2 while keeping the Serial Monitor on Serial:

// ESP32 UART2 example — read data from a peripheral device

#define PERIPHERAL_BAUD 9600  // Match your device's baud rate

void setup() {
  Serial.begin(115200);       // UART0: USB Serial Monitor
  Serial2.begin(PERIPHERAL_BAUD, SERIAL_8N1, 16, 17);  // UART2: RX=16, TX=17
  delay(1000);

  Serial.println("UART2 ready — forwarding data to Serial Monitor:");
}

void loop() {
  // Forward data from peripheral to Serial Monitor
  while (Serial2.available()) {
    char c = Serial2.read();
    Serial.print(c);
  }

  // Optionally send commands to the peripheral
  if (Serial.available()) {
    char c = Serial.read();
    Serial2.print(c);
  }
}

This pattern — one UART for debugging and another for the peripheral — is the standard approach for UART devices.

🔗Signal Levels

UART does not define voltage levels, so you need to make sure both devices use the same logic level:

DeviceLogic Level
ESP323.3 V
Arduino Uno/Mega5 V
Most GPS/Bluetooth modules3.3 V
RS-232 serial ports+/- 12 V

Connecting a 5 V UART device directly to an ESP32 can damage the ESP32's GPIO pins. Use a logic-level converter or a voltage divider on the RX line. The TX line (3.3 V out from the ESP32) is usually accepted by 5 V devices because 3.3 V is above their logic-HIGH threshold.

🔗Common Issues

ProblemCauseFix
Garbled text (random characters)Baud rate mismatchEnsure both devices use the exact same baud rate
No data receivedTX and RX swappedSwap the two wires — TX must connect to RX
No data receivedWrong UART port or pinsVerify you are using Serial2 and the correct GPIO numbers
ESP32 crashes on bootUsing UART1 default pins (GPIO 9/10)Remap UART1 to safe pins in the begin() call
Cannot upload codePeripheral connected to GPIO 1/3 (UART0)Disconnect the peripheral before uploading; use UART2 instead
Data loss at high speedNo flow control, buffer overflowReduce baud rate, or read data more frequently in loop()
Partial messagesReading before full message arrivesBuffer incoming data and parse only when a delimiter (like \n) is received

🔗Used In

These articles on this site use UART: