Skip to content

Commit

Permalink
Add dfu-util as flash method
Browse files Browse the repository at this point in the history
  • Loading branch information
benlye committed Apr 13, 2021
1 parent 6a47a9b commit af08a9c
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 4 deletions.
8 changes: 7 additions & 1 deletion octoprint_firmwareupdater/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from octoprint_firmwareupdater.methods import bootcmdr
from octoprint_firmwareupdater.methods import bossac
from octoprint_firmwareupdater.methods import dfuprog
from octoprint_firmwareupdater.methods import dfuutil
from octoprint_firmwareupdater.methods import lpc1768
from octoprint_firmwareupdater.methods import stm32flash
from octoprint_firmwareupdater.methods import marlinbft
Expand Down Expand Up @@ -52,6 +53,7 @@ def initialize(self):
bootcmdr=bootcmdr._check_bootcmdr,
bossac=bossac._check_bossac,
dfuprogrammer=dfuprog._check_dfuprog,
dfuutil=dfuutil._check_dfuutil,
lpc1768=lpc1768._check_lpc1768,
stm32flash=stm32flash._check_stm32flash,
marlinbft=marlinbft._check_marlinbft
Expand All @@ -61,6 +63,7 @@ def initialize(self):
bootcmdr=bootcmdr._flash_bootcmdr,
bossac=bossac._flash_bossac,
dfuprogrammer=dfuprog._flash_dfuprog,
dfuutil=dfuutil._flash_dfuutil,
lpc1768=lpc1768._flash_lpc1768,
stm32flash=stm32flash._flash_stm32flash,
marlinbft=marlinbft._flash_marlinbft
Expand Down Expand Up @@ -116,7 +119,8 @@ def flash_firmware(self):

# Save the printer port
self._logger.info("Printer port: {}".format(printer_port))
self.set_profile_setting("serial_port", printer_port)
if printer_port != "undefined":
self.set_profile_setting("serial_port", printer_port)

method = self.get_profile_setting("flash_method")
self._logger.info("Flash method: {}".format(method))
Expand Down Expand Up @@ -554,6 +558,8 @@ def get_settings_defaults(self):
"dfuprog_avrmcu": None,
"dfuprog_commandline": "sudo {dfuprogrammer} {mcu} flash {firmware} --debug-level 10",
"dfuprog_erasecommandline": "sudo {dfuprogrammer} {mcu} erase --debug-level 10 --force",
"dfuutil_path": None,
"dfuutil_commandline": "sudo {dfuutil} -a 0 -s 0x8000000:leave -D {firmware}",
"stm32flash_path": None,
"stm32flash_verify": True,
"stm32flash_boot0pin": "rts",
Expand Down
89 changes: 89 additions & 0 deletions octoprint_firmwareupdater/methods/dfuutil.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import re
import os
import sarge

DFUUTIL_ERASING = "Erase "
DFUUTIL_WRITING = "Download "
DFUUTIL_NOACCESS = "Cannot open DFU device"
DFUUTIL_NODEVICE = "No DFU capable USB device available"

def _check_dfuutil(self):
dfuutil_path = self.get_profile_setting("dfuutil_path")
pattern = re.compile("^(\/[^\0/]+)+$")

if not pattern.match(dfuutil_path):
self._logger.error(u"Path to dfu-util is not valid: {path}".format(path=dfuutil_path))
return False
elif dfuutil_path is None:
self._logger.error(u"Path to dfu-util is not set.")
return False
if not os.path.exists(dfuutil_path):
self._logger.error(u"Path to dfu-util does not exist: {path}".format(path=dfuutil_path))
return False
elif not os.path.isfile(dfuutil_path):
self._logger.error(u"Path to dfu-util is not a file: {path}".format(path=dfuutil_path))
return False
elif not os.access(dfuutil_path, os.X_OK):
self._logger.error(u"Path to dfu-util is not executable: {path}".format(path=dfuutil_path))
return False
else:
return True

def _flash_dfuutil(self, firmware=None, printer_port=None, **kwargs):
assert(firmware is not None)

dfuutil_path = self.get_profile_setting("dfuutil_path")
working_dir = os.path.dirname(dfuutil_path)

dfuutil_command = self.get_profile_setting("dfuutil_commandline")
dfuutil_command = dfuutil_command.replace("{dfuutil}", dfuutil_path)
dfuutil_command = dfuutil_command.replace("{firmware}", firmware)

import sarge
self._logger.info(u"Running '{}' in {}".format(dfuutil_command, working_dir))
self._send_status("progress", subtype="writing")
self._console_logger.info(dfuutil_command)
try:
p = sarge.run(dfuutil_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.2).decode('utf-8')
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 DFUUTIL_ERASING in line:
self._logger.info(u"Erasing memory...")
self._send_status("progress", subtype="erasing")
elif DFUUTIL_WRITING in line:
self._logger.info(u"Writing memory...")
self._send_status("progress", subtype="writing")
elif DFUUTIL_NOACCESS in line:
raise FlashException("Cannot access DFU device")
elif DFUUTIL_NODEVICE in line:
raise FlashException("No DFU device found")

if p.returncode == 0:
return True
else:
raise FlashException("dfu-util 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

class FlashException(Exception):
def __init__(self, reason, *args, **kwargs):
Exception.__init__(self, *args, **kwargs)
self.reason = reason
70 changes: 70 additions & 0 deletions octoprint_firmwareupdater/static/js/firmwareupdater.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,18 @@ $(function() {
return self.dfuPathBroken() || self.dfuPathOk();
});

// Observables for dfu-util config settings
self.configDfuUtilPath = ko.observable();
self.configDfuUtilCommandLine = ko.observable();

// Observables for dfu-util UI messages
self.dfuUtilPathBroken = ko.observable(false);
self.dfuUtilPathOk = ko.observable(false);
self.dfuUtilPathText = ko.observable();
self.dfuUtilPathHelpVisible = ko.computed(function() {
return self.dfuUtilPathBroken() || self.dfuUtilPathOk();
});

// Observables for stm32flash config settings
self.configStm32flashPath = ko.observable();
self.configStm32flashVerify = ko.observable();
Expand All @@ -163,6 +175,7 @@ $(function() {
self.showBossacConfig = ko.observable(false);
self.showLpc1768Config = ko.observable(false);
self.showDfuConfig = ko.observable(false);
self.showDfuUtilConfig = ko.observable(false);
self.showStm32flashConfig = ko.observable(false);
self.showMarlinBftConfig = ko.observable(false);

Expand Down Expand Up @@ -482,6 +495,7 @@ $(function() {
self.showBossacConfig(false);
self.showLpc1768Config(false);
self.showDfuConfig(false);
self.showDfuUtilConfig(false);
self.showStm32flashConfig(false);
self.showMarlinBftConfig(false);

Expand All @@ -496,6 +510,8 @@ $(function() {
self.showLpc1768Config(true);
} else if(value == 'dfuprogrammer'){
self.showDfuConfig(true);
} else if(value == 'dfuutil'){
self.showDfuUtilConfig(true);
} else if(value == 'stm32flash'){
self.showStm32flashConfig(true);
} else if(value == 'marlinbft'){
Expand Down Expand Up @@ -879,6 +895,10 @@ $(function() {
self.configDfuCommandLine(self.getProfileSetting("dfuprog_commandline"));
self.configDfuEraseCommandLine(self.getProfileSetting("dfuprog_erasecommandline"));

// Load the dfu-util settings
self.configDfuUtilPath(self.getProfileSetting("dfuutil_path"));
self.configDfuUtilCommandLine(self.getProfileSetting("dfuutil_commandline"));

// Load the lpc1768 settings
self.configLpc1768Path(self.getProfileSetting("lpc1768_path"));
self.configLpc1768UnmountCommand(self.getProfileSetting("lpc1768_unmount_command"));
Expand Down Expand Up @@ -1000,6 +1020,10 @@ $(function() {
profiles[index]["dfuprog_commandline"] = self.configDfuCommandLine();
profiles[index]["dfuprog_erasecommandline"] = self.configDfuEraseCommandLine();

// DFU-Util settings
profiles[index]["dfuutil_path"] = self.configDfuUtilPath();
profiles[index]["dfuutil_commandline"] = self.configDfuUtilCommandLine();

// LPC176x settings
profiles[index]["lpc1768_path"] = self.configLpc1768Path();
profiles[index]["lpc1768_unmount_command"] = self.configLpc1768UnmountCommand();
Expand Down Expand Up @@ -1127,6 +1151,10 @@ $(function() {
self.dfuPathOk(false);
self.dfuPathText("");

self.dfuUtilPathBroken(false);
self.dfuUtilPathOk(false);
self.dfuUtilPathText("");

self.lpc1768PathBroken(false);
self.lpc1768PathOk(false);
self.lpc1768PathText("");
Expand Down Expand Up @@ -1156,6 +1184,10 @@ $(function() {
self.configDfuEraseCommandLine(self.profileDefaults["dfuprog_erasecommandline"]);
};

self.resetDfuUtilCommandLine = function() {
self.configDfuUtilCommandLine(self.profileDefaults["dfuutil_commandline"]);
};

self.resetLpc1768UnmountCommand = function() {
self.configLpc1768UnmountCommand(self.profileDefaults["lpc1768_unmount_command"]);
}
Expand Down Expand Up @@ -1343,6 +1375,44 @@ $(function() {
}
};

self.testDfuUtilPath = function() {
var filePathRegEx = new RegExp("^(\/[^\0/]+)+$");

if (!filePathRegEx.test(self.configDfuUtilPath())) {
self.dfuUtilPathText(gettext("The path is not valid"));
self.dfuUtilPathOk(false);
self.dfuUtilPathBroken(true);
} else {
$.ajax({
url: API_BASEURL + "util/test",
type: "POST",
dataType: "json",
data: JSON.stringify({
command: "path",
path: self.configDfuUtilPath(),
check_type: "file",
check_access: "x"
}),
contentType: "application/json; charset=UTF-8",
success: function(response) {
if (!response.result) {
if (!response.exists) {
self.dfuUtilPathText(gettext("The path doesn't exist"));
} else if (!response.typeok) {
self.dfuUtilPathText(gettext("The path is not a file"));
} else if (!response.access) {
self.dfuUtilPathText(gettext("The path is not an executable"));
}
} else {
self.dfuUtilPathText(gettext("The path is valid"));
}
self.dfuUtilPathOk(response.result);
self.dfuUtilPathBroken(!response.result);
}
})
}
};

self.testStm32flashPath = function() {
var filePathRegEx = new RegExp("^(\/[^\0/]+)+$");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
<option value=bootcmdr>bootcommander (OpenBLT Bootloader)</option>
<option value=bossac>bossac (Atmel SAM Family)</option>
<option value=dfuprogrammer>dfu-programmer (Atmel AVR with DFU)</option>
<option value=dfuutil>dfu-util (STM32 with DFU)</option>
<option value=lpc1768>lpc176x (LPC176x Boards)</option>
<option value=marlinbft>marlinbft (Marlin Binary File Transfer)</option>
<option value=stm32flash>stm32flash (STM32 built-in bootloader)</option>
Expand Down Expand Up @@ -266,7 +267,23 @@
</div>
<hr>
</div>
<!-- end avrdude options -->
<!-- end dfu-programmer options -->

<!-- dfu-util options -->
<div data-bind="visible: showDfuUtilConfig">
<div class="control-group" data-bind="css: {error: dfuUtilPathBroken, success: dfuUtilPathOk}">
<label class="control-label">{{ _('Path to dfu-programmer') }}</label>
<div class="controls">
<div class="input-append">
<input type="text" class="input-block-level" data-bind='value: configDfuUtilPath, valueUpdate: "afterkeydown"'>
<button class="btn" type="button" data-bind="click: testDfuUtilPath, enable: configDfuUtilPath, css: {disabled: !configDfuUtilPath()}">{{ _('Test') }}</button>
</div>
<span class="help-block" data-bind="visible: dfuUtilPathBroken() || dfuUtilPathOk, text: dfuUtilPathText"></span>
</div>
</div>
<hr>
</div>
<!-- end dfu-util options -->

<!-- stm32flash options for STM32 MCUs -->
<div data-bind="visible: showStm32flashConfig">
Expand Down Expand Up @@ -579,7 +596,22 @@
</div>
</div>
</div>
<!-- End advanced bossac options-->
<!-- End advanced dfu-programmer options-->

<!-- Advanced dfu-util options -->
<div data-bind="visible: showDfuUtilConfig">
<div class="control-group">
<label class="control-label">{{ _('Flash command line') }}</label>
<div class="controls">
<div class="input-append">
<input type="text" class="input-block-level" data-bind="value: configDfuUtilCommandLine">
<button class="btn" type="button" data-bind="click: resetDfuUtilCommandLine">{{ _('Reset') }}</button>
</div>
<span class="help-block">{{ _('Customize the dfu-util command line.') }}</span>
</div>
</div>
</div>
<!-- End advanced dfu-util options-->

<!-- Advanced stm32flash options -->
<div data-bind="visible: showStm32flashConfig">
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.11.0b6"
plugin_version = "1.11.0b7"

# The plugin's description. Can be overwritten within OctoPrint's internal data via __plugin_description__ in the plugin
# module
Expand Down

0 comments on commit af08a9c

Please sign in to comment.