#prusawire #neopixel #klipper #rp2040 #kb2040 #einsy #level-shifting #rainbow-barf #led #3d-printing
# Overview
Front-panel accent lighting for [[erikbuild - Prusawire PW.0011 - Thoth Station]] — **two Rainbow Barf logo PCBs, 8× WS2812B-2020 each, 16 pixels total**. Not toolhead lighting; these mount on the front of the printer for aesthetics, nowhere near steppers or heater wiring.
The printer runs an Einsy RAMBo (atmega2560) flashed from Marlin to Klipper with a Raspberry Pi 4 host. The Einsy can't reasonably drive this, so an **Adafruit KB2040** runs as a secondary Klipper MCU over USB. Its 3.3 V logic is shifted to 5 V with a 74AHCT125; the LEDs get their own 24→5 V buck rather than anything USB-derived.
Electrical sibling of [[Voron V0 NeoPixel Power Injection]]; config sibling of [[Voron V0 NeoPixel Chain Mapping And Config]]. At 16 pixels this lands much closer to the V0 case than first assumed — same star-power topology, same need for a real 5 V rail.
# Resources
- [[Prusawire]] — parent build note
- [[Prusawire Software Installation and Configuration]] — Klipper install on the Einsy
- [[Klipper on Einsy Rambo]] — the 32u2 USB-bridge reflash and 2560 flashing this printer depends on
- [[Voron V0 NeoPixel Chain Mapping And Config]] — the Barf is 8 pixels, learned the hard way on [[V0.4351]]
- [[Voron V0 NeoPixel Power Injection]] — star-the-power / chain-the-data topology
- [[Stealthburner]]
- [WS2812B-2020 datasheet (Mouser)](https://www.mouser.com/pdfDocs/WS2812B-2020_V10_EN_181106150240761.pdf) — **16 mA per channel**, `VIH` 0.7·VDD, VDD characterized 4.5–5.5 V
- [Adafruit KB2040 product page](https://www.adafruit.com/product/5302) · [pinouts](https://learn.adafruit.com/adafruit-kb2040/pinouts) · [PrettyPins PDF](https://github.com/adafruit/Adafruit-KB2040-PCB/blob/main/Adafruit%20KB2040%20Pinout.pdf)
- [KB2040 CircuitPython `pins.c`](https://github.com/adafruit/circuitpython/blob/main/ports/raspberrypi/boards/adafruit_kb2040/pins.c) — authoritative silk-name → GPIO map
- [Adafruit 1787 — 74AHCT125 Quad Level-Shifter](https://www.adafruit.com/product/1787) ($1.50, DIP-14)
- [Klipper `[neopixel]` config reference](https://www.klipper3d.org/Config_Reference.html#neopixel)
- [Klipper `neopixel.c`](https://github.com/Klipper3d/klipper/blob/master/src/neopixel.c)
- [Klipper issue #3384](https://github.com/Klipper3d/klipper/issues/3384) — why `linux mcu` (Pi GPIO) can't do NeoPixel
- [Einsy RAMBo 1.1a schematic](https://github.com/ultimachine/Einsy-Rambo/blob/1.1a/board/Project%20Outputs/Schematic%20Prints_Einsy%20Rambo_1.1a.PDF)
# Design
## Power budget — the number that drives everything
| | Per channel | Per pixel (full white) | ×16 |
|---|---|---|---|
| WS2812B-2020 (datasheet) | 16 mA | 48 mA | **768 mA** |
Typical animated color effect runs roughly a third of that (~250 mA), but the rail has to be sized for all-white.
768 mA is the hinge. At the ~120 mA originally assumed, the cheap tricks all work. At 768 mA they don't:
- **Anything USB-derived is out.** That current would come off the Pi 4's ~1.2 A shared budget, through the KB2040's USB-C connector and whatever cable is in use. 768 mA through 1 m of 28 AWG loses ~0.3 V before anything else happens.
- **A series diode is out.** A 1N400x's Vf is current-dependent — roughly 0.6 V dim, 0.95 V at full white. LED VDD would *swing with whatever the strip is displaying*, ~4.4 V down to ~4.05 V. The datasheet characterizes at 4.5–5.5 V, and blue has the highest LED Vf so blue goes marginal first. For lighting whose only job is to look good, a supply that sags as a function of content is the wrong trade.
So: **LEDs stay at a clean 5 V from their own buck, and the data line gets a proper buffer.**
### The RAW pin is a trap here
The KB2040 breaks out a **RAW** pin that Adafruit documents as *"for powering NeoPixels or other 5V devices,"* with a jumper on the underside to bypass its 500 mA fuse for "up to 2 A." That 2 A figure describes the *board's* fuse, not the Pi's ability to source it. Bypassing the fuse to pull 768 mA through the Pi's USB port is exactly the thing the buck exists to avoid.
==Leave the RAW jumper alone. Don't use RAW.== The KB2040 is powered by USB (data + its own ~30 mA); the LEDs and the buffer are powered by the buck; grounds are common.
## Why not straight off the Einsy
- **Timing.** Klipper bit-bangs WS2812 with interrupts briefly disabled; on AVR it reads `TCNT1` directly because 16 MHz can't hit the timing otherwise. A hardware IRQ mid-frame aborts the transmission → `Neopixel update did not succeed`. The Einsy is already servicing four TMC2130s, two heaters and fan tach. Static colors mostly survive; `led_effect` animation does not — and animation is the entire point of front accent lighting.
- **Pins.** The one documented spare is **PK0** (analog 8) on the **P3 header** — the IR filament sensor line on an MK3S. Everything else means giving up the LCD headers.
## Why not the Pi 4 GPIO
Klipper's `linux mcu` does not implement `[neopixel]`. Userspace GPIO on Linux can't meet 800 kHz bit-bang timing deterministically. Not a config problem, not fixable.
## Board choice — KB2040
Three candidates were on hand. **KB2040 wins because it's RP2040.**
| | **KB2040** (chosen) | Pico 2 | Feather RP2350 HSTX + PSRAM |
|---|---|---|---|
| Silicon | **RP2040** | RP2350A (A2) | RP2350A (A2) |
| Klipper's own preference | **Preferred part** | Discouraged (E9 errata) | Discouraged (E9 errata) |
| Flashing | Standard UF2 | Autodetect friction | One forum report of flash-then-never-enumerate |
| Onboard NeoPixel | **Yes — GPIO17** | No | Yes |
| USB | USB-C | micro-USB | USB-C |
| Reserved pins | GPIO11 = BOOT button | GPIO 23/24/25/29 | GPIO8 = PSRAM CS |
| Form factor | Pro Micro, tucks behind a panel | larger | larger |
Choosing RP2040 deletes an entire category of caveats. Klipper maintainers don't recommend RP2350 — the **RP2350-E9 errata** plus no meaningful advantage for Klipper's purposes. E9 only affects GPIOs configured as **inputs with internal pull-downs**, so it wouldn't have bitten an output-only NeoPixel pin, but there's no reason to take on the argument when the preferred part is sitting in the drawer.
The onboard NeoPixel on **GPIO17** is a genuine bonus: it lets the whole Klipper → MCU → `SET_LED` chain be proven before a single wire is soldered. See [[#Bring-up order]].
### KB2040 pin map
Silk names aren't GPIO numbers. From CircuitPython's `pins.c`:
| Silk | GPIO | | Silk | GPIO |
|---|---|---|---|---|
| D0/TX | 0 | | D10 | 10 |
| D1/RX | 1 | | BOOT btn | 11 |
| D2–D9 | 2–9 | | SDA/D12 | 12 |
| CLK/SCK | 18 | | SCL/D13 | 13 |
| MOSI | 19 | | A0–A3 | 26–29 |
| MISO | 20 | | **onboard NeoPixel** | **17** |
**D10 = GPIO10** chosen for the data output: it sits on the top edge alongside `RAW`/`G`, so the outgoing 3-pin harness comes off one edge of the board.
## Level shifting — 74AHCT125
HCT-family logic powered at 5 V has **TTL input thresholds** (`VIH` 2.0 V at VCC 4.5 V), so it accepts 3.3 V in and swings a full 5 V out. AHCT is fast enough for 800 kHz with clean edges. Adafruit sells it as [product 1787](https://www.adafruit.com/product/1787) and names NeoPixels in the description.
Only pixel 1 ever sees the buffered signal — every pixel downstream regenerates at its own VDD via the built-in reshaping circuit. So the failure mode of getting this wrong is always "first pixel wrong, everything after inherits garbage," temperature- and batch-dependent, which is what makes bare 3.3 V so unpleasant to diagnose.
- **Must be AHCT or HCT, never AHC/HC.** Plain HC at 5 V has `VIH` 3.5 V — the exact problem being solved.
- `SN74AHCT125N` is DIP-14, perfboard-friendly. `SN74AHCT1G125` (SOT-23-5) if this becomes a real PCB. `SN74LVC1T45` also fine.
- **Avoid** `TXB0104` / `TXS0102` auto-direction translators — weak drivers and edge accelerators, known to misbehave on fast unidirectional signals.
- **Avoid the blue 4-channel BSS138 board** (HiLetgo / SparkFun BOB-12009 clone / Adafruit 757 — the 10-pack in the parts drawer). Details in [[#Decisions]]; short version, it's an I²C part and physically cannot produce a valid WS2812 bit.
## Schematic
### Power
```text
24 V (Prusawire PSU)
│
├── F0 300 mA optional — protects the 24 V tap itself
│
▼
Buck 24→5 V, ≥2 A set to 5.0 V (see note below)
│
│ 5 V out
▼
F1 1.5 A slow-blow ← the ONLY thing between the buck and everything else
│
└──┬── C1 1000 µF ─────────┐ bulk, shunt to GND
├── C2 100 nF ──────────┤ U1 decoupling, right at pin 14
├── U1 pin 14 (VCC) ────┤ U1 = SN74AHCT125N
├── Barf 1 VDD ──────────┤ star power
└── Barf 2 VDD ──────────┘
GND
```
Everything downstream of F1 is **one node**. C1 and C2 are shunt to ground, not in series.
### Data
```text
KB2040 RAW pin unused, jumper left OPEN
D10 (GPIO10) ── R1 330 Ω ──► U1 pin 2 (1A in)
U1 pin 1 (/1OE) ─────── GND
U1 pins 4, 9, 12 (unused A inputs) ── GND
U1 pin 7 (GND) ──────── GND
U1 pin 3 (1Y out) ── R2 330 Ω ──► Barf 1 DIN
Barf 1 DOUT ──────────────────────────────────────────────────► Barf 2 DIN
```
### Ground
One common node: buck GND · C1 · C2 · U1 pin 7 · Barf 1 GND · Barf 2 GND · KB2040 `G`. The KB2040 itself is powered by USB-C from the Pi 4, so USB ground ties in here too.
`R1` is not decoration: if the 5 V rail is off while the KB2040 is USB-powered, it limits backfeed through U1's input protection to ~10 mA. `R2` is edge damping at the source. Tie U1's three unused inputs (pins 4, 9, 12) to GND so they don't float.
## Fusing and rail integrity
**What F1 protects:** the LED harness, not the electronics. That wiring leaves the enclosure, runs to the front panel, and can chafe or get pinched. A dead short there against a 2 A buck with nothing in the way is a melted wire. U1 draws microamps and is never the fault.
**Why U1 sits after F1, not before:** a blown fuse then kills the buffer and the LEDs together. If U1 stayed powered with the LED rail dead, it would be driving 5 V into the DIN of an unpowered pixel — current through the pixel's input protection diode into its own VDD. R2 caps that at ~10 mA so it isn't destructive, but there's no reason to build the hazard in.
**Cartridge fuse, not a PPTC.** A resettable polyfuse is tempting, but they carry 0.1–0.3 Ω. At 768 mA that's up to 0.15 V dropped — eating the exact 5 V headroom the whole buck decision was about. A glass or blade fuse is 0.01–0.05 Ω, negligible.
**Slow-blow (T).** C1 at 1000 µF has real inrush. Most bucks soft-start enough to mask it, but a fast-acting 1.5 A can nuisance-trip on a cold power-up.
**Set the buck to 5.0 V.** ==The WS2812B-2020's absolute maximum VDD is 5.3 V== (datasheet, Absolute Maximum Ratings — note this is *lower* than the 5.5 V the Electrical Characteristics table implies, which is a common inconsistency in these datasheets). If compensating for wiring drop, 5.1 V measured at the Barf end under load is acceptable. Do not chase 5.5 V.
F1 does not protect the 24 V side. F0 is optional and much smaller — 768 mA at 5 V is only ~160 mA at 24 V plus buck losses, so ~300 mA.
## Topology
**Star the power, chain the data** — same as [[Voron V0 NeoPixel Power Injection]]. Each Barf PCB gets its own 5 V/GND drop from the distribution point; only DO→DI hops between boards. The 2020-package boards have thin traces and shouldn't be asked to carry the second board's current.
## BOM
| Ref | Part | Notes |
|---|---|---|
| — | Adafruit KB2040 | On hand, new in package. Castellated + through-hole; headers or direct solder. |
| U1 | SN74AHCT125N (DIP-14) | **AHCT/HCT only.** Tie `/1OE` low, unused inputs to GND. Adafruit 1787, $1.50. |
| — | Buck converter 24→5 V, ≥2 A | Off the printer's 24 V. Not a mini-360 — thermally marginal at 800 mA. Set output to 5.0 V. |
| C1 | 1000 µF / 10 V electrolytic | Bulk, shunt to GND at the distribution point. Polarity matters. |
| C2 | 100 nF ceramic | U1 decoupling, shunt to GND right at pin 14. |
| F1 | 1.5 A **slow-blow cartridge** + holder | Between buck 5 V out and the whole downstream node. **Not a PPTC** — series resistance eats rail headroom. |
| F0 | 300 mA (optional) | On the 24 V tap, ahead of the buck. |
| R1, R2 | 330 Ω 1/4 W ×2 | Buffer input protection / output damping. |
| — | Perfboard, 3-pin JST ×2, 2-pin power ×2 | |
# Configuration
## Building the firmware
`make menuconfig`:
- Micro-controller architecture: **Raspberry Pi RP2040**
- Bootloader offset: **No bootloader**
- Communication interface: **USBSERIAL**
Hold **BOOT**, tap **RST** (or plug in USB-C while holding BOOT). The board mounts as `RPI-RP2`; copy `out/klipper.uf2` onto it. Standard RP2040 path — none of the RP2350 flashing workarounds apply.
```bash
ls /dev/serial/by-id/
```
## printer.cfg
```ini
[mcu led]
serial: /dev/serial/by-id/usb-Klipper_rp2040_XXXXXXXXXXXX-if00
# Onboard status pixel — keep it, it's free diagnostics
[neopixel onboard]
pin: led:gpio17
chain_count: 1
color_order: GRB
[neopixel barf]
pin: led:gpio10
chain_count: 16
color_order: GRB
initial_RED: 0.0
initial_GREEN: 0.0
initial_BLUE: 0.0
```
## Bring-up order
The onboard NeoPixel makes this testable in stages instead of all at once:
1. Flash, confirm the MCU enumerates and Klipper connects — nothing wired yet.
2. `SET_LED LED=onboard RED=1 GREEN=0 BLUE=0` → onboard pixel turns red. This proves Klipper's `[neopixel]` plumbing, the GPIO map, and the `SET_LED` syntax with zero external hardware.
3. Only then wire the buffer and the Barfs. Anything that breaks from here is wiring or level-shifting, not software.
Per-board segmentation for `led_effect` — Barf 1 is pixels 1–8, Barf 2 is 9–16.
```gcode
SET_LED LED=barf RED=1 GREEN=0 BLUE=0 INDEX=1 TRANSMIT=0
SET_LED LED=barf RED=0 GREEN=1 BLUE=0 INDEX=8 TRANSMIT=0
SET_LED LED=barf RED=0 GREEN=0 BLUE=1 INDEX=9 TRANSMIT=0
SET_LED LED=barf RED=1 GREEN=1 BLUE=1 INDEX=16
```
# Decisions
- **Decision**: KB2040 as a secondary Klipper MCU rather than the Einsy's PK0.
- **Why**: Removes the AVR timing fragility and the free-pin shortage in one move, keeps PK0 free for a filament sensor, and makes `led_effect` animation viable.
- **Alternatives**: PK0 on P3 (static colors only); Pi 4 GPIO (not implemented in Klipper).
- **Decision**: KB2040 over the Pico 2 and the Feather RP2350 HSTX.
- **Why**: It's RP2040, which is the part Klipper maintainers actually recommend — that single fact deletes the E9-errata discussion and the RP2350 flashing workarounds. Onboard NeoPixel gives a zero-hardware bring-up test. USB-C, and a Pro Micro footprint that hides behind a front panel.
- **Alternatives**: Pico 2 and Feather both work but are RP2350. The Feather's HSTX and 8 MB PSRAM are dead weight under Klipper — saved for a project that uses them.
- **Decision**: 74AHCT125 buffer with LEDs at full 5 V, rather than dropping the chain to ~4.3 V with a series diode.
- **Why**: At 768 mA the diode's Vf varies with brightness, so LED VDD would track chain content down to ~4.05 V — below the datasheet's 4.5 V characterization floor, with blue going marginal first. Unacceptable for lighting whose purpose is appearance. **This reverses the earlier call**, which was made assuming 2 pixels / ~120 mA.
- **Alternatives**: Whole-chain diode — correct and near-free at ≤10 pixels, wrong here. Sacrificial pixel at reduced VDD — electrically ideal (fixed 48 mA through the diode, stable 4.3 V, DOUT clears the 3.5 V the 5 V pixels need) but the Barf's eight LEDs share one PCB rail, so it would take a discrete WS2812 spliced in ahead of the boards plus one permanently off-color pixel. Not worth it versus a $1.50 IC.
- **Rejected**: bare 3.3 V. Works often enough to pass a bench test and fail in the enclosure.
- **Rejected — and worth remembering, because it's the obvious thing to reach for**: the blue 4-channel bi-directional level converter already in the parts drawer ([HiLetgo 10-pack, B07F7W91LC](https://www.amazon.com/HiLetgo-Channels-Converter-Bi-Directional-3-3V-5V/dp/B07F7W91LC) — SparkFun BOB-12009 clone, sold by Adafruit as product 757; 4× BSS138 + 10 kΩ pull-ups). It is **open-drain**, so the rising edge is an RC curve, not a driven edge. With the pixel's 15 pF input plus ~20 pF of wiring, τ = 10 kΩ × 35 pF = 350 ns and reaching `VIH` (3.5 V) takes ≈420 ns — longer than the entire 220–380 ns `T0H` window that defines a zero bit. Falling edges are fast (the MOSFET pulls down actively), so the result is a sawtooth and every bit is garbage. Off by an order of magnitude, not marginal. The product title says `IIC I2C` for a reason: I²C is open-drain by spec, runs at 100–400 kHz, and tolerates slow rises. Keep the boards for I²C work — they're the right part for that.
- Quick field test for any candidate board: power off, measure `HV1` to `HV`. ~10 kΩ means open-drain, reject. A real buffer reads open.
- The 1 kΩ pull-up mod would work electrically (τ → 35 ns) but means desoldering eight 0603s, 5 mA continuous sink when low, and the LV side still fighting. Not worth it against a $1.50 IC.
- **Decision**: dedicated 24→5 V buck rather than the KB2040's RAW pin or the Einsy's 5 V rail.
- **Why**: 768 mA off a Pi 4 USB port is most of its shared budget, and cable drop eats headroom before the LEDs see it. The RAW jumper's "2 A" rating is about the board's fuse, not the host's supply. The Einsy's onboard SMPS also feeds logic and 5 V fans behind a 5 A fuse — not worth the risk for a cosmetic load.
# Troubleshooting
- **Symptom**: first pixel wrong color / flickering, rest of chain garbage.
- **Cause**: data line below `VIH`. Classic level-shift signature.
- **Fix**: verify U1 VCC is at 5 V and `/1OE` is actually tied low — a floating `/1OE` leaves the output tri-stated and looks identical to no signal.
- **Symptom**: colors swapped.
- **Cause**: `color_order` mismatch. The datasheet confirms GRB for WS2812B-2020, so GRB should be right — but verify rather than assume.
- **Fix**: flip to `RGB`.
- **Symptom**: last pixels dimmer or color-shifted than the first.
- **Cause**: voltage drop along the chain.
- **Fix**: confirm each Barf has its own 5 V/GND drop rather than daisy-chained power.
- **Symptom**: nothing at all, and the onboard pixel doesn't respond either.
- **Cause**: software/MCU side, not wiring.
- **Fix**: work [[#Bring-up order]] from step 1.
- **Symptom**: `Neopixel update did not succeed`.
- **Cause**: MCU too loaded to finish the frame. Should not occur on RP2040 driving 16 pixels. If it does, something else is wrong.
- **Fix**: see [[Voron V0 NeoPixel Chain Mapping And Config]].
# Build Log
## 2026-09-17
- Scoped the problem. Confirmed Klipper's AVR NeoPixel path exists but is unsuitable under print load; confirmed `linux mcu` can't do it at all. Settled on a secondary MCU over USB.
- Initial design assumed 2 pixels / ~120 mA → whole-chain diode drop off USB power.
- **Corrected**: it's two Barf boards, 16× WS2812B-2020, 768 mA at full white. Pulled the datasheet (16 mA/channel). Redesigned around a 74AHCT125 buffer and a dedicated 24→5 V buck; USB power and the series diode both dropped.
- Checked the 4-channel BSS138 level converter already on hand — rejected, open-drain, rise time ~420 ns against a 220–380 ns `T0H` window. Reasoning recorded in [[#Decisions]] so this doesn't get re-litigated later.
- **Board settled: KB2040**, new in package. Being RP2040 rather than RP2350 removes the E9-errata caveat and the flashing workarounds entirely. Confirmed the silk→GPIO map against CircuitPython `pins.c`: data out on **D10 = GPIO10**, onboard NeoPixel on **GPIO17**.
- Drag-chain data routing concern dropped — these mount on the front panel, away from steppers and heater wiring.
- Corrected the power schematic — the first draft was ambiguous about F1 and drew C1/C2 as if in series. F1 now explicitly sits between the buck's 5 V output and a single downstream node carrying C1, C2, U1 VCC and both Barf VDDs. Added fuse-type reasoning (slow-blow cartridge, not PPTC) and the 5.3 V absolute-max VDD limit.
- ==Nothing built yet — perfboard assembly pending.==
# TODO
- ==Order the SN74AHCT125N (Adafruit 1787) and a ≥2 A 24→5 V buck. Check whether the buck shares the KB2040's enclosure or needs its own mount.==
- ~~Check whether a plain RP2040 board is on hand instead of RP2350~~ — resolved 2026-09-17, the KB2040 is RP2040.
- ==Decide front-panel mounting for two Barf logos — spacing, diffusion, and whether the PCBs sit behind printed lenses.==
- ==Confirm `color_order` empirically on first power-up rather than trusting GRB.==
- ==Design `led_effect` segmentation: two 8-pixel logo segments, decide whether they mirror or run as one 16-pixel field.==
- ==Consider whether the STEMMA QT port earns a second job later — it's I²C on GPIO12/13 and currently unused (and the BSS138 boards are the right shifter for that, if a 5 V I²C device ever shows up).==
- Wire it up and log results.
# References
- [[Prusawire]]
- [[erikbuild - Prusawire PW.0011 - Thoth Station]]
- [[Voron V0 NeoPixel Chain Mapping And Config]]
- [[Voron V0 NeoPixel Power Injection]]
- [[Prusawire Software Installation and Configuration]]
- [[Stealthburner]]