我正在使用FTDI FT4232芯片在Windows 10上扩展四个COM端口。
这些扩展的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可以获取可用于区分这四个端口的位置信息。
~$: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
任何人都知道这个问题吗?
实际上在serial.tools.list_ports.comports返回的ListPortInfo中有一个location字段。但是,看起来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(通过创建补丁+拉取请求在本地或全局),也可以使用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存储库中打开一个问题。