Skip to content

[codex] Add ESP32 flash profiles, USB control, and SD assets#244

Open
mariusandra wants to merge 27 commits into
mainfrom
codex/esp32-flash-profiles
Open

[codex] Add ESP32 flash profiles, USB control, and SD assets#244
mariusandra wants to merge 27 commits into
mainfrom
codex/esp32-flash-profiles

Conversation

@mariusandra

@mariusandra mariusandra commented Jun 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add ESP32 flash size profiles for 4MB, 8MB, and 32MB builds, with 4MB avoiding OTA partitions
  • add ESP32 SD-card asset mounting so FAT32 SD cards can serve /srv/assets, and hide the SD card field group unless that option is enabled
  • add Waveshare ESP32-S3 ePaper 13.3E6 and ESP32-S3 PhotoPainter hardware presets
  • keep the new ESP32 frame form from preselecting a display panel; users must explicitly choose a panel or hardware preset
  • use the locked mariusandra/pixie dependency for ESP32 builds instead of requiring a separate embedded Pixie checkout
  • record embedded ESP32 boot metadata from bootup events, mark browser/USB-flashed firmware as deployed when the boot follows a generated image, and compare git-describe firmware versions correctly in the deploy UI
  • fall back from configured embedded frame hosts to the last boot IP over plain HTTP for direct scene/control requests
  • avoid embedded OTA failures when HTTPS is pointed at an IP not covered by the frame certificate, and continue to the plain HTTP boot-IP fallback after TLS verification errors
  • protect ESP32 setup/control/status routes on normal Wi-Fi with frame admin Basic auth or the backend machine token, while leaving hotspot provisioning open
  • relax the ESP32 native HTTPS heap pre-check so boards with enough contiguous internal heap attempt httpd_ssl_start instead of skipping at the old 96KB total-free threshold
  • add a Web Serial USB API bridge for embedded status/image/render/scene upload/scene activation/OTA commands, and use it for fast deploy, render, and scene activation when the frame has an active or remembered USB port
  • refresh the backend current-frame image cache from USB by pulling usb_api image after render-complete serial logs, uploading it to the backend, and broadcasting frame_rendered
  • persist the last displayed packed-frame hash in NVS, skip panel refresh when a render matches what is already on screen, and choose packed vs hash-only preview storage from available 8-bit PSRAM
  • expose ESP32 firmware footprint metadata and show flash partitions, measured binary sizes, and runtime PSRAM buffers in the Firmware drawer
  • keep the firmware footprint card visible while firmware is queued, building, browser-flashing, or shown from the main deploy view, while preserving layout metadata across live firmware status updates
  • keep browser flashing on the Firmware drawer, select Logs behind the drawer during flashing/USB log streaming, and rebuild stale/missing generated firmware automatically before flash/download
  • add global backend host/port defaults for new frames, with blank values falling back to the detected browser host/port
  • make the deploy drawer URL-backed, including embedded firmware views, and close stale deploy state before opening Add scene
  • add Add scene personal favourites: star template rows, persist favourite template ids in backend settings, and bulk-add all starred scenes from the drawer
  • add Noto Color Emoji as a runtime font fallback

Root Cause

ESP32 frames can be flashed outside the normal deploy path, so the backend was still comparing against the old last_successful_deploy snapshot even after the device reported a newer booted firmware. Direct commands could also keep targeting a stale mDNS/HTTPS host even when the ESP32 had reported a usable boot IP.

For native ESP32 HTTPS, FrameOS was skipping the HTTPS server before trying ESP-IDF's allocator whenever total free internal heap was below 96KB. On the observed 7.3e boot, the device had about 54KB free internal heap and a 53KB largest block, so the old total-free guard rejected it even though the contiguous block was large enough to make an HTTPS start attempt reasonable.

The OTA trigger used the same direct frame HTTP helper, but a TLS certificate verification error on the first HTTPS candidate aborted the request before the embedded boot-IP fallback could run. If frame_host was an IP like 10.8.0.232 and the certificate only covered a hostname, the backend returned a 502 even though the ESP32 had reported a reachable plain HTTP boot IP.

The ESP32 setup UI was exposed on the normal LAN with no firmware-side request authentication. That made setup/control routes too sensitive for public reachability. The firmware now bakes/stores frame admin credentials, challenges browsers with Basic auth outside hotspot mode, denies setup access when no admin credentials exist, and still accepts the backend server_api_key bearer token for automated deploy/control requests.

When a board is plugged in over USB but LAN HTTP is unreachable, the UI looked connected because logs streamed, while deploy/control/image paths still depended on HTTP. The new serial API gives the browser a direct command path over the already-selected USB port.

The current frame image had the same gap: serial logs are client-local, so a USB-only render could finish without the backend ever seeing a render event or refreshing /image over HTTP. The frontend now detects render-complete USB log lines, pulls the preview through usb_api image, and uploads that image into the backend cache used by the normal frame preview URL.

