Unit 7 · Lesson 2

Swerve Hardware Overview

Before the first line of code is deployed to a swerve robot, twelve CAN devices need to be configured, calibrated, and verified. This lesson covers everything that happens in Phoenix Tuner X and Constants.java before you touch the SwerveModule class — the work that determines whether the robot drives correctly or drives sideways.

By the end of this lesson, you will:

  • Plan a 13-device CAN bus with unique, organized IDs and document it in Constants.java
  • Explain what a CANcoder offset is, why it's required, and how to measure it in Phoenix Tuner X
  • Configure a CANcoderConfiguration in code with the correct magnet offset
  • Initialize a TalonFX drive motor and steering motor with the correct gear ratio, neutral mode, and current limits for swerve
  • Initialize a Pigeon2 gyroscope and verify heading output
  • Convert encoder ticks to meters per second using the drive gear ratio and wheel circumference

CAN Bus Planning: Twelve Devices, One Network

The CAN bus is a single serial network. Every device — motor controllers, encoders, gyros — shares it. Each device must have a unique integer ID from 0 to 62. At 12 swerve devices plus a PDH and any mechanism motors, you can easily have 20+ devices on one bus. Collisions and conflicts become likely unless IDs are planned before the first wire is crimped.

The 2910 convention groups devices by type, not by module. This makes debugging easier: if motor IDs 1–4 are all drive motors, a missing ID in the 1–4 range instantly points to a drive motor problem. Click any cell in the map below to see the device it represents.

2910 CAN ID Convention — Swerve Drivetrain
ID: 1
TalonFX
Drive — Front Left
ID: 2
TalonFX
Drive — Front Right
ID: 3
TalonFX
Drive — Back Left
ID: 4
TalonFX
Drive — Back Right
ID: 11
TalonFX
Steer — Front Left
ID: 12
TalonFX
Steer — Front Right
ID: 13
TalonFX
Steer — Back Left
ID: 14
TalonFX
Steer — Back Right
ID: 21
CANcoder
Encoder — Front Left
ID: 22
CANcoder
Encoder — Front Right
ID: 23
CANcoder
Encoder — Back Left
ID: 24
CANcoder
Encoder — Back Right
ID: 30
Pigeon 2
Gyroscope
← click a device to see its role and configuration notes

CANcoder Offset Calibration: The Most Critical Step

The CANcoder reports absolute wheel angle. But "absolute" is relative — the sensor doesn't know which direction is "straight forward" for your robot. That definition is called the magnet offset, and you set it once in Phoenix Tuner X during initial hardware bringup.

If the offset is wrong, the steering PID will command the wheels to what it thinks is "straight" — but the wheels will actually point at a random angle. The robot will drive sideways, spin, or refuse to go straight, and no amount of code debugging will fix it. This is a hardware calibration problem, not a code problem.

1
Position the robot
Place the robot on a flat surface. Manually align all four wheels to point straight forward (parallel to the robot's long axis, toward the robot's front). The beveled edge of the wheel should face the same direction on all four modules. Precision here matters — use a straight edge against the robot frame if needed.
2
Read the absolute position in Tuner X
Open Phoenix Tuner X. Connect to the robot via USB or radio. Navigate to each CANcoder device. In the self-test or signals view, read the Absolute Position value — this is in rotations (0.0 to 1.0) or degrees depending on your view. Write this value down for each module. This raw reading is your offset: it is the angle the encoder reports when the wheel is at zero degrees.
3
Store offsets in Constants.java
Add each offset as a double constant in your Constants.Swerve class, in rotations (Phoenix 6 uses rotations as its native unit). Example: kFrontLeftOffset = -0.413. Negate the raw reading — Phoenix 6's MagnetSensorSettings.withMagnetOffset() takes a value that cancels the offset, so the negation is intentional.
4
Verify after deploying
After deploying code with your offsets applied, publish each CANcoder's getAbsolutePosition() to SmartDashboard and enable the robot. All four values should read approximately 0.0 with the wheels in their straight-forward position. If any module reads a non-zero value, re-measure that module's offset in Tuner X and update the constant.

The Constants File: Your Hardware Contract

All gear ratios, offsets, wheel dimensions, and CAN IDs live in Constants.java. This file is the single source of truth for your drivetrain's physical properties. Click the highlighted tokens in the code below to understand each constant's physical meaning.

Constants.java — Swerve drivetrain constants
public final class Constants {
  public static final class Swerve {

