Complete Guide to Reading and Writing from COM Ports Using PySerial in Windows

Nov 28, 2025 · Programming · 10 views · 7.8

Keywords: PySerial | COM Ports | Serial Communication | Windows | Python Serial Programming

Abstract: This article provides a comprehensive guide to serial port communication using PySerial library in Windows operating systems. Starting from COM port identification and enumeration, it systematically explains how to properly configure and open serial ports, and implement data transmission and reception. The article focuses on resolving the naming differences between Windows and Unix systems, offering complete code examples and best practice recommendations including timeout settings, data encoding processing, and proper resource management. Through practical case studies, it demonstrates how to establish stable serial communication connections ensuring data transmission reliability and efficiency.

COM Port Identification and Enumeration

When performing serial communication in Windows systems, the first step is to identify available COM ports. PySerial provides the serial.tools.list_ports module to obtain the list of currently available serial ports in the system. The following code demonstrates how to enumerate all COM ports:

import serial.tools.list_ports as port_list
ports = list(port_list.comports())
for p in ports:
    print(p)

Executing the above code will output results similar to:

COM7 - Prolific USB-to-Serial Comm Port (COM7)
COM1 - Communications Port (COM1)

It's important to note that Windows systems use the COMx naming convention (such as COM1, COM7), which is fundamentally different from device file paths like /dev/ttyUSB0 or /dev/ttyS0 in Unix/Linux systems.

Serial Port Configuration and Opening

After identifying the target COM port, it's necessary to properly configure and open the serial connection. PySerial's Serial class provides flexible configuration options:

import serial

# Basic opening method
ser = serial.Serial('COM7')
print(f"Opened port: {ser.name}")

# Opening method with parameter configuration
serialPort = serial.Serial(
    port="COM4", 
    baudrate=9600, 
    bytesize=8, 
    timeout=2, 
    stopbits=serial.STOPBITS_ONE
)

Key configuration parameter descriptions:

Data Transmission Operations

To send data to the serial port, use the write() method, noting that data must be passed in byte form:

# Send string data
ser.write(b"Hello World\r\n")

# Send specific commands
command = b"AT\r\n"
ser.write(command)

# Send hexadecimal data
hex_data = bytes([0x01, 0x02, 0x03])
ser.write(hex_data)

In Windows systems, line endings typically use \r\n (carriage return + line feed), which differs from the \n used in Unix systems. Adjustments should be made according to specific device requirements.

Data Reception and Processing

There are multiple methods for reading data from serial ports. Choose the appropriate reading method based on specific requirements:

# Read single byte
byte_data = ser.read()
print(f"Received byte: {byte_data}")

# Read specified number of bytes
buffer = ser.read(10)  # Read 10 bytes
print(f"Received data: {buffer}")

# Read one line of data (until newline character is encountered)
line = ser.readline()
print(f"Received line: {line}")

# Complete example of continuous data reading
serialString = ""
while True:
    # Wait and read data
    data = serialPort.readline()
    
    # Attempt to decode and process data
    try:
        decoded_data = data.decode("ascii")
        print(f"Received: {decoded_data}")
        serialString += decoded_data
    except UnicodeDecodeError:
        print(f"Received raw byte data: {data}")

Data decoding is a crucial aspect of serial communication. If the device sends ASCII text, use decode('ascii') for decoding; if it sends binary data, process the byte data directly.

Advanced Configuration and Resource Management

To ensure program stability and proper resource release, it's recommended to use context managers for serial port connection management:

# Use with statement for automatic resource management
with serial.Serial('COM7', 9600, timeout=1) as ser:
    ser.write(b"Test Message\r\n")
    response = ser.readline()
    print(f"Device response: {response}")
# Serial port automatically closes after exiting the with block

Alternatively, create a Serial instance first, then configure and open it:

ser = serial.Serial()
ser.port = 'COM1'
ser.baudrate = 19200
ser.timeout = 2
ser.open()

if ser.is_open:
    print("Serial port successfully opened")
    # Perform communication operations
    ser.close()

Error Handling and Debugging Techniques

In practical applications, it's essential to properly handle potential exceptions:

import serial
import time

try:
    with serial.Serial('COM7', 9600, timeout=2) as ser:
        # Send test command
        ser.write(b"AT\r\n")
        
        # Wait and read response
        time.sleep(0.1)  # Allow time for device response
        response = ser.read_all()
        
        if response:
            print(f"Device response: {response}")
        else:
            print("No response received from device")
            
except serial.SerialException as e:
    print(f"Serial port operation error: {e}")
except Exception as e:
    print(f"Other error: {e}")

Debugging recommendations:

Platform Compatibility Considerations

Although this article focuses on Windows platform, when writing cross-platform applications, consider:

import serial
import sys

# Select port name based on operating system
if sys.platform.startswith('win'):
    port_name = 'COM7'
elif sys.platform.startswith('linux'):
    port_name = '/dev/ttyUSB0'
elif sys.platform.startswith('darwin'):  # macOS
    port_name = '/dev/tty.usbserial'
else:
    port_name = None

if port_name:
    try:
        with serial.Serial(port_name, 9600, timeout=1) as ser:
            # Cross-platform communication code
            pass
    except Exception as e:
        print(f"Unable to open port {port_name}: {e}")

By following the methods and best practices introduced in this article, you can achieve stable and reliable serial communication in Windows systems, meeting the communication requirements of various embedded devices, sensors, industrial controllers, and other equipment.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.