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.
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.
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.
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.
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.
Same shape. rxbuf is a constructor argument here, so it is harder to get wrong than the Arduino version.
from machine import UART, Pin
uart = UART(1, baudrate=9600, tx=Pin(17), rx=Pin(16), rxbuf=2048)
while True:
line = uart.readline()
if line and line.startswith(b'$GPGGA'):
print(line.decode().strip())readline returns None rather than blocking when there is no complete line, so this loop never stalls waiting for a sentence that has not arrived yet.
When it does not work
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.
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.
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.
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.
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.