For e-paper refresh avoidance, the useful invariant is the panel-packed output, not the RGB source. The firmware now hashes the packed buffer and stores that small state in NVS across boots/deep sleep. It keeps the full packed snapshot in PSRAM only when the largest 8-bit PSRAM block and remaining free PSRAM can afford it; otherwise it stays in hash-only mode to avoid blowing up smaller boards.

For firmware sizing, the build output had measured binary sizes but the UI did not show how those bytes map to flash slots or how panel buffers map to PSRAM. The firmware metadata now derives that layout from the same flash profile, partition CSV, panel dimensions, and render buffer formulas used by the build/runtime code.

The workspace had two deploy drawer states: the URL-backed workspace drawer selection and the local deployPlanModalOpen right-panel state on frame/scene/app pages. Opening Add scene cleared the workspace drawer but could leave the local deploy panel open above it, while deploy sub-view switches only changed local state and never made the URL round-trip capable.

Browser flashing used the normal frame-tool navigation action to open Logs after flashing, which also closed the deploy drawer. At the same time, a first boot log could make an embedded frame eligible for the main deploy view, causing the Firmware view and footprint card to disappear mid-flash. Firmware status updates could also replace rich layout metadata with sparse queued/stale records.

Validation

  • pytest -q backend/app/api/tests/test_log.py backend/app/models/tests/test_log.py backend/app/utils/tests/test_frame_http.py
  • pytest -q backend/app/tasks/tests/test_frame_deploy_workflow.py::test_plan_fast_for_embedded_uses_http_scene_reload_plan backend/app/tasks/tests/test_frame_deploy_workflow.py::test_execute_embedded_fast_uploads_scenes_then_reloads
  • pytest -q backend/app/api/tests/test_frames.py::test_api_frame_new_embedded_waveshare_13in3e6_preset backend/app/api/tests/test_frames.py::test_api_frame_new_embedded_waveshare_photopainter_preset backend/app/api/tests/test_frames.py::test_api_frame_embedded_usb_deploy_complete_marks_snapshot backend/app/api/tests/test_frames.py::test_api_frame_embedded_usb_deploy_complete_rejects_non_embedded backend/app/utils/tests/test_frame_http.py
  • pytest -q backend/app/api/tests/test_frames.py
  • pytest -q backend/app/api/tests/test_embedded_firmware.py
  • pytest -q backend/app/utils/tests/test_frame_http.py backend/app/api/tests/test_embedded_firmware.py
  • pytest -q backend/app/models/tests/test_settings.py backend/app/api/tests/test_settings.py
  • pnpm --dir frontend run build:kea
  • pnpm --dir frontend exec tsc --noEmit
  • git diff --check
  • FRAMEOS_SELECTED_PANEL=EPD_7in3e idf.py -B build-codex-https-guard build
  • flashed the connected ESP32-S3 on /dev/cu.usbmodem1101; usb_api status returned the new render fields, usb_api render acknowledged, and the follow-up status reported lastRefreshSkipped=true, snapshotMode=packed, and previewBytes=192000

@mariusandra mariusandra changed the title [codex] Add ESP32 flash profiles Add ESP32 flash profiles Jun 27, 2026
@mariusandra mariusandra changed the title Add ESP32 flash profiles [codex] Add ESP32 flash profiles and SD assets Jun 27, 2026
@mariusandra mariusandra changed the title [codex] Add ESP32 flash profiles and SD assets [codex] Add ESP32 flash profiles, SD assets, and Waveshare preset Jun 27, 2026
@mariusandra mariusandra changed the title [codex] Add ESP32 flash profiles, SD assets, and Waveshare preset [codex] Add ESP32 flash profiles, SD assets, and Waveshare presets Jun 27, 2026
@mariusandra mariusandra changed the title [codex] Add ESP32 flash profiles, SD assets, and Waveshare presets [codex] Add ESP32 flash profiles, USB control, and SD assets Jun 27, 2026
@mariusandra mariusandra marked this pull request as ready for review June 27, 2026 16:21

Copy link
Copy Markdown
Collaborator Author

Added in e5b5cb57:

  • bulk "Add all starred scenes" now keeps each original template associated with the scenes it creates, so the template preview image is saved onto those new scene IDs
  • remote zip template installs also preserve the original template image metadata when their scenes are loaded

Validation rerun:

  • pnpm --dir frontend run build:kea
  • pnpm --dir frontend exec tsc --noEmit
  • git diff --check

Copy link
Copy Markdown
Collaborator Author

Follow-up in 7e08caf0:

  • fixed TemplateDrawer hook order by calling the split-screen logic hook before the early returns, using a stable fallback frame id when the drawer is closed

Validation rerun:

  • pnpm --dir frontend run build:kea
  • pnpm --dir frontend exec tsc --noEmit
  • git diff --check

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant