Measuring an LED pole until it browns out (on purpose)
Real current, voltage, and light measurements from an SK6813 LED installation: what FastLED's power limiter actually delivers, a per-pixel power model good enough to predict any pattern's draw, and a deliberately induced power supply collapse.
Why
We build LED poles: steel tubes wrapped in six channels of double-row SK6813-mini strip (480 pixels per channel, 2,880 pixels per pole) driven by an ESP32 receiving frames over Ethernet at 60 fps from a Raspberry Pi.
Our worst field failure mode is ESP32 brownout. If the LEDs demand more
current than the supply can deliver, the 5 V rail sags, the ESP32's brownout
detector fires, and the controller reboots mid-show. The firmware defends
against this with FastLED's power limiter,
FastLED.setMaxPowerInMilliWatts(30000), but we had reasons to distrust that
number: FastLED estimates power from a generic per-LED model, and anecdotally
the pole "safely drew 42 W for weeks" with a 30 W setting.
So we built a measurement bench and asked four questions:
- What does the FastLED limiter actually limit to?
- How much power does a pixel draw, per color channel, per byte value?
- Can we predict a whole pattern's power from its pixels?
- What does the failure actually look like, and can we make it happen on demand?
The bench
A dedicated Raspberry Pi 4 reads seven I2C sensors through a PCA9548 multiplexer:
- 6x INA260 precision current/voltage/power monitors, one inline in each strip channel's 5 V feed;
- 1x TSL2591 light sensor taped face-down over a single LED, giving an objective optical measurement immune to camera auto-exposure.
The pole under test is powered by a Mean Well LPV-100-5 (5 V, 12 A). Test frames were generated directly on the bench Pi and multicast to the controller in the pole's native UDP frame format, giving per-pixel control of all 2,880 pixels with no pattern engine in the loop. The ESP32's serial console and its heartbeat/alert broadcasts were logged throughout.
Two measurement gotchas worth passing on:
- A single INA260 conversion lies on PWM loads. SK6813 pixels chop their current at kHz rates, and 1.1 ms single-shot samples alias the ripple ringing on the supply leads: we measured "0 to 28.8 V" on a healthy 5 V rail. With 64-sample on-chip averaging (a ~70 ms window) the same rail reads 5.16 ± 0.33 V. Average everything.
- Bond your grounds. The sensor's bus-voltage measurement is referenced to its own GND pin. Until we ran one jumper from the PSU negative to the sensor chain, voltage readings floated between 0.5 V and 28 V while current readings stayed plausible, a nasty half-broken state.
Finding 1: a pixel's current is linear, and color doesn't matter
We drove a 60-pixel block through every byte value on each channel separately, measuring channel current with everything else dark.
![]()
Three results, all cleaner than we expected:
- Red, green, and blue draw identically: 48.0, 48.4, and 47.9 µA per pixel per byte-count. There is no "expensive channel" on SK6813, a common LED-strip folk belief that turns out not to apply here.
- White is exactly additive: R+G+B predicts white within 1.4%.
- Current is linear in byte value from byte 8 to 255, with residuals under 0.1 mA. There is no gamma anywhere in this electrical path.
On top of that sits a fixed cost: 0.72 mA per pixel of quiescent draw, which is 2.08 A (10.4 W) for the whole pole showing black. The lights-off tax is real.
The light output is linear too, on all four channels:

