2026-06-28 04:14:30 +02:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
#
|
|
|
|
|
# setup-firmware-toolchain.sh — one-shot bring-up of the MEMLNaut firmware
|
|
|
|
|
# build environment. Idempotent: safe to re-run.
|
|
|
|
|
#
|
|
|
|
|
# It will:
|
refactor(firmware): delete vendored daisysp and the input_router layer
Phase 1 group 8 (S8, ST2, L14).
- S8: the vendored src/daisysp tree (96 files, 41 .cpp compiled into every
firmware build) and its sketch-tree symlink. Zero consumers — nisps replaced
daisysp's PitchShifter with a custom granular implementation. Corrected
firmware/README.md and setup-firmware-toolchain.sh, which called it a live
submodule. The attribution comment in nisps/dsp/pitch_shift.hpp stays: it is
an honest provenance note about a port, not a dependency.
Noted while verifying: src/memllib/examples/KassiaAudioApp includes
../../daisysp/..., but examples/ is not symlinked into the sketch tree and is
never compiled by the firmware build, so the deletion stands.
- ST2: input_router.hpp was a zero-logic speculative layer with one consumer;
the .ino now calls bind_peripherals directly.
- L14: peripherals.hpp — deleted kAnalogInputCount, PeripheralBindings and the
unused spread local, and extracted the duplicated commit-and-train block into
one helper. The audit's suggested commit_and_train(mode, feedback) signature
could NOT be behaviour-identical: repositioning() implies placing() (both
live in ExploreState::Placing), so dispatching on feedback state inside the
helper would silently reroute a TogB2 press mid-reposition from commit_place
to commit_reposition, which differs (no snapshot restore, clears
reposition_). Implemented as commit_and_train(mode, feedback, bool
reposition) with the branch decided at the call site, preserving behaviour.
Firmware is not compiled by any gate yet (that arrives with the PlatformIO
migration, plan §5/S9), so this group is verified by reading and grep only.
Gates: run-all-tests.sh ALL GREEN.
2026-07-21 12:49:25 +02:00
|
|
|
# 1. Initialise the git submodule (memllib).
|
2026-06-28 04:14:30 +02:00
|
|
|
# 2. Install arduino-cli (to ~/.local/bin) if it is not already on PATH.
|
|
|
|
|
# 3. Install the earlephilhower rp2040 core (provides the
|
|
|
|
|
# solderparty_rp2350_stamp_xl board + the ARM GCC toolchain).
|
|
|
|
|
# 4. Install the Arduino libraries the firmware needs
|
|
|
|
|
# (TFT_eSPI, TFT_eWidget, MIDI Library).
|
|
|
|
|
# 5. Drop the MEMLNaut-specific TFT_eSPI config (ILI9341, 320x240, MEMLNaut
|
|
|
|
|
# pins) into the installed TFT_eSPI library as User_Setup.h, backing up
|
|
|
|
|
# the stock one first. memllib `#include <User_Setup.h>` directly, so this
|
|
|
|
|
# is the load-bearing step that makes the display compile + work.
|
|
|
|
|
# 6. Optionally compile the SelfTest firmware to verify the whole chain.
|
|
|
|
|
#
|
|
|
|
|
# Usage:
|
|
|
|
|
# scripts/setup-firmware-toolchain.sh # full setup + verify build
|
|
|
|
|
# scripts/setup-firmware-toolchain.sh --no-build # setup only, skip the build
|
|
|
|
|
#
|
|
|
|
|
# Run this on your firmware build machine (Linux or macOS), NOT inside a
|
|
|
|
|
# sandbox. The rp2040 core download is large (hundreds of MB: it bundles the
|
|
|
|
|
# arm-none-eabi GCC toolchain).
|
|
|
|
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
# ---- config -------------------------------------------------------------
|
|
|
|
|
RP2040_INDEX_URL="https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json"
|
|
|
|
|
ARDUINO_LIBS=("TFT_eSPI" "TFT_eWidget" "MIDI Library")
|
|
|
|
|
DO_BUILD=1
|
|
|
|
|
|
|
|
|
|
for arg in "$@"; do
|
|
|
|
|
case "$arg" in
|
|
|
|
|
--no-build) DO_BUILD=0 ;;
|
|
|
|
|
-h|--help) sed -n '2,33p' "$0"; exit 0 ;;
|
|
|
|
|
*) echo "unknown arg: $arg" >&2; exit 2 ;;
|
|
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
|
|
TFT_SETUP_SRC="$REPO_ROOT/firmware/MEMLNaut-NISPS/src/memllib/hardware/memlnaut/Setup9999_MEMLNaut.h.renameme"
|
|
|
|
|
|
|
|
|
|
log() { printf '\n\033[1;36m==> %s\033[0m\n' "$*"; }
|
|
|
|
|
warn() { printf '\033[1;33mwarning: %s\033[0m\n' "$*" >&2; }
|
|
|
|
|
die() { printf '\033[1;31merror: %s\033[0m\n' "$*" >&2; exit 1; }
|
|
|
|
|
|
|
|
|
|
# ---- 1. submodules ------------------------------------------------------
|
refactor(firmware): delete vendored daisysp and the input_router layer
Phase 1 group 8 (S8, ST2, L14).
- S8: the vendored src/daisysp tree (96 files, 41 .cpp compiled into every
firmware build) and its sketch-tree symlink. Zero consumers — nisps replaced
daisysp's PitchShifter with a custom granular implementation. Corrected
firmware/README.md and setup-firmware-toolchain.sh, which called it a live
submodule. The attribution comment in nisps/dsp/pitch_shift.hpp stays: it is
an honest provenance note about a port, not a dependency.
Noted while verifying: src/memllib/examples/KassiaAudioApp includes
../../daisysp/..., but examples/ is not symlinked into the sketch tree and is
never compiled by the firmware build, so the deletion stands.
- ST2: input_router.hpp was a zero-logic speculative layer with one consumer;
the .ino now calls bind_peripherals directly.
- L14: peripherals.hpp — deleted kAnalogInputCount, PeripheralBindings and the
unused spread local, and extracted the duplicated commit-and-train block into
one helper. The audit's suggested commit_and_train(mode, feedback) signature
could NOT be behaviour-identical: repositioning() implies placing() (both
live in ExploreState::Placing), so dispatching on feedback state inside the
helper would silently reroute a TogB2 press mid-reposition from commit_place
to commit_reposition, which differs (no snapshot restore, clears
reposition_). Implemented as commit_and_train(mode, feedback, bool
reposition) with the branch decided at the call site, preserving behaviour.
Firmware is not compiled by any gate yet (that arrives with the PlatformIO
migration, plan §5/S9), so this group is verified by reading and grep only.
Gates: run-all-tests.sh ALL GREEN.
2026-07-21 12:49:25 +02:00
|
|
|
log "Initialising git submodule (memllib)"
|
2026-06-28 04:14:30 +02:00
|
|
|
command -v git >/dev/null 2>&1 || die "git is not installed"
|
|
|
|
|
git -C "$REPO_ROOT" submodule update --init --recursive
|
|
|
|
|
|
|
|
|
|
[[ -f "$TFT_SETUP_SRC" ]] || die "TFT setup file missing after submodule init: $TFT_SETUP_SRC"
|
|
|
|
|
|
|
|
|
|
# ---- 2. arduino-cli -----------------------------------------------------
|
|
|
|
|
if ! command -v arduino-cli >/dev/null 2>&1; then
|
|
|
|
|
log "Installing arduino-cli into ~/.local/bin"
|
|
|
|
|
mkdir -p "$HOME/.local/bin"
|
|
|
|
|
curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh \
|
|
|
|
|
| BINDIR="$HOME/.local/bin" sh
|
|
|
|
|
export PATH="$HOME/.local/bin:$PATH"
|
|
|
|
|
command -v arduino-cli >/dev/null 2>&1 || die "arduino-cli install failed"
|
|
|
|
|
warn "arduino-cli was installed to ~/.local/bin — add it to your shell PATH permanently:"
|
|
|
|
|
warn " echo 'export PATH=\"\$HOME/.local/bin:\$PATH\"' >> ~/.bashrc # or ~/.zshrc"
|
|
|
|
|
else
|
|
|
|
|
log "arduino-cli already present: $(command -v arduino-cli) ($(arduino-cli version | head -1))"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# ---- 3. rp2040 core (earlephilhower) ------------------------------------
|
|
|
|
|
# Pass the board index via --additional-urls so we don't mutate the user's
|
|
|
|
|
# global arduino-cli config.
|
|
|
|
|
log "Installing rp2040:rp2040 core (this downloads the ARM toolchain — be patient)"
|
|
|
|
|
arduino-cli core update-index --additional-urls "$RP2040_INDEX_URL"
|
|
|
|
|
if arduino-cli core list 2>/dev/null | grep -q '^rp2040:rp2040'; then
|
|
|
|
|
log "rp2040:rp2040 already installed — checking for upgrade"
|
|
|
|
|
arduino-cli core upgrade rp2040:rp2040 --additional-urls "$RP2040_INDEX_URL" || true
|
|
|
|
|
else
|
|
|
|
|
arduino-cli core install rp2040:rp2040 --additional-urls "$RP2040_INDEX_URL"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# ---- 4. Arduino libraries ----------------------------------------------
|
|
|
|
|
log "Installing Arduino libraries: ${ARDUINO_LIBS[*]}"
|
|
|
|
|
arduino-cli lib update-index
|
|
|
|
|
for lib in "${ARDUINO_LIBS[@]}"; do
|
|
|
|
|
arduino-cli lib install "$lib"
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
# ---- 5. MEMLNaut TFT_eSPI User_Setup ------------------------------------
|
|
|
|
|
log "Configuring TFT_eSPI for the MEMLNaut display"
|
|
|
|
|
USER_DIR="$(arduino-cli config get directories.user 2>/dev/null || true)"
|
|
|
|
|
[[ -n "$USER_DIR" ]] || USER_DIR="$HOME/Arduino"
|
|
|
|
|
TFT_DIR="$USER_DIR/libraries/TFT_eSPI"
|
|
|
|
|
[[ -d "$TFT_DIR" ]] || die "TFT_eSPI library dir not found at $TFT_DIR (did lib install fail?)"
|
|
|
|
|
|
|
|
|
|
TFT_SETUP_DST="$TFT_DIR/User_Setup.h"
|
|
|
|
|
if [[ -f "$TFT_SETUP_DST" && ! -f "$TFT_SETUP_DST.orig" ]]; then
|
|
|
|
|
cp "$TFT_SETUP_DST" "$TFT_SETUP_DST.orig"
|
|
|
|
|
log "Backed up stock User_Setup.h -> User_Setup.h.orig"
|
|
|
|
|
fi
|
|
|
|
|
cp "$TFT_SETUP_SRC" "$TFT_SETUP_DST"
|
|
|
|
|
log "Installed MEMLNaut User_Setup.h into $TFT_DIR"
|
|
|
|
|
|
|
|
|
|
# ---- 6. verify build ----------------------------------------------------
|
|
|
|
|
if [[ "$DO_BUILD" -eq 1 ]]; then
|
|
|
|
|
log "Verifying: compiling the SelfTest firmware"
|
|
|
|
|
if "$REPO_ROOT/scripts/build-firmware.sh" SelfTest; then
|
|
|
|
|
log "SelfTest firmware built successfully ✔"
|
|
|
|
|
echo
|
|
|
|
|
echo "Next: flash it with"
|
|
|
|
|
echo " scripts/flash-firmware.sh"
|
|
|
|
|
echo "or build a normal mode, e.g. scripts/build-firmware.sh PAFSynth"
|
|
|
|
|
else
|
|
|
|
|
die "SelfTest build failed — see the arduino-cli output above. Toolchain/libs are installed; this is a compile error to debug."
|
|
|
|
|
fi
|
|
|
|
|
else
|
|
|
|
|
log "Setup complete (build skipped). Verify with:"
|
|
|
|
|
echo " scripts/build-firmware.sh SelfTest"
|
|
|
|
|
fi
|