92
votes

I am searching for a simple method to list all available com port on a PC.

I have found this method but it is Windows-specific: Listing serial (COM) ports on Windows?

I am using Python 3 with pySerial on a Windows 7 PC.

I have found in the pySerial API (http://pyserial.sourceforge.net/pyserial_api.html) a function serial.tools.list_ports.comports() that lists com ports (exactly what I want).

import serial.tools.list_ports
print(list(serial.tools.list_ports.comports()))

But it seems that it doesn't work. When my USB to COM gateway is connected to the PC (I see the COM5 in the Device Manager), this COM port isn't included in the list returned by list_ports.comports(). Instead I only get COM4 which seems to be connected to a modem (I don't see it in the COM&LPT section of Device Manager)!

Do you know why it doesn't work? Have you got another solution which is not system specific?

12
New readers: note that it's been over five years since this question was asked, and the bug in pySerial's comports() function that was described in this question (without precise information on how to reproduce it) has probably been fixed. Start by trying import serial.tools.list_ports; print([comport.device for comport in serial.tools.list_ports.comports()]). Only if that doesn't work for you are any of the answers below relevant to you.Mark Amery
Also to new readers: apparently due to changes in pySerial, the code described by the OP (and some of the answers) no longer produces a list of COM port names, whether complete or incomplete. Instead, it generates a list of object references to ListPortInfo objects. To get the names or other information you must use the attributes of these objects when building the list. See: pythonhosted.org/pyserial/…JDM

12 Answers

160
votes

This is the code I use.

Successfully tested on Windows 8.1 x64, Windows 10 x64, Mac OS X 10.9.x / 10.10.x / 10.11.x and Ubuntu 14.04 / 14.10 / 15.04 / 15.10 with both Python 2 and Python 3.

import sys
import glob
import serial


def serial_ports():
    """ Lists serial port names

        :raises EnvironmentError:
            On unsupported or unknown platforms
        :returns:
            A list of the serial ports available on the system
    """
    if sys.platform.startswith('win'):
        ports = ['COM%s' % (i + 1) for i in range(256)]
    elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
        # this excludes your current terminal "/dev/tty"
        ports = glob.glob('/dev/tty[A-Za-z]*')
    elif sys.platform.startswith('darwin'):
        ports = glob.glob('/dev/tty.*')
    else:
        raise EnvironmentError('Unsupported platform')

    result = []
    for port in ports:
        try:
            s = serial.Serial(port)
            s.close()
            result.append(port)
        except (OSError, serial.SerialException):
            pass
    return result


if __name__ == '__main__':
    print(serial_ports())
27
votes

You can use:

python -c "import serial.tools.list_ports;print serial.tools.list_ports.comports()"

Filter by know port: python -c "import serial.tools.list_ports;print [port for port in serial.tools.list_ports.comports() if port[2] != 'n/a']"

See more info here: https://pyserial.readthedocs.org/en/latest/tools.html#module-serial.tools.list_ports

24
votes

Basically mentioned this in pyserial documentation https://pyserial.readthedocs.io/en/latest/tools.html#module-serial.tools.list_ports

import serial.tools.list_ports
ports = serial.tools.list_ports.comports()

for port, desc, hwid in sorted(ports):
        print("{}: {} [{}]".format(port, desc, hwid))

Result :

COM1: Communications Port (COM1) [ACPI\PNP0501\1]

COM7: MediaTek USB Port (COM7) [USB VID:PID=0E8D:0003 SER=6 LOCATION=1-2.1]

11
votes

A possible refinement to Thomas's excellent answer is to have Linux and possibly OSX also try to open ports and return only those which could be opened. This is because Linux, at least, lists a boatload of ports as files in /dev/ which aren't connected to anything. If you're running in a terminal, /dev/tty is the terminal in which you're working and opening and closing it can goof up your command line, so the glob is designed to not do that. Code:

    # ... Windows code unchanged ...

    elif sys.platform.startswith ('linux'):
        temp_list = glob.glob ('/dev/tty[A-Za-z]*')

    result = []
    for a_port in temp_list:

        try:
            s = serial.Serial(a_port)
            s.close()
            result.append(a_port)
        except serial.SerialException:
            pass

    return result

This modification to Thomas's code has been tested on Ubuntu 14.04 only.

9
votes

refinement on moylop260's answer:

import serial.tools.list_ports
comlist = serial.tools.list_ports.comports()
connected = []
for element in comlist:
    connected.append(element.device)
print("Connected COM ports: " + str(connected))

This lists the ports that exist in hardware, including ones that are in use. A whole lot more information exists in the list, per the pyserial tools documentation

9
votes

one line solution with pySerial package.

python -m serial.tools.list_ports
4
votes

Probably late, but might help someone in need.

import serial.tools.list_ports


class COMPorts:

    def __init__(self, data: list):
        self.data = data

    @classmethod
    def get_com_ports(cls):
        data = []
        ports = list(serial.tools.list_ports.comports())

        for port_ in ports:
            obj = Object(data=dict({"device": port_.device, "description": port_.description.split("(")[0].strip()}))
            data.append(obj)

        return cls(data=data)

    @staticmethod
    def get_description_by_device(device: str):
        for port_ in COMPorts.get_com_ports().data:
            if port_.device == device:
                return port_.description

    @staticmethod
    def get_device_by_description(description: str):
        for port_ in COMPorts.get_com_ports().data:
            if port_.description == description:
                return port_.device


class Object:
    def __init__(self, data: dict):
        self.data = data
        self.device = data.get("device")
        self.description = data.get("description")


if __name__ == "__main__":
    for port in COMPorts.get_com_ports().data:
        print(port.device)
        print(port.description)

    print(COMPorts.get_device_by_description(description="Arduino Leonardo"))
    print(COMPorts.get_description_by_device(device="COM3"))
2
votes

Please, try this code:

import serial
ports = serial.tools.list_ports.comports(include_links=False)
for port in ports :
    print(port.device)

first of all, you need to import package for serial port communication, so:

import serial

then you create the list of all the serial ports currently available:

ports = serial.tools.list_ports.comports(include_links=False)

and then, walking along whole list, you can for example print port names:

for port in ports :
    print(port.device)

This is just an example how to get the list of ports and print their names, but there some other options you can do with this data. Just try print different variants after

port.

1
votes

try this code

import serial.tools.list_ports
for i in serial.tools.list_ports.comports():
    print(i) 

it returns

COM1 - Port de communication (COM1)
COM5 - USB-SERIAL CH340 (COM5)

if you just wont the name of the port for exemple COM1

import serial.tools.list_ports
for i in serial.tools.list_ports.comports():
    print(str(i).split(" ")[0])

it returns

COM1
COM5

as in my case py 3.7 64bits

0
votes

Several options are available:

Call QueryDosDevice with a NULL lpDeviceName to list all DOS devices. Then use CreateFile and GetCommConfig with each device name in turn to figure out whether it's a serial port.

Call SetupDiGetClassDevs with a ClassGuid of GUID_DEVINTERFACE_COMPORT.

WMI is also available to C/C++ programs.

There's some conversation on the win32 newsgroup and a CodeProject, er, project.

0
votes

Works only on Windows:

import winreg
import itertools

def serial_ports() -> list:
    path = 'HARDWARE\\DEVICEMAP\\SERIALCOMM'
    key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path)

    ports = []
    for i in itertools.count():
        try:
            ports.append(winreg.EnumValue(key, i)[1])
        except EnvironmentError:
            break

    return ports

if __name__ == "__main__":
    ports = serial_ports()
0
votes

something simple but I use it a lot.

import serial.tools.list_ports as ports

com_ports = list(ports.comports()) # create a list of com ['COM1','COM2'] 
    for i in com_ports:            
        print(i.device) # returns 'COMx'