A servo can move to a commanded angle, but it can’t always tell you where it actually ended up — especially once a load, a stripped gear tooth, or a software bug enters the picture. Encoders close that gap: they measure real position and feed it back to your control loop. Here’s when you need one, the main types, and how to wire one up.
Do you actually need an encoder?
Standard RC hobby servos already have a closed position loop inside the servo — a potentiometer on the output shaft tells the servo’s internal circuit board where it is, and that circuit drives the motor until it matches your commanded PWM angle. For most servo-driven hobby arms, you never see this loop — you just send angles and trust the servo.
You need an external encoder when:
- You drive a DC motor or stepper directly and need your own PID control loop — the motor has no built-in feedback.
- You want to verify the arm’s real position rather than assume the commanded angle was reached (useful after a collision or stall).
- You’re building inverse kinematics that needs precise joint-angle measurement, not just commanded values.
- You’re training a machine learning policy that needs accurate joint-state observations.
Incremental vs absolute encoders
Incremental encoders output a stream of pulses as the shaft rotates — typically two channels (A/B, or CLK/DT) 90° out of phase, which lets the controller determine both count and direction. The position is relative: at power-up the controller doesn’t know the actual angle, only changes from wherever it started. A homing routine (driving to a known limit switch or index pulse) establishes the zero point.
Absolute encoders output a unique reading for every shaft position — optical (Gray-code disc) or magnetic (Hall-effect, like the AS5600) designs are both common. The controller knows the real angle immediately at power-on, with no homing needed. Absolute encoders cost more but are worth it for any joint where “what if the arm moved while powered off” matters.
| Type | Pros | Cons | Typical use |
|---|---|---|---|
| Incremental (KY-040 style) | Cheap, simple, high resolution | Needs homing, loses position on power loss | Hand-cranked input, simple position tracking |
| Magnetic absolute (AS5600) | Knows position instantly, no homing | More wiring (I2C), costs more | Joint feedback on a custom-driven arm |
| Potentiometer | Cheapest, dead simple | Limited range (~270°), wears out, lower resolution | Budget single-turn joint feedback |
Wiring an incremental encoder (KY-040) to Arduino
const int PIN_CLK = 2; // interrupt-capable
const int PIN_DT = 3;
volatile long position = 0;
void readEncoder() {
int dtState = digitalRead(PIN_DT);
if (dtState == HIGH) position++;
else position--;
}
void setup() {
pinMode(PIN_CLK, INPUT);
pinMode(PIN_DT, INPUT);
Serial.begin(115200);
attachInterrupt(digitalPinToInterrupt(PIN_CLK), readEncoder, FALLING);
}
void loop() {
Serial.println(position);
delay(50);
}
position accumulates pulses — convert to degrees by dividing by the encoder’s
pulses-per-revolution and multiplying by 360.
Wiring a potentiometer for absolute feedback
Simplest option for a single joint:
const int POT_PIN = A0;
float readAngleDegrees() {
int raw = analogRead(POT_PIN); // 0-1023
return map(raw, 0, 1023, 0, 270); // pot's mechanical range
}
Wire the pot’s outer legs to 5V and GND, the wiper (centre leg) to the analog pin. Mechanically couple the pot shaft to the joint axis — a small gear or direct coupling both work. This is exactly what’s inside every hobby servo, just exposed for your own control loop.
Magnetic absolute encoders (AS5600)
For a no-contact, wear-free absolute encoder, an AS5600 magnetic encoder reads the angle of a small diametric magnet mounted on the shaft, over I2C:
#include <Wire.h>
#define AS5600_ADDR 0x36
#define ANGLE_REG 0x0C
float readAS5600Angle() {
Wire.beginTransmission(AS5600_ADDR);
Wire.write(ANGLE_REG);
Wire.endTransmission(false);
Wire.requestFrom(AS5600_ADDR, 2);
int raw = (Wire.read() << 8) | Wire.read();
return (raw & 0x0FFF) * 360.0 / 4096.0;
}
No mechanical contact means no wear — a meaningful upgrade over a potentiometer for a joint that moves constantly.
Using encoder feedback in your control loop
Once you have a reliable angle reading, feed it into a
PID controller as the current value instead of
trusting an open-loop commanded position:
float current = readAS5600Angle();
float output = pid.compute(targetAngle, current, dt);
driveMotor(output);
This closes the loop properly — the motor keeps correcting until the measured position matches the target, not just until enough time has passed for an assumed move to finish.
Picking hardware
A 5-pack of rotary encoder modules is inexpensive enough to put one on every joint of a budget build, and the same module doubles as a manual jog controller — wire one to a spare Arduino input and turn it by hand to drive a joint, useful for teaching a machine learning policy by demonstration or just jogging the arm during setup.
Frequently asked questions
Does a robotic arm need an encoder?
Not always. Standard RC hobby servos already contain an internal potentiometer that closes the position loop — you send an angle and the servo gets there without any extra feedback wiring from you. An external encoder is only needed when you drive a motor directly (DC motor, stepper without microstep confidence) or want to verify the arm's actual position rather than trust the commanded one.
What is the difference between an incremental and absolute encoder?
An incremental encoder outputs pulses as the shaft turns — you count pulses to track relative position, but the count resets to zero at power-up so the controller doesn't know the actual angle until it homes. An absolute encoder outputs a unique code for every shaft position, so the controller knows the exact angle immediately at power-on, with no homing routine required.
Can I use a potentiometer instead of an encoder?
Yes, for a budget single-turn joint. A potentiometer wired across 0-5V gives an analog voltage proportional to angle — read it on an Arduino analog pin and you have absolute position feedback with no library or pulse counting needed. The trade-off is limited rotation range (usually 270° or less) and lower resolution and lifespan than a true encoder.
How do I wire a rotary encoder to Arduino?
A typical incremental encoder (like a KY-040 module) has CLK, DT and SW pins. Connect CLK and DT to two digital pins (ideally interrupt-capable), VCC to 5V, GND to ground. Read the CLK/DT phase relationship in an interrupt handler to determine direction and count pulses to track position.