126 lines
3.8 KiB
Python
Executable file
126 lines
3.8 KiB
Python
Executable file
#!/usr/bin/python3
|
|
|
|
import serial
|
|
import serial.tools.list_ports
|
|
import os.path
|
|
import argparse
|
|
from time import sleep
|
|
|
|
parser = argparse.ArgumentParser(description='Reset an Arduino')
|
|
parser.add_argument('--zero', action='store_true', help='Reset Arduino Zero or similar Native USB to enter bootloader')
|
|
parser.add_argument('--caterina', action='store_true', help='Reset a Leonardo, Micro, Robot or LilyPadUSB.')
|
|
parser.add_argument('--verbose', action='store_true', help="Watch what's going on on STDERR.")
|
|
parser.add_argument('--period', default=0.1, help='Specify the DTR pulse width in seconds.')
|
|
parser.add_argument('port', nargs=1, help='Serial device e.g. /dev/ttyACM0')
|
|
args = parser.parse_args()
|
|
|
|
|
|
def list_ports(output=False):
|
|
""" Lists serial ports attached
|
|
|
|
:returns
|
|
A list of paths to serial ports on system
|
|
"""
|
|
ports = serial.tools.list_ports.comports()
|
|
connected = [port[0] for port in ports]
|
|
if output:
|
|
print(connected)
|
|
|
|
return connected
|
|
|
|
|
|
def new_port(old, new):
|
|
""" Checks if a new port has attached
|
|
|
|
Args:
|
|
old: previous list of ports to check
|
|
new: current list of ports to check
|
|
Returns:
|
|
index of port in 'new' if new port found, otherwise -1
|
|
"""
|
|
new_port = -1
|
|
|
|
for port in new:
|
|
if port not in old:
|
|
new_port = new.index(port)
|
|
break
|
|
|
|
return new_port
|
|
|
|
|
|
if args.zero:
|
|
# number of trys to attempt
|
|
zero_attempts = 20 # ~2 seconds
|
|
initial_ports = list_ports(args.verbose)
|
|
|
|
if args.verbose:
|
|
print('Attempting to enter bootloader using 1200 bps open/close on port %s' % args.port[0])
|
|
|
|
ser = serial.Serial(args.port[0], 57600)
|
|
ser.close()
|
|
ser.baudrate = 1200
|
|
|
|
# do the open/close at 1200 BAUD
|
|
ser.open()
|
|
ser.close()
|
|
|
|
if args.verbose:
|
|
print('Done. Waiting for bootloader port to attach...')
|
|
|
|
# get new list of ports
|
|
reset_ports = list_ports(args.verbose)
|
|
|
|
# wait for new port or port to return
|
|
port_index = new_port(initial_ports, reset_ports)
|
|
|
|
# keep checking until new port appears or timeout
|
|
while port_index < 0:
|
|
# count down attempts and leave if expired
|
|
zero_attempts -= 1
|
|
if zero_attempts < 0:
|
|
break
|
|
sleep(0.1)
|
|
# get list of ports after bootloader toggle performed
|
|
reset_ports = list_ports(args.verbose)
|
|
# if a port drops, set initial ports to reset ports so that
|
|
# next attached device will be new port
|
|
if (len(reset_ports) < len(initial_ports)):
|
|
initial_ports = reset_ports
|
|
# check if a new port has attached and return the index if it has
|
|
port_index = new_port(initial_ports, reset_ports)
|
|
# return the new port if detected, otherwise return passed port
|
|
if port_index == -1:
|
|
bootloader_port = args.port[0]
|
|
else:
|
|
bootloader_port = reset_ports[port_index]
|
|
|
|
# print so that `tail -1` can be piped for bootloader port
|
|
print(bootloader_port)
|
|
elif args.caterina:
|
|
if args.verbose: print('Forcing reset using 1200bps open/close on port %s' % args.port[0])
|
|
ser = serial.Serial(args.port[0], 57600)
|
|
ser.close()
|
|
|
|
if pyserial_version < 3:
|
|
ser.setBaudrate (1200)
|
|
else:
|
|
ser.baudrate = 1200
|
|
|
|
ser.open()
|
|
ser.setRTS(True) # RTS line needs to be held high and DTR low
|
|
ser.setDTR(False) # (see Arduino IDE source code)
|
|
ser.close()
|
|
sleep(1)
|
|
|
|
while not os.path.exists(args.port[0]):
|
|
if args.verbose: print('Waiting for %s to come back' % args.port[0])
|
|
sleep(1)
|
|
|
|
if args.verbose: print('%s has come back after reset' % args.port[0])
|
|
else:
|
|
if args.verbose: print('Setting DTR high on %s for %ss' % (args.port[0],args.period))
|
|
ser = serial.Serial(args.port[0], 115200)
|
|
ser.setDTR(False)
|
|
sleep(args.period)
|
|
ser.setDTR(True)
|
|
ser.close()
|