diff --git a/README.md b/README.md index b3b83c7..06c7249 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,70 @@ # OctoPrint Firmware Updater - -This plugin can be used to flash pre-compiled firmware images to your printer from a file or URL. - -

Firmware Updater

- -## Works with -* Atmel AVR family 8-bit MCUs (Atmega644, Atmega1280, Atmega1284p, and Atmega2560, etc.) -* Atmel SAM family 32-bit MCUs (Arduino DUE, etc.) -* LPC1768 boards (MKS SBASE, SKR v1.1 and v1.3, etc.) - -Please open a Github issue if you would like a new board or MCU to be supported. If it's a new type of board which requires hardware testing please consider making a [donation](#Donations) to help fund the costs. - -## Setup - +The Firmware Updater plugin can be used to flash pre-compiled firmware images to your printer from a local file or URL. + +Firmware Updater + +## Documentation Index +1. [Supported Boards](#supported-boards) +1. [Plugin Installation](#plugin-installation) +1. [Plugin Configuration](#plugin-configuration) +1. Board-Specific Configuration + 1. [ATmega Boards](#atmega-boards) + 1. [Avrdude Installation](#avrdude-installation) + 1. [Avrdude Configuration](#avrdude-configuration) + 1. [AT90USB Boards](#at90usb-boards) + 1. [Dfu-programmer Installation](#dfu-programmer-installation) + 1. [Dfu-programmer Configuration](#dfu-programmer-configuration) + 1. [LPC1768 Boards](#lpc1768-boards) + 1. [Usbmount Installation](#usbmount-installation) + 1. [SAM Boards](#sam-boards) + 1. [Bossac Installation](#bossac-installation) + 1. [Bossac Configuration](#bossac-configuration) +1. [Customizing the Command Lines](#customizing-the-command-lines) +1. [Pre and Post Flash Settings](#pre-and-post-flash-settings) +1. [Flashing](#flashing) +1. [Troubleshooting](#troubleshooting) +1. [Donations](#donations) + +## Supported Boards +The plugn supports a variety of boards, based on the MCU (processor) they have: +* 'ATmega' family 8-bit MCUs (RAMPS, Sanguinololu, Melzi, Anet, Creality, Ender, many others) +* 'AT90USB' family 8-bit MCUs (Printrboard) +* 'LPC1768' MCUs (MKS SBASE, SKR v1.1 and v1.3, etc.) +* 'SAM' family 32-bit MCUs (Arduino DUE, etc.) + +Please open a [Github issue](https://github.com/OctoPrint/OctoPrint-FirmwareUpdater/issues) if you would like a new board or MCU to be supported. If it's a new type of board which requires hardware testing please consider making a [donation](#Donations) to help fund the costs. + +## Plugin Installation Install via the bundled [Plugin Manager](https://github.com/foosel/OctoPrint/wiki/Plugin:-Plugin-Manager) or manually using this URL: - https://github.com/OctoPrint/OctoPrint-FirmwareUpdater/archive/master.zip -The appropriate flashing tool also needs to be installed. For 8-bit MCUs the tool is **avrdude**, for 32-bit MCUs the tool is **bossac**, for LPC1768 an external tool is not needed, but there is additional configuration to get make the on-board SD card accessible. +## Plugin Configuration +The appropriate flashing tool for the board type needs to be selected. + +| Board Family | Flashing Tool | +| --- | --- | +| ATmega | avrdude | +| AT90USB | dfu-programmer | +| LPC1768 | lpc1768 | +| SAM | bossac | + +Plugin settings vary depending on the flashing tool and are documented below. -### AVRDUDE Installation +## Board-Specific Configuration -AVRDUDE needs to be installed on the server where OctoPrint is running. +### ATmega Boards +To flash an ATmega-based board the tool `avrdude` needs to be installed on the OctoPrint host. -#### Raspberry Pi +#### Avrdude Installation +##### Raspberry Pi ``` sudo apt-get update sudo apt-get install avrdude ``` -#### Ubuntu (12.04 - 14.04 - 15.04) - +##### Ubuntu (12.04 - 14.04 - 15.04) Information about the package needed can be found here [Ubuntu avrdude package](https://launchpad.net/ubuntu/+source/avrdude) ``` @@ -41,30 +73,73 @@ sudo apt-get update sudo apt-get install avrdude ``` -### BOSSAC Installation -Bossac cannot be installed using a package manager as the packaged version is out of date and will not work. Installation from source is straight-forward. +#### Avrdude Configuration +The minimum settings are: +* AVR MCU Type +* Path to avrdude +* AVR Programmer Type + +Typical MCU/programmer combinations are: -#### Raspberry Pi / Ubuntu +| AVR MCU | Programmer | Example Board | +| --- | --- | --- | +| Atmega1284p | arduino | Anet A series, Creality, Ender, etc. | +| Atmega2560 | wiring | RAMPS, RAMbo, etc. | +| Atmega644p | arduino | Sanguinololu, Melzi | + +Optional advanced settings are available for: +* Baud rate - sets the speed for communication with the board +* Avrdude config file - overrides the default config file with a custom one +* Disabling write verification - speeds up flashing by not verifying the write operation +* Customizing the avrdude command line +* Disabling the bootloader warning - disables a warning which is shown the hex filename has 'bootloader' in it + +### AT90USB Boards +To flash an AT90USB-based board the tool `dfu-programmer` needs to be installed on the OctoPrint host. + +#### Dfu-programmer Installation +A version of `dfu-programmer` can be installed via `apt-get install` but it is outdated. Please build the latest version from [Github](https://github.com/dfu-programmer/dfu-programmer) using these commands: ``` -cd ~/ -sudo apt-get install libwxgtk3.0-dev libreadline-dev -wget https://github.com/shumatech/BOSSA/archive/1.7.0.zip -unzip 1.7.0.zip -cd BOSSA-1.7.0 -./arduino/make_package.sh -sudo cp ~/BOSSA-1.7.0/bin/bossac /usr/local/bin/ +cd ~ +sudo apt-get install autoconf libusb-1.0-0-dev +git clone https://github.com/dfu-programmer/dfu-programmer.git +cd dfu-programmer +./bootstrap.sh +./configure +make +sudo make install ``` +If there were no errors `dfu-programmer` should now be installed at /usr/local/bin/dfu-programmer. + +#### Dfu-programmer Configuration +The minimum settings are: +* AVR MCU Type +* Path to dfu-programmer -### LPC 1768 Installation +Optional advanced settings are available for: +* Customizing the command lines for erasing and flashing the board + +#### DFU Mode +AT90USB boards must be in **Boot** or **DFU** mode before they can be flashed. This is done by placing or removing a jumper then resetting the board. + +For Printrboard: +* Remove the BOOT jumper (for Rev D,E & F boards, install the BOOT jumper) +* Press and release the **Reset** button. +* Replace the BOOT jumper onto the board (for Rev D, E & F boards, remove the BOOT jumper) + +The board will now be ready for flashing. Once flashing is complete the board reset by pressing the **Reset** button again. + +### LPC1768 Boards Flashing an LPC1768 board requires that the host can mount the board's on-board SD card to a known mount point in the host filesystem. -There are several ways to do this, but using [usbmount](https://github.com/rbrito/usbmount) works well and is documented here. It will mount the SD card to `/media/usb`. +There are several ways to do this, but using [usbmount](https://github.com/rbrito/usbmount) works well and is documented below. It will mount the SD card to `/media/usb`. **Note:** The Marlin board configuration must have `USB_SD_ONBOARD` enabled so that the on-board SD card is presented to the host via the USB connection. This seems to be the default configuration for Marlin's LPC1768 boards. It is configured in the board's pins file. Once installed, usbmount requires some tweaking to make it work well on the Raspberry Pi. The instructions below assume that you are running OctoPrint on a Raspberry Pi, as the user 'pi'. +#### Usbmount Installation 1. Install usbmount `sudo apt-get install usbmount` @@ -95,54 +170,52 @@ Once installed, usbmount requires some tweaking to make it work well on the Rasp Once usbmount is installed and configured the LPC1768 on-board SD card should be mounted at `/media/usb` the next time it is plugged in or restarted. -#### Minimum Marlin Firmware Version +#### LPC1768 Configuration +The only required setting is the path to the firmware update folder. If using usbmount it will probably be `/media/usb`. + +Optional advanced settings are available for: +* Resetting the board prior to flashing - adds an extra board reset which can help ensure that the SD card is mounted correctly + +##### Minimum Marlin Firmware Version Some boards (e.g. SKR v1.3) have been known to ship with older Marlin firmware which does not support the `M997` command, so must be updated conventionally one time before using the plugin. A board running too-old Marlin firmware will log 'Board reset failed' when attempting to flash from the plugin. If flashing an existing Marlin installation, the existing firmware must be newer than March 2nd, 2019 (i.e [this commit](https://github.com/MarlinFirmware/Marlin/pull/13281)) as that is when the `M997` was added to support resetting the board. -#### Troubleshooting LPC1768 Uploads +##### Troubleshooting LPC1768 Uploads The firmware upload will fail if the SD card is not accessible, either because it is not mounted on the host, or because the printer firmware has control over it. -Try: -* Reset the board -* Check that the 'Path to firmware folder' 'Test' button gives a successful result -* Use the OctoPrint terminal to send an `M22` command to release the SD card from the firmware - -## Configuration - -In order to be able to flash firmware we need to select and configure a flash method. Once the flash method is selected additional options will be available. +### SAM Boards +To flash a SAM-based board the tool `bossac` needs to be installed on the OctoPrint host. -### AVRDUDE Configuration -

Firmware Updater Settings

- -The minimum settings are: -* Path to avrdude -* AVR MCU Type -* AVR Programmer Type - -Typical MCU/programmer combinations are: +#### Bossac Installation +Bossac cannot be installed using a package manager as the packaged version is out of date and will not work. Installation from source is straight-forward. -| AVR MCU | Programmer | Example Board | -| --- | --- | --- | -| Atmega1284p | arduino | Anet A series | -| Atmega2560 | wiring | RAMPS, RAMbo, etc. | -| Atmega644p | arduino | Sanguinololu, Melzi | +``` +cd ~/ +sudo apt-get install libwxgtk3.0-dev libreadline-dev +wget https://github.com/shumatech/BOSSA/archive/1.7.0.zip +unzip 1.7.0.zip +cd BOSSA-1.7.0 +./arduino/make_package.sh +sudo cp ~/BOSSA-1.7.0/bin/bossac /usr/local/bin/ +``` -### BOSSAC Configuration -

Firmware Updater Settings

+#### Bossac Configuration The only required setting is the path to the bossac binary. -### LPC1768 Configuration -The only required setting is the path to the firmware update folder. If using usbmount it will probably be `/media/usb`. +Optional advanced settings are available for: +* Disabling write verification - speeds up flashing by not verifying the write operation +* Customizing the bossac command line -### Customizing the Command Lines -The command lines for avrdude and bossac can be customized by editing the string in the advanced settings for the flash method. Text in braces (`{}`) will be substituted for preconfigured values if present. +## Customizing the Command Lines +The command lines for `avrdude`, `bossac`, and `dfu-programmer` can be customized by editing the string in the advanced settings for the flash method. Text in braces (`{}`) will be substituted for preconfigured values if present. | String | Description| | --- | --- | -| `{avrdude}` | Full path to the avrdude executable1 | -| `{bossac}` | Full path to the bossac executable2 | -| `{mcu}` | Avrdude MCU type1 | +| `{avrdude}` | Full path to the `avrdude` executable1 | +| `{bossac}` | Full path to the `bossac` executable2 | +| `{dfuprogrammer}` | Full path to the `dfu-programmer` executable3 | +| `{mcu}` | MCU type4 | | `{programmer}` | Avrdude programmer1 | | `{port}` | COM port the printer is connected to | | `{conffile}` | Full path to the avrdude configuration file1 | @@ -150,8 +223,10 @@ The command lines for avrdude and bossac can be customized by editing the string | `{disableverify}` | Switch to disable write verification | | `{firmware}` | Path to the uploaded firmware file | -1. Avrdude flash method only -2. Bossac dude flash method only +1. avrdude flash method only +2. bossac flash method only +3. dfu-programmer flash method only +4. avrdude and dfu-programmer flash methods #### Command Line Defaults Command lines can be returned to the default by clicking the **Reset** button. @@ -162,11 +237,19 @@ Command lines can be returned to the default by clicking the **Reset** button. ##### Bossac `{bossac} -i -p {port} -U true -e -w {disableverify} -b {firmware} -R` -### Post-flash Settings -

Firmware Updater Settings

+##### Dfu-programmer +Erase: `{bossac} -i -p {port} -U true -e -w {disableverify} -b {firmware} -R` +Flash: + +## Pre and Post-flash Settings +#### Pre-flash Command +Specify a system command to run on the host prior to flashing. + +#### Pre-flash Command +Specify a system command to run on the host after flashing. #### Post-flash Delay #### -This setting can be used to insert a delay of up to 180s after the firmware has been uploaded, before OctoPrint will try to reconnect to the printer. This can be useful if the board takes some time to restart. A delay of 20-30s is usually enough. +This setting can be used to insert a delay of up to 180s after the firmware has been uploaded. This can be useful if the board takes some time to restart. A delay of 20-30s is usually enough. #### Post-flash Gcode #### You can use the post-flash gcode settings to run gcode commands after a successful firmware flash. diff --git a/octoprint_firmwareupdater/__init__.py b/octoprint_firmwareupdater/__init__.py index 3d072e4..e994670 100644 --- a/octoprint_firmwareupdater/__init__.py +++ b/octoprint_firmwareupdater/__init__.py @@ -39,6 +39,12 @@ class FirmwareupdaterPlugin(octoprint.plugin.BlueprintPlugin, BOSSAC_VERIFYING = "bytes of flash" BOSSAC_NODEVICE = "No device found on" + DFUPROG_ERASING = "Erasing flash" + DFUPROG_WRITING = "Programming" + DFUPROG_VERIFYING = "Reading" + DFUPROG_VALIDATING = "Validating" + DFUPROG_NODEVICE = "no device present" + def __init__(self): self._flash_thread = None @@ -50,8 +56,8 @@ def __init__(self): def initialize(self): # TODO: make method configurable via new plugin hook "octoprint.plugin.firmwareupdater.flash_methods", # also include prechecks - self._flash_prechecks = dict(avrdude=self._check_avrdude, bossac=self._check_bossac, lpc1768=self._check_lpc1768) - self._flash_methods = dict(avrdude=self._flash_avrdude, bossac=self._flash_bossac, lpc1768=self._flash_lpc1768) + self._flash_prechecks = dict(avrdude=self._check_avrdude, bossac=self._check_bossac, dfuprogrammer=self._check_dfuprog, lpc1768=self._check_lpc1768) + self._flash_methods = dict(avrdude=self._flash_avrdude, bossac=self._flash_bossac, dfuprogrammer=self._flash_dfuprog, lpc1768=self._flash_lpc1768) console_logging_handler = logging.handlers.RotatingFileHandler(self._settings.get_plugin_logfile_path(postfix="console"), maxBytes=2*1024*1024) console_logging_handler.setFormatter(logging.Formatter("%(asctime)s %(message)s")) @@ -62,7 +68,6 @@ def initialize(self): self._console_logger.setLevel(logging.DEBUG) self._console_logger.propagate = False - # Event handler def on_event(self, event, payload): #self._logger.info("Got event: {}".format(event)) @@ -210,12 +215,16 @@ def _flash_worker(self, method, firmware, printer_port): self._logger.info("Firmware update started") if not method in self._flash_methods: - self._logger.error("Unsupported flashing method: {}".format(method)) + error_message = "Unsupported flashing method: {}".format(method) + self._logger.error(error_message) + self._send_status("flasherror", message=error_message) return flash_callable = self._flash_methods[method] if not callable(flash_callable): - self._logger.error("Don't have a callable for flashing method {}: {!r}".format(method, flash_callable)) + error_message = "Don't have a callable for flashing method {}: {!r}".format(method, flash_callable) + self._logger.error(error_message) + self._send_status("flasherror", message=error_message) return reconnect = None @@ -322,6 +331,7 @@ def _flash_avrdude(self, firmware=None, printer_port=None): import sarge self._logger.info(u"Running '{}' in {}".format(avrdude_command, working_dir)) + self._console_logger.info(u"") self._console_logger.info(avrdude_command) try: p = sarge.run(avrdude_command, cwd=working_dir, async=True, stdout=sarge.Capture(), stderr=sarge.Capture()) @@ -430,6 +440,7 @@ def _flash_bossac(self, firmware=None, printer_port=None): import sarge self._logger.info(u"Running '{}' in {}".format(bossac_command, working_dir)) + self._console_logger.info(u"") self._console_logger.info(bossac_command) try: p = sarge.run(bossac_command, cwd=working_dir, async=True, stdout=sarge.Capture(buffer_size=1), stderr=sarge.Capture(buffer_size=1)) @@ -440,7 +451,7 @@ def _flash_bossac(self, firmware=None, printer_port=None): if not output: p.commands[0].poll() continue - + for line in output.split("\n"): if line.endswith("\r"): line = line[:-1] @@ -455,11 +466,6 @@ def _flash_bossac(self, firmware=None, printer_port=None): elif self.BOSSAC_VERIFYING in line: self._logger.info(u"Verifying memory...") self._send_status("progress", subtype="verifying") - elif self.AVRDUDE_TIMEOUT in line: - p.close() - raise FlashException("Timeout communicating with programmer") - elif self.BOSSAC_NODEVICE in line: - raise FlashException("No device found") elif self.AVRDUDE_ERROR_VERIFICATION in line: raise FlashException("Error verifying flash") elif self.AVRDUDE_ERROR in line: @@ -468,6 +474,15 @@ def _flash_bossac(self, firmware=None, printer_port=None): if p.returncode == 0: return True else: + output = p.stderr.read(timeout=0.5) + for line in output.split("\n"): + if line.endswith("\r"): + line = line[:-1] + self._console_logger.info(u"> {}".format(line)) + + if self.BOSSAC_NODEVICE in line: + raise FlashException(line) + raise FlashException("bossac returned code {returncode}".format(returncode=p.returncode)) except FlashException as ex: @@ -501,6 +516,132 @@ def _check_bossac(self): else: return True + def _flash_dfuprog(self, firmware=None, printer_port=None): + assert(firmware is not None) + + if not self._erase_dfuprog(): + return False + + dfuprog_path = self._settings.get(["dfuprog_path"]) + dfuprog_avrmcu = self._settings.get(["dfuprog_avrmcu"]) + working_dir = os.path.dirname(dfuprog_path) + + dfuprog_command = self._settings.get(["dfuprog_commandline"]) + dfuprog_command = dfuprog_command.replace("{dfuprogrammer}", dfuprog_path) + dfuprog_command = dfuprog_command.replace("{mcu}", dfuprog_avrmcu) + dfuprog_command = dfuprog_command.replace("{firmware}", firmware) + + import sarge + self._logger.info(u"Running '{}' in {}".format(dfuprog_command, working_dir)) + self._send_status("progress", subtype="writing") + self._console_logger.info(dfuprog_command) + try: + p = sarge.run(dfuprog_command, cwd=working_dir, async=True, stdout=sarge.Capture(buffer_size=1), stderr=sarge.Capture(buffer_size=1)) + p.wait_events() + + while p.returncode is None: + output = p.stderr.read(timeout=0.5) + if not output: + p.commands[0].poll() + continue + + for line in output.split("\n"): + if line.endswith("\r"): + line = line[:-1] + self._console_logger.info(u"> {}".format(line)) + + if self.DFUPROG_WRITING in line: + self._logger.info(u"Writing memory...") + self._send_status("progress", subtype="writing") + if self.DFUPROG_VERIFYING in line: + self._logger.info(u"Verifying memory...") + self._send_status("progress", subtype="verifying") + elif self.DFUPROG_NODEVICE in line: + raise FlashException("No device found") + + if p.returncode == 0: + return True + else: + raise FlashException("dfu-programmer returned code {returncode}".format(returncode=p.returncode)) + + except FlashException as ex: + self._logger.error(u"Flashing failed. {error}.".format(error=ex.reason)) + self._send_status("flasherror", message=ex.reason) + return False + except: + self._logger.exception(u"Flashing failed. Unexpected error.") + self._send_status("flasherror") + return False + + def _erase_dfuprog(self): + dfuprog_path = self._settings.get(["dfuprog_path"]) + dfuprog_avrmcu = self._settings.get(["dfuprog_avrmcu"]) + working_dir = os.path.dirname(dfuprog_path) + + dfuprog_erasecommand = self._settings.get(["dfuprog_erasecommandline"]) + dfuprog_erasecommand = dfuprog_erasecommand.replace("{dfuprogrammer}", dfuprog_path) + dfuprog_erasecommand = dfuprog_erasecommand.replace("{mcu}", dfuprog_avrmcu) + + import sarge + self._logger.info(u"Running '{}' in {}".format(dfuprog_erasecommand, working_dir)) + self._console_logger.info(dfuprog_erasecommand) + try: + p = sarge.run(dfuprog_erasecommand, cwd=working_dir, async=True, stdout=sarge.Capture(buffer_size=1), stderr=sarge.Capture(buffer_size=1)) + p.wait_events() + + while p.returncode is None: + output = p.stderr.read(timeout=0.5) + if not output: + p.commands[0].poll() + continue + + for line in output.split("\n"): + if line.endswith("\r"): + line = line[:-1] + self._console_logger.info(u"> {}".format(line)) + + if self.DFUPROG_ERASING in line: + self._logger.info(u"Erasing memory...") + self._send_status("progress", subtype="erasing") + elif self.DFUPROG_NODEVICE in line: + raise FlashException("No device found") + + if p.returncode == 0: + return True + else: + raise FlashException("dfu-programmer returned code {returncode}".format(returncode=p.returncode)) + + except FlashException as ex: + self._logger.error(u"Erasing failed. {error}.".format(error=ex.reason)) + self._send_status("flasherror", message=ex.reason) + return False + except: + self._logger.exception(u"Erasing failed. Unexpected error.") + self._send_status("flasherror") + return False + + def _check_dfuprog(self): + dfuprog_path = self._settings.get(["dfuprog_path"]) + pattern = re.compile("^(\/[^\0/]+)+$") + + if not pattern.match(dfuprog_path): + self._logger.error(u"Path to dfu-programmer is not valid: {path}".format(path=dfuprog_path)) + return False + elif dfuprog_path is None: + self._logger.error(u"Path to dfu-programmer is not set.") + return False + if not os.path.exists(dfuprog_path): + self._logger.error(u"Path to dfu-programmer does not exist: {path}".format(path=dfuprog_path)) + return False + elif not os.path.isfile(dfuprog_path): + self._logger.error(u"Path to dfu-programmer is not a file: {path}".format(path=dfuprog_path)) + return False + elif not os.access(dfuprog_path, os.X_OK): + self._logger.error(u"Path to dfu-programmer is not executable: {path}".format(path=dfuprog_path)) + return False + else: + return True + def _flash_lpc1768(self, firmware=None, printer_port=None): assert(firmware is not None) assert(printer_port is not None) @@ -743,6 +884,10 @@ def get_settings_defaults(self): "bossac_path": None, "bossac_commandline": "{bossac} -i -p {port} -U true -e -w {disableverify} -b {firmware} -R", "bossac_disableverify": None, + "dfuprog_path": None, + "dfuprog_avrmcu": None, + "dfuprog_commandline": "sudo {dfuprogrammer} {mcu} flash {firmware} --debug-level 10", + "dfuprog_erasecommandline": "sudo {dfuprogrammer} {mcu} erase --debug-level 10 --force", "lpc1768_path": None, "lpc1768_preflashreset": True, "postflash_delay": "0", diff --git a/octoprint_firmwareupdater/static/js/firmwareupdater.js b/octoprint_firmwareupdater/static/js/firmwareupdater.js index eb36940..85adae5 100644 --- a/octoprint_firmwareupdater/static/js/firmwareupdater.js +++ b/octoprint_firmwareupdater/static/js/firmwareupdater.js @@ -13,6 +13,7 @@ $(function() { self.showAvrdudeConfig = ko.observable(false); self.showBossacConfig = ko.observable(false); self.showLpc1768Config = ko.observable(false); + self.showDfuConfig = ko.observable(false); self.showPostflashConfig = ko.observable(false); self.configEnablePostflashDelay = ko.observable(); self.configPostflashDelay = ko.observable(); @@ -69,6 +70,18 @@ $(function() { return self.lpc1768PathBroken() || self.lpc1768PathOk(); }); + // Config settings for dfu-programmer + self.configDfuMcu = ko.observable(); + self.configDfuPath = ko.observable(); + self.configDfuCommandLine = ko.observable(); + self.configDfuEraseCommandLine = ko.observable(); + self.dfuPathBroken = ko.observable(false); + self.dfuPathOk = ko.observable(false); + self.dfuPathText = ko.observable(); + self.dfuPathHelpVisible = ko.computed(function() { + return self.dfuPathBroken() || self.dfuPathOk(); + }); + self.flashPort = ko.observable(undefined); self.firmwareFileName = ko.observable(undefined); @@ -106,18 +119,27 @@ $(function() { self.showAvrdudeConfig(true); self.showBossacConfig(false); self.showLpc1768Config(false); + self.showDfuConfig(false); } else if(value == 'bossac') { self.showAvrdudeConfig(false); self.showBossacConfig(true); self.showLpc1768Config(false); + self.showDfuConfig(false); } else if(value == 'lpc1768'){ self.showAvrdudeConfig(false); self.showBossacConfig(false); self.showLpc1768Config(true); + self.showDfuConfig(false); + } else if(value == 'dfuprogrammer'){ + self.showAvrdudeConfig(false); + self.showBossacConfig(false); + self.showLpc1768Config(false); + self.showDfuConfig(true); } else { self.showAvrdudeConfig(false); self.showBossacConfig(false); self.showLpc1768Config(false); + self.showDfuConfig(false); } }); @@ -183,7 +205,15 @@ $(function() { alert = gettext("The lpc1768 firmware folder path is not configured."); } - if (!self.flashPort()) { + if (self.settingsViewModel.settings.plugins.firmwareupdater.flash_method() == "dfuprogrammer" && !self.settingsViewModel.settings.plugins.firmwareupdater.dfuprog_path()) { + alert = gettext("The dfu-programmer path is not configured."); + } + + if (self.settingsViewModel.settings.plugins.firmwareupdater.flash_method() == "dfuprogrammer" && !self.settingsViewModel.settings.plugins.firmwareupdater.dfuprog_avrmcu()) { + alert = gettext("The AVR MCU type is not selected."); + } + + if (!self.flashPort() &! self.settingsViewModel.settings.plugins.firmwareupdater.flash_method() == "dfuprogrammer") { alert = gettext("The printer port is not selected."); } @@ -415,6 +445,12 @@ $(function() { self.configBossacDisableVerification(self.settingsViewModel.settings.plugins.firmwareupdater.bossac_disableverify()); self.configBossacCommandLine(self.settingsViewModel.settings.plugins.firmwareupdater.bossac_commandline()); + // Load the dfu-programmer settings + self.configDfuPath(self.settingsViewModel.settings.plugins.firmwareupdater.dfuprog_path()); + self.configDfuMcu(self.settingsViewModel.settings.plugins.firmwareupdater.dfuprog_avrmcu()); + self.configDfuCommandLine(self.settingsViewModel.settings.plugins.firmwareupdater.dfuprog_commandline()); + self.configDfuEraseCommandLine(self.settingsViewModel.settings.plugins.firmwareupdater.dfuprog_erasecommandline()); + // Load the lpc1768 settings self.configLpc1768Path(self.settingsViewModel.settings.plugins.firmwareupdater.lpc1768_path()); if(self.settingsViewModel.settings.plugins.firmwareupdater.lpc1768_preflashreset() != 'false') { @@ -446,6 +482,10 @@ $(function() { bossac_path: self.configBossacPath(), bossac_disableverify: self.configBossacDisableVerification(), bossac_commandline: self.configBossacCommandLine(), + dfuprog_path: self.configDfuPath(), + dfuprog_avrmcu: self.configDfuMcu(), + dfuprog_commandline: self.configDfuCommandLine(), + dfuprog_erasecommandline: self.configDfuEraseCommandLine(), lpc1768_path: self.configLpc1768Path(), lpc1768_preflashreset: self.configLpc1768ResetBeforeFlash(), enable_preflash_commandline: self.configEnablePreflashCommandline(), @@ -473,9 +513,10 @@ $(function() { }; self.testAvrdudePath = function() { - var filePathRegEx = new RegExp("^(\/[^\0/]+)+$"); + var filePathRegEx_Linux = new RegExp("^(\/[^\0/]+)+$"); + var filePathRegEx_Windows = new RegExp("^[A-z]\:\\\\.+.exe$"); - if (!filePathRegEx.test(self.configAvrdudePath())) { + if ( !filePathRegEx_Linux.test(self.configAvrdudePath()) && !filePathRegEx_Windows.test(self.configAvrdudePath()) ) { self.avrdudePathText(gettext("The path is not valid")); self.avrdudePathOk(false); self.avrdudePathBroken(true); @@ -515,9 +556,10 @@ $(function() { }; self.testBossacPath = function() { - var filePathRegEx = new RegExp("^(\/[^\0/]+)+$"); + var filePathRegEx_Linux = new RegExp("^(\/[^\0/]+)+$"); + var filePathRegEx_Windows = new RegExp("^[A-z]\:\\\\.+.exe$"); - if (!filePathRegEx.test(self.configBossacPath())) { + if ( !filePathRegEx_Linux.test(self.configBossacPath()) && !filePathRegEx_Windows.test(self.configBossacPath()) ) { self.bossacPathText(gettext("The path is not valid")); self.bossacPathOk(false); self.bossacPathBroken(true); @@ -556,6 +598,52 @@ $(function() { self.configBossacCommandLine("{bossac} -i -p {port} -U true -e -w {disableverify} -b {firmware} -R"); }; + self.testDfuPath = function() { + var filePathRegEx = new RegExp("^(\/[^\0/]+)+$"); + + if (!filePathRegEx.test(self.configDfuPath())) { + self.dfuPathText(gettext("The path is not valid")); + self.dfuPathOk(false); + self.dfuPathBroken(true); + } else { + $.ajax({ + url: API_BASEURL + "util/test", + type: "POST", + dataType: "json", + data: JSON.stringify({ + command: "path", + path: self.configDfuPath(), + check_type: "file", + check_access: "x" + }), + contentType: "application/json; charset=UTF-8", + success: function(response) { + if (!response.result) { + if (!response.exists) { + self.dfuPathText(gettext("The path doesn't exist")); + } else if (!response.typeok) { + self.dfuPathText(gettext("The path is not a file")); + } else if (!response.access) { + self.dfuPathText(gettext("The path is not an executable")); + } + } else { + self.dfuPathText(gettext("The path is valid")); + } + self.dfuPathOk(response.result); + self.dfuPathBroken(!response.result); + } + }) + } + }; + + self.resetDfuCommandLine = function() { + self.configDfuCommandLine("sudo {dfuprogrammer} {mcu} flash {firmware} --debug-level 10 --force"); + }; + + self.resetDfuEraseCommandLine = function() { + self.configDfuEraseCommandLine("sudo {dfuprogrammer} {mcu} erase --debug-level 10"); + }; + self.testAvrdudeConf = function() { $.ajax({ url: API_BASEURL + "util/test", diff --git a/octoprint_firmwareupdater/templates/firmwareupdater_settings.jinja2 b/octoprint_firmwareupdater/templates/firmwareupdater_settings.jinja2 index 1969e55..f579fd2 100644 --- a/octoprint_firmwareupdater/templates/firmwareupdater_settings.jinja2 +++ b/octoprint_firmwareupdater/templates/firmwareupdater_settings.jinja2 @@ -70,8 +70,9 @@
  • Do not attempt to flash a firmware file which includes a bootloader
  • No warranty is given, and no responsibility can be accepted if there are problems. By using this plugin you accept the associated risks.

    - - +   
    Documentation
    +

    Documentation is available on Github

    + @@ -167,9 +169,35 @@ + +
    +
    + +
    + +
    +
    +
    + +
    +
    + + +
    + +
    +
    +
    +
    + + -
    +
    @@ -275,13 +303,36 @@
    + +
    +
    + +
    +
    + + +
    + {{ _('Customize the dfu-programmer erase command line.') }} +
    +
    +
    + +
    +
    + + +
    + {{ _('Customize the dfu-programmer flash command line.') }} +
    +
    +

    -
    +
    diff --git a/setup.py b/setup.py index 8058dad..7c5ad4a 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ plugin_name = "OctoPrint-FirmwareUpdater" # The plugin's version. Can be overwritten within OctoPrint's internal data via __plugin_version__ in the plugin module -plugin_version = "1.3.1" +plugin_version = "1.4.0" # The plugin's description. Can be overwritten within OctoPrint's internal data via __plugin_description__ in the plugin # module