【问题标题】:Listing available com ports with Python使用 Python 列出可用的 com 端口
【发布时间】:2012-08-18 21:39:33
【问题描述】:

我正在寻找一种简单的方法来列出 PC 上所有可用的 com 端口。

我找到了这种方法,但它是特定于 Windows 的:Listing serial (COM) ports on Windows?

我在 Windows 7 PC 上使用 Python 3 和 pySerial。

我在 pySerial API (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 网关连接到 PC 时(我在设备管理器中看到 COM5),此 COM 端口不包含在 list_ports.comports() 返回的列表中。相反,我只得到了似乎连接到调制解调器的 COM4(我在设备管理器的 COM&LPT 部分中看不到它)!

你知道为什么它不起作用吗?您是否有其他非系统特定的解决方案?

【问题讨论】:

  • 新读者:请注意,这个问题被问到已经过去五年多了,并且这个问题中描述的 pySerial 的 comports() 函数中的错误(没有关于如何重现它的精确信息)已经可能已修复。首先尝试import serial.tools.list_ports; print([comport.device for comport in serial.tools.list_ports.comports()])。只有当这对您不起作用时,以下任何答案才与您相关。
  • 也给新读者:显然由于 pySerial 的变化,OP 描述的代码(以及一些答案)不再生成 COM 端口名称列表,无论是完整的还是不完整的。相反,它会生成对ListPortInfo 对象的对象引用列表。要获取名称或其他信息,您必须在构建列表时使用这些对象的属性。见:pythonhosted.org/pyserial/…

标签: python pyserial


【解决方案1】:

这是我使用的代码。

使用 Python 2 和 Python 3 在 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 上成功测试。

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

【讨论】:

  • 我可能会添加一些系统版本检测并收工。我不知道“所有系统”是平等的,因为端口的获取方式取决于您的操作系统。我会简单地检测操作系统版本,然后在此基础上为各种情况提供一个开关。
  • 在 OSX 上测试时,我还必须添加 except OSError: pass
  • 如果端口已经打开会怎样?它会返回错误吗?但是该端口仍然可用。我只是想知道如何使用 python 在 Windows 上进行可行的 COM 端口断开。
  • @Manny42 我不明白你想要完成什么,但如果一个端口已经打开,它肯定不能用于其他程序并且不会被serial_ports() 函数列出(和不会引发异常)。为什么不用pyserial提供的close()函数来断开一个COM口呢?
  • @Thomas - 有没有办法获得端口的一些描述?我需要根据描述选择我选择的端口
【解决方案2】:

在 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 端口 (COM7) [USB VID:PID=0E8D:0003 SER=6 LOCATION=1-2.1]

【讨论】:

    【解决方案3】:

    你可以使用:

    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 的解决方案快得多。在带有 python 3.4.4 的 OSX 10.11.5 上,它返回一个空数组,所以它肯定缺少一些 com 端口。
    • 适用于 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/…
    • -1; OP 已经提到 serial.tools.list_ports.comports() 并解释说它在提问者的平台上无法正常工作。您没有添加任何不在问题中的信息。
    【解决方案4】:

    对 Thomas 的出色答案的一种可能的改进是让 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
    

    Thomas 代码的这种修改仅在 Ubuntu 14.04 上进行了测试。

    【讨论】:

      【解决方案5】:

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

      这列出了硬件中存在的端口,包括正在使用的端口。根据the pyserial tools documentation,列表中存在更多信息

      【讨论】:

      • -1 至少有几个原因:覆盖 list 之类的内置函数的名称是不好的做法,并且 connected 的四语句构造可以更简洁地写成 @ 987654326@.
      • 对于在嵌入式 C 领域花费了 1/2 职业生涯的人来说,我不适合单行在这里做任何事情。您当然可以在一条线上完成所有操作。我已经更正了“列表”失礼,不知道我是如何错过这样明显的事情的。干杯。
      • @Mark Amery。你真的认为单行是一个问答网站的好主意吗?
      • @cstrutton 我不明白你的问题。
      • @Mark Avery。 One-liners 模糊了新手用户的关键概念。例如,如果您使用列表推导来演示一个简单的概念,新手可能会迷失在列表推导中并错过基本概念。如果列表理解是最好的方法,请先显示长手版本,然后是如何缩短它。您在教授您的观点的同时加强了列表组合。
      【解决方案6】:

      使用 pySerial 包的一条线解决方案。

      python -m serial.tools.list_ports
      

      【讨论】:

      • 就我而言,这是最简单的答案。
      【解决方案7】:

      可能会迟到,但可能会帮助有需要的人。

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

      【讨论】:

        【解决方案8】:

        请试试这个代码:

        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 的串行版本需要做的事情
        【解决方案9】:

        试试这个代码

        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位

        【讨论】:

          【解决方案10】:

          有多种选择:

          使用 NULL lpDeviceName 调用 QueryDosDevice 以列出所有 DOS 设备。然后依次使用 CreateFile 和GetCommConfig 与每个设备名称来判断它是否是串口。

          使用 GUID_DEVINTERFACE_COMPORT 的 ClassGuid 调用 SetupDiGetClassDevs

          WMI is also available to C/C++ programs.

          win32 newsgroup 和 CodeProject 上有一些对话,呃,project

          【讨论】:

          • -1;这是一个 Python 问题,要求提供与平台无关的解决方案,而您的回答是特定于 Windows 的,并且没有具体说明如何在 Python 中执行任何这些操作。对于稍微不同的问题,这可能是一个很好的答案,但在这里不合适。
          【解决方案11】:

          仅适用于 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()
          

          【讨论】:

            【解决方案12】:

            一些简单的东西,但我经常使用它。

            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'        
            

            【讨论】:

              【解决方案13】:

              需要注意的一点,代码如下:

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

              返回以下内容:

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

              如果您希望按顺序列出端口,并且只列出您可用的端口,请尝试:(感谢 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
              

              这将返回以下内容:

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

              所以与第一个示例不同,结果是 ['COM7', 'COM1', 'COM8', 'COM4'],这次我按顺序获取了所有的 com 端口,并且只有可用的端口。如果您按顺序需要它们并测试它们是否可用,非常方便。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2013-11-08
                • 1970-01-01
                • 2012-05-20
                • 1970-01-01
                • 1970-01-01
                • 2010-11-15
                • 1970-01-01
                相关资源
                最近更新 更多