Unit 4 · Lesson 8

Enums

An enum is a type with a fixed set of named constants. Instead of using integers or strings to track robot state — where nothing stops you from using an invalid value — enums give you compiler-enforced, type-safe state management. They're the backbone of every clean FRC state machine.

By the end of this lesson, you will:

  • Declare an enum with named constants and use it in a switch statement
  • Add fields and methods to an enum to associate data with each constant
  • Build a robot state machine using an enum and a switch to drive mechanism behavior
  • Recognize the WPILib vendor library enums: NeutralModeValue, InvertedValue, NeutralModeValue
  • Explain why enums are safer than integer or String constants for state management

The Problem With Integer States

Before enums, programmers tracked state with integers: 0 = idle, 1 = intaking, 2 = scoring. This works — until someone passes 7, or -1, or confuses which subsystem uses which constants. The code compiles, the robot enables, and nothing behaves as expected.

// ❌ Integer state — nothing prevents invalid values private int intakeState = 0; // 0=idle, 1=intaking, 2=ejecting intakeState = 5; // compiles — no matching case in switch — silent bug intakeState = -1; // compiles — undefined behavior // ✅ Enum state — only valid values exist private IntakeState state = IntakeState.IDLE; state = IntakeState.INTAKING; // only declared constants allowed state = 5; // compile error — type mismatch

Enum Basics

// Simple enum — four named constants, no data public enum IntakeState { IDLE, INTAKING, EJECTING, GAME_PIECE_HELD } // Using enum values IntakeState current = IntakeState.IDLE; System.out.println(current); // prints: IDLE System.out.println(current.name()); // prints: IDLE (String) System.out.println(current.ordinal()); // prints: 0 (position in declaration) // Switch on enum — exhaustive and type-safe switch (current) { case IDLE: motor.set(0.0); break; case INTAKING: motor.set(INTAKE_SPEED); break; case EJECTING: motor.set(-INTAKE_SPEED); break; case GAME_PIECE_HELD: motor.set(0.0); break; }

Enums with Fields and Methods

An enum is a full Java class — each constant can carry its own data and behavior. This is where enums become truly powerful for FRC: the motor output associated with each state travels with the state itself.

// Enum with fields — each constant carries its own motor output public enum IntakeState { IDLE (0.0, "Waiting"), INTAKING (0.65, "Intaking"), EJECTING (-0.5, "Ejecting"), GAME_PIECE_HELD(0.0, "Holding"); public final double motorOutput; public final String displayName; // Enum constructor — called once per constant at class load IntakeState(double motorOutput, String displayName) { this.motorOutput = motorOutput; this.displayName = displayName; } } // The switch collapses to a single line — state carries its own output private void updateIntake() { motor.set(currentState.motorOutput); SmartDashboard.putString("Intake", currentState.displayName); }

The Robot State Machine

The most important use of enums in FRC is the state machine: a private enum field tracks the current mechanism state, and each periodic call evaluates that state and applies transitions. Use the simulator below to step through a complete intake state machine.

Intake State Machine Simulator
IDLE
INTAKING
HOLDING
EJECTING
Active switch case
case IDLE: motor.set(0.0);
case INTAKING: motor.set(0.65);
case HOLDING: motor.set(0.0);
case EJECTING: motor.set(-0.5);
Current values
stateIDLE
motor0.0
gamePiecefalse
State machine ready. Press A to start intaking.

Enums in WPILib and Vendor Libraries

You've been using vendor-library enums since Unit 5 without necessarily recognizing them as enums. They're the same pattern: a fixed set of named, compiler-verified options.

// NeutralModeValue — what happens when you set output to 0 motor.setNeutralMode(NeutralModeValue.Brake); // resists motion motor.setNeutralMode(NeutralModeValue.Coast); // free-wheels // InvertedValue — motor direction cfg.MotorOutput.Inverted = InvertedValue.Clockwise_Positive; cfg.MotorOutput.Inverted = InvertedValue.CounterClockwise_Positive; // These are enums — you can only pass the declared constants. // Passing an integer or string would be a compile error. // This is the entire point: the valid options are enumerated.
// Robot-level game phase public enum MatchPhase { DISABLED, AUTO, TELEOP, TEST } // Arm position targets with associated setpoints public enum ArmPosition { STOW (0.0), INTAKE (15.0), SCORE_L1(60.0), SCORE_L2(90.0), SCORE_L3(118.0); public final double angleDeg; ArmPosition(double angleDeg) { this.angleDeg = angleDeg; } } // Caller — no magic numbers, no invalid values possible arm.setTarget(ArmPosition.SCORE_L2); // arm.setTarget(90.0) — possible, but less readable and less safe
🔍 LRI Observation

The clearest signal that a team has internalized enums is when I read their state machine code. A team that uses enums for arm positions and intake states can answer "what state is the arm in right now?" in one variable read — armState.name() prints the exact, human-readable state. A team using integers answers with "well, 2 means score, and 3 means stow, but we changed the order last week, so…" Enums make state self-documenting. When something breaks at competition, readable state is directly proportional to debugging speed.

⚙️ 🔌 System Check
  • Use enums for all robot mechanism state variables. Replace integer and string state flags with named enum constants. The compiler guarantees only valid states exist. private IntakeState state = IntakeState.IDLE; is correct. private int state = 0; is not.
  • Add fields to enums when state carries associated data. Motor outputs, setpoint angles, display names — these belong on the enum constant itself. The enum becomes the single source of truth for everything associated with that state.
  • Switch statements on enums should have a default case. Even though all enum values should be covered by named cases, a default that calls a safe fallback protects against future enum values added without updating the switch.
  • Use name() for dashboard display. SmartDashboard.putString("State", currentState.name()) gives you a readable state label for free — no manual string mapping required.

Knowledge Check

You have private int armState = 0; where 0=stow, 1=intake, 2=score. A teammate sets armState = 7 accidentally. What happens?
  • 1A compile error — 7 is not a valid arm state
  • 2The code compiles and runs; the switch on armState finds no matching case and falls through to default (or does nothing if there's no default), leaving the arm in undefined behavior with no compiler warning
  • 3Java clamps the value to the valid range automatically
  • 4A runtime exception is thrown at the switch statement
An enum constant SCORE_L2 is declared with a field: SCORE_L2(90.0). How do you read the angle value?
  • 1ArmPosition.SCORE_L2.ordinal()
  • 2ArmPosition.SCORE_L2.toString()
  • 3ArmPosition.SCORE_L2.angleDeg — assuming the field is declared public final double angleDeg in the enum body
  • 4ArmPosition.SCORE_L2.getValue()
Why is motor.setNeutralMode(NeutralModeValue.Brake) safer than motor.setNeutralMode(1)?
  • 1The integer version runs slower at runtime
  • 2The enum version is type-safe: the compiler only accepts declared NeutralModeValue constants; the integer version accepts any int, making it possible to pass an invalid value that causes undefined motor behavior with no compile error
  • 3CTRE's API only accepts enum types and the integer version wouldn't compile anyway
  • 4No difference — both produce the same bytecode
💪 Practice Prompt — Unit 4 Capstone

Complete State Machine with Enum

  1. Define an IntakeState enum with fields for motorOutput and displayName. Include at least four states: IDLE, INTAKING, GAME_PIECE_HELD, and EJECTING.
  2. Modify your IntakeSubsystem to use a private IntakeState currentState field. In periodic(), implement a switch on currentState that applies the correct motor output from the enum's field. Add transitions: pressing A starts intaking, beam break triggers holding, pressing B starts ejecting, releasing B returns to idle.
  3. Add a getStateName() method that returns currentState.name(). Call it from SmartDashboard in periodic(). Deploy and confirm the state label updates correctly as you trigger transitions.
  4. Add an ArmPosition enum to your arm subsystem with at least three positions and their target angles. Replace the hardcoded angle constants in your arm methods with ArmPosition enum references.
  5. Bonus (Unit 4 capstone): Look at your completed subsystem classes across all Unit 4 lessons. Write a one-page design review: Which OOP concepts from Unit 4 (classes/objects, constructors, static, encapsulation, inheritance, polymorphism, abstract/interfaces, enums) appear in your code? For each one, describe a specific line where it appears and why it's the right tool there.