Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add LED pattern API for easily animating addressable LEDs #6344

Open
wants to merge 38 commits into
base: main
Choose a base branch
from

Conversation

SamCarlberg
Copy link
Member

Add LEDReader and LEDWriter helper interfaces to facilitate composing simple patterns into more complex ones, e.g. LEDPattern.solid(Color.kBlue).breathe(Seconds.of(0.75)). Pattern composition relies on changing out the write behavior; for example, offsetBy increments the indexes to write to; while blink will switch between playing a base pattern and turning off all the LEDs.

Add a view class for splitting a single large buffer into smaller distinct sections, which is useful for dealing with long chained LED strips mounted on different parts of a robot. Views cannot be written directly to an LED strip (in fact, trying to do so won't even compile)

Adds some utility methods to the Color class for interpolating between two colors, and support color representations with 32-bit integers to avoid object allocations

Examples

Command based subsystem:

class LEDs extends SubsystemBase {
  private final AddressableLED m_led;
  private final AddressableLEDBuffer m_ledData;
  private final AddressableLEDBufferView m_leftSide;
  private final AddressableLEDBufferView m_rightSide;

  public LEDs() {
    m_led = new AddressableLED(9);
    m_led.setLength(120);
    m_ledData = new AddressableLEDBuffer(120);
    m_leftSide = m_ledData.createView(0, 59);
    m_rightSide = m_ledData.createView(60, 119).reversed();
  }

  @Override
  public void periodic() {
    m_led.writeData(m_ledData);
  }

  public Command runAnimation(LEDPattern animation) {
    return run(() -> {
      animation.applyTo(m_leftSide);
      animation.applyTo(m_rightSide);
    });
  }
}

Animated rainbow:

LEDPattern rainbow = LEDPattern.rainbow(255, 255).scrollAtRelativeSpeed(Percent.per(Second).of(50));

rainbow

Flag:

LEDPattern canada = LEDPattern.steps(Map.of(0, Color.kRed, 1 / 3.0, Color.kWhite, 2 / 3.0, Color.kRed));
LEDPattern canadaScroll = canada.scrollAtRelativeSpeed(Percent.per(Second).of(33));
image

canadascroll

Playing animations for an alliance color based on driverstation data:

LEDPattern breatheBlue = LEDPattern.solid(Color.kFirstBlue).breathe(Seconds.of(2));
LEDPattern breatheRed = LEDPattern.solid(Color.kFirstRed).breathe(Seconds.of(2));
LEDPattern flashYellow = LEDPattern.solid(Color.kYellow).blink(Seconds.of(0.5));

LEDPattern allianceColor = (read, write) -> {
  LEDPattern alliancePattern;
  if (Driverstation.getAlliance().isPresent()) {
    if (Driverstation.getAlliance.get() == Driverstation.Alliance.Blue) {
      alliancePattern = breatheBlue;
    } else {
      alliancePattern = breatheRed;
    }
  } else {
    alliancePattern = flashYellow;
  }
  alliancePattern.applyTo(read, write);
};

alliancecolors

Add LEDReader and LEDWriter helper interfaces to facilitate composing simple patterns into more complex ones, e.g. `LEDPattern.solid(Color.kBlue).breathe(Seconds.of(0.75))`

Add a view class for splitting a single large buffer into smaller distinct sections, which is useful for dealing with long chained LED strips mounted on different parts of a robot

Remove addressableled example rainbow test; it's redundant with the LEDPattern tests
@SamCarlberg SamCarlberg added the component: wpilibj WPILib Java label Feb 4, 2024
@SamCarlberg SamCarlberg requested review from a team as code owners February 4, 2024 20:32
@SamCarlberg SamCarlberg changed the title Add LED pattern features for easily animating addressable LEDs Add LED pattern API for easily animating addressable LEDs Feb 4, 2024
Copy link
Member

@Starlight220 Starlight220 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This API looks amazing!

What is planned for C++ parity?

@SamCarlberg
Copy link
Member Author

What is planned for C++ parity?

No plans for C++ at the moment. The Java API is something I wrote for my FRC team's use so they could make LED patterns without needing to figure out animations; I figured I would contribute it upstream so it could help other teams. But I'm not a C++ developer, and I won't have time to learn the cpp-isms to write a quality port between mentoring my team and resuming work on robotbuilder2.

Default behavior in LEDReader does the same thing, no need to duplicate it in the buffer class
Permits views of views, for example

Requires views to use separate reader/writer fields because we can't represent intersection types directly in fields. It would require a generic type bound on the view class, which would pollute field declarations (eg `View<View<Buffer>>` for a view-of-a-view)
@SamCarlberg SamCarlberg added the help: needs C++ Java exists, needs C++ port label Mar 22, 2024
Helpful for displaying progressbar-like animations

Masks can be useful for making tracer effects with a single patch of illuminated LEDs scrolling across an LED strip, where blends would not work (since they modify brightness, which masks do not)
Helpful for quickly adjusting LED brightness
@SamCarlberg SamCarlberg requested review from PeterJohnson and a team as code owners May 18, 2024 15:48
Copy link
Contributor

@spacey-sooty spacey-sooty left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think you might wanna base this off main 😉

@SamCarlberg
Copy link
Member Author

Ah right, I forgot this PR was targeting the development branch

@SamCarlberg SamCarlberg changed the base branch from development to main May 18, 2024 16:36
Move floorMod to the LEDPattern class for reusability in tests. Maybe it should live in wpimath
Copy link
Contributor

@KangarooKoala KangarooKoala left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, looks pretty good! A few doc comments are missing / incomplete, though: ApplyTo(std::span<>, LEDWriterFn) is missing @param, ApplyTo(std::span<>) is missing the entire doc comment, and ScrollAtRelativeSpeed() is missing @param and @return.
Unfortunately, I haven't been able to spend enough time to completely review the logic, but nothing jumps out as wrong. I will say that it would be nice to be able to configure the starting time so that you could guarantee that patterns like the scroll will start in a predictable state at the start of a command regardless of when the command is started, but that could be added later.

wpilibc/src/main/native/cpp/LEDPattern.cpp Outdated Show resolved Hide resolved
wpilibc/src/main/native/include/frc/LEDPattern.h Outdated Show resolved Hide resolved
Copy link
Member

@PeterJohnson PeterJohnson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The C++ implementations need lifetime fixes. Since the LEDPattern is a value type, it's unsafe to capture references to it. Similarly std::span is a reference type that needs to be copied into a std::vector when captured.

wpilibc/src/main/native/include/frc/LEDPattern.h Outdated Show resolved Hide resolved
wpilibc/src/main/native/include/frc/LEDPattern.h Outdated Show resolved Hide resolved
wpilibc/src/main/native/include/frc/LEDPattern.h Outdated Show resolved Hide resolved
wpilibc/src/main/native/include/frc/LEDPattern.h Outdated Show resolved Hide resolved
wpilibc/src/main/native/cpp/LEDPattern.cpp Outdated Show resolved Hide resolved
wpilibc/src/main/native/include/frc/LEDPattern.h Outdated Show resolved Hide resolved
wpilibc/src/main/native/cpp/LEDPattern.cpp Outdated Show resolved Hide resolved
wpimath/src/main/native/include/frc/MathUtil.h Outdated Show resolved Hide resolved
wpimath/src/main/native/include/frc/MathUtil.h Outdated Show resolved Hide resolved
Co-authored-by: Tyler Veness <calcmogul@gmail.com>
@PeterJohnson
Copy link
Member

It appears the floormod/floordiv change broke tests?

@calcmogul
Copy link
Member

calcmogul commented May 30, 2024

Since FloorDiv() and FloorMod() have no unit tests, we should probably add some. As for the bug, my guess is the xor sign check gives the wrong answer if the integers are mismatched sizes. We can either constrain the parameters and return type to be the same integral type, whichever it is, or we could make the sign check more robust.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: wpilibj WPILib Java help: needs C++ Java exists, needs C++ port
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants