From 22f0ab2b5b71bb51873e979ddab33afd23122ea5 Mon Sep 17 00:00:00 2001 From: Jake Lyell Date: Wed, 7 Jan 2026 11:08:55 +1100 Subject: [PATCH 1/6] Add option to flash to backup partition or current partition --- src/app/page.tsx | 104 ++++++++++++++++++++++-------- src/esp/useEspOperations.ts | 123 ++++++++++++++++++++++++++++++++++-- 2 files changed, 194 insertions(+), 33 deletions(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index c729991..59068ff 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -101,38 +101,76 @@ export default function Home() { your device using Save full flash above.

- Flash English/Chinese firmware will download the firmware, + Flash to current partition will download the firmware and + overwrite your current running partition. The device will reboot + with the new firmware on the same partition. This is fast and + retains all settings. +

+

+ Flash to backup partition will download the firmware, overwrite the backup partition with the new firmware, and swap over to using this partition (leaving your existing firmware as - the new backup). This is significantly faster than a full flash - write and will retain all your settings. If it goes wrong, it - should be fine to run again. + the new backup). This is also fast and retains all settings, with + the option to swap back if needed.

- - - - + + + + + + + + + + + + + @@ -146,7 +184,19 @@ export default function Home() { } disabled={isRunning} > - Flash firmware from file + Flash file to current + + {process.env.NODE_ENV === 'development' && ( diff --git a/src/esp/useEspOperations.ts b/src/esp/useEspOperations.ts index 1d104e4..6e71140 100644 --- a/src/esp/useEspOperations.ts +++ b/src/esp/useEspOperations.ts @@ -25,6 +25,61 @@ export function useEspOperations() { const flashRemoteFirmware = async ( getFirmware: () => Promise, + ) => { + initializeSteps([ + 'Connect to device', + 'Download firmware', + 'Read otadata partition', + 'Flash app partition', + 'Reset device', + ]); + + const espController = await runStep('Connect to device', async () => { + const c = await EspController.fromRequestedDevice(); + await c.connect(); + return c; + }); + + const firmwareFile = await runStep('Download firmware', getFirmware); + + const currentPartitionLabel = await runStep( + 'Read otadata partition', + async (): Promise => { + const partition = await espController.readOtadataPartition((_, p, t) => + updateStepData('Read otadata partition', { + progress: { current: p, total: t }, + }), + ); + + return partition.getCurrentBootPartitionLabel(); + }, + ); + + const flashAppPartitionStepName = `Flash app partition (${currentPartitionLabel})`; + updateStepData('Flash app partition', { name: flashAppPartitionStepName }); + await runStep(flashAppPartitionStepName, () => + espController.writeAppPartition( + currentPartitionLabel, + firmwareFile, + (_, p, t) => + updateStepData(flashAppPartitionStepName, { + progress: { current: p, total: t }, + }), + ), + ); + + await runStep('Reset device', () => espController.disconnect()); + }; + + const flashEnglishFirmware = async () => + flashRemoteFirmware(() => getOfficialFirmware('3.1.1-EN')); + const flashChineseFirmware = async () => + flashRemoteFirmware(() => getOfficialFirmware('3.1.5-CH')); + const flashCrossPointFirmware = async () => + flashRemoteFirmware(() => getCommunityFirmware('CrossPoint')); + + const flashRemoteFirmwareToBackup = async ( + getFirmware: () => Promise, ) => { initializeSteps([ 'Connect to device', @@ -84,14 +139,66 @@ export function useEspOperations() { await runStep('Reset device', () => espController.disconnect()); }; - const flashEnglishFirmware = async () => - flashRemoteFirmware(() => getOfficialFirmware('3.1.1-EN')); - const flashChineseFirmware = async () => - flashRemoteFirmware(() => getOfficialFirmware('3.1.5-CH')); - const flashCrossPointFirmware = async () => - flashRemoteFirmware(() => getCommunityFirmware('CrossPoint')); + const flashEnglishFirmwareToBackup = async () => + flashRemoteFirmwareToBackup(() => getOfficialFirmware('3.1.1-EN')); + const flashChineseFirmwareToBackup = async () => + flashRemoteFirmwareToBackup(() => getOfficialFirmware('3.1.5-CH')); + const flashCrossPointFirmwareToBackup = async () => + flashRemoteFirmwareToBackup(() => getCommunityFirmware('CrossPoint')); const flashCustomFirmware = async (getFile: () => File | undefined) => { + initializeSteps([ + 'Read file', + 'Connect to device', + 'Read otadata partition', + 'Flash app partition', + 'Reset device', + ]); + + const fileData = await runStep('Read file', async () => { + const file = getFile(); + if (!file) { + throw new Error('File not found'); + } + return new Uint8Array(await file.arrayBuffer()); + }); + + const espController = await runStep('Connect to device', async () => { + const c = await EspController.fromRequestedDevice(); + await c.connect(); + return c; + }); + + const currentPartitionLabel = await runStep( + 'Read otadata partition', + async (): Promise => { + const partition = await espController.readOtadataPartition((_, p, t) => + updateStepData('Read otadata partition', { + progress: { current: p, total: t }, + }), + ); + + return partition.getCurrentBootPartitionLabel(); + }, + ); + + const flashAppPartitionStepName = `Flash app partition (${currentPartitionLabel})`; + updateStepData('Flash app partition', { name: flashAppPartitionStepName }); + await runStep(flashAppPartitionStepName, () => + espController.writeAppPartition( + currentPartitionLabel, + fileData, + (_, p, t) => + updateStepData(flashAppPartitionStepName, { + progress: { current: p, total: t }, + }), + ), + ); + + await runStep('Reset device', () => espController.disconnect()); + }; + + const flashCustomFirmwareToBackup = async (getFile: () => File | undefined) => { initializeSteps([ 'Read file', 'Connect to device', @@ -382,6 +489,10 @@ export function useEspOperations() { flashChineseFirmware: wrapWithRunning(flashChineseFirmware), flashCrossPointFirmware: wrapWithRunning(flashCrossPointFirmware), flashCustomFirmware: wrapWithRunning(flashCustomFirmware), + flashEnglishFirmwareToBackup: wrapWithRunning(flashEnglishFirmwareToBackup), + flashChineseFirmwareToBackup: wrapWithRunning(flashChineseFirmwareToBackup), + flashCrossPointFirmwareToBackup: wrapWithRunning(flashCrossPointFirmwareToBackup), + flashCustomFirmwareToBackup: wrapWithRunning(flashCustomFirmwareToBackup), saveFullFlash: wrapWithRunning(saveFullFlash), writeFullFlash: wrapWithRunning(writeFullFlash), fakeWriteFullFlash: wrapWithRunning(fakeWriteFullFlash), From 2b871c79a18ea5b9f1f427acd4c57d85deff417f Mon Sep 17 00:00:00 2001 From: Jake Lyell Date: Wed, 7 Jan 2026 11:24:37 +1100 Subject: [PATCH 2/6] move flash to current to the debug page --- src/app/debug/page.tsx | 83 +++++++++++++++++++++++++++++++++++++++++- src/app/page.tsx | 50 +++---------------------- 2 files changed, 88 insertions(+), 45 deletions(-) diff --git a/src/app/debug/page.tsx b/src/app/debug/page.tsx index d974b85..9d19b42 100644 --- a/src/app/debug/page.tsx +++ b/src/app/debug/page.tsx @@ -15,6 +15,7 @@ import { Separator, Stack, Table, + Text, } from '@chakra-ui/react'; import { useEspOperations } from '@/esp/useEspOperations'; import Steps from '@/components/Steps'; @@ -23,6 +24,7 @@ import OtaPartition, { OtaPartitionDetails } from '@/esp/OtaPartition'; import HexSpan from '@/components/HexSpan'; import HexViewer from '@/components/HexViewer'; import { downloadData } from '@/utils/download'; +import FileUpload, { FileUploadHandle } from '@/components/FileUpload'; function OtadataDebug({ otaPartition }: { otaPartition: OtaPartition }) { const bootPartitionLabel = otaPartition.getCurrentBootPartitionLabel(); @@ -197,8 +199,9 @@ function AppDebug({ } export default function Debug() { - const { debugActions, stepData, isRunning } = useEspOperations(); + const { actions, debugActions, stepData, isRunning } = useEspOperations(); const [debugOutputNode, setDebugOutputNode] = useState(null); + const appPartitionFileInput = React.useRef(null); return ( @@ -288,6 +291,84 @@ export default function Debug() { + +
+ Overwrite current partition (Advanced) + +

+ These are advanced flashing options for users who want to flash + firmware directly to the currently selected partition, as opposed + to the backup partition. +

+

+ Flash to current partition will download the firmware and + overwrite your current running firmware. The device will reboot + with the new firmware on the same partition. +

+
+ + + + Warning: Current firmware will be overwritten + + Flashing to the current partition will overwrite the + currently used firmware and leave the backup partition unchanged. + Proceed with caution. + + + +
+ + + + + + + + + + + + + + + + + +
+ Steps diff --git a/src/app/page.tsx b/src/app/page.tsx index 59068ff..2b68c96 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -100,31 +100,21 @@ export default function Home() { Before using this, I'd strongly recommend taking a backup of your device using Save full flash above.

-

- Flash to current partition will download the firmware and - overwrite your current running partition. The device will reboot - with the new firmware on the same partition. This is fast and - retains all settings. -

Flash to backup partition will download the firmware, overwrite the backup partition with the new firmware, and swap over to using this partition (leaving your existing firmware as - the new backup). This is also fast and retains all settings, with + the new backup). This is fast and retains all settings, with the option to swap back if needed.

+

+ For more advanced flashing options (like flashing to the current + partition), see the Debug page. +

- - From 5d2f8472e73105b75035c0179783419bec93fc9c Mon Sep 17 00:00:00 2001 From: Jake Lyell Date: Thu, 15 Jan 2026 09:46:10 +1100 Subject: [PATCH 4/6] Accidental styling change fix --- src/app/page.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index a6b2ff8..657ca82 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -116,7 +116,7 @@ export default function Home() { + diff --git a/src/app/page.tsx b/src/app/page.tsx index 84db74f..0aa3611 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -124,7 +124,8 @@ export default function Home() { overwrite the backup partition with the new firmware, and swap over to using this partition (leaving your existing firmware as the new backup). This is fast and retains all settings, with the - option to swap back if needed. + option to swap back if needed. If it goes wrong, it should be fine + to run again.

For more advanced flashing options (like flashing to the current @@ -177,7 +178,7 @@ export default function Home() { } disabled={isRunning} > - Flash file to backup + Flash firmware from file to backup {process.env.NODE_ENV === 'development' && ( From 2e768446f384b81045ea63db81c099d42aab196b Mon Sep 17 00:00:00 2001 From: Jake Lyell Date: Tue, 20 Jan 2026 21:52:08 +1100 Subject: [PATCH 6/6] remove unused import --- src/app/debug/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/debug/page.tsx b/src/app/debug/page.tsx index 03c0def..e3d17aa 100644 --- a/src/app/debug/page.tsx +++ b/src/app/debug/page.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { ReactNode, useRef, useEffect, useState } from 'react'; +import React, { ReactNode, useEffect, useState } from 'react'; import { Alert, Badge,