- FRC Game Manual
- FRC Game Q&A

# Event-Based Programming With EventLoop

Many operations in robot code are driven by certain conditions; buttons are one common example. Conditions can be polled with an imperative programming style by using an `if` statement in a periodic method. As an alternative, WPILib offers an event-driven programming style of API in the shape of the `EventLoop` and `BooleanEvent` classes.

Note

The example code here is taken from the EventLoop example project (Java/C++).

## EventLoop

The `EventLoop` class is a “container” for pairs of conditions and actions, which can be polled using the `poll()`/`Poll()` method. When polled, every condition will be queried and if it returns `true` the action associated with the condition will be executed.

JAVA

      private final EventLoop m_loop = new EventLoop();
      private final Joystick m_joystick = new Joystick(0);
      @Override
      public void robotPeriodic() {
        // poll all the bindings
        m_loop.poll();
      }

C++

      frc::EventLoop m_loop{};
      void RobotPeriodic() override { m_loop.Poll(); }

Warning

The `EventLoop`’s `poll()` method should be called consistently in a `*Periodic()` method. Failure to do this will result in unintended loop behavior.

## BooleanEvent

The `BooleanEvent` class represents a boolean condition: a `BooleanSupplier` (Java) / `std::function<bool()>` (C++).

To bind a callback action to the condition, use `ifHigh()`/`IfHigh()`:

JAVA

        BooleanEvent atTargetVelocity =
            new BooleanEvent(m_loop, m_controller::atSetpoint)
                // debounce for more stability
                .debounce(0.2);

        // if we're at the target velocity, kick the ball into the shooter wheel
        atTargetVelocity.ifHigh(() -> m_kicker.set(0.7));

C++

        frc::BooleanEvent atTargetVelocity =
            frc::BooleanEvent(
                &m_loop,
                [&controller = m_controller] { return controller.AtSetpoint(); })
                // debounce for more stability
                .Debounce(0.2_s);

        // if we're at the target velocity, kick the ball into the shooter wheel
        atTargetVelocity.IfHigh([&kicker = m_kicker] { kicker.Set(0.7); });

Remember that button binding is declarative: bindings only need to be declared once, ideally some time during robot initialization. The library handles everything else.

## Composing Conditions

`BooleanEvent` objects can be composed to create composite conditions. In C++ this is done using operators when applicable, other cases and all compositions in Java are done using methods.

### and() / &&

The `and()`/`&&` composes two `BooleanEvent` conditions into a third condition that returns `true` only when both of the conditions return `true`.

JAVA

        // if the thumb button is held
        intakeButton
            // and there is not a ball at the kicker
            .and(isBallAtKicker.negate())
            // activate the intake
            .ifHigh(() -> m_intake.set(0.5));

C++

        // if the thumb button is held
        (intakeButton
         // and there is not a ball at the kicker
         && !isBallAtKicker)
            // activate the intake
            .IfHigh([&intake = m_intake] { intake.Set(0.5); });

### or() / ||

The `or()`/`||` composes two `BooleanEvent` conditions into a third condition that returns `true` only when either of the conditions return `true`.

JAVA

        // if the thumb button is not held
        intakeButton
            .negate()
            // or there is a ball in the kicker
            .or(isBallAtKicker)
            // stop the intake
            .ifHigh(m_intake::stopMotor);

C++

        // if the thumb button is not held
        (!intakeButton
         // or there is a ball in the kicker
         || isBallAtKicker)
            // stop the intake
            .IfHigh([&intake = m_intake] { intake.Set(0.0); });

### negate() / !

The `negate()`/`!` composes one `BooleanEvent` condition into another condition that returns the opposite of what the original conditional did.

JAVA

            // and there is not a ball at the kicker
            .and(isBallAtKicker.negate())

C++

         // and there is not a ball at the kicker
         && !isBallAtKicker)

### debounce() / Debounce()

To avoid rapid repeated activation, conditions (especially those originating from digital inputs) can be debounced with the WPILib Debouncer class using the debounce method:

JAVA

        BooleanEvent atTargetVelocity =
            new BooleanEvent(m_loop, m_controller::atSetpoint)
                // debounce for more stability
                .debounce(0.2);

C++

        frc::BooleanEvent atTargetVelocity =
            frc::BooleanEvent(
                &m_loop,
                [&controller = m_controller] { return controller.AtSetpoint(); })
                // debounce for more stability
                .Debounce(0.2_s);

### rising(), falling()

Often times it is desired to bind an action not to the current state of a condition, but instead to when that state changes. For example, binding an action to when a button is newly pressed as opposed to when it is held. This is what the `rising()` and `falling()` decorators do: `rising()` will return a condition that is `true` only when the original condition returned `true` in the current polling and `false` in the previous polling; `falling()` returns a condition that returns `true` only on a transition from `true` to `false`.

Warning

Due to the “memory” these conditions have, do not use the same instance in multiple places.

JAVA

        // when we stop being at the target velocity, it means the ball was shot
        atTargetVelocity
            .falling()
            // so stop the kicker
            .ifHigh(m_kicker::stopMotor);

C++

        // when we stop being at the target velocity, it means the ball was shot
        atTargetVelocity
            .Falling()
            // so stop the kicker
            .IfHigh([&kicker = m_kicker] { kicker.Set(0.0); });

### Downcasting `BooleanEvent` Objects

To convert `BooleanEvent` objects to other types, most commonly the `Trigger` subclass used for binding commands to conditions, the generic `castTo()`/`CastTo()` decorator exists:

JAVA

    Trigger trigger = booleanEvent.castTo(Trigger::new);

C++

    frc2::Trigger trigger = booleanEvent.CastTo<frc2::Trigger>();

Note

In Java, the parameter expects a method reference to a constructor accepting an `EventLoop` instance and a `BooleanSupplier`. Due to the lack of method references, this parameter is defaulted in C++ as long as a constructor of the form `Type(frc::EventLoop*, std::function<bool()>)` exists.