Lux tracks byte value point-for-point (byte 128 gives 0.509 of byte 255's output), which also means lux tracks watts: LED efficiency is flat across the range at these timescales. If your LEDs look nonlinear, that's your gamma table, not the silicon.
The pattern power model
Those two constants are the whole model. For a frame of RGB bytes:
current_mA = 0.72 x N_pixels + 0.048 x sum(every channel byte in the frame)
For our pole: I = 2.1 A + 0.048 mA x sum_of_all_bytes. Any pattern's power
can now be computed from its pixel data before it ever plays.
How good is it? Our first whole-pole validation run said "within 10-25%, over-predicting," and we had a tidy physical story ready (voltage starvation at the far end of each 480-pixel run). Then we noticed the trap: those measurements ran with the stock FastLED limiter armed, and FastLED's internal power estimate crosses its budget below the point where its hard clamp is obvious. The limiter was silently scaling frames we believed were clean.

Re-measured with the limiter parked at an unreachable 200 W, the whole-pole
curve tracks the per-pixel model within 3% up to 9.5 A, and the leftover
2-3% is honest rail sag. Two lessons for the price of one: the model predicts
real pattern power almost exactly, and a power limiter contaminates
measurements well below the point where you notice it clamping. If you are
characterizing LEDs behind FastLED, park max_power out of reach first or
your data quietly measures the limiter instead of the hardware.
Finding 2: what the FastLED limiter actually delivers
With the model in hand, we ramped a solid white frame while stepping
setMaxPowerInMilliWatts through 30 W, 45 W, and 60 W, rebooting between
settings. A detail worth knowing: FastLED only reads this value at boot.
A runtime change silently does nothing to the frame limiter until restart.

The measured clamp is linear in the setting but nowhere near equal to it:
| setting | delivered (measured) |
|---|---|
| 30 W | 35.0 W |
| 45 W | 46.0 W |
| 60 W | 56.1 W |
delivered = 14.0 W + 0.70 x setting for this pole (R² ≈ 1; the middle point was measured after fitting the other two and landed within 1%).
The shape makes sense once you know FastLED's internals: its power model both overestimates per-pixel modulation draw (about 1.4x on SK6813-mini) and subtracts its own quiescent estimate from the budget, double-counting the idle draw. The two errors partially cancel near 50 W, which is presumably why nobody notices until they measure. The takeaway is not "FastLED is broken." It's that the setting is a knob, not a specification, and each LED type and count needs a two-point calibration like this one.
Finding 3: the failure is a cliff
Everything above happens in the safe zone. The real question was the failure.
First surprise: we couldn't brown it out with content alone at sane settings. With the limiter at 60 W, we hit the pole with 42 instant black-to-white steps up to 10.7 A of demand, the worst dI/dt a pattern could ever produce. The rail never fell below 4.71 V, and delivering the same steps as a 100 ms ramp barely changed the dip. A properly calibrated limiter absorbs any content transient; the supply's regulation, not the step rate, is what matters.
At the stock 30 W setting the margin is even more decisive: five instant black-to-full-white steps (the worst frame any pattern can produce) held the rail at 4.87 V, drawing the calibrated 35 W. On this supply, the stock setting makes content-induced brownout empirically impossible.
So we raised the limiter beyond the supply's capability (100 W setting) and escalated:

- Byte 32 white (14.4 A demanded, 120% of the PSU's rating): survives. The rail sags to 4.57 V and the Mean Well just delivers it, indefinitely.
- Byte 40 white (about 18.7 A demanded): collapse, 3 for 3. The waveform is fascinating. The rail holds ~4.55 V for about 80 ms (the supply's overload timer riding through what it hopes is an inrush), then folds back to 1.6 V. The ESP32 browns out, the load vanishes, the supply recovers, the controller reboots: the exact field failure, reproduced on demand.

There is no gradual danger zone. Between "sustains 120% forever" and "collapses in 80 ms" lies a single step of our test. The boundary is sharp because two protection systems face off: the supply's constant-current overload behavior versus the load's willingness to keep pulling.
The controller's own instrumentation confirmed each event: the firmware
counts brownout resets in RTC memory and broadcasts a PoleFX_alert line on
the network. Worth actually listening to; ours were being silently dropped
by the receiving server (fixed now).
What this means for brownout-proofing
Measured conclusions, in order of leverage:
- Calibrate the software limiter; it's the actual protection. Set
max_powerso the delivered watts (via the calibration line, not the setting) stay at or below the supply's rating. On this 60 W supply a setting of about 65 W delivers roughly 60 W; we run a 30 W setting (35 W delivered) for margin. With that in place, no content, including worst-case instant white, gets near the cliff. - Power the ESP32 through a buck-boost, not an LDO. Our collapse waveform shows the rail parking at 1.6 V during the fault. No 3.3 V LDO survives that, which is why the controller reboots rather than riding through and shedding load. A buck-boost (we're planning the TPS63020, which regulates 3.3 V from inputs down to 1.8 V) would keep the brain alive through the entire measured fault waveform. A controller that stays up during collapse can blank the LEDs, kill the demand, and let the supply recover, turning a reboot loop into a one-frame flicker.
- Per-output load switches (SY6288 or similar) close the loop. With the controller alive through faults, switchable outputs let it shed strips, stagger power-up inrush, and re-admit load gradually after a fault. Our step data says inrush from content isn't the killer, but staged re-admission after collapse is where the switches earn their place.
- Model your content if you bypass a limiter. The per-pixel model (0.72 mA/px + 48 µA/px/byte) predicts any frame's draw within 3%. We now compute worst-frame power for patterns at upload time.
Reproducing this
The whole bench is about $130 of sensors: INA260 breakouts on a STEMMA QT chain (no address jumpers needed, one I2C mux channel per sensor), a TSL2591 taped to an LED, and a Raspberry Pi. The most valuable habits: average over PWM ripple, bond the grounds once, verify every firmware reboot actually happened (lossy UDP config protocols will gaslight you), and let the failure happen. The collapse waveform taught us more than every safe measurement combined.
Data, scripts, and figures for this post were captured on a live bench in a single evening; all numbers are measured, not simulated.