    // ── Module physical geometry ──────────────────
    public static final double kTrackWidthMeters = 0.546; // L-R wheel spacing
    public static final double kWheelBaseMeters = 0.546; // F-B wheel spacing
    public static final double kWheelDiameterMeters= 0.1016;// 4 inches = 0.1016 m
    public static final double kWheelCircumference =
      kWheelDiameterMeters * Math.PI;

    // ── Drive motor gear ratio ────────────────────
    // MK4i L2 = 6.75:1 (motor rotations per wheel rotation)
    public static final double kDriveGearRatio = 6.75;
    public static final double kSteerGearRatio = 150.0 / 7.0; // ≈21.43:1

    // ── Velocity conversion ───────────────────────
    // rotations/sec → meters/sec
    public static final double kDriveConversionFactor =
      kWheelCircumference / kDriveGearRatio;

    // ── CAN IDs ───────────────────────────────────
    public static final int kFLDrive=1, kFRDrive=2, kBLDrive=3, kBRDrive=4;
    public static final int kFLSteer=11,kFRSteer=12,kBLSteer=13,kBRSteer=14;
    public static final int kFLEncoder=21,kFREncoder=22,kBLEncoder=23,kBREncoder=24;
    public static final int kPigeonId=30;

    // ── CANcoder offsets (negated Tuner X readings)
    public static final double kFLOffset = -0.413;
    public static final double kFROffset = -0.278;
    public static final double kBLOffset = 0.135;
    public static final double kBROffset = -0.091;

    // ── Drive limits ──────────────────────────────
    public static final double kMaxSpeedMetersPerSecond = 4.5;
    public static final double kMaxAngularRadPerSecond = 2 * Math.PI;
    public static final double kDriveCurrentLimitAmps = 40;
    public static final double kSteerCurrentLimitAmps = 20;
  }
}
← click a highlighted token

Phoenix 6 Device Configuration

Phoenix 6 uses a configuration object pattern: you build a config object, set all its properties, then apply it to the device with device.getConfigurator().apply(config). This is done once in the constructor. The key configurations for swerve are covered below.

SwerveModule.java — hardware configuration in constructor
// ── CANcoder configuration ────────────────────────
CANcoderConfiguration ccConfig = new CANcoderConfiguration();
ccConfig.MagnetSensor
  .withMagnetOffset(magnetOffsetRotations)
  .withAbsoluteSensorDiscontinuityPoint(0.5)
  .withSensorDirection(SensorDirectionValue.CounterClockwise_Positive);
m_cancoder.getConfigurator().apply(ccConfig);

// ── Drive motor configuration ─────────────────────
TalonFXConfiguration driveConfig = new TalonFXConfiguration();
driveConfig.MotorOutput
  .withNeutralMode(NeutralModeValue.Brake);
driveConfig.CurrentLimits
  .withStatorCurrentLimit(Constants.Swerve.kDriveCurrentLimitAmps)
  .withStatorCurrentLimitEnable(true);
driveConfig.Feedback
  .withSensorToMechanismRatio(Constants.Swerve.kDriveGearRatio);
m_driveMotor.getConfigurator().apply(driveConfig);

// ── Steering motor configuration ──────────────────
TalonFXConfiguration steerConfig = new TalonFXConfiguration();
steerConfig.MotorOutput
  .withNeutralMode(NeutralModeValue.Brake);
steerConfig.CurrentLimits
  .withStatorCurrentLimit(Constants.Swerve.kSteerCurrentLimitAmps)
  .withStatorCurrentLimitEnable(true);
steerConfig.Feedback
  .withSensorToMechanismRatio(Constants.Swerve.kSteerGearRatio)
  .withRemoteCANcoder(m_cancoder);
m_steerMotor.getConfigurator().apply(steerConfig);
← click a highlighted token
🔍 LRI Perspective: "Configuration at inspection time"

When I inspect a swerve robot and the code deploys but the wheels don't track straight, the first thing I ask is: "Did you verify your CANcoder offsets after the last time someone manually rotated a module?" A single accidental bump of a module during transport or pit setup changes the wheel angle but leaves the encoder reading the same. The offset is now wrong. This is why 2910's pre-match protocol always includes a Shuffleboard check: all four CANcoder absolute positions should read near 0.0 when the wheels are straight. That check takes 15 seconds and has prevented dozens of match-day failures.

Gear Ratio Conversions: From Ticks to Reality

