How to wire addressable LEDs off the PDH's switchable channel, budget their current draw, and use WPILib's AddressableLED API to signal robot state.
to read
words
sections
Power the strip from the PDH's switchable channel through a 5V regulator, run the data line from a roboRIO PWM header, and control it with WPILib's AddressableLED and LEDPattern classes. That's the whole system in one sentence. The rest of this guide is the wiring details that keep it from tripping a breaker, the current math so you don't oversize a strip your regulator can't feed, and the actual API calls that turn "blinking rainbow" into "the drive team can tell the intake is loaded without looking at a laptop."
Addressable LED strips like the WS2812B (REV sells one as the 5V Addressable LED Strip, part REV-11-1198) need two separate things from your robot, and it's easy to wire one and forget the other.
Power comes from the battery side. The PDH's switchable channel is the natural place to plug in an LED strip's power leads, because it's a channel you can turn fully on or off from code, separate from your always-on channels. But that channel outputs battery voltage, REV's own spec puts the PDH's operating range at roughly 4.7 to 18V, not the regulated 5V a WS2812B strip needs. So the switchable channel feeds a buck converter or a VRM (Voltage Regulator Module), and the regulator's 5V output feeds the strip's V+ and ground. Don't wire a 5V strip straight to a 12V channel; you'll cook it.
Data comes from the roboRIO, not the PDH. You run a single signal wire from one of the roboRIO's PWM headers to the strip's data-in pin. WPILib's docs flag a real tradeoff here: roboRIO PWM/servo ports output 6V, but WS2812B LEDs are built for 5V. They'll run fine at 6V, it just may shorten their lifespan. For a single build season most teams don't bother with a level shifter; if you want the strip to last multiple years, add one. Whatever regulator you use for the strip's power, tie its ground to the roboRIO's ground too, a floating data reference is a common cause of flickering or garbage colors on the first few pixels.
Two hardware limits worth knowing before you plan a layout: the roboRIO can only run one AddressableLED object at a time, so if you want LEDs in multiple locations, daisy-chain them into one long strip and split it in software (more on that below), or use a PWM Y-cable to run identical patterns on two runs. And the data pin has to be an actual PWM header, not the MXP breakout, not a DIO port.
If you'd rather not manage pixel data in code at all, REV's Blinkin LED driver is a separate option: it takes 12V directly (no regulator needed), stores presets onboard, and you select a pattern by sending it a PWM value like a motor controller, not through the AddressableLED API. It's simpler to wire but you give up per-pixel control, which is the whole point of the API this article covers.
Two rules make the switchable channel the right home for LEDs rather than a normal always-hot low-current channel. The CUSTOM CIRCUIT definition in the current Robot Construction Rules (any active electrical item that isn't an actuator or core control-system part) covers your LED strip, and the rule governing the PDH's switched channel restricts it to controlling non-actuator custom circuits, which LEDs are. Separately, R611 requires all wiring and devices to be electrically isolated from the robot frame, and specifically calls out decorative lights and grounded-enclosure devices as things teams forget to isolate. Rule numbers shift between seasons, so check the current Game Manual's electrical section before inspection rather than trusting a number printed here.
In code, the switchable channel is controlled through the PowerDistribution class, not AddressableLED:
PowerDistribution pdh = new PowerDistribution(1, PowerDistribution.ModuleType.kRev);
pdh.setSwitchableChannel(true);
It doesn't come on by default just because your regulator is plugged in, you have to call setSwitchableChannel(true) somewhere in your code, and it's a common first-day gotcha when a fresh regulator setup stays dark. REV's own spec also lists a maximum switching frequency of about 10Hz for that channel, so don't try to strobe LEDs by rapidly toggling it in a loop. Do all your blinking and pattern work in software against the strip itself; use the switchable channel only as an on/off master switch.
REV specs its own 5V strip at roughly 3 amps per meter (60 LEDs) with every LED at full white brightness. The switchable channel supports up to 15A continuous on a replaceable ATM/APM fuse, which on paper covers about 5 meters, 300 pixels, of that strip at max brightness. In practice your regulator's own current rating is usually the tighter limit, size it to the strip's worst case, not to what the channel can theoretically pass.
Real robots draw far less than the worst case, because full white at full brightness on every pixel is rare in an actual pattern. LEDPattern.atBrightness() lets you cap the whole pattern's output so a rainbow or a solid color never approaches that ceiling, which is worth doing even if your math says you have headroom, since a brownout mid-match is a bad time to find out otherwise.
Set it up once in a subsystem, then hand out LEDPatterns from commands:
public class LEDSubsystem extends SubsystemBase {
private static final int kPort = 9;
private static final int kLength = 120;
private final AddressableLED led = new AddressableLED(kPort);
private final AddressableLEDBuffer buffer = new AddressableLEDBuffer(kLength);
public LEDSubsystem() {
led.setLength(buffer.getLength());
led.start();
setDefaultCommand(runPattern(LEDPattern.solid(Color.kBlack)).withName("Off"));
}
@Override
public void periodic() {
led.setData(buffer);
}
public Command runPattern(LEDPattern pattern) {
return run(() -> pattern.applyTo(buffer));
}
}
setLength() is expensive, WPILib's docs say to call it once at startup, never in periodic(). The buffer gets written by whatever pattern is currently applied, and led.setData(buffer) in periodic() is what actually pushes it out to the strip every loop.
LEDPattern is the part that saves you from writing per-pixel math by hand. LEDPattern.solid(Color) is a flat color, LEDPattern.rainbow(saturation, value) scrolls a hue sweep, LEDPattern.gradient() blends between colors, and LEDPattern.progressMaskLayer(DoubleSupplier) turns a 0-to-1 value into a lit fraction of the strip, which is exactly what you want for showing elevator height or climb progress as a growing bar of light. Modifiers chain onto any pattern: .scrollAtAbsoluteSpeed(), .blink(), .breathe(), .atBrightness(), .mask() to combine two patterns, and .reversed() if your strip runs the wrong direction. If one physical strip covers two zones of the robot, AddressableLEDBufferView lets you slice the buffer and apply different patterns to each section without wiring two strips.
This is where LEDs earn their keep. A few patterns that map directly to the API above:
DriverStation.getAlliance() returns Optional<Alliance>, and WPILib's docs are explicit that it can come back empty before the Driver Station connects, so don't read it in a constructor or robotInit(), poll it in periodic() and handle the empty case. Set LEDPattern.solid(Color.kRed) or kBlue once it resolves, and anyone on the field can tell your DS is on the wrong alliance station before you even try to enable.LEDPattern.synchronizedBlink(RobotController::getRSLState) blinks your strip in lockstep with the mandatory RSL, solid for on-and-disabled, blinking for enabled, so a glance at either one tells the drive team the same thing instead of sending a mixed signal.DriverStation.getMatchTime() gives an approximate, unofficial seconds-remaining count, WPILib's own docs warn it's not precise enough to dispute a call or guarantee timing, but it's plenty accurate for triggering a color change or strobe in the last 20-30 seconds as a heads-up.For more on the electrical side of the robot the switchable channel lives on, see the PDH guide and the general wiring guide. None of that overlaps with the roboRIO/radio blink-code reference in the status lights guide, that article covers diagnosing hardware, this one covers building a display. And if you're new to organizing subsystems and commands the way the LEDSubsystem above does, the command-based programming guide covers that structure from the ground up.
This article is AI-assisted: drafted from primary sources, then reviewed and edited by hand. Errors still get through. When one is reported we fix it and write down what changed — publicly, in the corrections log.
Keep going
You came here for one answer. These lessons teach the same subject properly, in the order a team actually learns it. All 35 are free and none of them need an account to read.
Go deeper
LearnFRC has 394 free FRC lessons across every department. Create a free account to save your place, track your progress, and earn a certificate.
Create a free accountKeep reading
Structured lessons and quizzes across every department. Create a free account to save your progress, track your team, and earn a certificate.