ARIA-88 Build Manual
88

ARIA-88

Autonomous Robotic Instrumental Apparatus  /  Build Manual  /  Rev 1.3
CHANNELS
96
RAIL
24V
BUILD A
$2166
ERC
CLEAN
Front elevation of the instrument: strings and tuning pins across the top, the sampled hammer action beneath them, the full 88-note keyboard, and the magenta bank of 44 solenoid coils and plungers below the keybed. Ray-traced cutaway of the finished instrument: an upright piano case rendered as a transparent shell, with the 88-note keyboard, the cyan 96-channel solenoid rail directly beneath it, and the magenta electronics bay holding six driver boards and the power supply.
FINISHED INSTRUMENT · 1504 × 720 × 1270 mm
CASE · KEYBED · 96-CHANNEL RAIL · DRIVER STACK · PSU
3912 EDGES
MODEL ARIA-88 holographic build · hardware/models/aria88-holographic.glb PARTS 88 keys · 37 sharps · 44 sampled actions · 44 solenoids · 3 pedals WIREFRAME 3912 polylines, WebGL2, drag to orbit RENDER Blender 5.2 EEVEE, 160 samples, headless The cutaway is a model built for this instrument: 88 keys and 37 sharps, the sampled hammer action with its strings and tuning pins, and the retrofit itself as 44 solenoid coils, plungers, brackets and data wires under the keybed. Cyan is the instrument, magenta is the electronics. It is scaled to a true 1504 mm studio upright. Dimensioned drawings are §05 and §06 — this is the illustration, not the source of truth.
01 / THE INSTRUMENT
What gets built

A stranger walks up to an upright piano, says "play something sad in D minor", and the keys move. No screen, no app. Real strings, real hammers, and the only interface is speaking to it.

Ninety-six channels under the keybed — 88 keys, a damper pair, spares. Six driver boards on one bus, one Teensy, a 24 V rail, and a speech pipeline that turns a sentence into a schedule of solenoid pulses.

THE ONE CHANGE THAT IS NOT OPTIONAL
The originating proposal specified TPIC6B595 shift registers to drive the coils. That part is rated 150 mA continuous per output; the solenoid draws ~750 mA. It fails on the first chord. Use a plain 74HC595 carrying signal only, into a discrete logic-level MOSFET per note.
TWO BUILDS, ONE RAIL
BuildWhereBrainAll-in
A — connectedGallery, venue, homeRaspberry Pi 5, cloud speech2166
B — offlineFestival, desert, no signalJetson Orin Nano, all local2489

The actuator half is identical, so building A first and swapping the brain later costs nothing extra and gets sound out six weeks sooner.

WHAT ELSE CHANGED FROM THE PROPOSAL
  • Prices were two years stale. Pi 5 8GB is $175, not the $95 MSRP; the Jetson went $249→$399 in July 2026.
  • The piano was not in the BOM. Nor moving it, tuning it twice, the damper actuator, fusing, or an E-stop. About $795 unaccounted.
  • Velocity was one line and is really a week. It is a scheduling problem, not a duty cycle — see §09.
  • What it got right: the 24 V rail is correctly sized, and one Teensy genuinely beats the two- and three-MCU splits the reference builds use.
02 / HOLOGRAPHIC PRE-VIZ
Live · WebGL2 · no libraries

The rail as the control surface sees it. Fifty-two white keys, thirty-six black, each depressing and bleeding light when its coil engages, over a strip showing all 88 channels of the bus. Two channels are faulted — ch44 and ch67 — exactly as the reference build reports them.

SWEEP · 01→88 @ 22 ms
CHANNEL ARRAY / 88 + DAMPER
ENGAGED 00 IDLE 86 FAULT 02
WHAT THE PRE-VIZ IS FOR
  • Simultaneity, seen. Run CHORD and watch the engaged count. The supply carries 33 at once; MIDI files routinely ask for more, which is why admission control exists in §09.
  • Fault legibility. A dead channel has to read as dead from across a room, not as a note that happened not to play.
  • Sweep timing. 01→88 at 22 ms is the bring-up test in Stage 4. If it looks wrong here it will sound wrong there.
  • PURGE. The E-stop path. Everything releases, the bus goes cold, and the interface says so rather than going quiet.
