Arduino UART Serial Communication
Introduction
Serial communication is one of the most fundamental ways your Arduino can talk to other devices. UART (Universal Asynchronous Receiver/Transmitter) is the hardware that enables this communication. In this tutorial, we'll explore how UART Serial communication works on Arduino, why it's important, and how to use it in your projects.
When you connect your Arduino to your computer via USB, you're actually using Serial communication! This makes it not just a theoretical concept but a practical tool you use every time you upload code or debug your projects.
What is UART Serial Communication?
UART Serial is a form of asynchronous communication where data is transmitted bit by bit over a single wire, without an accompanying clock signal. Let's break down what this means:
- Universal: It's a standard protocol that works across different devices
- Asynchronous: No clock signal is shared between devices; instead, both sides agree on a speed (baud rate)
- Receiver/Transmitter: Data flows in both directions
A typical UART connection uses two wires for data:
- TX (Transmit): Sends data from your Arduino to another device
- RX (Receive): Receives data from another device to your Arduino
How Serial Communication Works on Arduino
Arduino boards come with at least one hardware UART. The Arduino Uno has one UART on pins 0 (RX) and 1 (TX), while other boards like the Mega have multiple UARTs.
When you use the Serial functions in Arduino, you're interacting with this hardware. Here's how the Arduino handles Serial communication:
- Initialization: Set the baud rate (communication speed)
- Data Packaging: Adds start/stop bits to each byte
- Transmission/Reception: Sends or receives one bit at a time
- Buffer Management: Stores incoming data until your code reads it
Each byte sent includes:
- 1 start bit (always 0)
- 8 data bits (the actual information)
- 1 or more stop bits (always 1)
This packaging ensures both devices stay synchronized during communication.
Setting Up Serial Communication
Let's start with the basics. Here's how to initialize and use Serial communication:
void setup() {
// Initialize serial communication at 9600 bits per second
Serial.begin(9600);
// Wait for serial port to connect (needed for native USB port only)
while (!Serial) {
; // wait for serial port to connect
}
Serial.println("Serial communication started!");
}
void loop() {
// Your code here
}
Common baud rates include 9600, 57600, and 115200. Higher rates allow faster communication but may be less reliable over longer distances or with cheaper hardware.
Sending Data from Arduino
Sending data is straightforward using the various Serial print functions:
void setup() {
Serial.begin(9600);
}
void loop() {
// Send a string
Serial.println("Hello from Arduino!");
// Send numbers
Serial.print("Integer: ");
Serial.println(42);
// Send decimals with specified precision
Serial.print("Float with 2 decimals: ");
Serial.println(3.14159, 2); // Outputs: 3.14
// Send data in different formats
Serial.print("Binary: ");
Serial.println(42, BIN); // Outputs: 101010
Serial.print("Hexadecimal: ");
Serial.println(42, HEX); // Outputs: 2A
delay(1000); // Wait a second between outputs
}
Receiving Data on Arduino
Receiving data is a bit more complex because you need to check if data is available and then read it properly:
const int ledPin = 13; // Built-in LED
void setup() {
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
Serial.println("Type '1' to turn LED ON or '0' to turn LED OFF");
}
void loop() {
// Check if data is available
if (Serial.available() > 0) {
// Read the incoming byte
char incomingByte = Serial.read();
// Echo the incoming character
Serial.print("Received: ");
Serial.println(incomingByte);
// Act based on the character received
if (incomingByte == '1') {
digitalWrite(ledPin, HIGH);
Serial.println("LED ON");
}
else if (incomingByte == '0') {
digitalWrite(ledPin, LOW);
Serial.println("LED OFF");
}
// Clear any additional characters in buffer
while (Serial.available() > 0) {
Serial.read();
}
}
}
Handling Larger Amounts of Data
For more complex scenarios, you might need to read entire strings or numbers:
void setup() {
Serial.begin(9600);
Serial.println("Enter a value (ending with newline):");
}
void loop() {
// Read a full line when available
if (Serial.available() > 0) {
String receivedString = Serial.readStringUntil('
');
// Trim any whitespace
receivedString.trim();
// Convert string to integer if needed
int receivedValue = receivedString.toInt();
// Echo it back
Serial.print("You sent: ");
Serial.println(receivedString);
Serial.print("As integer: ");
Serial.println(receivedValue);
Serial.println("Enter another value:");
}
}
Serial Communication Between Two Arduinos
You can connect two Arduinos together using their UART pins. Connect the TX pin of one Arduino to the RX pin of the other and vice versa:
Here's an example for the sender Arduino:
void setup() {
Serial.begin(9600);
}
void loop() {
// Send a counter value
static int counter = 0;
Serial.println(counter);
counter++;
delay(1000); // Send once per second
}
And for the receiver Arduino:
void setup() {
Serial.begin(9600);
// Initialize a second serial port for debugging
// (if available on your Arduino model)
Serial1.begin(9600);
// Otherwise, comment out Serial1 lines and use LEDs or other outputs to verify
}
void loop() {
if (Serial.available() > 0) {
String receivedString = Serial.readStringUntil('
');
// Do something with the received data
int value = receivedString.toInt();
// Print to debug serial port
Serial1.print("Received: ");
Serial1.println(value);
// Alternative: Blink an LED 'value' times
}
}
Note: On Arduino Uno, which has only one hardware UART, you'll need to disconnect the RX/TX pins from the other Arduino during programming, as they're also used for USB communication with your computer.
Advanced Serial Techniques
Software Serial
If you need more UART ports than your Arduino provides in hardware, you can use the SoftwareSerial library to create additional serial ports on other digital pins:
#include <SoftwareSerial.h>
// Create a software serial port on pins 10 (RX) and 11 (TX)
SoftwareSerial mySerial(10, 11);
void setup() {
// Initialize hardware serial for debugging
Serial.begin(9600);
// Initialize software serial for communication
mySerial.begin(9600);
Serial.println("Software Serial Example");
}
void loop() {
// Forward data from software serial to hardware serial
if (mySerial.available()) {
Serial.write(mySerial.read());
}
// Forward data from hardware serial to software serial
if (Serial.available()) {
mySerial.write(Serial.read());
}
}
Software Serial has limitations compared to hardware UART:
- Lower maximum baud rate
- Can miss data when receiving at high speeds
- May interfere with other timing-sensitive operations
Parsing Complex Data
For complex data protocols, you'll want to parse the incoming stream into structured data:
// Example: Parsing comma-separated values
void setup() {
Serial.begin(9600);
Serial.println("Send comma-separated values (format: x,y,z)");
}
void loop() {
if (Serial.available() > 0) {
String data = Serial.readStringUntil('
');
// Parse the comma-separated string
int firstComma = data.indexOf(',');
int secondComma = data.indexOf(',', firstComma + 1);
if (firstComma != -1 && secondComma != -1) {
String xStr = data.substring(0, firstComma);
String yStr = data.substring(firstComma + 1, secondComma);
String zStr = data.substring(secondComma + 1);
int x = xStr.toInt();
int y = yStr.toInt();
int z = zStr.toInt();
// Use the parsed values
Serial.print("Received x: ");
Serial.print(x);
Serial.print(", y: ");
Serial.print(y);
Serial.print(", z: ");
Serial.println(z);
Serial.print("Sum: ");
Serial.println(x + y + z);
}
}
}
Real-World Applications
1. Connecting to Sensors
Many sensors communicate using serial protocols. Here's an example with a GPS module:
#include <SoftwareSerial.h>
SoftwareSerial gpsSerial(3, 2); // RX, TX
void setup() {
// Open serial communications with computer
Serial.begin(9600);
// Open serial communications with GPS
gpsSerial.begin(9600);
Serial.println("GPS Reading Example");
}
void loop() {
// Read data from GPS and forward to Serial Monitor
if (gpsSerial.available()) {
char c = gpsSerial.read();
Serial.write(c);
}
}
2. Connecting to a Computer Interface
You can create interactive applications that respond to commands from a computer:
const int ledPin = 13;
void setup() {
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
Serial.println("Available commands:");
Serial.println(" ON - Turn on LED");
Serial.println(" OFF - Turn off LED");
Serial.println(" BLINK n - Blink LED n times");
Serial.println(" STATUS - Report LED status");
}
void loop() {
if (Serial.available() > 0) {
String command = Serial.readStringUntil('
');
command.trim();
command.toUpperCase();
if (command == "ON") {
digitalWrite(ledPin, HIGH);
Serial.println("LED turned ON");
}
else if (command == "OFF") {
digitalWrite(ledPin, LOW);
Serial.println("LED turned OFF");
}
else if (command.startsWith("BLINK ")) {
int times = command.substring(6).toInt();
Serial.print("Blinking LED ");
Serial.print(times);
Serial.println(" times");
for (int i = 0; i < times; i++) {
digitalWrite(ledPin, HIGH);
delay(200);
digitalWrite(ledPin, LOW);
delay(200);
}
Serial.println("Blinking complete");
}
else if (command == "STATUS") {
if (digitalRead(ledPin) == HIGH) {
Serial.println("LED is ON");
} else {
Serial.println("LED is OFF");
}
}
else {
Serial.println("Unknown command");
}
}
}
3. Data Logging System
Create a system that logs sensor readings to a computer:
const int sensorPin = A0; // Analog input pin
unsigned long lastLogTime = 0;
const unsigned long logInterval = 5000; // Log every 5 seconds
void setup() {
Serial.begin(9600);
Serial.println("Time(ms),Sensor Value");
}
void loop() {
unsigned long currentTime = millis();
// Log data at specified intervals
if (currentTime - lastLogTime >= logInterval) {
int sensorValue = analogRead(sensorPin);
// Print in CSV format
Serial.print(currentTime);
Serial.print(",");
Serial.println(sensorValue);
lastLogTime = currentTime;
}
// Check for commands
if (Serial.available() > 0) {
String command = Serial.readStringUntil('
');
command.trim();
if (command == "FASTER") {
logInterval = 1000; // Change to 1 second
Serial.println("# Logging speed increased to 1 second");
}
else if (command == "SLOWER") {
logInterval = 10000; // Change to 10 seconds
Serial.println("# Logging speed decreased to 10 seconds");
}
else if (command == "NORMAL") {
logInterval = 5000; // Reset to 5 seconds
Serial.println("# Logging speed set to 5 seconds");
}
}
}
Common Pitfalls and Solutions
1. Baud Rate Mismatch
If the baud rates don't match between devices, communication will fail or result in garbage data.
Solution: Always ensure both sides are configured for the same baud rate.
2. Buffer Overflow
The Arduino has limited buffer space for incoming Serial data. If you don't read it quickly enough, the buffer will overflow and you'll lose data.
Solution: Read data promptly and consider flow control mechanisms.
3. Voltage Level Incompatibility
Arduino operates at 5V (or 3.3V for some boards), but other devices might use different voltage levels.
Solution: Use level shifters when connecting to devices with different voltage requirements.
4. RX/TX Reversal
A common mistake is mixing up the RX and TX connections between devices.
Solution: Remember that RX on one device connects to TX on the other, and vice versa.
Summary
UART Serial communication is a fundamental skill for Arduino programmers. We've covered:
- The basics of how UART works
- Setting up serial communication
- Sending and receiving data
- Advanced techniques like Software Serial
- Real-world applications
- Common pitfalls and solutions
With these skills, you can now connect your Arduino to computers, sensors, displays, and other microcontrollers, opening up endless possibilities for your projects.
Exercises for Practice
-
Basic Communication: Write a program that echoes anything sent from the Serial Monitor back with "You said: " prefixed.
-
Command Parser: Create a program that accepts commands to control multiple LEDs (e.g., "LED1 ON", "LED2 OFF").
-
Data Logger: Connect a temperature sensor to your Arduino and log readings to your computer at regular intervals.
-
Two-Way Communication: Set up communication between two Arduinos where one sends sensor readings and the other displays them on an LCD.
-
Serial Protocol: Design a simple protocol for sending multiple values with error checking (e.g., checksum).
Additional Resources
- Arduino Serial Reference
- SoftwareSerial Library
- Serial Communication Principles
- Debugging with Serial Monitor
With practice, you'll become proficient at using Serial communication in your Arduino projects, allowing for more complex and interactive applications!
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)