Python Serial Communication — Core Concepts
What Serial Communication Is
Serial communication sends data one bit at a time over a wire. It is one of the oldest and most universal ways for computers to talk to external devices. Despite being decades old, serial remains the primary interface for Arduino boards, CNC machines, 3D printers, GPS modules, industrial PLCs, scientific instruments, and embedded systems.
Most modern “serial” connections actually use USB, which emulates a traditional serial port. When you plug in an Arduino, the operating system creates a virtual serial port that applications use exactly like a physical RS-232 connection.
Key Parameters
Both sides of a serial connection must agree on these settings:
- Baud rate — Transmission speed in bits per second. Common values: 9600, 19200, 38400, 57600, 115200. The most common default is 9600 for simple devices and 115200 for faster ones.
- Data bits — Bits per character. Almost always 8.
- Stop bits — Marks the end of each character. Usually 1.
- Parity — Error detection bit. Usually None for USB connections.
- Flow control — Prevents buffer overflow. None for most hobbyist connections, hardware (RTS/CTS) for industrial equipment.
The shorthand “9600 8N1” means 9600 baud, 8 data bits, No parity, 1 stop bit.
The pyserial Library
pyserial is the standard Python library for serial communication:
pip install pyserial
Basic Read and Write
import serial
import time
# Open the serial port
ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
time.sleep(2) # wait for Arduino to reset after connection
# Send a command
ser.write(b'LED_ON\n')
# Read the response
response = ser.readline().decode('utf-8').strip()
print(f"Arduino says: {response}")
ser.close()
The timeout=1 parameter means readline() will wait up to 1 second for data. Without a timeout, it blocks forever if no data arrives.
Finding Available Ports
from serial.tools import list_ports
ports = list_ports.comports()
for port in ports:
print(f"{port.device}: {port.description} [{port.hwid}]")
This helps you discover which port your device is on, especially when the port name changes between connections.
Context Manager Usage
import serial
with serial.Serial('/dev/ttyUSB0', 115200, timeout=1) as ser:
ser.write(b'STATUS\n')
response = ser.readline()
print(response.decode().strip())
# Port automatically closed when exiting the block
Reading Strategies
Serial data arrives as a continuous stream. How you read it depends on what the device sends:
Line-Based Reading
Most devices end messages with a newline character:
while True:
line = ser.readline().decode('utf-8').strip()
if line:
print(f"Received: {line}")
Fixed-Size Reading
Some protocols send fixed-length packets:
# Read exactly 10 bytes
data = ser.read(10)
Delimiter-Based Reading
Read until a specific byte sequence:
data = ser.read_until(b'\r\n') # read until carriage return + newline
Non-Blocking Check
Check if data is available without blocking:
if ser.in_waiting > 0:
data = ser.read(ser.in_waiting)
process(data)
Practical Example: Arduino Sensor Logger
Python side:
import serial
import csv
import time
with serial.Serial('/dev/ttyUSB0', 9600, timeout=2) as ser:
time.sleep(2) # Arduino reset delay
with open('sensor_log.csv', 'w', newline='') as csvfile:
writer = csv.writer(csvfile)
writer.writerow(['timestamp', 'temperature', 'humidity'])
while True:
line = ser.readline().decode('utf-8').strip()
if line:
parts = line.split(',')
if len(parts) == 2:
timestamp = time.strftime('%Y-%m-%d %H:%M:%S')
writer.writerow([timestamp] + parts)
csvfile.flush()
print(f"{timestamp}: temp={parts[0]}, humidity={parts[1]}")
Arduino side sends: 23.5,65.2\n every second.
Common Misconception
A frequent mistake is opening the serial port and immediately sending data. Many devices (especially Arduino) reset when a serial connection is established. The bootloader runs for 1-2 seconds before your sketch starts. If you send data during this window, the device misses it. Always add a short delay after opening the port:
ser = serial.Serial('/dev/ttyUSB0', 9600)
time.sleep(2) # wait for device to be ready
ser.write(b'START\n')
Troubleshooting Common Issues
- Permission denied on Linux — Add your user to the
dialoutgroup:sudo usermod -a -G dialout $USER, then log out and back in - Garbled text — Baud rate mismatch. Both sides must use the same value.
- No response — Check
ser.in_waitingto verify data is arriving. Verify the correct port withlist_ports. - Port busy — Another program (Arduino IDE serial monitor) has the port open. Only one program can use a serial port at a time.
One thing to remember: Serial communication with pyserial is deceptively simple — open a port, read and write bytes — but reliable communication requires matching parameters, handling timeouts, and respecting device startup timing.
See Also
- Python Behavior Trees Robotics How robots make decisions using a tree-shaped rulebook that keeps them organized, like a flowchart that tells a robot what to do in every situation.
- Python Bluetooth Ble How Python connects to fitness trackers, smart locks, and wireless sensors using the invisible radio signals all around you.
- Python Circuitpython Hardware Why CircuitPython makes wiring up LEDs, sensors, and motors as easy as plugging in a USB drive.
- Python Computer Vision Autonomous How self-driving cars use cameras and Python to see the road, spot pedestrians, read signs, and understand traffic — like giving a car human eyes and a brain.
- Python Home Assistant Automation How Python turns your home into a smart home that reacts to you automatically, like a helpful invisible butler.