Basics · 11 min read

What Is UART? TX/RX Wiring, Baud Rate and Arduino Serial Guide

What is UART and how does it work? TX/RX crossover, baud rate, start/stop bits, linking two Arduinos, SoftwareSerial, 5V/3.3V levels, ESP32 Serial2, Pico UART.

The short answer to what is UART: UART (Universal Asynchronous Receiver-Transmitter) is the serial communication hardware that lets two devices exchange data byte by byte over just two data wires (TX and RX) plus a common GND. Every line you see in the Serial Monitor when your Arduino is plugged into the computer arrives over UART. Dozens of modules, such as GPS receivers, the HC-05 Bluetooth module, the ESP-01 Wi-Fi module, the SIM800L GSM module, fingerprint readers and MP3 players, also talk to the Arduino over UART.

In this post we explain step by step why UART needs no clock line, what baud rate is, how a byte is framed with start, data and stop bits, why TX and RX are crossed, how to make two Arduinos talk first one way and then both ways, the limits of SoftwareSerial, how to connect 5 V and 3.3 V boards safely, the ESP32's three UARTs and the Raspberry Pi Pico's UART pins. In the last section you'll see how to build packets with start and end characters so data never gets mixed up, and the most common mistakes.

CP2102 USB-TTL converter
CP2102 USB-TTL converter: it turns the computer's USB port into a UART with TX, RX and GND pins; the USB chip on the Arduino Uno does the same job.

What is UART and how does it work?

The word "asynchronous" in UART's name is the key: in I2C and SPI a clock signal is sent along with the data, whereas UART has no clock line. Instead, both sides agree on a speed in advance, called the baud rate. When idle, the line sits HIGH (1). To send a byte, the transmitter pulls the line LOW for one bit time; this is the start bit, telling the receiver "start counting now". The 8 data bits follow, least significant bit (LSB) first, an optional parity bit may come next, and finally the line is held HIGH for at least one bit time; this is the stop bit. The Arduino's default setting is 8N1: 8 data bits, no parity, 1 stop bit.

BitStartD0D1D2D3D4D5D6D7Stop
Letter 'A' (0x41 = 0100 0001)0100000101
Line levelLOWHIGHLOWLOWLOWLOWLOWHIGHLOWHIGH

Each bit lasts 1 / baud seconds. At 9600 baud a bit takes 104.2 microseconds; since an 8N1 frame is 10 bits per byte (start + 8 data + stop), at most 960 bytes per second can be sent. After catching the falling edge of the start bit, the receiver samples the line in the middle of each bit. Even if the two clocks differ slightly, data is read correctly as long as the error accumulated over 10 bits stays under half a bit; that's why the total baud error must stay below roughly 2–3%. With its 16 MHz crystal the Arduino Uno can't produce 115200 baud exactly, and the real rate is about 2.1% off; it still works because it's within the limit.

BaudBit timeMax bytes per second (8N1)Where it's used
9600104.2 µs960NEO-6M GPS, HC-05 data mode, DFPlayer Mini, the classic Serial Monitor setting
1920052.1 µs1920Some sensors and older modems
3840026.0 µs3840HC-05 AT command mode
5760017.4 µs5760Older Arduino bootloaders, upper limit for SoftwareSerial
1152008.68 µs11520ESP32 and ESP-01, fast debug output
1000000 and above1 µs or less100,000+Short board-to-board links, fast data logging

UART vs I2C vs SPI

The three most common serial communication methods on the Arduino are UART, I2C and SPI. UART works one-to-one between two devices only, and both sides can talk whenever they like (full duplex). I2C gathers dozens of devices on the same two wires using addresses; SPI is much faster with four wires but needs a separate select pin for each device. For details, see the What is I2C and What is SPI posts.

FeatureUARTI2CSPI
Wires2 (TX, RX) + GND2 (SDA, SCL) + GND4 (SCK, MOSI, MISO, CS) + GND
Clock lineNone (asynchronous)YesYes
Number of devices2 (point-to-point)Up to 127, by addressAs many as CS pins
Typical speed9600–115200 baud100–400 kHz1–80 MHz
Both directions at onceYes (full duplex)NoYes
Typical modulesGPS, Bluetooth, GSM, ESP-01, fingerprintOLED, LCD, RTC, BME280SD card, RFID, TFT, LoRa

