Python自动选择串口(用于Arduino)

30

目前,Python程序必须知道设备(Arduino)所在的端口,才能与该设备通信。

问题:每当设备被拔出并重新插入时,其COM端口会发生变化,因此必须再次将正确的串行端口提供给Python以便找到设备。

如何让Python(使用pySerial)自动搜索要使用的正确串行端口?Python是否可以正确地将位于串行端口上的设备识别为Arduino?


如果您的问题是USB设备断开连接并重新连接时计算机端口发生更改,那么答案可能取决于操作系统。您是否在使用pySerial? - DrV
@user3735428 是的,我正在使用pySerial。希望能找到一个在Windows/Mac/Linux上都能运行的解决方案。 - Nyxynyx
4个回答

45

使用以下代码查看所有可用的串行端口:

import serial.tools.list_ports
ports = list(serial.tools.list_ports.comports())
for p in ports:
    print p

这给我带来了以下结果:

('COM4', 'Arduino Due Programming Port (COM4)', 'USB VID:PID=2341:003D SNR=75330303035351300230')
('COM11', 'RS-232 Port (COM11)', 'FTDIBUS\\VID_0856+PID_AC27+BBOPYNPPA\\0000')

要确定它是否是Arduino,您可以执行以下操作:

    if "Arduino" in p.description:
        print "This is an Arduino!"

1
如果您连接到Arduino Due上的本机USB端口,则此方法也适用。我尚未在任何其他Arduino上进行过测试。 - Matt Williams
8
“p[1]” 更好地写作 “p.description”。 - Eric
如果您检查“VID:PID = 2341:0043”的p.hwid,那就是Arduino Uno,并且它也适用于仿冒品。您需要使用不同的PID(只需像帖子中一样扫描端口以找到正确的PID)。 - Daniel B.
3
这也适用于Linux,即使端口名称不同且“Arduino”不在p.description中。相反,p.manufacturer包含“Arduino”,而p.serial_number告诉我哪个是哪个 :-) - AstroFloyd
你也可以使用 serial.tools.list_ports.grep('Arduino') 来过滤掉不需要的设备。你可以通过字符串或正则表达式进行搜索 https://pyserial.readthedocs.io/en/latest/tools.html#serial.tools.list_ports.grep - Jonathan Holvey

24

使用serial.tools.list_ports.comports,我们可以找到并连接到Arduino:

import warnings
import serial
import serial.tools.list_ports

arduino_ports = [
    p.device
    for p in serial.tools.list_ports.comports()
    if 'Arduino' in p.description  # may need tweaking to match new arduinos
]
if not arduino_ports:
    raise IOError("No Arduino found")
if len(arduino_ports) > 1:
    warnings.warn('Multiple Arduinos found - using the first')

ser = serial.Serial(arduino_ports[0])

如果你知道每次都在寻找完全相同的Arduino,你可以过滤使用p.serial_number

import serial.tools.list_ports

def find_arduino(serial_number):
    for pinfo in serial.tools.list_ports.comports():
        if pinfo.serial_number == serial_number:
            return serial.Serial(pinfo.device)
    raise IOError("Could not find an arduino - is it plugged in?")

ser = find_arduino(serial_number='85430353531351B09121')

1
工作得非常好,但仅适用于原始的Arduino。非原始的Arduino将报告不同,例如“通用CDC”...最终最好的解决方案是列出所有可能的USB设备,然后允许用户使用运行时标志选择一个。 - dust
@dust:没错,但对于通常情况下你需要在多个USB端口和计算机之间传输硬件的情况,这种技术是最方便的。如果你想要检测以前未见过的类似Arduino的设备,那么你会遇到更大的困难,像你提到的强制用户选择可能是最好的方法。 - Eric
能否从Arduino编程中提取描述信息?这样每个Python程序就可以找到并连接到它所需的确切Arduino。 - Espen

