Arduino Serial Communication
By Flavio
Tagged: Arduino
How Arduino serial works: set a baud rate, print and read bytes in the Serial Monitor, and fix garbage output or a sketch that never starts.
To communicate between Arduino and other devices we can use serial communication. This is often used to “talk” to a computer, to visualize data coming from the Arduino, and to debug your projects. Or to communicate with other devices, too.
Arduino comes with the built-in Serial library. You don’t need to import anything special to use it. For libraries you install yourself, see Arduino libraries.
The USB connection we make between the computer and the Arduino to upload the program to the device is automatically set up to handle serial communication, so we can send and receive messages through the Arduino IDE.
Let’s now see how to send data through the serial.
In the setup() function of an Arduino program you need to initialize the serial communication by setting the data rate, expressed in bauds, for serial data transmission. Typically 9600:
Serial.begin(9600);
bauds = bits per second
9600 is the usual rate in examples. 115200 is the other one you will see when a sketch prints a lot. The second argument of begin() sets the framing. The default is SERIAL_8N1, which is 8 data bits, no parity, and 1 stop bit. Leave it alone unless the device on the other end asks for something else, such as even parity.
Then you can use one of the several Serial functions.
For example Serial.print():
Serial.print("Hello!");
print() sends human-readable text. Serial.print(78) sends the characters 7 and 8, not the byte 78.
Let’s try it by compiling and uploading this program to the Arduino:
void setup() {
Serial.begin(9600);
Serial.print("Hello!");
}
void loop() {
}
Save and upload the program to the Arduino, then click the top-right button “Serial Monitor” in the Arduino IDE:

The button will open the serial interface monitor on the computer. In IDE 2 that same button opens the monitor as a panel, and the baud menu is in the panel. The screenshots here are the older window. Make sure the menu matches the rate in the program, and you should see the “Hello!” string being printed:

When the monitor shows garbage
On an Uno, a Nano, or a Mega, USB goes through a serial adapter chip, and that chip uses the baud rate from Serial.begin(). If the sketch says 9600 and the monitor says 115200, the bytes are still arriving, and they decode as the wrong characters. You get garbage. Match the two numbers.
On a board with native USB, such as a Leonardo, a Micro, or an MKR, Serial is the USB port itself. For that port Serial.begin() does not set a bus speed. Any rate you pass is fine, and a mismatch with the monitor menu is not what produces garbage. Serial1 on those boards is a real UART. The baud rate matters there, because the other device is clocking bits on the wire.
Serial.print() is just one of the functions you can run.
You also have Serial.println() that writes something and adds a \n line terminator, so every message is printed on its own line.
You can also send messages to the Arduino from this serial interface monitor. See the input box with the Send button on top?
In the Arduino program you can use Serial.read(). Characters are encoded using ASCII, and if you run this program:
void setup() {
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0) {
Serial.print(Serial.read());
}
}
You will see in the serial interface printed back the ASCII decimal value of the characters you send. read() returns the byte as a number, and print() then prints that number in decimal. Try writing test and hit Send:

It’s best to use Serial.println() to have each number printed on a single line:

116 is the ASCII encoding for t
101 is the ASCII encoding for e
115 is the ASCII encoding for s
And 10 is the new line character.
The monitor can append that newline for you. Its line-ending menu is usually Newline, Carriage return, Both NL & CR, or No line ending. test with Newline selected is the five bytes above. No line ending, and the 10 disappears.
See my list of printable and non-printable ASCII codes.
To print the character instead of the ASCII encoding, use the Serial.write() function:
void setup() {
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0) {
Serial.write(Serial.read());
}
}

write() sends the byte as itself. print() would have turned it into decimal digits. That is the whole difference between the two sketches.
Serial.available() makes sure there’s data ready to be read. It returns the number of bytes available to read (0 when the buffer is empty). The receive buffer holds 64 bytes. Serial.read() returns -1 when there’s no data, so if we call it without this check we’ll just see a bunch of -1 repeated in the serial monitor.
Bytes that arrive after the buffer is full are thrown away. If a sketch prints in loop() and only sometimes reads, a fast sender will lose the middle of a message. Read as the bytes come in.
write() and print() can also stall. If the transmit buffer has room, they return before the bits have left the board. If the buffer is full, they wait. A Serial.println() on every pass of loop(), with no delay(), fills that buffer, the print waits, and the rest of the sketch barely runs. Print on a timer, or check Serial.availableForWrite() before you write.
Read a whole line
read() is one byte. For a line typed in the monitor, read until the newline:
void setup() {
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0) {
String line = Serial.readStringUntil('\n');
line.trim();
Serial.print("You sent: ");
Serial.println(line);
}
}
readStringUntil() stops at the terminator and does not include it. trim() drops the \r you still have when the monitor is set to Both NL & CR.
It also stops when time runs out. The timeout defaults to 1000 milliseconds, and Serial.setTimeout() changes it. If Send does not append a newline, the call sits for that long and then returns whatever arrived. Set the menu to Newline so Send ends the line. readString(), parseInt(), and readBytes() use the same timeout.
Wait for the port, on some boards
On boards with native USB, if (Serial) is false until the computer has opened the port. On other boards, and on ports that are not USB (Serial1 and the rest), it is always true.
void setup() {
Serial.begin(9600);
while (!Serial) {
;
}
}
That loop is how a Leonardo sketch waits so the first println() is not lost. Each check also waits 10 milliseconds, so don’t call if (Serial) on every pass of loop() once you are running.
Leave the loop out of a sketch that must run with no computer attached. On a Leonardo, a Micro, or an MKR it will sit in setup() forever if you never open the monitor. On an Uno the check is always true, so the loop does not wait.
Since Arduino 1.0, Serial.flush() waits until outgoing bytes have been sent. It does not throw away unread input. Older tutorials that call flush() to empty the receive buffer are describing the behavior from before 1.0.
There are more functions you can use in the Serial object.
Check them all in details here: https://docs.arduino.cc/language-reference/en/functions/communication/serial/.
On older boards (Uno, Nano, Mini, and Mega), pins 0 and 1 are shared with the USB connection. Don’t use them for I/O while you use Serial over USB, or you might have interferences in your data. On MKR boards (like the MKR WiFi 1010), USB serial is separate. Pins 13 (RX) and 14 (TX) are Serial1, a second hardware port you use to talk to other devices.
Want me to talk about your product? You can sponsor this site.
Related posts about electronics: