> 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/documentation/understanding/config-organization.md).

# Organizing configs in constants

As your robot grows, managing configuration across multiple subsystems can become unwieldy. YAMS supports a clean pattern where you define all your configs in a centralized Constants file, then bind them to subsystems at construction time using `.withSubsystem()`.

## The problem

Without organization, each subsystem file contains its own configuration inline:

```java
// ArmSubsystem.java - config mixed with subsystem logic
public class ArmSubsystem extends SubsystemBase {
  private final Arm arm;
  
  public ArmSubsystem() {
    SmartMotorControllerConfig smcConfig = new SmartMotorControllerConfig(this)
        .withGearing(new MechanismGearing(GearBox.fromReductionStages(5, 4, 3)))
        .withClosedLoopController(5, 0, 0.1)
        .withFeedforward(new ArmFeedforward(0.1, 0.3, 0.5, 0.01))
        .withTrapezoidalProfile(RotationsPerSecond.of(1.0), RotationsPerSecondPerSecond.of(2.0));
    
    SmartMotorController smc = new TalonFXWrapper(new TalonFX(1), DCMotor.getKrakenX60(1), smcConfig);
    
    ArmConfig armConfig = new ArmConfig()
        .withLength(Inches.of(18))
        // ... more config
    
    this.arm = new Arm(armConfig, smc);
  }
}
```

This approach has drawbacks:

* Configs are scattered across many files
* Hard to compare settings between mechanisms
* Tuning requires opening multiple files
* CAN IDs, motor types, and gear ratios are buried in subsystem code

## The solution: Centralized constants with `withSubsystem()`

YAMS configs can be created **without** a subsystem reference, then bound later using `.withSubsystem()`. This enables a clean separation:

```java
// Constants.java - ALL configs in one place
public final class Constants {
  
  public static final class ArmConstants {
    public static final int CAN_ID = 1;
    
    public static final SmartMotorControllerConfig SMC_CONFIG = 
        new SmartMotorControllerConfig()  // No subsystem yet!
            .withGearing(new MechanismGearing(GearBox.fromReductionStages(5, 4, 3)))
            .withClosedLoopController(5, 0, 0.1)
            .withFeedforward(new ArmFeedforward(0.1, 0.3, 0.5, 0.01))
            .withTrapezoidalProfile(RotationsPerSecond.of(1.0), RotationsPerSecondPerSecond.of(2.0));
    
    public static final ArmConfig ARM_CONFIG = 
        new ArmConfig()  // No SmartMotorController yet!
            .withLength(Inches.of(18))
            .withHardLimits(Rotations.of(-0.3), Rotations.of(0.3))
            .withTelemetry("Arm", TelemetryVerbosity.HIGH);
  }
  
  public static final class ElevatorConstants {
    public static final int CAN_ID = 2;
    
    public static final SmartMotorControllerConfig SMC_CONFIG = 
        new SmartMotorControllerConfig()
            .withGearing(new MechanismGearing(GearBox.fromReductionStages(5, 4)))
            .withDrumRadius(Inches.of(0.75))
            .withClosedLoopController(10, 0, 0.5)
            .withFeedforward(new ElevatorFeedforward(0.1, 0.2, 0.5, 0.01))
            .withTrapezoidalProfile(MetersPerSecond.of(1.0), MetersPerSecondPerSecond.of(2.0));
    
    public static final ElevatorConfig ELEVATOR_CONFIG = 
        new ElevatorConfig()
            .withCarriageWeight(Pounds.of(10))
            .withHardLimits(Meters.of(0), Meters.of(1.5))
            .withTelemetry("Elevator", TelemetryVerbosity.HIGH);
  }
  
  public static final class ShooterConstants {
    public static final int CAN_ID = 3;
    
    public static final SmartMotorControllerConfig SMC_CONFIG = 
        new SmartMotorControllerConfig()
            .withGearing(new MechanismGearing(GearBox.fromReductionStages(1)))
            .withClosedLoopController(0.5, 0, 0)
            .withFeedforward(new SimpleMotorFeedforward(0.1, 0.12, 0.01));
    
    public static final FlyWheelConfig FLYWHEEL_CONFIG = 
        new FlyWheelConfig()
            .withDiameter(Inches.of(4))
            .withTelemetry("Shooter", TelemetryVerbosity.HIGH);
  }
}
```

Then in your subsystems, use `.withSubsystem()` to bind the config:

```java
// ArmSubsystem.java - clean and focused
public class ArmSubsystem extends SubsystemBase {
  private final Arm arm;
  
  public ArmSubsystem() {
    // Bind the config to this subsystem
    SmartMotorControllerConfig smcConfig = ArmConstants.SMC_CONFIG
        .withSubsystem(this);
    
    // Create SmartMotorController with the bound config
    SmartMotorController smc = new TalonFXWrapper(
        new TalonFX(ArmConstants.CAN_ID), 
        DCMotor.getKrakenX60(1), 
        smcConfig
    );
    
    // Clone ArmConfig and pass smc to the mechanism constructor
    ArmConfig armConfig = ArmConstants.ARM_CONFIG.clone();
    
    this.arm = new Arm(armConfig, smc);
  }
  
  // Commands and business logic only - no config clutter!
  public Command goToAngle(Angle target) {
    return arm.runTo(target, Degrees.of(1));  // tolerance required
  }
}
```

## Benefits of this pattern

| Benefit                    | Description                                            |
| -------------------------- | ------------------------------------------------------ |
| **Single Source of Truth** | All CAN IDs, gear ratios, PID gains in one file        |
| **Easy Comparison**        | See all mechanism configs side-by-side                 |
| **Faster Tuning**          | Change gains without navigating subsystem files        |
| **Cleaner Subsystems**     | Subsystem classes focus on behavior, not configuration |
| **Reusable Configs**       | Share base configs between similar mechanisms          |

## Complete example: Constants file

```java
package frc.robot;

import static edu.wpi.first.units.Units.*;

import edu.wpi.first.math.controller.ArmFeedforward;
import edu.wpi.first.math.controller.ElevatorFeedforward;
import edu.wpi.first.math.controller.SimpleMotorFeedforward;
import yams.gearing.GearBox;
import yams.gearing.MechanismGearing;
import yams.mechanisms.config.ArmConfig;
import yams.mechanisms.config.ElevatorConfig;
import yams.mechanisms.config.FlyWheelConfig;
import yams.motorcontrollers.SmartMotorControllerConfig;
import yams.motorcontrollers.SmartMotorControllerConfig.TelemetryVerbosity;

public final class Constants {

  // ==================== ARM ====================
  public static final class ArmConstants {
    public static final int CAN_ID = 1;
    public static final DCMotor MOTOR = DCMotor.getKrakenX60(1);
    
    public static final SmartMotorControllerConfig SMC_CONFIG = 
        new SmartMotorControllerConfig()
            .withGearing(new MechanismGearing(GearBox.fromReductionStages(5, 4, 3)))
            .withClosedLoopController(5, 0, 0.1)
            .withFeedforward(new ArmFeedforward(0.1, 0.3, 0.5, 0.01))
            .withTrapezoidalProfile(RotationsPerSecond.of(1.0), RotationsPerSecondPerSecond.of(2.0))
            .withIdleMode(MotorMode.BRAKE);
    
    public static final ArmConfig ARM_CONFIG = 
        new ArmConfig()
            .withLength(Inches.of(18))
            .withHardLimits(Rotations.of(-0.3), Rotations.of(0.3))
            .withTelemetry("Arm", TelemetryVerbosity.HIGH);
  }

  // ==================== ELEVATOR ====================
  public static final class ElevatorConstants {
    public static final int CAN_ID = 2;
    public static final DCMotor MOTOR = DCMotor.getKrakenX60(1);
    
    public static final SmartMotorControllerConfig SMC_CONFIG = 
        new SmartMotorControllerConfig()
            .withGearing(new MechanismGearing(GearBox.fromReductionStages(5, 4)))
            .withDrumRadius(Inches.of(0.75))
            .withClosedLoopController(10, 0, 0.5)
            .withFeedforward(new ElevatorFeedforward(0.1, 0.2, 0.5, 0.01))
            .withTrapezoidalProfile(MetersPerSecond.of(1.0), MetersPerSecondPerSecond.of(2.0))
            .withIdleMode(MotorMode.BRAKE);
    
    public static final ElevatorConfig ELEVATOR_CONFIG = 
        new ElevatorConfig()
            .withCarriageWeight(Pounds.of(10))
            .withHardLimits(Meters.of(0), Meters.of(1.5))
            .withTelemetry("Elevator", TelemetryVerbosity.HIGH);
  }

  // ==================== SHOOTER ====================
  public static final class ShooterConstants {
    public static final int UPPER_CAN_ID = 3;
    public static final int LOWER_CAN_ID = 4;
    public static final DCMotor MOTOR = DCMotor.getKrakenX60(1);
    
    public static final SmartMotorControllerConfig SMC_CONFIG = 
        new SmartMotorControllerConfig()
            .withGearing(new MechanismGearing(GearBox.fromReductionStages(1)))
            .withClosedLoopController(0.5, 0, 0)
            .withFeedforward(new SimpleMotorFeedforward(0.1, 0.12, 0.01))
            .withIdleMode(MotorMode.COAST);
    
    public static final FlyWheelConfig FLYWHEEL_CONFIG = 
        new FlyWheelConfig()
            .withDiameter(Inches.of(4))
            .withTelemetry("Shooter", TelemetryVerbosity.HIGH);
  }
}
```

