Skip to content

Commit af6b10c

Browse files
authored
Merge pull request #537 from makermelissa-ai-assistant/issue-399-serial-files
Use serial file transfer on supported firmware
2 parents e123429 + 710bed5 commit af6b10c

3 files changed

Lines changed: 50 additions & 4 deletions

File tree

js/workflows/usb.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class USBWorkflow extends Workflow {
2525
this._messageCallback = null;
2626
this._btnSelectHostFolderCallback = null;
2727
this._btnUseHostFolderCallback = null;
28+
this._usbDriveDisabled = false;
2829
this.buttonStates = [
2930
{request: false, select: false},
3031
{request: true, select: false},
@@ -63,6 +64,12 @@ class USBWorkflow extends Workflow {
6364
}
6465
this._isDisconnecting = true;
6566

67+
// Newer CircuitPython firmware lets the editor temporarily make the
68+
// filesystem writable over the REPL by taking the CIRCUITPY USB drive
69+
// offline. Put the drive back before closing the serial connection so
70+
// it is available to the host again after a clean disconnect.
71+
await this._restoreUsbDrive();
72+
6673
// If we got here because the underlying device was lost (NetworkError),
6774
// the writable stream is errored and any further writes will throw
6875
// unhandled NetworkErrors. Force reconnect=false so we don't loop
@@ -321,6 +328,40 @@ class USBWorkflow extends Workflow {
321328
}
322329
}
323330

331+
async _trySerialFileTransfer() {
332+
try {
333+
if (await this.showBusy(this.repl.tryDisableUsbDrive())) {
334+
this._usbDriveDisabled = true;
335+
console.log("CIRCUITPY USB drive disabled; using serial file transfer");
336+
return true;
337+
}
338+
} catch (error) {
339+
console.warn("Unable to disable the CIRCUITPY USB drive:", error);
340+
}
341+
return false;
342+
}
343+
344+
async _restoreUsbDrive() {
345+
if (!this._usbDriveDisabled) {
346+
return;
347+
}
348+
349+
try {
350+
if (await this.repl.enableUsbDrive()) {
351+
console.log("CIRCUITPY USB drive restored");
352+
} else {
353+
console.warn("CircuitPython could not restore the CIRCUITPY USB drive");
354+
}
355+
} catch (error) {
356+
// A physical disconnect makes the REPL unavailable before this
357+
// cleanup can run. The next hard reset restores the firmware's
358+
// default USB drive setting.
359+
console.warn("Unable to restore the CIRCUITPY USB drive:", error);
360+
} finally {
361+
this._usbDriveDisabled = false;
362+
}
363+
}
364+
324365
// Workflow specific Functions
325366
async _switchToDevice(device) {
326367
device.removeEventListener("message", this._messageCallback);
@@ -366,6 +407,12 @@ class USBWorkflow extends Workflow {
366407
// At this point we should see if we should init the file client and check if have a saved dir handle
367408
let fileops = new FileOps(this.repl, false);
368409
if (await this.showBusy(fileops.isReadOnly())) {
410+
if (await this._trySerialFileTransfer()) {
411+
this.initFileClient(new ReplFileTransferClient(this.connectionStatus.bind(this), this.repl));
412+
this.onConnected();
413+
return;
414+
}
415+
369416
// UID Only needed for matching the CIRCUITPY drive with the Serial Terminal
370417
await this.showBusy(this._getDeviceUid());
371418
let modal = this.connectDialog.getModal();

package-lock.json

Lines changed: 2 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
},
1616
"dependencies": {
1717
"@adafruit/ble-file-transfer-js": "adafruit/ble-file-transfer-js#1.0.5",
18-
"@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.3.0",
18+
"@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.4.0",
1919
"@astral-sh/ruff-wasm-web": "^0.15.21",
2020
"@codemirror/lang-css": "^6.3.1",
2121
"@codemirror/lang-html": "^6.4.11",

0 commit comments

Comments
 (0)