From b301e22f8205818fae34c1aa137d3714a56f04bb Mon Sep 17 00:00:00 2001 From: Nigdzie <46287652+Nigdzie@users.noreply.github.com> Date: Mon, 15 Dec 2025 21:42:17 +0100 Subject: [PATCH 01/17] Update docs, now we have landing page. --- docs/index.html | 485 ++++------------------------------------ docs/janos_flash.html | 509 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 550 insertions(+), 444 deletions(-) create mode 100644 docs/janos_flash.html diff --git a/docs/index.html b/docs/index.html index 08c0fe9..dee1178 100644 --- a/docs/index.html +++ b/docs/index.html @@ -3,45 +3,39 @@ - projectZero Web Flasher + projectZero Tools -
-
projectZero ESP32-C5
-

Flash projectZero straight from your browser

-

WebSerial flasher powered by esptool-js. Click Connect, pick the LAB C5 serial port (Chrome/Edge desktop), then Flash to stream bootloader, partition table, and firmware from the latest GitHub Release.

+
projectZero Tools
+

Pick your flasher

+

Browser-based tools for LAB C5: ESP32-C5 firmware (JanOS) and Flipper FAP uploader.

- -
-
-

Control

-
-
- - - -
-
- Ready to connect. - - -
-
- -
-
-
-
- -
-

Release info

-

Loading manifest

