Chirp: Building a Hot-Reloadable MIDI Router on Teensy with Wren Scripting

What Chirp Actually Does

Most hardware MIDI routers are fixed-function: patch X to Y, done. Chirp takes a different approach. It runs a small scripting VM on a Teensy microcontroller connected to a 5×5 MIDI breakout board — five inputs, five outputs — and lets you write routing logic in Wren scripts that you can update live, without interrupting MIDI flow or rebooting the device. Each script has access to all ports and can call into other scripts, which makes it closer to a programmable patch bay than a static router.

Why Wren and Not Lua

Lua is the obvious answer for embedded scripting. It has a long track record on constrained hardware, solid documentation, and plenty of examples ported to microcontrollers. Wren is newer and less common in that space, but it has a few properties that make it an interesting fit here.

Wren is class-based rather than prototype-based like Lua, which makes script organization more predictable when multiple scripts need to coexist and call each other. Its VM is small — designed to be embedded in games and tools — and the garbage collector is tunable, which matters on a microcontroller where heap pressure is real. The language was designed by Bob Nystrom, who documented the entire interpreter construction process in his book Crafting Interpreters, so the internals are unusually well-understood for a niche language.

Getting Wren’s GC to behave on Teensy hardware requires adjustment. The default heap growth settings are calibrated for desktop environments where a few extra megabytes cost nothing. On a Teensy the limits are tighter, so setting a smaller initial heap size and a conservative growth factor is one of the first things to tackle when porting Wren to a microcontroller. The Chirp project documents the settings it landed on, which is a useful starting point.

Storage: SD Card With Flash Fallback

A scripting engine on a Teensy runs into a storage problem fast. Internal flash on most Teensy boards is not designed for frequent small-file writes, and if you are generating and minifying .wren files at build time and pushing updates regularly, available space goes quickly.

Chirp solves this with an SD card reader. Scripts live on SD by default; if SD initialization fails at boot, the system falls back to internal flash automatically. The .wren files are generated and minified during the build process and placed on the device without manual file management. That two-tier approach is sensible for something that lives in a MIDI rig where silent failures are unacceptable.

The minification step matters. Wren source files are readable and commented at development time, then stripped down to the minimum before being written to the device. You get maintainable source without paying the storage cost of whitespace and comments at runtime.

The arduino-cli and platform.txt Problem

Building Teensy projects with arduino-cli instead of the Arduino IDE is workable, but there is a bug in the Teensy core (present at least through version 1.60) that causes extra build parameters passed via arduino-cli to be silently ignored. The root cause is in the Teensy platform.txt file, which does not pass those parameters through in its build steps.

The fix is a manual patch to platform.txt. Not ideal, but it works until PJRC ships an update. If you are setting up a Makefile-driven Teensy build and your custom compiler flags seem to have no effect, that is the first place to look. Filing a detailed issue — naming the specific flags that get dropped and showing the relevant platform.txt sections — gives the maintainers what they need to act on it.

The Makefile approach itself is worth noting. It keeps the build pipeline independent of any IDE, so you can use VS Code, CLion, Vim, or anything else without fighting a project format. arduino-cli handles the actual compilation; the Makefile just orchestrates it.

The Real Use Case: CC Remapping a Broken Beatstep Pro

The NDS.wren script in the Chirp repo is a concrete example of what this system is actually for. The starting problem: an Arturia Beatstep Pro with a burned USB interface. The BSP is only configurable over USB, so the 16 knobs on the control section are stuck sending CC values that cannot be changed. The MIDI channel is still configurable, which is the hook the solution uses.

The target synth is a Novation DrumStation, a synthesizer with a large number of addressable parameters spread across multiple instruments and voices. The mapping works like this: 16 CCs on 16 channels gives 256 unique addresses, which get mapped to the full DrumStation parameter set via a JSON file the script loads at startup. A small OLED display shows the current parameter name and the adjacent ones in the map, so navigating to a specific instrument and parameter doesn’t require memorizing anything.

No computer in the signal path. No DAW open. No restart to change the mapping — edit the script or the JSON, push it, keep playing.

Architecture Notes Worth Borrowing

A few patterns in Chirp are worth adopting for similar embedded scripting projects:

  • Hot-reload scripting without connection loss is a genuine workflow improvement in a live MIDI context. The ability to update routing logic mid-session changes what the device can do.
  • Inter-script communication — each script can access all others — lets you build modular setups where one script handles clock, another routing, another display, without tight coupling between them.
  • Generating and minifying scripts at build time, rather than writing them directly to the device, keeps on-device code lean while letting you maintain readable source in version control.
  • The SD card with flash fallback pattern is worth reusing in any embedded project where config or script files get written. Flash wear limits and capacity constraints at this scale are real.

The project is open source at github.com/RomanKubiak/chirp. The codebase is being cleaned up from its LLM-assisted origins, so the code quality is actively improving. If you run a complex MIDI rig that needs more than a fixed patch bay, it is worth looking at as both a usable tool and a template for this kind of architecture.

Sources

Related Articles

Similar Posts