Monday, April 6, 2015

Python auto detect Arduino connected serial port

This example TRY to detect Arduino Uno connected serial port automatically, such that no need to hard code "'ttyACM0", "'ttyACM1"... It run on both Python 2 and 3. I test it on Ubuntu Linux only. It's a trial experience only, not a complete solution.



PySerial (ref: http://pyserial.sourceforge.net/pyserial_api.html) provide a function serial.tools.list_ports.comports(), it return an iterable that yields tuples of three strings:
- port name as it can be passed to serial.Serial or serial.serial_for_url()
- description in human readable form
- sort of hardware ID. E.g. may contain VID:PID of USB-serial adapters.

Items are returned in no particular order. It may make sense to sort the items. Also note that the reported strings are different across platforms and operating systems, even for the same device.

To simplify, I assume the port name (eg. /dev/ttyACM0) is in the 1st item returned, and have not handle any other case.

As shown in the post "Get idVendor and idProduct of your Arduino/USB devices", we know VID:PID of Arduino Uno is 2341:0043. So we can compare it with what returned from serial.tools.list_ports.comports() to determine is it Arduino Uno. And for simplify also, I assume it's in VID:PID=2341:0043 format.

findUno.py
import serial.tools.list_ports
import sys
import atexit
import platform

print("=== Auto scan for Arduino Uno connected port===")
print("")
print(platform.system(), platform.release())
print(platform.dist())
print("Python version " + platform.python_version())
print("")

def findArduinoUnoPort():
    portList = list(serial.tools.list_ports.comports())
    for port in portList:
        if "VID:PID=2341:0043" in port[0]\
            or "VID:PID=2341:0043" in port[1]\
            or "VID:PID=2341:0043" in port[2]:
            print(port)
            print(port[0])
            print(port[1])
            print(port[2])

            #please note: it is not sure [0]
            #returned port[] is no particular order
            #so, may be [1], [2]
            return port[0]

def doAtExit():
    if serialUno.isOpen():
        serialUno.close()
        print("Close serial")
        print("serialUno.isOpen() = " + str(serialUno.isOpen()))

atexit.register(doAtExit)

unoPort = findArduinoUnoPort()
if not unoPort:
    print("No Arduino Uno found")
    sys.exit("No Arduino Uno found - Exit")

print("Arduino Uno found: " + unoPort)
print()

serialUno = serial.Serial(unoPort, 9600)
print("serialUno.isOpen() = " + str(serialUno.isOpen()))

while True:
    while (serialUno.inWaiting()==0):
        pass
    valueRead = serialUno.readline(500)
    print(valueRead)



In Arduino Uno side, just a simple program to repeatly call Serial.println() for testing.
AnalogInSerialOut.ino
const int analogIn = A0;
int analogVal = 0;

void setup() {
  Serial.begin(9600);
}

void loop() {

  analogVal = analogRead(analogIn);
  Serial.println(analogVal);
  delay(1000);
}

1 comment: