diff --git a/wpilibcExamples/src/main/cpp/examples/AddressableLED/cpp/Robot.cpp b/wpilibcExamples/src/main/cpp/examples/AddressableLED/cpp/Robot.cpp index d59b370b4e..d3356c65b8 100644 --- a/wpilibcExamples/src/main/cpp/examples/AddressableLED/cpp/Robot.cpp +++ b/wpilibcExamples/src/main/cpp/examples/AddressableLED/cpp/Robot.cpp @@ -2,54 +2,37 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -#include +#include "Robot.h" -#include -#include -#include +void Robot::RobotInit() { + // Default to a length of 60, start empty output + // Length is expensive to set, so only set it once, then just update data + m_led.SetLength(kLength); + m_led.SetData(m_ledBuffer); + m_led.Start(); +} -class Robot : public frc::TimedRobot { - static constexpr int kLength = 60; +void Robot::RobotPeriodic() { + // Fill the buffer with a rainbow + Rainbow(); + // Set the LEDs + m_led.SetData(m_ledBuffer); +} - // PWM port 9 - // Must be a PWM header, not MXP or DIO - frc::AddressableLED m_led{9}; - std::array - m_ledBuffer; // Reuse the buffer - // Store what the last hue of the first pixel is - int firstPixelHue = 0; - - public: - void Rainbow() { - // For every pixel - for (int i = 0; i < kLength; i++) { - // Calculate the hue - hue is easier for rainbows because the color - // shape is a circle so only one value needs to precess - const auto pixelHue = (firstPixelHue + (i * 180 / kLength)) % 180; - // Set the value - m_ledBuffer[i].SetHSV(pixelHue, 255, 128); - } - // Increase by to make the rainbow "move" - firstPixelHue += 3; - // Check bounds - firstPixelHue %= 180; +void Robot::Rainbow() { + // For every pixel + for (int i = 0; i < kLength; i++) { + // Calculate the hue - hue is easier for rainbows because the color + // shape is a circle so only one value needs to precess + const auto pixelHue = (firstPixelHue + (i * 180 / kLength)) % 180; + // Set the value + m_ledBuffer[i].SetHSV(pixelHue, 255, 128); } - - void RobotInit() override { - // Default to a length of 60, start empty output - // Length is expensive to set, so only set it once, then just update data - m_led.SetLength(kLength); - m_led.SetData(m_ledBuffer); - m_led.Start(); - } - - void RobotPeriodic() override { - // Fill the buffer with a rainbow - Rainbow(); - // Set the LEDs - m_led.SetData(m_ledBuffer); - } -}; + // Increase by to make the rainbow "move" + firstPixelHue += 3; + // Check bounds + firstPixelHue %= 180; +} #ifndef RUNNING_FRC_TESTS int main() { diff --git a/wpilibcExamples/src/main/cpp/examples/AddressableLED/include/Robot.h b/wpilibcExamples/src/main/cpp/examples/AddressableLED/include/Robot.h new file mode 100644 index 0000000000..77090b6a57 --- /dev/null +++ b/wpilibcExamples/src/main/cpp/examples/AddressableLED/include/Robot.h @@ -0,0 +1,28 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#pragma once + +#include + +#include +#include + +class Robot : public frc::TimedRobot { + public: + void Rainbow(); + void RobotInit() override; + void RobotPeriodic() override; + + private: + static constexpr int kLength = 60; + + // PWM port 9 + // Must be a PWM header, not MXP or DIO + frc::AddressableLED m_led{9}; + std::array + m_ledBuffer; // Reuse the buffer + // Store what the last hue of the first pixel is + int firstPixelHue = 0; +}; diff --git a/wpilibcExamples/src/test/cpp/examples/AddressableLED/cpp/RainbowTest.cpp b/wpilibcExamples/src/test/cpp/examples/AddressableLED/cpp/RainbowTest.cpp new file mode 100644 index 0000000000..553a2bbaec --- /dev/null +++ b/wpilibcExamples/src/test/cpp/examples/AddressableLED/cpp/RainbowTest.cpp @@ -0,0 +1,53 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "Robot.h" + +void AssertIndexColor(std::array data, + int index, int hue, int saturation, int value); + +TEST(RainbowTest, RainbowPattern) { + Robot robot; + robot.RobotInit(); + frc::sim::AddressableLEDSim ledSim = + frc::sim::AddressableLEDSim::CreateForChannel(9); + EXPECT_TRUE(ledSim.GetRunning()); + EXPECT_EQ(60, ledSim.GetLength()); + + auto rainbowFirstPixelHue = 0; + std::array data; + for (int iteration = 0; iteration < 100; iteration++) { + SCOPED_TRACE(fmt::format("Iteration {} of 100", iteration)); + robot.RobotPeriodic(); + ledSim.GetData(data.data()); + for (int i = 0; i < 60; i++) { + SCOPED_TRACE(fmt::format("LED {} of 60", i)); + auto hue = (rainbowFirstPixelHue + (i * 180 / 60)) % 180; + AssertIndexColor(data, i, hue, 255, 128); + } + rainbowFirstPixelHue += 3; + rainbowFirstPixelHue %= 180; + } +} + +void AssertIndexColor(std::array data, + int index, int hue, int saturation, int value) { + frc::Color8Bit color{frc::Color::FromHSV(hue, saturation, value)}; + + EXPECT_EQ(0, data[index].padding); + EXPECT_EQ(color.red, data[index].r & 0xFF); + EXPECT_EQ(color.green, data[index].g & 0xFF); + EXPECT_EQ(color.blue, data[index].b & 0xFF); +} diff --git a/wpilibcExamples/src/test/cpp/examples/AddressableLED/cpp/main.cpp b/wpilibcExamples/src/test/cpp/examples/AddressableLED/cpp/main.cpp new file mode 100644 index 0000000000..285c1d5267 --- /dev/null +++ b/wpilibcExamples/src/test/cpp/examples/AddressableLED/cpp/main.cpp @@ -0,0 +1,17 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#include + +#include "gtest/gtest.h" + +/** + * Runs all unit tests. + */ +int main(int argc, char** argv) { + HAL_Initialize(500, 0); + ::testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + return ret; +} diff --git a/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/addressableled/RainbowTest.java b/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/addressableled/RainbowTest.java new file mode 100644 index 0000000000..90acfb08bd --- /dev/null +++ b/wpilibjExamples/src/test/java/edu/wpi/first/wpilibj/examples/addressableled/RainbowTest.java @@ -0,0 +1,54 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package edu.wpi.first.wpilibj.examples.addressableled; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import edu.wpi.first.hal.HAL; +import edu.wpi.first.wpilibj.simulation.AddressableLEDSim; +import edu.wpi.first.wpilibj.util.Color; +import edu.wpi.first.wpilibj.util.Color8Bit; +import org.junit.jupiter.api.Test; + +class RainbowTest { + @Test + void rainbowPatternTest() { + HAL.initialize(500, 0); + try (var robot = new Robot()) { + robot.robotInit(); + AddressableLEDSim ledSim = AddressableLEDSim.createForChannel(9); + assertTrue(ledSim.getRunning()); + assertEquals(60, ledSim.getLength()); + + var rainbowFirstPixelHue = 0; + for (int iteration = 0; iteration < 100; iteration++) { + robot.robotPeriodic(); + var data = ledSim.getData(); + for (int i = 0; i < 60; i++) { + final var hue = (rainbowFirstPixelHue + (i * 180 / 60)) % 180; + assertIndexColor(data, i, hue, 255, 128); + } + rainbowFirstPixelHue += 3; + rainbowFirstPixelHue %= 180; + } + } + } + + private void assertIndexColor(byte[] data, int index, int hue, int saturation, int value) { + var color = new Color8Bit(Color.fromHSV(hue, saturation, value)); + int b = data[index * 4]; + int g = data[(index * 4) + 1]; + int r = data[(index * 4) + 2]; + int z = data[(index * 4) + 3]; + + assertAll( + () -> assertEquals(0, z), + () -> assertEquals(color.red, r & 0xFF), + () -> assertEquals(color.green, g & 0xFF), + () -> assertEquals(color.blue, b & 0xFF)); + } +}