-
-
  • ROM mode: Hold BOOT on the LAB C5, plug the board in, then release.
  • -
  • Close serial hogs: quit qFlipper/monitors or anything holding the COM port.
  • -
  • Stuck- Unplug, re-run Connect, plug again with BOOT held, then Flash.
  • -
  • Reset: after flashing, press RESET on the board if it does not auto-reboot.
  • -
    -
    -
    - -
    -
    -

    What happens

    -

    Using esptool-js directly: enter stub, set baud, then flash each image at the offsets from manifest.json. Progress and MD5 checks mirror the Python esptool flow.

    -
    -
    -

    Need offline-

    -

    Grab the same assets from Releases and run flash_board.py in ESP32C5/binaries-esp32c5 if your browser blocks Web Serial.

    +
    +
    +

    JanOS ESP32-C5 flasher

    +

    Flash LAB C5 firmware straight from the browser via WebSerial (esptool-js). Pulls images from the latest release manifest.

    + Open JanOS flasher
    -
    -

    Browser support

    -

    Chrome or Edge on desktop over HTTPS. Web Serial is required. If you see Web Serial not available, switch browsers.

    +
    +

    Flipper FAP uploader

    +

    Upload the latest Lab_C5 .fap (Unleashed/Momentum) over CDC/CLI, auto-cleans old versions, optional auto-launch.

    + Open FAP uploader
    - - diff --git a/docs/janos_flash.html b/docs/janos_flash.html new file mode 100644 index 0000000..08c0fe9 --- /dev/null +++ b/docs/janos_flash.html @@ -0,0 +1,509 @@ + + + + + + projectZero Web Flasher + + + + + + + +
    +
    +
    projectZero ESP32-C5
    +

    Flash projectZero straight from your browser

    +

    WebSerial flasher powered by esptool-js. Click Connect, pick the LAB C5 serial port (Chrome/Edge desktop), then Flash to stream bootloader, partition table, and firmware from the latest GitHub Release.

    +
    + +
    +
    +

    Control

    +
    +
    + + + +
    +
    + Ready to connect. + + +
    +
    + +
    +
    +
    +
    + +
    +

    Release info

    +

    Loading manifest

    +
    +
  • ROM mode: Hold BOOT on the LAB C5, plug the board in, then release.
  • +
  • Close serial hogs: quit qFlipper/monitors or anything holding the COM port.
  • +
  • Stuck- Unplug, re-run Connect, plug again with BOOT held, then Flash.
  • +
  • Reset: after flashing, press RESET on the board if it does not auto-reboot.
  • +
    +
    +
    + +
    +
    +

    What happens

    +

    Using esptool-js directly: enter stub, set baud, then flash each image at the offsets from manifest.json. Progress and MD5 checks mirror the Python esptool flow.

    +
    +
    +

    Need offline-

    +

    Grab the same assets from Releases and run flash_board.py in ESP32C5/binaries-esp32c5 if your browser blocks Web Serial.

    +
    +
    +

    Browser support

    +

    Chrome or Edge on desktop over HTTPS. Web Serial is required. If you see Web Serial not available, switch browsers.

    +
    +
    +
    + + + + From a2ea330ef870d57ac9350fb317e15f6087e79a55 Mon Sep 17 00:00:00 2001 From: Nigdzie <46287652+Nigdzie@users.noreply.github.com> Date: Mon, 15 Dec 2025 21:48:40 +0100 Subject: [PATCH 02/17] update url's for flasher --- docs/manifest.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/manifest.json b/docs/manifest.json index 1cdf30b..de0389a 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -4,8 +4,8 @@ "build": "latest", "chipFamily": "ESP32-C5", "parts": [ - { "path": "firmware/bootloader.bin", "offset": 8192 }, - { "path": "firmware/partition-table.bin", "offset": 32768 }, - { "path": "firmware/projectZero.bin", "offset": 65536 } + { "path": "https://raw.githubusercontent.com/C5Lab/projectZero/main/ESP32C5/binaries-esp32c5/bootloader.bin", "offset": 8192 }, + { "path": "https://raw.githubusercontent.com/C5Lab/projectZero/main/ESP32C5/binaries-esp32c5/partition-table.bin", "offset": 32768 }, + { "path": "https://raw.githubusercontent.com/C5Lab/projectZero/main/ESP32C5/binaries-esp32c5/projectZero.bin", "offset": 65536 } ] } From 91aaf0d695518829b08667c77bba87e5032048c4 Mon Sep 17 00:00:00 2001 From: Nigdzie <46287652+Nigdzie@users.noreply.github.com> Date: Mon, 15 Dec 2025 21:55:08 +0100 Subject: [PATCH 03/17] Docs - janos_flasher - additional reset after flash --- docs/janos_flash.html | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/janos_flash.html b/docs/janos_flash.html index 08c0fe9..73e735a 100644 --- a/docs/janos_flash.html +++ b/docs/janos_flash.html @@ -469,6 +469,13 @@

    Browser support

    }; await state.esploader.writeFlash(flashOptions); await state.esploader.after(); + try { + setStatus("Flash complete. Resetting ..."); + await state.esploader.hardReset(); + log("Hard reset sent."); + } catch (e) { + log(`Reset failed (ignored): ${e.message || e}`); + } ui.progress.style.width = "100%"; setStatus("Flash complete. Press RESET if the board doesn't reboot."); log("Flash complete."); From 558af41d640476270e05cb430493e8439911892f Mon Sep 17 00:00:00 2001 From: Nigdzie <46287652+Nigdzie@users.noreply.github.com> Date: Mon, 15 Dec 2025 22:10:06 +0100 Subject: [PATCH 04/17] fix docs - janos_flasher - reset and serial console --- docs/janos_flash.html | 47 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/docs/janos_flash.html b/docs/janos_flash.html index 73e735a..5318432 100644 --- a/docs/janos_flash.html +++ b/docs/janos_flash.html @@ -294,6 +294,7 @@

    Browser support

    esploader: null, chip: null, isFlashing: false, + monitoring: false, }; const log = (msg) => { @@ -349,6 +350,43 @@

    Browser support

    } }; + + const startMonitor = async () => { + if (state.monitoring || !state.reader) return; + state.monitoring = true; + log("Serial monitor started. Disconnect to stop."); + try { + while (state.monitoring && state.connected) { + const { value, done } = await state.reader.read(); + if (done) break; + if (value) { + ui.log.textContent += value; + ui.log.scrollTop = ui.log.scrollHeight; + } + } + } catch (err) { + log(`Monitor stopped: ${err.message || err}`); + } finally { + state.monitoring = false; + } + }; + + const tryReset = async () => { + const t = state.transport; + if (state.esploader && typeof state.esploader.hardReset === "function") { + await state.esploader.hardReset(); + return "hardReset()"; + } + if (t && typeof t.setDTR === "function" && typeof t.setRTS === "function") { + await t.setDTR(false); + await t.setRTS(true); + await new Promise((r) => setTimeout(r, 120)); + await t.setRTS(false); + return "DTR/RTS toggle"; + } + throw new Error("Reset not supported by transport"); + }; + const connect = async () => { ensureSupport(); ui.connectBtn.disabled = true; @@ -450,6 +488,7 @@

    Browser support

    ui.flashBtn.disabled = true; ui.connectBtn.disabled = true; state.isFlashing = true; + state.monitoring = false; ui.progress.style.width = "0%"; try { const files = await fetchFiles(); @@ -471,14 +510,15 @@

    Browser support

    await state.esploader.after(); try { setStatus("Flash complete. Resetting ..."); - await state.esploader.hardReset(); - log("Hard reset sent."); + const how = await tryReset(); + log(`Reset sent (${how}).`); } catch (e) { log(`Reset failed (ignored): ${e.message || e}`); } ui.progress.style.width = "100%"; - setStatus("Flash complete. Press RESET if the board doesn't reboot."); + setStatus("Flash complete. Monitoring serial; unplug/disconnect to stop."); log("Flash complete."); + startMonitor().catch((err) => log(`Monitor start failed: ${err.message || err}`)); } catch (err) { console.error(err); setStatus(err.message || "Flash failed", "error"); @@ -491,6 +531,7 @@

    Browser support

    }; const disconnect = async () => { + state.monitoring = false; try { if (state.transport) { await state.transport.disconnect(); From 8e0091b80d33e4b77a88d258e3152596dffe68b9 Mon Sep 17 00:00:00 2001 From: Nigdzie <46287652+Nigdzie@users.noreply.github.com> Date: Mon, 15 Dec 2025 22:27:31 +0100 Subject: [PATCH 05/17] fix docs - janos_flasher - console monitor after flash --- docs/janos_flash.html | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/janos_flash.html b/docs/janos_flash.html index 5318432..fa30b24 100644 --- a/docs/janos_flash.html +++ b/docs/janos_flash.html @@ -295,6 +295,8 @@

    Browser support

    chip: null, isFlashing: false, monitoring: false, + connected: false, + monitorReader: null, }; const log = (msg) => { @@ -352,22 +354,29 @@

    Browser support

    const startMonitor = async () => { - if (state.monitoring || !state.reader) return; + if (state.monitoring || !state.port?.readable) return; state.monitoring = true; log("Serial monitor started. Disconnect to stop."); try { + const decoder = new TextDecoderStream(); + const pipe = state.port.readable.pipeTo(decoder.writable); + const reader = decoder.readable.getReader(); + state.monitorReader = reader; while (state.monitoring && state.connected) { - const { value, done } = await state.reader.read(); + const { value, done } = await reader.read(); if (done) break; if (value) { ui.log.textContent += value; ui.log.scrollTop = ui.log.scrollHeight; } } + await reader.cancel().catch(() => {}); + await pipe.catch(() => {}); } catch (err) { log(`Monitor stopped: ${err.message || err}`); } finally { state.monitoring = false; + state.monitorReader = null; } }; @@ -416,6 +425,7 @@

    Browser support

    setStatus(`Connected to ${state.chip}. Ready to flash.`); log(`Connected: ${state.chip}`); ui.flashBtn.disabled = false; + state.connected = true; } catch (err) { console.error(err); setStatus(err.message || "Connection failed", "error"); @@ -533,6 +543,9 @@

    Browser support

    const disconnect = async () => { state.monitoring = false; try { + if (state.monitorReader) { + await state.monitorReader.cancel().catch(() => {}); + } if (state.transport) { await state.transport.disconnect(); } else if (state.port && state.port.close) { @@ -546,6 +559,7 @@

    Browser support

    ui.chip.style.display = "none"; ui.flashBtn.disabled = true; setStatus("Disconnected."); + state.connected = false; }; ui.connectBtn.addEventListener("click", () => connect().catch(() => {})); From ea4c780d77999906c9c5e360a7bc7ae6ce4aedd2 Mon Sep 17 00:00:00 2001 From: Nigdzie <46287652+Nigdzie@users.noreply.github.com> Date: Mon, 15 Dec 2025 22:33:40 +0100 Subject: [PATCH 06/17] revert to a2ea330: update url's for flasher --- docs/janos_flash.html | Bin 19258 -> 35220 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/janos_flash.html b/docs/janos_flash.html index fa30b2415c59370c3ac5d3ae16800c8168011860..c16e347b3e2c7d048d7a1b2b6d0720a58821bf63 100644 GIT binary patch literal 35220 zcmeI5X>(Oqc81UAROLU&a@>*AtqU@P!Nzn0hBhg1Q-G^0aa=|-iiJc=!Zc0u*OR=@ zUUzBF=MLcbLsBZCd(YX!ny05 z{$aRr{(VK)Hix5OfB0%x)yO0L8^`SG-p=&U&aJ{x)(o`(U7pY-iSBM$@otj)UMbre1srq9Fmy)XveoC%84;R~&O zVc45e;Z$(%>+`Yx1(QvUInby{SnoxF(J!<6sUX`7_--fr!YOEUsvo#_APC`rNlr

    xp3-M?@NM=ZgT*vBs|qZ-ee} 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 R_(p5Sao+es)V6mX{trHS`NaSL literal 19258 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 ZCFrh45EFN}?Q2WweI?vo<^NLF{{hhCVWt28 From 35f2d14269edd542a4ed6cc4194b1bfcb34ab2a8 Mon Sep 17 00:00:00 2001 From: OyczE <46287652+OyczE@users.noreply.github.com> Date: Mon, 22 Dec 2025 19:36:28 +0100 Subject: [PATCH 07/17] Delete ESP32C5/.vscode/settings.json --- ESP32C5/.vscode/settings.json | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 ESP32C5/.vscode/settings.json diff --git a/ESP32C5/.vscode/settings.json b/ESP32C5/.vscode/settings.json deleted file mode 100644 index 25e20bb..0000000 --- a/ESP32C5/.vscode/settings.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "idf.flashType": "UART", - "idf.monitorPort": "COM10", - "idf.port": "/dev/tty.usbserial-240", - "idf.portWin": "COM10", - "idf.espIdfPathWin": "C:\\Users\\mati\\Documents\\GitHub\\ESP\\master\\esp-idf", - "idf.toolsPathWin": "C:\\Users\\mati\\.espressif", - "idf.pythonInstallPath": "C:\\Users\\mati\\.espressif\\tools\\idf-python\\3.11.2\\python.exe", - "idf.openOcdConfigs": [ - "board/esp32c5-builtin.cfg" - ], - "idf.customExtraVars": { - "IDF_TARGET": "esp32c5" - }, - "clangd.path": "C:\\Users\\mati\\.espressif\\tools\\esp-clang\\esp-19.1.2_20250312\\esp-clang\\bin\\clangd.exe", - "clangd.arguments": [ - "--background-index", - "--query-driver=C:\\Users\\mati\\.espressif\\tools\\riscv32-esp-elf\\esp-15.2.0_20250920\\riscv32-esp-elf\\bin\\riscv32-esp-elf-gcc.exe", - "--compile-commands-dir=c:\\Users\\mati\\Documents\\GitHub\\projectZero\\ESP32C5\\build" - ] -} From 03295e266ebdbb4f71e030efb5174fac3c9682ba Mon Sep 17 00:00:00 2001 From: OyczE <46287652+OyczE@users.noreply.github.com> Date: Tue, 23 Dec 2025 17:31:53 +0100 Subject: [PATCH 08/17] Implement branch selection for .fap downloads Added branch selection for fetching .fap assets. --- docs/flipper_fap.html | 71 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 13 deletions(-) diff --git a/docs/flipper_fap.html b/docs/flipper_fap.html index c444849..b57981b 100644 --- a/docs/flipper_fap.html +++ b/docs/flipper_fap.html @@ -163,6 +163,13 @@

    Control

    Run app after upload
    +
    + + +
    - - - - -
    -
    - Ready to connect. - - -
    -
    - -
    -
    -
    -
    - -
    -

    Release info

    -

    Loading manifest

    -
    -
  • ROM mode: Hold BOOT on the LAB C5, plug the board in, then release.
  • -
  • Close serial hogs: quit qFlipper/monitors or anything holding the COM port.
  • -
  • Stuck- Unplug, re-run Connect, plug again with BOOT held, then Flash.
  • -
  • Reset: after flashing, press RESET on the board if it does not auto-reboot.
  • -
    -
    -
    - -
    -
    -

    What happens

    -

    Using esptool-js directly: enter stub, set baud, then flash each image at the offsets from manifest.json. Progress and MD5 checks mirror the Python esptool flow.

    -
    -
    -

    Need offline-

    -

    Grab the same assets from Releases and run flash_board.py in ESP32C5/binaries-esp32c5 if your browser blocks Web Serial.

    -
    -
    -

    Browser support

    -

    Chrome or Edge on desktop over HTTPS. Web Serial is required. If you see Web Serial not available, switch browsers.

    -
    -
    -
    - - - - + + + + + + projectZero Web Flasher + + + + + + + +
    +
    +
    projectZero ESP32-C5
    +

    Flash projectZero straight from your browser

    +

    WebSerial flasher powered by esptool-js. Click Connect, pick the LAB C5 serial port (Chrome/Edge desktop), then Flash to stream bootloader, partition table, and firmware from the latest GitHub Release.

    +
    + +
    +
    +

    Control

    +
    +
    + + + + +
    +
    + Ready to connect. + + +
    +
    + +
    +
    +
    +
    + +
    +

    Release info

    +

    Loading manifest

    +
    +
  • ROM mode: Hold BOOT on the LAB C5, plug the board in, then release.
  • +
  • Close serial hogs: quit qFlipper/monitors or anything holding the COM port.
  • +
  • Stuck- Unplug, re-run Connect, plug again with BOOT held, then Flash.
  • +
  • Reset: after flashing, press RESET on the board if it does not auto-reboot.
  • +
    +
    +
    + +
    +
    +

    What happens

    +

    Using esptool-js directly: enter stub, set baud, then flash each image at the offsets from manifest.json. Progress and MD5 checks mirror the Python esptool flow.

    +
    +
    +

    Need offline-

    +

    Grab the same assets from Releases and run flash_board.py in ESP32C5/binaries-esp32c5 if your browser blocks Web Serial.

    +
    +
    +

    Browser support

    +

    Chrome or Edge on desktop over HTTPS. Web Serial is required. If you see Web Serial not available, switch browsers.

    +
    +
    +
    + + + + + diff --git a/docs/manifest.json b/docs/manifest.json index de0389a..71b9167 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -8,4 +8,4 @@ { "path": "https://raw.githubusercontent.com/C5Lab/projectZero/main/ESP32C5/binaries-esp32c5/partition-table.bin", "offset": 32768 }, { "path": "https://raw.githubusercontent.com/C5Lab/projectZero/main/ESP32C5/binaries-esp32c5/projectZero.bin", "offset": 65536 } ] -} +} \ No newline at end of file From 990e33646445107ef2639197e1c0dab48005ee1a Mon Sep 17 00:00:00 2001 From: Nigdzie <46287652+Nigdzie@users.noreply.github.com> Date: Wed, 24 Dec 2025 13:29:18 +0100 Subject: [PATCH 16/17] Fix webflash --- docs/janos_flash.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/janos_flash.html b/docs/janos_flash.html index 805e9e4..29217f8 100644 --- a/docs/janos_flash.html +++ b/docs/janos_flash.html @@ -366,7 +366,7 @@

    Browser support

    } catch {} let cleaned = path.startsWith("/") ? path.slice(1) : path; const branch = getBranch(); - if (branch !== "main" && cleaned.startsWith("firmware/")) { + if (cleaned.startsWith("firmware/")) { cleaned = cleaned.replace(/^firmware\//, "ESP32C5/binaries-esp32c5/"); } return `https://raw.githubusercontent.com/C5Lab/projectZero/${branch}/${cleaned}`; From f6fbc6c123f7857576b8ed0a80f5467f16626637 Mon Sep 17 00:00:00 2001 From: Nigdzie <46287652+Nigdzie@users.noreply.github.com> Date: Wed, 24 Dec 2025 13:35:23 +0100 Subject: [PATCH 17/17] Fix flasher --- docs/janos_flash.html | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/docs/janos_flash.html b/docs/janos_flash.html index 29217f8..ff991d8 100644 --- a/docs/janos_flash.html +++ b/docs/janos_flash.html @@ -320,6 +320,7 @@

    Browser support

    const state = { manifest: null, + latestRelease: null, files: [], port: null, transport: null, @@ -395,6 +396,13 @@

    Browser support

    return next; }; + const fetchLatestRelease = async () => { + const apiUrl = "https://api.github.com/repos/C5Lab/projectZero/releases/latest"; + const res = await fetch(apiUrl, { cache: "no-store" }); + if (!res.ok) throw new Error(`GitHub release fetch failed: ${res.status} ${res.statusText}`); + return res.json(); + }; + const loadManifest = async () => { try { const manifestUrl = getManifestUrl(); @@ -403,10 +411,21 @@

    Browser support

    const manifestRaw = await res.json(); const manifest = rewritePartsForBranch(manifestRaw, getBranch()); state.manifest = manifest; + let releaseLabel = manifest.version || "n/a"; + if (getBranch() === "main") { + try { + const release = await fetchLatestRelease(); + state.latestRelease = release; + releaseLabel = release.tag_name || release.name || releaseLabel; + } catch (err) { + console.warn(err); + } + } ui.manifest.style.display = "inline-flex"; - ui.manifest.textContent = `Build ${manifest.build || "n/a"} - ${manifest.version || "n/a"} - ${getBranch()}`; + ui.manifest.textContent = `Build ${manifest.build || "n/a"} - ${releaseLabel} - ${getBranch()}`; const files = (manifest.parts || []).length; - ui.releaseInfo.innerHTML = `Manifest ${manifest.name || "projectZero"} - ${files} file(s) - chip ${manifest.chipFamily || "ESP32-C5"} - ${getBranch()}`; + const releaseText = state.latestRelease ? ` - latest ${releaseLabel}` : ""; + ui.releaseInfo.innerHTML = `Manifest ${manifest.name || "projectZero"}${releaseText} - ${files} file(s) - chip ${manifest.chipFamily || "ESP32-C5"} - ${getBranch()}`; setStatus("Manifest loaded."); } catch (err) { setStatus("Manifest not found. Publish a release first.", "error");