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.
6.7 KiB
name, description
| name | description |
|---|---|
| build-firmware | 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 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
cap_sys_admin (and blocks the bare unshare syscall) from the
capability set regardless of uid, which container image-layer
extraction and namespace setup both require. Confirmed by hand:
docker run hello-world fails to extract even the tiny hello-world
layer ("failed to extract layer... operation not permitted" with the
overlayfs snapshotter; "unshare: operation not permitted" even with
the vfs storage driver instead). This is a hard restriction of the
sandbox itself, not a permissions/setup problem -- don't spend time
re-trying --privileged-equivalent flags or alternate storage drivers,
none of it routes around a missing cap_sys_admin.
The workaround: skip containers entirely and install ESP-IDF the same
way a developer would set it up on their own machine (git clone +
ESP-IDF's own install.sh) -- that path needs nothing this sandbox
disallows, just normal file/process operations.
Setup (once per fresh container)
bash .claude/skills/build-firmware/setup.sh
Installs (via real apt-get -- this container actually has root and a
working package manager, unlike run-server's Chromium bootstrap which
had neither):
- OS build deps:
python3/venv/pip,cmake,ninja-build,flex/bison/gperf,build-essential,libusb-1.0-0. - ESP-IDF itself: a shallow, single-branch, recursive-submodule clone
of
release/v6.0(~700MB) into~/.espressif-idf/esp-idf-- matches the IDF version CI's Docker image pins. Only clones once; re-runningsetup.shnever touches an existing checkout. - The esp32c6+esp32s3 toolchains + Python venv, via ESP-IDF's own
./install.sh esp32c6,esp32s3-- scoped to just this project's two chip targets (seefirmware/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.shis already idempotent on its own, sosetup.shalways 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 install) -- if this container ever runs as non-root, this setup
doesn't apply as-is (would need the same non-root apt-download +
dpkg-deb -x extraction dance run-server's setup.sh uses for
Chromium).
Disk: budget ~4GB free before starting (esp-idf checkout + toolchain +
Python env land around 3.4GB in ~/.espressif, plus the ~700MB
checkout itself). Confirmed working with as little as ~7GB free.
Build
bash .claude/skills/build-firmware/build.sh # devkit (default)
bash .claude/skills/build-firmware/build.sh xiao
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 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 .claude/skills/build-firmware/build.sh xiao flash -p /dev/ttyUSB0
flash/monitor need an actual attached device and serial port --
this sandbox has neither, so those only work when this skill runs
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 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),
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
Both board variants (devkit set-target esp32c6 + build, xiao
set-target esp32c6 + build) built successfully end-to-end using this
exact setup.sh/build.sh pair, producing real
espresso_frame.bin images with normal free-space margins (41%/36%
of their respective app partitions) and no errors -- only one
pre-existing, unrelated warning (battery.c's unused TAG when that
file's logging is compiled out). This is a real compile check, not
just a syntax read -- if a future change breaks the build, this skill
will actually catch it.
Troubleshooting
docker: ... unshare: operation not permitted/failed to extract layer ... operation not permitted: expected in this sandbox, see the top of this file. Don't debug it further -- use this skill's native install instead.ESP-IDF not found at ... -- run setup.sh first:build.sh's own check for a missing$IDF_DIR/export.sh-- runsetup.sh(see above) before the first build.idf.py: command not foundif you try to run it directly: same gotchafirmware/build_for_board.shalready documents --idf.pyis normally a shell function from ESP-IDF'sexport.sh, not on PATH as a real executable, so it isn't inherited into a script's own subshell even after sourcingexport.shin your interactive shell first. Usebuild.sh(orbuild_for_board.sh, which callspython "$IDF_PATH/tools/idf.py"directly) instead of typingidf.pyin a fresh script/subshell.- Disk pressure during
install.sh: this environment runs close to full (single-digit GB free is normal, not a sign of a leak) --df -h /before runningsetup.shif a build mysteriously fails partway with a "no space left on device"-shaped error.