2
"""
Written on a Windows 10 Computer, Python 2.7.9 Version.

This program automatically detects and lists ports.  If no ports are found, it simply shells out.  In the printout below "list(serial.tools.list_ports.comports())" finds two ports and the program lists them out - a listout shown below:

     COM5 - USB-SERIAL CH340 (COM5)
     Found Arduino Uno on COM5
     COM4 - Microsoft USB GPS Port (COM4)

As each port is found, "CH340," (the name of the Adruino Uno) is searched for in the listed port with the "while int1 < 9:" loop.  The first "If" statement looks for "CH340" and when found the integer value "int1" will be the same as the com port #. With a concatination,  the operation "str1 = "COM" + str2" gives the com port name of the Adruino, eg. "COM5."  The next "IF" statement looks for both "CH340" AND str1, ("COM5") in the above case.  The statement "Found Arduino Uno on COM5" prints out, and "str1" is used in setting up the com port:

ser = serial.Serial(str1, 9600, timeout=10)

This program goes on to open the com port and prints data from the Adruino.

The modules "serial, sys, time, serial.tools.list_ports" must all be imported.

Written by Joseph F. Mack 01/29/2016.  "A BIG Thank you" to all the individuals whose programs I "borrowed" from that are available in the many forums for Python and PYGame users!
"""

import serial
import sys
import time
import serial.tools.list_ports

serPort = ""
int1 = 0
str1 = ""
str2 = ""

# Find Live Ports
ports = list(serial.tools.list_ports.comports())
for p in ports:
   print p # This causes each port's information to be printed out.
           # To search this p data, use p[1].

   while int1 < 9:   # Loop checks "COM0" to "COM8" for Adruino Port Info. 

      if "CH340" in p[1]:  # Looks for "CH340" in P[1].
            str2 = str(int1) # Converts an Integer to a String, allowing:
            str1 = "COM" + str2 # add the strings together.

      if "CH340" in p[1] and str1 in p[1]: # Looks for "CH340" and "COM#"
         print "Found Arduino Uno on " + str1
         int1 = 9 # Causes loop to end.

      if int1 == 8:
         print "UNO not found!"
         sys.exit() # Terminates Script.

      int1 = int1 + 1

time.sleep(5)  # Gives user 5 seconds to view Port information -- can be   changed/removed.

# Set Port
ser = serial.Serial(str1, 9600, timeout=10) # Put in your speed and timeout value.

# This begins the opening and printout of data from the Adruino.

ser.close()  # In case the port is already open this closes it.
ser.open()   # Reopen the port.

ser.flushInput()
ser.flushOutput()

int1 = 0
str1 = ""
str2 = ""

while int1==0:

   if "\n" not in str1:        # concatinates string on one line till a line feed "\n"
      str2 = ser.readline()    # is found, then prints the line.
      str1 += str2
   print(str1)
   str1=""
   time.sleep(.1)

print 'serial closed'
ser.close()

我假设这个程序是在连接到Arduino的计算机上运行的。是这样吗?保存这个文件使用什么扩展名? - acpilot
1
这是一段糟糕的代码。int<n>不是一个合理的变量名,而8和9是非常神奇的数字。 - Eric
1
这段代码无法在真正的Arduino上运行。真正的Arduino使用FTDI USB-Serial桥接器,而CH340/CH341串口是江苏秦恒芯片,只存在于廉价的中国Arduino“克隆版”中。 - AlwaysLearning

-3

尝试这段代码(仅适用于Windows用户。MAC用户可以从这个概念中撤回想法)

import serial
import time
list=['COM1','COM2','COM3','COM4','COM5','COM6','COM7','COM8','COM9','COM10','COM11','COM12','COM13','COM14','COM15','COM16','COM17','COM18',]



COM1='COM1'
COM2='COM2'
COM3='COM3'
COM4='COM4'
COM5='COM5'
COM6='COM6'
COM7='COM7'
COM8='COM8'
COM9='COM9'
COM10='COM10'
COM11='COM11'
COM12='COM12'
COM13='COM13'
COM14='COM14'
COM15='COM15'
COM16='COM16'
COM17='COM17'
COM18='COM18'
COM19='COM19'
time.sleep(1)
ser = serial.Serial()

ser.baudrate = 9600

i=1

while True:
    time.sleep(.2)
    print(i)
    ser.port = list[i]
    try:

        ser.open()
        if ser.isOpen()==True:
            print('connected')
            #print('arduino is on COMPORT'.join(i))
            break
        break

    except:
        print('waiting')
        i=i+1
        if i==18:
            print('Kindly remove usb cable and try again')
            break


print('here we go')
while True:
    print(ser.readline())

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