The TalonFX reports position in rotations of the motor shaft. To get wheel rotations, divide by the gear ratio. To get distance traveled, multiply by wheel circumference. Phoenix 6's withSensorToMechanismRatio(gearRatio) does this conversion automatically — after applying it, getPosition().getValueAsDouble() returns wheel rotations directly.

What you wantFormulaPhoenix 6 method
Wheel rotations from motor rotationsmotorRot ÷ gearRatioAutomatic after withSensorToMechanismRatio()
Wheel distance (meters)wheelRot × circumferenceMultiply getPosition() by kWheelCircumference
Wheel velocity (m/s)motorRPS ÷ gearRatio × circumferenceMultiply getVelocity() by kWheelCircumference
Steering angle (rotations)Direct CANcoder readingm_cancoder.getAbsolutePosition().getValueAsDouble()
🔌 System Check — Hardware Verification Sequence

Before running any swerve code, complete in this exact order:

  • Open Tuner X. Confirm all 13 expected devices appear by name and CAN ID. No yellow warnings, no "No Firmware" entries.
  • For each CANcoder: read Absolute Position with wheels pointing forward. Record the values. These become your offset constants (negated) in Constants.java.
  • For each TalonFX: run a self-test snapshot. Confirm no fault flags. Check firmware versions match your Phoenix 6 vendordep version.
  • Verify the Pigeon 2 by rotating the robot by hand on blocks. Confirm heading changes appropriately in SmartDashboard (clockwise should increase heading if using standard WPILib convention with Rotation2d.fromDegrees(-pigeon.getYaw())).
  • Deploy your Constants file and confirm SmartDashboard shows all four CANcoder absolute positions near 0.0 with wheels in the forward position.

Knowledge Check

1. You deploy your swerve code for the first time. All wheels spin but the robot drives sideways when you push forward. The most likely cause is:

  • A The drive motors are inverted in code.
  • B The CANcoder offsets are wrong — the steering PID is targeting what it thinks is "forward" but the wheels are actually aligned 90° from the robot's front.
  • C The gear ratio constant is incorrect.
  • D The CAN IDs are swapped between two modules.

2. Your SDS MK4i module uses an L2 gear ratio (6.75:1). The TalonFX reports a velocity of 30 rotations/second. What is the wheel's velocity in meters/second? (Wheel diameter = 4 inches = 0.1016 m.)

  • A 30 × π × 0.1016 = 9.57 m/s
  • B (30 ÷ 6.75) × (π × 0.1016) = 4.44 × 0.319 ≈ 1.42 m/s
  • C 30 × 0.1016 ÷ 6.75 = 0.452 m/s
  • D 30 ÷ 6.75 = 4.44 m/s

3. Why is NeutralModeValue.Brake required for steering motors on a swerve drive, while it is a judgment call for drive motors?

  • A Brake mode prevents the steering motor from overheating.
  • B If a steering motor is in coast mode and the robot is disabled or the command stops, the wheel can rotate freely — meaning it could be pointing anywhere when the robot re-enables. Brake mode holds the wheel at its last commanded angle, ensuring the module starts from a known position.
  • C Brake mode is required for any motor controlled by a PID loop.
  • D Coast mode is forbidden for CAN-connected motors by the FRC rules.
💪 Practice Prompt

Configure Your Hardware Before Touching SwerveModule

  1. Complete your Constants.Swerve class with all values from the Lesson 1 practice prompt plus the four CANcoder offsets measured in Tuner X. Confirm your gear ratio and wheel diameter match your physical module spec sheet (SDS documentation is at swervedrivespecialties.com).
  2. Using the formula kWheelCircumference / kDriveGearRatio, compute the kDriveConversionFactor for your module. Add a comment showing the calculation explicitly — this number will appear in five places in your code and must be correct.
  3. Open Tuner X and perform a self-test snapshot on each TalonFX drive and steering motor. Screenshot or record any fault flags. Document which motors you expect to be inverted (on SDS MK4i: drive motors on the left side typically need inversion to produce positive-forward output). Add these as boolean constants: kFLDriveInverted = true, etc.
  4. Stretch goal: Write a short autonomous test command (or just code in teleopInit for testing) that does nothing except publish the absolute position of all four CANcoders to SmartDashboard. Deploy it and manually turn each wheel by hand. Confirm the readings change correctly and that zero-position readings are near your stored offsets. This verifies your CAN bus, your IDs, and your encoder communication in one step.