Перелік доступних com-портів за допомогою Python


86

Я шукаю простий спосіб перерахувати всі доступні com-порти на ПК.

Я знайшов цей метод, але він специфічний для Windows: Перелік послідовних (COM) портів у Windows?

Я використовую Python 3 з pySerial на ПК з Windows 7.

Я знайшов в API pySerial ( http://pyserial.sourceforge.net/pyserial_api.html ) функцію, serial.tools.list_ports.comports()яка перелічує com-порти (саме те, що я хочу).

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

Але, здається, це не працює. Коли мій шлюз USB до COM підключений до ПК (я бачу COM5 у диспетчері пристроїв), цей порт COM не входить до списку, що повертаєтьсяlist_ports.comports() . Натомість я отримую лише COM4, ​​який, здається, підключений до модему (я не бачу його в розділі COM & LPT в Диспетчері пристроїв)!

Ви знаєте, чому це не працює? У вас є інше рішення, яке не є специфічним для системи?


21
Нові читачі: зауважте, що минуло більше п’яти років з моменту того, як було поставлено це запитання, і помилка у comports()функції pySerial, яка була описана в цьому питанні (без точної інформації про те, як її відтворити), ймовірно, була виправлена. Почніть із спроб import serial.tools.list_ports; print([comport.device for comport in serial.tools.list_ports.comports()]). Лише якщо це не працює для вас, будь-яка з наведених нижче відповідей стосується вас.
Mark Amery

Також для нових читачів: очевидно, через зміни в pySerial, код, описаний OP (та деякі відповіді), більше не створює переліку імен COM-портів, будь то повним чи неповним. Натомість він генерує список посилань на ListPortInfoоб’єкти на об’єкти. Щоб отримати імена або іншу інформацію, ви повинні використовувати атрибути цих об’єктів при складанні списку. Див .: pythonhosted.org/pyserial/…
JDM,

Відповіді:


155

Це код, яким я користуюся.

Успішно протестовано на Windows 8.1 x64, Windows 10 x64, Mac OS X 10.9.x / 10.10.x / 10.11.x та Ubuntu 14.04 / 14.10 / 15.04 / 15.10 як на Python 2, так і на 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())

Я міг би додати до цього деяке виявлення версії системи і назвати це днем. Мені не відомо про рівність "усіх систем", оскільки порти отримуються по-різному залежно від вашої ОС. Я просто виявив версію ОС, тоді на основі цього маю перемикач, доступний для різних випадків.
jml

1
Мені також довелося додати except OSError: passпід час тестування на OSX.
nvahalik

4
Що станеться, якщо порт вже відкритий? Це поверне помилку, чи не так? Але порт все одно буде доступний. Мені просто цікаво, як зробити життєздатне відключення COM-порту у Windows за допомогою python.
Менні42

@ Manny42 Я не розумію, що ви намагаєтесь досягти, але якщо порт вже відкритий, він, звичайно, недоступний для інших програм і не буде перерахований serial_ports()функцією (і не спричинить виняток). Чому б вам не використовувати close()функцію, надану pyserial, для відключення COM-порту?
тфельдман

@Thomas - чи є спосіб отримати якийсь опис порту? Мені потрібно вибрати порт, який я вибрав на основі опису

25

Ви можете використовувати:

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

Фільтрувати за відомим портом: python -c "import serial.tools.list_ports;print [port for port in serial.tools.list_ports.comports() if port[2] != 'n/a']"

Детальніше див. Тут: https://pyserial.readthedocs.org/en/latest/tools.html#module-serial.tools.list_ports


Це чудово працює на OSX 10.11.5 з python 2.7.11 і, здається, набагато швидше, ніж рішення від Thomas. На OSX 10.11.5 з python 3.4.4 він повертає порожній масив, тому йому точно не вистачає деяких портів com.
doizuc

Чудово працює з Windows7 та Python 2.7. Дуже корисно, він повертає такий набір: ('COM114', 'USB Serial Port (COM114)', 'FTDIBUS\\VID_0403+PID_6001+7&2A8DEF85&0&2\\0000')що дозволяє фільтрувати за допомогою постачальника USB PID / VID, саме цього я і шукав.
Річард Аплін,

Щойно протестоване на платі Linux (Debian, ядро ​​3.4), однаково хороші результати, цього разу включаючи серійний номер пристрою USB ('/dev/ttyACM1', 'ttyACM1', 'USB VID:PID=0483:5752 SNR=8D7B179D5354')Відмінне рішення. Дякую!
Річард Аплін,

