diff --git a/.github/workflows/inspector.json b/.github/workflows/inspector.json index 83b55abcc8..71f688e7ed 100644 --- a/.github/workflows/inspector.json +++ b/.github/workflows/inspector.json @@ -26,5 +26,5 @@ "latestVersion":"6a5b6352807a8759bd0f012e57695c47f7ef7324" } ], - "ignoredFiles": ["source/docs/software/commandbased/command-scheduler.rst", "source/docs/software/hardware-apis/pneumatics/pressure.rst", "source/docs/software/hardware-apis/pneumatics/solenoids.rst", "source/docs/software/advanced-controls/state-space/state-space-pose-estimators.rst", "source/docs/software/commandbased/profilepid-subsystems-commands.rst", "source/docs/software/commandbased/subsystems.rst", "source/docs/software/telemetry/writing-sendable-classes.rst", "source/docs/software/advanced-controls/trajectories/troubleshooting.rst", "source/docs/software/hardware-apis/motors/wpi-drive-classes.rst", "source/docs/software/pathplanning/trajectory-tutorial/creating-drive-subsystem.rst", "source/docs/software/pathplanning/trajectory-tutorial/creating-following-trajectory.rst", "source/docs/software/pathplanning/trajectory-tutorial/entering-constants.rst", "source/docs/software/convenience-features/event-based.rst", "source/docs/software/dashboards/glass/field2d-widget.rst", "source/docs/software/dashboards/smartdashboard/choosing-an-autonomous-program-from-smartdashboard.rst", "source/docs/software/hardware-apis/misc/addressable-leds.rst"] + "ignoredFiles": ["source/docs/software/commandbased/command-scheduler.rst", "source/docs/software/hardware-apis/pneumatics/pressure.rst", "source/docs/software/hardware-apis/pneumatics/solenoids.rst", "source/docs/software/advanced-controls/state-space/state-space-pose-estimators.rst", "source/docs/software/commandbased/profilepid-subsystems-commands.rst", "source/docs/software/commandbased/subsystems.rst", "source/docs/software/telemetry/writing-sendable-classes.rst", "source/docs/software/advanced-controls/trajectories/troubleshooting.rst", "source/docs/software/hardware-apis/motors/wpi-drive-classes.rst", "source/docs/software/pathplanning/trajectory-tutorial/creating-drive-subsystem.rst", "source/docs/software/pathplanning/trajectory-tutorial/creating-following-trajectory.rst", "source/docs/software/pathplanning/trajectory-tutorial/entering-constants.rst", "source/docs/software/convenience-features/event-based.rst", "source/docs/software/dashboards/glass/field2d-widget.rst", "source/docs/software/dashboards/smartdashboard/choosing-an-autonomous-program-from-smartdashboard.rst"] } diff --git a/poetry.lock b/poetry.lock index fc8ebb62f4..3397032f85 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "alabaster" @@ -1704,37 +1704,51 @@ python-versions = ">=3.6" files = [ {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"}, - {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:d92f81886165cb14d7b067ef37e142256f1c6a90a65cd156b063a43da1708cfd"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"}, {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"}, - {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:b5edda50e5e9e15e54a6a8a0070302b00c518a9d32accc2346ad6c984aacd279"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"}, {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"}, - {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:7048c338b6c86627afb27faecf418768acb6331fc24cfa56c93e8c9780f815fa"}, {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win32.whl", hash = "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win_amd64.whl", hash = "sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b"}, {file = "ruamel.yaml.clib-0.2.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875"}, - {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3fcc54cb0c8b811ff66082de1680b4b14cf8a81dce0d4fbf665c2265a81e07a1"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win32.whl", hash = "sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b"}, {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"}, - {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:665f58bfd29b167039f714c6998178d27ccd83984084c286110ef26b230f259f"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win32.whl", hash = "sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe"}, {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"}, - {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9eb5dee2772b0f704ca2e45b1713e4e5198c18f515b52743576d196348f374d3"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win32.whl", hash = "sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5"}, {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15"}, {file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"}, diff --git a/source/_static/css/frc-rtd.css b/source/_static/css/frc-rtd.css index caf08e9b8c..8565d98320 100644 --- a/source/_static/css/frc-rtd.css +++ b/source/_static/css/frc-rtd.css @@ -45,6 +45,7 @@ .rst-content video { max-width: 100%; height: auto; + cursor: pointer; } .document img { diff --git a/source/docs/software/hardware-apis/misc/addressable-leds.rst b/source/docs/software/hardware-apis/misc/addressable-leds.rst index d6a5686f36..0cac97b54b 100644 --- a/source/docs/software/hardware-apis/misc/addressable-leds.rst +++ b/source/docs/software/hardware-apis/misc/addressable-leds.rst @@ -19,36 +19,30 @@ After the length of the strip has been set, you'll have to create an ``Addressab .. tab-item:: Java :sync: Java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/addressableled/Robot.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2025.1.1-beta-1/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/addressableled/Robot.java :language: java - :lines: 17-32 + :lines: 32-47 :linenos: - :lineno-start: 17 + :lineno-start: 34 .. tab-item:: C++ :sync: C++ - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/AddressableLED/include/Robot.h + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2025.1.1-beta-1/wpilibcExamples/src/main/cpp/examples/AddressableLED/include/Robot.h :language: c++ :lines: 12-12, 18-27 :linenos: :lineno-start: 11 - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/AddressableLED/cpp/Robot.cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2025.1.1-beta-1/wpilibcExamples/src/main/cpp/examples/AddressableLED/cpp/Robot.cpp :language: c++ :lines: 7-13 :linenos: :lineno-start: 7 -.. 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 +## Controlling Sections of an LED Strip -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. +The roboRIO can only control a single addressable LED output at a time, but there are often multiple physical LED strips daisy-chained around a robot, or a single flexible LED strip wrapped around structures on a robot. Individual sections can be accessed in Java using ``AddressableLEDBufferView``. Buffer views behave like subsections of the larger buffer, and can be accessed using indices in the typical [0, length) range. They can also be reversed, to allow for parallel serpentine sections to be animated in the same physical orientation (i.e. both sections would animate "forward" in the same direction, even if the strips are physically tip-to-tail). .. tab-set:: @@ -56,31 +50,44 @@ RGB stands for Red, Green, and Blue. This is a fairly common color model as it's :sync: Java ```Java - 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); + // Create the buffer + AddressableLEDBuffer m_buffer = new AddressableLEDBuffer(120); + + // Create the view for the section of the strip on the left side of the robot. + // This section spans LEDs from index 0 through index 59, inclusive. + AddressableLEDBufferView m_left = m_buffer.createView(0, 59); + + // The section of the strip on the right side of the robot. + // This section spans LEDs from index 60 through index 119, inclusive. + // This view is reversed to cancel out the serpentine arrangement of the + // physical LED strip on the robot. + AddressableLEDBufferView m_right = m_buffer.createView(60, 119).reversed(); ``` .. tab-item:: C++ :sync: C++ ```C++ - for (int i = 0; i < kLength; i++) { - m_ledBuffer[i].SetRGB(255, 0, 0); - } - m_led.SetData(m_ledBuffer); - ``` + // Create the buffer + std::array m_buffer; -### Using HSV Values + // Create the view for the section of the strip on the left side of the robot. + // This section spans LEDs from index 0 through index 59, inclusive. + std::view m_left = + std::ranges::take_view(m_buffer, 60); -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](https://www.google.com/search?q=color+picker), 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. + // The section of the strip on the right side of the robot. + // This section spans LEDs from index 60 through index 119, inclusive. + // This view is reversed to cancel out the serpentine arrangement of the + // physical LED strip on the robot. + std::view m_right = + std::ranges::reverse_view( + std::ranges::drop_view(m_buffer, 60)); + ``` -.. image:: images/hsv-models.png - :alt: HSV models picture +## LED Patterns -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). +The ``LEDPattern`` API simplifies setting LED data. Rather than needing to manually loop over every LED index, you can apply a pattern object to the data buffer directly. LED patterns are stateless, and can safely be applied to multiple buffers or views. .. tab-set:: @@ -88,10 +95,13 @@ LEDs can be set with the ``setHSV`` method that takes 4 arguments: index of the :sync: Java ```Java - 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); - } + // Create an LED pattern that sets the entire strip to solid red + LEDPattern red = LEDPattern.solid(Color.kRed); + + // Apply the LED pattern to the data buffer + red.applyTo(m_ledBuffer); + + // Write the data to the LED strip m_led.setData(m_ledBuffer); ``` @@ -99,58 +109,922 @@ LEDs can be set with the ``setHSV`` method that takes 4 arguments: index of the :sync: C++ ```C++ - for (int i = 0; i < kLength; i++) { - m_ledBuffer[i].SetHSV(0, 100, 100); - } + // Create an LED pattern that sets the entire strip to solid red + LEDPattern red = LEDPattern.Solid(Color::kRed); + + // Apply the LED pattern to the data buffer + red.ApplyTo(m_ledBuffer); + + // Write the data to the LED strip m_led.SetData(m_ledBuffer); ``` -## Creating a Rainbow Effect +### Creating a Rainbow Effect + +Using the built in ``LEDPattern.rainbow`` method, we can create a pattern that displays a full rainbow across an entire LED strip. Then, by calling ``scrollAtAbsoluteSpeed`` we can make it animate and cycle around the strip. ``rainbow`` accepts two arguments - one for the saturation and one for the value, expressed as a number from 0 to 255. -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. +.. note:: Animating effects like scrolling use the :ref:`Java units library ` and the :ref:`C++ units library ` for speeds and durations. -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. +The base rainbow pattern will look like this: -.. 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()``. +.. image:: images/rainbow.png + :alt: A full-brightness rainbow pattern + :width: 900 .. tab-set:: .. tab-item:: Java :sync: Java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/addressableled/Robot.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2025.1.1-beta-1/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/addressableled/Robot.java :language: java - :lines: 42-55 + :lines: 21-31 :linenos: - :lineno-start: 42 + :lineno-start: 21 .. tab-item:: C++ :sync: C++ - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/AddressableLED/cpp/Robot.cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2025.1.1-beta-1/wpilibcExamples/src/main/cpp/examples/AddressableLED/include/Robot.h :language: c++ - :lines: 22-35 + :lines: 27-37 :linenos: - :lineno-start: 22 + :lineno-start: 27 -Now that we have our ``rainbow`` method created, we have to actually call the method and set the data of the LED. +Now that the rainbow pattern is defined, we only need to apply it. .. tab-set:: .. tab-item:: Java :sync: Java - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/addressableled/Robot.java + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2025.1.1-beta-1/wpilibjExamples/src/main/java/edu/wpi/first/wpilibj/examples/addressableled/Robot.java :language: java - :lines: 34-40 + :lines: 50-56 :linenos: - :lineno-start: 34 + :lineno-start: 50 .. tab-item:: C++ :sync: C++ - .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2024.3.2/wpilibcExamples/src/main/cpp/examples/AddressableLED/cpp/Robot.cpp + .. remoteliteralinclude:: https://raw.githubusercontent.com/wpilibsuite/allwpilib/v2025.1.1-beta-1/wpilibcExamples/src/main/cpp/examples/AddressableLED/cpp/Robot.cpp :language: c++ :lines: 15-20 :linenos: :lineno-start: 15 + +.. only:: html + + .. video:: images/scrolling-rainbow.mp4 + :loop: + :nocontrols: + :poster: ../../../../_images/rainbow.png + :width: 900 + +.. only:: not html + + .. image:: images/discontinuous-gradient.png + :width: 900 + +### Controlling when patterns are applied + +Use commands. The command framework is specifically built for managing when actions run and stop, and prevents multiple actions from running simultaneously. + +.. tab-set:: + + .. tab-item:: Java + :sync: Java + + ```Java + public class LEDSubsystem extends SubsystemBase { + private static final int kPort = 9; + private static final int kLength = 120; + + private final AddressableLED m_led; + private final AddressableLEDBuffer m_buffer; + + public LEDSubsystem() { + m_led = new AddressableLED(kPort); + m_buffer = new AddressableLEDBuffer(kLength); + m_led.setLength(kLength); + m_led.start(); + + // Set the default command to turn the strip off, otherwise the last colors written by + // the last command to run will continue to be displayed. + // Note: Other default patterns could be used instead! + setDefaultCommand(runPattern(LEDPattern.solid(Color.kBlack)).withName("Off")); + } + + @Override + public void periodic() { + // Periodically send the latest LED color data to the LED strip for it to display + m_led.setData(m_buffer); + } + + /** + * Creates a command that runs a pattern on the entire LED strip. + * + * @param pattern the LED pattern to run + */ + public Command runPattern(LEDPattern pattern) { + return run(() -> pattern.apply(m_buffer)); + } + } + ``` + + .. tab-item:: C++ + :sync: C++ + + Header: + + ```C++ + class LEDSubsystem : public SubsystemBase { + public: + LEDSubsystem(); + void Periodic() override; + + frc::CommandPtr RunPattern(frc::LEDPattern pattern); + + private: + static constexpr int kPort = 9; + static constexpr int kLength = 120; + frc::AddressableLED m_led{kPort}; + std::array m_ledBuffer; + } + ``` + + ```C++ + LEDSubsystem::LEDSubsystem() { + m_led.SetLength(kLength); + m_led.Start(); + + // Set the default command to turn the strip off, otherwise the last colors written by + // the last command to run will continue to be displayed. + // Note: Other default patterns could be used instead! + SetDefaultCommand(RunPattern(frc::LEDPattern::Solid(frc::Color::kBlack)).WithName("Off")); + } + + LEDSubsystem::Periodic() { + // Periodically send the latest LED color data to the LED strip for it to display + m_led.SetData(m_ledBuffer); + } + + frc::CommandPtr LEDSubsystem::RunPattern(frc::LEDPattern pattern) { + // std::move is necessary for inline pattern declarations to work + // Otherwise we could have a use-after-free! + return Run([this, pattern = std::move(pattern)] { pattern.ApplyTo(m_buffer); }); + } + ``` + + +### Basic effects + +The basic effects can all be created from the factory methods declared in the ``LEDPattern`` class + +#### Solid color + +.. image:: images/solid.png + :alt: A solid red LED pattern + :width: 900 + +The solid color pattern sets the target LED buffer to a single solid color. + +.. tab-set:: + + .. tab-item:: Java + :sync: Java + + ```Java + // Create an LED pattern that sets the entire strip to solid red + LEDPattern red = LEDPattern.solid(Color.kRed); + + // Apply the LED pattern to the data buffer + red.applyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.setData(m_ledBuffer); + ``` + + .. tab-item:: C++ + :sync: C++ + + ```C++ + // Create an LED pattern that sets the entire strip to solid red + LEDPattern red = LEDPattern.Solid(Color::kRed); + + // Apply the LED pattern to the data buffer + red.ApplyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.SetData(m_ledBuffer); + ``` + +#### Continuous Gradient + +The gradient pattern sets the target buffer to display a smooth gradient between the specified colors. The gradient wraps around so scrolling effects can be seamless. + +.. image:: images/continuous-gradient.png + :alt: A contiuous red-to-blue-to-red gradient + :width: 900 + +.. tab-set:: + + .. tab-item:: Java + :sync: Java + + ```Java + // Create an LED pattern that displays a red-to-blue gradient. + // The LED strip will be red at both ends and blue in the center, + // with smooth gradients between + LEDPattern gradient = LEDPattern.gradient(LEDPattern.GradientType.kContinuous, Color.kRed, Color.kBlue); + + // Apply the LED pattern to the data buffer + gradient.applyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.setData(m_ledBuffer); + ``` + + .. tab-item:: C++ + :sync: C++ + + ```C++ + // Create an LED pattern that displays a red-to-blue gradient. + // The LED strip will be red at both ends and blue in the center, + // with smooth gradients between + std::array colors{Color::kRed, Color::kBlue}; + LEDPattern gradient = LEDPattern.Gradient(LEDPattern::GradientType::kContinuous, colors); + + // Apply the LED pattern to the data buffer + gradient.ApplyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.SetData(m_ledBuffer); + ``` + +#### Discontinuous Gradient + +The gradient pattern sets the target buffer to display a smooth gradient between the specified colors. The gradient does not wrap around so it can be used for non-scrolling patterns that don't care about continuity. + +.. image:: images/discontinuous-gradient.png + :alt: A discontiuous red-to-blue gradient + :width: 900 + +.. tab-set:: + + .. tab-item:: Java + :sync: Java + + ```Java + // Create an LED pattern that displays a red-to-blue gradient. + // The LED strip will be red at one end and blue at the other. + LEDPattern gradient = LEDPattern.gradient(LEDPattern.GradientType.kDiscontinuous, Color.kRed, Color.kBlue); + + // Apply the LED pattern to the data buffer + gradient.applyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.setData(m_ledBuffer); + ``` + + .. tab-item:: C++ + :sync: C++ + + ```C++ + // Create an LED pattern that displays a red-to-blue gradient. + // The LED strip will be red at one end and blue at the other. + std::array colors{Color::kRed, Color::kBlue}; + LEDPattern gradient = LEDPattern.Gradient(LEDPattern::GradientType::kDiscontinuous, colors); + + // Apply the LED pattern to the data buffer + gradient.ApplyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.SetData(m_ledBuffer); + ``` + +#### Steps + +.. image:: images/steps.png + :alt: Steps of solid red on one half and solid blue on the other + :width: 900 + +Displays segments of solid colors along the target buffer. This combines well with mask and overlay combination effects. + +Steps are specified as a combination of the *starting position* of that color, as a number between 0 (start of the buffer) and 1 (end of the buffer). + +.. note:: If the first step does not start at zero, every LED before that step starts will be set to black - effectively, as if there is a default step of ``(0, Color.kBlack)`` that can be overwritten. + +.. tab-set:: + + .. tab-item:: Java + :sync: Java + + ```Java + // Create an LED pattern that displays the first half of a strip as solid red, + // and the second half of the strip as solid blue. + LEDPattern steps = LEDPattern.steps(Map.of(0, Color.kRed, 0.5, Color.kBlue)); + + // Apply the LED pattern to the data buffer + steps.applyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.setData(m_ledBuffer); + ``` + + .. tab-item:: C++ + :sync: C++ + + ```C++ + // Create an LED pattern that displays the first half of a strip as solid red, + // and the second half of the strip as solid blue. + std::array, 2> colorSteps{std::pair{0.0, Color::kRed}, + std::pair{0.5, Color::kBlue}}; + LEDPattern steps = LEDPattern.Steps(colorSteps); + + // Apply the LED pattern to the data buffer + gradient.ApplyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.SetData(m_ledBuffer); + ``` + +#### Progress mask + +.. only:: html + + .. video:: images/progress-mask.mp4 + :loop: + :nocontrols: + :width: 900 + +.. only:: not html + + .. image:: images/discontinuous-gradient.png + :width: 900 + +Slightly different from the basic color patterns, the progress mask pattern generates a white-and-black pattern where the white portion is a varying length depending on the value of the value function. This can be combined with another pattern using a :ref:`mask ` to display a portion of another base pattern depending on the progress of some process - such as the position of a mechanism in its range of motion (eg an elevator's height) or the progress of a PID controller towards its goal. + +.. tab-set:: + + .. tab-item:: Java + :sync: Java + + ```Java + // Create an LED pattern that displays a black-and-white mask that displays the current height of an elevator + // mechanism. This can be combined with other patterns to change the displayed color to something other than white. + LEDPattern pattern = LEDPattern.progressMaskLayer(() -> m_elevator.getHeight() / m_elevator.getMaxHeight()); + + // Apply the LED pattern to the data buffer + pattern.applyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.setData(m_ledBuffer); + ``` + + .. tab-item:: C++ + :sync: C++ + + ```C++ + // Create an LED pattern that displays a black-and-white mask that displays the current height of an elevator + // mechanism. This can be combined with other patterns to change the displayed color to something other than white. + LEDPattern pattern = LEDPattern::ProgressMaskLayer([&]() { m_elevator.GetHeight() / m_elevator.GetMaxHeight() }); + + // Apply the LED pattern to the data buffer + pattern.ApplyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.SetData(m_ledBuffer); + ``` + +### Modifying effects + +Basic LED patterns can be combined with modifier effects to create new patterns with a combination of effects. Multiple modifiers can be used together to create complex patterns. + +.. note:: The built in animating effects like blinking and scrolling are based on the time returned by ``WPIUtilJNI.now()`` - in effect, they will play as if they started when the robot booted. Because all built in animation patterns are periodic, this means that the *first* period of a pattern may be truncated at any arbitrary point between 0% and 100%, and every period after that will play normally. + +#### Offset + +.. image:: images/offset.png + :alt: A discontinuous gradient, offset by 40 pixels + :width: 900 + +Offsets can be used to bias patterns forward of backward by a certain number of pixels. Offset patterns will wrap around the end of an LED strip; offset values can be positive (biasing *away* from the start of the strip) or negative (biasing *towards* the start of the strip). + +.. tab-set:: + + .. tab-item:: Java + :sync: Java + + ```Java + // Create an LED pattern that displays a red-to-blue gradient, offset 40 pixels forward. + LEDPattern base = LEDPattern.discontinuousGradient(Color.kRed, Color.kBlue); + LEDPattern pattern = base.offsetBy(40); + LEDPattern negative = base.offsetBy(-20); // Equivalent to the above when applied to a 60-LED buffer + + // Apply the LED pattern to the data buffer + pattern.applyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.setData(m_ledBuffer); + ``` + + .. tab-item:: C++ + :sync: C++ + + ```C++ + // Create an LED pattern that displays a red-to-blue gradient, offset 40 pixels forward. + std::array colors{Color::kRed, Color::kBlue}; + LEDPattern base = LEDPattern::DiscontinuousGradient(colors); + LEDPattern pattern = base.OffsetBy(40); + LEDPattern negative = base.OffsetBy(-20); // Equivalent to the above when applied to a 60-LED buffer + + // Apply the LED pattern to the data buffer + heightDisplay.ApplyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.SetData(m_ledBuffer); + ``` + +#### Reverse + +.. image:: images/reverse.png + :alt: A discontinuous gradient running from blue-to-red instead of red-to-blue + :width: 900 + +Patterns and animations can be reversed to flip the direction that patterns are applied in; instead of starting from the lowest-indexed pixel in a buffer or view, a reversed pattern will start from the highest-indexed pixel and move toward the lowest-index pixel. A reversed :ref:`scrolling pattern ` will scroll in reverse, as if its velocity's sign was flipped. + +.. tab-set:: + + .. tab-item:: Java + :sync: Java + + ```Java + // Create an LED pattern that displays a red-to-blue gradient, then reverse it so it displays blue-to-red. + LEDPattern base = LEDPattern.discontinuousGradient(Color.kRed, Color.kBlue); + LEDPattern pattern = base.reversed(); + + // Apply the LED pattern to the data buffer + pattern.applyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.setData(m_ledBuffer); + ``` + + .. tab-item:: C++ + :sync: C++ + + ```C++ + // Create an LED pattern that displays a red-to-blue gradient, then reverse it so it displays blue-to-red. + std::array colors{Color::kRed, Color::kBlue}; + LEDPattern base = LEDPattern::DiscontinuousGradient(colors); + LEDPattern pattern = base.Reversed(); + + // Apply the LED pattern to the data buffer + heightDisplay.ApplyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.SetData(m_ledBuffer); + ``` + +#### Scroll + +.. only:: html + + .. video:: images/scroll-relative.mp4 + :loop: + :nocontrols: + :poster: ../../../../_images/discontinuous-gradient.png + :width: 900 + + .. video:: images/scroll-absolute.mp4 + :loop: + :nocontrols: + :poster: ../../../../_images/discontinuous-gradient.png + :width: 900 + +.. only:: not html + + .. image:: images/discontinuous-gradient.png + :width: 900 + + .. image:: images/discontinuous-gradient.png + :width: 900 + +Scrolling can be controlled in two different ways: either at a speed as a function of the length of the buffer or view to which it is applied (i.e., the scrolling speed is in terms of percentage per second, or a similar unit), or as a function of the density of the physical LED strips (i.e. scrolling speed is in meters per second, or a similar unit). Relative velocities are particularly useful when a scrolling pattern is applied to different LED strips with different LED spacing (such as one strip with 120 LEDs per meter daisy chained to a second strip with 60 or 144 LEDs per meter), when prototyping before having a particular LED strip in mind (where the density isn't yet known), or when LED strips are quickly changed out. Scrolling at a fixed real-world speed (eg ``InchesPerSecond.of(2)``) may be more understandable to readers, but will move faster or slower when applied to an LED strip with a lower or higher pixel density, respectively. + +.. tab-set:: + + .. tab-item:: Java + :sync: Java + + ```Java + // Create an LED pattern that displays a red-to-blue gradient, then scroll at one quarter of the LED strip's length per second. + // For a half-meter length of a 120 LED-per-meter strip, this is equivalent to scrolling at 12.5 centimeters per second. + Distance ledSpacing = Meters.of(1 / 120.0); + LEDPattern base = LEDPattern.discontinuousGradient(Color.kRed, Color.kBlue); + LEDPattern pattern = base.scrollAtRelativeSpeed(Percent.per(Second).of(25)); + LEDPattern absolute = base.scrollAtAbsoluteSpeed(Centimeters.per(Second).of(12.5), ledSpacing); + + // Apply the LED pattern to the data buffer + pattern.applyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.setData(m_ledBuffer); + ``` + + .. tab-item:: C++ + :sync: C++ + + ```C++ + // Create an LED pattern that displays a red-to-blue gradient, then scroll at one quarter of the LED strip's length per second. + // For a half-meter length of a 120 LED-per-meter strip, this is equivalent to scrolling at 12.5 centimeters per second. + std::array colors{Color::kRed, Color::kBlue}; + LEDPattern base = LEDPattern::DiscontinuousGradient(colors); + LEDPattern pattern = base.ScrollAtRelativeSpeed(units::hertz_t{0.25}); + LEDPattern absolute = base.ScrollAtAbsoluteSpeed(0.125_mps, units::meter_t{1/120.0}); + + // Apply the LED pattern to the data buffer + heightDisplay.ApplyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.SetData(m_ledBuffer); + ``` + +#### Breathe + +.. only:: html + + .. video:: images/breathe.mp4 + :loop: + :nocontrols: + :poster: ../../../../_images/discontinuous-gradient.png + :width: 900 + +.. only:: not html + + .. image:: images/discontinuous-gradient.png + :width: 900 + +A breathing modifier will make the base pattern brighten and dim in a sinusoidal pattern over the given period of time. Brightness is relative to the original brightness of the base pattern - breathing will only make it dimmer, never brighter than the original. + +.. tab-set:: + + .. tab-item:: Java + :sync: Java + + ```Java + // Create an LED pattern that displays a red-to-blue gradient, breathing at a 2 second period (0.5 Hz) + LEDPattern base = LEDPattern.discontinuousGradient(Color.kRed, Color.kBlue); + LEDPattern pattern = base.breathe(Seconds.of(2)); + + // Apply the LED pattern to the data buffer + pattern.applyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.setData(m_ledBuffer); + ``` + + .. tab-item:: C++ + :sync: C++ + + ```C++ + // Create an LED pattern that displays a red-to-blue gradient, breathing at a 2 second period (0.5 Hz) + std::array colors{Color::kRed, Color::kBlue}; + LEDPattern base = LEDPattern::DiscontinuousGradient(colors); + LEDPattern pattern = base.Breathe(2_s); + + // Apply the LED pattern to the data buffer + heightDisplay.ApplyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.SetData(m_ledBuffer); + ``` + +#### Blink + +.. only:: html + + .. video:: images/blink-symmetric.mp4 + :loop: + :nocontrols: + :poster: ../../../../_images/discontinuous-gradient.png + :width: 900 + + .. video:: images/blink-asymmetric.mp4 + :loop: + :nocontrols: + :poster: ../../../../_images/discontinuous-gradient.png + :width: 900 + +.. only:: not html + + .. image:: images/discontinuous-gradient.png + :width: 900 + + .. image:: images/discontinuous-gradient.png + :width: 900 + +Blinking can be done in one of three ways: + +1. Symmetrically, where an equal amount of time is spent in the "on" and "off" states per cycle +2. Asymetrically, where the time spent "on" can be configured independently from the time spent "off" +3. Synchronously, where the time spent on and off is synchronized with an external source (for example, the state of the RSL) + +.. tab-set:: + + .. tab-item:: Java + :sync: Java + + ```Java + // Create an LED pattern that displays a red-to-blue gradient, blinking at various rates. + LEDPattern base = LEDPattern.discontinuousGradient(Color.kRed, Color.kBlue); + + // 1.5 seconds on, 1.5 seconds off, for a total period of 3 seconds + LEDPattern pattern = base.blink(Seconds.of(1.5)); + + // 2 seconds on, 1 second off, for a total period of 3 seconds + LEDPattern asymmetric = base.blink(Seconds.of(2), Seconds.of(1)); + + // Turn the base pattern on when the RSL is on, and off when the RSL is off + LEDPattern sycned = base.synchronizedBlink(RobotController::getRSLState); + + // Apply the LED pattern to the data buffer + pattern.applyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.setData(m_ledBuffer); + ``` + + .. tab-item:: C++ + :sync: C++ + + ```C++ + // Create an LED pattern that displays a red-to-blue gradient, blinking at various rates. + std::array colors{Color::kRed, Color::kBlue}; + LEDPattern base = LEDPattern::DiscontinuousGradient(colors); + + // 1.5 seconds on, 1.5 seconds off, for a total period of 3 seconds + LEDPattern pattern = base.Blink(1.5_s); + + // 2 seconds on, 1 second off, for a total period of 3 seconds + LEDPattern asymmetric = base.Blink(2_s, 1_s)); + + // Turn the base pattern on when the RSL is on, and off when the RSL is off + LEDPattern sycned = base.SynchronizedBlink([]() { return RobotController.GetRSLState(); }); + + // Apply the LED pattern to the data buffer + pattern.ApplyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.SetData(m_ledBuffer); + ``` + +#### Brightness + +.. image:: images/brightness.png + :alt: A discontinuous gradient at half brightness + :width: 900 + +Patterns can be brightened and dimmed relative to their original brightness; a brightness value of 100% is identical to the original pattern, a value of 200% is twice as bright, and a value of 0% is completely turned off. This can be useful in a pinch to tone down patterns that are too bright (apologies to the 2024 NE Greater Boston district event staff, who were subjected to a maximimum brightness white flashing pattern with a precursor version of this library before the brightness modifier was added). + +.. note:: For speed, brightness calculations are done naively in the RGB color space instead of HSL/HSV/Lab. This sacrifices accuracy, so large changes in brightness may look undersaturated. + +.. tab-set:: + + .. tab-item:: Java + :sync: Java + + ```Java + // Create an LED pattern that displays a red-to-blue gradient at half brightness + LEDPattern base = LEDPattern.discontinuousGradient(Color.kRed, Color.kBlue); + LEDPattern pattern = base.atBrightness(Percent.of(50)); + + // Apply the LED pattern to the data buffer + pattern.applyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.setData(m_ledBuffer); + ``` + + .. tab-item:: C++ + :sync: C++ + + ```C++ + // Create an LED pattern that displays a red-to-blue gradient at half brightness + std::array colors{Color::kRed, Color::kBlue}; + LEDPattern base = LEDPattern::DiscontinuousGradient(colors); + LEDPattern pattern = base.AtBrightness(0.5); + + // Apply the LED pattern to the data buffer + pattern.ApplyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.SetData(m_ledBuffer); + ``` + +### Combinatory effects + +Complex LED patterns are built up from combining simple base patterns (such as solid colors or gradients) with animating effects (such as scrolling or breathing) and combinatory effects (like masks and overlays). Multiple effects can be combined at once, like in the scrolling rainbow effect above that takes a basic base effect - a static rainbow - and then adds a scrolling effect to it. + +#### Mask + +.. only:: html + + .. video:: images/mask.mp4 + :loop: + :nocontrols: + :poster: ../../../../_images/discontinuous-gradient.png + :width: 900 + +.. only:: not html + + .. image:: images/rainbow.png + :width: 900 + +Masks work by combining the RGB values of two patterns and keeping only the values that are shared by both. The combination works on the individual bits of each color using a bitwise AND operation - for example, if a pixel's red channel were set to 255 by one pattern (represented as 11111111 in binary), then the output red color would be identical to the red channel of the second pattern. If the first pattern sets it to zero (00000000 in binary), then the output red color would also be zero, regardless of whatever the second pattern sets. For this reason, black (all zeroes) and white (all ones) masks are very useful for selectively enabling and disabling parts of another pattern. Other mask colors can be used as well: masking with solid red would keep only the red channel of the original pattern, while discarding all green and blue values. + +.. tab-set:: + + .. tab-item:: Java + :sync: Java + + ```Java + // Create an LED pattern that displays a red-to-blue gradient at a variable length + // depending on the relative position of the elevator. The blue end of the gradient + // will only be shown when the elevator gets close to its maximum height; otherwise, + // that end will be solid black when the elevator is at lower heights. + LEDPattern base = LEDPattern.discontinuousGradient(Color.kRed, Color.kBlue); + LEDPattern mask = LEDPattern.progressMaskLayer(() -> m_elevator.getHeight() / m_elevator.getMaxHeight()); + LEDPattern heightDisplay = base.mask(mask); + + // Apply the LED pattern to the data buffer + heightDisplay.applyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.setData(m_ledBuffer); + ``` + + .. tab-item:: C++ + :sync: C++ + + ```C++ + // Create an LED pattern that displays a red-to-blue gradient at a variable length + // depending on the relative position of the elevator. The blue end of the gradient + // will only be shown when the elevator gets close to its maximum height; otherwise, + // that end will be solid black when the elevator is at lower heights. + std::array colors{Color::kRed, Color::kBlue}; + LEDPattern base = LEDPattern::DiscontinuousGradient(colors); + LEDPattern mask = LEDPattern::ProgressMaskLayer([&]() { m_elevator.GetHeight() / m_elevator.GetMaxHeight() }); + LEDPattern heightDisplay = base.Mask(mask); + + // Apply the LED pattern to the data buffer + heightDisplay.ApplyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.SetData(m_ledBuffer); + ``` + + +.. only:: html + + .. video:: images/rainbow-with-scrolling-mask.mp4 + :loop: + :nocontrols: + :poster: ../../../../_images/rainbow.png + :width: 900 + +.. only:: not html + + .. image:: images/rainbow.png + :width: 900 + +Masks can also be animated (see :ref:`progressMask `). Masking a base pattern with a scrolling pattern will result in a panning effect. The animation above was generated by masking a rainbow pattern with a scrolling white/black pattern + +.. tab-set:: + + .. tab-item:: Java + :sync: Java + + ```Java + Map maskSteps = Map.of(0, Color.kWhite, 0.5, Color.kBlack); + LEDPattern base = LEDPattern.rainbow(255, 255); + LEDPattern mask = + LEDPattern.steps(maskSteps).scrollAtRelativeSpeed(Percent.per(Second).of(0.25)); + + LEDPattern pattern = base.mask(mask); + + // Apply the LED pattern to the data buffer + pattern.applyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.setData(m_ledBuffer); + ``` + + .. tab-item:: C++ + :sync: C++ + + ```C++ + std::array, 2> maskSteps{std::pair{0.0, Color::kWhite}, + std::pair{0.5, Color::kBlack}}; + LEDPattern base = LEDPattern::Rainbow(255, 255); + LEDPattern mask = + LEDPattern::Steps(maskSteps).ScrollAtRelativeSpeed(units::hertz_t{0.25}); + + LEDPattern pattern = base.Mask(mask); + + // Apply the LED pattern to the data buffer + pattern.ApplyTo(m_ledBuffer); + + // Write the data to the LED strip + m_led.SetData(m_ledBuffer); + ``` + +#### Overlay + +Overlays can be used to "stack" patterns atop each other, where black pixels (set to ``Color.kBlack``, RGB value #000000) are treated as transparent and allow a lower layer to be displayed. Upper layers are typically combined with :ref:`masks ` to set transparent sections; recall that masking a pixel with ``Color.kBlack`` will *set* that pixel to black, which will then be treated by the overlay as transparent. + +#### Blend + +Blends will combine the output colors of patterns together, by averaging out the individual RGB colors for every pixel. Like the :ref:`brightness modifier `, this tends to output colors that are more desaturated than its inputs. + +## Low Level Access + +``LEDPattern`` is an easy and convenient way of controlling LEDs, but direct access to the LED colors is sometimes needed for custom patterns and animations. + +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. Low-level access is typically done with an indexed for-loop that iterates over each LED index of the section to control. This method can be used for both ``AddressableLEDBuffer`` and ``AddressableLEDBufferView`` objects in Java, and for ``std::span`` for C++. + +.. note:: RGB stands for Red, Green, and Blue. This is a fairly common color model as it's quite easy to understand, and it corresponds with a typical LED configuration that's comprised of one red, one green, and one blue sub-LED. 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. + +.. note:: 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](https://www.google.com/search?q=color+picker), 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. + +These examples demonstrate setting an entire LED strip to solid red using the RGB and HSV methods: + +.. tab-set:: + + .. tab-item:: Java (RGB) + :sync: Java + + ```Java + 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); + ``` + + .. tab-item:: C++ (RGB) + :sync: C++ + + ```C++ + for (int i = 0; i < kLength; i++) { + m_ledBuffer[i].SetRGB(255, 0, 0); + } + m_led.SetData(m_ledBuffer); + ``` + + .. tab-item:: Java (HSV) + + ```Java + 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); + ``` + + .. tab-item:: C++ (HSV) + + ```C++ + for (int i = 0; i < kLength; i++) { + m_ledBuffer[i].SetHSV(0, 100, 100); + } + m_led.SetData(m_ledBuffer); + ``` + + +### Using HSV Values + +.. image:: images/hsv-models.png + :alt: HSV models picture + :width: 900 + + +.. only:: html + + .. raw:: html + + diff --git a/source/docs/software/hardware-apis/misc/images/blink-asymmetric.gif b/source/docs/software/hardware-apis/misc/images/blink-asymmetric.gif new file mode 100644 index 0000000000..d04884b04e Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/blink-asymmetric.gif differ diff --git a/source/docs/software/hardware-apis/misc/images/blink-asymmetric.mp4 b/source/docs/software/hardware-apis/misc/images/blink-asymmetric.mp4 new file mode 100644 index 0000000000..70bfa340a2 Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/blink-asymmetric.mp4 differ diff --git a/source/docs/software/hardware-apis/misc/images/blink-symmetric.gif b/source/docs/software/hardware-apis/misc/images/blink-symmetric.gif new file mode 100644 index 0000000000..92e37ee63e Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/blink-symmetric.gif differ diff --git a/source/docs/software/hardware-apis/misc/images/blink-symmetric.mp4 b/source/docs/software/hardware-apis/misc/images/blink-symmetric.mp4 new file mode 100644 index 0000000000..41772a5ba8 Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/blink-symmetric.mp4 differ diff --git a/source/docs/software/hardware-apis/misc/images/breathe.gif b/source/docs/software/hardware-apis/misc/images/breathe.gif new file mode 100644 index 0000000000..207c4eae64 Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/breathe.gif differ diff --git a/source/docs/software/hardware-apis/misc/images/breathe.mp4 b/source/docs/software/hardware-apis/misc/images/breathe.mp4 new file mode 100644 index 0000000000..11d40260c3 Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/breathe.mp4 differ diff --git a/source/docs/software/hardware-apis/misc/images/brightness.png b/source/docs/software/hardware-apis/misc/images/brightness.png new file mode 100644 index 0000000000..d37153eb50 Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/brightness.png differ diff --git a/source/docs/software/hardware-apis/misc/images/continuous-gradient.png b/source/docs/software/hardware-apis/misc/images/continuous-gradient.png new file mode 100644 index 0000000000..075d7f5e4e Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/continuous-gradient.png differ diff --git a/source/docs/software/hardware-apis/misc/images/discontinuous-gradient.png b/source/docs/software/hardware-apis/misc/images/discontinuous-gradient.png new file mode 100644 index 0000000000..98f43a54a2 Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/discontinuous-gradient.png differ diff --git a/source/docs/software/hardware-apis/misc/images/mask.gif b/source/docs/software/hardware-apis/misc/images/mask.gif new file mode 100644 index 0000000000..1fc7c3a5c5 Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/mask.gif differ diff --git a/source/docs/software/hardware-apis/misc/images/mask.mp4 b/source/docs/software/hardware-apis/misc/images/mask.mp4 new file mode 100644 index 0000000000..730c82818e Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/mask.mp4 differ diff --git a/source/docs/software/hardware-apis/misc/images/masked-rainbow-scrolling.gif b/source/docs/software/hardware-apis/misc/images/masked-rainbow-scrolling.gif new file mode 100644 index 0000000000..1ce517c70f Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/masked-rainbow-scrolling.gif differ diff --git a/source/docs/software/hardware-apis/misc/images/masked-rainbow-scrolling.mp4 b/source/docs/software/hardware-apis/misc/images/masked-rainbow-scrolling.mp4 new file mode 100644 index 0000000000..d68ed3b6a1 Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/masked-rainbow-scrolling.mp4 differ diff --git a/source/docs/software/hardware-apis/misc/images/offset.png b/source/docs/software/hardware-apis/misc/images/offset.png new file mode 100644 index 0000000000..2e37e6245e Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/offset.png differ diff --git a/source/docs/software/hardware-apis/misc/images/progress-mask.gif b/source/docs/software/hardware-apis/misc/images/progress-mask.gif new file mode 100644 index 0000000000..0d02f2bf60 Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/progress-mask.gif differ diff --git a/source/docs/software/hardware-apis/misc/images/progress-mask.mp4 b/source/docs/software/hardware-apis/misc/images/progress-mask.mp4 new file mode 100644 index 0000000000..8196a9eadc Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/progress-mask.mp4 differ diff --git a/source/docs/software/hardware-apis/misc/images/rainbow-with-scrolling-mask.gif b/source/docs/software/hardware-apis/misc/images/rainbow-with-scrolling-mask.gif new file mode 100644 index 0000000000..53aac2d009 Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/rainbow-with-scrolling-mask.gif differ diff --git a/source/docs/software/hardware-apis/misc/images/rainbow-with-scrolling-mask.mp4 b/source/docs/software/hardware-apis/misc/images/rainbow-with-scrolling-mask.mp4 new file mode 100644 index 0000000000..e8a8c0b299 Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/rainbow-with-scrolling-mask.mp4 differ diff --git a/source/docs/software/hardware-apis/misc/images/rainbow.png b/source/docs/software/hardware-apis/misc/images/rainbow.png new file mode 100644 index 0000000000..1f74e48caa Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/rainbow.png differ diff --git a/source/docs/software/hardware-apis/misc/images/reverse.png b/source/docs/software/hardware-apis/misc/images/reverse.png new file mode 100644 index 0000000000..901d90fc46 Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/reverse.png differ diff --git a/source/docs/software/hardware-apis/misc/images/scroll-absolute.gif b/source/docs/software/hardware-apis/misc/images/scroll-absolute.gif new file mode 100644 index 0000000000..dd49f04725 Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/scroll-absolute.gif differ diff --git a/source/docs/software/hardware-apis/misc/images/scroll-absolute.mp4 b/source/docs/software/hardware-apis/misc/images/scroll-absolute.mp4 new file mode 100644 index 0000000000..021b07bae2 Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/scroll-absolute.mp4 differ diff --git a/source/docs/software/hardware-apis/misc/images/scroll-relative.gif b/source/docs/software/hardware-apis/misc/images/scroll-relative.gif new file mode 100644 index 0000000000..e686e8f83e Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/scroll-relative.gif differ diff --git a/source/docs/software/hardware-apis/misc/images/scroll-relative.mp4 b/source/docs/software/hardware-apis/misc/images/scroll-relative.mp4 new file mode 100644 index 0000000000..894b8335f6 Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/scroll-relative.mp4 differ diff --git a/source/docs/software/hardware-apis/misc/images/scrolling-rainbow.gif b/source/docs/software/hardware-apis/misc/images/scrolling-rainbow.gif new file mode 100644 index 0000000000..cf25ec50e6 Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/scrolling-rainbow.gif differ diff --git a/source/docs/software/hardware-apis/misc/images/scrolling-rainbow.mp4 b/source/docs/software/hardware-apis/misc/images/scrolling-rainbow.mp4 new file mode 100644 index 0000000000..0f2d716a4f Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/scrolling-rainbow.mp4 differ diff --git a/source/docs/software/hardware-apis/misc/images/solid.png b/source/docs/software/hardware-apis/misc/images/solid.png new file mode 100644 index 0000000000..0960b86b0c Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/solid.png differ diff --git a/source/docs/software/hardware-apis/misc/images/steps.png b/source/docs/software/hardware-apis/misc/images/steps.png new file mode 100644 index 0000000000..c2b42fe69e Binary files /dev/null and b/source/docs/software/hardware-apis/misc/images/steps.png differ