## Complete example: Subsystem using constants

```java
package frc.robot.subsystems;

import com.ctre.phoenix6.hardware.TalonFX;
import edu.wpi.first.units.measure.Angle;
import edu.wpi.first.wpilibj2.command.Command;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import frc.robot.Constants.ArmConstants;
import yams.mechanisms.config.ArmConfig;
import yams.mechanisms.positional.Arm;
import yams.motorcontrollers.SmartMotorController;
import yams.motorcontrollers.SmartMotorControllerConfig;
import yams.motorcontrollers.remote.TalonFXWrapper;

public class ArmSubsystem extends SubsystemBase {

  private final Arm arm;

  public ArmSubsystem() {
    // Step 1: Bind SmartMotorControllerConfig to this subsystem
    SmartMotorControllerConfig smcConfig = ArmConstants.SMC_CONFIG
        .withSubsystem(this);

    // Step 2: Create SmartMotorController
    SmartMotorController smc = new TalonFXWrapper(
        new TalonFX(ArmConstants.CAN_ID),
        ArmConstants.MOTOR,
        smcConfig
    );

    // Step 3: Clone ArmConfig and pass smc to the mechanism constructor
    ArmConfig armConfig = ArmConstants.ARM_CONFIG.clone();

    // Step 4: Create Arm mechanism
    this.arm = new Arm(armConfig, smc);
  }

  // ==================== COMMANDS ====================
  
  public Command runToAngle(Angle target) {
    return arm.runTo(target, Degrees.of(1));  // tolerance required
  }

  public Command holdAngle(Angle target) {
    return arm.run(target);
  }

  public Command stow() {
    return arm.runTo(Rotations.of(0), Degrees.of(1));  // tolerance required
  }

  // ==================== ACCESSORS ====================
  
  public Arm getArm() {
    return arm;
  }
}
```

## Organizing multiple config files

For larger robots, you may want to split configs into separate files:

```
frc/robot/
├── Constants.java           // General constants (CAN IDs, ports)
├── configs/
│   ├── ArmConfigs.java      // Arm-related configs
│   ├── ElevatorConfigs.java // Elevator-related configs
│   ├── ShooterConfigs.java  // Shooter-related configs
│   └── DriveConfigs.java    // Swerve/drive configs
└── subsystems/
    ├── ArmSubsystem.java
    ├── ElevatorSubsystem.java
    └── ...
```

{% hint style="info" %}
**Important**: When using this pattern, you **must** call `.withSubsystem()` before passing the `SmartMotorControllerConfig` to a `SmartMotorController`. Pass the `SmartMotorController` directly as the second argument to the mechanism constructor (e.g., `new Arm(armConfig, smc)`).
{% endhint %}

## When not to use this pattern

This pattern adds a layer of indirection. For simple robots with 1-2 mechanisms, inline configuration may be clearer. Consider using centralized configs when:

* You have 3+ mechanisms
* Multiple team members tune different subsystems
* You want to quickly compare settings across mechanisms
* You're building a constants dashboard or config loader
