Skip to content
Tutorial
12 min read
October 17, 2026

ESP32 UART Pins: Serial, Serial2 & Custom GPIO Mapping Guide

ESP32 has three hardware UARTs. Learn the default TX and RX pins, how to use Serial2 and remap UART1, connect GPS, GSM and Arduino modules safely, use an AT-command bridge, and fix baud rate, wiring and voltage-level problems.

Every time you open the Serial Monitor, you are using a UART. It is the oldest and simplest serial link in electronics, two wires and an agreed speed, and it is how the ESP32 talks to your computer, GPS modules, GSM modems, fingerprint readers, mmWave radar sensors, other microcontrollers and a long list of AT-command devices.

The ESP32 has three hardware UARTs, which is a luxury compared with the single serial port on an Arduino Uno. You can print debug messages to your computer on one, read a GPS on another and talk to a sensor on the third, all at different baud rates and all in hardware, with no SoftwareSerial hacks. The catch is knowing which pins each one uses by default, which ones are unavailable, and how to remap them. This guide covers all of it, with working examples.

Image pending: ESP32 connected to a GPS module on Serial2 while the USB cable shows live NMEA sentences in the Serial Monitor

How UART Works

A UART connection needs just two signal wires plus a shared ground:

  • TX (transmit): the pin that sends data.
  • RX (receive): the pin that listens.
  • GND: a common reference. Forgetting this is a classic cause of garbage output.

The connection is a crossover: the ESP32's TX goes to the other device's RX, and the ESP32's RX goes to the other device's TX. Wiring TX to TX is the most common UART mistake and produces total silence.

There is no clock line. Both sides must agree on the baud rate (bits per second, for example 9600 or 115200) and the frame format, almost always 8N1: 8 data bits, no parity, 1 stop bit. If the baud rate is wrong, you receive unreadable characters.

ESP32 UART Pins

UARTArduino objectDefault TXDefault RXNotes
UART0SerialGPIO 1GPIO 3Connected to the USB chip. Used for uploading and the Serial Monitor.
UART1Serial1GPIO 10GPIO 9Default pins are flash pins on WROOM-32 boards. You must remap it.
UART2Serial2GPIO 17GPIO 16Free to use on standard boards. The usual choice for a second device.

Two practical points follow from this table:

  1. Leave UART0 alone. GPIO 1 and 3 are wired to the programming chip. Attaching a device there interferes with uploads and floods your device with boot messages. Use it only for the computer.
  2. UART2 is the easy second port. On a DevKit V1 it is labeled RX2 (GPIO 16) and TX2 (GPIO 17). See the ESP32 pinout guide. On WROVER modules GPIO 16 and 17 are used by PSRAM, so remap Serial2 there.

Thanks to the GPIO matrix, any UART can be moved to almost any pin, as the next section shows.

Image pending: Pinout diagram of the ESP32 DevKit highlighting UART0 on GPIO 1 and 3, UART2 on GPIO 16 and 17, and a remapped UART1 on GPIO 25 and 26

Using Serial2 on the Default Pins

void setup() {
  Serial.begin(115200);                          // to the computer
  Serial2.begin(9600, SERIAL_8N1, 16, 17);       // baud, format, RX pin, TX pin
}

void loop() {
  // forward anything from the device to the computer
  while (Serial2.available()) {
    Serial.write(Serial2.read());
  }
}

Note the argument order for begin(): baud, config, RX pin, TX pin. Mixing up RX and TX in this call is as easy as mixing up the wires.

Remapping a UART to Any Pins

Create a UART on any free pins by naming the port and giving it the pin numbers:

HardwareSerial sensorSerial(1);   // use hardware UART1

void setup() {
  Serial.begin(115200);
  sensorSerial.begin(9600, SERIAL_8N1, 25, 26);   // RX = 25, TX = 26
}

This is the standard fix for UART1, whose default pins sit on the flash chip. You can also move Serial2 the same way: Serial2.begin(115200, SERIAL_8N1, 32, 33);.

Use output-capable pins for TX. GPIO 34, 35, 36 and 39 are input only, so they work for RX but not TX.

Test Your UART With a Loopback

You can verify the pins and code without any device. Connect GPIO 17 (TX2) to GPIO 16 (RX2) with one jumper wire, so the ESP32 hears itself.

void setup() {
  Serial.begin(115200);
  Serial2.begin(115200, SERIAL_8N1, 16, 17);
  delay(500);

  Serial2.println("Hello UART");
  delay(100);

  if (Serial2.available()) {
    String reply = Serial2.readStringUntil('\n');
    Serial.println("Received: " + reply);
  } else {
    Serial.println("Nothing received. Check the jumper.");
  }
}

void loop() {
}

You should see Received: Hello UART. If you see nothing, the jumper is not seated or the pins do not match your board.

Image pending: Jumper wire between TX2 (GPIO 17) and RX2 (GPIO 16) on an ESP32 and the Serial Monitor showing the received loopback message

Example: Reading a GPS Module

A NEO-6M GPS module outputs standard NMEA text at 9600 baud by default. Wire it to Serial2:

GPS moduleESP32
VCC3.3V or 5V (check your module)
GNDGND
TXGPIO 16 (RX2)
RXGPIO 17 (TX2)

Install TinyGPSPlus from the Library Manager, then:

#include <TinyGPSPlus.h>

TinyGPSPlus gps;

void setup() {
  Serial.begin(115200);
  Serial2.begin(9600, SERIAL_8N1, 16, 17);
}

void loop() {
  while (Serial2.available()) {
    gps.encode(Serial2.read());
  }

  if (gps.location.isUpdated()) {
    Serial.printf("Lat: %.6f  Lon: %.6f  Sats: %d\n",
                  gps.location.lat(), gps.location.lng(), gps.satellites.value());
  }
}

A cold start can take several minutes outdoors with a clear sky view, and indoor reception is usually nothing at all. If nothing prints, first confirm raw NMEA text arrives by echoing Serial2.read() to the monitor.

Image pending: NEO-6M GPS module wired to GPIO 16 and 17 on an ESP32 with Serial Monitor output showing latitude, longitude and satellite count

Example: A USB-to-AT-Command Bridge

Many modules (SIM800 GSM, HC-05 Bluetooth, ESP-01, LoRa modems) are configured by typing AT commands. A bridge sketch passes everything you type in the Serial Monitor to the module and prints its replies:

void setup() {
  Serial.begin(115200);
  Serial2.begin(9600, SERIAL_8N1, 16, 17);
  Serial.println("Bridge ready. Type AT commands.");
}

void loop() {
  if (Serial.available())  Serial2.write(Serial.read());
  if (Serial2.available()) Serial.write(Serial2.read());
}

Set the Serial Monitor line ending to Both NL & CR so the module sees a proper command terminator. Type AT and expect OK.

Talking to an Arduino: Mind the Voltage

Linking an ESP32 to an Arduino Uno is common, but the two run at different logic levels: the ESP32 at 3.3V, the Uno at 5V.

  • ESP32 TX to Arduino RX: safe. The Uno reads 3.3V as a valid HIGH.
  • Arduino TX to ESP32 RX: not safe without protection. A 5V signal into the ESP32 can damage it. Use a voltage divider (for example 1 kΩ from the Arduino TX to the ESP32 RX and 2 kΩ from that point to GND, which gives about 3.3V) or a level shifter.

Share a ground between the boards, and match the baud rate on both sides.

Image pending: Schematic of an Arduino Uno TX line passing through a 1k and 2k voltage divider into the ESP32 RX2 pin with a shared ground

Handling More Data: Buffers and Parsing

The default receive buffer is 256 bytes. If a device sends bursts faster than your loop reads them, bytes are lost. Enlarge the buffer before calling begin():

Serial2.setRxBufferSize(2048);
Serial2.begin(115200, SERIAL_8N1, 16, 17);

Reading tips:

  • Always check available() before reading.
  • Do not block the loop with long delay() calls while data is arriving.
  • To read a full line, use readStringUntil('\n'). For binary protocols, read exact byte counts with readBytes(buffer, length).
  • Set Serial2.setTimeout(50) so read calls do not wait a full second for a missing terminator.

Common Problems

Garbage characters. The baud rates do not match, or the device is not using 8N1. Check the device datasheet.

Complete silence. TX and RX are not crossed, ground is not shared, or the module is not powered. Try swapping the two data wires.

Upload fails when a device is attached. The device is on GPIO 1 or 3 and interferes with flashing. Move it to Serial2, or disconnect it during upload.

Boot loops or odd behavior. A device is pulling a strapping pin during boot. The UART2 pins 16 and 17 are safe, so prefer them.

Data drops at high speed. Increase the receive buffer and read more often. Use 115200 or lower unless the wiring is short and clean.

Works for a while, then freezes. The loop is too slow to empty the buffer, or the module is browning out the supply. Check power.

Frequently Asked Questions

How many UARTs does the ESP32 have? Three on the original ESP32 (UART0, UART1 and UART2). The ESP32-S3 also has three, and the ESP32-C3 has two.

Can I use SoftwareSerial on ESP32? You do not need it. Use a hardware UART. A SoftwareSerial library for ESP32 exists, but hardware ports are more reliable at high speed.

What is the maximum baud rate? Up to 5 Mbps in theory. For breadboard wiring, 115200 to 921600 is practical.

Can I use Serial2 with WiFi? Yes, there is no conflict.

Which pins should I use for a second device? GPIO 16 and 17 for Serial2, or remap to any free pair such as 25 and 26. See the ESP32 pinout guide for safe pins.

Where next? Learn the other two buses in the I2C guide and the SPI guide.

Image pending: Finished test bench with an ESP32 using Serial for the computer, Serial2 for a GPS module and a remapped UART1 for a second sensor

Signal In

Built from what makers search for

The lookups piling up in SolderHub's search bar every day shape what we publish next — new component pages, board guides, and firmware notes, sent out as they go live.

By subscribing you agree to our Privacy Policy. Unsubscribe anytime.