One cut, no dual path. Closes ALIGNMENT defect 3 ("Arduino-CLI build
machinery is actively hostile") and vision bullet 4.
platformio.ini carries 16 [env:], one per variant, each passing
-DMEMLNAUT_MODE_TYPE; selftest passes -DNISPS_SELFTEST=1 instead. The env list
IS the registry now — the .ino comment-registry and the NISPS_ST_* token-paste
table are deleted rather than migrated. L12 noted that table was already
silently missing the currently-shipped SLPWorkshop variant, which is the whole
argument against having a second list.
Also deleted: the Python/sed machinery that rewrote the COMMITTED .ino on every
build, the sketch symlink forest, the global TFT_eSPI User_Setup.h mutation
(now -D flags — TFT_eSPI's own documented PlatformIO recipe), the UF2
boot-mount detection stack (upload_protocol=picotool talks to the bootloader
directly), and build-firmware-arch.sh entirely. Scripts 683 -> 435 lines.
memllib is vendored at lib/memllib/ from upstream e291192; no submodules
remain. VENDORED.md records provenance and the re-sync procedure.
S9: a firmware-build CI job compiles three representative envs against a cached
toolchain and reports per-variant flash/RAM. Firmware is in an automated gate
for the FIRST time. The old ci.yml comment justified excluding it as "low
verification value" — an assessment that did not survive contact, since the
SelfTest variant sat broken for an unknown period calling a DisplayDriver
method that did not exist at the pinned memllib commit, and nothing noticed
because nothing built it.
Verified: all 16 envs build from an empty cache, each within ~520 bytes of the
arduino-cli binary it replaces, flash and RAM. Measured as .text+.rodata /
.data+.bss+vector+uninitialized — NOT PlatformIO's console line, which
double-counts .data on this board. This does not prove the hardware boots; no
flash+smoke test was possible and that stays an operator chokepoint.
slpworkshop 248232/145028 pafsynth 256880/149716 selftest 216228/17960
(all 16 in the CI log format; none exceeds 2% of a 16 MB flash)
Two traps recorded so nobody rediscovers them: vendoring memllib's subdirs
without a src/ wrapper makes PlatformIO's library builder silently compile
NOTHING while still linking; and project build_flags land BEFORE the
framework's own -std=gnu++17 -Os, so build_unflags is required.
CORRECTION carried in this commit: the firmware sizes in c19d846's message and
the first version of the memllib recon doc were wrong — SLPWorkshop 145348,
PAFSynth 145300, SelfTest 141840. They came from building variants in sequence
through a SHARED incremental arduino-cli build directory, which reused stale
objects and under-reported by ~75 KB. Clean-cache rebuilds of the identical
commit give 216736/18492 for SelfTest. The real cost of the memllib upstream
bump is +216 bytes flash, not +316. Never measure firmware size through a
reused build dir.
HISTORY NOTE: this commit and the docs commit before it were rebuilt (force-push,
2026-07-21) so that each contains only what its message describes. The first
versions had the firmware deletions stranded in the docs commit by a shared-index
race between concurrent agents; content is byte-identical to the originals.
Gates: run-all-tests.sh ALL GREEN (nisps/ untouched by this change beyond
include paths); 16/16 pio envs build.
4.9 KiB
memllib — vendored, not a submodule
This directory is a vendored copy of the load-bearing subset of
MusicallyEmbodiedML/memllib, the
hardware-abstraction library for the MEMLNaut board (audio driver, TFT display, MIDI
I/O, peripherals). It replaced the src/memllib git submodule during the Phase 4
PlatformIO migration (2026-07-21).
Provenance
- Upstream repo:
https://github.com/MusicallyEmbodiedML/memllib.git - Vendored at commit:
e291192d8e4f2fca7b79670c4df9c2ec8bdf03cd(upstreammain, "l r input swap") - License: MPL-2.0 (
LICENSEin this directory, copied verbatim from upstream)
What was copied, what was dropped
Copied verbatim, directory structure unchanged, under src/: audio/, hardware/,
interface/, synth/, utils/, PicoDefs.hpp. LICENSE sits at this directory's
root (metadata, not source). These are exactly the subdirectories the firmware sketch
used to reach via its symlink forest (firmware/MEMLNaut-NISPS/src/memllib before this
migration) — only the wrapping src/ folder and the library.properties manifest are
new, both required for PlatformIO to discover and recursively compile this tree (see
below).
Dropped: examples/ (17 files — never compiled; the firmware never referenced it, and
its content that mattered was already ported into nisps/ml/{jolt,ou_noise,feedback, geo_push}.hpp per the pre-Phase-4 submodule-bump decision), .git (submodule gitlink),
.gitignore (build-artifact patterns, meaningless once vendored — this repo's own
.gitignore covers it), README.md (described the old Arduino-IDE TFT_eSPI
User_Setup_Select.h copy-paste workflow, which PlatformIO replaces with
-D USER_SETUP_LOADED=1 + explicit build flags in platformio.ini — see there).
98 files, ~1.9 MB total — all of memllib bar examples/; there is no smaller subset to
lift (every one of the 24 .cpp translation units here is reached by at least one
compiled firmware variant).
Internal include convention (do not break)
Files inside this tree include each other with paths relative to src/ as the root
(e.g. src/hardware/memlnaut/MEMLNaut.cpp does #include "../PicoDefs.hpp",
src/hardware/memlnaut/display/View.cpp does #include "../../PicoDefs.hpp").
This directory is consumed as a PlatformIO Arduino-format library (lib/memllib/, with
library.properties + a src/ subfolder — the standard 1.5 Arduino library layout).
PlatformIO's Library Dependency Finder therefore adds lib/memllib/src (not
lib/memllib itself) to the include search path and recursively compiles every source
file under src/.
Do not vendor these five subdirectories directly under lib/memllib/ (i.e. without
the src/ wrapper) — that was tried first and silently compiles nothing: PlatformIO's
ArduinoLibBuilder, when it finds no src/ subfolder, falls back to a non-recursive
"files directly in the library root" scan (the historical Arduino 1.0 library format,
which only special-cases a utility/ subfolder). Nested folders like audio/ or
hardware/ are silently invisible to the build under that fallback — it links, or
rather fails to link, with undefined reference to MEMLNaut::... for every symbol in
this library. The src/ subfolder switches PlatformIO onto the recursive path.
Firmware code outside this tree (../../src/main.cpp, ../../glue/*.hpp) includes
headers here relative to src/ as the root, e.g. #include "audio/AudioDriver.hpp",
#include "hardware/memlnaut/MEMLNaut.hpp" — no memllib/ or src/ prefix, because
lib/memllib/src/ is the include root PlatformIO adds.
Re-syncing with upstream
There is no submodule to bump anymore, so a re-sync is a manual, documented diff:
- Clone upstream at the desired commit:
git clone https://github.com/MusicallyEmbodiedML/memllib.git /tmp/memllib-upstream - Diff the five subdirs +
PicoDefs.hppagainst this directory'ssrc/, e.g.:diff -ru /tmp/memllib-upstream/audio firmware/MEMLNaut-NISPS/lib/memllib/src/audio # ...repeat for hardware/ interface/ synth/ utils/ PicoDefs.hpp - Copy over the changed files (
cp -a), re-rundiff -ruboth ways to confirm nothing outside the tracked subset leaked in and nothing was silently dropped. - Update the "Vendored at commit" line above to the new upstream SHA + its subject line.
- Rebuild every
platformio.inienv (pio run) and diff flash/RAM sizes against the previous vendored commit's numbers — a size jump with no corresponding upstream feature is a signal something unexpected changed. - Commit the vendored-file changes and this doc update together.
If upstream ever restructures these directories (renames, new cross-subdir relative
includes), the internal-include convention above may need re-verification — grep for
#include "\.\./ inside this tree and confirm every relative path still resolves.