1. Introduction
In this guide, we explore how the ESP32 communicates with a computer using serial communication. This is traditionally the very first topic taught in any embedded systems or IoT course, and for very good reasons which we will unpack below.
2. Why Start With Serial Communication?
When teaching embedded systems, it is tempting to jump straight into flashy features like Wi-Fi or Bluetooth. However, starting with serial communication has several practical advantages:
- Simplicity: The libraries required (like
Serialin Arduino) are minimal and easy to use. - Instant feedback: It gives you a quick and easy "output device" — your computer screen — to see what your microcontroller is doing.
- Foundational concept: Serial communication clarifies core ideas (timing, data framing, synchronization) that reappear in almost every other communication protocol you will learn later, such as I2C and SPI.
- Universality: Nearly every microcontroller — not just the ESP32 — needs to talk to a computer at some point, whether for debugging, data logging, or firmware uploads.
3. The Basics of Serial Communication
Serial communication is simply the process of sending data one bit at a time, sequentially, over a single wire (per direction), as opposed to parallel communication where multiple bits travel simultaneously over multiple wires.
Definition UART stands for Universal Asynchronous Receiver/Transmitter. It's not a communication protocol like SPI and I2C, but a physical circuit in a microcontroller, or a stand-alone IC. A UART's main purpose is to transmit and receive serial data.
On the ESP32, this communication happens between the microcontroller and a connected device — most commonly a computer, but it is equally applicable to other peripherals such as:
- Bluetooth modules
- GSM / GPRS modules
- RFID readers
- GPS modules
- Other microcontrollers or sensor boards
4. The USB-to-UART Bridge Chip
Modern laptops and desktops do not have a native serial (UART) port anymore. So how does the ESP32 talk to your computer over a simple USB cable?
The answer lies in a small chip soldered onto every ESP32 development board — commonly the CP2102 (Silicon Labs) or CH340 chip. This chip converts the USB signals from a computer into UART signals that the ESP32 can understand, and provides a convenient way to program the ESP32 and allows it to communicate with the computer via USB.
Why the TX and RX lines must cross
Notice in the diagram that the transmit (TX) line of one device connects to the receive (RX) line of the other, and vice versa. This is a fundamental UART wiring rule: connect the TX pin on one device to the RX pin on the connected device, and vice versa.
5. Asynchronous vs. Synchronous Communication
UART communication is asynchronous. This is one of its most defining — and sometimes confusing — characteristics. Let's break down what that means.
What "asynchronous" really means
In synchronous communication (like SPI), a dedicated clock wire is shared between devices, so both sides know exactly when to sample each bit. In asynchronous communication such as UART, no clock signal is shared between the ESP32 and the computer.
Because there is no shared clock, the receiving UART cannot simply "count" bits arriving on the wire. Instead, the transmitting UART frames each byte of data with special marker bits:
Instead of a clock signal, the transmitting UART adds start and stop bits to the data packet being transferred. These bits define the beginning and end of the data packet so the receiving UART knows when to start reading the bits. When the receiving UART detects a start bit, it starts to read the incoming bits at a specific frequency known as the baud rate.
6. Understanding Baud Rate
Baud rate is a measure of the speed of data transfer, expressed in bits per second (bps). It is essentially the "agreed talking speed" between the ESP32 and the computer that we described in the analogy above.
Common Baud Rates
| Baud Rate | Typical Use Case |
|---|---|
| 9600 | Classic default rate for many legacy sensors and modules |
| 19200 | Slightly faster, used for some GSM/GPS modules |
| 38400 | Mid-range speed for certain peripherals |
| 115200 | Most common default for the ESP32 Arduino core and Serial Monitor |
| 230400 and above | High-speed data logging / debugging |
On the ESP32 specifically: when you use Serial.begin(115200) you are initializing a serial communication using UART0 at a 115200 baud rate.
7. Full-Duplex Communication (TX and RX Lines)
UART uses two separate wires — one for transmitting (TX) and one for receiving (RX). UART is a simple, two-wire communication protocol that uses a TX (transmit) and RX (receive) line for full-duplex communication. This means data can technically flow in both directions simultaneously, because each direction has its own dedicated wire.
In practice, most UART-based communication with the ESP32 (including talking to the Serial Monitor) works as full-duplex, but many applications use it in a half-duplex, "send-then-wait-for-reply" style for simplicity.
8. Writing Your First Serial Program on the ESP32
Now let's put the theory into practice. The Arduino Serial library gives us simple functions to send and receive data over UART0 — the same UART wired to the on-board USB bridge chip.
Example 1 — Basic "Hello World" over Serial
void setup() {
Serial.begin(115200); // Start UART0 at 115200 baud
}
void loop() {
Serial.println("Hello, ESP32!"); // Send text to the Serial Monitor
delay(1000); // Wait 1 second
}
Example 2 — Two-Way Communication (Computer ⇆ ESP32)
This example listens for incoming characters typed into the Serial Monitor and echoes back what the ESP32 received: void setup() { Serial.begin(9600); pinMode(LED_BUILTIN, OUTPUT); } void loop() { if (Serial.available()) { String command = Serial.readStringUntil('\n'); if (command == "ON") { digitalWrite(LED_BUILTIN, HIGH); Serial.println("Turn LED ON"); } else if (command == "OFF") { digitalWrite(LED_BUILTIN, LOW); Serial.println("Turn LED OFF"); } } }
void setup() {
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
if (Serial.available()) {
String command = Serial.readStringUntil('\n');
if (command == "ON") {
digitalWrite(LED_BUILTIN, HIGH);
Serial.println("Turn LED ON");
}
else if (command == "OFF") {
digitalWrite(LED_BUILTIN, LOW);
Serial.println("Turn LED OFF");
}
}
}
How to Upload and Test This Code
- Open Arduino IDE
- Tools → Board → select your ESP32 board
- Tools → Port → select the correct COM port
- Copy and paste the code above into a new sketch
- Click the Upload button (right arrow icon)
- Wait for "Done uploading" message
- Tools → Serial Monitor
- Set baud rate to match Serial.begin() value
- Type ON or OFF and press Send
Serial.begin() in your code, otherwise you will only see garbled characters.
Using Additional UARTs
The ESP32 also supports extra hardware UARTs (UART1, UART2) for talking to external modules like GPS or GSM without interfering with the USB debug console: There are three serial ports on the ESP32 known as U0UXD, U1UXD and U2UXD, all working at 3.3V TTL level, corresponding to UART0, UART1, and UART2.
#define RXD2 16
#define TXD2 17
HardwareSerial mySerial(2);
void setup() {
Serial.begin(115200); // UART0: USB debug console
mySerial.begin(9600, SERIAL_8N1, RXD2, TXD2); // UART2: external device
}
void loop() {
while (mySerial.available()) {
Serial.write(mySerial.read()); // Forward UART2 data to USB console
}
}
9. Key Takeaways
- Serial (UART) communication is the simplest and most fundamental way for the ESP32 to talk to a computer or other peripherals.
- An on-board USB-to-UART bridge chip (CP2102/CH340) allows the ESP32 to use a regular USB cable even though computers lack a native serial port.
- TX and RX lines must always be crossed between two communicating devices.
- UART is asynchronous — there is no shared clock, so both devices must agree on the same baud rate and use start/stop bits to frame each byte.
- 115200 baud is the most common default speed for the ESP32, but it must match on both ends of the link.
- The ESP32 offers multiple hardware UARTs, letting you reserve UART0 for programming/debugging while using UART1/UART2 for external peripherals.