Python中枚举串口的跨平台方法是什么(包括虚拟端口)?

31

注意: 我使用的是Python 2.7和pySerial进行串行通信。

我找到了这篇文章,其中列出了两种方法:http://www.zaber.com/wiki/Software/Python#Displaying_a_list_of_available_serial_ports

这种方法适用于Windows和Linux,但有时会在Linux上错过虚拟端口。

import serial

def scan():
   # scan for available ports. return a list of tuples (num, name)
   available = []
   for i in range(256):
       try:
           s = serial.Serial(i)
           available.append( (i, s.portstr))
           s.close()
       except serial.SerialException:
           pass
   return available

print "Found ports:"
for n,s in scan(): print "(%d) %s" % (n,s)

还有一个仅在Linux上适用的选项,但它包括虚拟端口:

import serial, glob

def scan():
   # scan for available ports. return a list of device names.
   return glob.glob('/dev/ttyS*') + glob.glob('/dev/ttyUSB*')

print "Found ports:"
for name in scan(): print name

假设我可以通过平台检测,在Linux上运行第二种方法(包括虚拟端口),在Windows上运行第一种方法,那么Mac怎么办?

如何枚举串口(包括虚拟串口),无论在哪个平台下都能实现呢?

编辑

我找到了几个相关的问题:


4
多年来,我一直在做跨平台串口通信的事情,每次都要编写相同的愚蠢平台检测、文件名匹配和手动调整的代码,十分繁琐。我很愿意看到你能否得到好的解决方案。 - detly
好吧,我想那就是它必须的了 :(. 等我有时间了,我会在 Linux 和 Mac 上测试它,并看看能否将其打包成一个库。 - Chris Laplante
给它几天时间,也许会有更优雅的解决方案 :) - detly
我想在这里添加一些内容。bitpim在多个平台上都有相当多的comscan代码。也许从那里获取一些代码来构建跨平台串口枚举器会很有用。http://bitpim.svn.sourceforge.net/viewvc/bitpim/trunk/bitpim/src/comscan.py?revision=4835&view=markup - Nandeep Mali
@n9986:看起来非常有前途,谢谢!你应该把它作为答案。 - Chris Laplante
FYI,我正在寻找同样的东西。有趣的比较是Arduino IDE/Processing如何为工具菜单枚举其串行端口:https://github.com/arduino/Arduino/blob/master/arduino-core/src/processing/app/SerialPortList.java#L40 简而言之:正则表达式城市! - tardate
5个回答

8

这是我一直在使用的方法。它是我上面发布的方法的混合体。不过,我仍然希望看到更好的解决方案。

# A function that tries to list serial ports on most common platforms
def list_serial_ports():
    system_name = platform.system()
    if system_name == "Windows":
        # Scan for available ports.
        available = []
        for i in range(256):
            try:
                s = serial.Serial(i)
                available.append(i)
                s.close()
            except serial.SerialException:
                pass
        return available
    elif system_name == "Darwin":
        # Mac
        return glob.glob('/dev/tty*') + glob.glob('/dev/cu*')
    else:
        # Assume Linux or something else
        return glob.glob('/dev/ttyS*') + glob.glob('/dev/ttyUSB*')

1
如果平台不是Windows,您可能想要使用 serial.tools.list_ports_posix。除了OSX和Linux,它还支持几个Posix平台。 - Abe Karplus
串口设备在Mac和Linux上不必遵循这些模式,对吧?我认为可以使用udev规则来为它们在/dev/中分配任何名称。 - Mu Mind
在OS X上,有时会出现与上述模式不匹配的'/dev/tty...'。例如,来自华为的两个不同的USB存储设备调制解调器可能会出现为'/dev/cu.HUAWEIMobile-'或'/dev/tty.HUAWEIMobile-'。 - fmalina
@Mu Mind,您能详细说明一下udev规则吗? - fmalina
我的方法可能在Linux/MacOS上不可靠(或不正确?)因此我将授予@n9986赏金(但现在还不接受)。当我可以验证它时,我会接受它。 - Chris Laplante
显示剩余2条评论

