Skip to main content

Overview

WPILib provides classes for controlling pneumatic systems, including solenoids for actuators and compressors for maintaining air pressure. This guide covers the Pneumatics Control Module (PCM) and Pneumatic Hub (PH).

Pneumatics Modules

WPILib supports two types of pneumatics modules:
  • CTRE PCM (Pneumatics Control Module) - Legacy CAN-based module
  • REV PH (Pneumatic Hub) - Current generation CAN-based module with additional features

Module Types

import edu.wpi.first.wpilibj.PneumaticsModuleType;

// Specify module type
PneumaticsModuleType.CTREPCM;  // CTRE PCM
PneumaticsModuleType.REVPH;    // REV Pneumatic Hub

Solenoids

Source: wpilibj/src/main/java/edu/wpi/first/wpilibj/Solenoid.java:14 Solenoids are electrically controlled pneumatic valves for actuating cylinders.

Single Solenoid

Single-acting solenoids have one state (extend/retract) controlled by air pressure:
import edu.wpi.first.wpilibj.Solenoid;
import edu.wpi.first.wpilibj.PneumaticsModuleType;

public class IntakeSubsystem {
  private Solenoid m_solenoid;

  public IntakeSubsystem() {
    // Solenoid on channel 0 of default REV PH module
    m_solenoid = new Solenoid(PneumaticsModuleType.REVPH, 0);
    
    // Or specify module ID
    m_solenoid = new Solenoid(1, PneumaticsModuleType.REVPH, 0);
  }

  public void extend() {
    m_solenoid.set(true);
  }

  public void retract() {
    m_solenoid.set(false);
  }

  public void toggle() {
    m_solenoid.toggle();
  }

  public boolean isExtended() {
    return m_solenoid.get();
  }
}

Double Solenoid

Double-acting solenoids control both extend and retract with air pressure:
import edu.wpi.first.wpilibj.DoubleSolenoid;
import edu.wpi.first.wpilibj.PneumaticsModuleType;
import static edu.wpi.first.wpilibj.DoubleSolenoid.Value;

public class ClawSubsystem {
  private DoubleSolenoid m_solenoid;

  public ClawSubsystem() {
    // Forward channel 0, reverse channel 1
    m_solenoid = new DoubleSolenoid(
      PneumaticsModuleType.REVPH,
      0,  // Forward channel
      1   // Reverse channel
    );
  }

  public void open() {
    m_solenoid.set(Value.kForward);
  }

  public void close() {
    m_solenoid.set(Value.kReverse);
  }

  public void off() {
    // Turn off both solenoids (holds position)
    m_solenoid.set(Value.kOff);
  }

  public Value getPosition() {
    return m_solenoid.get();
  }

  public boolean isOpen() {
    return m_solenoid.get() == Value.kForward;
  }
}

Pulse Control

Source: wpilibj/src/main/java/edu/wpi/first/wpilibj/Solenoid.java:133 Create timed pulses for quick actuations:
Solenoid solenoid = new Solenoid(PneumaticsModuleType.REVPH, 0);

// Set pulse duration (PCM: 0.01-2.55s, PH: 0.001-65.534s)
solenoid.setPulseDuration(0.5); // 500ms pulse

// Trigger the pulse
solenoid.startPulse();
// Solenoid will activate for 500ms then deactivate automatically

Compressor

Source: wpilibj/src/main/java/edu/wpi/first/wpilibj/Compressor.java:14 The compressor automatically maintains air pressure. It typically doesn’t need to be controlled directly.

Basic Compressor Usage

import edu.wpi.first.wpilibj.Compressor;
import edu.wpi.first.wpilibj.PneumaticsModuleType;

public class RobotContainer {
  private Compressor m_compressor;

  public RobotContainer() {
    // Compressor on default module
    m_compressor = new Compressor(PneumaticsModuleType.REVPH);
    
    // Or specify module ID
    m_compressor = new Compressor(1, PneumaticsModuleType.REVPH);
    
    // Compressor automatically runs in closed loop by default
  }

  public void disableCompressor() {
    m_compressor.disable();
  }

  public void enableCompressor() {
    m_compressor.enableDigital();
  }

  public boolean isCompressorRunning() {
    return m_compressor.isEnabled();
  }
}

Compressor Control Modes

Source: wpilibj/src/main/java/edu/wpi/first/wpilibj/Compressor.java:129
Compressor compressor = new Compressor(PneumaticsModuleType.REVPH);

