TL;DR: When Workbench hangs when trying to put a device into DFU mode, end the task and then retry. To reset a device without pressing the button or unplugging it you can use particle usb reset.
Internally, Workbench uses particle usb dfu to place devices into DFU mode. If you specify the device name in the project, I believe it will specify the name of the device as an additional argument to particle usb dfu.
In my experience, when a request to place a device into DFU mode hangs, the device has made it into DFU mode but Particle CLI does not realize and eventually times out after a minute.
If you send a SIGTERM to particle usb dfu with ^C and then attempt to run particle usb dfu again, the subsequent invocation will realize that the device is DFU mode.
I managed to sporadically reproduce this on a photon running 2.0.1 using particle-cli 2.10.0. I’m uncertain if the use of a USB hub influences particle usb dfu to hang more often.
$ particle usb listen # Put photon in listening mode
Done.
$ particle identify # Get deviceID and deviceOS version
Your device id is [REDACTED]
Your system firmware version is 2.0.1
$ system_profiler SPUSBDataType # Verify that photon is present on USB
--- Wait 5 minutes ---
$ particle usb dfu # Put photon in DFU mode. Fails.
# Hangs for one minute, and then the following error is printed:
TimeoutError [VError]
at RequestSender.delay (/Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/device.js:106:13)
at /Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/device.js:212:17
at async Promise.all (index 0)
at async CLI.runCommand (/Users/nrobinson/.particle/node_modules/particle-cli/dist/app/cli.js:160:7)
at async CLI.run (/Users/nrobinson/.particle/node_modules/particle-cli/dist/app/cli.js:190:14) {
jse_shortmsg: '',
jse_info: {},
message: ''
}
$ particle usb reset # Reset the device. Succeeds.
Here are the commands again, but simplified:
$ particle usb listen
$ particle identify
$ system_profiler SPUSBDataType
$ particle usb dfu
$ particle usb reset
Looking at ~/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/device.js, the error happens in the enterDfuMode method:
enterDfuMode({
timeout = _config.globalOptions.requestTimeout
} = {}) {
if (this.isInDfuMode) {
return;
}
return this.timeout(timeout, async s => {
await s.sendRequest(_request.Request.DFU_MODE);
await s.close();
let isInDfuMode;
while (!isInDfuMode) {
try {
await s.open({
includeDfu: true
});
isInDfuMode = s.device.isInDfuMode;
} catch (error) {// device is reconnecting, ignore
}
await s.close();
await s.delay(500);
}
});
}
Line 212 is await s.delay(500);
Update:
In ~/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/device-base.js the open() method is defined beginning on line 158.
I changed all instances of this._log.trace to console.log.
When reproduced:
$ particle usb dfu
▌ Getting device information...Opening device
Device ID: [REDACTED]
Firmware version: 2.0.1
Device is open
▀ Sending a command to the device...Opening device
Device ID: [REDACTED]
Firmware version: 2.0.1
Device is open
▌ Sending a command to the device...Opening device
Device ID: [REDACTED]
Firmware version: 2.0.1
Device is open
▌ Sending a command to the device...Opening device
Device ID: [REDACTED]
Firmware version: 2.0.1
Device is open
▌ Sending a command to the device...Opening device
Device ID: [REDACTED]
▌ Sending a command to the device...Opening device
Device ID: [REDACTED]
▌ Sending a command to the device...Opening device
Device ID: [REDACTED]
▌ Sending a command to the device...Opening device
Device ID: [REDACTED]
▄ Sending a command to the device...Opening device
Device ID: [REDACTED]
▄ Sending a command to the device...Opening device
Device ID: [REDACTED]
▄ Sending a command to the device...Opening device
# Repeats for many lines
TimeoutError [VError]
at RequestSender.delay (/Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/device.js:106:13)
at /Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/device.js:212:17
at async Promise.all (index 0)
at async CLI.runCommand (/Users/nrobinson/.particle/node_modules/particle-cli/dist/app/cli.js:160:7)
at async CLI.run (/Users/nrobinson/.particle/node_modules/particle-cli/dist/app/cli.js:190:14) {
jse_shortmsg: '',
jse_info: {},
message: ''
}
I then tried adding console.log(error) on line 209 in the catch block in device.js:
$ particle usb dfu
▌ Getting device information...Opening device
Device ID: [REDACTED]
Firmware version: 2.0.1
Device is open
▀ Sending a command to the device...Opening device
Device ID: [REDACTED]
Firmware version: 2.0.1
Device is open
▌ Sending a command to the device...Opening device
Device ID: [REDACTED]
Firmware version: 2.0.1
Device is open
▌ Sending a command to the device...Opening device
Device ID: [REDACTED]
Firmware version: 2.0.1
Device is open
▄ Sending a command to the device...Opening device
Device ID: [REDACTED]
▌ Sending a command to the device...UsbError [VError]: Unknown interface
at /Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/usb-device-node.js:140:25
at new Promise (<anonymous>)
at new F (/Users/nrobinson/.particle/node_modules/particle-cli/node_modules/@babel/runtime-corejs2/node_modules/core-js/library/modules/_export.js:36:28)
at UsbDevice.claimInterface (/Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/usb-device-node.js:135:12)
at Dfu.open (/Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/dfu.js:168:21)
at /Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/device-base.js:192:26
at async openDeviceById (/Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/device-base.js:1039:3)
at async RequestSender.open (/Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/device.js:79:19)
at async /Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/device.js:204:11
at async Promise.all (index 0) {
jse_shortmsg: 'Unknown interface',
jse_info: {},
message: 'Unknown interface'
}
▄ Sending a command to the device...Opening device
Device ID: [REDACTED]
UsbError [VError]: Unknown interface
at /Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/usb-device-node.js:140:25
at new Promise (<anonymous>)
at new F (/Users/nrobinson/.particle/node_modules/particle-cli/node_modules/@babel/runtime-corejs2/node_modules/core-js/library/modules/_export.js:36:28)
at UsbDevice.claimInterface (/Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/usb-device-node.js:135:12)
at Dfu.open (/Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/dfu.js:168:21)
at /Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/device-base.js:192:26
at async openDeviceById (/Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/device-base.js:1039:3)
at async RequestSender.open (/Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/device.js:79:19)
at async /Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/device.js:204:11
at async Promise.all (index 0) {
jse_shortmsg: 'Unknown interface',
jse_info: {},
message: 'Unknown interface'
}
# Repeats for many lines
TimeoutError [VError]
at RequestSender.delay (/Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/device.js:106:13)
at /Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/device.js:213:17
at async Promise.all (index 0)
at async CLI.runCommand (/Users/nrobinson/.particle/node_modules/particle-cli/dist/app/cli.js:160:7)
at async CLI.run (/Users/nrobinson/.particle/node_modules/particle-cli/dist/app/cli.js:190:14) {
jse_shortmsg: '',
jse_info: {},
message: ''
}
To resolve this bug, I think it would be good for enterDfuMode() to catch the UsbError:
catch (error) {
console.log(error);
if (error instanceof _error.UsbError) {
console.log("Aborted!");
return;
}
}
When reproduced:
$ particle usb dfu
▌ Getting device information...Opening device
Device ID: [REDACTED]
Firmware version: 2.0.1
Device is open
▀ Sending a command to the device...Opening device
Device ID: [REDACTED]
Firmware version: 2.0.1
Device is open
▌ Sending a command to the device...Opening device
Device ID: [REDACTED]
Firmware version: 2.0.1
Device is open
▌ Sending a command to the device...Opening device
Device ID: [REDACTED]
Firmware version: 2.0.1
Device is open
▄ Sending a command to the device...Opening device
▌ Sending a command to the device...Device ID: [REDACTED]
UsbError [VError]: Unknown interface
at /Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/usb-device-node.js:140:25
at new Promise (<anonymous>)
at new F (/Users/nrobinson/.particle/node_modules/particle-cli/node_modules/@babel/runtime-corejs2/node_modules/core-js/library/modules/_export.js:36:28)
at UsbDevice.claimInterface (/Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/usb-device-node.js:135:12)
at Dfu.open (/Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/dfu.js:168:21)
at /Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/device-base.js:192:26
at async openDeviceById (/Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/device-base.js:1039:3)
at async RequestSender.open (/Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/device.js:79:19)
at async /Users/nrobinson/.particle/node_modules/particle-cli/node_modules/particle-usb/lib/device.js:204:11
at async Promise.all (index 0) {
jse_shortmsg: 'Unknown interface',
jse_info: {},
message: 'Unknown interface'
}
Aborted!
Done.
The photon successfully enters DFU mode and particle usb dfu catches the error and aborts.
Interestingly enough, I can occasionally trigger a hang by simply running particle usb dfu soon after manually resetting the photon.
Also, I’m looking at particle-usb on GitHub, and the latest release is v1.2.2, yet particle-cli v2.10.0 is uses particle-usb 1.2.1, but I doubt that impacts this issue.
I have opened an issue in particle-usb.