This is a simple guide about UART serial communication protocol with the ESP32 using Arduino IDE. We’ll take a look at the basics of UART, default and custom UART pins, basic functions, and communication between boards.
Table of Contents:
Throughout this guide, we’ll cover the following topics:
- Introducing ESP32 UART Communication Protocol
- UART0 and the Serial Monitor
- ESP32 Setting Custom UART Pins
- UART Communication with Other Devices
Prerequisites
This tutorial focuses on programming the ESP32 using the Arduino core. Before proceeding, you should have the ESP32 Arduino core installed in your Arduino IDE. Follow the next tutorial to install the ESP32 on the Arduino IDE, if you haven’t already.
Alternatively, you can also use VS Code with the PlatformIO extension to program your boards using the Arduino core:
Introducing ESP32 UART Communication Protocol
UART (Universal Asynchronous Receiver-Transmitter) is a serial communication protocol that allows two devices to communicate.
Unlike SPI or I2C, which are synchronous, UART is asynchronous, meaning it does not use a clock signal to synchronize the data transmission between devices. However, both devices must agree on the baud rate (speed of transmission).
In UART communication, data is transferred serially, bit by bit (hence the term serial), at a pre-defined baud rate (bits per second). UART uses a single data line for transmitting (TX) and one for receiving (RX).
UART ports allow us to communicate with other devices, such as other microcontroller boards (an Arduino, an ESP8266, another ESP32 board, or others), the computer, sensors, GPS or Bluetooth modules, some types of displays, and more.
UART Interface
For UART communication, you need the following lines:
- TX (Transmit): Sends data.
- RX (Receive): Receives data.
- GND: Common ground
ESP32 UART Peripherals
The ESP32 supports up to three UART interfaces: UART0, UART1, and UART2, depending on the ESP32 board model you’re using.
- UART0 is usually reserved for communication with the serial monitor during upload and debugging. However, you can also use it for communication with other devices after uploading the code if the Serial Monitor is not needed.
- UART1 and UART2: available to communicate with external devices.
ESP32 Default UART Pins
Like I2C and SPI, these UART pins can be mapped to any GPIO pin on the ESP32. However, they have a default pin assignment on most board models.
For most ESP32 boards the UART pin assignment is as follows:
UART Port | TX | RX | Remarks |
UART0 | GPIO 1 | GPIO 3 | Used for Serial Monitor and uploading code; Can be assigned to other GPIOs; |
UART1 | GPIO 10 | GPIO 9 | Must be assigned to other GPIOs |
UART2 | GPIO 17 | GPIO 16 | Can be assigned to other GPIOs |
About UART1 (GPIO 9 and GPIO10) – these GPIOs are connected to the ESP32 SPI flash memory, so you can’t use them like that. To use UART1 to communicate with other devices, you must define different pins using the HardwareSerial library.
If you’re using an ESP32-S3, the assignment is completely different. The following table shows the default UART0, UART1, and UART2 RX and TX pins for the ESP32-S3:
UART Port | TX | RX | Remarks |
UART0 | GPIO 43 | GPIO 44 | Cannot be changed |
UART1 | GPIO 17 | GPIO 18 | Can be assigned to other GPIOs |
UART2 | — | — | Assign any pins of your choice |
Note: Depending on the board, the default UART pins might be different. Always check the pinout for your specific board. You can also reassign UART pins in code if your board doesn’t have them pre-assigned.
UART0 and the Serial Monitor
In most IDEs like the Arduino IDE or PlatformIO, the Serial Monitor interacts with the ESP32 over a USB connection. This is internally mapped to UART0 on the ESP32, so when you open the Serial Monitor, you are using UART communication to send and receive messages.
Note: you can use UART0 to communicate with other devices after uploading code to the board if you don’t use the Serial Monitor.
Here’s a basic example that shows a two-way communication between the ESP32 and the Serial Monitor (your computer).
/*********
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete instructions at https://RandomNerdTutorials.com/esp32-uart-communication-serial-arduino/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/
String receivedMessage = ""; // Variable to store the complete message
void setup() {
// Start the Serial Monitor at a baud rate of 115200
Serial.begin(115200);
// Print an initial message to the Serial Monitor
Serial.println("ESP32 is ready. Please enter a message:");
}
void loop() {
// Check if data is available in the Serial buffer
while (Serial.available()) {
char incomingChar = Serial.read(); // Read each character from the buffer
if (incomingChar == '\n') { // Check if the user pressed Enter (new line character)
// Print the message
Serial.print("You sent: ");
Serial.println(receivedMessage);
// Clear the message buffer for the next input
receivedMessage = "";
} else {
// Append the character to the message string
receivedMessage += incomingChar;
}
}
}
When you use Serial.begin(115200) you are initializing a serial communication using UART0 at a 115200 baud rate.
Serial.begin(115200);
To write data to the Serial Monitor, you use the print() or println() methods on the Serial instance.
Serial.println("ESP32 is ready. Please enter a message:");
You can also send data from the Serial Monitor to the ESP32. To read that data, you can use the read() method. To check if there are any available bytes to read, you can use the available() method.
if (Serial.available()) {
char incomingData = Serial.read(); // Read the incoming data from Serial Monitor
Testing the Example
Upload the code to your ESP32. After uploading, open the Serial Monitor at a baud rate of 115200. Then, press the ESP32 RST button to start running the program.
It should print a message “ESP32 is ready. Please enter a message.“
There’s a field on the Serial Monitor where you can write data to send to the ESP32 (highlighted in yellow). Write something on that field and press enter to send it to the ESP32. It will be printed back.
ESP32 Setting Custom UART Pins
Setting custom UART pins is quite simple. As we’ve seen previously, the ESP32 has three UART ports you can use: UART0, UART 1 and UART 2.
To use any of those ports, you just need to create an HardwareSerial instance on the desired UART port. For example, the following line creates a serial instance called mySerial using UART2.
HardwareSerial mySerial(2);
Then, you can use any pins of your choice. You just need to pass them to the begin() method like this:
mySerial.begin(GPS_BAUD, SERIAL_8N1, RX_GPIO, TX_GPIO);
In which RX_GPIO and TX_GPIO are the UART pins of your choice.
UART Communication with Other Devices
Now, let’s take a look at how to use UART to communicate with other devices. We’ll see an example to communicate with a GPS module, and an example to communicate with another ESP32.
Serial Communication with a GPS Module
To start a UART communication in your ESP32 code, you need to specify the UART port, baud rate, and pin mapping. Here’s an example of initializing UART communication with a GPS module—this can be applied to other modules or devices.
/*********
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete instructions at https://RandomNerdTutorials.com/esp32-neo-6m-gps-module-arduino/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/
// Define the RX and TX pins for Serial 2
#define RXD2 16
#define TXD2 17
#define GPS_BAUD 9600
// Create an instance of the HardwareSerial class for Serial 2
HardwareSerial gpsSerial(2);
void setup(){
// Serial Monitor
Serial.begin(115200);
// Start Serial 2 with the defined RX and TX pins and a baud rate of 9600
gpsSerial.begin(GPS_BAUD, SERIAL_8N1, RXD2, TXD2);
Serial.println("Serial 2 started at 9600 baud rate");
}
void loop(){
while (gpsSerial.available() > 0){
// get the byte data from the GPS
char gpsData = gpsSerial.read();
Serial.print(gpsData);
}
delay(1000);
Serial.println("-------------------------------");
}
This sketch assumes you are using GPIO 16 and GPIO 17 as RX and TX serial pins to establish serial communication with the GPS module. If you’re using other pins you should edit that on the following lines:
// Define the RX and TX pins for Serial 2
#define RXD2 16
#define TXD2 17
Then, we define the module baud rate on the following line.
#define GPS_BAUD 9600
We create an instance of the HardwareSerial to use UART 2 called gpsSerial.
// Create an instance of the HardwareSerial class for Serial 2
HardwareSerial gpsSerial(2);
In the setup(), we initiate the Serial Monitor.
// Serial Monitor
Serial.begin(115200);
Next, we initialize a serial communication with the GPS module using the begin() method on the gpsSerial() instance.
// Start Serial 2 with the defined RX and TX pins and a baud rate of 9600
gpsSerial.begin(GPS_BAUD, SERIAL_8N1, RXD2, TXD2);
Serial.println("Serial 2 started at 9600 baud rate");
The begin() method accepts as arguments the baud rate, communication frame format (data, parity and stop bits, SERIAL_8N1 is the default), and RX and TX pins.
SERIAL_8N1 means 8 data bits, No parity, 1 stop bit, which is the most commonly used. You can check all the options here.
In the loop(), the code checks if there is GPS data on the serial port with the available() method.
void loop(){
while (gpsSerial.available() > 0){
When data is available, we read it using the read() method and then print it in the Serial Monitor.
void loop(){
while (gpsSerial.available() > 0){
// get the byte data from the GPS
char gpsData = gpsSerial.read();
Serial.print(gpsData);
}
delay(1000);
Serial.println("-------------------------------");
}
For the complete instructions to test this code, make sure to take a look at our GPS Module tutorial: ESP32 with NEO-6M GPS Module (Arduino IDE).
ESP32 Serial Communication Between Boards (Sender and Receiver)
If you want to communicate with another ESP32, it is not much different. You basically initialize a serial communication on the desired GPIOs and then use the functions to read and send data via serial.
To show you how this works, we’ll send data via Serial from one ESP32 board to the other. The ESP32 sender will continuously send a message with a counter over UART. The ESP32 receiver board receiver will receive the number from the other board.
To test the example:
- you need two ESP32 boards;
- one board will be the sender and the other will be the receiver
- we’ll use UART1 and we’ll assign TX to GPIO 19 and RX to GPIO 21 (you can use any other pins)—we’re not using the default pins to show you how it’s done;
- you need to connect the RX from one board to the TX of the other and connect the GNDs together
ESP32 #1 (Sender) | ESP32 #2 (Receiver) |
TXD1 (GPIO 19) | RXD1 (GPIO 21) |
RXD1 (GPIO 21) | TXD1 (GPIO 19) |
GND | GND |
ESP32 Sender Code
Here’s the code for the ESP32 sender. Upload it to your board.
/*********
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete instructions at https://RandomNerdTutorials.com/esp32-uart-communication-serial-arduino/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/
// Define TX and RX pins for UART (change if needed)
#define TXD1 19
#define RXD1 21
// Use Serial1 for UART communication
HardwareSerial mySerial(1);
int counter = 0;
void setup() {
Serial.begin(115200);
mySerial.begin(9600, SERIAL_8N1, RXD1, TXD1); // UART setup
Serial.println("ESP32 UART Transmitter");
}
void loop() {
// Send message over UART
mySerial.println(String(counter));
Serial.println("Sent: " + String(counter));
// increment the counter
counter++;
delay(1000);
}
How Does the Code Work?
Let’s take a look at how the code works.
Start by defining the TX and RX pins you want to use. We’re using GPIO19 and GPIO21, but you can use any other pins.
#define TXD1 19
#define RXD1 21
We’re using UART1, but for this example we could also have chosen UART2. To use UART1, we start by creating an HardwareSerial instance called mySerial.
// UART1
HardwareSerial mySerial(1);
Then, we create a variable to hold the number we want to send to the other board called counter.
int counter = 0;
In the setup(), we initialize the serial communication on UART1 by calling the begin() method on the mySerial object.
mySerial.begin(9600, SERIAL_8N1, RXD1, TXD1); // UART setup
In the loop(), we send the counter via serial by using the println() method on our mySerial object. This is what will send the number to the other board.
mySerial.println(String(counter));
Then, we increment the counter on each loop.
// increment the counter
counter++;
A new message is sent every second, but you can change the delay time if needed.
delay(1000);
Uploading the Code
Upload the code to the sender board. After uploading open the serial monitor at a baud rate of 115200. You’ll see that it will start sending data via UART.
Now, let’s prepare the receiver to receive the data.
ESP32 Receiver Code
Here’s the code for the ESP32 receiver. Upload it to your board.
/*********
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete instructions at https://RandomNerdTutorials.com/esp32-uart-communication-serial-arduino/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/
// Define TX and RX pins for UART (change if needed)
#define TXD1 19
#define RXD1 21
// Use Serial1 for UART communication
HardwareSerial mySerial(2);
void setup() {
Serial.begin(115200);
mySerial.begin(9600, SERIAL_8N1, RXD1, TXD1); // UART setup
Serial.println("ESP32 UART Receiver");
}
void loop() {
// Check if data is available to read
if (mySerial.available()) {
// Read data and display it
String message = mySerial.readStringUntil('\n');
Serial.println("Received: " + message);
}
}
How Does the Code Work?
Setting up the serial connection is the same as we’ve seen in the previous example. The loop() is different. In this case, we’ll listen for incoming data.
Then, to receive data from the other board, we start by checking if there are any available bytes to read.
// Check if data is available to read
if (mySerial.available()) {
Then, we read the incoming data and saved it in the message variable using the readStringUntil() method.
String message = mySerial.readStringUntil('\n');
This method reads data from the UART serial buffer as a String, continuing to read characters until it encounters the newline character ‘\n‘ .
The ‘\n‘ is typically used to mark the end of a message or line. When it encounters this character in the incoming data stream, it stops reading and returns the string up to that point.
Upload the Code
Upload the previous code to the receiver board. Open a Serial Monitor connection with this new board.
The ESP32 sender will continuously send a message with a counter over UART. The ESP32 board receiver will receive the number from the other board.
Now you have one ESP32 board sending data to the other.
Wrapping Up
This tutorial was a getting started guide to UART communication with the ESP32. We’ve seen how UART works, how to use the ESP32 UART ports on the desired pins, and how to send data from one ESP32 to the other.
We hope you found this guide useful.
We have guides for other communication protocols with the ESP32 that you may find useful.
- ESP32 I2C Communication: Set Pins, Multiple Bus Interfaces and Peripherals (Arduino IDE)
- ESP32 SPI Communication: Set Pins, Multiple SPI Bus Interfaces, and Peripherals (Arduino IDE)
- ESP32 Wireless Communication Protocols
Learn more about the ESP32 with our resources:
Thanks for reading.
Thanks a lot: this is very useful to many of us.
Anyway compiling sketches for the ESP32 using Arduino IDE is almost unpossible because a very long time. Normally it takes about 3 or more minutes to compile and this is not acceptable for a practical use. Any solution to this big problem ?
Thanks
It may be due to updates on the Arduino IDE.
I’m not sure…
Regards,
Sara
I know lot of people that are expeiencing a very long compiling time as I told. Question: are you experiencing the same problem , please ? If Not, could you please inform about your configuration ?
Thanks
Merci tout est claire
I am having issues using the data received to do something. If the counter reaches ten for example I would like to serial.printing (“we reached ten”); I have tried a few different approaches using if(message == “10”) {} I have been unsuccessful so far. My goal is to send a color as a string “blue” and receive it as a string the perform a function to change LEDs. I am assuming the counter is sending the numbers as strings.
can I use esp1 to read the sensor and then the data is sent to esp2, because I have tried in arduino mega to esp32 that when reading the sensor it does not want to send, but when the sensor is not connected it wants to send data again….