TX and RX crossover and a common GND

The golden rule of UART wiring: one device's TX (transmit) pin connects to the other's RX (receive) pin, so the wires cross over. Think of it as mouth to ear: where one speaks, the other must listen. If you connect TX to TX, two outputs are tied together and no data gets through. The third wire, GND, gives both boards the same voltage reference; without it, "HIGH" and "LOW" become meaningless and incoming data turns into garbage characters. Even if the boards are powered from separate sources, their grounds must be connected.

Arduino AWireArduino BNote
TX (D11, SoftwareSerial)→RX (D10, SoftwareSerial)What A sends goes to B
RX (D10, SoftwareSerial)←TX (D11, SoftwareSerial)B's reply comes to A
GND—GNDCommon ground, mandatory
5Voptional5VOnly if just one board is powered over USB
On the Arduino Uno and Nano, the hardware UART pins D0 (RX) and D1 (TX) are also connected to the USB chip. If you try to upload with a module connected to these pins, the upload fails with a "not in sync" error, and in the Serial Monitor the module's data mixes with the computer's. Either unplug the D0/D1 wires while uploading, or use other pins with SoftwareSerial as in the examples in this post.

Making two Arduinos talk: one way

Arduino Uno
Arduino Uno: because its only hardware UART (D0/D1) is reserved for USB, linking two Unos with SoftwareSerial on D10/D11 is the easiest way.

In the first example, board A sends the value of a potentiometer on A0 to board B ten times a second, and board B sets the brightness of the LED on D9 accordingly. Both boards use SoftwareSerial on D10 (RX) and D11 (TX), which keeps the USB connection and Serial Monitor free. Because the values are sent with println(), each number ends with the "\r\n" (line end) characters; the receiver knows the number is complete when it sees them. Wiring: A's D11 to B's D10, A's D10 to B's D11, and the grounds together.

C++ (Arduino IDE)
1#include <SoftwareSerial.h>
2
3SoftwareSerial link(10, 11);    // RX = 10, TX = 11 (diğer karta / to the other board)
4
5void setup() {
6  Serial.begin(9600);           // USB: bilgisayar / computer
7  link.begin(9600);             // iki tarafta aynı baud / same baud on both sides
8}
9
10void loop() {
11  int deger = analogRead(A0);   // potansiyometre 0-1023 / potentiometer 0-1023
12  link.println(deger);          // ör. "512\r\n" / e.g. "512\r\n"
13  Serial.println(deger);
14  delay(100);                   // saniyede 10 ölçüm / 10 readings per second
15}
16
C++ (Arduino IDE)
1#include <SoftwareSerial.h>
2
3SoftwareSerial link(10, 11);    // RX = 10, TX = 11
4const int LED_PIN = 9;          // PWM pini / PWM pin
5
6char tampon[8];                 // en fazla 7 karakter + '\0' / up to 7 chars + '\0'
7byte konum = 0;
8
9void setup() {
10  Serial.begin(9600);
11  link.begin(9600);
12  pinMode(LED_PIN, OUTPUT);
13}
14
15void loop() {
16  while (link.available()) {               // gelen her baytı oku / read every incoming byte
17    char c = link.read();
18    if (c == '\n') {                       // satır bitti / line complete
19      tampon[konum] = '\0';
20      int deger = atoi(tampon);
21      analogWrite(LED_PIN, map(deger, 0, 1023, 0, 255));
22      Serial.println(deger);
23      konum = 0;
24    } else if (c != '\r' && konum < sizeof(tampon) - 1) {
25      tampon[konum++] = c;                 // satıra ekle / append to the line
26    }
27  }
28}
29

There are two things to notice in the receiver code. First, data never arrives "in one piece": "512" arrives as three separate bytes, about 1 millisecond apart. That's why we collect the bytes in a buffer and convert them to a number when the line end arrives. Second, the code never waits: functions like readString() or parseInt() wait 1 second by default when no data comes and slow your program down. Reading bytes as they arrive is the most reliable and fastest approach. We also limit the buffer size so corrupted data can't be written past the end of the array.

Two-way communication: command and reply

Real projects usually need a two-way conversation: one board sends a command, the other carries it out and replies. In the example below, board A works as a bridge: it forwards every character you type in the Serial Monitor to B and prints every character coming from B. Board B understands the commands "LED ON", "LED OFF" and "DURUM?" (status), replies to each one, and also reports on its own when the button on D2 is pressed. Don't forget to set the Serial Monitor line ending to Newline; B uses that character to know the command has ended.

C++ (Arduino IDE)
1#include <SoftwareSerial.h>
2
3SoftwareSerial link(10, 11);    // RX = 10, TX = 11
4
5void setup() {
6  Serial.begin(9600);
7  link.begin(9600);
8  Serial.println("Komut yaz / type a command: LED ON, LED OFF, DURUM?");
9}
10
11void loop() {
12  if (Serial.available()) link.write(Serial.read());   // bilgisayar -> B kartı / computer -> board B
13  if (link.available()) Serial.write(link.read());     // B kartı -> bilgisayar / board B -> computer
14}
15
C++ (Arduino IDE)
1#include <SoftwareSerial.h>
2
3SoftwareSerial link(10, 11);    // RX = 10, TX = 11
4const int LED_PIN = 13;         // kart üstündeki LED / on-board LED
5const int BUTON_PIN = 2;        // buton GND'ye / button to GND
6
7String satir = "";
8int oncekiButon = HIGH;
9
10void komutCalistir(String k) {
11  k.trim();                                  // \r ve boşlukları at / strip \r and spaces
12  if (k == "LED ON") {
13    digitalWrite(LED_PIN, HIGH);
14    link.println("OK LED=1");
15  } else if (k == "LED OFF") {
16    digitalWrite(LED_PIN, LOW);
17    link.println("OK LED=0");
18  } else if (k == "DURUM?") {                // durum sorgusu / status query
19    link.print("LED=");
20    link.print(digitalRead(LED_PIN));
21    link.print(" BUTON=");
22    link.println(digitalRead(BUTON_PIN) == LOW ? 1 : 0);
23  } else {
24    link.print("HATA / unknown: ");
25    link.println(k);
26  }
27}
28
29void setup() {
30  link.begin(9600);
31  pinMode(LED_PIN, OUTPUT);
32  pinMode(BUTON_PIN, INPUT_PULLUP);
33  satir.reserve(32);
34}
35
36void loop() {
37  while (link.available()) {
38    char c = link.read();
39    if (c == '\n') {
40      komutCalistir(satir);
41      satir = "";
42    } else if (satir.length() < 31) {
43      satir += c;
44    }
45  }
46
47  int buton = digitalRead(BUTON_PIN);        // değişince kendiliğinden bildir / report changes unprompted
48  if (buton != oncekiButon) {
49    oncekiButon = buton;
50    link.println(buton == LOW ? "BUTON BASILDI / pressed" : "BUTON BIRAKILDI / released");
51    delay(20);                               // basit debounce / simple debounce
52  }
53}
54

The trim() call on board B removes the \r character that arrives when the Serial Monitor is set to "Both NL & CR", along with extra spaces, so commands are recognised with either setting. Using reserve(32) for the String object prevents memory from being reallocated every time a character is added and reduces fragmentation of the Uno's 2 KB of RAM. This structure is also the basis for talking to the Arduino from a Python program on your computer; details in the Arduino Python serial communication post.

The limits of SoftwareSerial

SoftwareSerial imitates a UART in software instead of hardware: the processor times every bit itself. That's very handy, but it has a cost. While transmitting, interrupts are disabled for the whole of each byte; at 9600 baud that's about 1 millisecond per byte, during which millis() can drift, servo pulses can jitter and data arriving on another serial port can be lost. Only one SoftwareSerial port can listen at a time; if you open a second one, you have to choose which one listens with listen(). On boards like the Mega and Leonardo, only certain pins with interrupt support can be used as RX.

