Of course! Using Python with serial ports is a common task for communicating with hardware like microcontrollers (Arduino, Raspberry Pi Pico), GPS modules, sensors, and other embedded systems.

The most popular and recommended library for this is pyserial.
Here's a comprehensive guide covering everything from installation to advanced usage.
Installation
First, you need to install the pyserial library. Open your terminal or command prompt and run:
pip install pyserial
Finding Your Serial Port
Before you can communicate, you need to know the name of your serial port. On different operating systems, these names look different:
- Windows: Looks like a
COMport (e.g.,COM3,COM10). You can find these in the Device Manager under "Ports (COM & LPT)". - Linux: Typically starts with
/dev/tty(e.g.,/dev/ttyUSB0for a USB-to-serial adapter,/dev/ttyACM0for an Arduino). - macOS: Usually starts with
/dev/tty(e.g.,/dev/tty.usbserial-XXXXor/dev/tty.usbmodemXXXX).
How to Find it Programmatically with pyserial
You can use pyserial to list all available serial ports. This is extremely useful for making your code portable.
import serial.tools.list_ports
# Get a list of all available ports
ports = serial.tools.list_ports.comports()
print("Available Serial Ports:")
for port in ports:
print(f"Device: {port.device}")
print(f" Name: {port.name}")
print(f" Description: {port.description}")
print(f" Hardware ID: {port.hwid}")
print("-" * 20)
# A more direct way to get just the device names
port_list = [port.device for port in ports]
print(f"\nPort List: {port_list}")
Basic Usage: Reading and Writing
This is the core of serial communication. The key is to use a try...finally block to ensure the serial port is always closed, even if an error occurs.
Example 1: Simple Read and Write
This example connects to a port, sends a command, and waits for a response.
import serial
import time
# --- Configuration ---
# Replace 'COM3' with your actual serial port
SERIAL_PORT = 'COM3'
BAUD_RATE = 9600 # Must match the baud rate of the connected device
try:
# 1. Initialize the serial connection
# The 'with' statement is recommended as it automatically handles closing the port
with serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1) as ser:
print(f"Connected to {ser.name} at {ser.baudrate} baud.")
# 2. Write data to the serial port
# Data must be in bytes. Use .encode() to convert a string.
command_to_send = "GET_DATA\n" # Example command
ser.write(command_to_send.encode('utf-8'))
print(f"Sent: {command_to_send}")
# 3. Read data from the serial port
# ser.in_waiting checks how many bytes are in the buffer
if ser.in_waiting > 0:
# ser.readline() reads until a newline or timeout
received_data = ser.readline()
# Decode the bytes back to a string
print(f"Received: {received_data.decode('utf-8').strip()}")
else:
print("No data received within timeout period.")
except serial.SerialException as e:
print(f"Error: Could not open serial port {SERIAL_PORT}.")
print(f"Details: {e}")
except Exception as e:
print(f"An unexpected error occurred: {e}")
Example 2: Continuous Reading (e.g., from a sensor)
This is a common pattern for reading a stream of data from a device.
import serial
import time
SERIAL_PORT = '/dev/ttyUSB0'
BAUD_RATE = 115200
try:
with serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1) as ser:
print(f"Connected to {ser.name}. Starting continuous read...")
ser.reset_input_buffer() # Clear any old data
while True:
if ser.in_waiting > 0:
line = ser.readline()
# Decode and strip to remove newline characters
print(line.decode('utf-8').strip())
# A small sleep to prevent the loop from consuming 100% CPU
time.sleep(0.1)
except KeyboardInterrupt:
print("\nProgram terminated by user.")
except serial.SerialException as e:
print(f"Error: Could not open serial port {SERIAL_PORT}.")
print(f"Details: {e}")
Key Serial Object Properties and Methods
Here are the most important attributes and methods you'll use:
| Category | Method/Attribute | Description |
|---|---|---|
| Connection | serial.Serial(port, baudrate, ...) |
Constructor to open a port. |
ser.close() |
Closes the port. Handled automatically by with. |
|
ser.is_open |
Boolean, True if the port is open. |
|
| Writing | ser.write(data) |
Writes data (must be bytes) to the port. |
ser.write_timeout |
Timeout for write operations. | |
| Reading | ser.read(size) |
Reads exactly size bytes from the port. |
ser.readline() |
Reads until a newline (\n) or timeout. |
|
ser.readall() |
Reads all available data in the buffer. | |
ser.in_waiting |
Number of bytes in the input buffer. | |
ser.timeout |
Timeout for read operations (default is None, which blocks forever). |
|
| Configuration | ser.baudrate |
Get/set the baud rate. |
ser.parity |
Get/set parity (serial.PARITY_NONE, serial.PARITY_EVEN, etc.). |
|
ser.bytesize |
Get/set data size (e.g., serial.EIGHTBITS). |
|
ser.stopbits |
Get/set stop bits (e.g., serial.STOPBITS_ONE). |
|
| Control | ser.reset_input_buffer() |
Clears the input buffer. |
ser.reset_output_buffer() |
Clears the output buffer. | |
ser.flush() |
Waits until all data in the output buffer is written. |
Troubleshooting Common Issues
-
SerialException: could not open port 'COM3': Permission denied- Cause (Linux/macOS): You don't have permission to access the device.
- Solution: Add your user to the
dialoutgroup (Linux). You may need to log out and log back in for the change to take effect.sudo usermod -a -G dialout $USER
- Cause (Windows): Another program (like Arduino IDE, PuTTY) is already using the port.
- Solution: Close all other programs that might be using the COM port.
-
No data is being received, or the data is garbled.
- Cause #1: Mismatched Baud Rate. The baud rate in your Python script must exactly match the baud rate configured on the device you're talking to.
- Cause #2: Hardware Flow Control (RTS/CTS). If enabled on one end but not the other, it can block communication. Try disabling it with
ser.rtscts = False. - Cause #3: Line Endings. Devices expect a specific line ending (e.g.,
\n,\r\n, or just\r). Ifser.readline()isn't working, tryser.read_until(b'\n')and ensure yourwrite()command includes the correct ending.
-
TimeoutErrororser.readline()returns empty bytes.- Cause: The device hasn't sent any data within the timeout period.
- Solution: Increase the timeout value (e.g.,
timeout=2). Make sure your device is actually sending data and that your wiring is correct.
-
UnicodeDecodeErrorwhen trying to decode received bytes.- Cause: The device is sending non-text data (binary data), but you're trying to treat it as a string (UTF-8).
- Solution: If you expect binary data, don't decode it. Work with the
bytesobject directly. If you expect text, check the encoding. Sometimes ASCII is used instead of UTF-8:data.decode('ascii').
