Listing available com ports with Python
Asked Answered
F

13

128

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?

Formant answered 23/8, 2012 at 11:25 Comment(2)
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.Driscoll
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/…Tertius
A
194

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())
Aciculum answered 8/1, 2013 at 21:38 Comment(12)
I might add to this some system version detection and call it a day. I'm not aware of "all systems" being equal in that ports are acquired differently depending on your OS. I'd simply detect the OS version, then based on that have a switch available for the various cases.Buckthorn
I also had to add except OSError: pass when testing on OSX.Mockheroic
What would happen if a port is already opened ? It would return an error right ? But the port would still be available. I'm just wondering how to make a viable COM port deconnection on windows with python.Tosha
@Tosha I don't understand what you are trying to accomplish, but if a port is already opened it surely is not available for other programs and would not be listed by the serial_ports() function (and wouldn't raise an exception). Why don't you use the close() function provided by pyserial to disconnect a COM-port?Aciculum
@Thomas - is there a way to get some description of the port aswell ?I have a need to pick the port of my choice based on the descriptionAvina
I had to add an except IOError on Linux.Lentiginous
How can have the device name in the result so i can identify a particular device ?Wile
If already opened serial ports should be included too, do: except (OSError, serial.SerialException) as e: if "PermissionError" in str(e): result.append(port)Magenmagena
If you want the description or device name, you'll need to use another method like serial.tools.list_ports.comports().Complicacy
This solution seems not to work anymore. AttributeError: module 'serial' has no attribute 'SerialException'Lastminute
This worked today ! without modification flawlessly on my Raspberry Pi Zero W V1.1 (Python 3).Krill
Take care when using pseudo terminals for testing. For this I added a line to iterate through /dev/pts/[0-9]* but this will screw up your stdout - so make sure to not open them to test if they are usableJigsaw
G
67

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]

Greenhorn answered 15/10, 2018 at 2:58 Comment(0)
W
31

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

Wobbling answered 12/11, 2015 at 3:44 Comment(5)
This works great on OSX 10.11.5 with python 2.7.11 and seems to be much faster than the solution from Thomas. On OSX 10.11.5 with python 3.4.4 it returns an empty array, so it's definitely missing some com ports.Huonghupeh
Works great with Windows7 and Python 2.7. Very usefully, it returns a tuple like this: ('COM114', 'USB Serial Port (COM114)', 'FTDIBUS\\VID_0403+PID_6001+7&2A8DEF85&0&2\\0000') Which allows you to filter by Vendor USB PID/VID, which is exactly what I was after.Jaipur
Just tested on a Linux board (Debian, 3.4 kernel), equally good results, this time including the USB device serial number ('/dev/ttyACM1', 'ttyACM1', 'USB VID:PID=0483:5752 SNR=8D7B179D5354') Great solution. Thx!Jaipur
There's a very similar answer here: #24215143Rici
-1; the OP already mentioned serial.tools.list_ports.comports() and explained that it didn't work correctly on the asker's platform. You're adding no information that wasn't in the question.Driscoll
O
22

one line solution with pySerial package.

python -m serial.tools.list_ports
Oversight answered 5/11, 2018 at 17:3 Comment(1)
This is the easiest answer as far as I'm concerned.Musketeer
S
12

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.

Spirituel answered 13/7, 2014 at 6:21 Comment(0)
S
10

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

Someone answered 1/8, 2017 at 18:20 Comment(8)
-1 for at least a couple of reasons: it's bad practice to override the names of builtins like list, and your four-statement construction of connected could be written significantly more succinctly as connected = [port.device for port in serial.tools.list_ports.comports()].Driscoll
for the guy who's spent 1/2 a career in embedded C, I'm not one for the single line do everything here style. You certainly can do it all on one line. I've corrected the 'list' faux pas, not sure how I missed something glaring like that. Cheers.Someone
@Mark Amery. Do you really think one-liners are a good idea for a question and answer site?Schizo
@Schizo I don't understand your question.Driscoll
@Mark Avery. One-liners obscure key concepts from novice users. For example, if you use a list comprehension to demonstrate a simple concept, a novice might get lost in the list comprehension and miss the base concept. If a list comprehension is the best way to do it, show the long hand version first, then how to shorten it. You teach your point and reinforce the list comp at the same time.Schizo
@Schizo Meh; to my sensibilities the comprehension is the most readable way of writing it, but maybe you're right that a Python novice would prefer this way. There's little reason I see to show both - teaching about comprehensions isn't the point of the answer, and the objective should just be to make what's going on as understandable to the reader as possible.Driscoll
FWIW, I'm keeping my -1 on this despite the edit because (like moylop260's answer) it doesn't really address the question posed, which was about why some ports weren't appearing in the list returned by serial.tools.list_ports.comports().Driscoll
python -m pip install pyserial the moduleLahdidah
H
6

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"))
Hawsehole answered 18/2, 2021 at 19:11 Comment(0)
B
4

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.

Brame answered 24/9, 2018 at 20:27 Comment(1)
I think you need to do import serial.tools.list_ports, or at least that is what I needed to do on my version of serial on Python 3.7 with PySerial 3.4Loveridge
O
4

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'        
Oogonium answered 13/5, 2021 at 13:13 Comment(0)
A
3

One thing to note, codes like this:

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

Return the following:

COM7 - Standard Serial over Bluetooth link (COM7) COM1 - Communications Port (COM1) COM8 - Standard Serial over Bluetooth link (COM8) COM4 - USB-SERIAL CH340 (COM4)

If you want the ports listed in order, and only the ones available to you, try:(credit to tfeldmann)

   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

This returns the following:

['COM1', 'COM4', 'COM8']

So unlike the first example, where the result was ['COM7', 'COM1', 'COM8', 'COM4'], this time I get all of the com ports in order, and only the ones available. Very handy if you need them in order, and tested to see if they're available.

Aniseikonia answered 29/12, 2021 at 0:33 Comment(0)
P
1

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

Pyatt answered 19/8, 2020 at 20:23 Comment(0)
Q
1

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()
Quatre answered 29/10, 2020 at 18:36 Comment(0)
E
0

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.

Eyewash answered 23/8, 2012 at 11:29 Comment(1)
-1; this was a Python question asking for a platform-agnostic solution, and you've responded with an answer that's Windows-specific and gives no indication of how to do any of this in Python in particular. This could be a great answer to a slightly different question, but is out of place here.Driscoll

© 2022 - 2024 — McMap. All rights reserved.