FeatureHardware UART (Serial)SoftwareSerialAltSoftSerial
Pins on the UnoD0 (RX), D1 (TX), shared with USBAlmost any pin (PCINT for RX)Fixed: D8 (RX), D9 (TX)
Highest reliable speed1–2 Mbaud57600 baud (115200 is marginal)Stable at moderate speeds (9600–38400)
CPU loadVery low (hardware buffer)High, interrupts off while sendingMedium, uses Timer1
Send and receive at the same timeYesNoYes
More than one port4 on the MegaOnly one listens at a timeSingle port
ConflictsUploading and Serial MonitorTiming-sensitive libraries like Servo, NeoPixelD10 PWM and Servo (Timer1)
If you'll use more than one UART module (e.g. GPS + Bluetooth), choose an Arduino Mega with four hardware UARTs (Serial1–Serial3 in addition to Serial) or an ESP32 with three. On the Leonardo and Pro Micro, USB is separate, so Serial1 on D0/D1 is completely free.

Matching 5V and 3.3V levels

In UART, the "HIGH" level is the board's operating voltage: 5 V on the Arduino Uno, 3.3 V on the ESP32, Raspberry Pi Pico, ESP-01 and most newer modules. Connecting a 5 V TX output directly to a 3.3 V RX input exposes the input to overvoltage and damages the pin over time or immediately; the ESP32 and the RP2040-based Pico are not 5 V tolerant. The other direction is usually fine: the Uno treats 3.0 V and above as HIGH, so it can read a 3.3 V TX, but because that's close to the limit, a level shifter is more robust on long wires.

ConnectionWhat you needExample
Uno TX (5 V) → ESP32 / Pico RX (3.3 V)Voltage divider or level shifter1 kΩ in series + 2 kΩ to GND: 5 × 2/3 ≈ 3.3 V
ESP32 / Pico TX (3.3 V) → Uno RX (5 V)Usually directThe Uno counts above 3.0 V as HIGH
Uno TX → HC-05 RXVoltage divider recommendedThe HC-05 RX is a 3.3 V level input
Uno TX → SIM800L RXVoltage dividerThe SIM800L signal level is around 2.8 V
Both boards 5 V or both 3.3 VDirectUno ↔ Nano, ESP32 ↔ Pico
A voltage divider is only suitable for one-way, low-to-medium speed signals (e.g. up to 115200 baud). For bidirectional or very fast lines, use a BSS138-based bidirectional logic level shifter. You can work out divider resistor values with the voltage divider calculator; level shifters are covered in detail in the logic level shifter post.

ESP32 UART: Serial, Serial1 and Serial2

The ESP32 has three hardware UARTs, and the best part is that thanks to its GPIO matrix the UART pins can be moved to almost any pin you like. UART0 (Serial) is connected to the USB-serial chip and used for the Serial Monitor. UART2 (Serial2) sits on GPIO 16 (RX) and 17 (TX) by default. UART1's old default pins (GPIO 9 and 10) are connected to the board's flash memory and can't be used directly, so always start Serial1 with pins: begin(baud, SERIAL_8N1, RX, TX). The code below builds a two-way bridge between the Serial Monitor and a device on UART2 (a GPS, another Arduino, a sensor); it's ideal for watching a GPS module's NMEA sentences.

UARTArduino nameDefault RX / TXNote
UART0SerialGPIO 3 / GPIO 1Connected to the USB-serial chip, uploading and Serial Monitor
UART1Serial1GPIO 9 / GPIO 10 (flash, don't use)Pass other pins in begin(), e.g. 25 / 26
UART2Serial2GPIO 16 / GPIO 17On WROVER boards 16/17 are used by PSRAM, pick other pins
C++ (Arduino IDE)
1// ESP32: UART0 = Serial (USB), UART2 = Serial2 (GPIO 16/17)
2const int RXD2 = 16;            // karşı cihazın TX'i buraya / the other device's TX goes here
3const int TXD2 = 17;            // karşı cihazın RX'ine / to the other device's RX
4
5void setup() {
6  Serial.begin(115200);                          // USB Seri Monitör / USB Serial Monitor
7  Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);   // baud, çerçeve, RX, TX / baud, frame, RX, TX
8  // UART1'i de istediğin pinlere taşıyabilirsin / UART1 can be moved to any free pins:
9  // Serial1.begin(115200, SERIAL_8N1, 25, 26);
10  Serial.println("UART2 hazir / ready");
11}
12
13void loop() {
14  while (Serial2.available()) Serial.write(Serial2.read());   // UART2 -> USB
15  while (Serial.available()) Serial2.write(Serial.read());    // USB -> UART2
16}
17

The ESP32's UARTs can go up to 5 Mbaud and each has a hardware buffer; the default receive buffer is 256 bytes and can be enlarged before begin() with a call like Serial2.setRxBufferSize(1024). This matters for GPS modules that send dozens of sentences a second or GSM modules with long replies. Since the ESP32's pins are 3.3 V, don't forget the divider from the previous section when connecting a 5 V Arduino. You can see the ESP32 pinout in the Pinout tool.

Raspberry Pi Pico UART pins and MicroPython

The Raspberry Pi Pico has two hardware UARTs (uart0 and uart1), and each can be routed to several different pin pairs. In MicroPython, the REPL and print() output come over a separate virtual serial port on USB, so UART0 is completely free; there's no need to unplug wires when uploading code as on the Arduino. The Pico's pins are 3.3 V and on the RP2040-based Pico they are not 5 V tolerant. The code below does on the Pico what board B did on the Arduino: it reads lines from UART0, turns the on-board LED on or off with the "LED ON" and "LED OFF" commands and replies. If you want it to talk to the Uno bridge sketch, don't forget a voltage divider on the Uno's TX line.

UARTTX pinsRX pinsMicroPython example
uart0GP0, GP12, GP16GP1, GP13, GP17UART(0, 9600, tx=Pin(0), rx=Pin(1))
uart1GP4, GP8GP5, GP9UART(1, 9600, tx=Pin(4), rx=Pin(5))
MicroPython
1from machine import UART, Pin
2import time
3
4uart = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1))   # UART0: GP0 = TX, GP1 = RX
5led = Pin("LED", Pin.OUT)                             # Pico ve Pico W dahili LED / on-board LED
6
7tampon = b""
8while True:
9    if uart.any():
10        tampon += uart.read()                         # gelen baytlar / incoming bytes
11        while b"\n" in tampon:                        # her tam satırı işle / handle each full line
12            satir, tampon = tampon.split(b"\n", 1)
13            try:
14                komut = satir.decode().strip()
15            except UnicodeError:
16                continue                              # bozuk bayt: atla / corrupt byte: skip
17            if komut == "LED ON":
18                led.on()
19                uart.write("OK LED=1\n")
20            elif komut == "LED OFF":
21                led.off()
22                uart.write("OK LED=0\n")
23            else:
24                uart.write("HATA / unknown: " + komut + "\n")
25    if len(tampon) > 64:                              # satır sonu hiç gelmezse taşmayı önle / avoid overflow
26        tampon = b""
27    time.sleep_ms(5)
28

Packet framing: start and end characters

UART is a stream of bytes; it doesn't know where anything starts or ends. When the sender writes "23.5,48,812", the receiver may have started listening halfway through, a byte may have been lost to noise, or two readings may run together. The solution is to turn the data into a packet: if every packet starts with "<" and ends with ">", the receiver ignores everything until the first "<", processes the packet when ">" arrives and throws away any half packets in between. All the sender has to do is print the values between Serial.print('<'), commas and Serial.print('>'). You can try the receiver code below straight away by typing <23.5,48,812> in the Serial Monitor.