03 / REQUIREMENTS
Testable · numeric

Musical

R1All 88 keys individually actuated, MIDI 21–108. Out of range is dropped, not clamped.
R2Minimum 10 simultaneous notes; design target 24.
R3At least 8 distinguishable dynamic levels per note, monotonic with velocity.
R4Damper pedal actuated from CC 64.
R5Chord notes sound within ±10 ms of each other.
R6The piano still plays by hand, normally, with the system installed.

Interaction

R7Spoken request to first note: <3 s (A), <5 s (B).
R8Speech captured at 3 m with crowd noise.
R9Build B runs with no network of any kind.
R10Unrecognised request degrades to a spoken reply, never to silence.

Electrical

R1124 V rail, ≥25 A, separate from 5 V logic. Grounds bonded at one point.
R12Each channel switches ≥1 A at 24 V and clamps its own flyback.
R13Teensy is 3.3 V and not 5 V tolerant — level shifting mandatory.
R14Rail fused; E-stop kills 24 V only so logic survives to report why.

Safety — violating these damages hardware

R15No coil energised longer than 10 s. The coil overheats at ~40 s.
R16Never more than 30 channels energised at once.
R17All channels off on boot, USB loss, timeout and watchdog — output-enable held safe by a physical resistor.
04 / SCHEMATICS
KiCad 10 · ERC clean
BoardQtySheetPartsNetsERC
16-channel solenoid driver6A291580
Controller — Teensy, level shift, 5 V1A49150
One channel · ×96
Shift register carries signal only. The MOSFET carries the coil current.
+24V JF-1039B 24 V · 25 N ~750 mA measured 1N5408 flyback mandatory CH1_LO Q1 · IRLZ44N logic-level, 55 V 47 A CH1_G 100 R from 74HC595 Qa signal only 100 k
Driver board · ×6
One IDC-10 bus in, straight out to the next board
BUS IN IDC-10 from prev U1 74HC595 8 outputs QH' U2 74HC595 8 outputs BUS OUT IDC-10 to next ×6 boards = 96 channels R98 / R99 pull ~OE and ~SRCLR high — outputs are Hi-Z at power-on 16 gate signals · · · CH1 CH2 CH3 CH14 CH15 CH16 each = 100 R + 100 k + IRLZ44N + 1N5408 + screw terminal 24 V IN 30 A fuse 2 × 4700 µF to all 16 solenoids bulk absorbs the strike transient
Controller · ×1
3.3 V in, 5 V out — the buffer is why this board exists
Teensy 4.1 3.3 V logic not 5 V tolerant SER SRCLK RCLK ~OE 74HCT125 4 × buffer HCT reads 3.3 V as HIGH at 5 V V_CC SER_IN SRCLK RCLK ~OE IDC-10 to 6 driver boards daisy-chained +5V R-78E5.0-1.0 +24V +5V same net logic rail from 24 V, not USB — defined state with no host attached +24V 2.18 V → Teensy ADC 100 k 10 k
SAFETY IN COPPER, NOT FIRMWARE
R98 pulls the registers' ~OE high, so outputs sit high-impedance at power-on. With the per-channel gate pulldowns every MOSFET is off before any code runs — R17 satisfied by two resistors rather than by trusting firmware. R99 holds ~SRCLR so a floating bus cannot clear the registers mid-note.
ERC — 469 TO 0
pass 1 469 violations · 339 off-grid endpoints
pass 2 91 · snapped placements to 2.54 mm
pass 3 34 · emitted project symbol library
pass 4 0 · footprint names corrected for KiCad 10
check netlists agree: 58 nets · GND 49 · +24V 35
>_

The 339 off-grid endpoints are the ones that mattered: pins that only looked connected, producing a schematic that renders perfectly and a netlist that is wrong.

05 / MECHANISM
build123d · hidden-line from the solid

Every drawing here is a projection of the actual solid — visible edges in flux blue, hidden in steel. Change a constant at the top of hardware/cad/piano_rig.py and all of them follow, along with the STEP files a fabricator would receive.

JF-1039B · isometric
Open frame, 24 V, 25 N, 10 mm stroke
JF-1039B · front
Yoke, bobbin, plunger at rest
Rail · 8 of 96
Two staggered rows — 88 will not fit in one
Assembly · side elevation, trimmed to the mechanism
Key as a lever · balance pin · solenoid under the tail · the 0.5 mm lost-motion gap
FURTHER ANGLES
Solenoid · plan
Mounting ears, M3 at 46 mm centres
Solenoid · side
Yoke depth and plunger extension
Key · isometric
480 mm lever with the balance-pin mortise
Rail · front elevation
Two rows, seen end-on
Rail · plan
The stagger, and why one row will not fit
Assembly · isometric
Key, pivot and solenoid in space
GEOMETRY
ConstantMeaningmm
SOL_L × W × HOpen-frame envelope32·22·20
STROKEPlunger travel10.0
KEY_LPiano key, front to tail480.0
PIVOT_XBalance pin from the front180.0
PUSH_XWhere the plunger meets the key330.0
LOST_MGap — the whole of Stage 50.5
KEY_PITCHWhite key spacing23.5
Why two rowsA 32 mm solenoid body on a 23.5 mm key pitch does not fit in a single row. The rail is two-deep and staggered, which is what the drawing above proves rather than asserts.
THE LEVER, IN ONE SENTENCE

Pushing up behind the balance pin is identical to pressing down in front of it. That is why the solenoids hide under the keybed, why the piano still plays by hand, and why nothing is visible from the front.

hardware/cad/piano_rig.py — excerpt
# plunger tip reaches SOL_H + STROKE above the base;
# drop the solenoid so it lands LOST_M below the key
z = -(SOL_H + STROKE) - LOST_M
sol = solenoid().locate(Location((PUSH_X, 0, z)))
shell
~/.claude/skills/render/.venv/bin/python3 hardware/cad/piano_rig.py
16:31:02 solenoid bbox 46.0 × 22.0 × 30.0
16:31:03 assembly bbox 480.0 × 66.0 × 50.5
16:31:05 rail bbox 231.0 × 74.0 × 40.0 · 8 channels
16:31:06 wrote step, glb, svg ×5
>_
06 / THE ACTION
What the solenoid is actually pushing

A key is not a switch. Pressing it starts a chain of seven linked parts that ends with a felt hammer thrown at a string and a damper lifted off it — and the retrofit has to drive that chain without altering any of it. Every figure below names the parts a technician names, and every regulation figure is the published vertical-action spec, not an estimate.

Reference drawing · named parts and regulation
Force path 1→9 · damper and return 10→14 · retrofit 15–16
UPRIGHT ACTION — SIDE ELEVATION, ONE NOTE FRONT ←→ BACK · STRINGS VERTICAL AT THE BACK · STOCK PARTS BLUE, DAMPER TRAIN GREEN, RETROFIT MAGENTA FORCE PATH KEY — the lever, 470 mm BALANCE PIN — the fulcrum CAPSTAN — screws, takes up lost motion WIPPEN — rides the capstan JACK — escapes at let-off HAMMER BUTT — pivots on its flange SHANK — 125 mm HAMMER HEAD — felt STRING DAMPER + RETURN DAMPER SPOON — on the wippen back DAMPER LEVER → HEAD BRIDLE STRAP + BUTT SPRING BACKCHECK — catches the butt tail HAMMER REST RAIL RETROFIT — TWO PARTS SOLENOID JF-1039B, 24 V FELTED ROD TIP 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 REGULATION KEY DIP 10.0 mm BLOW 47 mm LET-OFF 2 mm DROP 1.5 mm BACKCHECK 15 mm DAMPER LIFT ½ travel LOST MOTION 0.5 mm set individually, ×88 — Stage 5 MAGENTA BAND: where a solenoid may sit — behind the pin, clear of the capstan THE RETROFIT ADDS TWO PARTS AND ALTERS NONE — EVERY STOCK COMPONENT ABOVE THE KEY STAYS STANDARD, WHICH IS WHAT KEEPS R6 TRUE
THE STROKE, IN THREE STATES

Modelled as solids and projected, so the linkage positions are geometry rather than illustration. Watch the hammer: it is thrown, not pushed — the jack escapes 2 mm before the string and the hammer completes the last of its travel on momentum alone.

