# Piano LED Visualizer — Circle firmware Bare-metal firmware for a Raspberry Pi Zero that lights a WS2812B strip above an 88-key keybed in response to MIDI. The design rationale, bill of materials, electrical notes and project phases live in [PIANO-LED-CIRCLE-PLAN.md](PIANO-LED-CIRCLE-PLAN.md). This file covers only how to build and run the firmware (plan Phase 1). ## What this is The Pi is a **USB MIDI gadget**. The PC is the host and owns everything else — the piano connection, the learning software, the song library. From the PC's side this firmware is just another ALSA MIDI output port: ``` Casio PX-S7000 --USB-B--> PC --USB--> Pi Zero (this firmware) --> WS2812B strip ``` There is no network stack, no filesystem, no shell. The Pi boots into this firmware in about a second and does one job. Circle has no OTG support, so the USB controller is gadget-only here. The piano **cannot** be plugged into the Pi directly; all MIDI arrives from the PC. ## Layout | Path | | |---|---| | `firmware/config.h` | Every tunable. Start here. | | `firmware/pianoleds.cpp` | Note-to-LED mapping, colour, brightness clamps. | | `firmware/kernel.cpp` | USB gadget lifecycle and the main loop. | | `tests/` | Host-side tests for the mapping and clamps. | | `circle/` | Circle as a submodule, pinned to `Step51`. | | `build.sh` | Builds both kernel images. | ## Building Circle is a submodule pinned to `Step51`, so clone recursively: ```sh git clone --recursive # or, in an existing clone: git submodule update --init ``` Then: ```sh sudo apt-get install gcc-arm-none-eabi # Debian/Ubuntu ./build.sh ``` The pin is deliberate. A pinned Circle tree still builds in five years; nothing here tracks a moving upstream. This produces two images in `boot/`, which coexist on one card: | Image | `RASPPI` | Model | |---|---|---| | `kernel.img` | 1 | Pi Zero / Zero W (ARM1176) | | `kernel7.img` | 2 | Pi Zero 2 / Zero 2 W (Cortex-A7) | The Pi picks the right one at boot, so the same SD card runs on either model. ## SD card FAT32, single partition. Copy in: - `boot/kernel.img` and `boot/kernel7.img` - From the [Raspberry Pi firmware repo](https://github.com/raspberrypi/firmware) `boot/` directory: `bootcode.bin`, `start.elf`, `fixup.dat` - `cmdline.txt` (optional). Circle reads kernel options from it; useful for raising the log level while bringing the board up. Nothing is ever written to the card at runtime, so pulling the power mid-note cannot corrupt it. ## Wiring Verified against `circle/addon/WS28XX`: `CWS28XXStripe` clocks the WS2812B waveform out over SPI at a fixed 6.4 MHz, encoding one LED bit per SPI byte. On SPI master device 0 that puts the data line on: **MOSI = GPIO10 (BCM) = physical pin 19.** Three things from plan section 7 that are not optional: - **Level shifter.** WS2812B wants logic high at 0.7 × VDD = 3.5V on a 5V rail; the Pi's GPIO is 3.3V. Put a **74AHCT125** on the data line. Skipping this is the single most common cause of "the strip flickers intermittently". - **Common ground.** The Pi's ground and the LED supply's ground must be tied. - **Power injection.** Feed 5V at both ends of the strip. Connect the PC to the Zero's **USB** port, not **PWR**, with a data cable. ## Configuring Everything is in `firmware/config.h`, and every value there is a Phase 0 question. Two are load-bearing: - **`STRIP_REVERSED`** — whether pixel 0 sits at the bass or treble end. Decide this *after* the strip is physically mounted; it is one flag to flip. - **`GLOBAL_BRIGHTNESS` and `MAX_LIT_KEYS`** — the power clamps. 176 LEDs at full white would draw ~10.5A against a 6A supply. Real playing never comes close, but these two make a whited-out strip *unreachable* rather than merely unlikely. Do not raise them without redoing the arithmetic in plan section 7. Any of them can also be overridden at build time without editing the file: ```sh make -C firmware EXTRADEFINE=-DSTRIP_REVERSED=1 ``` ## MIDI behaviour - Notes 21–108 (A0–C8) map to the strip; anything outside is dropped. - Note On with velocity 0 is treated as Note Off. - CC 120 (All Sound Off) and CC 123 (All Notes Off) clear the strip. - Notes on `HINT_MIDI_CHANNEL` (default channel 16) light in a separate colour, for the plan's Phase 3 "light the next key to play". A key actually being played takes precedence over a hint on the same key. - Notes held when the USB host suspends are cleared, so nothing stays lit. ## Tests ```sh ./tests/run.sh ``` Compiles the real `firmware/pianoleds.cpp` against stubbed Circle headers and exercises the mapping, the note-off paths, the range clamping and both power clamps across nine configuration variants. This does not need the ARM toolchain and does not replace bench-testing on real hardware — it checks the arithmetic, not the wiring.