C++ (Arduino IDE)
1// Paket biçimi / packet format: <sicaklik,nem,isik>  ör. / e.g. <23.5,48,812>
2const byte MAKS = 32;
3char tampon[MAKS];
4byte konum = 0;
5bool paketIcinde = false;                    // '<' görüldü mü? / have we seen '<'?
6
7void paketiIsle(char* p) {
8  char* parca = strtok(p, ",");              // ilk alan / first field
9  if (!parca) return;
10  float sicaklik = atof(parca);
11  parca = strtok(NULL, ",");
12  if (!parca) return;                        // eksik alan: paketi at / missing field: drop
13  int nem = atoi(parca);
14  parca = strtok(NULL, ",");
15  if (!parca) return;
16  int isik = atoi(parca);
17
18  Serial.print("Sicaklik / temp: ");
19  Serial.print(sicaklik, 1);
20  Serial.print("  Nem / humidity: ");
21  Serial.print(nem);
22  Serial.print("  Isik / light: ");
23  Serial.println(isik);
24}
25
26void setup() {
27  Serial.begin(9600);                        // Seri Monitör'den <23.5,48,812> yazarak dene / test from the Serial Monitor
28}
29
30void loop() {
31  while (Serial.available()) {
32    char c = Serial.read();
33    if (c == '<') {                          // yeni paket başladı / a new packet starts
34      paketIcinde = true;
35      konum = 0;
36    } else if (c == '>' && paketIcinde) {    // paket bitti / packet complete
37      tampon[konum] = '\0';
38      paketIcinde = false;
39      paketiIsle(tampon);
40    } else if (paketIcinde) {
41      if (konum < MAKS - 1) tampon[konum++] = c;
42      else paketIcinde = false;              // çok uzun, bozuk: at / too long, corrupt: drop
43    }                                        // paket dışındaki baytlar yok sayılır / bytes outside packets are ignored
44  }
45}
46
MethodExampleProsCons
Text with line endings512\nSimplest, readable in the Serial MonitorMid-line you can't tell where it starts
Start and end characters<23.5,48,812>Recovers by itself after losing syncCan't use < and > inside the data
Text with checksum (like NMEA)$GPGGA,...*47Corrupted packets are detectedA bit more calculation and code
Binary packet0xAA, length, data, CRCShortest and fastestHard to read and debug

In noisy environments or with long wires, you can go a step further and add a checksum to the packet. The NMEA format used by GPS modules is a good example: all characters between "$" and "*" are XORed together and the result is written at the end as a two-digit hexadecimal number. The receiver does the same calculation and drops the packet if it doesn't match. That protects you from sneaky errors such as a corrupted "812" being processed as "872".

The sender must not be faster than the receiver can process. The Arduino Uno's hardware receive buffer is only 64 bytes; if you use long delay() calls in loop(), incoming bytes are lost once the buffer fills. On the receiver, use millis() instead of delay() and check Serial.available() on every pass of the loop.

UART project ideas

  • Phone control: the HC-05 Bluetooth module is really a wireless UART; control an LED, a relay or a robot car from your phone with the HC-05.
  • GPS logging: parse the NMEA sentences a NEO-6M GPS module sends over UART and write the position to an SD card.
  • Two-board system: connect an Uno that reads the sensors to an ESP32 that sends the data to the internet over Wi-Fi, via UART.
  • SMS alarm: send AT commands to a SIM800L module over UART and text yourself when a door opens.
  • Computer control panel: send commands to the Arduino from a desktop app written in Python and plot the sensor data.
  • MP3 player: choose tracks, set the volume and play voice alerts over UART with a DFPlayer Mini.

Common mistakes and fixes

SymptomLikely causeFix
Garbage characters in the Serial MonitorBaud rates don't matchUse the same baud rate in the code and the Serial Monitor
No data at allTX wired to TX, RX to RXCross the wires: TX → RX, RX → TX
Random corrupted charactersGND not connectedConnect the grounds of both boards
Upload fails (not in sync)A module is connected to D0/D1Unplug the wires while uploading or use SoftwareSerial
Commands aren't recognisedLine ending is \r\n or missingSet the Serial Monitor to Newline and clean the text with trim()
The end of long messages goes missingThe 64-byte buffer overflowedAvoid delay(), read data as it arrives, lower the rate
ESP32 RX pin doesn't work or is damaged5 V TX connected directlyAdd a voltage divider or level shifter
SoftwareSerial fails at 115200Software timing can't keep upUse 57600 or lower, or switch to a hardware UART
If a UART module doesn't answer, first connect it directly to the computer with a USB-TTL converter and send commands from a serial terminal. If the module works there, the problem is in the Arduino code or wiring; if it doesn't work there either, check the baud rate, the power supply and the module itself.

UART is the first communication method to learn in electronics: two wires, a common GND and the same baud rate are all it takes, and when set up correctly it works for years without trouble. Once you've got crossover wiring, level matching and packet framing down, you can use dozens of modules from GPS to GSM with ease. In the BlueGrays app you'll find step-by-step projects with circuit diagrams and code for one-way and two-way communication between two Arduinos, lighting an LED from your phone over Bluetooth and a Bluetooth-controlled car.