Там дуже аналогічну відповідь тут: stackoverflow.com/questions/24214643 / ...
Gladclef

2
-1; OP вже згадав serial.tools.list_ports.comports()та пояснив, що він не працював коректно на платформі запитувача. Ви не додаєте жодної інформації, про яку не йшлося.
Mark Amery

16

В основному це згадується в pyserial документації 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))

Результат:

COM1: Порт зв'язку (COM1) [ACPI \ PNP0501 \ 1]

COM7: Порт USB MediaTek (COM7) [USB VID: PID = 0E8D: 0003 SER = 6 МІСЦЕ = 1-2.1]


11

Можливим вдосконаленням чудової відповіді Томаса є те, що Linux і, можливо, OSX також намагаються відкрити порти і повернути лише ті, які можна було відкрити. Це тому, що Linux, принаймні, перераховує завантаження портів як файли в / dev /, які ні до чого не пов'язані. Якщо ви працюєте в терміналі, / dev / tty - це термінал, в якому ви працюєте, і відкриття та закриття може перевершити ваш командний рядок, тому glob призначений для того, щоб цього не робити. Код:

    # ... 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

Ця модифікація коду Томаса була протестована лише на Ubuntu 14.04.


8

уточнення відповіді moylop260 :

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))

Тут перелічено порти, які існують в апаратному забезпеченні, включаючи використовувані. Відповідно до документації до інструментів pyserial, у списку є набагато більше інформації


3
-1 принаймні з кількох причин: погана практика замінювати імена вбудованих файлів типу list, і ваша конструкція з чотирьох операторів connectedможе бути написана значно коротше як connected = [port.device for port in serial.tools.list_ports.comports()].
Mark Amery

5
для хлопця, який провів 1/2 кар'єри у вбудованому C, я не той, хто в одній лінії робить усе тут. Ви, звичайно, можете зробити це на одному рядку. Я виправив помилковий "список", не впевнений, як я пропустив щось таке яскраве. Ура.
Грамбо

3
@Mark Amery. Ви справді вважаєте, що однокласні вкладиші - це гарна ідея для сайту з питаннями та відповідями?
cstrutton

@cstrutton Я не розумію твого запитання.
Mark Amery

2
@ Марк Евері. Однокласники не дають зрозуміти ключовим поняттям від початківців користувачів. Наприклад, якщо ви використовуєте розуміння списку, щоб продемонструвати просту концепцію, новачок може загубитися в розумінні списку і пропустити базову концепцію. Якщо розуміння списку є найкращим способом зробити це, спочатку покажіть довгу версію руки, а потім, як її скоротити. Ви одночасно викладаєте свою думку і підсилюєте список.
cstrutton


2

Спробуйте цей код:

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

Перш за все, вам потрібно імпортувати пакет для послідовного зв'язку, тому:

import serial

тоді ви створюєте список усіх доступних на даний момент послідовних портів:

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

а потім, проходячи повз цілий список, ви можете, наприклад, надрукувати імена портів:

for port in ports :
    print(port.device)

Це лише приклад того, як отримати список портів та надрукувати їх імена, але є деякі інші варіанти, які ви можете зробити з цими даними. Просто спробуйте надрукувати різні варіанти після

порт.


Я думаю, вам потрібно зробити імпорт serial.tools.list_ports, або, принаймні, це те, що мені потрібно було зробити у моїй версії серіалу на Python 3.7 з PySerial 3.4
Том Мідделтін

0

Доступно кілька варіантів:

Зателефонуйте QueryDosDevice за допомогою NULL lpDeviceName, щоб перерахувати всі пристрої DOS. Потім використовуйте CreateFile та GetCommConfig з кожним іменем пристрою, щоб визначити, чи це послідовний порт.

Викличте SetupDiGetClassDevs із ClassGuid GUID_DEVINTERFACE_COMPORT.

WMI також доступний для програм C / C ++ .

Там якийсь - то розмова на win32 телеконференції і CodeProject, ер, проект .


3
-1; це було запитання Python, що вимагало агностичного рішення платформи, і ви відповіли відповіддю, яка стосується Windows, і не вказує, як робити щось із цього в Python зокрема. Це може бути чудовою відповіддю на дещо інше запитання, але тут воно недоречне.
Mark Amery

0

спробуйте цей код

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

воно повертається

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

якщо ви просто не звикли називати порт для прикладу COM1

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

воно повертається

COM1
COM5

як у моєму випадку py 3,7 64 біта


0

Працює лише в 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()
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.