Add firmware variant selection to build scripts
This commit is contained in:
parent
314408aba9
commit
39312a0d1f
7 changed files with 235 additions and 17 deletions
|
|
@ -1,2 +1,3 @@
|
||||||
{"id":"int-691bec44","kind":"field_change","created_at":"2026-04-15T15:26:14.033393505Z","actor":"w1n5t0n","issue_id":"meml-kx6a","extra":{"field":"status","new_value":"in_progress","old_value":"open"}}
|
{"id":"int-691bec44","kind":"field_change","created_at":"2026-04-15T15:26:14.033393505Z","actor":"w1n5t0n","issue_id":"meml-kx6a","extra":{"field":"status","new_value":"in_progress","old_value":"open"}}
|
||||||
{"id":"int-55a0a37d","kind":"field_change","created_at":"2026-04-15T15:32:17.624534929Z","actor":"w1n5t0n","issue_id":"meml-kx6a","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Added firmware helper scripts and updated docs to the RP2350/C++20/O3 build flow"}}
|
{"id":"int-55a0a37d","kind":"field_change","created_at":"2026-04-15T15:32:17.624534929Z","actor":"w1n5t0n","issue_id":"meml-kx6a","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Added firmware helper scripts and updated docs to the RP2350/C++20/O3 build flow"}}
|
||||||
|
{"id":"int-c4271206","kind":"field_change","created_at":"2026-04-15T15:35:23.153751063Z","actor":"w1n5t0n","issue_id":"meml-efb2","extra":{"field":"status","new_value":"in_progress","old_value":"open"}}
|
||||||
|
|
|
||||||
|
|
@ -244,6 +244,9 @@ git submodule update --init --recursive
|
||||||
# Build only
|
# Build only
|
||||||
scripts/build-firmware.sh
|
scripts/build-firmware.sh
|
||||||
|
|
||||||
|
# Build a specific variant
|
||||||
|
scripts/build-firmware.sh memlcelium
|
||||||
|
|
||||||
# Flash a previously-built UF2
|
# Flash a previously-built UF2
|
||||||
scripts/flash-firmware.sh
|
scripts/flash-firmware.sh
|
||||||
|
|
||||||
|
|
@ -252,6 +255,7 @@ scripts/build-and-flash-firmware.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
The scripts build for `rp2040:rp2040:solderparty_rp2350_stamp_xl:opt=Optimize3` and force C++20 via `compiler.cpp.extra_flags=-std=gnu++20`.
|
The scripts build for `rp2040:rp2040:solderparty_rp2350_stamp_xl:opt=Optimize3` and force C++20 via `compiler.cpp.extra_flags=-std=gnu++20`.
|
||||||
|
If no variant is passed to `build-firmware.sh` in an interactive shell, it parses the available `MEMLNautMode*` options from `MEMLNaut-NISPS.ino`, prompts for one, and rewrites the active `MEMLNAUT_MODE_TYPE` before compiling.
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
|
|
|
||||||
2
MAP.md
2
MAP.md
|
|
@ -53,7 +53,7 @@ MEMLNaut-NISPS: Neural Interactive Shaping of Parameter Spaces. Two living artef
|
||||||
- `README.md` — short quickstart.
|
- `README.md` — short quickstart.
|
||||||
|
|
||||||
## Entry points
|
## Entry points
|
||||||
- **Firmware**: `scripts/build-firmware.sh`, `scripts/flash-firmware.sh`, or `scripts/build-and-flash-firmware.sh` (requires submodules initialised). The scripts target `rp2040:rp2040:solderparty_rp2350_stamp_xl:opt=Optimize3` and force C++20. Execution = `setup()`/`loop()` on Core 0, `setup1()`/`loop1()` on Core 1, audio ISR on Core 1.
|
- **Firmware**: `scripts/build-firmware.sh`, `scripts/flash-firmware.sh`, or `scripts/build-and-flash-firmware.sh` (requires submodules initialised). `build-firmware.sh` can take an explicit variant name like `memlcelium` or prompt interactively from the parsed `MEMLNautMode*` list and rewrite the active mode in `MEMLNaut-NISPS.ino`. The scripts target `rp2040:rp2040:solderparty_rp2350_stamp_xl:opt=Optimize3` and force C++20. Execution = `setup()`/`loop()` on Core 0, `setup1()`/`loop1()` on Core 1, audio ISR on Core 1.
|
||||||
- **Playground**: `cd playground && python3 -m http.server` (or `serve.sh` / `serve-coop.py`), open `a-immersive.html`. Append `?debug=1` to expose `window.__nisps`.
|
- **Playground**: `cd playground && python3 -m http.server` (or `serve.sh` / `serve-coop.py`), open `a-immersive.html`. Append `?debug=1` to expose `window.__nisps`.
|
||||||
- **WASM rebuild**: `cd playground/wasm && ./build.sh` (needs `emcc`).
|
- **WASM rebuild**: `cd playground/wasm && ./build.sh` (needs `emcc`).
|
||||||
- **Tests**: `npx playwright test` (auto-spawns server on 7331).
|
- **Tests**: `npx playwright test` (auto-spawns server on 7331).
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ scripts/build-and-flash-firmware.sh
|
||||||
Notes:
|
Notes:
|
||||||
- The scripts build for `rp2040:rp2040:solderparty_rp2350_stamp_xl` with `Optimize3`.
|
- The scripts build for `rp2040:rp2040:solderparty_rp2350_stamp_xl` with `Optimize3`.
|
||||||
- The build forces C++20 because the firmware uses `std::span` and concepts.
|
- The build forces C++20 because the firmware uses `std::span` and concepts.
|
||||||
|
- `build-firmware.sh` accepts an optional variant name such as `memlcelium` or `breakor`. If you omit it in an interactive shell, the script parses `MEMLNaut-NISPS.ino`, prompts for a variant, and rewrites the active `MEMLNAUT_MODE_TYPE` before building.
|
||||||
- `flash-firmware.sh` accepts an optional mountpoint argument, or auto-detects common UF2 bootloader mounts such as `/run/media/$USER/RP2350` and `/run/media/$USER/RPI-RP2`.
|
- `flash-firmware.sh` accepts an optional mountpoint argument, or auto-detects common UF2 bootloader mounts such as `/run/media/$USER/RP2350` and `/run/media/$USER/RPI-RP2`.
|
||||||
|
|
||||||
## Web Playground
|
## Web Playground
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@ usage() {
|
||||||
cat <<'EOF'
|
cat <<'EOF'
|
||||||
Usage:
|
Usage:
|
||||||
scripts/build-and-flash-firmware.sh
|
scripts/build-and-flash-firmware.sh
|
||||||
scripts/build-and-flash-firmware.sh [mountpoint]
|
scripts/build-and-flash-firmware.sh [variant]
|
||||||
|
scripts/build-and-flash-firmware.sh --variant VARIANT [mountpoint]
|
||||||
|
|
||||||
If no mountpoint is provided, the flash step auto-detects a standard UF2
|
If no mountpoint is provided, the flash step auto-detects a standard UF2
|
||||||
bootloader location such as /run/media/$USER/RP2350 or /run/media/$USER/RPI-RP2.
|
bootloader location such as /run/media/$USER/RP2350 or /run/media/$USER/RPI-RP2.
|
||||||
|
|
@ -19,21 +20,48 @@ if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
case $# in
|
source "$SCRIPT_DIR/firmware-common.sh"
|
||||||
0)
|
load_firmware_variants
|
||||||
;;
|
|
||||||
1)
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
usage >&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
"$SCRIPT_DIR/build-firmware.sh"
|
variant_arg="${MEMLNAUT_FIRMWARE_VARIANT:-}"
|
||||||
|
mountpoint=""
|
||||||
|
|
||||||
if [[ $# -eq 1 ]]; then
|
while [[ $# -gt 0 ]]; do
|
||||||
"$SCRIPT_DIR/flash-firmware.sh" "$1"
|
case "$1" in
|
||||||
|
--variant)
|
||||||
|
if [[ $# -lt 2 ]]; then
|
||||||
|
echo "error: --variant requires a value" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
variant_arg="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--variant=*)
|
||||||
|
variant_arg="${1#*=}"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if [[ -z "$variant_arg" ]] && resolve_firmware_variant "$1" >/dev/null 2>&1; then
|
||||||
|
variant_arg="$1"
|
||||||
|
elif [[ -z "$mountpoint" ]]; then
|
||||||
|
mountpoint="$1"
|
||||||
|
else
|
||||||
|
usage >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -n "$variant_arg" ]]; then
|
||||||
|
"$SCRIPT_DIR/build-firmware.sh" --variant "$variant_arg"
|
||||||
|
else
|
||||||
|
"$SCRIPT_DIR/build-firmware.sh"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$mountpoint" ]]; then
|
||||||
|
"$SCRIPT_DIR/flash-firmware.sh" "$mountpoint"
|
||||||
else
|
else
|
||||||
"$SCRIPT_DIR/flash-firmware.sh"
|
"$SCRIPT_DIR/flash-firmware.sh"
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,13 @@ source "$SCRIPT_DIR/firmware-common.sh"
|
||||||
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
|
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
|
||||||
cat <<'EOF'
|
cat <<'EOF'
|
||||||
Usage:
|
Usage:
|
||||||
scripts/build-firmware.sh [extra arduino-cli compile args...]
|
scripts/build-firmware.sh [variant]
|
||||||
|
scripts/build-firmware.sh --variant VARIANT [extra arduino-cli compile args...]
|
||||||
|
scripts/build-firmware.sh [variant] -- [extra arduino-cli compile args...]
|
||||||
|
|
||||||
Builds the MEMLNaut firmware with the repo's known-good RP2350 target and C++20 flag.
|
Builds the MEMLNaut firmware with the repo's known-good RP2350 target and C++20 flag.
|
||||||
|
If no variant is provided and the script is run in a TTY, it prompts from the parsed
|
||||||
|
MEMLNaut mode list in MEMLNaut-NISPS.ino and rewrites the active mode before building.
|
||||||
EOF
|
EOF
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
@ -18,12 +22,49 @@ fi
|
||||||
ensure_arduino_cli
|
ensure_arduino_cli
|
||||||
ensure_submodules_ready
|
ensure_submodules_ready
|
||||||
|
|
||||||
|
variant_arg="${MEMLNAUT_FIRMWARE_VARIANT:-}"
|
||||||
|
compile_extra=()
|
||||||
|
|
||||||
|
load_firmware_variants
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--variant)
|
||||||
|
if [[ $# -lt 2 ]]; then
|
||||||
|
echo "error: --variant requires a value" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
variant_arg="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--variant=*)
|
||||||
|
variant_arg="${1#*=}"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
compile_extra+=("$@")
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if [[ -z "$variant_arg" ]] && resolve_firmware_variant "$1" >/dev/null 2>&1; then
|
||||||
|
variant_arg="$1"
|
||||||
|
else
|
||||||
|
compile_extra+=("$1")
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
set_firmware_variant "$variant_arg"
|
||||||
mkdir -p "$BUILD_DIR"
|
mkdir -p "$BUILD_DIR"
|
||||||
|
|
||||||
echo "Building firmware:"
|
echo "Building firmware:"
|
||||||
echo " sketch: $SKETCH_PATH"
|
echo " sketch: $SKETCH_PATH"
|
||||||
echo " fqbn: $FQBN"
|
echo " fqbn: $FQBN"
|
||||||
echo " out: $BUILD_DIR"
|
echo " out: $BUILD_DIR"
|
||||||
|
echo " variant: $(variant_alias "$ACTIVE_FIRMWARE_VARIANT")"
|
||||||
|
|
||||||
compile_args=(
|
compile_args=(
|
||||||
compile
|
compile
|
||||||
|
|
@ -32,7 +73,7 @@ compile_args=(
|
||||||
--output-dir "$BUILD_DIR"
|
--output-dir "$BUILD_DIR"
|
||||||
"$SKETCH_PATH"
|
"$SKETCH_PATH"
|
||||||
)
|
)
|
||||||
compile_args+=("$@")
|
compile_args+=("${compile_extra[@]}")
|
||||||
|
|
||||||
arduino-cli "${compile_args[@]}"
|
arduino-cli "${compile_args[@]}"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,9 @@ UF2_PATH_DEFAULT="${MEMLNAUT_FIRMWARE_UF2:-$BUILD_DIR/${SKETCH_NAME}.uf2}"
|
||||||
FQBN="${MEMLNAUT_FIRMWARE_FQBN:-rp2040:rp2040:solderparty_rp2350_stamp_xl:opt=Optimize3}"
|
FQBN="${MEMLNAUT_FIRMWARE_FQBN:-rp2040:rp2040:solderparty_rp2350_stamp_xl:opt=Optimize3}"
|
||||||
CXX20_BUILD_PROPERTY="${MEMLNAUT_FIRMWARE_CXX20_PROPERTY:-compiler.cpp.extra_flags=-std=gnu++20}"
|
CXX20_BUILD_PROPERTY="${MEMLNAUT_FIRMWARE_CXX20_PROPERTY:-compiler.cpp.extra_flags=-std=gnu++20}"
|
||||||
|
|
||||||
|
FIRMWARE_VARIANTS=()
|
||||||
|
ACTIVE_FIRMWARE_VARIANT=""
|
||||||
|
|
||||||
ensure_arduino_cli() {
|
ensure_arduino_cli() {
|
||||||
if ! command -v arduino-cli >/dev/null 2>&1; then
|
if ! command -v arduino-cli >/dev/null 2>&1; then
|
||||||
echo "error: arduino-cli is not installed or not on PATH" >&2
|
echo "error: arduino-cli is not installed or not on PATH" >&2
|
||||||
|
|
@ -92,3 +95,143 @@ assert_boot_mount() {
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
load_firmware_variants() {
|
||||||
|
mapfile -t FIRMWARE_VARIANTS < <(
|
||||||
|
grep -E '^[[:space:]]*(//[[:space:]]*)?#define[[:space:]]+MEMLNAUT_MODE_TYPE[[:space:]]+MEMLNautMode[A-Za-z0-9_]+' "$SKETCH_PATH" \
|
||||||
|
| sed -E 's/^[[:space:]]*(\/\/[[:space:]]*)?#define[[:space:]]+MEMLNAUT_MODE_TYPE[[:space:]]+//'
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ ${#FIRMWARE_VARIANTS[@]} -eq 0 ]]; then
|
||||||
|
echo "error: could not find any MEMLNaut mode variants in $SKETCH_PATH" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ACTIVE_FIRMWARE_VARIANT="$(
|
||||||
|
grep -E '^[[:space:]]*#define[[:space:]]+MEMLNAUT_MODE_TYPE[[:space:]]+MEMLNautMode[A-Za-z0-9_]+' "$SKETCH_PATH" \
|
||||||
|
| sed -E 's/^[[:space:]]*#define[[:space:]]+MEMLNAUT_MODE_TYPE[[:space:]]+//' \
|
||||||
|
| head -n 1
|
||||||
|
)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variant_alias() {
|
||||||
|
local variant="$1"
|
||||||
|
variant="${variant#MEMLNautMode}"
|
||||||
|
printf '%s\n' "$variant" | tr '[:upper:]' '[:lower:]'
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_firmware_variant() {
|
||||||
|
local requested="$1"
|
||||||
|
local requested_lower
|
||||||
|
local variant
|
||||||
|
|
||||||
|
requested_lower="$(printf '%s' "$requested" | tr '[:upper:]' '[:lower:]')"
|
||||||
|
for variant in "${FIRMWARE_VARIANTS[@]}"; do
|
||||||
|
if [[ "$requested" == "$variant" || "$requested_lower" == "$(printf '%s' "$variant" | tr '[:upper:]' '[:lower:]')" || "$requested_lower" == "$(variant_alias "$variant")" ]]; then
|
||||||
|
printf '%s\n' "$variant"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
choose_firmware_variant() {
|
||||||
|
local requested="${1:-}"
|
||||||
|
local resolved=""
|
||||||
|
local variant
|
||||||
|
local choice
|
||||||
|
|
||||||
|
load_firmware_variants
|
||||||
|
|
||||||
|
if [[ -n "$requested" ]]; then
|
||||||
|
if resolved="$(resolve_firmware_variant "$requested")"; then
|
||||||
|
printf '%s\n' "$resolved"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "error: unknown firmware variant: $requested" >&2
|
||||||
|
echo "available variants:" >&2
|
||||||
|
for variant in "${FIRMWARE_VARIANTS[@]}"; do
|
||||||
|
echo " - $(variant_alias "$variant") ($variant)" >&2
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -t 0 && -t 1 ]]; then
|
||||||
|
echo "Select a firmware variant to build:"
|
||||||
|
local idx=1
|
||||||
|
for variant in "${FIRMWARE_VARIANTS[@]}"; do
|
||||||
|
if [[ "$variant" == "$ACTIVE_FIRMWARE_VARIANT" ]]; then
|
||||||
|
echo " $idx) $(variant_alias "$variant") ($variant, current)"
|
||||||
|
else
|
||||||
|
echo " $idx) $(variant_alias "$variant") ($variant)"
|
||||||
|
fi
|
||||||
|
idx=$((idx + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
read -r -p "Variant number or name: " choice
|
||||||
|
if [[ "$choice" =~ ^[0-9]+$ ]]; then
|
||||||
|
if (( choice >= 1 && choice <= ${#FIRMWARE_VARIANTS[@]} )); then
|
||||||
|
printf '%s\n' "${FIRMWARE_VARIANTS[choice-1]}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
elif resolved="$(resolve_firmware_variant "$choice" 2>/dev/null)"; then
|
||||||
|
printf '%s\n' "$resolved"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo "Invalid selection. Enter a number from the list or a variant name."
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$ACTIVE_FIRMWARE_VARIANT" ]]; then
|
||||||
|
echo "No variant specified and no interactive terminal available; using current variant: $(variant_alias "$ACTIVE_FIRMWARE_VARIANT")" >&2
|
||||||
|
printf '%s\n' "$ACTIVE_FIRMWARE_VARIANT"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "error: no variant specified and no active variant found in $SKETCH_PATH" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
set_firmware_variant() {
|
||||||
|
local requested="$1"
|
||||||
|
local selected
|
||||||
|
|
||||||
|
selected="$(choose_firmware_variant "$requested")"
|
||||||
|
python - "$SKETCH_PATH" "$selected" <<'PY'
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sketch_path = Path(sys.argv[1])
|
||||||
|
selected = sys.argv[2]
|
||||||
|
|
||||||
|
pattern = re.compile(r'^(\s*)(//\s*)?#define(\s+MEMLNAUT_MODE_TYPE\s+)(MEMLNautMode[A-Za-z0-9_]+)(\s*)$')
|
||||||
|
lines = sketch_path.read_text().splitlines()
|
||||||
|
updated = []
|
||||||
|
found_selected = False
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
match = pattern.match(line)
|
||||||
|
if not match:
|
||||||
|
updated.append(line)
|
||||||
|
continue
|
||||||
|
|
||||||
|
indent, _, middle, variant, trailing = match.groups()
|
||||||
|
if variant == selected:
|
||||||
|
updated.append(f"{indent}#define{middle}{variant}{trailing}")
|
||||||
|
found_selected = True
|
||||||
|
else:
|
||||||
|
updated.append(f"{indent}// #define{middle}{variant}{trailing}")
|
||||||
|
|
||||||
|
if not found_selected:
|
||||||
|
raise SystemExit(f"selected variant not found in sketch: {selected}")
|
||||||
|
|
||||||
|
sketch_path.write_text("\n".join(updated) + "\n")
|
||||||
|
PY
|
||||||
|
|
||||||
|
ACTIVE_FIRMWARE_VARIANT="$selected"
|
||||||
|
echo "Selected firmware variant: $(variant_alias "$selected") ($selected)"
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue