feat(skill): isolate firmware emulator invocation
This commit is contained in:
@@ -9,17 +9,7 @@ description: "Build and interactively test Xteink ESP32-C3 firmware in the nativ
|
||||
|
||||
Build firmware for an Xteink device, boot it in the native emulator, and validate behavior through serial logs, physical controls, screenshots, SD state, and network access.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Point `XTEINK_QEMU_REPO` at a prepared QEMU checkout containing `dist/qemu-native`:
|
||||
|
||||
```sh
|
||||
export XTEINK_QEMU_REPO=${XTEINK_QEMU_REPO:-../qemu}
|
||||
export XTEINK_EMU_STATE=/tmp/xteink-firmware-$UID
|
||||
export XTEINK_EMU_SD=/tmp/xteink-firmware-$UID.img
|
||||
```
|
||||
|
||||
The emulator exits with setup instructions when its native QEMU distribution is missing.
|
||||
Resolve script paths relative to this skill directory. `scripts/xteink-emu.sh` stores the qemu-xteink checkout location, builds native QEMU when missing, and runs the emulator. If its location is unset, run the `variable.sh --set` command printed by the wrapper after asking the user for the checkout path.
|
||||
|
||||
## Workflow
|
||||
|
||||
@@ -28,6 +18,8 @@ The emulator exits with setup instructions when its native QEMU distribution is
|
||||
```sh
|
||||
pio run
|
||||
export XTEINK_FIRMWARE=.pio/build/default/firmware.bin
|
||||
export XTEINK_EMU_STATE=/tmp/xteink-firmware-$UID
|
||||
export XTEINK_EMU_SD=/tmp/xteink-firmware-$UID.img
|
||||
```
|
||||
|
||||
2. Create a persistent FAT32 SD image once, unless the test requires a fresh card:
|
||||
@@ -40,9 +32,8 @@ The emulator exits with setup instructions when its native QEMU distribution is
|
||||
3. Start a clean emulator process while preserving SD contents:
|
||||
|
||||
```sh
|
||||
uv run --script "$XTEINK_QEMU_REPO/scripts/xteink-emu.py" stop \
|
||||
--state-dir "$XTEINK_EMU_STATE" || true
|
||||
uv run --script "$XTEINK_QEMU_REPO/scripts/xteink-emu.py" start \
|
||||
scripts/xteink-emu.sh stop --state-dir "$XTEINK_EMU_STATE" || true
|
||||
scripts/xteink-emu.sh start \
|
||||
--state-dir "$XTEINK_EMU_STATE" \
|
||||
--firmware "$XTEINK_FIRMWARE" \
|
||||
--sdcard "$XTEINK_EMU_SD"
|
||||
@@ -53,7 +44,7 @@ The emulator exits with setup instructions when its native QEMU distribution is
|
||||
4. Observe serial behavior with either a persistent regex cursor or the raw log:
|
||||
|
||||
```sh
|
||||
uv run --script "$XTEINK_QEMU_REPO/scripts/xteink-emu.py" wait \
|
||||
scripts/xteink-emu.sh wait \
|
||||
--state-dir "$XTEINK_EMU_STATE" --timeout 30 --match 'pattern'
|
||||
tail -f "$XTEINK_EMU_STATE/serial.log"
|
||||
```
|
||||
@@ -61,12 +52,9 @@ The emulator exits with setup instructions when its native QEMU distribution is
|
||||
5. Drive the physical controls:
|
||||
|
||||
```sh
|
||||
uv run --script "$XTEINK_QEMU_REPO/scripts/xteink-emu.py" button \
|
||||
--state-dir "$XTEINK_EMU_STATE" bottom-2
|
||||
uv run --script "$XTEINK_QEMU_REPO/scripts/xteink-emu.py" button \
|
||||
--state-dir "$XTEINK_EMU_STATE" power --down
|
||||
uv run --script "$XTEINK_QEMU_REPO/scripts/xteink-emu.py" button \
|
||||
--state-dir "$XTEINK_EMU_STATE" power --up
|
||||
scripts/xteink-emu.sh button --state-dir "$XTEINK_EMU_STATE" bottom-2
|
||||
scripts/xteink-emu.sh button --state-dir "$XTEINK_EMU_STATE" power --down
|
||||
scripts/xteink-emu.sh button --state-dir "$XTEINK_EMU_STATE" power --up
|
||||
```
|
||||
|
||||
Available buttons are `left`, `right`, `bottom-1` through `bottom-4`, and `power`. Use the labels rendered by the current screen as the authoritative mapping. Allow the e-ink panel to refresh after state-changing input.
|
||||
@@ -74,20 +62,11 @@ The emulator exits with setup instructions when its native QEMU distribution is
|
||||
6. Capture and inspect the screen after meaningful transitions:
|
||||
|
||||
```sh
|
||||
uv run --script "$XTEINK_QEMU_REPO/scripts/xteink-emu.py" screenshot \
|
||||
scripts/xteink-emu.sh screenshot \
|
||||
--state-dir "$XTEINK_EMU_STATE" --output /tmp/xteink-screen.png
|
||||
```
|
||||
|
||||
7. Enter text when the firmware's on-screen keyboard is focused:
|
||||
|
||||
```sh
|
||||
uv run --script "$XTEINK_QEMU_REPO/scripts/xteink-type-keyboard.py" \
|
||||
"$XTEINK_EMU_STATE" normal 'Example'
|
||||
uv run --script "$XTEINK_QEMU_REPO/scripts/xteink-type-keyboard.py" \
|
||||
"$XTEINK_EMU_STATE" url 'https://example.com'
|
||||
```
|
||||
|
||||
8. For network tests, connect the firmware to the single open Wi-Fi network named `qemu`, then validate the feature's observable result.
|
||||
7. For network tests, connect the firmware to the single open Wi-Fi network named `qemu`, then validate the feature's observable result.
|
||||
|
||||
## Diagnose Failures
|
||||
|
||||
@@ -100,6 +79,5 @@ The emulator exits with setup instructions when its native QEMU distribution is
|
||||
## Cleanup
|
||||
|
||||
```sh
|
||||
uv run --script "$XTEINK_QEMU_REPO/scripts/xteink-emu.py" stop \
|
||||
--state-dir "$XTEINK_EMU_STATE"
|
||||
scripts/xteink-emu.sh stop --state-dir "$XTEINK_EMU_STATE"
|
||||
```
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env bash
|
||||
# Skill-local variable store. Values live in <skill-dir>/.vars/<NAME>.
|
||||
#
|
||||
# Usage:
|
||||
# variable.sh --get NAME # prints value to stdout, exits 0
|
||||
# # or prints a self-explaining hint to
|
||||
# # stderr and exits 2 if unset.
|
||||
# variable.sh --set NAME VALUE # writes value, exits 0.
|
||||
#
|
||||
# Callers should treat a non-zero exit as fatal; the stderr message tells
|
||||
# the caller (agent or user) exactly how to populate the missing value.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SKILL_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
STORE="$SKILL_DIR/.vars"
|
||||
SELF="$0"
|
||||
|
||||
usage() {
|
||||
cat >&2 <<EOF
|
||||
Usage:
|
||||
$SELF --get NAME
|
||||
$SELF --set NAME VALUE
|
||||
EOF
|
||||
exit 2
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
--get)
|
||||
[[ $# -eq 2 ]] || usage
|
||||
name="$2"
|
||||
file="$STORE/$name"
|
||||
if [[ ! -f "$file" ]]; then
|
||||
cat >&2 <<EOF
|
||||
$SELF: $name is not set.
|
||||
Ask the user for the value, then set it:
|
||||
$SELF --set $name <value>
|
||||
EOF
|
||||
exit 2
|
||||
fi
|
||||
cat "$file"
|
||||
;;
|
||||
--set)
|
||||
[[ $# -eq 3 ]] || usage
|
||||
name="$2"; value="$3"
|
||||
[[ "$name" =~ ^[A-Z][A-Z0-9_]*$ ]] || {
|
||||
echo >&2 "$SELF: invalid name '$name' (must match [A-Z][A-Z0-9_]*)"
|
||||
exit 2
|
||||
}
|
||||
mkdir -p "$STORE"
|
||||
# Self-ignore the store so values never get committed, even if the
|
||||
# skill root lacks a .gitignore entry for .vars/.
|
||||
[[ -f "$STORE/.gitignore" ]] || printf '*\n' > "$STORE/.gitignore"
|
||||
printf '%s' "$value" > "$STORE/$name"
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
QEMU_REPO="$("$SCRIPT_DIR/variable.sh" --get XTEINK_QEMU_REPO)"
|
||||
EMU="$QEMU_REPO/scripts/xteink-emu.py"
|
||||
QEMU="$QEMU_REPO/dist/qemu-native/bin/qemu-system-riscv32"
|
||||
|
||||
if [[ ! -f "$EMU" ]]; then
|
||||
echo >&2 "$QEMU_REPO is not a qemu-xteink checkout (missing scripts/xteink-emu.py)"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ ! -x "$QEMU" ]]; then
|
||||
make -C "$QEMU_REPO" xteink
|
||||
fi
|
||||
|
||||
exec "$EMU" "$@"
|
||||
Reference in New Issue
Block a user