Skip to content

The IO Layer Pattern

The most important architectural rule in MARSLib is the separation of hardware from logic. We use the AdvantageKit IO abstraction pattern to ensure that our robot code is deterministic, testable, and capable of bit-perfect log replay.

First, we define an interface that lists all inputs the subsystem needs from the hardware. We use @AutoLog from AdvantageKit to automatically generate the logging boilerplate.

public interface FlywheelIO {
@AutoLog
public static class FlywheelIOInputs {
public double positionRad = 0.0;
public double velocityRadPerSec = 0.0;
public double appliedVolts = 0.0;
public double currentAmps = 0.0;
}
public default void updateInputs(FlywheelIOInputs inputs) {}
public default void setVoltage(double volts) {}
}

The “Real” setup talks to actual motors. It has no logic—it only moves data between the hardware and the Inputs object.

public class FlywheelIOTalonFX implements FlywheelIO {
private final [TalonFX](https://v6.docs.ctr-electronics.com/en/stable/docs/api-reference/api-usage/talonfx.html) motor;
public FlywheelIOTalonFX(int id) {
motor = new TalonFX(id);
}
@Override
public void updateInputs(FlywheelIOInputs inputs) {
inputs.positionRad = motor.getPosition().getValueAsDouble();
inputs.velocityRadPerSec = motor.getVelocity().getValueAsDouble();
}
@Override
public void setVoltage(double volts) {
motor.setControl(new VoltageOut(volts));
}
}

The “Sim” setup uses math to calculate how a motor would behave. MARSLib uses WPILib Physics Sims (like DCMotorSim) and MARSPhysicsWorld for high-fidelity physics.

public class FlywheelIOSim implements FlywheelIO {
private final DCMotorSim sim = new DCMotorSim(DCMotor.getFalcon500(1), 1.0, 0.01);
@Override
public void updateInputs(FlywheelIOInputs inputs) {
sim.update(0.020);
inputs.positionRad = sim.getAngularPositionRad();
inputs.velocityRadPerSec = sim.getAngularVelocityRadPerSec();
}
}

In RobotContainer, we decide which version to use based on the robot mode. The Subsystem itself only ever sees the FlywheelIO interface!

FlywheelIO io;
if (isReal) {
io = new FlywheelIOTalonFX(1);
} else {
io = new FlywheelIOSim();
}
subsystem = new FlywheelSubsystem(io);