Decision flowchart: Potentiometer V2 vs Rotation Sensor for V1 Hero Bot.
📐 Sensors · Engineer · Intermediate
V5 Rotation Sensor — Overview 🌀
The V5 Rotation Sensor is a hall-effect magnetic angle sensor on a Smart port. It tracks shaft rotation continuously, in 1/100-degree increments (centidegrees), with no mechanical stops. It's the canonical sensor for odometry tracking wheels and the higher-resolution alternative to the Potentiometer V2 for arm/lift angle.
🎯
What this guide is for: the Rotation Sensor as it's used on V5RC robots running PROS + EZ-Template (sometimes paired with LemLib). Hardware, mounting, code patterns, and a clear decision section comparing the Rotation Sensor against the Potentiometer V2 for arm control. Sources verified against the VEX product page, the VEX Knowledge Base, and the PROS V5 documentation.
At a Glance
Part Number
276-7705 (V5 Rotation Sensor)
Connection
3-pin V5 Smart Cable to any Smart Port (1–21)
Sensing Method
Hall-effect magnetic angle (no mechanical wear)
Rotation Range
Infinite — tracks multi-turn position with no mechanical stops
Resolution
1/100 of a degree (centidegrees) — 36,000 ticks per revolution
Default Data Rate
~10 ms (configurable down to 5 ms)
Shaft Interface
1/4″ square hole — press-fits standard VEX shaft inserts
Reversible in Code
Yes — set_reversed(bool) flips the count direction
What Makes It Useful
Infinite rotation — unlike the Potentiometer V2 (333° range), the Rotation Sensor keeps counting indefinitely. Required for odometry tracking wheels (which spin many revolutions during a match) and useful for any mechanism that may exceed 333°.
High resolution — 36,000 ticks per revolution (vs. ~360 for a 1°-quantized Potentiometer reading). For odom, this is the difference between drift-prone tracking and precise pose estimation.
Smart port — uses your Brain's Smart Cable bandwidth, not an ADI port. Plug-and-play with PROS' pros::Rotation class.
Wear-free — the hall-effect magnetic readout has no mechanical contact (unlike the original Potentiometer's carbon track, which can wear). Should outlive the season.
Where It Fits in Spartan's Sensor Stack
Per the Sensor Onboarding Roadmap, the Rotation Sensor sits in Tier 4 (game-element-aware sensing) for arm/lift use, and Tier 5 (advanced positioning) for odometry tracking wheels. It's not a Tier 1 install — new teams should master IMU + motor encoders first — but it becomes nearly mandatory once you start tuning autons against odometry.
💡
For Override 2026-27: the Hero Bot baseline arm uses preset angles (ground, low goal 3.25″, mid 5.8″, high 8.7″). Either a Potentiometer V2 or a Rotation Sensor works. See Section 7: Rotation Sensor vs. Potentiometer for Arm Control for the decision matrix.
Related Guides
sensors-discrete — Potentiometer V2 deep dive (the cheaper, simpler alternative for fixed-range arm angle)
sensors-roadmap — where this sensor fits in the priority stack
odom-pod-build — building tracking wheel pods around this sensor
The sensor reports two things continuously: an absolute position counter and an instantaneous angle within one revolution. Knowing the difference matters for both odometry and arm control.
Cumulative rotation count, in centidegrees (cd). 1 full revolution = 36,000 cd. Multi-turn capable: spins past 36,000 keep counting (e.g., 5 revolutions = 180,000 cd). This is the value you use for odometry tracking wheels.
get_angle()
Current angle within one revolution, 0–36,000 cd. Wraps at every full turn. This is the value you use when you want absolute angle within a single rotation, like a wheel's heading or an arm whose total range is <360°.
get_velocity()
Angular velocity in centidegrees/second. Useful for velocity-feedforward control or for detecting when a mechanism has stalled (velocity ≈ 0 with motor power applied).
Centidegrees, In Plain Numbers
The 1/100-degree resolution sounds abstract until you map it to physical motion:
1 cd = 0.01°. So a reading of 4500 means 45.00°.
36,000 cd = 360° = 1 full revolution.
An Override arm moving from ground (0°) to high goal (~90°) traverses ~9000 cd.
The Hero Bot arm presets in advanced-robot use 0/600/1400/2400 cd — that's 0° / 6° / 14° / 24° on a high-reduction mechanism (gear-reduced from the motor), or it could mean 6/14/24 cd of sensor reading for a direct-coupled arm with a smaller swing. Always sanity-check against the physical mechanism: 600 cd is only 6°, which is small.
An odom tracking wheel with a 2″ diameter = π × 2 = 6.28″ circumference. So 36,000 cd of sensor rotation = 6.28″ of robot travel. That's 5,732 cd per inch — the conversion factor your odom math uses.
Position vs. Angle: When Each Is Right
Use get_position() for tracking wheels. The wheel will spin many revolutions in a match. You need cumulative rotation, not "where is the wheel pointing right now."
Use get_position() for arms that may exceed 360°. Even rare cases (a flipped chain bar that loops around) can confuse get_angle() by wrapping at unexpected times.
Use get_angle() for arms with strict <360° range. If an arm physically cannot rotate past 360°, get_angle() behaves like an absolute encoder and you don't need to track wraps. But honestly, most teams just use get_position() for everything because it's simpler to reason about.
Use get_velocity() for stall detection. Pair with a torque check on the motor: if motor current is high and Rotation Sensor velocity is near zero, the mechanism is stuck.
⚠️
Position is signed and accumulates indefinitely. If you spin the sensor 100 revolutions clockwise then 1 counter-clockwise, position is at +99 × 36,000 = +3,564,000 cd. int32_t can hold ±2.1 billion cd (~59,000 revolutions), so overflow isn't a practical concern. But you should still call reset_position() at initialize() or autonomous() start so each match begins from a known zero.
2 of 7
// Section 03
Mounting & Hardware 🔧
The Rotation Sensor reads a shaft. Mount it so the shaft turns cleanly, the sensor body is fixed, and the cable doesn't snag during mechanism travel. Different use cases (odom pod vs. arm) have different mounting concerns.
Physical Setup
The sensor has a 1/4″ square hole that accepts a standard VEX High Strength Shaft Insert. The shaft passes through the sensor; the sensor body bolts to the structure. As the shaft rotates, the sensor reads the angle.
Shaft: use a 1/4″ square shaft cut to the length you need (typically 0.5–1.5″). The shaft must rotate freely inside the sensor — binding will jam your readings.
Insert: for thinner mechanisms, use the round-shaft version of the High Strength Shaft Insert (from kit 276-1411 or similar) to adapt smaller wheels (e.g., 2″ omni for odom pods).
Bolting: the sensor body has 4 mounting holes that match standard VEX hole spacing. Two 8-32 screws through C-channel are usually sufficient. Don't over-tighten — the plastic body will distort and bind the shaft.
Cable strain relief: route the Smart Cable so it doesn't pull the sensor as the mechanism moves. Use cable ties to anchor the cable to a non-moving structure within 4″ of the sensor.
For Odometry Tracking Wheels
The Rotation Sensor is the canonical tracking-wheel sensor in V5RC. Per odom-pod-build, the standard configuration is:
2″ omni wheel (276-7254) on a 1/4″ square shaft
Shaft passes through the Rotation Sensor
Sensor body is rigid; wheel + shaft float against the floor via a spring/rubber-band-loaded pivot
Pivot keeps the wheel pressed to the floor as the chassis flexes
Two pods (parallel + perpendicular) give x/y position; pair with IMU for heading
See odom-pod-types for the swing-arm vs. fixed-mount tradeoffs and odometry for how the cumulative position counts feed into pose estimation.
For Arm/Lift Angle
Mount the Rotation Sensor on the arm's pivot axle so the shaft is the pivot. Two common configurations:
Direct-coupled. The arm's pivot shaft passes through the Rotation Sensor. The sensor reads true arm angle 1:1. Simplest; preferred for most arms. Works as long as the sensor body can be rigidly attached to a non-rotating structure on one side of the pivot.
Geared down (high reduction). The arm pivot drives a gear train that turns the sensor shaft at a higher RPM. This means the sensor reads more counts per degree of arm motion — finer effective resolution. Useful only if you need sub-1° precision on a small arm; for most Override applications, direct-coupled is plenty.
💡
Mount once, calibrate forever. Once the sensor is bolted to the arm, the relationship between sensor counts and arm angle is fixed for that mount. If you remove and remount, you'll need to re-zero. Best practice: in initialize(), run arm_rot.reset_position() when the arm is in a known reference pose (typically all-the-way-down, resting against a hard stop or limit switch). Then arm angle = position / scale_factor.
Wiring
Smart Cable: any 3-pin V5 Smart Cable. Length matters — use the shortest cable that reaches; long cables introduce noise and snag risk.
Smart Port: any port 1–21 on the V5 Brain. The port number is what you pass to the pros::Rotation constructor. Document your port assignments in code (header file or comment block) to avoid swap mistakes.
Don't share ports. Each sensor needs its own port. The 21 Smart Ports are a finite resource — budget them. A typical Override Hero Bot uses 4 motor ports + IMU + 2 odom rotation sensors = 7 ports, leaving 14 for additional sensors and motors.
3 of 7
// Section 04
PROS Code Patterns 💻
The pros::Rotation class is small and stable. The interesting code is what you build around it: the arm position controller, the odom integration, the sensor-fusion checks. Worked examples below.
The PROS API Surface
Header
#include "pros/rotation.hpp"
Class
pros::Rotation
Constructor
pros::Rotation rot(port); — port is 1–21
Position
int32_t get_position() — cumulative cd, can be negative
Angle
int32_t get_angle() — 0–36000 cd, wraps
Velocity
int32_t get_velocity() — cd/sec
Reset
int32_t reset_position() — sets position to 0
Set Position
int32_t set_position(uint32_t pos) — force a specific value
Reverse
int32_t set_reversed(bool reversed) — flip count direction
Data Rate
int32_t set_data_rate(uint32_t rate) — ms between updates, default 10, min 5
// PROS C++ — arm to target angle using Rotation Sensor
// EN4: rewrite in your own words for your notebook.
#include "pros/motor_group.hpp"
#include "pros/rotation.hpp"
#include <cstdlib>
pros::Motor arm(14);
pros::Rotation arm_rot(15); // Smart port 15
// Convert centidegrees to degrees for human-readable code
constexpr double cd_per_deg = 100.0;
// P-controller: drive arm to target angle (in degrees)
void arm_to_angle(double target_deg) {
const double kP = 0.15; // tune for your mechanism
const double tolerance_deg = 0.5;
while (true) {
double current_deg = arm_rot.get_position() / cd_per_deg;
double error = target_deg - current_deg;
if (std::abs(error) < tolerance_deg) {
arm.move(0);
arm.brake();
return;
}
double power = error * kP;
if (power > 127) power = 127;
if (power < -127) power = -127;
arm.move(power);
pros::delay(10);
}
}
// In initialize(), zero the sensor at known reference (arm fully down)
void initialize() {
arm_rot.reset_position();
}
Code Pattern: Override Arm Presets
The advanced-robot page uses arm preset constants. Here's the Rotation Sensor version of that pattern:
// PROS C++ — Override Hero Bot arm presets via Rotation Sensor
// EN4: rewrite in your own words for your notebook.
namespace ArmPresets {
// Centidegree targets for the Hero Bot arm
// Adjust based on your physical mechanism + reduction
constexpr int32_t GROUND = 0; // 0.00 deg — start position
constexpr int32_t LOW_GOAL = 600; // 6.00 deg
constexpr int32_t MID_GOAL = 1400; // 14.00 deg
constexpr int32_t HIGH_GOAL = 2400; // 24.00 deg
constexpr int32_t TOLERANCE = 50; // 0.50 deg
}
void arm_to_preset(int32_t target_cd) {
const double kP = 0.15;
while (true) {
int32_t error = target_cd - arm_rot.get_position();
if (std::abs(error) < ArmPresets::TOLERANCE) {
arm.brake();
return;
}
int32_t power = (int32_t)(error * kP);
if (power > 127) power = 127;
if (power < -127) power = -127;
arm.move(power);
pros::delay(10);
}
}
// Driver control: button-based arm presets
void opcontrol() {
pros::Controller master(pros::E_CONTROLLER_MASTER);
while (true) {
if (master.get_digital_new_press(pros::E_CONTROLLER_DIGITAL_DOWN)) {
arm_to_preset(ArmPresets::GROUND);
} else if (master.get_digital_new_press(pros::E_CONTROLLER_DIGITAL_RIGHT)) {
arm_to_preset(ArmPresets::LOW_GOAL);
} else if (master.get_digital_new_press(pros::E_CONTROLLER_DIGITAL_UP)) {
arm_to_preset(ArmPresets::MID_GOAL);
} else if (master.get_digital_new_press(pros::E_CONTROLLER_DIGITAL_LEFT)) {
arm_to_preset(ArmPresets::HIGH_GOAL);
}
pros::delay(10);
}
}
Code Pattern: Odom Tracking Wheel
The Rotation Sensor reading is the input to your pose-estimation math. The conversion is wheel-circumference dependent:
// PROS C++ — converting Rotation Sensor cd to inches of robot travel
// EN4: rewrite in your own words for your notebook.
#include "pros/rotation.hpp"
pros::Rotation tracking_par(16); // parallel pod
pros::Rotation tracking_perp(17); // perpendicular pod
// Tracking wheel constants
constexpr double WHEEL_DIAMETER_IN = 2.0;
constexpr double WHEEL_CIRC_IN = WHEEL_DIAMETER_IN * 3.14159265;
constexpr double CD_PER_REV = 36000.0;
constexpr double CD_PER_INCH = CD_PER_REV / WHEEL_CIRC_IN;
// = 5732 cd/in for a 2" wheel
double get_par_inches() {
return tracking_par.get_position() / CD_PER_INCH;
}
double get_perp_inches() {
return tracking_perp.get_position() / CD_PER_INCH;
}
// Reset both at the start of every match
void initialize() {
tracking_par.reset_position();
tracking_perp.reset_position();
}
Sensor Fusion: Rotation + IMU
The Rotation Sensor gives translation; the IMU gives heading. Together they give pose. Per the Sensor Onboarding Roadmap, the sensor-fusion pattern is:
Read parallel and perpendicular tracking wheel positions (in inches)
Read IMU heading (in radians)
Compute pose deltas using your odometry math (small-angle approximation or arc-based)
Update global x, y, theta
For a worked walkthrough, see odometry § Sensor Fusion. For the LemLib version of the same thing, see Section 5 below.
4 of 7
// Section 05
EZ-Template & LemLib Integration 🔗
EZ-Template handles the chassis with motor encoders. LemLib uses the Rotation Sensor as a first-class citizen for tracking wheels. Both can run alongside your own arm-controller task.
EZ-Template: Subsystem Pattern
EZ-Template doesn't directly consume the Rotation Sensor for chassis odometry — it uses motor encoders for distance and IMU for heading. Where the Rotation Sensor fits is in subsystem control (arm, lift, intake) running as a parallel task, just like the Potentiometer pattern in sensors-discrete § EZ-Template Integration.
// PROS task: arm position controller using Rotation Sensor (alongside EZ-Template)
// EN4: rewrite in your own words for your notebook.
extern pros::Motor arm;
extern pros::Rotation arm_rot;
struct ArmState {
pros::Mutex mutex;
int32_t target_cd = 0;
bool active = false;
};
ArmState arm_state;
void arm_task_fn(void* p) {
const double kP = 0.15;
while (true) {
arm_state.mutex.take();
bool active = arm_state.active;
int32_t target = arm_state.target_cd;
arm_state.mutex.give();
if (active) {
int32_t error = target - arm_rot.get_position();
int32_t power = (int32_t)(error * kP);
if (power > 127) power = 127;
if (power < -127) power = -127;
arm.move(power);
}
pros::delay(10);
}
}
void arm_set_target(int32_t target_cd) {
arm_state.mutex.take();
arm_state.target_cd = target_cd;
arm_state.active = true;
arm_state.mutex.give();
}
void initialize() {
arm_rot.reset_position();
pros::Task t(arm_task_fn, nullptr, "arm");
}
// Auton can now drive AND raise the arm in parallel
void example_auton() {
arm_set_target(1400); // arm to mid goal, runs in background
chassis.pid_drive_set(36_in, DRIVE_SPEED, true);
chassis.pid_wait();
// Both done by here — chassis arrived AND arm at mid goal
}
LemLib: First-Class Tracking Wheel Support
LemLib is a chassis library that uses Rotation Sensor tracking wheels directly. The construction pattern wraps Rotation Sensors in lemlib::TrackingWheel objects:
// PROS C++ — LemLib chassis with Rotation Sensor tracking wheels
// EN4: rewrite in your own words for your notebook.
#include "lemlib/api.hpp"
#include "pros/rotation.hpp"
#include "pros/imu.hpp"
// Smart port assignments
pros::MotorGroup left_drive({-1, -2});
pros::MotorGroup right_drive({3, 4});
pros::Rotation tracking_par(16); // parallel pod
pros::Rotation tracking_perp(17); // perpendicular pod
pros::Imu imu(15);
// Tracking wheels (LemLib wrappers)
lemlib::TrackingWheel par_wheel(
&tracking_par, // pointer to Rotation Sensor
lemlib::Omniwheel::NEW_2, // 2" omni
-2.5 // signed distance from tracking center, inches
);
lemlib::TrackingWheel perp_wheel(
&tracking_perp,
lemlib::Omniwheel::NEW_2,
1.5
);
lemlib::OdomSensors sensors(&par_wheel, nullptr, &perp_wheel, nullptr, &imu);
// Drivetrain
lemlib::Drivetrain drivetrain(&left_drive, &right_drive,
11.0, // track width
lemlib::Omniwheel::NEW_325, 360, 8);
// Tuning constants (placeholder — must be tuned for your robot)
lemlib::ControllerSettings lateral_controller(10, 0, 30, 3, 1, 100, 3, 500, 20);
lemlib::ControllerSettings angular_controller(2, 0, 10, 3, 1, 100, 3, 500, 0);
lemlib::Chassis chassis(drivetrain, lateral_controller, angular_controller, sensors);
void initialize() {
chassis.calibrate(); // calibrates IMU and zeros tracking wheels
}
void autonomous() {
chassis.setPose(0, 0, 0);
chassis.moveToPoint(24, 24, 4000);
chassis.moveToPose(48, 24, 90, 4000);
}
The signed distance values (-2.5, 1.5) tell LemLib where each pod sits relative to the tracking center. Get these wrong and your odom will drift unpredictably. Measure them carefully on the physical robot.
⚠️
EZ-Template + LemLib are usually one or the other. Some teams run both (EZ-Template for the chassis PID, LemLib for path-following) but it's confusing for new programmers. Pick one chassis library per robot and stick with it. Mixing them is an advanced pattern.
Reading the Sensor in Pure Auton (No Library)
You don't need EZ-Template or LemLib to use the Rotation Sensor — PROS is enough. For odom, this is hard work but possible. For arm control, this is the most common pattern and works fine alongside any chassis library:
// PROS C++ — minimal auton using Rotation Sensor for arm angle
// EN4: rewrite in your own words for your notebook.
extern pros::Rotation arm_rot;
extern pros::Motor arm;
void wait_until_arm_at(int32_t target_cd, int32_t tolerance_cd = 50, int timeout_ms = 3000) {
int32_t start = pros::millis();
while (true) {
int32_t current = arm_rot.get_position();
if (std::abs(current - target_cd) < tolerance_cd) return;
if (pros::millis() - start > timeout_ms) return; // safety timeout
pros::delay(10);
}
}
// Use in auton:
void simple_auton() {
arm.move(80);
wait_until_arm_at(1400); // wait until arm reaches mid goal
arm.brake();
// ... rest of auton
}
5 of 7
// Section 06
Override 2026-27 Use Cases 🎯
The Rotation Sensor has three main Override applications: arm angle for goal targeting (3 goal heights), odom tracking wheels for cross-field navigation, and (more niche) cup-flip mechanism state tracking.
📍
Status: Override 2026-27 confirmed per the v0.1 game manual (April 27, 2026). Goals are 3.25″ / 5.8″ / 8.7″ tall, robots have a 1+1 cup-and-pin possession limit (SG6), and the endgame requires ≤18″ height inside the midfield (SG12.1). The applications below reflect those facts.
Use Case 1: Arm Angle for Goal Targeting
Override has 3 goal heights (low 3.25″, mid 5.8″, tall 8.7″). The Hero Bot arm needs to reach all three. A Rotation Sensor on the arm pivot lets you:
Define preset angles for each goal height (see Section 4 code patterns)
Drive to a preset on a button press during driver control
Drive to a preset before scoring in autonomous, with a known timeout
Hold position against gravity using a P-controller (or stop the motor and let mechanical advantage hold — depends on your arm geometry)
This is the same use case the Potentiometer V2 covers. Section 7 (next) compares them head-to-head.
Use Case 2: Odom Tracking Wheels for Cross-Field Navigation
Override scoring is distributed: 4 alliance goals, 4 short neutral goals across quadrants, 1 tall center goal. A typical match has the robot moving across multiple goals. Encoder-only odometry (using motor encoders) drifts after long routes; tracking wheels via Rotation Sensors are much more accurate.
Skills runs (60 seconds): highest-value application. Encoder drift accumulates over a 60-second route; tracking wheels stay accurate.
Match autons (15 seconds): arguably overkill — encoder odom + IMU is usually fine for 15 seconds. But tracking wheels still help if your auton routes across the field rather than staying in one zone.
Cross-quadrant routes: if your auton flips a Toggle in one quadrant then drives to score in another, tracking wheels reduce position drift during the long traversal.
The Override field layout (per the v0.1 manual) is 12′ × 12′ with goals near corners and along the perimeter midpoints. Cross-field travel can exceed 8 feet in a single auton segment — the kind of distance where encoder drift becomes noticeable.
Use Case 3: Cup-Flip Mechanism State (More Niche)
Cups have one transparent side and one opaque side. Per SC3, a pin in the transparent side scores; a pin in the opaque side does not. Some teams will build a cup-flip mechanism — a small rotating cradle that flips the cup before placement to ensure transparent-side-up.
A Rotation Sensor on the flip mechanism's pivot tells your code "the cup is flipped" or "the cup is in original orientation"
Or a simpler approach: limit switches at the two end positions (see sensors-discrete § Switches) — cheaper, fewer Smart ports used
The Rotation Sensor wins if you want the flip to stop at any angle (e.g., for an in-progress cup orientation correction); the limit switch wins if you only need two states
For detecting cup orientation before flipping (i.e., reading the cup itself), use the Optical Sensor — see sensors-optical § Override Use Cases.
What the Rotation Sensor Doesn't Help With
Pin color identification. Use the Optical Sensor (RGB hue detection) instead.
Toggle state. The four field Toggles change yellow-pin scoring per quadrant. Per the v0.1 manual, Toggles are field elements with their own state — not something the Rotation Sensor reads. See override-toggle-strategy for how Toggles affect scoring.
Endgame collapse confirmation. Use a limit switch at the collapsed-arm position — cheaper, simpler, more reliable than reading angle for a binary "am I ≤18″?" check.
Distance to a goal. Use the Distance Sensor or AI Vision Sensor with AprilTags.
6 of 7
// Section 07
Rotation Sensor vs. Potentiometer V2 — Decision Matrix ⚖️
For arm and lift angle measurement, the Potentiometer V2 (276-7417) and the Rotation Sensor (276-7705) are both reasonable choices. The decision depends on rotation range, precision needs, port budget, and cost. Here's how to choose.
Side-by-Side Specs
Spec
Potentiometer V2 (276-7417)
Rotation Sensor (276-7705)
Connection
3-wire ADI (analog port)
Smart Cable (Smart port)
Sensing Range
~333° usable (single rotation only)
Infinite (multi-turn capable)
Resolution
~1° practical (analog noise)
0.01° (centidegrees, 36000/rev)
Reading Type
Absolute (knows angle on power-up)
Relative (needs zeroing each session)
Update Rate
~10 ms (ADI poll)
10 ms default, 5 ms minimum
Cost
$30 for 2-pack ($15 each)
~$50 each
PROS Class
pros::adi::Potentiometer
pros::Rotation
Mechanical Wear
Carbon track (V2 is improved over original; still has finite life)
Hall-effect magnetic (no contact, no wear)
Used By LemLib
No (not a tracking-wheel option)
Yes (first-class support)
The Decision, Plainly
⬢ Use a Potentiometer V2 when:
The mechanism rotates within a fixed range under 333° (most arms, most lifts, most cup-flip mechanisms)
You need absolute angle on power-up — e.g., you want to know the arm position at the start of a match without re-zeroing
Your Smart Port budget is tight (Pot V2 uses an ADI port, not a Smart port)
Cost matters — Pot V2 is half the price of the Rotation Sensor
You don't need sub-degree precision (1° resolution is plenty for arm presets at 6° / 14° / 24°)
🌀 Use a Rotation Sensor when:
You're building odom tracking wheels — this is the canonical use, no contest
The mechanism can rotate beyond 333° (rare for arms, but happens with chain bars or wrap-around mechanisms)
You need very precise angle (sub-degree) — e.g., for a fine catapult charge or a precision turret
You're using LemLib and want chassis tracking wheels
Long-term reliability matters (no mechanical wear) and you can afford the cost
For Override 2026-27, What We Recommend
Spartan's V1 Hero Bot baseline (per the kickoff brief) calls for IMU + Potentiometer for arm angle. After looking at this in detail, that recommendation holds:
The Override arm range is small. Ground to high goal is roughly 0°–90° on the arm pivot (or much less if geared down). Both sensors handle this; the Pot V2's 333° range is plenty.
1° precision is enough. Goal heights are physical targets; the arm can be off by a few degrees and still place a pin/cup correctly. Sub-degree precision doesn't buy you anything for this game.
The Pot V2 is half the cost. Across 6 teams (39 students), that adds up. $90 saved per robot is real budget money.
Smart port budget matters for Override. A typical Hero Bot uses 4 motor ports + IMU + potentially 2 odom Rotation Sensors + AI Vision Sensor + Distance Sensor + Optical Sensor = 10+ Smart ports. Putting the arm sensor on an ADI port leaves Smart ports free for vision and distance sensors that have no ADI alternative.
Where you should switch to a Rotation Sensor: if you decide to build odom tracking wheels for skills runs (recommended for Period 4 vet teams chasing high skills scores), use Rotation Sensors for those. Don't mix — if you're investing in Rotation Sensors anyway, it can be cleaner to put one on the arm too. But for V1, Pot V2 is the right call.
What About a Motor Encoder for Arm Angle?
The Smart Motor has a built-in encoder. Why not use that?
Backlash and slop. Gearboxes have play. The motor encoder reads the motor shaft, not the arm pivot — so you're reading "where I commanded the arm" plus backlash, not "where the arm actually is." For coarse positioning this is fine; for precise angle control it accumulates error.
No absolute reference on power-up. Like the Rotation Sensor, the motor encoder is relative; you need to zero it. Both sensors require a known reference position at startup.
For Override arm presets, motor encoder is sufficient. If you want zero extra hardware, you can run the arm purely off motor encoder readings. The advanced-robot.html worked example actually uses this approach. The Pot V2 (or Rotation Sensor) becomes a sanity check or upgrade path.
💡
Tier order for arm sensing: motor encoder (free, built-in) → Potentiometer V2 (cheap, absolute, simple) → Rotation Sensor (expensive, precise, multi-turn). Most V1 Override Hero Bots can stop at motor encoder + a limit switch at home position. Pot V2 is the next upgrade. Rotation Sensor is the upgrade after that, and is rarely worth it for arm control alone.
Inside the original VEX Potentiometer is a strip of carbon-coated plastic with a metal contact (called a wiper) that slides along it as the shaft turns. The wiper's position changes the electrical resistance, and that's how the sensor knows the angle. It's the same mechanism as a guitar volume knob.
The problem: the wiper rubs the carbon every time the shaft turns. After many cycles, the carbon wears down at the spots that get the most travel. The reading at those worn spots gets jittery or wrong. Like a record needle skipping on a scratched LP.
The V5 Rotation Sensor uses a completely different idea. A small magnet rides on the shaft. The sensor body has a chip that detects the magnetic field direction without touching anything. As the magnet rotates, the field rotates, and the chip reports the angle.
Nothing slides. Nothing wears. The reading stays the same after a million revolutions. That's why competitive teams trust the Rotation Sensor for tracking wheels — a tracking wheel might spin 500+ revolutions in a single skills run, and you can't afford the reading to degrade mid-route.