Motor Controllers
A motor controller sits between your code and the motor's electrical power. It translates a -1.0 to 1.0 software command into a voltage and current that spins a motor. This lesson covers the two motor controller families you will use in FRC — CTRE's TalonFX and REV's SparkMax — and the configuration patterns that protect your hardware.
By the end of this lesson, you will:
- Construct a
TalonFXorSparkMaxwith the correct CAN ID and configure it once inrobotInit() - Set motor output, read velocity, position, and current draw from both controller families
- Apply a stator current limit that protects the motor from stall damage
- Choose brake vs. coast neutral mode and explain the mechanical consequence of each
- Invert motor direction correctly without changing the physical wiring
The Two Families
Most FRC teams use motor controllers from one of two vendors. CTRE (Cross The Road Electronics) makes the TalonFX — the all-in-one motor controller integrated into the Falcon 500 and Kraken X60. REV Robotics makes the SPARK MAX and SPARK Flex, used with NEO and NEO Vortex brushless motors. Both families are competitive and capable; many teams use both on the same robot.
The vendor library you installed in Unit 0 provides the Java classes for each. CTRE uses the Phoenix 6 API; REV uses REVLib. The APIs are different — same concept, different method names. The comparison tabs later in this lesson show equivalent operations side by side.
TalonFX: The Configuration Pattern
Phoenix 6 uses a configuration object pattern: you build a TalonFXConfiguration, set all your parameters on it, then call motor.getConfigurator().apply(config) once in robotInit(). The configuration is stored in the motor controller's flash memory and persists even through a power cycle — though re-applying it each match guarantees you start with the correct values.
Click each highlighted section of the configuration block below to understand what it does and what happens if you skip it.
Current Limiting: The Most Important Protection
A brushless motor's stall current — what it draws when it's commanded to move but physically can't — can exceed 100 amps. Most PDH breakers are rated at 40A for drive motors. Without a current limit, a stalled motor will trip the breaker, cutting power to that motor for the rest of the match. With a software current limit, the controller reduces output before the breaker trips.
During robot inspections I look for motor controllers without current limits. A TalonFX integrated Falcon 500 can draw over 105A at stall. If you put a 40A breaker on a motor with no software current limit and stall it — intake jam, arm hitting a hard stop, climber at full extension — the breaker trips. Not after a delay, not with a warning: instantly. The motor goes silent mid-match. Every motor on your robot that can stall should have a current limit. This takes two lines of configuration and costs you nothing except a few minutes to choose a reasonable starting value.
Neutral Mode: Brake vs. Coast
Neutral mode controls what happens when the motor output is set to zero. Brake mode actively shorts the motor leads — the motor resists motion and the robot decelerates quickly. Coast mode disconnects the motor leads — the robot coasts freely. The choice is mechanical and strategic, not just software preference.
Reading from Motor Controllers
Motor controllers report sensor data back to the roboRIO over the CAN bus. The TalonFX has an integrated encoder built into the motor — no separate encoder needed. SparkMax reads the encoder attached to the motor's hall-effect sensor (NEO) or an external encoder.
TalonFX vs. SparkMax: API Comparison
The tabs below show equivalent operations in both APIs. The conceptual model is identical — the syntax differs.
Both APIs accept a duty-cycle output from -1.0 (full reverse) to +1.0 (full forward). Phoenix 6 also supports velocity and position closed-loop control natively; REV's closed-loop is configured through the SparkMax PID controller object.
TalonFX uses Phoenix 6's StatusSignal pattern — every reading returns a signal object with .getValueAsDouble(). SparkMax returns primitive doubles directly from the encoder object. Both report velocity in their native units: TalonFX in rotations/second, SparkMax in RPM.
Both APIs protect the motor from stall damage via current limits, but the terminology differs. TalonFX uses stator current (current flowing through the motor windings). SparkMax uses supply current (current drawn from the PDH). Stator limits are generally more protective for the motor itself.
SparkMax configuration is volatile by default — it resets to factory defaults on power loss. Call motor.burnFlash() to save settings to the controller's flash memory. However: burnFlash() takes ~200ms and can only be called a limited number of times (rated for ~10,000 writes). Call it once during the initial setup session, not every match.
Many mechanisms use two or more motors mechanically linked together (gearbox with two motors, elevator with two motors). Rather than commanding each motor separately, set one as the "leader" and the others as "followers" — followers mirror the leader's output automatically.
Motor Inversion
On most mechanisms, two motors on opposite sides of a gearbox spin in opposite directions to produce the same mechanical output (both wheels push forward). Rather than rewiring hardware, invert one motor in software. Inversion should be applied in configuration, not by negating the output value — negating the output in code bypasses inversion tracking and causes confusing behavior in closed-loop control.
- Every motor that can stall must have a current limit. Calculate your limit based on the PDH breaker rating (typically 40A for drive). Set the software limit ~10A below the hardware breaker rating to prevent trips under normal operation.
- Set neutral mode per mode: Brake for teleop and auto, Coast for disabled. This prevents the robot from dragging during pit movement while maintaining stopping authority during competition.
- Apply all configuration in
robotInit(), once per power cycle. Never callgetConfigurator().apply()inside a periodic method — it sends CAN traffic and can block the loop. - Use
InvertedValueenum for TalonFX inversion, not output negation. Software inversion is part of the motor's configuration model and interacts correctly with closed-loop control. - Read
getStatorCurrent()and log it to SmartDashboard. Current draw is the most reliable early indicator of a mechanical problem — a motor drawing 2x its normal current is a mechanism about to jam or a chain about to snap.
Knowledge Check
motor1.set(0.5) and motor2.set(-0.5) every cycle. What is wrong with this approach?Configure, Command, and Monitor a Motor
- Add a
TalonFX driveMotorfield to yourRobot.java. InrobotInit(), construct it with the correct CAN ID fromConstants.javaand call aprivate void configureMotor(TalonFX motor)helper that applies aTalonFXConfigurationwith a 50A stator current limit, Brake neutral mode, and no inversion. - In
teleopPeriodic(), read the left stick Y-axis (with negation and deadband from Lesson 2) and pass it todriveMotor.set(). Test with a physical robot or simulation: confirm forward stick drives the motor forward. - In
robotPeriodic(), publishdriveMotor.getStatorCurrent().getValueAsDouble(),getVelocity().getValueAsDouble(), andgetPosition().getValueAsDouble()to SmartDashboard. Watch the values change as you command the motor. - In
disabledInit(), switch the motor to Coast mode. InteleopInit(), switch back to Brake. Verify the transition by enabling and disabling while monitoring dashboard output. - Add a second
TalonFX followerMotor. Configure it as a follower ofdriveMotor. Command only the leader — confirm the follower mirrors it. - Bonus: Repeat steps 1–3 using a SparkMax with REVLib instead of TalonFX with Phoenix 6. What API differences did you notice? Which pattern did you find more readable?