30

Currently the python program must know which port a device (Arduino) is on before Python can communicate the device.

Problem: Whenever the device is plugged out and back in, its COM port changes, so the correct serial port must be given to Python again for it to find the device.

How can Python (using pySerial) automatically search for the correct serial port to use? Is it possible for python to correctly identify the device on a serial port as an Arduino?

dsolimano
  • 8,870
  • 3
  • 48
  • 63
Nyxynyx
  • 61,411
  • 155
  • 482
  • 830
  • If your problem is that the port changes in your computer as the USB device is disconnected and reconnected, then the answer may depend on the operating system. Are you using pySerial? – DrV Jun 13 '14 at 22:41
  • @user3735428 Yes I'm using pySerial. Hoping for a solution that works on Windows/Mac/Linux – Nyxynyx Jun 14 '14 at 00:49

4 Answers4

45

Use the following code to see all the available serial ports:

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

This gives me the following:

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

To work out if it's an Arduino you could do something like:

    if "Arduino" in p.description:
        print "This is an Arduino!"
Matt Williams
  • 1,596
  • 17
  • 24
  • 1
    This also works if you're plugged into the Native USB port on the Arduino Due. I haven't tested it on any other Arduino's. – Matt Williams Aug 04 '14 at 05:11
  • 8
    `p[1]` is better spelt `p.description` – Eric Dec 03 '16 at 23:22
  • If you check p.hwid for "VID:PID=2341:0043", that is an arduino uno, and that will work with the knockoffs as well. You would need to use a different PID (just scan your ports like in the post to find the correct one). – Daniel B. Apr 26 '20 at 04:35
  • 3
    This works for Linux as well, even though the ports have different names and "Arduino" is not in `p.description`. Instead, `p.manufacturer` contains "Arduino" and `p.serial_number` tells me which is which :-) – AstroFloyd Nov 24 '20 at 10:41
  • You can also use `serial.tools.list_ports.grep('Arduino')` to filter out unwanted devices. You can search by string or regular expression https://pyserial.readthedocs.io/en/latest/tools.html#serial.tools.list_ports.grep – Jonathan Holvey Feb 13 '21 at 07:46
24

Using serial.tools.list_ports.comports, we can find and connect to an arduino with:

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

If you know you're looking for exactly the same arduino each time, you can filter on p.serial_number instead

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')
Eric
  • 95,302
  • 53
  • 242
  • 374
  • 1
    Works very nice, but only for original Arduino. Non-original Arduinos will report differently, e.g. "Generic CDC"... ultimately the best solution would be to list all possible USB devices and then allow a user to pick one using a runtime flag. – dust Dec 06 '16 at 19:08
  • @dust: Right, but for the common case where you're bouncing one piece of hardware between multiple USB ports and computers, this technique is most convenient. If you're looking to detect previously-unseen arduino-like devices, you're going to have a harder time, and forcing the user to choose as you mention is probably the best path – Eric Dec 07 '16 at 13:39
  • Is it possible to program the description given from the Arduino? Then each python will find and connect to the exact Arduino it needs. – Espen Apr 16 '19 at 13:36
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()
Eric
  • 95,302
  • 53
  • 242
  • 374
  • I assume this program is run on the computer plugged into the Arduino. Is that right? What extension is used to save this file? – acpilot Apr 19 '16 at 19:19
  • 1
    This is terribly-written code. `int` is not a sensible variable name, and 8 and 9 are deeply magic numbers – Eric Nov 10 '16 at 15:14
  • 1
    This code won't work for genuine Arduinos. Genuine Arduinos use FTDI USB-Serial bridges whereas the CH340/CH341 serial ports are Jiangsu Qinheng chips and are found only on cheap Chinese Arduino "clones." – AlwaysLearning Dec 22 '18 at 12:36
-3

Try this code (only for windows users. MAC user can withdraw idea from this concept)

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())
kAliert
  • 768
  • 9
  • 21