# State MachinesState machines are a powerful way to organize robot mechanism control code.

## Overview

Imagine a traffic light. It is always in one of three states: **RED**, **YELLOW**, or **GREEN**. It cannot be in two states at once. Each state has a set of rules for when to switch to the next state.

A **state machine** works exactly the same way for your robot. Your lift, intake, or any mechanism is always in one defined state, and button presses (or sensor readings) trigger transitions between states.

**Why does this matter?**

Without a state machine, mechanism control code quickly turns into a mess of if/else statements that get tangled up and cause unpredictable behavior. State machines make complex behavior predictable, easy to debug, and easy to expand.

## The Problem State Machines Solve

Suppose you want a lift with three positions: down, middle, and up. Your first instinct might be:

```python
if controller.buttonLUp.pressing():
    lift_motor.spin(FORWARD, 80, PERCENT)
else:
    lift_motor.stop()
```

This works for a simple case — but what about:
- Holding the lift at exactly the right height?
- Making sure it does not over-extend?
- Triggering the intake automatically when the lift reaches a position?
- Preventing the driver from accidentally driving the lift into the ground?

All of these become very hard to manage without structure. A state machine gives you that structure.

## Core Concept: States and Transitions

A state machine has two key parts:

1. **States** — the possible conditions your mechanism can be in (e.g., `RETRACTED`, `LOW`, `HIGH`)
2. **Transitions** — the rules for moving from one state to another (e.g., "when the driver presses L1, go from LOW to HIGH")

At any moment, the mechanism is in exactly **one state**. The code for that state runs until a transition moves it to the next state.

## A Simple Lift State Machine

### Step 1 — Define your states

```python
# VEX IQ Python — define states as constants
RETRACTED = 0
LOW = 1
HIGH = 2
MANUAL = 3
```

```cpp
// VEX V5 C++ — use an enum for cleaner code
enum LiftState {
    RETRACTED,
    LOW,
    HIGH,
    MANUAL
};
```

### Step 2 — Track the current state

```python
# VEX IQ Python
current_state = RETRACTED
```

```cpp
// VEX V5 C++
LiftState currentState = RETRACTED;
```

### Step 3 — Write the control loop

Each pass through the loop does two things:
1. Check for transitions (button presses)
2. Act based on the current state (run motors)

```python
# VEX IQ Python — lift state machine
current_state = RETRACTED
last_l1 = False
last_l2 = False

while True:
    l1 = controller.buttonLUp.pressing()
    l2 = controller.buttonLDown.pressing()

    # --- TRANSITIONS (detect button press rising edge) ---
    if l1 and not last_l1:
        # L1 cycles up through states
        if current_state == RETRACTED:
            current_state = LOW
        elif current_state == LOW:
            current_state = HIGH

    if l2 and not last_l2:
        # L2 cycles down through states
        if current_state == HIGH:
            current_state = LOW
        elif current_state == LOW:
            current_state = RETRACTED

    # --- ACTIONS (run motors based on current state) ---
    if current_state == RETRACTED:
        lift_motor.spin_to_position(0, DEGREES, 60, PERCENT)

    elif current_state == LOW:
        lift_motor.spin_to_position(90, DEGREES, 60, PERCENT)

    elif current_state == HIGH:
        lift_motor.spin_to_position(200, DEGREES, 60, PERCENT)

    # Save button history for rising edge detection
    last_l1 = l1
    last_l2 = l2
    wait(20, MSEC)
```

```cpp
// VEX V5 C++ — lift state machine
LiftState currentState = RETRACTED;
bool lastL1 = false;
bool lastL2 = false;

while (true) {
    bool l1 = Controller1.ButtonL1.pressing();
    bool l2 = Controller1.ButtonL2.pressing();

    // Transitions
    if (l1 && !lastL1) {
        if (currentState == RETRACTED) currentState = LOW;
        else if (currentState == LOW)  currentState = HIGH;
    }
    if (l2 && !lastL2) {
        if (currentState == HIGH) currentState = LOW;
        else if (currentState == LOW) currentState = RETRACTED;
    }

    // Actions
    switch (currentState) {
        case RETRACTED:
            LiftMotor.spinToPosition(0, degrees, 60, percent, false);
            break;
        case LOW:
            LiftMotor.spinToPosition(90, degrees, 60, percent, false);
            break;
        case HIGH:
            LiftMotor.spinToPosition(200, degrees, 60, percent, false);
            break;
    }

    lastL1 = l1;
    lastL2 = l2;
    wait(20, msec);
}
```

## State Diagrams

Before writing code, draw a state diagram. It is just a picture of your states with arrows showing transitions.

```
         [ L1 pressed ]              [ L1 pressed ]
RETRACTED ──────────────▶ LOW ──────────────────▶ HIGH
          ◀──────────────     ◀──────────────────
         [ L2 pressed ]              [ L2 pressed ]
```

Drawing this first helps you catch missing transitions and impossible states before you write a single line of code. Put your state diagram in your engineering notebook!

## Adding Sensor-Triggered Transitions

States do not have to be triggered only by buttons — sensors work too. This is called a **sensor-triggered transition**.

```python
# VEX IQ Python — automatically raise the lift when an object is detected
if distance_sensor.object_distance(MM) < 80:
    current_state = LOW  # object is close — raise lift automatically
```

This is how you can build "smart" robots that react to the game field automatically.

## Gravity Compensation

When a lift arm is extended, gravity pulls it down. The motor has to fight gravity to hold the arm in place. You can help by adding a small constant power term when the lift is not retracted.

```python
# VEX IQ Python — add a small "hold" power to fight gravity
GRAVITY_HOLD = 5  # tune this value — the minimum power that keeps arm still

if current_state == HIGH:
    lift_motor.spin_to_position(200, DEGREES, 60, PERCENT)
    # After reaching position, apply hold power
    lift_motor.spin(FORWARD, GRAVITY_HOLD, PERCENT)
```

Start with a small value (5–10%) and increase it until the arm holds its position without drifting down.

## Common Mistakes

- **No rising-edge detection** — Without checking `last_button`, the state machine transitions 50 times per second while the button is held, cycling through all states instantly.
- **Skipping the state diagram** — Always draw it first. You will almost always find a missing transition you had not thought of.
- **One too many states** — Keep it simple. Add states only when you actually need them.
- **Mixing state machine logic with drive code** — Keep your lift state machine separate from your drive code. Running them in the same big block of code creates hard-to-find bugs.

## Related Topics

- [Software: Driver Control](Software-DriverControl.md)
- [Software: Competition Template](Software-CompetitionTemplate.md)
- [Mechanisms: Lifts](Mechanisms-Lifts.md)
- [Hardware: Motor Encoders](Hardware-MotorEncoders.md)