Frequently asked questions

What does UART stand for and what is it used for?

UART stands for Universal Asynchronous Receiver-Transmitter. It lets two devices exchange data byte by byte without a clock line, using only TX and RX wires and a baud rate agreed in advance. The Arduino Serial Monitor, GPS, Bluetooth and GSM modules all use UART.

How do you connect TX and RX?

One device's TX pin goes to the other's RX pin, and its RX pin to the other's TX pin, so the connection is crossed. The GND pins of both devices must also be connected. Connecting TX to TX blocks the data completely.

What is baud rate and which one should I choose?

Baud rate is the number of bits sent per second and must be the same on both sides. The most common values with Arduino modules are 9600 and 115200; 9600 is the default for most GPS and Bluetooth modules, while 115200 is preferred for the ESP32 and fast output. Start with the default rate in the module's documentation.

Is UART the same as a serial port?

In everyday use they often mean the same thing. UART is the hardware unit that serialises data; a serial port is the COM or /dev/tty connection on a computer. RS-232 and RS-485 are standards that use different voltage levels to carry UART signals over longer distances.

How can I use more than one UART on an Arduino?

The Uno has a single hardware UART shared with USB; for extra ports you use SoftwareSerial, but only one can listen at a time. The Arduino Mega has three extra hardware UARTs (Serial1, Serial2 and Serial3), the ESP32 has three in total and the Raspberry Pi Pico has two.

How long can a UART cable be?

TTL-level UART at 3.3 V or 5 V works reliably up to a few metres at low baud rates, and the distance shrinks as the speed goes up. For longer runs, RS-485 transceivers (such as the MAX485) reach hundreds of metres, and RS-232 about 15 metres.

See all projects
One-Way Communication Between 2 Arduinos
#57Intermediate
Premium

One-Way Communication Between 2 Arduinos

You get two Arduinos talking over a single wire: press the button on one and the LED on the other lights up, press it again and it goes off. The sending board reports the button press with a short mes

4.4(37)
50
Two-Way Communication Between 2 Arduinos
#58Intermediate
Premium

Two-Way Communication Between 2 Arduinos

Two Arduinos are wired together and talk to each other: press the button on one and the light on the other comes on, press it again and it goes off. Both boards run the same program, so each one both

4.5(20)
26
Control an LED from Your Phone via Bluetooth
#34Beginner
Premium

Control an LED from Your Phone via Bluetooth

A small rehearsal for switching the lights in your room on and off from your phone. The Bluetooth module passes the letters sent from your phone to the Arduino; one letter turns on the red LED, anothe

4.8(68)
89
Bluetooth Controlled Car with L298N
#80Advanced
Premium

Bluetooth Controlled Car with L298N

Turn your phone into a remote and drive your own robot car. The Arduino receives commands from a remote-control app on your phone through a Bluetooth module and spins the wheels with a motor driver; t

5.0(38)
65
Python Developer Keyboard
#66Intermediate
Premium

Python Developer Keyboard

If you are tired of typing the same patterns over and over while coding, this little keyboard is made for you. The Arduino Leonardo connects to your computer just like a keyboard, and each key on the

4.6(35)
45
HC-SR04 Distance Measurement (Serial Display)
#27Beginner

HC-SR04 Distance Measurement (Serial Display)

The HC-SR04 sensor works just like a bat finding its way in the dark: it sends out a sound you cannot hear, listens for the echo and measures how far away the object in front of it is. The Arduino wri

4.6(46)
71
Temperature and Humidity with DHT11
#55Intermediate

Temperature and Humidity with DHT11

How warm is your room, and is the air humid or dry? The DHT11 sensor measures temperature and humidity, and the Arduino writes the values on a two-line LCD screen, refreshing every couple of seconds.

4.7(47)
73
Reading Light Intensity with the ESP32 LDR Module
#113Beginner

Reading Light Intensity with the ESP32 LDR Module

Real-time monitoring of the LDR module's raw ADC value and relative light percentage calculated from dark and light reference values in the Serial Monitor. This percentage is not a lux measurement. Teaches the module’s calibration logic. It is ideal to complete this before Project 10, as it is used to determine the threshold value.

4.6(36)
55