ESP32/Buses/33. UART and baud rates
Your chip
Your language
Buses · 33 of 81

UART and baud rates

Two wires crossed over, one agreed speed, and no clock between them. It is the oldest bus here and the one that fails most quietly - a GPS that drops half its sentences is not a bad GPS, it is a buffer that overflowed.

/esp32/uart-and-baud-rates · arduino · S3

The buffer nobody sizes

Serial data does not wait for you. It lands in a driver buffer — 256 bytes by default — whether your loop is ready or not.

115200 baud into a 256-byte buffer
keeping up
Baud rate115200
Time your loop spends not reading20 ms
Buffer size (setRxBufferSize)256 B
Bytes arriving
230
Lost
0
Buffer holds
22 ms
The buffer holds 22 ms of traffic and you come back every 20 ms. That is the whole test. Note that it is a race you can lose by changing something unrelated — adding a display refresh to the loop is enough — which is why a GPS that worked yesterday starts producing half-sentences today.

Reading lines without blocking

The pattern above is worth copying: read whatever is available, build a line character by character, act on it when the newline arrives. Never while (!Serial1.available()) {} — that is a loop that waits forever the one time the device does not answer.

Three UARTs, and which are free

The classic ESP32 has three. UART0 is the USB console — using it for anything else means losing your debug output. UART1 and UART2 are yours, on any pins.

The C3 and C6 have two, and on a native-USB board the console is separate hardware, so both UARTs are actually free. That is a small but real advantage when you have a GPS and a fingerprint reader on the same board.

On your S3
ChipXtensa LX7 · 2 × 240 MHz
Board settingESP32S3 Dev Module
Default I2CSDA 8 · SCL 9
Watch out forThe port vanishes after upload

The code

A second UART on any two pins, with the buffer sized before begin. That order matters - setRxBufferSize after begin does nothing and reports nothing.

gps_reader.ino
void setup() {
  Serial.begin(115200);                 // the USB console

  Serial1.setRxBufferSize(2048);        // BEFORE begin, or it is ignored
  Serial1.begin(9600, SERIAL_8N1, 16, 17);   // baud, format, RX, TX
}

void loop() {
  static char line[120];
  static int n = 0;

  while (Serial1.available()) {
    char c = Serial1.read();
    if (c == '\n' || n == sizeof(line) - 1) {
      line[n] = 0;
      n = 0;
      if (strncmp(line, "$GPGGA", 6) == 0) Serial.println(line);
    } else if (c != '\r') {
      line[n++] = c;
    }
  }
}

The classic ESP32 has three UARTs, the C3 and C6 have two, and one of them is always the USB console. Serial1 and Serial2 are the ones you get.

When it does not work

Garbage characters instead of text

The two ends disagree about baud. There is no clock wire, so each side times the bits itself and a mismatch produces plausible-looking nonsense rather than an error.

Whole sentences go missing at random

The receive buffer overflowed while your loop was busy. Nothing reports it. Raise the buffer size, stop blocking in the loop, or read the port from its own task.

Nothing arrives at all

TX and RX are not crossed. The board's TX goes to the device's RX. Two devices both transmitting into each other's transmit pin is completely silent and looks like a dead module.

It works at 9600 and not at 115200

Long wires, or a 5 V device. UART at 3.3 V over 30 cm of unshielded jumper is fine at 9600 and marginal at 115200. Shorten it, or slow down.

Where this goes next

UART reaches a desk. The next page is the same protocol over a kilometre of factory cable.

RS485 and Modbus

Edit this page — content/esp32/uart-and-baud-rates.mdx

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse ESP32 on the forum