7

BitPim在多个平台上都有相当多的comscan代码。从中提取一些代码以构建跨平台串口枚举器可能很有用。您可以直接在命令行中运行检测代码进行测试。

链接到源文件comscan.py

在Linux中,似乎无法检测到“/dev/ttyS”端口。在第378行下面添加以下行可解决此问题:

("/dev/ttyS", "Standard Serial Port", "serial"),

作者已经让添加不同类型串口标识变得容易。

在Mac上,它正常工作。(有一个Arduino方便测试)

在Windows上,它成功检测到已连接端口(对于已连接的FTDI连接器)。需要pywin32

使用pyserial,我尝试了以下操作:

python -m serial.tools.list_ports

在Windows上似乎不起作用。在Mac上可以工作。在Linux上可以工作。

有趣的是,在此命令的文档中看到了平台部分:

Platform :  Posix (/dev files)
Platform :  Linux (/dev files, sysfs and lsusb)
Platform :  Windows (setupapi, registry)

我认为将两者合并应该可以得到一个(几乎)100%可靠的串口枚举器。

编辑:在Python 2.7中尝试了以上所有方法。


感谢您发布所有这些内容!我已经给了您赏金,因为我认为这是迄今为止最有前途的答案。当我(或其他人)有机会验证它时,我会接受它。 - Chris Laplante
谢谢!我会看看能否稍后找到更多相关信息(从网上或我的笔记中)。我会在这里添加它。 - Nandeep Mali
1
serial.tools.list_ports 在 Windows 的 Python 3.4 上成功地帮我检测到了 FTDI 数据线。 :) - Sam Finnigan

4

不幸的是,在Windows上,“comports()”没有给我任何东西。另一方面,上面的第一种方法正确地列出了我正在使用的(单个)端口。 - Chris Laplante
1
似乎对虚拟USB串口无效(这在新电脑中基本上是常态)。 - Jason S

0

我不知道你是否仍在寻找答案,但由于我有一个可行的解决方案,所以我想发布它。这里是 getports包,作为我的Arduino数据记录器项目的一部分。我已经测试过它可以在Mac、某些Linux版本和Windows 7上运行。与bitpim的comscan不同,它不使用任何非标准库模块。此外,在Linux或Mac上,它不使用/dev globbing,因此重命名规则不应该成为问题。(在Linux上它使用sysfs,在Mac上它使用IOKit。)我不知道它是否可以检测Linux上的虚拟串口,但如果不能,请尝试替换。

sys_suffix = '/device/'

使用

sys_suffix = ''

in linuxgetports.py


0

我有一个在Windows和Linux上都有效的解决方案。想着如果有帮助的话,我会把它添加到这里。期望的端口号作为命令行参数发送给pytest_configure。

def pytest_configure(config):
global tty
if sys.platform == "win32":           # OS is Windows
    # use case - send a comma-separated list of COM ports e.g. 3,6 or /dev/ttyUSB0,/dev/ttyACM1
    if ',' in str(config.getoption('--tty')):
        params = str(config.getoption('--tty')).split(",")
        tty = [('COM'+str(params[0])),('COM'+str(params[1]))]
    else:
        tty = 'COM' + str(config.getoption('--tty'))
elif sys.platform == "linux":      # OS is linux/debian
    if ',' in config.getoption('--tty'):
        tty = (config.getoption('--tty')).split(",")
    else:
        tty = config.getoption('--tty')



def openPort(portNum):
try:
    from serial.tools.list_ports import comports
except ImportError:
    return None
if comports:
    ports_list = list(comports())
for port in ports_list:
    if portNum in port:
        try:
            serialData = serial.Serial(port=portNum, baudrate=115200, parity=serial.PARITY_NONE,
                                       stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS)
            return serialData
        except serial.serialutil.SerialException:
            return -1

可以始终使用glob语句来搜索可用的端口(例如Linux的示例):
TTYs = glob.glob("/dev/ttyACM*") + glob.glob("/dev/ttyUSB*")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接