ESP Platform

Documentación oficial

Engineering · project

ESP Platform Phase 1E Validation

Documentación asociada a AEOS 0.7.0Revisión 1Actualizado 14/07/2026 03:47:21 CESTvalidated

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