Skip to content

Commit

Permalink
Added auto flash feature
Browse files Browse the repository at this point in the history
Signed-off-by: paulober <[email protected]>
  • Loading branch information
paulober committed Sep 13, 2024
1 parent a408b9c commit f4fc00b
Show file tree
Hide file tree
Showing 6 changed files with 504 additions and 1 deletion.
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,11 @@
"command": "micropico.garbageCollect",
"title": "Trigger garbage collection",
"category": "MicroPico"
},
{
"command": "micropico.flashPico",
"title": "Flash Pico in BOOTSEL mode",
"category": "MicroPico"
}
],
"menus": {
Expand Down Expand Up @@ -651,6 +656,7 @@
"fs-extra": "^11.2.0",
"lodash": "^4.17.21",
"rimraf": "^6.0.1",
"undici": "^6.19.8",
"uuid": "^10.0.0",
"which": "^4.0.0"
},
Expand Down
2 changes: 1 addition & 1 deletion rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default {
file: 'dist/extension.cjs',
format: 'cjs',
sourcemap: true,
exports: 'named',
exports: 'named'
},
external: [
'vscode'
Expand Down
39 changes: 39 additions & 0 deletions src/activator.mts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
type ActiveEnvironmentPathChangeEvent,
PythonExtension,
} from "@vscode/python-extension";
import { flashPicoInteractively } from "./flash.mjs";

/*const pkg: {} | undefined = vscode.extensions.getExtension("paulober.pico-w-go")
?.packageJSON as object;*/
Expand All @@ -56,6 +57,7 @@ export default class Activator {

private autoConnectTimer?: NodeJS.Timeout;
private comDevice?: string;
private noCheckForUSBMSDs = false;

constructor() {
this.logger = new Logger("Activator");
Expand Down Expand Up @@ -1588,6 +1590,32 @@ export default class Activator {
);
}
);
context.subscriptions.push(disposable);

disposable = vscode.commands.registerCommand(
commandPrefix + "flashPico",
async () => {
const result = await vscode.window.showInformationMessage(
"This will flash the latest MicroPython firmware to your Pico. " +
"Do you want to continue?",
{
modal: true,
detail:
"Note: Only Raspberry Pi Pico boards are supported. " +
"Make sure it is connected and in BOOTSEL mode. " +
"You can verify this by checking if a drive " +
"labeled RPI-RP2 or RP2350 is mounted.",
}
);

if (result === undefined) {
return;
}

await flashPicoInteractively(true);
}
);
context.subscriptions.push(disposable);

const packagesWebviewProvider = new PackagesWebviewProvider(
context.extensionUri
Expand Down Expand Up @@ -1739,6 +1767,12 @@ export default class Activator {
PicoMpyCom.getSerialPorts()
.then(async ports => {
if (ports.length === 0) {
if (!this.noCheckForUSBMSDs) {
// must be reset after checkForUSBMSDs if it want to continue
this.noCheckForUSBMSDs = true;
await this.checkForUSBMSDs();
}

return;
}

Expand Down Expand Up @@ -1785,6 +1819,11 @@ export default class Activator {
this.autoConnectTimer = setInterval(onAutoConnect, 1500);
}

private async checkForUSBMSDs(): Promise<void> {
const result = await flashPicoInteractively();
this.noCheckForUSBMSDs = result;
}

private showNoActivePythonError(): void {
vscode.window
.showWarningMessage(
Expand Down
120 changes: 120 additions & 0 deletions src/downloadFirmware.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { tmpdir } from "os";
import { basename, join } from "path";
import { createWriteStream } from "fs";
import { request } from "undici";

export enum SupportedFirmwareTypes {
pico,
picow,
pico2,
}

function firmwareTypeToDownloadURL(
firmwareType: SupportedFirmwareTypes
): string {
switch (firmwareType) {
case SupportedFirmwareTypes.pico:
return "https://micropython.org/download/RPI_PICO/";
case SupportedFirmwareTypes.picow:
return "https://micropython.org/download/RPI_PICO_W/";
case SupportedFirmwareTypes.pico2:
return "https://micropython.org/download/RPI_PICO2/";
}
}

async function extractUf2Url(
url: string,
allowPreview: boolean
): Promise<string | null> {
try {
// Fetch the content of the URL using Undici
const { body, headers } = await request(url);
const contentType = headers["content-type"];

// Check if the content is HTML
if (contentType?.includes("text/html")) {
let html = "";
for await (const chunk of body) {
html += chunk;
}

// Split the document at <h2>Firmware</h2>
const splitHtml = html.split("<h2>Firmware</h2>");
if (splitHtml.length < 2) {
console.log("No Firmware section found.");

return null;
}

const firmwareSection = splitHtml[1];

// Use regex to find the first .uf2 URL inside <strong> tags
const uf2Regex = allowPreview
? /<a[^>]+href="([^"]+\.uf2)"/
: /<strong>\s*<a[^>]+href="([^"]+\.uf2)"/;
const match = uf2Regex.exec(firmwareSection);

if (match?.[1]) {
return match[1];
} else {
console.log("No .uf2 link found inside <strong>.");

return null;
}
} else {
console.log("The URL did not return HTML content.");

return null;
}
} catch (error) {
console.error("Error fetching or processing the URL:", error);

return null;
}
}

export async function downloadFirmware(
firmwareType: SupportedFirmwareTypes
): Promise<string | undefined> {
const url = firmwareTypeToDownloadURL(firmwareType);
const uf2Url = await extractUf2Url(
url,
// TODO: remove after stable builds for Pico2 are available
firmwareType === SupportedFirmwareTypes.pico2
);

if (!uf2Url) {
console.error("No UF2 URL found.");

return;
}

try {
// Fetch the .uf2 file using Undici
const { body } = await request(`https://micropython.org${uf2Url}`);

// Get the filename from the URL
const fileName = basename(uf2Url);

// Create the path for the file in the system temp directory
const tempDir = tmpdir();
const filePath = join(tempDir, fileName);

// Stream the file content to the temp directory
const fileStream = createWriteStream(filePath);
body.pipe(fileStream);

await new Promise<void>((resolve, reject) => {
fileStream.on("finish", resolve);
fileStream.on("error", reject);
});

console.log(`Firmware downloaded to: ${filePath}`);

return filePath;
} catch (error) {
console.error("Error downloading the UF2 file:", error);

return;
}
}
Loading

0 comments on commit f4fc00b

Please sign in to comment.