как получить информацию о местоположении микросхемы FTDI FT4232 или серийный номер в windows с python? - PullRequest
0 голосов
/ 28 апреля 2020

Я использую чип FTDI FT4232 для расширения четырех COM-портов на Windows 10.

Эти расширенные COM-порты связаны с четырьмя различными устройствами. Поэтому, если я хочу общаться с другими устройствами через эти COM-порты, мне нужно знать, какие порты вызывать.

Я кодирую с python, вот что я получил с pyserial.

>python -m serial.tools.list_ports -v
COM3
    desc: USB Serial Port (COM3)
    hwid: USB VID:PID=0403:6011 SER=5
COM4
    desc: USB Serial Port (COM4)
    hwid: USB VID:PID=0403:6011 SER=5
COM5
    desc: USB Serial Port (COM5)
    hwid: USB VID:PID=0403:6011 SER=5
COM6
    desc: USB Serial Port (COM6)
    hwid: USB VID:PID=0403:6011 SER=5
4 ports found

Но в Linux pyserial может получить информацию о местоположении, которая может использоваться для различения guish этих четырех портов.

~$:python3 -m serial.tools.list_ports -v
/dev/ttyUSB0        
    desc: Quad RS232-HS
    hwid: USB VID:PID=0403:6011 LOCATION=1-2.1:1.0
/dev/ttyUSB1        
    desc: Quad RS232-HS
    hwid: USB VID:PID=0403:6011 LOCATION=1-2.1:1.1
/dev/ttyUSB2        
    desc: Quad RS232-HS
    hwid: USB VID:PID=0403:6011 LOCATION=1-2.1:1.2
/dev/ttyUSB3        
    desc: Quad RS232-HS
    hwid: USB VID:PID=0403:6011 LOCATION=1-2.1:1.3
4 ports found

У кого-нибудь есть идеи по поводу этой проблемы?

Временное решение: измените list_ports_ windows .py следующим образом:

    elif szHardwareID_str.startswith('FTDIBUS'):
        m = re.search(r'VID_([0-9a-f]{4})\+PID_([0-9a-f]{4})(\+(\w*))&(\w*)&(\w*)&(\w*)&(\w*)?', szHardwareID_str, re.I)
        if m:
            info.vid = int(m.group(1), 16)
            info.pid = int(m.group(2), 16)
            if m.group(8):
                info.serial_number =  int(m.group(8),16)
        # USB location is hidden by FDTI driver :(
        info.hwid = info.usb_info()

Затем вы можете вызвать нужный порт с помощью vid + pid + serial_number.

1 Ответ

0 голосов
/ 28 апреля 2020

На самом деле в ListPortInfo есть поле location , возвращаемое serial.tools.list_ports.comports . Однако похоже, что windows реализация list_ports (list_ports_ windows .py), похоже, не может правильно восстановить эту информацию. Я использовал модуль регистрации, чтобы исследовать это, добавив следующую строку в мой pyserial (установлен на win10 64, python3 .8). Добавление

logging.debug("szHardwareID_str: %s" % szHardwareID_str) #added this lin

после

# stringify
szHardwareID_str = szHardwareID.value

принесло мне:

DEBUG:root:szHardwareID_str: ACPI\PNP0501\0
DEBUG:root:szHardwareID_str: FTDIBUS\VID_0403+PID_6010+6&192CD50C&0&13&2\0000
DEBUG:root:szHardwareID_str: FTDIBUS\VID_0403+PID_6010+6&192CD50C&0&13&1\0000

Итак, можно увидеть, что местоположение на самом деле находится внутри этой строки, но более поздний анализ строки не удается правильно извлечь эту информацию. Т.е. реализованный поиск не соответствует locationID:

m = re.search(r'VID_([0-9a-f]{4})(&PID_([0-9a-f]{4}))?(&MI_(\d{2}))?(\\(\w+))?', szHardwareID_str, re.I)

Короче говоря: насколько мне известно, вы можете модифицировать pyserial (локально или глобально, создавая patch + pull) запрос) или вы вручную «извлекаете» информацию с помощью модуля pyserial.tools.list_ports. Например что-то вроде:

import ctypes
import serial
import logging

import serial.tools.list_ports_windows as lp

logging.basicConfig(level=logging.DEBUG)

################copied from list_ports_windows.py
GUIDs = (lp.GUID * 8)()  # so far only seen one used, so hope 8 are enough...
guids_size = lp.DWORD()
if not lp.SetupDiClassGuidsFromName(
        "Ports",
        GUIDs,
        ctypes.sizeof(GUIDs),
        ctypes.byref(guids_size)):
    raise ctypes.WinError()

# repeat for all possible GUIDs
for index in range(guids_size.value):
    bInterfaceNumber = None
    g_hdi = lp.SetupDiGetClassDevs(
        ctypes.byref(GUIDs[index]),
        None,
        lp.NULL,
        lp.DIGCF_PRESENT)  # was DIGCF_PRESENT|DIGCF_DEVICEINTERFACE which misses CDC ports

    devinfo = lp.SP_DEVINFO_DATA()
    devinfo.cbSize = ctypes.sizeof(devinfo)
    index = 0
    while lp.SetupDiEnumDeviceInfo(g_hdi, index, ctypes.byref(devinfo)):
        index += 1

        # get the real com port name
        hkey = lp.SetupDiOpenDevRegKey(
            g_hdi,
            ctypes.byref(devinfo),
            lp.DICS_FLAG_GLOBAL,
            0,
            lp.DIREG_DEV,  # DIREG_DRV for SW info
            lp.KEY_READ)
        port_name_buffer = ctypes.create_unicode_buffer(250)
        port_name_length = lp.ULONG(ctypes.sizeof(port_name_buffer))
        lp.RegQueryValueEx(
            hkey,
            "PortName",
            None,
            None,
            ctypes.byref(port_name_buffer),
            ctypes.byref(port_name_length))
        lp.RegCloseKey(hkey)

        # unfortunately does this method also include parallel ports.
        # we could check for names starting with COM or just exclude LPT
        # and hope that other "unknown" names are serial ports...
        if port_name_buffer.value.startswith('LPT'):
            continue

        # hardware ID
        szHardwareID = ctypes.create_unicode_buffer(250)
        # try to get ID that includes serial number
        if not lp.SetupDiGetDeviceInstanceId(
                g_hdi,
                ctypes.byref(devinfo),
                #~ ctypes.byref(szHardwareID),
                szHardwareID,
                ctypes.sizeof(szHardwareID) - 1,
                None):
            # fall back to more generic hardware ID if that would fail
            if not lp.SetupDiGetDeviceRegistryProperty(
                    g_hdi,
                    ctypes.byref(devinfo),
                    lp.SPDRP_HARDWAREID,
                    None,
                    ctypes.byref(szHardwareID),
                    ctypes.sizeof(szHardwareID) - 1,
                    None):
                # Ignore ERROR_INSUFFICIENT_BUFFER
                if ctypes.GetLastError() != lp.ERROR_INSUFFICIENT_BUFFER:
                    raise ctypes.WinError()
        # stringify
        szHardwareID_str = szHardwareID.value

        print(szHardwareID_str)

Однако общее решение (например, патч) будет лучшим способом ИМХО. Может быть, вы могли бы просто открыть номер в официальных репозиториях Pyserial.

...