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.
133 lines
6.7 KiB
Markdown
133 lines
6.7 KiB
Markdown
---
|
|
name: build-firmware
|
|
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 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
|
|
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-running
|
|
`setup.sh` never 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 (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
|
|
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
|
|
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
|
|
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` -- run `setup.sh` (see
|
|
above) before the first build.
|
|
- **`idf.py: command not found` if you try to run it directly**: same
|
|
gotcha `firmware/build_for_board.sh` already documents -- `idf.py` is
|
|
normally a shell *function* from ESP-IDF's `export.sh`, not on PATH
|
|
as a real executable, so it isn't inherited into a script's own
|
|
subshell even after sourcing `export.sh` in your interactive shell
|
|
first. Use `build.sh` (or `build_for_board.sh`, which calls
|
|
`python "$IDF_PATH/tools/idf.py"` directly) instead of typing
|
|
`idf.py` in 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 running `setup.sh` if a build mysteriously fails
|
|
partway with a "no space left on device"-shaped error.
|