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
This commit is contained in:
@@ -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.
|
||||||
Reference in New Issue
Block a user