Addressable LEDs
LED strips have been commonly used by teams for several years for a variety of reasons. They allow teams to debug robot functionality from the audience, provide a visual marker for their robot, and can simply add some visual appeal. WPILib has an API for controlling WS2812 LEDs with their data pin connected via PWM.
Note
LEDs can be controlled through this API while the robot is disabled.
Instantiating the AddressableLED Object
You first create an AddressableLED
object that takes the PWM port as an argument. It must be a PWM header on the roboRIO. Then you set the number of LEDs located on your LED strip, which can be done with the setLength()
function.
Warning
It is important to note that setting the length of the LED header is an expensive task and it’s not recommended to run this periodically.
After the length of the strip has been set, you’ll have to create an AddressableLEDBuffer
object that takes the number of LEDs as an input. You’ll then call myAddressableLed.setData(myAddressableLEDBuffer)
to set the led output data. Finally, you can call myAddressableLed.start()
to write the output continuously. Below is a full example of the initialization process.
Note
C++ does not have an AddressableLEDBuffer, and instead uses an Array.
17 @Override
18 public void robotInit() {
19 // PWM port 9
20 // Must be a PWM header, not MXP or DIO
21 m_led = new AddressableLED(9);
22
23 // Reuse buffer
24 // Default to a length of 60, start empty output
25 // Length is expensive to set, so only set it once, then just update data
26 m_ledBuffer = new AddressableLEDBuffer(60);
27 m_led.setLength(m_ledBuffer.getLength());
28
29 // Set the data
30 m_led.setData(m_ledBuffer);
31 m_led.start();
32 }
12class Robot : public frc::TimedRobot {
13 private:
14 static constexpr int kLength = 60;
15
16 // PWM port 9
17 // Must be a PWM header, not MXP or DIO
18 frc::AddressableLED m_led{9};
19 std::array<frc::AddressableLED::LEDData, kLength>
20 m_ledBuffer; // Reuse the buffer
21 // Store what the last hue of the first pixel is
22 int firstPixelHue = 0;
7void Robot::RobotInit() {
8 // Default to a length of 60, start empty output
9 // Length is expensive to set, so only set it once, then just update data
10 m_led.SetLength(kLength);
11 m_led.SetData(m_ledBuffer);
12 m_led.Start();
13}
Note
The roboRIO only supports only 1 AddressableLED
object. As WS2812B LEDs are connected in series, you can drive several strips connected in series from from AddressableLED
object.
Setting the Entire Strip to One Color
Color can be set to an individual led on the strip using two methods. setRGB()
which takes RGB values as an input and setHSV()
which takes HSV values as an input.
Using RGB Values
RGB stands for Red, Green, and Blue. This is a fairly common color model as it’s quite easy to understand. LEDs can be set with the setRGB
method that takes 4 arguments: index of the LED, amount of red, amount of green, amount of blue. The amount of Red, Green, and Blue are integer values between 0-255.
for (var i = 0; i < m_ledBuffer.getLength(); i++) {
// Sets the specified LED to the RGB values for red
m_ledBuffer.setRGB(i, 255, 0, 0);
}
m_led.setData(m_ledBuffer);
for (int i = 0; i < kLength; i++) {
m_ledBuffer[i].SetRGB(255, 0, 0);
}
m_led.SetData(m_ledBuffer);
Using HSV Values
HSV stands for Hue, Saturation, and Value. Hue describes the color or tint, saturation being the amount of gray, and value being the brightness. In WPILib, Hue is an integer from 0 - 180. Saturation and Value are integers from 0 - 255. If you look at a color picker like Google’s, Hue will be 0 - 360 and Saturation and Value are from 0% to 100%. This is the same way that OpenCV handles HSV colors. Make sure the HSV values entered to WPILib are correct, or the color produced might not be the same as was expected.
LEDs can be set with the setHSV
method that takes 4 arguments: index of the LED, hue, saturation, and value. An example is shown below for setting the color of an LED strip to red (hue of 0).
for (var i = 0; i < m_ledBuffer.getLength(); i++) {
// Sets the specified LED to the HSV values for red
m_ledBuffer.setHSV(i, 0, 100, 100);
}
m_led.setData(m_ledBuffer);
for (int i = 0; i < kLength; i++) {
m_ledBuffer[i].SetHSV(0, 100, 100);
}
m_led.SetData(m_ledBuffer);
Creating a Rainbow Effect
The below method does a couple of important things. Inside of the for loop, it equally distributes the hue over the entire length of the strand and stores the individual LED hue to a variable called hue
. Then the for loop sets the HSV value of that specified pixel using the hue
value.
Moving outside of the for loop, the m_rainbowFirstPixelHue
then iterates the pixel that contains the “initial” hue creating the rainbow effect. m_rainbowFirstPixelHue
then checks to make sure that the hue is inside the hue boundaries of 180. This is because HSV hue is a value from 0-180.
Note
It’s good robot practice to keep the robotPeriodic()
method as clean as possible, so we’ll create a method for handling setting our LED data. We’ll call this method rainbow()
and call it from robotPeriodic()
.
42 private void rainbow() {
43 // For every pixel
44 for (var i = 0; i < m_ledBuffer.getLength(); i++) {
45 // Calculate the hue - hue is easier for rainbows because the color
46 // shape is a circle so only one value needs to precess
47 final var hue = (m_rainbowFirstPixelHue + (i * 180 / m_ledBuffer.getLength())) % 180;
48 // Set the value
49 m_ledBuffer.setHSV(i, hue, 255, 128);
50 }
51 // Increase by to make the rainbow "move"
52 m_rainbowFirstPixelHue += 3;
53 // Check bounds
54 m_rainbowFirstPixelHue %= 180;
55 }
22void Robot::Rainbow() {
23 // For every pixel
24 for (int i = 0; i < kLength; i++) {
25 // Calculate the hue - hue is easier for rainbows because the color
26 // shape is a circle so only one value needs to precess
27 const auto pixelHue = (firstPixelHue + (i * 180 / kLength)) % 180;
28 // Set the value
29 m_ledBuffer[i].SetHSV(pixelHue, 255, 128);
30 }
31 // Increase by to make the rainbow "move"
32 firstPixelHue += 3;
33 // Check bounds
34 firstPixelHue %= 180;
35}
Now that we have our rainbow
method created, we have to actually call the method and set the data of the LED.
34 @Override
35 public void robotPeriodic() {
36 // Fill the buffer with a rainbow
37 rainbow();
38 // Set the LEDs
39 m_led.setData(m_ledBuffer);
40 }
15void Robot::RobotPeriodic() {
16 // Fill the buffer with a rainbow
17 Rainbow();
18 // Set the LEDs
19 m_led.SetData(m_ledBuffer);
20}