From 6ffbaa46c510ad92fb5604fb5c23bc8e1dc5b7cf Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Fri, 8 Mar 2019 20:42:00 -0500 Subject: [PATCH] flash_usb: Add a helper script for flashing Klipper over USB Add a script to help flash Klipper over USB. This tool can instruct Klipper to enter into bootloader mode and it can help track changes in device name during that transition. Signed-off-by: Kevin O'Connor --- scripts/flash_usb.py | 185 ++++++++++++++++++++++++++++++++++++++ scripts/install-octopi.sh | 2 +- 2 files changed, 186 insertions(+), 1 deletion(-) create mode 100755 scripts/flash_usb.py diff --git a/scripts/flash_usb.py b/scripts/flash_usb.py new file mode 100755 index 00000000..5b1a6865 --- /dev/null +++ b/scripts/flash_usb.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python2 +# Tool to enter a USB bootloader and flash Klipper +# +# Copyright (C) 2019 Kevin O'Connor +# +# This file may be distributed under the terms of the GNU GPLv3 license. +import sys, os, re, subprocess, optparse, time, fcntl, termios, struct + +class error(Exception): + pass + +# Attempt to enter bootloader via 1200 baud request +def enter_bootloader(device): + try: + f = open(device, 'rb') + fd = f.fileno() + fcntl.ioctl(fd, termios.TIOCMBIS, struct.pack('I', termios.TIOCM_DTR)) + t = termios.tcgetattr(fd) + t[4] = t[5] = termios.B1200 + termios.tcsetattr(fd, termios.TCSANOW, t) + fcntl.ioctl(fd, termios.TIOCMBIC, struct.pack('I', termios.TIOCM_DTR)) + f.close() + except (IOError, OSError) as e: + pass + time.sleep(1.0) + +# Translate a serial device name to a stable serial name in /dev/serial/by-path/ +def translate_serial_to_tty(device): + ttyname = os.path.realpath(device) + for fname in os.listdir('/dev/serial/by-path/'): + fname = '/dev/serial/by-path/' + fname + if os.path.realpath(fname) == ttyname: + return ttyname, fname + return ttyname, ttyname + +# Translate a serial device name to a usb path (suitable for dfu-util) +def translate_serial_to_usb_path(device): + realdev = os.path.realpath(device) + fname = os.path.basename(realdev) + try: + lname = os.readlink("/sys/class/tty/" + fname) + except OSError as e: + raise error("Unable to find tty device") + ttypath_r = re.compile(r".*/usb\d+.*/(?P\d+-[0-9.]+):\d+\.\d+/.*") + m = ttypath_r.match(lname) + if m is None: + raise error("Unable to find tty usb device") + return m.group("path") + +# Flash via a call to bossac +def flash_bossac(device, binfile, extra_flags=[]): + ttyname, pathname = translate_serial_to_tty(device) + enter_bootloader(pathname) + if os.path.exists(ttyname) and not os.path.exists(pathname): + pathname = ttyname + baseargs = ["lib/bossac/bin/bossac", "-U", "-p", pathname] + args = baseargs + extra_flags + ["-w", binfile, "-v", "-b"] + sys.stderr.write(" ".join(args) + '\n\n') + res = subprocess.call(args) + if res != 0: + raise error("Error running bossac") + if "-R" not in extra_flags: + time.sleep(0.500) + args = baseargs + ["-b", "-R"] + try: + subprocess.check_output(args, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + pass + +# Invoke the dfu-util program +def call_dfuutil(flags, binfile): + args = ["dfu-util"] + flags + ["-D", binfile] + sys.stderr.write(" ".join(args) + '\n\n') + res = subprocess.call(args) + if res != 0: + raise error("Error running dfu-util") + +# Flash via a call to dfu-util +def flash_dfuutil(device, binfile, extra_flags=[]): + hexfmt_r = re.compile(r"^[a-fA-F0-9]{4}:[a-fA-F0-9]{4}$") + if hexfmt_r.match(device.strip()): + call_dfuutil(["-d", ","+device.strip()] + extra_flags, binfile) + return + buspath = translate_serial_to_usb_path(device) + enter_bootloader(device) + call_dfuutil(["-p", buspath] + extra_flags, binfile) + + +###################################################################### +# Device specific helpers +###################################################################### + +def flash_atsam(options, binfile): + try: + flash_bossac(options.device, binfile, ["-e"]) + except error as e: + sys.stderr.write("Failed to flash to %s: %s\n" % ( + options.device, str(e))) + sys.exit(-1) + +def flash_atsamd(options, binfile): + extra_flags = ["--offset=" + options.offset, "-R"] + try: + flash_bossac(options.device, binfile, extra_flags) + except error as e: + sys.stderr.write("Failed to flash to %s: %s\n" % ( + options.device, str(e))) + sys.exit(-1) + +SMOOTHIE_HELP = """ +Failed to flash to %s: %s + +If flashing Klipper to a Smoothieboard for the first time it may be +necessary to manually place the board into "bootloader mode" - press +and hold the "Play button" and then press and release the "Reset +button". + +When a Smoothieboard is in bootloader mode it can be flashed with the +following command: + make flash FLASH_DEVICE=1d50:6015 + +Alternatively, one can flash a Smoothieboard via SD card - copy the +"out/klipper.bin" file to a file named "firmware.bin" on an SD card +and then restart the Smoothieboard with that SD card. + +""" + +def flash_lpc176x(options, binfile): + try: + flash_dfuutil(options.device, binfile) + except error as e: + sys.stderr.write(SMOOTHIE_HELP % (options.device, str(e))) + sys.exit(-1) + +STM32F1_HELP = """ +Failed to flash to %s: %s + +If the device is already in bootloader mode it can be flashed with the +following command: + make flash FLASH_DEVICE=1eaf:0003 + +If attempting to flash via 3.3V serial, then use: + make serialflash FLASH_DEVICE=%s + +""" + +def flash_stm32f1(options, binfile): + try: + flash_dfuutil(options.device, binfile, ["-R", "-a", "2"]) + except error as e: + sys.stderr.write(STM32F1_HELP % ( + options.device, str(e), options.device)) + sys.exit(-1) + +MCUTYPES = { + 'atsam': flash_atsam, 'atsamd': flash_atsamd, + 'lpc176x': flash_lpc176x, 'stm32f1': flash_stm32f1 +} + + +###################################################################### +# Startup +###################################################################### + +def main(): + usage = "%prog [options] -t -d " + opts = optparse.OptionParser(usage) + opts.add_option("-t", "--type", type="string", dest="mcutype", + help="micro-controller type") + opts.add_option("-d", "--device", type="string", dest="device", + help="serial port device") + opts.add_option("-o", "--offset", type="string", dest="offset", + help="flash offset") + options, args = opts.parse_args() + if len(args) != 1: + opts.error("Incorrect number of arguments") + if options.mcutype not in MCUTYPES: + opts.error("Not a valid mcu type") + if not options.device: + sys.stderr.write("\nPlease specify FLASH_DEVICE\n\n") + sys.exit(-1) + MCUTYPES[options.mcutype](options, args[0]) + +if __name__ == '__main__': + main() diff --git a/scripts/install-octopi.sh b/scripts/install-octopi.sh index 74b7bdf2..a260daae 100755 --- a/scripts/install-octopi.sh +++ b/scripts/install-octopi.sh @@ -16,7 +16,7 @@ install_packages() # AVR chip installation and building PKGLIST="${PKGLIST} avrdude gcc-avr binutils-avr avr-libc" # ARM chip installation and building - PKGLIST="${PKGLIST} stm32flash libnewlib-arm-none-eabi" + PKGLIST="${PKGLIST} stm32flash dfu-util libnewlib-arm-none-eabi" PKGLIST="${PKGLIST} gcc-arm-none-eabi binutils-arm-none-eabi" # Update system package info