1 · At rest
Hammer on the rail, 47 mm from the string, damper down
2 · Let-off
Key 8.2 mm down, jack about to trip, damper already lifted
3 · Strike
Key at full 10 mm dip, hammer at the string
Action · isometric
The same parts as solids, so the thicknesses read
WHY EACH ONE MATTERS TO THE RETROFIT
PartWhat it does to you
CapstanThis is where lost motion is normally taken up. Your rod adds a second lost-motion adjustment below the key — set the capstan first, then the rod.
Jack / let-offThe hammer is free for the last 2 mm. Pulse energy past let-off does nothing except heat the coil.
BackcheckCatches the hammer 15 mm from the string. If the key is held down by a coil, the hammer stays checked and cannot repeat — this is why R15's auto-off matters musically, not just thermally.
Damper spoonLifts at about half key travel, so a half-pressed key sustains without sounding. A weak pulse can lift the damper and never strike.
Bridle strapReturns the hammer. It is also the first thing to perish on an old upright — check all 88 before you blame your firmware.
THE KEYBOARD ITSELF

One octave at true widths — seven naturals at 22.4 mm and five sharps at 11 mm, the sharps starting 95 mm behind the key fronts. This is the pitch the rail has to fit under, and it is why the solenoids sit in two staggered rows.

Octave · plan
C to B · 157 mm across · sharps offset per the standard layout
Octave · isometric
Sharps standing 10 mm proud of the naturals
THE REFERENCE BUILDS DO NOT DOCUMENT THIS
Only one of the four prior-art builds includes an action diagram, and the one it links is a grand action — a different mechanism with a repetition lever, on a piano whose strings lie horizontal. An upright has no repetition lever; it returns the hammer with a bridle strap and a butt spring. If you are working from those repos, this is the drawing they are missing.
07 / PARTS
Quoted 21 Aug 2026 · or allowance
PartNumberSpecQtyExt $Basis
Core actuator rail — both builds
Key solenoidJF-1039B24 V, 25 N, 10 mm. Rated 400 mA, measures ~750 mA96288ALLOW
Damper solenoidJF-1050BTwo in series — one lacks the travel218ALLOW
Power MOSFETIRLZ44NPBFInfineon logic-level, 55 V 47 A, TO-220AB · $0.7642 @100, 26,228 in stock10076QUOTED
Shift registerSN74HC595NTI 16-PDIP · signal only · $1.37 @102027QUOTED
Level shifterSN74HCT125NTI · HCT not HC · $0.9255QUOTED
Flyback diode1N54083 A 1000 V, one per coil · $0.1331 @10010013QUOTED
Gate passives100 R series + 100 k pulldown per channel20010ALLOW
Driver PCB6 × 16-channel 2-layer, includes one respin185ALLOW
Connectors5.08 mm terminals, IDC headers135ALLOW
MicrocontrollerTeensy 4.1600 MHz M7, native USB MIDI · $31.50 in stock · one spare263QUOTED
Power supplySE-600-24Mean Well 24 V 25 A 600 W · $89.95, 86 in stock190QUOTED
Bulk capacitance4700 µF 50 V at each board422ALLOW
Protection30 A ANL fuse, mushroom E-stop, DC distribution145ALLOW
Harness + bus20 AWG ×500 ft, 8 AWG ×25 ft, ferrules1132ALLOW
Mounting + feltUnder-keybed board, rod extensions, damper felt1135ALLOW
CORE RAIL1045
Build A — cloud connected
ComputeRaspberry Pi 5 8GB$175 in stock — proposal carried the $95 MSRP1175QUOTED
Pi accessoriesCooler, 64 GB A2 card, 27 W supply135ALLOW
Mic arrayreSpeaker XVF38004-mic, AEC + beamforming, 5 m · $60.99161QUOTED
NetworkWi-Fi 6 router + LTE fallback155ALLOW
BUILD A ADD-ON326
Build B — fully offline
Edge computeJetson Orin Nano Super$399 · DC-native, so no inverter off-grid1399LISTED
Directional micShotgun or boundary — crowd noise defeats a 360° array175ALLOW
Local corpus1 TB NVMe: MIDI library, Whisper, Ollama weights185ALLOW
Sealed enclosureNEMA box, MERV-13, positive pressure190ALLOW
BUILD B ADD-ON649
The instrument — absent from the original BOM entirely
Used upright pianoFree to $400. Play it before you accept it1200ALLOW
Piano movingLocal professional, +$40–100 per stair flight1275MARKET
Tuning ×2Before install and after — the move alone detunes it2200ALLOW
Shop toolsCrimper, DMM, current-limiting bench supply1120ALLOW
INSTRUMENT & LOGISTICS795
THE COUNTERFEIT QUESTION, SETTLED
Every cheap marketplace listing for the IRLZ44N is a house-brand re-mark — UMW, MNS, HXY — and the cheapest non-remarked part there is $0.7848 for the wrong package. Genuine Infineon from a franchised distributor is $0.7642. Cheaper and authentic, so there is nothing to trade off. The failure mode is a latched-on coil inside a wooden instrument.
08 / BUDGET
USD · Aug 2026
ConfigurationCost+15%Proposal
Build A — connected21662491840
Build B — offline248928621260
Build B + off-grid power33393839

