Skip to content

Commit c26af86

Browse files
Add unzipping of esp32 binaries
1 parent aa4da50 commit c26af86

File tree

2 files changed

+40
-111
lines changed

2 files changed

+40
-111
lines changed

kernel/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@
5757
"@jupyterlite/server": "^0.5.0-alpha.0",
5858
"@types/crypto-js": "^4.2.2",
5959
"crypto-js": "^4.2.0",
60-
"esptool-js": "^0.5.4"
60+
"esptool-js": "^0.5.4",
61+
"unzipit":"^1.4.3"
6162
},
6263
"devDependencies": {
6364
"@jupyterlab/builder": "~4.3.3",

kernel/src/services/FirmwareService.ts

Lines changed: 38 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { FirmwareOption } from '../constants'
22
import { defaultFirmwareOptions } from '../constants'
33
import { DeviceService } from './DeviceService';
44
// const { Octokit } = require('@octokit/rest');
5+
import { unzipit } from 'unzipit';
56

67
export class FirmwareService {
78
private firmwareString: string | null = null;
@@ -16,15 +17,6 @@ export class FirmwareService {
1617
// Get the firmware options from GitHub (or local files).
1718
// Maybe we'll add an explicit button for updating the firmware options in the future...
1819
// We'll initialize our private firmwareOptions here with the reqFirmwareOptionsGitHub() method.
19-
// console.log("In the constructor about to fetch firmware options from GitHub...");
20-
// this.reqFirmwareOptionsGitHub().then((res) => {
21-
// // log the res
22-
// console.log('Firmware options response:', res);
23-
// this.firmwareOptions = res;
24-
// })
25-
26-
// Log the firmware options to the console for debugging.
27-
// console.log('Firmware options:', this.firmwareOptions);
2820

2921
// Initialize firmware options with defaultFirmwareOptions.
3022
this.firmwareOptions = defaultFirmwareOptions;
@@ -36,6 +28,27 @@ export class FirmwareService {
3628
}
3729
}
3830

31+
private async unzipStreamToVariable(stream: ReadableStream<Uint8Array>): Promise<{ [filename: string]: Uint8Array }> {
32+
const reader = stream.getReader();
33+
let chunks = [];
34+
while (true) {
35+
const { done, value } = await reader.read();
36+
if (done) {
37+
break;
38+
}
39+
chunks.push(value);
40+
}
41+
const allChunks = new Uint8Array(chunks.flatMap(chunk => [...chunk]));
42+
const { entries } = await unzipit.unzip(allChunks);
43+
44+
const unzippedData: { [filename: string]: Uint8Array } = {};
45+
for (const entry of Object.values(entries) as { name: string; arrayBuffer: () => Promise<Uint8Array> }[]) {
46+
unzippedData[entry.name] = new Uint8Array(await entry.arrayBuffer());
47+
}
48+
return unzippedData;
49+
}
50+
51+
3952
// We'll use this method to fetch the firmware options from GitHub.
4053
// We could alternatively use local files in ../binaries/ so we aren't sending a request to GitHub every time.
4154
private async reqFirmwareOptionsGitHub(): Promise<Record<string, FirmwareOption>> {
@@ -67,25 +80,9 @@ export class FirmwareService {
6780
firmwareId = 'm-' + firmwareId.replace('minimal-', '');
6881
}
6982

70-
71-
// curl method to download based on ID:
72-
// curl -L \
73-
// -H "Accept: application/vnd.github+json" \
74-
// -H "Authorization: Bearer <YOUR-TOKEN>" \
75-
// -H "X-GitHub-Api-Version: 2022-11-28" \
76-
// https://api.github.com/repos/OWNER/REPO/releases/assets/ASSET_ID
77-
7883
//method 1: use the browser_download_url from the asset object.
7984
const firmwareUrl = asset.browser_download_url;
8085

81-
//method 2: use the asset ID to formulate a GET request to the GitHub API with octokit.
82-
// Instead let's use the asset ID to formulate an GitHub API URL.
83-
// We'll still use the 'URL' var for now but it's actually a formatted GET req
84-
//const firmwareUrl = 'GET /repos/sparkfun/micropython/releases/assets/' + asset.id;
85-
86-
//method 3: use the asset ID to formulate a direct url to fetch the asset from with similar method as curl.
87-
// const firmwareUrl = `https://api.github.com/repos/sparkfun/micropython/releases/assets/${asset.id}`;
88-
8986
// log the asset object
9087
console.log('Asset:', asset);
9188

@@ -189,6 +186,8 @@ export class FirmwareService {
189186
throw new Error(`Invalid firmware selection or no URL for: ${firmwareId}`);
190187
}
191188

189+
// GitHub requires a CORS proxy to fetch files from their API. We'll use a public CORS proxy for now.
190+
// See: https://github.com/orgs/community/discussions/106849
192191
const cors_proxy = "https://corsproxy.io/?url=";
193192
console.log("Performing fetch for firmware:", cors_proxy + selectedFirmware.url);
194193

@@ -200,98 +199,27 @@ export class FirmwareService {
200199
},
201200
});
202201

203-
// console.log('Firmware fetch result:', result);
204-
205-
// method 2: use the asset ID to formulate a GET request to the GitHub API with octokit.
206-
// const octokit = new Octokit({});
207-
208-
// firmware URLs from above: const firmwareUrl = 'GET /repos/sparkfun/micropython/releases/assets/' + asset.id;
209-
// const response = await octokit.request(selectedFirmware.url, {
210-
// owner: 'sparkfun',
211-
// repo: 'micropython',
212-
// asset_id: selectedFirmware.url.split('/').pop(), // Extract the asset ID from the URL
213-
// headers: {
214-
// 'X-GitHub-Api-Version': '2022-11-28'
215-
// }
216-
// });
217-
218-
// console.log('Response from GitHub:', response);
219-
// throw new Error('Purposeful error out during testing.'); // TODO: Remove this line when done testing.
220-
221-
// method 3: use the asset ID to formulate a direct url to fetch the asset from with similar method as curl.
222-
// we can look at the headers in the curl command to see what we need to add to our fetch request.
223-
// const headers = new Headers({
224-
// 'Accept': 'application/vnd.github+json',
225-
// 'X-GitHub-Api-Version': '2022-11-28',
226-
// });
227-
228-
// Since we are getting cors errors,
229-
// const result = await fetch(selectedFirmware.url, {
230-
// headers:{
231-
// // 'Accept': 'application/vnd.github+json',
232-
// 'Accept': 'application/octet-stream',
233-
// 'X-GitHub-Api-Version': '2022-11-28',
234-
// // 'Authorization': `Bearer ${import.meta.env.VITE_GITHUB_TOKEN}` // Use your GitHub token here.
235-
// }
236-
// });
237-
238-
// console.log('Result:', result);
239-
240-
// // Now we check the result to see if it's ok and then we can actually read from the body.
241-
// if (!result.ok) {
242-
// console.log("Error fetching firmware:", result.status, result.statusText);
243-
// }
244-
245-
// // stream the response
246-
// const reader = result.body?.getReader();
247-
// if (!reader) {
248-
// throw new Error('Failed to get reader from response body.');
249-
// }
250-
251-
// let receivedLength = 0; // received bytes
252-
// const chunks: Uint8Array[] = []; // chunks of received data
253-
254-
// while (true) {
255-
// const { done, value } = await reader.read();
256-
// if (done) {
257-
// break;
258-
// }
259-
// chunks.push(value);
260-
// receivedLength += value.length;
261-
// console.log(`Received ${receivedLength} bytes`);
262-
// }
263-
264-
// console.log('All chunks received:', chunks);
265-
266-
// // Combine all chunks into a single Uint8Array
267-
// const chunksAll = new Uint8Array(receivedLength);
268-
// let position = 0;
269-
// for (const chunk of chunks) {
270-
// chunksAll.set(chunk, position); // copy chunk to the final array
271-
// position += chunk.length; // update position
272-
// }
273-
// console.log('All chunks combined:', chunksAll);
274-
275-
276-
// // Convert the Uint8Array to a string
277-
// const firmwareString = Array.from(chunksAll)
278-
// .map(byte => String.fromCharCode(byte))
279-
// .join('');
280-
281-
// console.log('Firmware string:', firmwareString);
282-
283-
// throw new Error('Purposeful error out during testing.'); // TODO: Remove this line when done testing.
284-
285202
if (!result.ok) {
286203
console.log("Error fetching firmware:", result.status, result.statusText);
287204
throw new Error(`Failed to fetch firmware: ${result.status} ${result.statusText}`);
288205
}
289206

290-
this.firmwareBlob = await result.blob();
291-
const uint8Array = new Uint8Array(await this.firmwareBlob.arrayBuffer());
292-
this.firmwareString = Array.from(uint8Array)
207+
const firmwareData = await this.unzipStreamToVariable(result.body!);
208+
209+
const firmwareDataMP = firmwareData['micropython.bin'];
210+
211+
this.firmwareString = Array.from(firmwareDataMP)
293212
.map(byte => String.fromCharCode(byte))
294213
.join('');
214+
215+
// Log the raw firmware data for debugging.
216+
console.log('Raw firmware data:', this.firmwareString);
217+
218+
// this.firmwareBlob = await result.blob();
219+
// const uint8Array = new Uint8Array(await this.firmwareBlob.arrayBuffer());
220+
// this.firmwareString = Array.from(uint8Array)
221+
// .map(byte => String.fromCharCode(byte))
222+
// .join('');
295223

296224
console.log('Downloaded SFE FIRMWARE. Firmware string size:', this.firmwareString.length);
297225
return this.firmwareString;

0 commit comments

Comments
 (0)