ESP Platform Phase 1E Validation
ESP Platform Phase 1E Validation
Required checks:
- Backend is active through systemd.
- Web is available at
http://192.168.1.62:88/. .binupload works.- Firmware binary is stored under
/srv/esp-platform/firmware. - Metadata is stored in PostgreSQL table
firmware. - Size and SHA-256 are calculated automatically.
- Firmware list displays the uploaded firmware.
- Download endpoint returns the stored
.bin.
API endpoints:
GET /api/v1/firmware
GET /api/v1/firmware/{id}
POST /api/v1/firmware
GET /api/v1/firmware/{id}/download
Phase 1E Validation Run - 2026-07-06
Validation was executed directly inside VM esp-platform.
Result: passed.
Observed test values:
- Uploaded firmware id:
1 - Uploaded filename:
validation.bin - Metadata name:
Validation Firmware - Application:
AZ-TEST - Hardware:
ESP32 DevKit - Stored filesystem path:
/srv/esp-platform/firmware/038cfdcf-3ff2-45c5-ba5f-d7ec5515593c.bin - Size:
33bytes - SHA-256:
82d3c475f7f8e72e521e38e41e60efc6b6375452fbf2ea336e4430daea54c4f8
Checks passed:
esp-platform-backendactive through systemd.- Web accessible through Nginx at
http://192.168.1.62:88/. - API accessible through Nginx at
http://192.168.1.62:88/api/v1/firmware. - Upload stored metadata in PostgreSQL.
- Upload stored
.binin filesystem. - Downloaded file matched original size and SHA-256.
Phase 2 Browser Validation - 2026-07-06
Validation performed:
http://192.168.1.62:88/flasherloads successfully.GET /api/v1/firmwareis consumed by the page.- Direct HTTP access by VM IP is not a secure Web Serial context; the page shows a warning and disables serial connection.
- Temporary local tunnel tested:
ssh -N -L 18888:127.0.0.1:88 codex@192.168.1.62
http://localhost:18888/flasherloads successfully.- Through localhost, the secure-context warning is not shown and the Connect ESP32 button is enabled.
Pending physical validation:
- Upload real firmware
.binfrom the administration page. - Select the firmware in
/flasher. - Connect ESP32 by USB in Chrome/Edge.
- Grant serial-port permission.
- Flash firmware and confirm ESP32 boots.
Phase 2 HTTPS Pre-Validation - 2026-07-06
NPM HTTPS route configured by project owner.
Checked URL:
https://iot.aeizoon.com/flasher
Result:
- Page loads correctly through HTTPS.
- Insecure-context warning is not displayed.
- Connect ESP32 button is enabled by the page.
- Firmware catalog loads successfully.
- Catalog currently contains no firmware after removal of the Phase 1E validation test firmware.
Physical validation remains pending with a real firmware .bin and ESP32 connected to the Mac.
Phase 2 Physical Validation Attempt - ESP32 Ethernet/WiFi AliExpress - 2026-07-06
Hardware and connection:
- Board: ESP32 Ethernet/WiFi AliExpress board.
- USB connection: external USB-TTL adapter.
- Browser route:
https://iot.aeizoon.com/flasher.
Observed successful steps:
- Web Serial works through HTTPS.
- Flasher connects to the ESP32.
- Chip detected:
ESP32-D0WD rev 1. - MAC address detected.
- Stub upload works.
- Flash ID is read.
- Firmware download from backend works.
- Downloaded firmware size:
1,274,512bytes.
Observed baudrate behavior:
115200is stable.921600fails after baudrate change with errors similar to:Unable to verify flash chip connectionSerial data stream stopped
Decision applied:
- Default baudrate changed to
115200. - Higher baudrates remain available under the advanced speed group.
Observed flashing failure before correction:
File 1 doesn't fit in the available flash
The error occurred with both:
- flash address
0x0 - flash address
0x10000
Review result:
esptool-jsexpectsfileArray: [{ data: Uint8Array, address: number }]; the implementation usesUint8Arrayfromresponse.arrayBuffer().- The library performs a fit check when
flashSize !== "keep". - Passing
flashSize: "detect"directly towriteFlash()is unsafe becauseflashSizeBytes("detect")does not resolve to the detected byte size for the fit check. - The Flasher now calls
detectFlashSize()after connecting and sends the resolved size string, such as4MB, towriteFlash(). - If auto-detection is unavailable, the Flasher sends
keepto avoid the false fit check.
Additional logging added before flashing:
- detected flash size;
- configured flash size sent to
esptool-js; - firmware byte length;
- binary data type and
Uint8Arraycheck; - flash start address;
- calculated end address.
Second physical validation result:
- The same
File 1 doesn't fit in the available flasherror was observed again. - The browser output did not include the newly added diagnostic lines, which confirmed that the HTTPS route was still serving a cached older
flasher.js.
Correction applied after second attempt:
- Flasher assets now use cache-busting version
phase2-20260706-3. - The Flasher logs its build at page load:
Flasher build: phase2-20260706-3. - Internal Spring/Nginx delivery for static assets now uses no-cache headers.
- NPM still adds external cache headers, so every Flasher JS change must also bump the asset version query string.
- Default Flash size changed to
Keep firmware settingto bypass theesptool-jspre-write fit check. Auto-detected size checkremains available as a diagnostic option, not the default.
Current status:
- Ready for another physical validation attempt at
115200baud. - Confirm the Flasher console starts with
Flasher build: phase2-20260706-3. - Use Flash size
Keep firmware setting. - If it still fails, capture the Flasher console log and browser console output.
Phase 2 Physical Flash Success - 2026-07-06
Physical flashing succeeded through https://iot.aeizoon.com/flasher using Flasher build phase2-20260706-3.
Observed result:
- Firmware
freepocket 0.3.5jdownloaded from backend. - Firmware size:
1,274,512bytes. - ESP32 detected:
ESP32-D0WD revision 1. - Flash size detected:
4MB. - Flash address:
0x0. - Configured flash size sent to
esptool-js:keep. - Written bytes:
1,274,512. - Compressed bytes:
804,935. - Write duration:
72.507seconds at115200. esptool-jsrequested hard reset via RTS after flashing.
Follow-up UI adjustment:
- Flasher build
phase2-20260706-4adds anErase ESP32button. - The erase button is enabled only after a serial connection is established.
- It asks for confirmation before calling
eraseFlash(). - The button is visually separated from
Disconnectwith a4cmleft margin on desktop layouts.
Phase 2 Serial Monitor and Hardware Reset - 2026-07-06
Flasher build phase2-20260706-6 adds post-flash serial diagnostics:
Start Serial Monitoropens the selected Web Serial port at the currently selected baudrate.- If the esptool flasher connection is still open, it is closed first and the same port is reopened for serial reading.
- Serial bytes are written directly into the Flasher console so the ESP32 boot log can be inspected.
Stop Monitorcloses the serial monitor and releases the port.Hardware Resetsends reset through esptool when the flasher connection is active.- When the serial monitor is active,
Hardware Resetpulses RTS through Web Serial so boot output can be observed immediately.
Operational test path:
- Flash firmware successfully.
- Click
Start Serial Monitor. - Click
Hardware Resetor press the board reset button. - Confirm boot output appears in the console.
Phase 2 FreePocket Firmware Layout Finding - 2026-07-07
A comparison with https://freepocket.chrisoft.io/ showed that the external FreePocket installer is powered by ESP Web Tools and uses a manifest with multiple flash parts, not a single binary written at 0x0.
Observed external manifest layout:
- bootloader:
0x1000 - partitions:
0x8000 - boot_app0:
0xE000 - application:
0x10000 - LittleFS:
0x290000
The firmware uploaded to ESP Platform as freepocket 0.3.5j has size 1,274,512 bytes and starts with an ESP image header at offset 0x0:
- magic:
0xE9 - segments:
6 - flash mode byte:
0x02 - flash size/frequency byte:
0x2f - entry point:
0x400827ac
This indicates that the uploaded file is likely an application image intended to be written at 0x10000, not a complete flash image intended for 0x0.
Important consequence:
- Flashing this app image at
0x0overwrites the bootloader area and can prevent the ESP32 from booting correctly. - Flashing it at
0x10000only works if the target ESP32 already has compatible bootloader, partition table, boot_app0 and filesystem/data partitions. - A clean device or a previously erased device requires a multi-part firmware package/manifest, not only the application
.bin.
Next required platform correction:
- ESP Platform should distinguish between single full-image firmware and multi-part ESP32 firmware packages.
- For ESP-IDF/Arduino app-only binaries, the flasher should require or infer address
0x10000instead of defaulting to0x0. - For FreePocket-style installs, ESP Platform needs manifest/multi-part upload support before it can reproduce the external installer behavior exactly.
Phase 2 External FreePocket Files Retrieved - 2026-07-07
The public files referenced by https://freepocket.chrisoft.io/manifest_latest.json were downloaded for technical comparison and validation reference into:
/opt/esp-platform/repo/firmware/reference/freepocket
Downloaded layout:
| Offset | File | Size | SHA-256 |
| --- | --- | ---: | --- |
| 0x1000 | pincho_solax.bootloader.bin | 18,992 | 644de0067047e22380034b8989c39e5d2882f7538c698788866ca5130427322e |
| 0x8000 | pincho_solax.partitions.bin | 3,072 | 148b959cbff1c38aa8e1d5c0ba9d612c54997b945e56a63f41223eef650653a1 |
| 0xE000 | boot_app0.bin | 8,192 | f94c5d786a7a8fab06ac5d10e33bf37711a6697636dc037559ea19cc410a17f0 |
| 0x10000 | pincho_solax.bin | 1,072,320 | 026f043621c2338c3172751f54e2c9ca9bb9f1741b78c0b8799cf78f6843f9ac |
| 0x290000 | pincho_solax.littlefs.bin | 1,441,792 | 0edc7b60e8162ab7177992783648dd8d5857dab463c2f0a60715aa40af57a0e1 |
Important version note:
- The public manifest reports version
0.3.5f. - The firmware currently uploaded to ESP Platform is labeled
0.3.5jand has size1,274,512bytes. - These should not be treated as the same firmware package.
Use of the retrieved files:
- They are suitable as a reference to reproduce the external installer behavior in a validation environment.
- ESP Platform still needs explicit multi-part package support before these files are represented cleanly in the database and web UI.
Phase 2 Multi-Part Firmware Support - 2026-07-07
ESP Platform now supports a minimal multi-part firmware package model for Phase 2 validation.
Backend changes:
- Added
firmware.package_typewith valuesSIMPLEandMULTIPART. - Added
firmware_parttable with per-part metadata:- part order;
- display name;
- original filename;
- filesystem path;
- flash offset;
- size;
- SHA-256 checksum.
- Added API download endpoint for parts:
GET /api/v1/firmware/{id}/parts/{partId}/download
GET /api/v1/firmwarenow returnspackageType,partCountandparts[].
Seeded validation package:
- Firmware id:
3 - Name:
FreePocket full install - Version:
0.3.5f - Type:
MULTIPART - Total size:
2,544,368bytes - Source reference: public FreePocket ESP Web Tools manifest.
Flasher changes:
- Flasher build:
phase2-20260706-7. - Multi-part firmware cards are displayed with a distinct blue
Multi-partbadge. - Simple
.binfirmware remains supported asSingle .bin. - For
MULTIPART, the Flasher downloads all parts and sends a multi-entryfileArraytoesptool-js. - For
MULTIPART, the Flasher uses:eraseAll: trueflashMode: keepflashFreq: keep- configured flash size from the UI, default
keep.
Validation performed:
- Backend service active after Flyway V3 migration.
- API lists both the existing simple firmware and the new multi-part FreePocket package.
- All five part download endpoints return HTTP 200 with expected sizes.
Physical validation pending:
- Select
FreePocket full install 0.3.5ffrom/flasher. - Confirm the card is marked
Multi-part. - Connect ESP32 at
115200. - Flash selected firmware.
- Start Serial Monitor.
- Send
Hardware Resetor press reset manually. - Confirm complete boot log and device operation.
Phase 2 FreePocket AP Password and Version Search - 2026-07-07
The FreePocket documentation page states that, after first boot, the ESP32 creates an AP named fp_xxxxxx and the default password is <redacted>. The same page states the default local IP is 192.168.4.1 with web user admin and password <redacted>.
Binary inspection confirms related defaults inside the downloaded public package:
- LittleFS contains
WEB_USERNAME:admin. - LittleFS contains
WEB_PASSWORD:<redacted>. - Application strings contain AP creation format
fp_%02x%02x%02xand log textenabling AP: ssid:%s, password:****.
Public installer version search:
https://freepocket.chrisoft.io/references onlymanifest_latest.json.- The public manifest reports version
0.3.5f. - Tested common manifest/version names such as
manifest_0.3.5g.json,manifest_0.3.5j.json,manifest_stable.json,manifest_beta.json: onlymanifest_latest.jsonis exposed.
Current conclusion:
- Public direct ESP Web Tools installer exposes only FreePocket
0.3.5f. - The
0.3.5jbinary uploaded manually to ESP Platform appears to come from another source or unpublished package and should not be assumed to match the public multi-part package.
Additional version finding:
manifest_latest.jsonreports0.3.5f.- The downloaded
pincho_solax.binapplication image contains internal string0.3.5g. - ESP Platform display metadata was updated to
manifest 0.3.5f / app 0.3.5gto avoid confusing this package with the manually uploaded app-only0.3.5jfirmware.
Phase 2 Patched AP Password Package - 2026-07-07
A test firmware package was created by binary patching the public FreePocket multi-part package from default string <redacted> to <redacted>.
Reason:
- The ESP32 AP appears as
fp_xxxxxx, but the default password did not work reliably in physical testing. - The string
<redacted>appears once in the application image and once in the LittleFS image. - The replacement
<redacted>has the same length as<redacted>, so the patch does not shift binary offsets.
Created package:
- Firmware id:
4 - Name:
FreePocket full install AP <redacted> - Version label:
manifest 0.3.5f / app 0.3.5g / AP <redacted> - Type:
MULTIPART - Expected AP/web password for validation:
<redacted>
Changed checksums:
- application
pincho_solax.bin:4454f8d1ecb422741ff7fa535f664bb063286d687c98f1beeda98d6b2655a16b - littlefs
pincho_solax.littlefs.bin:cabb88ab8b621e56eece8f74173ec4014cebb3e3965c8232ccc96fcc026ddbd0
Unchanged parts:
- bootloader
- partition table
- boot_app0
Validation performed:
- The patched package appears in
/api/v1/firmware. - All five part download endpoints return HTTP 200.
- The patched application and LittleFS files contain
<redacted>and no longer expose<redacted>through simple string inspection.
Risk note:
- This is a binary patch for validation only. If LittleFS metadata or firmware logic validates file contents internally, the device may ignore the patched value or behave differently. A source-level firmware build would be cleaner once source/build inputs are available.
Phase 2 Patched AP Password Package Repair - 2026-07-07
Physical test result:
- Flashing
FreePocket full install AP <redacted>caused repeatedSW_RESETbefore FreePocket printed its normal[general] starting programlog line. - The bootloader reached
entry 0x400805f0, then the ESP32 reset repeatedly.
Cause found:
- Binary patching the application image changed bytes inside
pincho_solax.bin. - The ESP32 application image footer still contained the original checksum and validation SHA-256 digest.
esptool image_inforeported:- checksum invalid;
- validation hash invalid.
Repair applied:
- Recomputed the ESP image checksum byte from
0x82to0x87. - Recomputed the appended validation SHA-256 digest.
- Replaced the application part for firmware id
4with the repaired image.
Repaired application checksum:
- File SHA-256:
fff0335928c92bc3150b5e527d11543b1db28c2dca22bde69ce152f33e105e22 - ESP image checksum: valid
- ESP validation hash: valid
Next physical validation:
- Flash
FreePocket full install AP <redacted>again from/flasher. - Confirm the normal FreePocket boot log appears.
- Try AP password
<redacted>.
Admin Firmware UI Maintenance - 2026-07-07
Changes validated:
/admin/firmwarenow exposes a delete action for stored firmware records and their filesystem files./admin/firmwarenow exposes a multi-part ESP32 package upload form with explicit offsets for bootloader, partitions, boot_app0, application, and filesystem/LittleFS images.- Multi-part firmware entries are visually distinguished with blue badges.
- The shared web palette was adjusted away from green success/console accents toward blue.
/flasherand/admin/firmwarewere checked for horizontal page overflow after the layout/CSS changes.
Functional validation:
- Temporary single
.binupload returned HTTP 302, appeared through/api/v1/firmware, and was deleted through/admin/firmware/{id}/delete. - Temporary multi-part upload with five
.binfiles returned HTTP 302, appeared through/api/v1/firmwareasMULTIPARTwithpartCount=5, and was deleted through/admin/firmware/{id}/delete. - After deletion, no temporary validation firmware remained in
/api/v1/firmware. - Backend service
esp-platform-backendwas rebuilt, redeployed, restarted, and remained active.
Browser validation through https://iot.aeizoon.com:
/admin/firmwareloaded CSSapp.css?v=phase2-20260707-1./admin/firmwareshowed the multi-part upload form and delete buttons./admin/firmwarereported no horizontal overflow at 1280 px viewport width./flasherloaded CSSapp.css?v=phase2-20260707-1./flashershowedErase ESP32,Start Serial Monitor,Stop Monitor, andHardware Resetcontrols./flasherreported no horizontal overflow at 1280 px viewport width.
Firmware Lock/Edit Administration - 2026-07-07
Changes validated:
- Firmware records now have an
enabledstate stored in PostgreSQL. - Existing firmware records were migrated as enabled by default through Flyway
V4__firmware_enabled.sql. /admin/firmwareshows firmware status asEnabledorLocked.- Clicking a firmware name in
Available firmwareopens/admin/firmware/{id}/edit. - The edit screen allows updating name, version, application, compatible hardware, description, and enabled state without replacing stored binaries.
/admin/firmwareprovides quickLock/Unlockactions.- Locked firmware is hidden from
GET /api/v1/firmware, so it is not offered by the Flasher Web catalog. - Locked firmware download endpoints return an error instead of serving the binary/parts.
Functional validation:
- Temporary firmware upload returned HTTP 302 and appeared in
/api/v1/firmwareas enabled. - Lock action returned HTTP 200.
- After locking, the temporary firmware no longer appeared in
/api/v1/firmware. - Direct download of the locked firmware returned HTTP 400.
- Unlock action returned HTTP 200.
- Edit action returned HTTP 200 and changed metadata visible from
/api/v1/firmware/{id}. - Delete action returned HTTP 200 and removed the temporary firmware.
Browser validation through https://iot.aeizoon.com:
/admin/firmwareloaded CSSapp.css?v=phase2-20260707-2.- Firmware names rendered as edit links.
- Lock buttons rendered for enabled firmware.
/admin/firmware/{id}/editrendered the metadata form and enabled checkbox./admin/firmwareand/admin/firmware/{id}/editreported no horizontal overflow at 1280 px viewport width.
Firmware Part Download And Replacement - 2026-07-08
Changes validated:
- Multipart firmware edit pages now expose a
Downloadaction for each current firmware part. - Multipart firmware edit pages now expose a per-part
.binreplacement form. - Admin part downloads use
/admin/firmware/{id}/parts/{partId}/downloadand are intended for maintenance/review. - Public/API part downloads remain protected by firmware enabled state.
- Replacing a part stores the new binary in filesystem, updates original filename, size, SHA-256, and recalculates the parent package size/checksum.
Functional validation:
- Temporary multipart firmware upload returned HTTP 302.
- Admin download of the temporary bootloader part returned the expected original bytes.
- Replacing that bootloader part returned HTTP 200.
- The part filename, size, and SHA-256 changed to match the replacement
.bin. - The parent multipart package total size changed and package SHA-256 was recalculated.
- Temporary multipart firmware was deleted successfully and did not remain in
/api/v1/firmware.
Browser validation through https://iot.aeizoon.com:
/admin/firmware/4/editloaded CSSapp.css?v=phase2-20260708-1.- The edit page rendered 5 part rows, 5
Downloadlinks, 5 file inputs, and 5Replacebuttons. - The page reported no horizontal overflow at 1280 px viewport width.
ESP32 Merged Image Assembler - 2026-07-12
Implementation validated:
- Added derived artifact storage for merged ESP32 images linked to the source multipart firmware.
- Added PostgreSQL migration
V5__firmware_derived_artifacts.sqlwith merge parameters onfirmwareand derived artifact metadata infirmware_derived_artifact. - Installed and exposed official
esptoolas/usr/local/bin/esptoolon the VM. - Backend generation uses
esptool --chip <chip> merge-bin --flash-mode <mode> --flash-freq <freq> --flash-size <size> -o <output> <offset> <file> .... - Backend validates segment existence, offsets, overlap, flash-size range, stored file sizes, SHA-256 checksums, and supported esptool parameters before merge.
- Merged output is stored under
/srv/esp-platform/firmware/derived/firmware-<id>/. - Generated artifacts record source firmware, filename, size, SHA-256, generated date, flash address
0x0, parameters, status, error message, and command line. - Admin firmware edit page now shows merge parameters,
Generar imagen unificada, derived artifacts, download, regeneration by creating a new artifact, and delete-artifact-only actions. - Flasher Web catalog now exposes generated
MERGED_BINartifacts as selectable flashable items, using address0x0without manual offsets. - Existing multipart flashing remains available and unchanged.
Validation results:
- Initial generation with FreePocket forced to filesystem/spiffs offset
0x310000and flash size4MBfailed correctly with:Segment spiffs exceeds configured flash size 4MB. - The existing FreePocket binaries stored in ESP Platform have a final filesystem image of
1,441,792bytes. At0x310000that image exceeds 4 MB; at the previously validated0x290000layout it fits exactly inside 4 MB. - FreePocket package id
4was kept on its effective validated layout to preserve existing multipart flashing behavior. - Generated merged artifact for firmware id
4:- Artifact id:
2 - Filename:
firmware-4-merged.bin - Size:
4,128,768bytes - SHA-256:
3b0eb453795260d52c0ce7eae731f618ced9a2cc98720b6a32e5aab279d2a516 - Flash address:
0x0 - Parameters:
ESP32 / dio / 80m / 4MB
- Artifact id:
- Download of
/api/v1/firmware/4/artifacts/2/downloadreturned HTTP 200 and SHA-256 matched PostgreSQL. - A temporary multipart package was created to validate derived artifact deletion:
- upload returned HTTP 302;
- generation returned HTTP 302 and produced a
GENERATEDartifact; - deleting only the derived artifact returned HTTP 302 and left the firmware record intact;
- deleting the temporary firmware then removed the source package.
- Service was restarted after Flyway repair/checksum alignment; Flyway validates 5 migrations and backend starts successfully.
Operational note:
- A requested layout of
0x310000 -> spiffs.binwith4MBis only valid when the spiffs/filesystem image size is small enough to end at or before0x400000. The backend now rejects invalid combinations instead of producing a misleading merged image.
Merged Image Published As Autonomous Firmware - 2026-07-12
Decision update:
- A merged image generated from a multipart package is now published as a normal
SIMPLEfirmware record. - The generated firmware is operationally autonomous: it can be edited, locked/unlocked, downloaded and selected by the Flasher Web like any single-block
.binuploaded manually. - The origin is preserved only as descriptive context and generation audit metadata; the generated firmware is not behaviorally dependent on the source multipart package.
Validated generated firmware:
- Firmware id:
16 - Name:
FreePocket full install AP <redacted> - merged image - Type:
SIMPLE - Original filename:
firmware-4-merged.bin - Size:
4,128,768bytes - SHA-256:
3b0eb453795260d52c0ce7eae731f618ced9a2cc98720b6a32e5aab279d2a516 - Download endpoint:
/api/v1/firmware/16/download - Source firmware dependency: none (
sourceFirmwareId = null)
Validation:
/admin/firmwareshowsFreePocket full install AP <redacted> - merged imageas a normal firmware row./admin/firmware/16/editis available./api/v1/firmware/16/downloadreturned HTTP 200.- Downloaded file size and SHA-256 matched the database metadata.
- Backend logs after deployment showed no new errors or Flyway validation failures.
Artifact Library And Firmware Clone Safety - 2026-07-12
Implementation validated:
- Added independent
artifactstorage library backed by/srv/esp-platform/artifacts. - Added
firmware_segmentas the active firmware-to-binary relation table. - Removed legacy
firmware_partwith migrationV9__drop_legacy_firmware_part_table.sql; the active model is nowfirmware,firmware_segment, andartifact. - Each
firmware_segmentreferences oneartifactthrough a PostgreSQL foreign key. - Artifacts are identified by SHA-256 and reused when the same binary is uploaded again.
- Existing firmware binaries were consolidated into
/srv/esp-platform/artifacts/<sha-prefix>/<sha>.bin. - Firmware deletion now removes the firmware record and its segment relations only; it does not delete physical artifacts directly.
- Orphan physical artifact deletion is performed only through the explicit orphan cleanup operation.
- Firmware records can share the same artifact path after migration
V8__allow_firmware_records_to_share_artifacts.sqlremoved the legacyfirmware.stored_filenameuniqueness constraint.
Validation script:
- Script:
/opt/esp-platform/repo/scripts/validate-artifact-library.sh - Base URL used:
http://127.0.0.1:8080
Validated flow:
- Created a temporary single-file firmware through
/admin/firmware. - Verified metadata in
firmware,firmware_segment, andartifact. - Cloned the firmware through
/admin/firmware/{id}/clone. - Verified clone and original shared the same
artifact_id. - Deleted the original firmware.
- Verified the clone still downloaded successfully from
/api/v1/firmware/{cloneId}/downloadand SHA-256 matched. - Deleted the clone.
- Verified the artifact became orphaned but still existed physically.
- Ran
/admin/artifacts/cleanup-orphans. - Verified the orphan artifact row and physical
.binwere removed.
Result:
Artifact library validation passed. Test firmware was cloned, original deleted, clone download verified, clone deleted, orphan artifact cleaned.- The same validation script was re-run successfully after applying
V9__drop_legacy_firmware_part_table.sql.
FreePocket NVS Diagnostic Preparation - 2026-07-12
Prepared diagnostic workflow after LittleFS preconfiguration failed to change runtime Wi-Fi behavior.
Findings:
- FreePocket NVS partition is
nvsat offset0x9000, size0x5000/ 20480 bytes. - Partition table source artifact:
/srv/esp-platform/artifacts/14/148b959cbff1c38aa8e1d5c0ba9d612c54997b945e56a63f41223eef650653a1.bin. - Official ESP-IDF NVS parser installed under
/opt/esp-platform/tools/esp-idf/components/nvs_flash/nvs_partition_tool/nvs_tool.py. - Repo scripts prepared:
/opt/esp-platform/repo/scripts/freepocket-nvs-info.sh/opt/esp-platform/repo/scripts/analyze-nvs-dump.sh/opt/esp-platform/repo/scripts/compare-nvs-dumps.sh
- Diagnostic guide:
/opt/esp-platform/repo/docs/diagnostics/freepocket-nvs-diagnostics.md.
Next required physical data:
- NVS dump immediately after flashing and before manual configuration.
- NVS dump after manual configuration with SSID
Solax, Wi-Fi password<redacted>, web useradmin, and web password <redacted>.
Flasher Web Read Flash Region - 2026-07-12
Implemented an advanced read-only flash dump function in /flasher for NVS diagnostics.
Validated by HTTP/HTTPS delivery:
/flashernow contains advanced sectionRead flash region.- Preset
FreePocket NVSsets offset0x9000and size0x5000. - Suggested filenames are available:
freepocket-nvs-before.binfreepocket-nvs-after-solax-admin.bin
- The operation uses the current Web Serial/esptool-js connection and calls
readFlash(offset, size, progressCallback). - The downloaded artifact is produced locally in the browser as an
application/octet-stream.binfile. - The function is disabled until an ESP32 is connected and is disabled during flashing, erasing, and serial monitor use.
- Baudrate default remains
115200. - No firmware, LittleFS, or NVS image generation was added or modified.
Deployment validation:
- Backend service active after deployment.
https://iot.aeizoon.com/flasherserves JS buildphase2-20260712-2.- Backend logs after restart show Flyway validation success and no runtime errors.
Physical validation still pending:
- Read
freepocket-nvs-before.binfrom the real ESP32 immediately after flashing. - Configure the device manually for
Solax/<redacted>and web useradminwith password <redacted>. - Read
freepocket-nvs-after-solax-admin.binfrom the same board. - Upload both dumps to
/opt/esp-platform/repo/diagnostics/nvs/and runscripts/compare-nvs-dumps.sh.
Backups Stage 1 - Read-Only Capture - 2026-07-12
Implemented Stage 1 only:
- Navigation now includes Firmware, Flasher, Diagnostics and Backups.
/backupsexposes five backup sections, with only Stage 1 operations enabled:- Read complete flash;
- Read partitions.
- Stage 2, Stage 3 and Stage 4 actions are visible but disabled pending approval.
/diagnosticsprovides an entry point for current NVS diagnostic workflow.
Implemented read-only behavior:
- Backups page connects to ESP32 through Web Serial and esptool-js.
- It detects chip, revision, MAC, flash ID and flash size when available.
- Complete flash reads use offset
0x0and detected flash size. - Partition table reads use offset
0x8000and size0x1000. - Partition parser interprets ESP-IDF partition entries: name, type, subtype, offset, size and flags.
- Synthetic readable regions are added for bootloader and partition table.
- Downloads are local browser
.binfiles plus metadata JSON with SHA-256 values. - No backend upload, ZIP creation, restore, firmware import, firmware modification, LittleFS modification or NVS modification was implemented.
Static validation:
- Maven package completed successfully.
- Parser logic was checked against the FreePocket partition artifact and detected 6 real partitions:
nvs0x9000/0x5000;otadata0xE000/0x2000;app00x10000/0x140000;app10x150000/0x140000;spiffs0x290000/0x160000;coredump0x3F0000/0x10000.
Physical validation still pending:
- Detect flash size of 4 MB from a real ESP32.
- Read full flash and verify exact size
4,194,304bytes. - Verify SHA-256 from metadata.
- Read partition table through Web Serial.
- Download at least one selected partition.
- Confirm the ESP32 is unchanged after read-only operations.
Backups Stage 1 UX Review and Stage 2 Package Base - 2026-07-12
Implemented and deployed:
- UI terminology changed to
Create complete device backup,Analyze flash layoutandDownload selected regions. - Partition table now includes Description and Classification.
- Badges classify regions as Reusable, Sensitive, Device state or Diagnostic.
- Safe defaults exclude NVS, OTA data, coredump and NVS keys.
- Sensitive/manual selections show a warning.
Cancel operationis available during reads and uploads.- Destination selector added: local download or save in ESP Platform.
- Backend backup model added:
device_backup,backup_artifact,backup_region,backup_capture_session,backup_audit_log. - Server backups stored in
/srv/esp-platform/backups. - Backup ZIP package creation implemented for saved server backups.
- Restore and import remain disabled.
Backend validation completed:
POST /api/v1/backups/capture-sessions
POST /api/v1/backups/capture-sessions/{id}/chunks
POST /api/v1/backups/capture-sessions/{id}/verify
POST /api/v1/backups/capture-sessions/{id}/complete
POST /api/v1/backups/{id}/package
GET /api/v1/backups/{id}/package/download
DELETE /api/v1/backups/{id}
Results:
- Flyway migration V10 applied successfully.
- Backend service active through systemd.
/backupsserved correctly through backend./api/v1/backupsserved correctly through Nginx on port 88.- Temporary 4-byte validation capture saved, packaged and deleted.
- ZIP content validated with
unzip -l:manifest.jsonfull-flash.binchecksums.sha256
- Partial capture discard validated: no temp file and no backup record remained.
Physical validation pending with ESP32 connected through browser:
- Create a complete capture and save it in ESP Platform.
- Cancel a capture and verify no partial file remains.
- Download a complete local capture.
- Verify size and SHA-256.
- Select NVS manually and verify warning.
- Save and discard a real temporary session.
- Create ZIP from real backup and validate manifest/checksums.
- Confirm the ESP32 is not modified by read operations.
Backups Physical Validation Attempt - 2026-07-14
Route used:
https://iot.aeizoon.com/backups
Browser build shown by the page:
stage1-stage2-20260712-2
Server-side real backup already present
A real ESP32 backup exists in ESP Platform and was validated from the VM:
- Backup id:
2 - Name:
ESP32 2CBCBB75F274 backup 2026-07-13 - Chip:
ESP32-D0WD (revision 1) - Chip revision:
1 - MAC:
2CBCBB75F274 - Flash ID:
16405e - Flash size:
4,194,304bytes - Stored file:
/srv/esp-platform/backups/device-backup-c403ea4c-51eb-4e65-9989-8f48e8df23bc/full-flash.bin - Stored size:
4,194,304bytes - SHA-256:
ca965603a5c02668e63721b34c05fba0dc2494e3050edcb36b1c621bcafe785a
Checksum command result:
ca965603a5c02668e63721b34c05fba0dc2494e3050edcb36b1c621bcafe785a full-flash.bin
Real ZIP package validation
Generated package:
/srv/esp-platform/backups/device-backup-c403ea4c-51eb-4e65-9989-8f48e8df23bc/esp-platform-backup-2.zip
ZIP artifact:
- Size:
773,498bytes - SHA-256:
a8f67972d6d8fa39cecb2a6032e4138de9942b0e17ec590f8ab13eead4cb4006e29767b0
ZIP content:
manifest.json
full-flash.bin
checksums.sha256
unzip -t result:
No errors detected in compressed data
checksums.sha256 content:
ca965603a5c02668e63721b34c05fba0dc2494e3050edcb36b1c621bcafe785a full-flash.bin
74072f72c15855ecefd15e59497a09ac1c70d58796bed71b54b5141f31d16cf7 manifest.json
Session and partial-file state
After validation:
backup_capture_session:0open sessions./srv/esp-platform/backups/tmp: no partial capture files observed.
Browser/Web Serial attempt
Chrome was opened at https://iot.aeizoon.com/backups.
First connect attempt:
No serial port was selected.
Second connect attempt:
Serial port WebSerial VendorID 0x403 ProductID 0x6001
ERROR: Could not connect to ESP32. Failed to execute open on SerialPort: Failed to open serial port.
Observed possible cause:
- Chrome showed several existing ESP Platform tabs, including
/flasher,/diagnosticsand another/backupstab from previous work. - The automation could not close those user tabs because Chrome denied that operation.
- The USB serial adapter was detected, but Chrome could not open it. This usually means another tab/application owns the port, the serial monitor is still active, or the USB adapter needs to be unplugged/replugged.
Pending physical checks after freeing the serial port:
- Cancellation during a real read.
- Local full dump download from browser.
- Partition read and selected-region download.
- Confirmation that the ESP32 continues booting normally after read-only operations.
Technical notes reviewed
Chunk size:
- Browser read/upload chunk size is
0x10000bytes, i.e.65,536bytes.
Connection loss behavior:
- If Web Serial or esptool-js throws during a read/upload, the UI reports a readable error.
- For server captures, the current session is discarded in the error path when the session id is still active in the browser flow.
- Existing completed backups are not modified.
Retries:
- There is no automatic retry for failed flash read chunks or failed uploads.
- This is intentional for now because re-reading flash over Web Serial after an interruption may leave the port state ambiguous. Manual retry is required.
Duplicate detection:
- Backups currently record SHA-256 but do not prevent duplicate
device_backuprows with the same full-flash checksum. - Firmware artifacts have SHA-256 reuse logic; backup artifacts do not yet deduplicate storage.
Session timeout:
- Capture sessions are created with
expires_at = created_at + 6 hours. - The timeout is recorded in PostgreSQL.
Automatic cleanup:
- Explicit discard removes temporary files.
- Successful complete moves the temp file into the backup directory and removes the temporary session directory.
- No scheduled automatic cleanup job for abandoned expired sessions exists yet.
- Operational cleanup should be added before relying on long unattended captures.
Backups Final Physical Validation Closure - 2026-07-14
Route used:
https://iot.aeizoon.com/backups
https://iot.aeizoon.com/flasher
Device:
- Chip:
ESP32-D0WD (revision 1) - Revision:
1 - MAC:
2c:bc:bb:75:f2:74 - Flash ID:
16405e - Flash size:
4MB/4,194,304bytes - Browser baudrate:
115200
Complete local dump
Local dump downloaded successfully from the browser:
/Users/juan/Downloads/ESP32_2CBCBB75F274_full_20260713T222532Z.bin
Result:
- Size:
4,194,304bytes - SHA-256:
ed346457ca6b4be74719292c4761f7cdc39f7f3abdca6356e261d922c421e350
Expected previous server backup SHA-256:
ca965603a5c02668e63721b34c05fba0dc2494e3050edcb36b1c621bcafe785a
The full-image SHA-256 does not match the previous server backup. Region comparison shows the difference is limited to NVS:
| Region | Result | | --- | --- | | bootloader | same | | partition-table | same | | nvs | different | | otadata | same | | app0 | same | | app1 | same | | spiffs | same | | coredump | same |
Conclusion: firmware/application regions are unchanged. The complete dump differs because NVS changed between captures, which is expected for device state/configuration.
Partition download
Downloaded real partition:
/Users/juan/Downloads/ESP32_2CBCBB75F274_app0_0x10000_20260713T223331Z.bin
Result:
- Partition:
app0 - Offset:
0x10000 - Size:
1,310,720bytes /0x140000 - SHA-256:
d79d4b3db73b490429122d64e076dedecf3c3a198497374ce3a8fa4b575fd948 - Matches server backup app0 SHA-256: yes
Reset and boot confirmation
Reset method:
- Opened
/flasher. - Connected to the same ESP32 at
115200. - Started Serial Monitor.
- Pressed
Hardware Reset.
Observed serial boot output:
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
mode:DIO, clock div:1
entry 0x400805f0
[general] starting program (boot #0), version 0.3.5g (esp32)
[files] reading config file
[io] starting taskserial
[webif] starting webserver
[wireless] enabling AP: ssid:fp_75f274, password:****
[ethernet] eth connected
[ethernet] eth disconnected
Conclusion:
- ESP32 still boots after read-only backup operations.
- Application starts as before.
- Webserver starts.
- AP starts.
- Ethernet behavior remains the same observed device behavior and was not changed by Backups.
Final state
Server cleanup/state after validation:
backup_capture_session:0rows./srv/esp-platform/backups/tmp: no partial files.- Persisted backup count:
1real backup.
Validated requirements:
- Complete 4 MB server capture: passed.
- Server SHA-256 and exact size: passed.
- Real ZIP creation/download/content validation: passed.
- ZIP content:
manifest.json,full-flash.bin,checksums.sha256: passed. - Real cancellation during read: passed.
- No partial files after cancellation: passed.
- No orphan sessions: passed.
- Local full dump download: passed, with expected NVS-only SHA drift from previous capture.
- Real partition download: passed with
app0. - ESP32 unchanged and booting: passed.
Decision:
- Backups Stage 1 is closed and approved.
- Backups Stage 2 package creation is closed and approved.
- Restore backup package remains not implemented.
- Import firmware from connected ESP32 remains not implemented.
Backup Progress and Firmware Fingerprint - 2026-07-14
Implemented:
- Total progress bar for full-operation progress.
- Current-step progress bar for active block, selected region, upload chunk or flashing segment.
- Visual states: Waiting, Reading, Uploading, Processing, Completed, Cancelled, Failed.
- Backups UI no longer uses a chunk-local bar as the only progress indicator.
- Flasher UI uses the same dual-progress model for read flash region and multi-segment writes.
- Device Backup SHA-256 remains the full flash hash.
- Firmware Fingerprint SHA-256 excludes NVS, OTA data, coredump and diagnostic/device-specific regions.
- Firmware Fingerprint includes bootloader, partition table, app partitions and reusable filesystem regions.
- Saved backup rows show both hashes and repository match count.
Physical validation result: approved.
Date: 2026-07-14.
Environment:
- URL:
https://iot.aeizoon.com/backups - Build:
stage1-stage2-20260714-1 - Device: ESP32-D0WD revision 1
- MAC:
2c:bc:bb:75:f2:74 - Flash size:
4,194,304bytes / 4 MB - Baudrate:
115200
Validated results:
- Global progress remained continuous and monotonic during a complete 4 MB read.
- Partial progress reset correctly by current block/region.
- Complete local dump downloaded successfully.
- Complete local dump size:
4,194,304bytes. - Complete local dump SHA-256:
a9ea663ba7afc32f90e73bf25d4e535aeeb182e9285ec5e7c974e4cc67d58600. - Existing server backup SHA-256 remains:
ca965603a5c02668e63721b34c05fba0dc2494e3050edcb36b1c621bcafe785a. - SHA drift between complete dumps is accepted because mutable NVS/device state can change between captures.
- Firmware Fingerprint SHA-256 is stored separately from Device Backup SHA-256.
- Current Firmware Fingerprint SHA-256:
4ee0ccd14c1d85ed814cfe8067e6834bec7081a1bf5005768a087cd2e8e6283f. - Firmware Fingerprint size:
4,096,000bytes. - Repository match count:
1. - Real
app0partition downloaded successfully. app0size:1,310,720bytes /0x140000.app0SHA-256:d79d4b3db73b490429122d64e076dedecf3c3a198497374ce3a8fa4b575fd948.- Metadata JSON for local single-file captures now exposes top-level
sha256andsizeBytes, while preservingfiles[]. - ESP32 reset was validated manually after read operations.
- Firmware boot was validated manually after read operations.
- Read-only complete dump and partition download did not alter the device.
Decision:
- Progress global continuo: approved.
- Progress parcial por bloque/region: approved.
- Complete 4 MB local download: approved.
app0partition download: approved.- Metadata JSON correction: approved.
- Firmware Fingerprint separated from full dump SHA: approved.
- ESP32 functional integrity after read-only operations: approved.
- Restore backup package remains not implemented.
- Import firmware from connected ESP32 remains not implemented.
Restore Backup Package - 2026-07-14
Status: implemented, software validated and physically validated on ESP32 test device.
Implemented:
- Restore from saved ESP Platform backup.
- Restore from uploaded
esp-platform-backup.zip. - Backend package validation before restore options are shown.
- Complete restore from
full-flash.binat0x0. - Selective restore by region.
- Safe defaults excluding NVS, OTA data, coredump and sensitive/diagnostic/device-state regions.
- Explicit
RESTOREconfirmation. - Optional safety backup before writing.
- Optional post-write verification by reading back written bytes.
- Best-effort Secure Boot and Flash Encryption detection warnings.
- Restore audit through
backup_restore_sessionandbackup_audit_log.
Software validation completed:
- Saved backup
#2validates as restorable. - Restore validation returns ESP32 family, 4 MB flash size and 8 regions.
- Safe defaults are
bootloader,partition-table,app0,app1,spiffs. - Backend restore-session download returns
4,194,304bytes. - Backend restore-session download SHA-256 is
ca965603a5c02668e63721b34c05fba0dc2494e3050edcb36b1c621bcafe785a. - Real
esp-platform-backup-2.zipupload validates successfully. - Corrupted checksum ZIP is rejected with HTTP 400 and message
Restore package validation failed: Checksum mismatch for full-flash.bin. - Restore UI loads without JavaScript console errors.
- Restore UI renders package summary, compatibility report and region table.
Physical validation completed on 2026-07-14:
-
Complete restore from saved backup
#2: passed.- Target: ESP32-D0WD revision 1, MAC
2c:bc:bb:75:f2:74, Flash ID16405e, detected flash size4,194,304bytes / 4 MB. - Safety backup was enabled and downloaded before writing.
- Safety backup file:
ESP32_2CBCBB75F274_safety_before_restore_20260714T000935Z.bin. - Safety backup size:
4,194,304bytes. - Safety backup SHA-256:
cd3517473707d59c3d915b52a3e16213cadce80d9ffb2b4371958fb7acb51a08. - Restore wrote
4,194,304bytes from offset0x0. - Restore source SHA-256:
ca965603a5c02668e63721b34c05fba0dc2494e3050edcb36b1c621bcafe785a. - Post-write verification read back
full-flash.binand matched SHA-256ca965603a5c02668e63721b34c05fba0dc2494e3050edcb36b1c621bcafe785a. - RTS hard reset was requested after restore.
- Target: ESP32-D0WD revision 1, MAC
-
Selective
app0restore: passed.- Only
app0was selected. - NVS, OTA data and filesystem were not selected.
app0write offset:0x10000.app0size:1,310,720bytes.app0SHA-256 verified after write:d79d4b3db73b490429122d64e076dedecf3c3a198497374ce3a8fa4b575fd948.- NVS before SHA-256:
83db022f60712229ae24ca0412897611d656ea5c66102d3f87d5eb3014619172. - NVS after SHA-256:
83db022f60712229ae24ca0412897611d656ea5c66102d3f87d5eb3014619172. - Result: NVS remained unchanged.
- Only
-
Cancellation during write: passed.
- A complete restore was started with erase enabled.
- Cancel was requested during the writing phase after erase and after writing had begun.
- UI result:
Restore cancelled; device state may be incomplete. - No success state was shown for the cancelled operation.
- Serial port was released and the device could be reconnected.
- Audit event recorded:
RESTORE_CANCELLEDat2026-07-14 00:46:44 UTC. - Device was recovered afterwards using a complete restore with verification.
-
Incompatibility blocking: passed.
- Controlled package with valid checksums but invalid region metadata was rejected before writing.
- Backend response:
Restore package validation failed: Region bootloader exceeds flash size.; Region bootloader overlaps coredump. - No restore execution session was allowed for that package.
-
Altered checksum ZIP: passed at restore validation boundary.
- Controlled ZIP with altered
checksums.sha256was rejected before writing. - Backend response:
Restore package validation failed: Checksum mismatch for full-flash.bin. - The package does not reach executable restore state.
- Browser automation could not attach the ZIP through Chrome native file chooser because Chrome returned
Not allowed; the same uploaded-package validation path was verified through the backend endpoint and had previously been validated with a good ZIP from the UI.
- Controlled ZIP with altered
Operational observations:
Execute restoreis enabled only when an ESP32 is connected, a restore package is validated, compatibility is notincompatible, and the confirmation field contains exactlyRESTORE.- Selecting all partitions does not disable execution by itself; selecting NVS, OTA data or coredump triggers explicit warnings because those regions are device-specific or diagnostic.
- Historical restore events are tracked in
backup_audit_log; repeated executions from the same validated package may update the samebackup_restore_sessionrow.
Decision:
- Restore backup package is physically validated and approved for MVP Flasher/Backups closure.
- Compare with repository remains postponed.
- Import firmware from connected ESP32 remains postponed.
- OTA, Provisioning, Fleet Management and Edge OS remain out of scope for this phase.
ESP Platform Phase 1E Validation
Required checks:
- Backend is active through systemd.
- Web is available at "http://192.168.1.62:88/".
- ".bin" upload works.
- Firmware binary is stored under "/srv/esp-platform/firmware".
- Metadata is stored in PostgreSQL table "firmware".
- Size and SHA-256 are calculated automatically.
- Firmware list displays the uploaded firmware.
- Download endpoint returns the stored ".bin".
API endpoints:
GET /api/v1/firmware
GET /api/v1/firmware/{id}
POST /api/v1/firmware
GET /api/v1/firmware/{id}/download
Phase 1E Validation Run - 2026-07-06
Validation was executed directly inside VM "esp-platform".
Result: passed.
Observed test values:
- Uploaded firmware id: "1"
- Uploaded filename: "validation.bin"
- Metadata name: "Validation Firmware"
- Application: "AZ-TEST"
- Hardware: "ESP32 DevKit"
- Stored filesystem path: "/srv/esp-platform/firmware/038cfdcf-3ff2-45c5-ba5f-d7ec5515593c.bin"
- Size: "33" bytes
- SHA-256: "82d3c475f7f8e72e521e38e41e60efc6b6375452fbf2ea336e4430daea54c4f8"
Checks passed:
- "esp-platform-backend" active through systemd.
- Web accessible through Nginx at "http://192.168.1.62:88/".
- API accessible through Nginx at "http://192.168.1.62:88/api/v1/firmware".
- Upload stored metadata in PostgreSQL.
- Upload stored ".bin" in filesystem.
- Downloaded file matched original size and SHA-256.
Phase 2 Browser Validation - 2026-07-06
Validation performed:
- "http://192.168.1.62:88/flasher" loads successfully.
- "GET /api/v1/firmware" is consumed by the page.
- Direct HTTP access by VM IP is not a secure Web Serial context; the page shows a warning and disables serial connection.
- Temporary local tunnel tested:
ssh -N -L 18888:127.0.0.1:88 codex@192.168.1.62
- "http://localhost:18888/flasher" loads successfully.
- Through localhost, the secure-context warning is not shown and the Connect ESP32 button is enabled.
Pending physical validation:
- Upload real firmware ".bin" from the administration page.
- Select the firmware in "/flasher".
- Connect ESP32 by USB in Chrome/Edge.
- Grant serial-port permission.
- Flash firmware and confirm ESP32 boots.
Phase 2 HTTPS Pre-Validation - 2026-07-06
NPM HTTPS route configured by project owner.
Checked URL:
https://iot.aeizoon.com/flasher
Result:
- Page loads correctly through HTTPS.
- Insecure-context warning is not displayed.
- Connect ESP32 button is enabled by the page.
- Firmware catalog loads successfully.
- Catalog currently contains no firmware after removal of the Phase 1E validation test firmware.
Physical validation remains pending with a real firmware ".bin" and ESP32 connected to the Mac.
Phase 2 Physical Validation Attempt - ESP32 Ethernet/WiFi AliExpress - 2026-07-06
Hardware and connection:
- Board: ESP32 Ethernet/WiFi AliExpress board.
- USB connection: external USB-TTL adapter.
- Browser route: "https://iot.aeizoon.com/flasher".
Observed successful steps:
- Web Serial works through HTTPS.
- Flasher connects to the ESP32.
- Chip detected: "ESP32-D0WD rev 1".
- MAC address detected.
- Stub upload works.
- Flash ID is read.
- Firmware download from backend works.
- Downloaded firmware size: "1,274,512" bytes.
Observed baudrate behavior:
- "115200" is stable.
- "921600" fails after baudrate change with errors similar to:
- "Unable to verify flash chip connection"
- "Serial data stream stopped"
Decision applied:
- Default baudrate changed to "115200".
- Higher baudrates remain available under the advanced speed group.
Observed flashing failure before correction:
File 1 doesn't fit in the available flash
The error occurred with both:
- flash address "0x0"
- flash address "0x10000"
Review result:
- "esptool-js" expects "fileArray: [{ data: Uint8Array, address: number }]"; the implementation uses "Uint8Array" from "response.arrayBuffer()".
- The library performs a fit check when "flashSize !== "keep"".
- Passing "flashSize: "detect"" directly to "writeFlash()" is unsafe because "flashSizeBytes("detect")" does not resolve to the detected byte size for the fit check.
- The Flasher now calls "detectFlashSize()" after connecting and sends the resolved size string, such as "4MB", to "writeFlash()".
- If auto-detection is unavailable, the Flasher sends "keep" to avoid the false fit check.
Additional logging added before flashing:
- detected flash size;
- configured flash size sent to "esptool-js";
- firmware byte length;
- binary data type and "Uint8Array" check;
- flash start address;
- calculated end address.
Second physical validation result:
- The same "File 1 doesn't fit in the available flash" error was observed again.
- The browser output did not include the newly added diagnostic lines, which confirmed that the HTTPS route was still serving a cached older "flasher.js".
Correction applied after second attempt:
- Flasher assets now use cache-busting version "phase2-20260706-3".
- The Flasher logs its build at page load: "Flasher build: phase2-20260706-3".
- Internal Spring/Nginx delivery for static assets now uses no-cache headers.
- NPM still adds external cache headers, so every Flasher JS change must also bump the asset version query string.
- Default Flash size changed to "Keep firmware setting" to bypass the "esptool-js" pre-write fit check.
- "Auto-detected size check" remains available as a diagnostic option, not the default.
Current status:
- Ready for another physical validation attempt at "115200" baud.
- Confirm the Flasher console starts with "Flasher build: phase2-20260706-3".
- Use Flash size "Keep firmware setting".
- If it still fails, capture the Flasher console log and browser console output.
Phase 2 Physical Flash Success - 2026-07-06
Physical flashing succeeded through "https://iot.aeizoon.com/flasher" using Flasher build "phase2-20260706-3".
Observed result:
- Firmware "freepocket 0.3.5j" downloaded from backend.
- Firmware size: "1,274,512" bytes.
- ESP32 detected: "ESP32-D0WD revision 1".
- Flash size detected: "4MB".
- Flash address: "0x0".
- Configured flash size sent to "esptool-js": "keep".
- Written bytes: "1,274,512".
- Compressed bytes: "804,935".
- Write duration: "72.507" seconds at "115200".
- "esptool-js" requested hard reset via RTS after flashing.
Follow-up UI adjustment:
- Flasher build "phase2-20260706-4" adds an "Erase ESP32" button.
- The erase button is enabled only after a serial connection is established.
- It asks for confirmation before calling "eraseFlash()".
- The button is visually separated from "Disconnect" with a "4cm" left margin on desktop layouts.
Phase 2 Serial Monitor and Hardware Reset - 2026-07-06
Flasher build "phase2-20260706-6" adds post-flash serial diagnostics:
- "Start Serial Monitor" opens the selected Web Serial port at the currently selected baudrate.
- If the esptool flasher connection is still open, it is closed first and the same port is reopened for serial reading.
- Serial bytes are written directly into the Flasher console so the ESP32 boot log can be inspected.
- "Stop Monitor" closes the serial monitor and releases the port.
- "Hardware Reset" sends reset through esptool when the flasher connection is active.
- When the serial monitor is active, "Hardware Reset" pulses RTS through Web Serial so boot output can be observed immediately.
Operational test path:
1. Flash firmware successfully.
2. Click "Start Serial Monitor".
3. Click "Hardware Reset" or press the board reset button.
4. Confirm boot output appears in the console.
Phase 2 FreePocket Firmware Layout Finding - 2026-07-07
A comparison with "https://freepocket.chrisoft.io/" showed that the external FreePocket installer is powered by ESP Web Tools and uses a manifest with multiple flash parts, not a single binary written at "0x0".
Observed external manifest layout:
- bootloader: "0x1000"
- partitions: "0x8000"
- boot_app0: "0xE000"
- application: "0x10000"
- LittleFS: "0x290000"
The firmware uploaded to ESP Platform as "freepocket 0.3.5j" has size "1,274,512" bytes and starts with an ESP image header at offset "0x0":
- magic: "0xE9"
- segments: "6"
- flash mode byte: "0x02"
- flash size/frequency byte: "0x2f"
- entry point: "0x400827ac"
This indicates that the uploaded file is likely an application image intended to be written at "0x10000", not a complete flash image intended for "0x0".
Important consequence:
- Flashing this app image at "0x0" overwrites the bootloader area and can prevent the ESP32 from booting correctly.
- Flashing it at "0x10000" only works if the target ESP32 already has compatible bootloader, partition table, boot_app0 and filesystem/data partitions.
- A clean device or a previously erased device requires a multi-part firmware package/manifest, not only the application ".bin".
Next required platform correction:
- ESP Platform should distinguish between single full-image firmware and multi-part ESP32 firmware packages.
- For ESP-IDF/Arduino app-only binaries, the flasher should require or infer address "0x10000" instead of defaulting to "0x0".
- For FreePocket-style installs, ESP Platform needs manifest/multi-part upload support before it can reproduce the external installer behavior exactly.
Phase 2 External FreePocket Files Retrieved - 2026-07-07
The public files referenced by "https://freepocket.chrisoft.io/manifest_latest.json" were downloaded for technical comparison and validation reference into:
/opt/esp-platform/repo/firmware/reference/freepocket
Downloaded layout:
| Offset | File | Size | SHA-256 |
| --- | --- | ---: | --- |
| "0x1000" | "pincho_solax.bootloader.bin" | "18,992" | "644de0067047e22380034b8989c39e5d2882f7538c698788866ca5130427322e" |
| "0x8000" | "pincho_solax.partitions.bin" | "3,072" | "148b959cbff1c38aa8e1d5c0ba9d612c54997b945e56a63f41223eef650653a1" |
| "0xE000" | "boot_app0.bin" | "8,192" | "f94c5d786a7a8fab06ac5d10e33bf37711a6697636dc037559ea19cc410a17f0" |
| "0x10000" | "pincho_solax.bin" | "1,072,320" | "026f043621c2338c3172751f54e2c9ca9bb9f1741b78c0b8799cf78f6843f9ac" |
| "0x290000" | "pincho_solax.littlefs.bin" | "1,441,792" | "0edc7b60e8162ab7177992783648dd8d5857dab463c2f0a60715aa40af57a0e1" |
Important version note:
- The public manifest reports version "0.3.5f".
- The firmware currently uploaded to ESP Platform is labeled "0.3.5j" and has size "1,274,512" bytes.
- These should not be treated as the same firmware package.
Use of the retrieved files:
- They are suitable as a reference to reproduce the external installer behavior in a validation environment.
- ESP Platform still needs explicit multi-part package support before these files are represented cleanly in the database and web UI.
Phase 2 Multi-Part Firmware Support - 2026-07-07
ESP Platform now supports a minimal multi-part firmware package model for Phase 2 validation.
Backend changes:
- Added "firmware.package_type" with values "SIMPLE" and "MULTIPART".
- Added "firmware_part" table with per-part metadata:
- part order;
- display name;
- original filename;
- filesystem path;
- flash offset;
- size;
- SHA-256 checksum.
- Added API download endpoint for parts:
- "GET /api/v1/firmware/{id}/parts/{partId}/download"
- "GET /api/v1/firmware" now returns "packageType", "partCount" and "parts[]".
Seeded validation package:
- Firmware id: "3"
- Name: "FreePocket full install"
- Version: "0.3.5f"
- Type: "MULTIPART"
- Total size: "2,544,368" bytes
- Source reference: public FreePocket ESP Web Tools manifest.
Flasher changes:
- Flasher build: "phase2-20260706-7".
- Multi-part firmware cards are displayed with a distinct blue "Multi-part" badge.
- Simple ".bin" firmware remains supported as "Single .bin".
- For "MULTIPART", the Flasher downloads all parts and sends a multi-entry "fileArray" to "esptool-js".
- For "MULTIPART", the Flasher uses:
- "eraseAll: true"
- "flashMode: keep"
- "flashFreq: keep"
- configured flash size from the UI, default "keep".
Validation performed:
- Backend service active after Flyway V3 migration.
- API lists both the existing simple firmware and the new multi-part FreePocket package.
- All five part download endpoints return HTTP 200 with expected sizes.
Physical validation pending:
- Select "FreePocket full install 0.3.5f" from "/flasher".
- Confirm the card is marked "Multi-part".
- Connect ESP32 at "115200".
- Flash selected firmware.
- Start Serial Monitor.
- Send "Hardware Reset" or press reset manually.
- Confirm complete boot log and device operation.
Phase 2 FreePocket AP Password and Version Search - 2026-07-07
The FreePocket documentation page states that, after first boot, the ESP32 creates an AP named "fp_xxxxxx" and the default password is "<redacted>". The same page states the default local IP is "192.168.4.1" with web user "admin" and password "<redacted>".
Binary inspection confirms related defaults inside the downloaded public package:
- LittleFS contains "WEB_USERNAME": "admin".
- LittleFS contains "WEB_PASSWORD": "<redacted>".
- Application strings contain AP creation format "fp_%02x%02x%02x" and log text "enabling AP: ssid:%s, password:****".
Public installer version search:
- "https://freepocket.chrisoft.io/" references only "manifest_latest.json".
- The public manifest reports version "0.3.5f".
- Tested common manifest/version names such as "manifest_0.3.5g.json", "manifest_0.3.5j.json", "manifest_stable.json", "manifest_beta.json": only "manifest_latest.json" is exposed.
Current conclusion:
- Public direct ESP Web Tools installer exposes only FreePocket "0.3.5f".
- The "0.3.5j" binary uploaded manually to ESP Platform appears to come from another source or unpublished package and should not be assumed to match the public multi-part package.
Additional version finding:
- "manifest_latest.json" reports "0.3.5f".
- The downloaded "pincho_solax.bin" application image contains internal string "0.3.5g".
- ESP Platform display metadata was updated to "manifest 0.3.5f / app 0.3.5g" to avoid confusing this package with the manually uploaded app-only "0.3.5j" firmware.
Phase 2 Patched AP Password Package - 2026-07-07
A test firmware package was created by binary patching the public FreePocket multi-part package from default string "<redacted>" to "<redacted>".
Reason:
- The ESP32 AP appears as "fp_xxxxxx", but the default password did not work reliably in physical testing.
- The string "<redacted>" appears once in the application image and once in the LittleFS image.
- The replacement "<redacted>" has the same length as "<redacted>", so the patch does not shift binary offsets.
Created package:
- Firmware id: "4"
- Name: "FreePocket full install AP <redacted>"
- Version label: "manifest 0.3.5f / app 0.3.5g / AP <redacted>"
- Type: "MULTIPART"
- Expected AP/web password for validation: "<redacted>"
Changed checksums:
- application "pincho_solax.bin": "4454f8d1ecb422741ff7fa535f664bb063286d687c98f1beeda98d6b2655a16b"
- littlefs "pincho_solax.littlefs.bin": "cabb88ab8b621e56eece8f74173ec4014cebb3e3965c8232ccc96fcc026ddbd0"
Unchanged parts:
- bootloader
- partition table
- boot_app0
Validation performed:
- The patched package appears in "/api/v1/firmware".
- All five part download endpoints return HTTP 200.
- The patched application and LittleFS files contain "<redacted>" and no longer expose "<redacted>" through simple string inspection.
Risk note:
- This is a binary patch for validation only. If LittleFS metadata or firmware logic validates file contents internally, the device may ignore the patched value or behave differently. A source-level firmware build would be cleaner once source/build inputs are available.
Phase 2 Patched AP Password Package Repair - 2026-07-07
Physical test result:
- Flashing "FreePocket full install AP <redacted>" caused repeated "SW_RESET" before FreePocket printed its normal "[general] starting program" log line.
- The bootloader reached "entry 0x400805f0", then the ESP32 reset repeatedly.
Cause found:
- Binary patching the application image changed bytes inside "pincho_solax.bin".
- The ESP32 application image footer still contained the original checksum and validation SHA-256 digest.
- "esptool image_info" reported:
- checksum invalid;
- validation hash invalid.
Repair applied:
- Recomputed the ESP image checksum byte from "0x82" to "0x87".
- Recomputed the appended validation SHA-256 digest.
- Replaced the application part for firmware id "4" with the repaired image.
Repaired application checksum:
- File SHA-256: "fff0335928c92bc3150b5e527d11543b1db28c2dca22bde69ce152f33e105e22"
- ESP image checksum: valid
- ESP validation hash: valid
Next physical validation:
- Flash "FreePocket full install AP <redacted>" again from "/flasher".
- Confirm the normal FreePocket boot log appears.
- Try AP password "<redacted>".
Admin Firmware UI Maintenance - 2026-07-07
Changes validated:
- "/admin/firmware" now exposes a delete action for stored firmware records and their filesystem files.
- "/admin/firmware" now exposes a multi-part ESP32 package upload form with explicit offsets for bootloader, partitions, boot_app0, application, and filesystem/LittleFS images.
- Multi-part firmware entries are visually distinguished with blue badges.
- The shared web palette was adjusted away from green success/console accents toward blue.
- "/flasher" and "/admin/firmware" were checked for horizontal page overflow after the layout/CSS changes.
Functional validation:
- Temporary single ".bin" upload returned HTTP 302, appeared through "/api/v1/firmware", and was deleted through "/admin/firmware/{id}/delete".
- Temporary multi-part upload with five ".bin" files returned HTTP 302, appeared through "/api/v1/firmware" as "MULTIPART" with "partCount=5", and was deleted through "/admin/firmware/{id}/delete".
- After deletion, no temporary validation firmware remained in "/api/v1/firmware".
- Backend service "esp-platform-backend" was rebuilt, redeployed, restarted, and remained active.
Browser validation through "https://iot.aeizoon.com":
- "/admin/firmware" loaded CSS "app.css?v=phase2-20260707-1".
- "/admin/firmware" showed the multi-part upload form and delete buttons.
- "/admin/firmware" reported no horizontal overflow at 1280 px viewport width.
- "/flasher" loaded CSS "app.css?v=phase2-20260707-1".
- "/flasher" showed "Erase ESP32", "Start Serial Monitor", "Stop Monitor", and "Hardware Reset" controls.
- "/flasher" reported no horizontal overflow at 1280 px viewport width.
Firmware Lock/Edit Administration - 2026-07-07
Changes validated:
- Firmware records now have an "enabled" state stored in PostgreSQL.
- Existing firmware records were migrated as enabled by default through Flyway "V4__firmware_enabled.sql".
- "/admin/firmware" shows firmware status as "Enabled" or "Locked".
- Clicking a firmware name in "Available firmware" opens "/admin/firmware/{id}/edit".
- The edit screen allows updating name, version, application, compatible hardware, description, and enabled state without replacing stored binaries.
- "/admin/firmware" provides quick "Lock" / "Unlock" actions.
- Locked firmware is hidden from "GET /api/v1/firmware", so it is not offered by the Flasher Web catalog.
- Locked firmware download endpoints return an error instead of serving the binary/parts.
Functional validation:
- Temporary firmware upload returned HTTP 302 and appeared in "/api/v1/firmware" as enabled.
- Lock action returned HTTP 200.
- After locking, the temporary firmware no longer appeared in "/api/v1/firmware".
- Direct download of the locked firmware returned HTTP 400.
- Unlock action returned HTTP 200.
- Edit action returned HTTP 200 and changed metadata visible from "/api/v1/firmware/{id}".
- Delete action returned HTTP 200 and removed the temporary firmware.
Browser validation through "https://iot.aeizoon.com":
- "/admin/firmware" loaded CSS "app.css?v=phase2-20260707-2".
- Firmware names rendered as edit links.
- Lock buttons rendered for enabled firmware.
- "/admin/firmware/{id}/edit" rendered the metadata form and enabled checkbox.
- "/admin/firmware" and "/admin/firmware/{id}/edit" reported no horizontal overflow at 1280 px viewport width.
Firmware Part Download And Replacement - 2026-07-08
Changes validated:
- Multipart firmware edit pages now expose a "Download" action for each current firmware part.
- Multipart firmware edit pages now expose a per-part ".bin" replacement form.
- Admin part downloads use "/admin/firmware/{id}/parts/{partId}/download" and are intended for maintenance/review.
- Public/API part downloads remain protected by firmware enabled state.
- Replacing a part stores the new binary in filesystem, updates original filename, size, SHA-256, and recalculates the parent package size/checksum.
Functional validation:
- Temporary multipart firmware upload returned HTTP 302.
- Admin download of the temporary bootloader part returned the expected original bytes.
- Replacing that bootloader part returned HTTP 200.
- The part filename, size, and SHA-256 changed to match the replacement ".bin".
- The parent multipart package total size changed and package SHA-256 was recalculated.
- Temporary multipart firmware was deleted successfully and did not remain in "/api/v1/firmware".
Browser validation through "https://iot.aeizoon.com":
- "/admin/firmware/4/edit" loaded CSS "app.css?v=phase2-20260708-1".
- The edit page rendered 5 part rows, 5 "Download" links, 5 file inputs, and 5 "Replace" buttons.
- The page reported no horizontal overflow at 1280 px viewport width.
ESP32 Merged Image Assembler - 2026-07-12
Implementation validated:
- Added derived artifact storage for merged ESP32 images linked to the source multipart firmware.
- Added PostgreSQL migration "V5__firmware_derived_artifacts.sql" with merge parameters on "firmware" and derived artifact metadata in "firmware_derived_artifact".
- Installed and exposed official "esptool" as "/usr/local/bin/esptool" on the VM.
- Backend generation uses "esptool --chip <chip> merge-bin --flash-mode <mode> --flash-freq <freq> --flash-size <size> -o <output> <offset> <file> ...".
- Backend validates segment existence, offsets, overlap, flash-size range, stored file sizes, SHA-256 checksums, and supported esptool parameters before merge.
- Merged output is stored under "/srv/esp-platform/firmware/derived/firmware-<id>/".
- Generated artifacts record source firmware, filename, size, SHA-256, generated date, flash address "0x0", parameters, status, error message, and command line.
- Admin firmware edit page now shows merge parameters, "Generar imagen unificada", derived artifacts, download, regeneration by creating a new artifact, and delete-artifact-only actions.
- Flasher Web catalog now exposes generated "MERGED_BIN" artifacts as selectable flashable items, using address "0x0" without manual offsets.
- Existing multipart flashing remains available and unchanged.
Validation results:
- Initial generation with FreePocket forced to filesystem/spiffs offset "0x310000" and flash size "4MB" failed correctly with: "Segment spiffs exceeds configured flash size 4MB."
- The existing FreePocket binaries stored in ESP Platform have a final filesystem image of "1,441,792" bytes. At "0x310000" that image exceeds 4 MB; at the previously validated "0x290000" layout it fits exactly inside 4 MB.
- FreePocket package id "4" was kept on its effective validated layout to preserve existing multipart flashing behavior.
- Generated merged artifact for firmware id "4":
- Artifact id: "2"
- Filename: "firmware-4-merged.bin"
- Size: "4,128,768" bytes
- SHA-256: "3b0eb453795260d52c0ce7eae731f618ced9a2cc98720b6a32e5aab279d2a516"
- Flash address: "0x0"
- Parameters: "ESP32 / dio / 80m / 4MB"
- Download of "/api/v1/firmware/4/artifacts/2/download" returned HTTP 200 and SHA-256 matched PostgreSQL.
- A temporary multipart package was created to validate derived artifact deletion:
- upload returned HTTP 302;
- generation returned HTTP 302 and produced a "GENERATED" artifact;
- deleting only the derived artifact returned HTTP 302 and left the firmware record intact;
- deleting the temporary firmware then removed the source package.
- Service was restarted after Flyway repair/checksum alignment; Flyway validates 5 migrations and backend starts successfully.
Operational note:
- A requested layout of "0x310000 -> spiffs.bin" with "4MB" is only valid when the spiffs/filesystem image size is small enough to end at or before "0x400000". The backend now rejects invalid combinations instead of producing a misleading merged image.
Merged Image Published As Autonomous Firmware - 2026-07-12
Decision update:
- A merged image generated from a multipart package is now published as a normal "SIMPLE" firmware record.
- The generated firmware is operationally autonomous: it can be edited, locked/unlocked, downloaded and selected by the Flasher Web like any single-block ".bin" uploaded manually.
- The origin is preserved only as descriptive context and generation audit metadata; the generated firmware is not behaviorally dependent on the source multipart package.
Validated generated firmware:
- Firmware id: "16"
- Name: "FreePocket full install AP <redacted> - merged image"
- Type: "SIMPLE"
- Original filename: "firmware-4-merged.bin"
- Size: "4,128,768" bytes
- SHA-256: "3b0eb453795260d52c0ce7eae731f618ced9a2cc98720b6a32e5aab279d2a516"
- Download endpoint: "/api/v1/firmware/16/download"
- Source firmware dependency: none ("sourceFirmwareId = null")
Validation:
- "/admin/firmware" shows "FreePocket full install AP <redacted> - merged image" as a normal firmware row.
- "/admin/firmware/16/edit" is available.
- "/api/v1/firmware/16/download" returned HTTP 200.
- Downloaded file size and SHA-256 matched the database metadata.
- Backend logs after deployment showed no new errors or Flyway validation failures.
Artifact Library And Firmware Clone Safety - 2026-07-12
Implementation validated:
- Added independent "artifact" storage library backed by "/srv/esp-platform/artifacts".
- Added "firmware_segment" as the active firmware-to-binary relation table.
- Removed legacy "firmware_part" with migration "V9__drop_legacy_firmware_part_table.sql"; the active model is now "firmware", "firmware_segment", and "artifact".
- Each "firmware_segment" references one "artifact" through a PostgreSQL foreign key.
- Artifacts are identified by SHA-256 and reused when the same binary is uploaded again.
- Existing firmware binaries were consolidated into "/srv/esp-platform/artifacts/<sha-prefix>/<sha>.bin".
- Firmware deletion now removes the firmware record and its segment relations only; it does not delete physical artifacts directly.
- Orphan physical artifact deletion is performed only through the explicit orphan cleanup operation.
- Firmware records can share the same artifact path after migration "V8__allow_firmware_records_to_share_artifacts.sql" removed the legacy "firmware.stored_filename" uniqueness constraint.
Validation script:
- Script: "/opt/esp-platform/repo/scripts/validate-artifact-library.sh"
- Base URL used: "http://127.0.0.1:8080"
Validated flow:
- Created a temporary single-file firmware through "/admin/firmware".
- Verified metadata in "firmware", "firmware_segment", and "artifact".
- Cloned the firmware through "/admin/firmware/{id}/clone".
- Verified clone and original shared the same "artifact_id".
- Deleted the original firmware.
- Verified the clone still downloaded successfully from "/api/v1/firmware/{cloneId}/download" and SHA-256 matched.
- Deleted the clone.
- Verified the artifact became orphaned but still existed physically.
- Ran "/admin/artifacts/cleanup-orphans".
- Verified the orphan artifact row and physical ".bin" were removed.
Result:
- "Artifact library validation passed. Test firmware was cloned, original deleted, clone download verified, clone deleted, orphan artifact cleaned."
- The same validation script was re-run successfully after applying "V9__drop_legacy_firmware_part_table.sql".
FreePocket NVS Diagnostic Preparation - 2026-07-12
Prepared diagnostic workflow after LittleFS preconfiguration failed to change runtime Wi-Fi behavior.
Findings:
- FreePocket NVS partition is "nvs" at offset "0x9000", size "0x5000" / 20480 bytes.
- Partition table source artifact: "/srv/esp-platform/artifacts/14/148b959cbff1c38aa8e1d5c0ba9d612c54997b945e56a63f41223eef650653a1.bin".
- Official ESP-IDF NVS parser installed under "/opt/esp-platform/tools/esp-idf/components/nvs_flash/nvs_partition_tool/nvs_tool.py".
- Repo scripts prepared:
- "/opt/esp-platform/repo/scripts/freepocket-nvs-info.sh"
- "/opt/esp-platform/repo/scripts/analyze-nvs-dump.sh"
- "/opt/esp-platform/repo/scripts/compare-nvs-dumps.sh"
- Diagnostic guide: "/opt/esp-platform/repo/docs/diagnostics/freepocket-nvs-diagnostics.md".
Next required physical data:
- NVS dump immediately after flashing and before manual configuration.
- NVS dump after manual configuration with SSID "Solax", Wi-Fi password "<redacted>", web user "admin", and web password <redacted>.
Flasher Web Read Flash Region - 2026-07-12
Implemented an advanced read-only flash dump function in "/flasher" for NVS diagnostics.
Validated by HTTP/HTTPS delivery:
- "/flasher" now contains advanced section "Read flash region".
- Preset "FreePocket NVS" sets offset "0x9000" and size "0x5000".
- Suggested filenames are available:
- "freepocket-nvs-before.bin"
- "freepocket-nvs-after-solax-admin.bin"
- The operation uses the current Web Serial/esptool-js connection and calls "readFlash(offset, size, progressCallback)".
- The downloaded artifact is produced locally in the browser as an "application/octet-stream" ".bin" file.
- The function is disabled until an ESP32 is connected and is disabled during flashing, erasing, and serial monitor use.
- Baudrate default remains "115200".
- No firmware, LittleFS, or NVS image generation was added or modified.
Deployment validation:
- Backend service active after deployment.
- "https://iot.aeizoon.com/flasher" serves JS build "phase2-20260712-2".
- Backend logs after restart show Flyway validation success and no runtime errors.
Physical validation still pending:
- Read "freepocket-nvs-before.bin" from the real ESP32 immediately after flashing.
- Configure the device manually for "Solax" / "<redacted>" and web user "admin" with password <redacted>.
- Read "freepocket-nvs-after-solax-admin.bin" from the same board.
- Upload both dumps to "/opt/esp-platform/repo/diagnostics/nvs/" and run "scripts/compare-nvs-dumps.sh".
Backups Stage 1 - Read-Only Capture - 2026-07-12
Implemented Stage 1 only:
- Navigation now includes Firmware, Flasher, Diagnostics and Backups.
- "/backups" exposes five backup sections, with only Stage 1 operations enabled:
- Read complete flash;
- Read partitions.
- Stage 2, Stage 3 and Stage 4 actions are visible but disabled pending approval.
- "/diagnostics" provides an entry point for current NVS diagnostic workflow.
Implemented read-only behavior:
- Backups page connects to ESP32 through Web Serial and esptool-js.
- It detects chip, revision, MAC, flash ID and flash size when available.
- Complete flash reads use offset "0x0" and detected flash size.
- Partition table reads use offset "0x8000" and size "0x1000".
- Partition parser interprets ESP-IDF partition entries: name, type, subtype, offset, size and flags.
- Synthetic readable regions are added for bootloader and partition table.
- Downloads are local browser ".bin" files plus metadata JSON with SHA-256 values.
- No backend upload, ZIP creation, restore, firmware import, firmware modification, LittleFS modification or NVS modification was implemented.
Static validation:
- Maven package completed successfully.
- Parser logic was checked against the FreePocket partition artifact and detected 6 real partitions:
- "nvs" "0x9000" / "0x5000";
- "otadata" "0xE000" / "0x2000";
- "app0" "0x10000" / "0x140000";
- "app1" "0x150000" / "0x140000";
- "spiffs" "0x290000" / "0x160000";
- "coredump" "0x3F0000" / "0x10000".
Physical validation still pending:
- Detect flash size of 4 MB from a real ESP32.
- Read full flash and verify exact size "4,194,304" bytes.
- Verify SHA-256 from metadata.
- Read partition table through Web Serial.
- Download at least one selected partition.
- Confirm the ESP32 is unchanged after read-only operations.
Backups Stage 1 UX Review and Stage 2 Package Base - 2026-07-12
Implemented and deployed:
- UI terminology changed to "Create complete device backup", "Analyze flash layout" and "Download selected regions".
- Partition table now includes Description and Classification.
- Badges classify regions as Reusable, Sensitive, Device state or Diagnostic.
- Safe defaults exclude NVS, OTA data, coredump and NVS keys.
- Sensitive/manual selections show a warning.
- "Cancel operation" is available during reads and uploads.
- Destination selector added: local download or save in ESP Platform.
- Backend backup model added: "device_backup", "backup_artifact", "backup_region", "backup_capture_session", "backup_audit_log".
- Server backups stored in "/srv/esp-platform/backups".
- Backup ZIP package creation implemented for saved server backups.
- Restore and import remain disabled.
Backend validation completed:
POST /api/v1/backups/capture-sessions
POST /api/v1/backups/capture-sessions/{id}/chunks
POST /api/v1/backups/capture-sessions/{id}/verify
POST /api/v1/backups/capture-sessions/{id}/complete
POST /api/v1/backups/{id}/package
GET /api/v1/backups/{id}/package/download
DELETE /api/v1/backups/{id}
Results:
- Flyway migration V10 applied successfully.
- Backend service active through systemd.
- "/backups" served correctly through backend.
- "/api/v1/backups" served correctly through Nginx on port 88.
- Temporary 4-byte validation capture saved, packaged and deleted.
- ZIP content validated with "unzip -l":
- "manifest.json"
- "full-flash.bin"
- "checksums.sha256"
- Partial capture discard validated: no temp file and no backup record remained.
Physical validation pending with ESP32 connected through browser:
1. Create a complete capture and save it in ESP Platform.
2. Cancel a capture and verify no partial file remains.
3. Download a complete local capture.
4. Verify size and SHA-256.
5. Select NVS manually and verify warning.
6. Save and discard a real temporary session.
7. Create ZIP from real backup and validate manifest/checksums.
8. Confirm the ESP32 is not modified by read operations.
Backups Physical Validation Attempt - 2026-07-14
Route used:
https://iot.aeizoon.com/backups
Browser build shown by the page:
stage1-stage2-20260712-2
Server-side real backup already present
A real ESP32 backup exists in ESP Platform and was validated from the VM:
- Backup id: "2"
- Name: "ESP32 2CBCBB75F274 backup 2026-07-13"
- Chip: "ESP32-D0WD (revision 1)"
- Chip revision: "1"
- MAC: "2CBCBB75F274"
- Flash ID: "16405e"
- Flash size: "4,194,304" bytes
- Stored file: "/srv/esp-platform/backups/device-backup-c403ea4c-51eb-4e65-9989-8f48e8df23bc/full-flash.bin"
- Stored size: "4,194,304" bytes
- SHA-256: "ca965603a5c02668e63721b34c05fba0dc2494e3050edcb36b1c621bcafe785a"
Checksum command result:
ca965603a5c02668e63721b34c05fba0dc2494e3050edcb36b1c621bcafe785a full-flash.bin
Real ZIP package validation
Generated package:
/srv/esp-platform/backups/device-backup-c403ea4c-51eb-4e65-9989-8f48e8df23bc/esp-platform-backup-2.zip
ZIP artifact:
- Size: "773,498" bytes
- SHA-256: "a8f67972d6d8fa39cecb2a6032e4138de9942b0e17ec590f8ab13eead4cb4006e29767b0"
ZIP content:
manifest.json
full-flash.bin
checksums.sha256
"unzip -t" result:
No errors detected in compressed data
"checksums.sha256" content:
ca965603a5c02668e63721b34c05fba0dc2494e3050edcb36b1c621bcafe785a full-flash.bin
74072f72c15855ecefd15e59497a09ac1c70d58796bed71b54b5141f31d16cf7 manifest.json
Session and partial-file state
After validation:
- "backup_capture_session": "0" open sessions.
- "/srv/esp-platform/backups/tmp": no partial capture files observed.
Browser/Web Serial attempt
Chrome was opened at "https://iot.aeizoon.com/backups".
First connect attempt:
No serial port was selected.
Second connect attempt:
Serial port WebSerial VendorID 0x403 ProductID 0x6001
ERROR: Could not connect to ESP32. Failed to execute open on SerialPort: Failed to open serial port.
Observed possible cause:
- Chrome showed several existing ESP Platform tabs, including "/flasher", "/diagnostics" and another "/backups" tab from previous work.
- The automation could not close those user tabs because Chrome denied that operation.
- The USB serial adapter was detected, but Chrome could not open it. This usually means another tab/application owns the port, the serial monitor is still active, or the USB adapter needs to be unplugged/replugged.
Pending physical checks after freeing the serial port:
1. Cancellation during a real read.
2. Local full dump download from browser.
3. Partition read and selected-region download.
4. Confirmation that the ESP32 continues booting normally after read-only operations.
Technical notes reviewed
Chunk size:
- Browser read/upload chunk size is "0x10000" bytes, i.e. "65,536" bytes.
Connection loss behavior:
- If Web Serial or esptool-js throws during a read/upload, the UI reports a readable error.
- For server captures, the current session is discarded in the error path when the session id is still active in the browser flow.
- Existing completed backups are not modified.
Retries:
- There is no automatic retry for failed flash read chunks or failed uploads.
- This is intentional for now because re-reading flash over Web Serial after an interruption may leave the port state ambiguous. Manual retry is required.
Duplicate detection:
- Backups currently record SHA-256 but do not prevent duplicate "device_backup" rows with the same full-flash checksum.
- Firmware artifacts have SHA-256 reuse logic; backup artifacts do not yet deduplicate storage.
Session timeout:
- Capture sessions are created with "expires_at = created_at + 6 hours".
- The timeout is recorded in PostgreSQL.
Automatic cleanup:
- Explicit discard removes temporary files.
- Successful complete moves the temp file into the backup directory and removes the temporary session directory.
- No scheduled automatic cleanup job for abandoned expired sessions exists yet.
- Operational cleanup should be added before relying on long unattended captures.
Backups Final Physical Validation Closure - 2026-07-14
Route used:
https://iot.aeizoon.com/backups
https://iot.aeizoon.com/flasher
Device:
- Chip: "ESP32-D0WD (revision 1)"
- Revision: "1"
- MAC: "2c:bc:bb:75:f2:74"
- Flash ID: "16405e"
- Flash size: "4MB" / "4,194,304" bytes
- Browser baudrate: "115200"
Complete local dump
Local dump downloaded successfully from the browser:
/Users/juan/Downloads/ESP32_2CBCBB75F274_full_20260713T222532Z.bin
Result:
- Size: "4,194,304" bytes
- SHA-256: "ed346457ca6b4be74719292c4761f7cdc39f7f3abdca6356e261d922c421e350"
Expected previous server backup SHA-256:
ca965603a5c02668e63721b34c05fba0dc2494e3050edcb36b1c621bcafe785a
The full-image SHA-256 does not match the previous server backup. Region comparison shows the difference is limited to NVS:
| Region | Result |
| --- | --- |
| bootloader | same |
| partition-table | same |
| nvs | different |
| otadata | same |
| app0 | same |
| app1 | same |
| spiffs | same |
| coredump | same |
Conclusion: firmware/application regions are unchanged. The complete dump differs because NVS changed between captures, which is expected for device state/configuration.
Partition download
Downloaded real partition:
/Users/juan/Downloads/ESP32_2CBCBB75F274_app0_0x10000_20260713T223331Z.bin
Result:
- Partition: "app0"
- Offset: "0x10000"
- Size: "1,310,720" bytes / "0x140000"
- SHA-256: "d79d4b3db73b490429122d64e076dedecf3c3a198497374ce3a8fa4b575fd948"
- Matches server backup app0 SHA-256: yes
Reset and boot confirmation
Reset method:
- Opened "/flasher".
- Connected to the same ESP32 at "115200".
- Started Serial Monitor.
- Pressed "Hardware Reset".
Observed serial boot output:
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
mode:DIO, clock div:1
entry 0x400805f0
[general] starting program (boot #0), version 0.3.5g (esp32)
[files] reading config file
[io] starting taskserial
[webif] starting webserver
[wireless] enabling AP: ssid:fp_75f274, password:****
[ethernet] eth connected
[ethernet] eth disconnected
Conclusion:
- ESP32 still boots after read-only backup operations.
- Application starts as before.
- Webserver starts.
- AP starts.
- Ethernet behavior remains the same observed device behavior and was not changed by Backups.
Final state
Server cleanup/state after validation:
- "backup_capture_session": "0" rows.
- "/srv/esp-platform/backups/tmp": no partial files.
- Persisted backup count: "1" real backup.
Validated requirements:
1. Complete 4 MB server capture: passed.
2. Server SHA-256 and exact size: passed.
3. Real ZIP creation/download/content validation: passed.
4. ZIP content: "manifest.json", "full-flash.bin", "checksums.sha256": passed.
5. Real cancellation during read: passed.
6. No partial files after cancellation: passed.
7. No orphan sessions: passed.
8. Local full dump download: passed, with expected NVS-only SHA drift from previous capture.
9. Real partition download: passed with "app0".
10. ESP32 unchanged and booting: passed.
Decision:
- Backups Stage 1 is closed and approved.
- Backups Stage 2 package creation is closed and approved.
- Restore backup package remains not implemented.
- Import firmware from connected ESP32 remains not implemented.
Backup Progress and Firmware Fingerprint - 2026-07-14
Implemented:
- Total progress bar for full-operation progress.
- Current-step progress bar for active block, selected region, upload chunk or flashing segment.
- Visual states: Waiting, Reading, Uploading, Processing, Completed, Cancelled, Failed.
- Backups UI no longer uses a chunk-local bar as the only progress indicator.
- Flasher UI uses the same dual-progress model for read flash region and multi-segment writes.
- Device Backup SHA-256 remains the full flash hash.
- Firmware Fingerprint SHA-256 excludes NVS, OTA data, coredump and diagnostic/device-specific regions.
- Firmware Fingerprint includes bootloader, partition table, app partitions and reusable filesystem regions.
- Saved backup rows show both hashes and repository match count.
Physical validation result: approved.
Date: 2026-07-14.
Environment:
- URL: "https://iot.aeizoon.com/backups"
- Build: "stage1-stage2-20260714-1"
- Device: ESP32-D0WD revision 1
- MAC: "2c:bc:bb:75:f2:74"
- Flash size: "4,194,304" bytes / 4 MB
- Baudrate: "115200"
Validated results:
1. Global progress remained continuous and monotonic during a complete 4 MB read.
2. Partial progress reset correctly by current block/region.
3. Complete local dump downloaded successfully.
4. Complete local dump size: "4,194,304" bytes.
5. Complete local dump SHA-256: "a9ea663ba7afc32f90e73bf25d4e535aeeb182e9285ec5e7c974e4cc67d58600".
6. Existing server backup SHA-256 remains: "ca965603a5c02668e63721b34c05fba0dc2494e3050edcb36b1c621bcafe785a".
7. SHA drift between complete dumps is accepted because mutable NVS/device state can change between captures.
8. Firmware Fingerprint SHA-256 is stored separately from Device Backup SHA-256.
9. Current Firmware Fingerprint SHA-256: "4ee0ccd14c1d85ed814cfe8067e6834bec7081a1bf5005768a087cd2e8e6283f".
10. Firmware Fingerprint size: "4,096,000" bytes.
11. Repository match count: "1".
12. Real "app0" partition downloaded successfully.
13. "app0" size: "1,310,720" bytes / "0x140000".
14. "app0" SHA-256: "d79d4b3db73b490429122d64e076dedecf3c3a198497374ce3a8fa4b575fd948".
15. Metadata JSON for local single-file captures now exposes top-level "sha256" and "sizeBytes", while preserving "files[]".
16. ESP32 reset was validated manually after read operations.
17. Firmware boot was validated manually after read operations.
18. Read-only complete dump and partition download did not alter the device.
Decision:
- Progress global continuo: approved.
- Progress parcial por bloque/region: approved.
- Complete 4 MB local download: approved.
- "app0" partition download: approved.
- Metadata JSON correction: approved.
- Firmware Fingerprint separated from full dump SHA: approved.
- ESP32 functional integrity after read-only operations: approved.
- Restore backup package remains not implemented.
- Import firmware from connected ESP32 remains not implemented.
Restore Backup Package - 2026-07-14
Status: implemented, software validated and physically validated on ESP32 test device.
Implemented:
- Restore from saved ESP Platform backup.
- Restore from uploaded "esp-platform-backup.zip".
- Backend package validation before restore options are shown.
- Complete restore from "full-flash.bin" at "0x0".
- Selective restore by region.
- Safe defaults excluding NVS, OTA data, coredump and sensitive/diagnostic/device-state regions.
- Explicit "RESTORE" confirmation.
- Optional safety backup before writing.
- Optional post-write verification by reading back written bytes.
- Best-effort Secure Boot and Flash Encryption detection warnings.
- Restore audit through "backup_restore_session" and "backup_audit_log".
Software validation completed:
1. Saved backup "#2" validates as restorable.
2. Restore validation returns ESP32 family, 4 MB flash size and 8 regions.
3. Safe defaults are "bootloader", "partition-table", "app0", "app1", "spiffs".
4. Backend restore-session download returns "4,194,304" bytes.
5. Backend restore-session download SHA-256 is "ca965603a5c02668e63721b34c05fba0dc2494e3050edcb36b1c621bcafe785a".
6. Real "esp-platform-backup-2.zip" upload validates successfully.
7. Corrupted checksum ZIP is rejected with HTTP 400 and message "Restore package validation failed: Checksum mismatch for full-flash.bin."
8. Restore UI loads without JavaScript console errors.
9. Restore UI renders package summary, compatibility report and region table.
Physical validation completed on 2026-07-14:
1. Complete restore from saved backup "#2": passed.
- Target: ESP32-D0WD revision 1, MAC "2c:bc:bb:75:f2:74", Flash ID "16405e", detected flash size "4,194,304" bytes / 4 MB.
- Safety backup was enabled and downloaded before writing.
- Safety backup file: "ESP32_2CBCBB75F274_safety_before_restore_20260714T000935Z.bin".
- Safety backup size: "4,194,304" bytes.
- Safety backup SHA-256: "cd3517473707d59c3d915b52a3e16213cadce80d9ffb2b4371958fb7acb51a08".
- Restore wrote "4,194,304" bytes from offset "0x0".
- Restore source SHA-256: "ca965603a5c02668e63721b34c05fba0dc2494e3050edcb36b1c621bcafe785a".
- Post-write verification read back "full-flash.bin" and matched SHA-256 "ca965603a5c02668e63721b34c05fba0dc2494e3050edcb36b1c621bcafe785a".
- RTS hard reset was requested after restore.
2. Selective "app0" restore: passed.
- Only "app0" was selected.
- NVS, OTA data and filesystem were not selected.
- "app0" write offset: "0x10000".
- "app0" size: "1,310,720" bytes.
- "app0" SHA-256 verified after write: "d79d4b3db73b490429122d64e076dedecf3c3a198497374ce3a8fa4b575fd948".
- NVS before SHA-256: "83db022f60712229ae24ca0412897611d656ea5c66102d3f87d5eb3014619172".
- NVS after SHA-256: "83db022f60712229ae24ca0412897611d656ea5c66102d3f87d5eb3014619172".
- Result: NVS remained unchanged.
3. Cancellation during write: passed.
- A complete restore was started with erase enabled.
- Cancel was requested during the writing phase after erase and after writing had begun.
- UI result: "Restore cancelled; device state may be incomplete."
- No success state was shown for the cancelled operation.
- Serial port was released and the device could be reconnected.
- Audit event recorded: "RESTORE_CANCELLED" at "2026-07-14 00:46:44 UTC".
- Device was recovered afterwards using a complete restore with verification.
4. Incompatibility blocking: passed.
- Controlled package with valid checksums but invalid region metadata was rejected before writing.
- Backend response: "Restore package validation failed: Region bootloader exceeds flash size.; Region bootloader overlaps coredump."
- No restore execution session was allowed for that package.
5. Altered checksum ZIP: passed at restore validation boundary.
- Controlled ZIP with altered "checksums.sha256" was rejected before writing.
- Backend response: "Restore package validation failed: Checksum mismatch for full-flash.bin."
- The package does not reach executable restore state.
- Browser automation could not attach the ZIP through Chrome native file chooser because Chrome returned "Not allowed"; the same uploaded-package validation path was verified through the backend endpoint and had previously been validated with a good ZIP from the UI.
Operational observations:
- "Execute restore" is enabled only when an ESP32 is connected, a restore package is validated, compatibility is not "incompatible", and the confirmation field contains exactly "RESTORE".
- Selecting all partitions does not disable execution by itself; selecting NVS, OTA data or coredump triggers explicit warnings because those regions are device-specific or diagnostic.
- Historical restore events are tracked in "backup_audit_log"; repeated executions from the same validated package may update the same "backup_restore_session" row.
Decision:
- Restore backup package is physically validated and approved for MVP Flasher/Backups closure.
- Compare with repository remains postponed.
- Import firmware from connected ESP32 remains postponed.
- OTA, Provisioning, Fleet Management and Edge OS remain out of scope for this phase.
Información técnica del documento
- Ruta lógica
- VALIDATION.md
- Commit Git
a83cef9e1ee0c5b0b22662fe3493139d77c23570- SHA-256
11cf496baa44bd9b29965553adcc4ce3398651da7b7bf2c784507342d4d4413a- Regeneración
scripts/build-documentation.sh