The 15% contingency is for a first build: one PCB respin, a bag of counterfeit MOSFETs, a solenoid batch that measures differently than the sample. The proposal was not padded — it was scoped to electronics only. About half the gap is the instrument and its logistics; most of the rest is 2026 silicon pricing.

SPEND ORDER — EACH TRANCHE GATED
WhenWhat$Gate
Wk 15 solenoids, 2 damper units80none — this is the gate
Wk 1–2One channel of parts40S1 passed
Wk 3PCB + one board's parts120S2 passed
Wk 5The big order455S3 passed
Wk 6Piano + move475
Wk 8Compute, mic, network326
Wk 12Build B only649if going offline
09 / BUILD GUIDE
10 stages · 13 weeks · ~180 h

Each stage ends in a test you can pass or fail. Nothing advances on a stage that has not passed — least of all Stage 1, which exists to stop you buying ninety-six of the wrong part.

S1

Qualify the solenoid

Wk 1 · $80 · 3 h

You needBench supply with current limit · DMM · kitchen scale · stopwatch

  1. Order five JF-1039B and two JF-1050B. Nothing else. Three to six weeks if they ship from China — order the day you decide to build.
  2. 24.0 V, limit 1.5 A. Energise one second and read the current. Expect ~750 mA; the datasheet says 400 and is wrong. Every power calculation downstream uses your number.
  3. Force: plunger on a kitchen scale, energise, read the peak. Expect ~2.5 kg.
  4. Thermal: energise continuously and time until too hot to hold. Expect ~40 s. This sets the firmware auto-off. Use a quarter of it.
  5. On the piano, clamp one under a key tail behind the balance rail with ~0.5 mm gap and pulse it by hand.
  6. Find the shortest pulse that sounds the note at all — the pianissimo floor.
  7. Find the pulse that plays it fortissimo. Longer only makes heat. Those two bracket the whole velocity range.
Gate

One key plays soft and loud on demand, with four numbers written down. If force is under ~15 N or thermal under 20 s, change the part now — before $288 of solenoids arrives.

S2

One channel, end to end

Wk 1–2 · $40 · 4 h

You needTeensy · 74HCT125 · 74HC595 · IRLZ44N · 1N5408 · oscilloscope

  1. Build the chain from §04 exactly.
  2. Before connecting any coil, power the logic with the Teensy unprogrammed and measure the gate. It must read 0 V. If it floats, the pulldown is wrong and every later stage is unsafe.
  3. Fit the flyback diode before the coil, not after.
  4. Scope across drain and source. Fire one pulse. With the diode fitted the drain clamps near 24.7 V; without it you would see hundreds. That is why you do not test without it.
  5. Ten thousand cycles at 1 Hz. Watch for resets; feel the MOSFET.
Gate

Key plays over USB MIDI, flyback visibly clamped, no brownouts, cool MOSFET. R12 and R13 demonstrated.

S3

The driver board

Wk 3–4 · $120 · 10 h
  1. Lay out from §04, or adapt a published reference layout — that can remove a week and a respin.
  2. Close the sourcing gap first. The schematic ships with only four of twenty-seven parts carrying MPNs. A fab house will not fill them in and a wrong footprint is a respin.
  3. Order seven boards, not six. The marginal cost is nearly zero.
  4. Pass one — signal only. Registers, decoupling, pull-ups, bus connectors. No MOSFETs. Hang LEDs off the sixteen outputs and shift a pattern through.
  5. Pass two — power. Only once the chain is provably correct, fit MOSFETs, gate networks, diodes, terminals.
  6. Burn in ten minutes, all sixteen. Measure the rail at the board, not at the supply.
Gate

Sixteen channels simultaneous for ten minutes, no thermal event, no rail sag. Budget one respin.

S4

All 96 channels on the bench

Wk 5–6 · $455 · 16 h
  1. Assemble the remaining five boards.
  2. Build the harness: 96 pairs, ferruled and labelled at both ends. Nobody has regretted over-labelling a 96-way loom.
  3. Power path: PSU → 30 A fuse → E-stop → distribution → six boards in 8 AWG. Bond signal to power ground at exactly one point.
  4. Bulk capacitors at each board, not at the supply — they hold the rail up during the strike, and that only works locally.
First power-on — this order and no other

a. E-stop pressed. b. Teensy disconnected. c. Energise and confirm every gate reads 0 V. d. Release the E-stop; confirm nothing twitches. e. Connect a Teensy running firmware that does nothing; confirm still nothing. f. Only now load the driver firmware.

If anything moves at c, d or e, stop. That is R17 failing, and it is the failure that burns coils.

  1. Implement the safety layer from §09.
  2. Then try to break each rule deliberately: forty simultaneous notes, a note-on with no note-off, USB yanked mid-chord. Each must fail safe.
Gate

Every channel addressable, and R15–R17 each demonstrated by actively trying to violate them.

S5

Mechanical integration

Wk 6–9 · $180 · 20–30 h

This is the stage that decides whether the project finishes. Ninety-six individual adjustments. It cannot be rushed, delegated or parallelised — give it three weeks of calendar and schedule nothing against it.

  1. Photograph everything before you touch it.
  2. Remove the action, labelling as you go. It only goes back one way.
  3. Measure on the instrument: front to balance pin, pin to push point, key tail travel. The lever ratio tells you how much stroke you actually need.
  4. Build the mounting board. Two staggered rows — §05 shows why one will not fit.
  5. Mark drill positions from the keys themselves, not from a drawing. Key spacing on a real piano is not uniform and a drawing is wrong by the third octave.
  6. Drill. This is the irreversible step.
  7. Fit rod extensions and set each tip 0.5 mm below its key at rest.
  8. Set lost motion on all eighty-eight. Too much and the note is late and weak; too little and the key never returns and will not repeat. This is the twenty hours.
  9. Felt every contact. Silicone-grease the action pins. The click is the difference between an instrument and a machine.
  10. Reinstall and play it by hand. If the touch feels different, you are not finished.
Gate

All 88 play from the rail, the piano still plays normally under your fingers (R6), no audible clack.

S6

Damper pedal

Wk 9 · $18 · 3 h
  1. Wire the two JF-1050B in series. One alone lacks the travel — a known trap that costs an evening.
  2. Mount near the pedal lever fulcrum, pushing down on the bottom board.
  3. Drive from channel 89, triggered by CC 64.
  4. Confirm the pedal still works under a foot.
Gate

Sustain audibly changes the decay. Cheapest musical improvement in the build.

S7

Voice pipeline · parallel from week 2

Wk 2–10 · 25 h

This is why the schedule is thirteen weeks and not nineteen. The stack targets a virtual MIDI port, so it is built and tested from week two while the hardware is still parts in boxes.

  1. Assemble and index the MIDI corpus — title, composer, key, tempo, mood.
  2. Speech to text; confirm it transcribes at three metres with music playing.
  3. Intent parsing: "something sad in D minor" → structured query.
  4. Fuzzy search over the corpus, plus a generative fallback.
  5. Dispatcher streaming MIDI to the controller, pointed at the virtual port.
Gate

Spoken request → correct MIDI in under three seconds, and an unrecognised request produces a spoken reply rather than silence (R10).

S8

Velocity calibration

Wk 9–10 · 12 h
  1. Sweep the velocity-phase duration for one note across the Stage 1 range, firing each step with a gap.
  2. Record peak level for every step; fit a curve mapping MIDI 1–127 to pulse duration.
  3. Repeat for all 88. Automate it — 88 × 8 points by hand is a weekend.
  4. Tune activation duration per note. Bass hammers are heavier and need longer to move.
  5. Settle hold duty by ear: full duty is quieter mechanically but hotter; reduced duty at high PWM is cooler but can sing.
Gate

A chromatic scale at one MIDI velocity sounds even across all 88. Audible, not measurable on paper.

S9

Integration and soak

Wk 11 · 8 h
  1. Point the dispatcher at the real controller.
  2. Play something dense and pedal-heavy. Watch the rail; listen for late notes.
  3. Two hours continuous unattended, then walk away.
  4. Return and check: stuck notes, dropped notes, warm boards, brownouts in the log.
Gate

Two hours, no thermal event, nothing stuck or dropped. If it survives this it will survive an audience.

S10

Hardening and deployment

Wk 12–13 · $649+ · 14 h
  1. Seal compute and drivers in a NEMA enclosure at positive pressure behind filtered fans. Air enters only through a filter.
  2. MERV-13 media, treated as a consumable. Carry spares.
  3. Optically isolate driver ground from the host so switching transients cannot reset the compute box.
  4. Move the stack fully local and test with the network physically unplugged (R9). Not airplane mode — unplugged.
  5. Size the power: a 2–2.4 kW inverter generator, or LiFePO₄ with solar. The Jetson's DC input removes the inverter from the compute half.
  6. Tune the piano after transport, then write the runbook.
Gate

A full day in dust, no filtered-air loss, no host resets, and a stranger able to walk up, speak, and hear it play.

10 / SOFTWARE
Pipeline · state · scheduling

Five stages between a sentence and a note, and a hard boundary in the middle. Everything left of the MIDI line is Python on the host and may be slow; everything right of it is C++ on the Teensy and may not.

Pipeline · host → controller → coils
Latency budget, the MCU boundary, and the fault path back
HOST — PYTHON TEENSY — C++ capture speech→text intent select dispatch mic arraybeamform + AEC faster-whisperCTranslate2 local LLM→ structured query corpus FTS+ generative fallback mido / rtmidi→ USB serial 200 ms600 ms 500 ms300 ms 50 ms MIDI scheduleroutput frame backwards from theuniversal key delay 96-bit shadow →74HC595 chain <1 ms 12 kHz PWM SAFETY LAYER — RUNS REGARDLESS 10 s auto-off · 30-channel cap · all-off on host silence 96 coils IRLZ44N · 24 V · 0.75 A fault + rail state → host · R10 needs something to say TOTAL ≈ 1.7 s OF A 3 s BUDGET (R7) — BUILD B IS SLOWER ON THE TWO MODEL STAGES AND BUDGETS 5 s
State machine + backwards scheduling
Why velocity is the only loudness term, and why a bass note starts earlier
STATE MACHINE — ONE CHANNEL OFF ACTIVATION VELOCITY ON DEACTIVATION coil cold 100 % duty · fixedovercomes inertia duty ∝ MIDI velocityTHE ONLY LOUDNESS TERM key held · 10 s maxhold duty calibrated key returnssets repeat rate R15 — forced release at 10 s, whatever MIDI says BACKWARDS SCHEDULING MIDI event arrives all notes down, together TREBLE light, quick BASS heavy hammer ACTIVATIONVELOCITYON ACTIVATIONVELOCITYON UNIVERSAL KEY DELAY — placed first, everything else backwards from its end THE BASS KEY STARTS EARLIER AND RUNS LONGER — IT STILL ARRIVES ON THE SAME EDGE (R5, ±10 ms)
THE LIBRARY PROBLEM

"Play something sad" only works if the corpus is tagged, and raw MIDI files are not. Budget time for indexing: title, composer, key, tempo, mood. Key and tempo come out of the files; mood does not, and is worth a one-off pass with a language model over titles and metadata before deployment rather than at request time.

DESIGN THE FAILURE FIRST
Most requests from strangers will not match anything. R10 exists because a piano that goes silent reads as broken, while one that says "I don't know that one, but here's something close" reads as alive. Write that path first — it is the one an audience hits most often.
WHY THE OUTPUT IS BIT-BANGED

Ninety-six channels of independent PWM is more than any microcontroller has in hardware. The answer is to hold a 96-bit shadow register, recompute it at the PWM tick, and clock the whole frame out to the chained 74HC595s. A Teensy 4.1 at 600 MHz does this with room to spare — an RP2040 at 133 MHz manages 256-level PWM on the same topology using DMA, so headroom is not the constraint.

Gate frequencyKeep it under about 20 kHz. Higher and gate-charge losses heat the MOSFETs for no benefit; much lower and the coils sing audibly, which in an acoustic instrument is unacceptable.
11 / FIRMWARE
Teensy 4.1 · safety first
THE FIVE-STATE SOLENOID

A solenoid moving a key is not on or off. OFF → ACTIVATION → VELOCITY → ON → DEACTIVATION. Activation runs at full duty to overcome inertia, fixed and calibrated per note. Only the velocity phase carries loudness. Feeding MIDI velocity straight to a duty cycle skips the momentum phase, so quiet notes fail to speak and loud ones arrive late.

Because every note takes a different time to reach the key, scheduling works backwards from a universal key delay — schedule the final state first, then place activation and velocity behind it. Chords land together instead of arpeggiating themselves (R5), at the cost of a fixed latency nobody can hear.

CouplingThe velocity table is only valid for the lost-motion settings it was measured against. Adjust one key's regulation and that key's curve is stale — which is why Stages 5 and 8 sit next to each other.
THE SAFETY LAYER
firmware — invariants
// none of these are configurable at runtime
constexpr uint32_t MAX_ON_MS      = 10000;  // R15: coil cooks at ~40 s
constexpr uint8_t  MAX_SIMULTANEOUS = 30;     // R16: supply, not musicality
constexpr uint32_t HOST_TIMEOUT_MS = 500;    // R17: silence means stop

void serviceSafety(uint32_t now) {
  // R15 — force-release anything held too long, whatever MIDI says
  for (uint8_t ch = 0; ch < N_CHANNELS; ++ch)
    if (state[ch] == ON && now - onSince[ch] > MAX_ON_MS)
      forceRelease(ch, "held past thermal limit");

  // R17 — a quiet host is a dead host
  if (now - lastHostMsg > HOST_TIMEOUT_MS) allChannelsOff();

  // R14 — the E-stop cut the rail; stop pretending we are playing
  if (analogRead(PIN_RAIL_SENSE) < RAIL_LIVE) {
    allChannelsOff();
    reportToHost("24V rail down — E-stop or fuse");
  }
}
ADMISSION CONTROL DROPS, IT DOES NOT QUEUE
A queued note arrives late, and a late note is worse than a missing one — it lands on the wrong beat and the ear catches it immediately. Dropping the quietest note in a thirty-note cluster is very nearly inaudible. The musically correct answer and the electrically safe answer happen to be the same.
12 / RUNBOOK
Operate · diagnose · maintain
START AND STOP
  1. E-stop pressed. Power the compute box first and let it boot fully.
  2. Confirm the controller enumerates and reports the rail as down.
  3. Release the E-stop. Confirm no key moves.
  4. One test note, then a test chord.
  5. To stop: halt playback, press the E-stop, then power down the compute box.
MAINTENANCE
  • Every event: confirm the E-stop actually cuts the rail. Ten seconds, and it is the one control that matters.
  • Monthly / after any move: tune, and spot-check regulation on a dozen keys across the range.
  • Outdoors: change enclosure filters on schedule. Positive pressure only works while air can get in.
  • Annually: re-run the full velocity calibration. Felt compresses; lost motion drifts.
WHEN IT MISBEHAVES
SymptomMost likely cause
One note never soundsLost motion too large on that key, or a dead channel. Fire it and listen for the plunger.
One note always quietStale calibration — re-sweep that note only
A note sticks onShorted MOSFET or a floating register. E-stop immediately — firmware gives you ten seconds, then it is on you
Notes drop in dense passagesAdmission control working as designed. Check the rail is not sagging first.
Chords arpeggiateUniversal key delay too short. Raise it.
Buzzing while heldHold PWM in the audible band — raise toward 12–20 kHz
Host resets mid-chordCheck every flyback diode, then that grounds bond at exactly one point
Whole rail deadFuse or E-stop. The controller should already be telling you which.
Piano feels wrong to playViolates R6. Find it before playing the instrument again.
88 SOLENOIDS · BUILD MANUAL REV 1.2 · SPEC 001 COMPLIANT
96 CHANNELS · ERC CLEAN · CRC OK