UART and Serial Links
| Difficulty | Intermediate |
|---|---|
| Category | Concepts |
| Supported boards | Arduino Uno R3 and compatible ATmega328P boards |
| Time needed | 45 minutes |
| Prerequisites | SPI, Faster and More Wires |
| Tested board | Not yet verified |
| Tested on | Not yet verified |
| Arduino IDE version | Not yet verified |
| Library | Not yet verified |
What this covers
The third bus, and the oldest. I2C and SPI were designed for chips on one board; a UART link is two wires between two independent devices, each with its own idea of the time.
It is how the Bluetooth module, the GPS receiver and the SMS module all talk to an Arduino, and it is the same mechanism as the serial monitor you have been using since your first sketch. Those two facts collide on an Uno in a way that catches everybody once.
Before you begin
Nothing to buy. Seeing What Your Program Is Doing and SPI, Faster and More Wires are the pages either side of this one.
How it works
Two wires, crossed:
| Wire | Means | Goes to |
|---|---|---|
TX |
Transmit | The other device’s RX |
RX |
Receive | The other device’s TX |
That crossing is the thing to remember. I2C’s SDA goes to SDA; SPI’s MOSI goes to MOSI; UART’s TX goes to RX. Wiring TX to TX connects two outputs together and nothing is ever received.
There is no clock wire. That is the whole design and the source of everything else on this page.
No clock means both ends must agree in advance
With no clock to follow, the receiver has to know how fast bits will arrive before any arrive. It starts its own timer when the line first drops, and samples at intervals it decides.
So both ends must be set to the same baud rate — bits per second. 9600 means a bit every 104 microseconds, and the receiver counts in those.
Get it wrong and you do not get an error. You get plausible-looking rubbish, because the receiver is faithfully sampling a signal at the wrong moments. Characters like ÿ and ? and random punctuation are the signature, and if you see them the first thing to check is the number in Serial.begin() against the number in the monitor’s corner.
Common rates, and what they are for
| Rate | Where it turns up |
|---|---|
| 9600 | The library’s default, and most modules out of the box |
| 38400 | HC-05 modules in command mode |
| 57600 | Some GPS receivers |
| 115200 | ESP32 and ESP8266 boot messages, fast logging |
Faster is not better here. At 115200 on a long wire, or through a breadboard with poor contacts, errors appear that 9600 would not have produced. Use the rate the module wants, and raise it only when you need the throughput.
The Uno has exactly one UART, and the USB cable is using it
This is the paragraph the page exists for.
D0 and D1 are the ATmega328P’s hardware serial pins. They are also wired to the USB chip that carries the serial monitor. One UART, two jobs.
The consequences are immediate and confusing if you have not been told:
- Anything plugged into
D0andD1fights the USB connection. Both talk at once and both are corrupted. - Uploading a sketch fails while a module is connected to those pins, because the upload is itself a serial conversation. The error mentions
avrdudeandstk500and says nothing about your wiring. - The serial monitor shows the module’s output mixed with your own prints, which looks like a software fault and is not.
So: do not put a module on D0 and D1 on an Uno. Use the pins for the USB cable, and give the module a software serial port instead.
A Mega has four hardware UARTs and an ESP32 has three, which is one of the real reasons to move a project to a bigger board. Uno, Nano, Mega, Pro Mini or ESP32 covers that choice.
SoftwareSerial, and what it costs
SoftwareSerial makes any two digital pins behave like a serial port, by bit-banging the timing in software.
// Siratek: a module on pins 10 and 11, with the USB serial left alone.
#include <SoftwareSerial.h>
// SoftwareSerial(RX, TX) - named from the ARDUINO's point of view.
// So the Arduino's RX pin goes to the module's TX pin.
SoftwareSerial module(10, 11);
void setup() {
Serial.begin(9600); // the USB cable, to your screen
module.begin(9600); // the module, on pins 10 and 11
Serial.println(F("Siratek: anything the module says appears below."));
}
void loop() {
// Module to screen.
while (module.available()) {
Serial.write(module.read());
}
// Screen to module, so you can type commands at it.
while (Serial.available()) {
module.write(Serial.read());
}
}
That sketch is a serial bridge, and it is the single most useful twenty lines in this part of the library. It lets you talk to an HC-05, a SIM800L or a GPS receiver by typing at it, before you write any logic at all. Every one of those tutorials starts by running something like it.
Note the argument order once, because it is the other half of the crossing: SoftwareSerial(RX, TX) is named from the Arduino’s side, so pin 10 is the Arduino’s receive pin and must be wired to the module’s transmit pin.
The three limits
It is not reliable much above 38400. The bit timing is done by counting instructions, and at high rates there is not enough time between bits to be sure of anything. 9600 is solid, 38400 is usually fine, 115200 is not.
Only one software port can listen at a time. You may create several, but only the one you last called .listen() on receives. Two GPS receivers on two software ports will not both work.
It blocks interrupts while sending or receiving. A byte at 9600 baud takes about a millisecond, and during it millis() is not counting and your interrupts are not firing. In a sketch doing careful timing, that matters — Responding Immediately with Interrupts is where this collides with servos and tone().
If any of those bite, the answer is a board with more hardware UARTs rather than a cleverer library.
3.3 volts, again
A UART’s idle state is the line held high, at whatever that device calls high. An Uno’s high is 5 V; an ESP32’s, a GPS module’s and most SIM modules’ is 3.3 V.
The direction that matters is Arduino TX to module RX: 5 V going into a 3.3 V input. The other direction is usually fine, because 3.3 V is high enough for the Uno to read as HIGH.
So the common fix is one divider or one converter channel, on one wire. Mixing 3.3 V and 5 V Safely has the detail, and the module pages say which of them need it.
Check yourself
Your module is on D0 and D1 and the upload fails. Why? The upload is a serial conversation on those same pins.
You see ÿ?ÿ in the monitor. The baud rates do not match.
SoftwareSerial module(10, 11) — which pin goes to the module’s TX? Pin 10, the Arduino’s receive pin.
Troubleshooting
| Symptom | Likely cause | Check or fix |
|---|---|---|
| Nothing received at all | TX to TX |
The two wires cross |
| Rubbish characters | Baud rates differ | Both ends, same number |
Upload fails, avrdude error |
A module on D0/D1 |
Unplug it to upload, then move it |
| Monitor shows two sets of output mixed | Module on the USB UART | Use SoftwareSerial on other pins |
| Works at 9600, fails at 115200 | SoftwareSerial’s limit | Stay at or below 38400 |
| Two software ports, only one works | Only the listening one receives | .listen(), or a bigger board |
| Servo jitters while data arrives | SoftwareSerial blocks interrupts | Hardware UART, or separate the timing |
| Module ignores commands | Missing line ending | Many want carriage return and newline |
| Module responds once, then stops | Buffer overrun, 64 bytes | Read it more often than it fills |
| 3.3 V module behaving oddly | 5 V on its RX |
A converter on that one wire |
Next steps
- Easier first: Seeing What Your Program Is Doing, which is this bus without the wiring.
- Related: Mixing 3.3 V and 5 V Safely, which nearly every UART module needs.
- Use it: Bluetooth Serial with an HC-05, Reading Position with a NEO-6M and Sending SMS Alerts with a SIM800L are three modules, one bus and three sets of commands.
Sources and attribution
All text and code in this tutorial are original Siratek work.
- The single hardware UART on the ATmega328P, its sharing with the USB interface on
D0andD1, the 64-byte receive buffer and the bit timing are properties of the chip and the Arduino core described in their documentation. - The SoftwareSerial limits — one listening port, interrupt blocking, and unreliability at high rates — are documented behaviour of that library.
Baud rates for particular modules are the usual defaults rather than measurements, and each module tutorial gives the figure for its own part. No third-party tutorial text, diagrams, photographs or code has been reproduced here.

