From e5b92de3e68ad212f6152f19619296e83d03ff39 Mon Sep 17 00:00:00 2001 From: prosolis <5590409+prosolis@users.noreply.github.com> Date: Thu, 27 Aug 2026 22:09:34 -0700 Subject: [PATCH] Add project plan Architecture, bill of materials, electrical notes and phasing for an LED strip above a Casio PX-S7000, driven as a USB MIDI gadget from the PC. Claude-Session: https://claude.ai/code/session_01TVCB25LBsmeteWvaSMz4Ne --- PIANO-LED-CIRCLE-PLAN.md | 235 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 PIANO-LED-CIRCLE-PLAN.md diff --git a/PIANO-LED-CIRCLE-PLAN.md b/PIANO-LED-CIRCLE-PLAN.md new file mode 100644 index 0000000..f7f86e0 --- /dev/null +++ b/PIANO-LED-CIRCLE-PLAN.md @@ -0,0 +1,235 @@ +# Piano LED Visualizer on Circle (bare metal) + +Kickoff document. Written 2026-08-27. + +Everything below is split into **VERIFIED** (checked against a primary source this +session, with the source named) and **ASSUMED** (plausible, load-bearing, not yet +confirmed). Do not promote an ASSUMED item to fact without checking it. + +--- + +## 1. Goal + +An LED strip above the keys of a Casio Privia PX-S7000 that lights the key being +played, and can light the *next* key to play when driven by learning software. + +Explicit non-goal: this is not a port of `onlaj/Piano-LED-Visualizer`. That project +is ~95% web UI, WiFi hotspot, song library, LCD menu, and MIDI file playback. In this +architecture the PC owns all of that. What remains is a few hundred lines of +greenfield firmware. + +## 2. Architecture (settled) + +``` +Casio PX-S7000 --USB-B--> PC (Debian 13, EliteOne AIO) --USB--> Pi Zero (Circle) --> WS2812B strip + | ^ + +-- Neothesia / sheet reader | + 5V PSU + +``` + +The PC is the hub. It is the only thing that talks to the piano. ALSA's sequencer +fans the piano's input port out to both the on-screen software and the Pi. + +The Pi presents itself to the PC as a **USB MIDI device**. From the PC's side it is +just another ALSA MIDI output port; `aconnect` the piano's input to it and the job +is done. + +### Why this shape + +- **No network stack on the Pi at all.** This was the deciding factor. An appliance + Linux box with an unauthenticated web UI that nobody ever patches is a real home + network risk, not a theoretical one. Circle with a USB gadget has no shell, no + sshd, no web server, no package manager, and no network interface. The category is + deleted rather than mitigated. +- **No 2.4GHz contention.** Pi Zero and Zero 2 W are 2.4GHz-only, which is the same + band as the WU-BT10's BLE. Using USB avoids reasoning about it. +- **Deterministic latency**, no scheduler, no WiFi power-save. +- **Still builds in five years.** A pinned Circle tree does. A Python app with pip + dependencies does not. +- **Power-cycle safe.** No writable filesystem to corrupt when the thing gets killed + at the wall. + +### Consequence to be aware of + +VERIFIED: *"Circle does not support OTG protocols, so the USB controller always works +in host or gadget mode and the connected peer must work in the opposite mode."* +(Circle CHANGELOG) + +So the Pi is gadget-only in this build. It **cannot** also host a USB MIDI device, +which means it can never take MIDI directly from the piano. All MIDI arrives from the +PC. That is exactly the architecture above, so this costs nothing here, but it does +close off "plug the piano into the Pi" as a fallback. + +## 3. Verified facts + +**Circle USB MIDI gadget** +- Introduced in **Release 45.3 (2023-10-06)**: *"This release comes with initial USB + gadget (aka device, peripheral) mode support, which is used to implement an USB + MIDI (v1.0) gadget."* +- Class `CUSBMIDIGadget`, header `include/circle/usb/gadget/usbmidigadget.h`. +- Usage: instantiate `CUSBMIDIGadget` **instead of** `CUSBHCIDevice`, then call + `Initialize()` and `UpdatePlugAndPlay()` as in host mode. +- Supported models: *"Raspberry Pi models (3)A(+), Zero (2) (W) and 4B"*. + **The Zero and Zero 2 W are explicitly supported.** The MiniDexed docs mention only + Pi 3/4; that was incomplete, not a restriction. +- Source: https://github.com/rsta2/circle/blob/master/CHANGELOG.md and + https://circle-rpi.readthedocs.io/en/47.0/subsystems/usb.html + +**Circle WS28XX driver** (`addon/WS28XX/`) +- `ws28xxstripe.cpp/h` - single-channel stripe driver +- `ws2812oversmi.cpp/h` - multi-channel driver over the Secondary Memory Interface +- `sample/` - example programs, including `sample/multichan/` +- Only the single-channel driver is needed here. One strip, one data line. + +**Casio WU-BT10** +- Bluetooth 5.0. Profiles: **A2DP (audio)** and **GATT (MIDI over BLE)**. Codec SBC. +- USB bus powered, 0.25W, no separate supply. +- Not used in this build's MIDI path. Freed up for streaming audio from the PC to the + piano's own speakers over A2DP, if wanted. +- Source: https://support.casio.com/en/support/answer.php?cid=008006001002&qid=104463&num=1 + +**Reference build** (https://yanniznik.com/building-a-led-piano-visualizer/) +- WS2812B, 144 LEDs/m, ~172 LEDs, 1.5-2m strip +- 5V 6A supply, 22-18 AWG wire, 5.5x2.5mm DC socket +- Aluminum LED profile with diffuser, double-sided tape +- ~$75-100 total. Includes a $30 iConnectivity mio MIDI interface **we do not need**, + because the PX-S7000 has USB-B. + +## 4. Assumptions to verify before spending money + +1. **PX-S7000 is class-compliant USB MIDI.** Almost certainly true for a 2022 + instrument, but unconfirmed. Test: plug into the PC, run `aconnect -l`, look for it. + This is step one of Phase 0 and it gates everything. +2. **Exact GPIO/pin for `CWS28XXStripe`.** Circle's single-channel driver is believed + to clock the waveform out over SPI, which would put data on the MOSI pin, but the + pin and the peripheral were not confirmed. **Read `addon/WS28XX/sample/` before + wiring anything.** +3. **Mounting geometry.** See section 8. Unsolved and unbudgeted. +4. **PLV web UI authentication.** Only matters if Phase 0 uses that project rather + than a throwaway script. Assume it is unauthenticated until shown otherwise. + +## 5. Bill of materials + +| Item | Note | +|---|---| +| Raspberry Pi Zero 2 W | Zero W also works. Zero 2 W is current and quad-core. Headers not needed if the LED line is soldered. | +| microSD card | Circle boots from FAT32. Tiny card is fine. | +| WS2812B strip, 144 LEDs/m, 2m | Need 176 LEDs = 1.222m. Buy 2m, cut to length. | +| 5V PSU, 6A | See the power note in section 7 before assuming this is enough. | +| 74AHCT125 level shifter | See section 7. Do not skip this on the assumption 3.3V will work. | +| Aluminum LED profile + diffuser, ~1.3m | The article is right that bare strip looks bad. | +| DC barrel socket 5.5x2.5mm, quick connect | | +| 22-18 AWG wire | Including a second run for power injection at the far end. | +| USB cable, PC to Pi Zero | Micro-USB, and it must be a **data** cable. Plug into the Zero's **USB** port, not **PWR**. | + +Roughly $50-70 with the mio omitted. + +## 6. Note-to-LED mapping + +88 keys span MIDI notes **21 (A0)** through **108 (C8)**. + +At 2 LEDs per key: `176 LEDs`. At 144 LEDs/m that is **1.222m**, which is why 144/m +is the density everyone uses. It lines up with a standard 88-key keybed almost exactly. + +``` +led_index = (note - 21) * 2 +``` + +Light `led_index` and `led_index + 1`. Clamp to `[0, 175]` and drop anything outside +21-108 rather than trusting the input. + +If the strip is mounted with pixel 0 at the treble end, invert: +`led_index = (108 - note) * 2`. Decide this **after** the strip is physically mounted, +and make it a compile-time constant so it is one flag to flip. + +## 7. Electrical notes + +**Power budget, and why 6A is a bet.** +176 LEDs at full white draw ~60mA each = **10.56A theoretical maximum**. A 6A supply +does not cover that. It does not need to: playing a ten-finger chord lights 20 LEDs, +about 1.2A, so 6A is enormous headroom for real use. + +But this makes a **global brightness clamp a firmware requirement, not a nicety**. A +bug that sets every pixel to white on a 6A supply browns out the rail. Cap global +brightness in the firmware, and cap the number of simultaneously lit pixels if you +ever add an idle animation. + +**Level shifting.** WS2812B wants logic high at 0.7 x VDD, which is **3.5V** on a 5V +rail. The Pi's GPIO is **3.3V**. This is marginal by design. It frequently works and +then intermittently doesn't, which is the worst failure mode to debug. Use a +**74AHCT125** on the data line. This is the single most common cause of "the strip +flickers and I don't know why." + +**Common ground.** The Pi's ground and the LED supply's ground must be tied together. +Non-optional. + +**Power injection.** Feed 5V at both ends of the strip. Over 1.2m at 144/m the far end +otherwise drifts dim and yellow. + +**Refresh floor.** WS2812B is 1.25us per bit, 24 bits per LED, so 30us per LED. +176 LEDs = **5.3ms** per full frame, plus a ~50us reset. Max ~190 Hz. Irrelevant for +this application but it is the hard latency floor, and it dominates the USB MIDI +transit time. + +## 8. The actual hard part + +The PX-S7000 is a slab. It has no shelf above the keybed the way an upright piano +does. The aluminum profile has to sit above the keys, parallel to them, at a fixed +offset, with nothing obvious to attach to. + +**This is the part of the project that will consume the most time, and it is not a +software problem.** Solve it, or at least prototype it in cardboard, before ordering +electronics. Take measurements off the actual instrument. + +## 9. Phases + +### Phase 0 - bench validation on Linux (disposable, one afternoon) + +The purpose is to separate "my wiring is wrong" from "my firmware is wrong." On the +first power-up of a strip you soldered yourself, that is the only question that +matters, and bare metal gives you no debugging surface to answer it. + +Throw all of this away afterwards. It never gets mounted, never gets a hostname, +never sits on the LAN permanently. + +1. Piano to PC over USB-B. Confirm it appears in `aconnect -l`. +2. Pi Zero on Raspberry Pi OS, `rpi_ws281x`, Python REPL. +3. Confirm: pixel 0 is at the end you think it is; `(note-21)*2` lights the right key; + the far end holds voltage under load; the colors look right *through the diffuser*, + which is not the same as how they look bare. +4. Decide brightness, colors, and whether velocity modulates anything. These are + product decisions, and answering them here means the firmware gets written once. + +Exit criteria: you know the hardware is good and you know exactly what you want the +firmware to do. + +### Phase 1 - Circle firmware + +1. Clone Circle, pin the release, build a stock sample for the Zero to confirm the + toolchain and the boot path. +2. Build and run `addon/WS28XX/sample/`. **Read it first for the pin assignment.** +3. Build and run a USB MIDI gadget sample. Confirm the PC enumerates it and it shows + up in `aconnect -l` on the PC side. +4. Join the two. `CUSBMIDIGadget` in, `CWS28XXStripe` out, the mapping from section 6 + in between. +5. Global brightness clamp per section 7. +6. Compile-time flag for strip orientation. + +The whole thing is a few hundred lines. The risk is in steps 2 and 3, not step 4. + +### Phase 2 - mechanical + +Per section 8. Can proceed in parallel with Phase 1, and should, because it has the +longest lead time and the most unknowns. + +### Phase 3 - learning integration + +Lighting the *next* note to play, driven by software on the PC. Deliberately deferred. +It is a protocol design question and it depends on what the PC ends up running. + +## 10. Sequencing warning + +At time of writing there is **no PC**. The EliteOne 800 G5 negotiation is unresolved. +Phase 0 step 1 cannot start without a machine. + +Phase 2 does not depend on the PC and can start immediately with a tape measure.