> For the complete documentation index, see [llms.txt](https://yams.yassrobotics.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yams.yassrobotics.com/api-reference/guides/building-an-elevator.md).

# Building an Elevator Subsystem

**Goal:** Create an `Elevator` subsystem with height-based position control and physics simulation.

***

{% stepper %}
{% step %}

#### Configure the motor for linear travel

The motor config must account for how rotations convert to linear distance. Either:

* Call `.withMechanismCircumference(distancePerRotation)` where the value equals `2π × drumRadius`.
* Or call `.withDrumRadius(radius)` which computes the circumference for you.

```java
SmartMotorControllerConfig motorConfig = new SmartMotorControllerConfig()
    .withMotorInverted(false)
    .withIdleMode(MotorMode.BRAKE)
    .withGearing(new MechanismGearing(GearBox.fromRatio(9.0)))
    .withMechanismCircumference(Meters.of(2 * Math.PI * 0.025))  // drum circumference
    .withCascadingElevatorStages(2)                               // 2-stage cascade
    .withClosedLoopController(1.2, 0.0, 0.05)
    .withFeedforward(new ElevatorFeedforward(0.1, 0.4, 0.02))
    .withSoftLimits(Meters.of(0.0), Meters.of(1.2))
    .withStatorCurrentLimit(Amps.of(60))
    .withTelemetry("ElevatorMotor", TelemetryVerbosity.HIGH);

SmartMotorController motor = new TalonFXWrapper(
    new TalonFX(2),
    DCMotor.getKrakenX60(1),
    motorConfig
);
```

{% endstep %}

{% step %}

#### Create an `ElevatorConfig`

Carriage weight is required for simulation to model gravity correctly. Drum radius and cascade stage count are configured on `SmartMotorControllerConfig` (see step 1).

```java
ElevatorConfig elevatorConfig = new ElevatorConfig()
    .withCarriageWeight(Kilograms.of(4.5))
    .withHardLimits(Meters.of(0.0), Meters.of(1.2))
    .withTelemetry("Elevator", TelemetryVerbosity.HIGH);
```

{% hint style="info" %}
Carriage weight is required for simulation. Without it, `simIterate()` cannot model gravity and the elevator will behave like a frictionless flywheel in sim. Drum circumference and cascade stages belong on `SmartMotorControllerConfig` via `.withMechanismCircumference()` and `.withCascadingElevatorStages()`.
{% endhint %}
{% endstep %}

{% step %}

#### Construct the `Elevator`

```java
Elevator elevator = new Elevator(elevatorConfig, motor);
```

{% endstep %}

{% step %}

#### Integrate into a `SubsystemBase`

```java
public class ElevatorSubsystem extends SubsystemBase {

    private final SmartMotorController motor;
    private final Elevator elevator;

    public ElevatorSubsystem() {
        SmartMotorControllerConfig motorConfig = new SmartMotorControllerConfig()
            .withMotorInverted(false)
            .withIdleMode(MotorMode.BRAKE)
            .withGearing(new MechanismGearing(GearBox.fromRatio(9.0)))
            .withMechanismCircumference(Meters.of(2 * Math.PI * 0.025))  // drum circumference
            .withCascadingElevatorStages(2)                               // 2-stage cascade
            .withClosedLoopController(1.2, 0.0, 0.05)
            .withFeedforward(new ElevatorFeedforward(0.1, 0.4, 0.02))
            .withSoftLimits(Meters.of(0.0), Meters.of(1.2))
            .withStatorCurrentLimit(Amps.of(60))
            .withTelemetry("ElevatorMotor", TelemetryVerbosity.HIGH);

        motor = new TalonFXWrapper(
            new TalonFX(2),
            DCMotor.getKrakenX60(1),
            motorConfig
        );

        ElevatorConfig elevatorConfig = new ElevatorConfig()
            .withCarriageWeight(Kilograms.of(4.5))
            .withHardLimits(Meters.of(0.0), Meters.of(1.2))
            .withTelemetry("Elevator", TelemetryVerbosity.HIGH);

        elevator = new Elevator(elevatorConfig, motor);
    }

    /** Move to a fixed height. */
    public Command setHeightCommand(Distance height) {
        return elevator.setHeight(height);
    }

    /** Move to height, finish when within tolerance. */
    public Command runToHeight(Distance height, Distance tolerance) {
        return elevator.runTo(height, tolerance);
    }

    public Distance getHeight() {
        return elevator.getHeight();
    }

    @Override
    public void periodic() {
        elevator.updateTelemetry();
    }

    @Override
    public void simulationPeriodic() {
        elevator.simIterate();
    }
}
```

{% endstep %}

{% step %}

#### Wire up triggers

`Elevator` exposes `gte(height)` and `lte(height)` triggers. Use them to gate game piece actions.

```java
Trigger atBottom = elevatorSubsystem.elevator.lte(Meters.of(0.05));
Trigger atScore  = elevatorSubsystem.elevator.gte(Meters.of(1.0));
```

{% endstep %}
{% endstepper %}

***

## Examples

{% @github-files/github-code-block url="<https://github.com/Yet-Another-Software-Suite/YAMS/blob/master/examples/simple_elevator/java/frc/robot/subsystems/ElevatorSubsystem.java>" %}

{% @github-files/github-code-block url="<https://github.com/Yet-Another-Software-Suite/YAMS/blob/master/examples/exponential_elevator/java/frc/robot/subsystems/ExponentiallyProfiledElevatorSubsystem.java>" %}

***

## Related pages

* [Elevator](/api-reference/java-reference/mechanisms/elevator.md)
* [ElevatorConfig](/api-reference/java-reference/config/elevator-config.md)
* [Configuring a Motor Controller](/api-reference/guides/configuring-a-motor.md)
* [Simulation](/api-reference/guides/simulation.md)
