-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjustfile
More file actions
342 lines (290 loc) · 17.6 KB
/
justfile
File metadata and controls
342 lines (290 loc) · 17.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# stackchan-kai development tasks.
#
# Install just: https://github.com/casey/just
# Install espup + esp toolchain:
# cargo install espup
# espup install
# source $HOME/export-esp.sh
#
# PORT defaults to /dev/ttyACM1 (Andy's CoreS3 USB-Serial-JTAG). Override
# with `just PORT=/dev/ttyACM0 flash` if your device enumerates differently.
set shell := ["bash", "-cu"]
# Path to the release firmware ELF. Used by `flash`, `monitor`, `fmr`.
firmware_elf := "target/xtensa-esp32s3-none-elf/release/stackchan-firmware"
# Default port for CoreS3 USB-Serial-JTAG.
# Linux enumerates as /dev/ttyACM*, macOS as /dev/cu.usbmodem*.
# Override by prefixing `just PORT=/dev/cu.usbmodem2101 …`.
PORT := if os() == "macos" { "/dev/cu.usbmodem2101" } else { "/dev/ttyACM1" }
# On Linux the `dialout` group gate requires `sg dialout -c '…'`; macOS
# grants serial access directly, so the wrapper is a no-op passthrough.
_serial_prefix := if os() == "macos" { "" } else { "sg dialout -c '" }
_serial_suffix := if os() == "macos" { "" } else { "'" }
# Default: list available recipes.
default:
@just --list
# ----- Session-start sanity check ------------------------------------------
# Print toolchain availability, device enumeration, working-tree state,
# and recent CI status. Read-only; no side effects. Useful at session
# start (human or AI) before reaching for code.
dev:
@bash scripts/dev-status.sh
# ----- Host-side -----------------------------------------------------------
# Fast inner-loop check — fmt + cargo check, no clippy or tests.
# Sub-second feedback for "did my edit compile?" iteration. Use this
# while iterating on a single file; run `just check` before committing.
check-fast:
cargo fmt --check
cargo check --workspace --exclude stackchan-firmware --exclude esp-tflite-micro-sys --all-features --all-targets --quiet
# Fast host checks — the same gates the pre-commit hook runs.
check:
cargo fmt --check
cargo clippy --workspace --exclude stackchan-firmware --exclude esp-tflite-micro-sys --all-features --all-targets -- -D warnings
cargo test --workspace --exclude stackchan-firmware --exclude esp-tflite-micro-sys --all-features
# Doc-drift guard — flag crates whose src/ has diverged > N commits from
# their README.md. Warn-only by default. Pass `STRICT=1` for non-zero
# exit on drift (CI uses this).
doc-drift:
@bash scripts/check-doc-drift.sh {{ if env_var_or_default("STRICT", "") == "1" { "--strict" } else { "" } }}
# Boot-log regression guard — diff /tmp/scfmr.log (last `just fmr` /
# `just fmr-agent` flash) against `tests/golden/boot.txt`. Reports
# missing golden lines (regression) and unexpected WARN/ERROR lines
# (new noise) with known-noise filtered. Run after every flash to catch
# silent regressions. Pass `--update` to rewrite the golden from the
# current live log when boot output changes intentionally.
verify-boot *args:
@bash scripts/check-boot.sh {{args}}
# Everything the CI host job runs (adds doc-lint + cargo-deny).
ci: check
cargo deny check
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --workspace --exclude stackchan-firmware --exclude esp-tflite-micro-sys --all-features
# MSRV check — matches the `msrv` CI job. Requires `rustup toolchain install 1.88`.
# Default features only — optional dev-tool features (e.g. `viz`) aren't MSRV-gated.
msrv:
cargo +1.88 build --workspace --exclude stackchan-firmware --exclude esp-tflite-micro-sys
# Craft + validate a STACKCHAN.RON offline before flashing. Host-only,
# feature-gated tool over the same `stackchan-net::config` API the
# firmware runs at boot. E.g. `just config list`,
# `just config template --palette cute --face-geometry chibi`,
# `just config validate /sd/STACKCHAN.RON`.
config *args:
cargo run -p stackchan-sim --bin sc-config --features config-cli -- {{args}}
# ----- Web dashboard ------------------------------------------------------
# Build the operator dashboard bundle. Vite + Solid → `web/dist/index.html`,
# then gzip → `web/dist/index.html.gz`. The firmware `include_bytes!`
# from the gzipped artifact, so this must run before any firmware build
# or check (the firmware recipes below chain through it).
web-build:
cd web && npm install --no-audit --no-fund && npm run build
# Type-check the web sources without producing a bundle. Faster than
# `web-build` for inner-loop iteration on the dashboard.
web-typecheck:
cd web && npm install --no-audit --no-fund && npm run typecheck
# Build the sidecar 3D companion bundle. Vite + three.js → `sidecar/web/dist/`.
# The sidecar's `register_companion` mounts this directory under `/companion/`
# when it exists. Independent of `web-build`; not chained into the firmware
# recipes because the companion is served by the host sidecar, not the device.
sidecar-companion-build:
cd sidecar/web && npm install --no-audit --no-fund && npm run build
sidecar-companion-typecheck:
cd sidecar/web && npm install --no-audit --no-fund && npm run typecheck
# ----- Firmware (requires `source ~/export-esp.sh` first) ------------------
# Firmware-side compile check. Runs from inside the firmware crate so the
# per-crate `.cargo/config.toml` (target + `build-std`) actually applies —
# `-p stackchan-firmware` from workspace root silently misses that.
# Chains through `web-build` because `http.rs` `include_bytes!`s the
# dashboard bundle, which is parse-time — the file must exist before
# `cargo check` parses the macro.
check-firmware: web-build
cd crates/stackchan-firmware && cargo +esp check
# Firmware strict clippy (matches the CI firmware job).
clippy-firmware: web-build
cd crates/stackchan-firmware && cargo +esp clippy --release -- -D warnings
# Full release build of the firmware binary.
build-firmware: web-build
cd crates/stackchan-firmware && cargo +esp build --release
# Release build with the `tracking-trace` cargo feature on. Emits
# structured defmt events from the camera-tracking pipeline (attention
# / engagement transitions, lock-fire latency, observation cadence).
# Use when measuring face-tracking behavior on the live unit.
build-firmware-trace:
cd crates/stackchan-firmware && cargo +esp build --release --features tracking-trace
# ----- Flash + monitor ----------------------------------------------------
#
# These recipes go through espflash over the serial-JTAG port. On Linux
# (distrobox), the `sg dialout` wrapper is injected automatically via
# `_serial_prefix`/`_serial_suffix`; on macOS the commands run directly.
#
# ## USB-Serial-JTAG reliability
#
# Prefer `fmr` (combined flash + monitor) over separate `flash; monitor`
# calls — each espflash invocation toggles DTR/RTS to reset the chip,
# and back-to-back resets against ESP32-S3's USB-Serial-JTAG peripheral
# can wedge the USB enumeration until a physical power cycle. The
# combined form issues one reset and transitions straight to monitor,
# keeping the port open. See `just reattach` for a no-reset way to
# pick up a running device's log without reflashing.
# Flash the latest release build. Rebuilds first.
# Prefer `fmr` for normal flash-and-monitor cycles — this recipe is
# split out only for CI or scripted workflows that don't want a monitor
# attached.
flash: build-firmware
{{_serial_prefix}}espflash flash --port {{PORT}} {{firmware_elf}}{{_serial_suffix}}
# Monitor defmt logs from a running device (no reflash). Exits on Ctrl+C.
# Default form triggers a chip reset on attach — use `just reattach`
# instead to preserve the current boot state.
monitor:
{{_serial_prefix}}espflash monitor --port {{PORT}} --log-format defmt --elf {{firmware_elf}}{{_serial_suffix}}
# Re-attach to a running device *without* resetting it. Useful when a
# monitor session dropped (`Ctrl+C`, terminal closed, ssh dropped) and
# you want to pick up the log stream without restarting the firmware.
# Also the safer choice when debugging the USB-JTAG disconnect pattern.
reattach:
{{_serial_prefix}}espflash monitor --no-reset --port {{PORT}} --log-format defmt --elf {{firmware_elf}}{{_serial_suffix}}
# Flash + monitor in one recipe. `fmr` = flash-monitor-reload, the
# default inner-loop verb. Build first, then flash, then stream logs.
# One port-open, one reset — preferred over split `flash; monitor`.
fmr: build-firmware
{{_serial_prefix}}espflash flash --monitor --log-format defmt --port {{PORT}} {{firmware_elf}}{{_serial_suffix}}
# Flash + monitor a `tracking-trace`-feature build in one shot. Emits
# structured defmt events from the camera-tracking pipeline
# (attention / engagement transitions, lock-fire latency, observation
# cadence). Filter the stream with `grep trk:` to isolate them.
fmr-trace: build-firmware-trace
{{_serial_prefix}}espflash flash --monitor --log-format defmt --port {{PORT}} {{firmware_elf}}{{_serial_suffix}}
# Agent-friendly fmr: kicks `just fmr` off inside a tmux session, tees
# the output to /tmp/scfmr.log, and polls until "boot complete" appears
# (with a 90 s timeout). Returns 0 on clean boot, 2 on panic, 3 on
# timeout. Designed for AI agents and other non-TTY shells where
# espflash's interactive monitor would otherwise fail to start. See
# CLAUDE.md "Flashing from an agent / non-TTY shell".
fmr-agent:
@bash scripts/fmr-agent.sh
# Path prefix for release bench example ELFs.
example_elf_dir := "target/xtensa-esp32s3-none-elf/release/examples"
# Calibration bench: flashes the sweep-and-print example + streams its
# defmt output. The bench binary halts after one full sweep; re-flash
# the main firmware with `just flash` or `just fmr` when done.
bench:
cd crates/stackchan-firmware && cargo +esp build --release --example bench
{{_serial_prefix}}espflash flash --monitor --log-format defmt --port {{PORT}} {{example_elf_dir}}/bench{{_serial_suffix}}
# Magnetometer bench: streams trim-compensated BMM150 readings at 5 Hz.
# Look for total field magnitude `sqrt(|B|²)` in the 25-65 µT range
# (earth field); deviations are hard-iron offsets from the nearby
# SCServo motors. Re-flash the main firmware with `just fmr` when done.
mag-bench:
cd crates/stackchan-firmware && cargo +esp build --release --example mag_bench
{{_serial_prefix}}espflash flash --monitor --log-format defmt --port {{PORT}} {{example_elf_dir}}/mag_bench{{_serial_suffix}}
# LED-ring bench: cycles through each Emotion palette entry every 2 s,
# independent of the modifier pipeline. Useful for verifying the PY32
# WS2812 fan-out without the main render stack in the loop.
leds-bench:
cd crates/stackchan-firmware && cargo +esp build --release --example leds_bench
{{_serial_prefix}}espflash flash --monitor --log-format defmt --port {{PORT}} {{example_elf_dir}}/leds_bench{{_serial_suffix}}
# AW88298 control-path bench: runs the amp's full I²C init sequence
# (reset → enable → configure I2S 16 kHz mono → mute → disable boost)
# and logs a heartbeat. Does NOT stream audio — I2S wiring lands in the
# follow-up audio-task PR. Verifies chip presence and register-sequence
# acceptance only.
aw88298-bench:
cd crates/stackchan-firmware && cargo +esp build --release --example aw88298_bench
{{_serial_prefix}}espflash flash --monitor --log-format defmt --port {{PORT}} {{example_elf_dir}}/aw88298_bench{{_serial_suffix}}
# ES7210 control-path bench: runs the ADC's full I²C init sequence
# (reset → clock tree for 12.288 MHz / 16 kHz → mic1+2 power-on → latch
# reset) and logs a heartbeat. Does NOT capture audio — I2S wiring
# lands in the follow-up audio-task PR. Verifies chip presence and
# register-sequence acceptance only.
es7210-bench:
cd crates/stackchan-firmware && cargo +esp build --release --example es7210_bench
{{_serial_prefix}}espflash flash --monitor --log-format defmt --port {{PORT}} {{example_elf_dir}}/es7210_bench{{_serial_suffix}}
# Audio playlist bench: brings up the full audio stack (I²S + AW88298
# + ES7210) and loops through every clip in the chirp library
# (BOOT_GREETING, WAKE_CHIRP, pickup chirp, low-battery alert) with
# 800 ms gaps. Use this when tuning clip amplitudes / durations /
# pitches without rebuilding the full firmware.
audio-bench:
cd crates/stackchan-firmware && cargo +esp build --release --example audio_bench
{{_serial_prefix}}espflash flash --monitor --log-format defmt --port {{PORT}} {{example_elf_dir}}/audio_bench{{_serial_suffix}}
# Tilt extremes calibration: drives the pitch servo through 0° → ±50°
# in 5° steps (5° past `MAX_TILT_DEG`'s safety bound) and reads back
# the live encoder, stopping the sweep early once readings plateau.
# Use the SUMMARY/SUGGEST lines to set EEPROM angle limits and
# `TILT_TRIM_DEG` in `head.rs`. Re-flash main firmware with
# `just fmr` when done.
tilt-extremes:
cd crates/stackchan-firmware && cargo +esp build --release --example tilt_extremes
{{_serial_prefix}}espflash flash --monitor --log-format defmt --port {{PORT}} {{example_elf_dir}}/tilt_extremes{{_serial_suffix}}
# Tilt freewheel diagnostic: disables torque on the pitch servo and
# live-streams the encoder reading at 5 Hz so the operator can hand-
# rotate the head and verify whether the internal position sensor is
# tracking. Use after `tilt-extremes` flags STUCK + OVERLOAD to
# distinguish "encoder dead" from "controller stuck in OVERLOAD".
tilt-freewheel:
cd crates/stackchan-firmware && cargo +esp build --release --example tilt_freewheel
{{_serial_prefix}}espflash flash --monitor --log-format defmt --port {{PORT}} {{example_elf_dir}}/tilt_freewheel{{_serial_suffix}}
# Ambient light sensor bench: streams LTR-553 lux readings at 2 Hz +
# proximity counts. Verifies init sequence + driver presence.
ambient-bench:
cd crates/stackchan-firmware && cargo +esp build --release --example ambient_bench
{{_serial_prefix}}espflash flash --monitor --log-format defmt --port {{PORT}} {{example_elf_dir}}/ambient_bench{{_serial_suffix}}
# IR-NEC bench: decodes incoming NEC frames from the IR receiver on
# GPIO21 (RMT channel 7) and prints `(address, command)` tuples. Use to
# discover your remote's codes for the `EmotionFromRemote` modifier mapping.
ir-bench:
cd crates/stackchan-firmware && cargo +esp build --release --example ir_bench
{{_serial_prefix}}espflash flash --monitor --log-format defmt --port {{PORT}} {{example_elf_dir}}/ir_bench{{_serial_suffix}}
# FT6336U touch bench: streams raw touch events (point count, x, y,
# pressure) so calibration drift or wiring issues surface as obviously-
# wrong coordinates.
touch-bench:
cd crates/stackchan-firmware && cargo +esp build --release --example touch_bench
{{_serial_prefix}}espflash flash --monitor --log-format defmt --port {{PORT}} {{example_elf_dir}}/touch_bench{{_serial_suffix}}
# BMI270 IMU bench: streams accel + gyro at 100 Hz with magnitude
# computed. Use to characterize gravity vector + bias on a static unit
# before hand-tuning PickupReaction thresholds.
imu-bench:
cd crates/stackchan-firmware && cargo +esp build --release --example imu_bench
{{_serial_prefix}}espflash flash --monitor --log-format defmt --port {{PORT}} {{example_elf_dir}}/imu_bench{{_serial_suffix}}
# Block-grid motion tracker bench: brings up the camera path and runs
# the `tracker` crate over each frame, logging motion centroids and
# proposed pan/tilt deltas. No servos commanded — algorithm validation
# only.
tracker-bench:
cd crates/stackchan-firmware && cargo +esp build --release --example tracker_bench
{{_serial_prefix}}espflash flash --monitor --log-format defmt --port {{PORT}} {{example_elf_dir}}/tracker_bench{{_serial_suffix}}
# Haar face cascade bench: streams camera frames through the tracker,
# scores each motion candidate's ROI through the OpenCV-converted
# frontal-face cascade, and logs per-frame step + cascade timings.
# Used to validate the FPS budget and detection accuracy on hardware
# before face detection ships in `main.rs`. No servos commanded.
face-bench:
cd crates/stackchan-firmware && cargo +esp build --release --example face_bench
{{_serial_prefix}}espflash flash --monitor --log-format defmt --port {{PORT}} {{example_elf_dir}}/face_bench{{_serial_suffix}}
# I²C bus probe: sweeps 0x08..=0x77 and logs which addresses ACK. Used
# to confirm chip presence on the shared I²C bus before committing to
# address constants in driver crates without a published datasheet.
# One-shot — re-flash the main firmware with `just fmr` when done.
i2c-probe:
cd crates/stackchan-firmware && cargo +esp build --release --example i2c_probe
{{_serial_prefix}}espflash flash --monitor --log-format defmt --port {{PORT}} {{example_elf_dir}}/i2c_probe{{_serial_suffix}}
# Si12T body-touch bench: initialises the 3-zone capacitive touch
# controller on the back of the head and polls OUTPUT1 at 50 ms,
# logging zone-state changes. Tap each pad to verify the bit-to-zone
# mapping the upstream M5Stack driver uses.
si12t-bench:
cd crates/stackchan-firmware && cargo +esp build --release --example si12t_bench
{{_serial_prefix}}espflash flash --monitor --log-format defmt --port {{PORT}} {{example_elf_dir}}/si12t_bench{{_serial_suffix}}
# Bake verbal-phrase PCM clips from the manifest using Piper TTS.
# Reads `crates/stackchan-tts/assets/manifest.toml` and writes raw
# 16 kHz / 16-bit / mono / little-endian PCM to
# `crates/stackchan-tts/assets/<locale>/<phrase_id>.pcm`. Prereqs
# (piper, sox) are checked by the script and the failure mode is a
# clear install hint, not a partial bake. Pass optional `<locale>
# <phrase>` args to bake one entry; no args bakes everything.
bake-tts *args:
bash scripts/bake-tts.sh {{args}}
# BLE smoke: scan for an advertised stack-chan and verify the GATT
# table can be read end-to-end. Optional first arg: device-name prefix
# (default `stackchan-`). Falls back to a printed manual procedure
# if BlueZ + a powered adapter aren't available — agent-friendly.
ble-smoke *args:
bash scripts/ble-smoke.sh {{args}}