Add server-side support for a second panel (13.3in Spectra 6 / EE02) and scaffold its firmware target
Build and push server image / test (push) Successful in 45s
Firmware build check / build-check (push) Successful in 2m50s
Build and push server image / build-and-push (push) Successful in 4m36s
Build and push server image / deploy (push) Failing after 1m34s

Server: Frame.panel_type (new column + migration) is auto-derived from
the device's reported board (X-Frame-Board), never user-set -- the
panel is a property of the hardware, not a picker in the UI.
image_pipeline's packing/render pipeline is parameterized by panel
geometry instead of hardcoded 800x480 globals, with the real confirmed
13.3in geometry (1600x1200) registered alongside the original 7.3in
panel. Existing 7.3in frames are unaffected (column default + board
mapping both resolve to the original panel).

Board identifiers are also renamed (devkit/xiao -> devkit_esp32c6/
xiao_esp32c6, plus new "ee02") since the EE02 board also carries a XIAO
module -- "xiao" alone stopped disambiguating hardware. The server
keeps accepting the legacy bare names indefinitely for already-flashed
devices.

Firmware: scaffolds a third build target (ee02, ESP32-S3 -- a real
chip-target change, not just a same-chip Kconfig variant like xiao) and
a new epd13in3e driver component skeleton. The actual panel init/LUT/
refresh register sequence isn't ported from vendor demo code yet (none
was available), so that component deliberately fails to compile
(#error) rather than risk sending unverified register values to real
hardware -- devkit/xiao are unaffected and build identically to before.
CI's ee02 build step is continue-on-error for the same reason.
This commit is contained in:
2026-08-04 20:08:22 +00:00
parent 1d39e439ff
commit 474b92a282
44 changed files with 1287 additions and 172 deletions
+25 -21
View File
@@ -1,10 +1,11 @@
---
name: build-firmware
description: Compile the espresso_frame ESP32-C6 firmware (firmware/) for both board variants without Docker -- a native, non-container ESP-IDF v6.0 install. Use when asked to build the firmware, verify a firmware/main/*.c change actually compiles, or check both the devkit and xiao board targets.
description: Compile the espresso_frame firmware (firmware/) for all three board variants (ESP32-C6 devkit/xiao, ESP32-S3 ee02) without Docker -- a native, non-container ESP-IDF v6.0 install. Use when asked to build the firmware, verify a firmware/main/*.c change actually compiles, or check the devkit/xiao/ee02 board targets.
---
Compiles `firmware/` (ESP-IDF, targeting ESP32-C6) locally, without
Docker -- CI's `firmware-build-check.yml`/`firmware-release-build.yml`
Compiles `firmware/` (ESP-IDF, targeting ESP32-C6 for devkit/xiao and
ESP32-S3 for ee02) locally, without Docker -- CI's
`firmware-build-check.yml`/`firmware-release-build.yml`
build inside the `espressif/idf:release-v6.0` container image, but
**this sandbox cannot run containers at all**: `docker.io` installs and
`dockerd` starts fine even as root, but the sandbox strips
@@ -39,13 +40,13 @@ had neither):
of `release/v6.0` (~700MB) into `~/.espressif-idf/esp-idf` -- matches
the IDF version CI's Docker image pins. Only clones once; re-running
`setup.sh` never touches an existing checkout.
- The esp32c6 toolchain + Python venv, via ESP-IDF's own
`./install.sh esp32c6` -- scoped to just this project's one target
(see `firmware/README.md`'s board table), not every chip ESP-IDF
supports, to keep the download/disk footprint down. `install.sh` is
already idempotent on its own, so `setup.sh` always calls it rather
than duplicating that check -- a re-run costs a few seconds once
everything's cached.
- The esp32c6+esp32s3 toolchains + Python venv, via ESP-IDF's own
`./install.sh esp32c6,esp32s3` -- scoped to just this project's two
chip targets (see `firmware/README.md`'s board table: devkit/xiao are
esp32c6, ee02 is esp32s3), not every chip ESP-IDF supports, to keep
the download/disk footprint down. `install.sh` is already idempotent
on its own, so `setup.sh` always calls it rather than duplicating
that check -- a re-run costs a few seconds once everything's cached.
Takes a few minutes on a cold run (mostly `install.sh`'s own pip/tool
downloads), well under a minute on a re-run. Needs real root (`apt-get
@@ -63,15 +64,17 @@ checkout itself). Confirmed working with as little as ~7GB free.
```bash
bash .claude/skills/build-firmware/build.sh # devkit (default)
bash .claude/skills/build-firmware/build.sh xiao
bash .claude/skills/build-firmware/build.sh both # both variants
bash .claude/skills/build-firmware/build.sh ee02
bash .claude/skills/build-firmware/build.sh both # devkit + xiao
bash .claude/skills/build-firmware/build.sh all # devkit + xiao + ee02
```
Each board gets its own build directory and generated sdkconfig (see
`firmware/build_for_board.sh`'s own comment) -- building one never
disturbs the other. `build.sh` auto-runs `set-target esp32c6` the very
first time a board is built (no generated sdkconfig yet); later builds
skip straight to `idf.py build`. Extra arguments pass straight through
to `idf.py`, e.g.:
disturbs the other. `build.sh` auto-runs `set-target` (esp32c6 for
devkit/xiao, esp32s3 for ee02) the very first time a board is built (no
generated sdkconfig yet); later builds skip straight to `idf.py build`.
Extra arguments pass straight through to `idf.py`, e.g.:
```bash
bash .claude/skills/build-firmware/build.sh xiao flash -p /dev/ttyUSB0
@@ -83,15 +86,16 @@ somewhere hardware is actually plugged in (a real dev machine, or a
differently-configured environment with device passthrough).
A clean build of one board takes ~30s once the target's already been
configured (~1,000 build steps total split across both boards, most of
configured (~1,000 build steps total split across the boards, most of
it ESP-IDF's own components -- this project's own `firmware/main/*.c`
and `firmware/components/*` sources are a small fraction of that and
compile in a few seconds). Output lands at
`firmware/build/espresso_frame.bin` (devkit) or
`firmware/build_xiao/espresso_frame.bin` (xiao) -- both paths are
gitignored (`firmware/.gitignore`... actually the repo root
`.gitignore`'s "ESP-IDF firmware build output" section), so nothing
here needs cleaning up before a commit.
`firmware/build/espresso_frame.bin` (devkit),
`firmware/build_xiao/espresso_frame.bin` (xiao), or
`firmware/build_ee02/espresso_frame.bin` (ee02, once its driver actually
compiles -- see the note above) -- all three paths are gitignored (the
repo root `.gitignore`'s "ESP-IDF firmware build output" section), so
nothing here needs cleaning up before a commit.
## Verified
+35 -17
View File
@@ -2,16 +2,25 @@
# Builds (or flashes/monitors, if a serial port is actually attached)
# the espresso_frame firmware for one board variant, via the project's
# own firmware/build_for_board.sh -- this script just sources the
# ESP-IDF environment first and auto-runs `set-target esp32c6` on a
# board's very first build (a fresh clone has no generated sdkconfig
# yet, same reasoning as CI's own build steps -- see firmware/README.md's
# "Building for the Seeed XIAO ESP32-C6" section).
# ESP-IDF environment first and auto-runs `set-target` (esp32c6 for
# devkit/xiao, esp32s3 for ee02) on a board's very first build (a fresh
# clone has no generated sdkconfig yet, same reasoning as CI's own build
# steps -- see firmware/README.md's "Building for the Seeed XIAO
# ESP32-C6" section).
#
# NOTE: ee02 builds will fail to compile -- deliberately -- until
# firmware/components/epd13in3e's panel driver is ported from vendor
# demo code (see that component's own top-of-file comment). The build
# plumbing itself (target selection, partition table, sdkconfig
# layering) is exercised regardless; only the final compile step fails.
#
# Usage:
# build.sh # build devkit (default)
# build.sh devkit
# build.sh xiao
# build.sh both # build both board variants
# build.sh ee02
# build.sh both # build devkit + xiao (unchanged meaning)
# build.sh all # build devkit + xiao + ee02
# build.sh xiao flash -p /dev/ttyUSB0 # only meaningful with real hardware attached
set -euo pipefail
@@ -33,16 +42,17 @@ cd "$firmware_dir"
build_one() {
local board="$1"
shift
local sdkconfig
local sdkconfig target
case "$board" in
devkit) sdkconfig="sdkconfig" ;;
xiao) sdkconfig="sdkconfig.xiao_local" ;;
*) echo "Unknown board '$board' -- expected 'devkit' or 'xiao'" >&2; exit 1 ;;
devkit) sdkconfig="sdkconfig"; target="esp32c6" ;;
xiao) sdkconfig="sdkconfig.xiao_local"; target="esp32c6" ;;
ee02) sdkconfig="sdkconfig.ee02_local"; target="esp32s3" ;;
*) echo "Unknown board '$board' -- expected 'devkit', 'xiao', or 'ee02'" >&2; exit 1 ;;
esac
if [ ! -f "$sdkconfig" ]; then
echo "==> $board: no generated sdkconfig yet, setting target esp32c6"
./build_for_board.sh "$board" set-target esp32c6
echo "==> $board: no generated sdkconfig yet, setting target $target"
./build_for_board.sh "$board" set-target "$target"
fi
local args=("$@")
@@ -55,9 +65,17 @@ build_one() {
board="${1:-devkit}"
shift || true
if [ "$board" = "both" ]; then
build_one devkit "$@"
build_one xiao "$@"
else
build_one "$board" "$@"
fi
case "$board" in
both)
build_one devkit "$@"
build_one xiao "$@"
;;
all)
build_one devkit "$@"
build_one xiao "$@"
build_one ee02 "$@"
;;
*)
build_one "$board" "$@"
;;
esac
+7 -6
View File
@@ -45,14 +45,15 @@ else
echo "esp-idf already cloned at $IDF_DIR"
fi
# 3. Toolchain + Python virtualenv, scoped to esp32c6 only -- this
# project's one target (see firmware/README.md's board table). Scoping
# avoids downloading toolchains for every chip ESP-IDF supports, which
# matters given this container's disk headroom. install.sh is already
# 3. Toolchain + Python virtualenv, scoped to esp32c6+esp32s3 only --
# this project's two chip targets (see firmware/README.md's board
# table: devkit/xiao are esp32c6, ee02 is esp32s3). Scoping avoids
# downloading toolchains for every chip ESP-IDF supports, which matters
# given this container's disk headroom. install.sh is already
# idempotent on its own (checks what's present and skips it), so this
# always calls it rather than trying to duplicate that check here --
# a re-run only costs a few seconds once everything's cached.
echo "running esp-idf install.sh esp32c6 (fast if already installed) ..."
(cd "$IDF_DIR" && ./install.sh esp32c6)
echo "running esp-idf install.sh esp32c6,esp32s3 (fast if already installed) ..."
(cd "$IDF_DIR" && ./install.sh esp32c6,esp32s3)
echo "setup complete -> $IDF_DIR/export.sh (build.sh sources this for you)"