// Digital mode (using pressure switch)
compressor.enableDigital();

// Analog mode (REV PH only with analog pressure sensor)
compressor.enableAnalog(100, 120); // Min 100 PSI, Max 120 PSI

// Hybrid mode (REV PH only - uses both digital and analog)
compressor.enableHybrid(100, 120);

// Disable compressor
compressor.disable();

Compressor Monitoring

Source: wpilibj/src/main/java/edu/wpi/first/wpilibj/Compressor.java:82
Compressor compressor = new Compressor(PneumaticsModuleType.REVPH);

// Check if compressor is running
boolean running = compressor.isEnabled();

// Check pressure switch state
boolean pressureLow = compressor.getPressureSwitchValue();

// Get current draw
double current = compressor.getCurrent();

// REV PH only - analog pressure sensor
double voltage = compressor.getAnalogVoltage();
double pressure = compressor.getPressure(); // PSI

Complete Pneumatics Example

import edu.wpi.first.wpilibj.TimedRobot;
import edu.wpi.first.wpilibj.DoubleSolenoid;
import edu.wpi.first.wpilibj.Solenoid;
import edu.wpi.first.wpilibj.Compressor;
import edu.wpi.first.wpilibj.Joystick;
import edu.wpi.first.wpilibj.PneumaticsModuleType;
import static edu.wpi.first.wpilibj.DoubleSolenoid.Value;

public class Robot extends TimedRobot {
  private Compressor m_compressor;
  private DoubleSolenoid m_clawSolenoid;
  private Solenoid m_armSolenoid;
  private Joystick m_joystick;

  @Override
  public void robotInit() {
    // Initialize pneumatics module
    m_compressor = new Compressor(PneumaticsModuleType.REVPH);
    
    // Claw - double solenoid on channels 0 and 1
    m_clawSolenoid = new DoubleSolenoid(
      PneumaticsModuleType.REVPH, 0, 1
    );
    
    // Arm - single solenoid on channel 2
    m_armSolenoid = new Solenoid(PneumaticsModuleType.REVPH, 2);
    
    m_joystick = new Joystick(0);
  }

  @Override
  public void teleopPeriodic() {
    // Button 1: Open claw
    if (m_joystick.getRawButtonPressed(1)) {
      m_clawSolenoid.set(Value.kForward);
    }
    
    // Button 2: Close claw
    if (m_joystick.getRawButtonPressed(2)) {
      m_clawSolenoid.set(Value.kReverse);
    }
    
    // Button 3: Toggle arm
    if (m_joystick.getRawButtonPressed(3)) {
      m_armSolenoid.toggle();
    }
    
    // Display compressor status
    if (m_compressor.isEnabled()) {
      System.out.println("Compressor running - Current: " + 
        m_compressor.getCurrent() + "A");
    }
  }

  @Override
  public void disabledInit() {
    // Retract all pneumatics when disabled
    m_clawSolenoid.set(Value.kOff);
    m_armSolenoid.set(false);
  }
}

Solenoid Safety

Source: wpilibj/src/main/java/edu/wpi/first/wpilibj/Solenoid.java:116

Check for Shorts

Solenoid solenoid = new Solenoid(PneumaticsModuleType.REVPH, 0);

// Check if solenoid is disabled due to short
if (solenoid.isDisabled()) {
  System.err.println("Solenoid is shorted and disabled!");
}

Best Practices

  • One compressor per robot - Only instantiate one Compressor object
  • Use double solenoids for critical functions - They provide positive control in both directions
  • Turn off solenoids when disabled - Save air and reduce wear
  • Monitor compressor current - High current may indicate leaks or problems
  • Check for shorts - Use isDisabled() to detect solenoid shorts
  • Use pulse mode for quick actions - Saves air for brief actuations
  • Size your air storage - Ensure you have enough air for a full match

Troubleshooting

Solenoid Not Working

  • Check CAN connection to pneumatics module
  • Verify correct channel numbers
  • Check for shorts with isDisabled()
  • Ensure compressor is building pressure
  • Verify pneumatic connections

Compressor Not Running

  • Check pressure switch wiring
  • Verify compressor is enabled: enableDigital()
  • Check current draw (should be 0A when not running)
  • Ensure 12V power to compressor

Low Pressure

  • Check for air leaks in system
  • Verify pressure switch threshold
  • Check compressor current (should be high when running)
  • Inspect airline connections

Next Steps

Build docs developers (and LLMs) love