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 animation classes #13

Merged
merged 3 commits into from
Feb 6, 2024
Merged

Add LED animation classes #13

merged 3 commits into from
Feb 6, 2024

Conversation

SamCarlberg
Copy link
Collaborator

@SamCarlberg SamCarlberg commented Feb 3, 2024

Also fixes a few CI things - spotbugs doesn't need to pass, and formatter diff stages work now

Using LEDs

Two things are needed in code to be able to use LEDs: an AddressableLED object, which is a WPI class that can write data out on a PWM port to control an LED strip, and an AddressableLEDBuffer, which is used to store the data for LED strips.

public class LEDSubsystem extends SubsystemBase {
  private final AddressableLED leds;
  private final AddressableLEDBuffer ledData;

  public LEDSubsystem() {
    leds = new AddressableLED(Constants.LEDConstants.ledPortNumber);
    leds.setLength(Constants.LEDConstants.ledLength);
    ledData = new AddressableLEDBuffer(Constants.LEDConstants.ledLength);
  }

  @Override
  public void periodic() {
    leds.setData(ledData);
  }
}

This is about as much as WPILib provides for us - if we want to do fancier things with LEDs, we'd need to write that ourselves:

public void setRed() {
  for (int ledIndex = 0; ledIndex < leds.getLength(); ledIndex ++) {
    ledData.setRGB(ledIndex, 255, 0, 0);
  }
}

If we want to do animated effects, that would be more complicated code, and would be repetitive (remember "Don't Repeat Yourself"?)

public void blinkRed() {
  if (WPIUtilJNI.now() % 2_000_000 < 1_000_000) {
    // blink "on" for the first half of a 2-second period
    // (WPIUtilJNI.now() returns a number in terms of microseconds, thus the 6 zeroes)
    for (int ledIndex = 0; ledIndex < leds.getLength(); ledIndex ++) {
      ledData.setRGB(ledIndex, 255, 0, 0);
    }
  } else {
    // blink "off" for the second half of the 2-second period
    for (int ledIndex = 0; ledIndex < leds.getLength(); ledIndex ++) {
      ledData.setRGB(ledIndex, 0, 0, 0);
    }
  }
}

Animating LEDs

To make it easier to manage animating LEDs without manually writing the color of each and every LED yourself, I've added a few classes to manage animations and building up more complex animations from simple ones. Animations require a reader and a writer to run - they deliberately don't work with LED data buffers directly, because that prevents animations from being efficiently chained together.

We can update the LED subsystem to work with animations - here, we add a dataView that animations can work with, and a method that runs an animation as a command. This lets us bind animations to button presses, robot state, or match timing just like any other command

public class LEDSubsystem extends SubsystemBase {
  private final AddressableLED leds;
  private final AddressableLEDBuffer ledData;

  // Use a view for compatibility with animations
  private final AddressableLEDBufferView dataView;

  public LEDSubsystem() {
    leds = new AddressableLED(Constants.LEDConstants.ledPortNumber);
    leds.setLength(Constants.LEDConstants.ledLength);
    ledData = new AddressableLEDBuffer(Constants.LEDConstants.ledLength);
    dataView = new AddressableLEDBufferView(ledData, 0, ledData.getLength());
  }

  @Override
  public void periodic() {
    leds.setData(ledData);
  }

  public Command runAnimation(Animation animation) {
    return run(() -> animation.update(dataView));
  }
}

Of course, we can also define some prebuilt commands in this subsystem:

public Command blinkRed() {
  return runAnimation(Animation.solid(Color.kRed).blink(Seconds.of(1));
}

public Command blinkBlue() {
  return runAnimation(Animation.solid(Color.kBlue).blink(Seconds.of(1));
}

And even make some fairly complicated animated patterns:

// A motionless rainbow flag - this will light up the LEDs in a rainbow pattern, but won't move
private static final Animation rainbowFlag =
  Animation.steps(
    Map.of(
      0.0 / 6, Color.Red,
      1.0 / 6, Color.kOrange,
      2.0 / 6, Color.kYellow,
      3.0 / 6, Color.kGreen,
      4.0 / 6, Color.kIndigo,
      5.0 / 6, Color.kViolet));

// But we can make it move! Use the "scrollAtRelativeSpeed" method to make it scroll across the LED strip
// This will make a full trip around every 2 seconds (50% per second)
private static final Animation rainbowFlagScroll =
  rainbowFlag.scrollAtRelativeSpeed(Percent.per(Second).of(50));

public Command rainbowFlagScroll() {
  return runAnimation(rainbowFlagScroll);
}

rainbowroad-horiz

Be sure to look at the Animation class, there's a lot of helpful things in there.

@TheExoneratedManiac TheExoneratedManiac merged commit 9170c6a into main Feb 6, 2024
3 of 5 checks passed
@TheExoneratedManiac TheExoneratedManiac deleted the animations branch February 6, 2024 20:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants