TheSolderBenchnotes from my electronics bench

Log #29 · Sep 27, 2026

Adding Wheel Encoders to a Robot

How optical and magnetic encoders count wheel rotation, how to read them with interrupts, and how to measure distance and drive straight.

Robotics · 4 min read · Intermediate

Two identical-looking motors never spin at exactly the same speed, so a robot told to drive straight slowly curves. Timed turns are just as unreliable: a 350 ms spin gives 90° on a fresh battery and 70° on a tired one. Wheel encoders fix both problems by measuring how far each wheel actually turned. This guide explains how they work, how to read them with interrupts and how to use the counts to measure distance and drive straight.

What an encoder measures

An encoder produces a pulse each time the wheel or motor shaft turns a small, fixed amount. Count the pulses and you know the rotation; count them over time and you know the speed.

The number of pulses per revolution is called resolution, often written as PPR (pulses per revolution) or CPR (counts per revolution).

Common encoder types

Optical slot encoders

Many 2WD kits include a slotted disc that mounts on the motor shaft and an optical interrupter module (often based on a sensor like the H206 or FC-03). An infrared beam shines through the slots; each time a solid section blocks it, the output changes.

  • A typical kit disc has 20 slots, so 20 pulses per wheel revolution.
  • They are cheap and easy, but they only give speed, not direction.
A slotted encoder disc passing through an optical sensor, producing a square wave of pulses as the wheel turns
Each slot passing through the infrared beam produces one pulse.

Magnetic quadrature encoders

Gear motors such as the N20 or JGA25 are available with a small magnet disc and two Hall-effect sensors on the back of the motor. They produce two signals, A and B, shifted by a quarter cycle. Which one changes first tells you the direction. Because they sit on the motor shaft before the gearbox, the counts per wheel revolution are high: a 7-pulse motor encoder with a 1:30 gearbox gives 210 pulses per wheel revolution, or more when you count every edge.

Reading pulses with interrupts

You could check the encoder pin in loop(), but if the loop is busy printing or waiting for a sensor, you miss pulses. The reliable way is an interrupt: the hardware pauses your program the instant the pin changes, runs a tiny function, then returns.

On an Uno or Nano, pins 2 and 3 support external interrupts, which is perfect for one encoder per wheel.

volatile long leftCount = 0;
volatile long rightCount = 0;

void leftPulse()  { leftCount++; }
void rightPulse() { rightCount++; }

void setup() {
  Serial.begin(115200);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(2), leftPulse,  RISING);
  attachInterrupt(digitalPinToInterrupt(3), rightPulse, RISING);
}

void loop() {
  noInterrupts();                 // copy safely while counts may change
  long l = leftCount, r = rightCount;
  interrupts();
  Serial.print("L:"); Serial.print(l);
  Serial.print(" R:"); Serial.println(r);
  delay(200);
}

Three rules for interrupt code:

  • Mark shared variables volatile, so the compiler always reads the latest value.
  • Keep the interrupt function tiny. No printing, no delays.
  • Copy multi-byte counters with interrupts briefly disabled, as above, so you never read a half-updated number.

Use the Serial Monitor to spin each wheel by hand and confirm the counts rise.

From counts to distance

For a wheel of diameter D, one revolution moves the robot π × D.

distance per pulse = π × D ÷ pulses per revolution

With 65 mm wheels and a 20-slot disc:

π × 65 ÷ 20 ≈ 10.2 mm per pulse

So to drive 1 meter, run the motors until the count reaches about 1000 ÷ 10.2 ≈ 98 pulses. Resolution matters here: with 20 pulses per revolution, you can only measure distance in 10 mm steps.

Accurate turns

For a differential-drive robot spinning on the spot, each wheel travels along a circle whose diameter is the track width W, the distance between the wheels. For a 90° turn each wheel moves a quarter of that circle:

arc = π × W ÷ 4

With W = 130 mm, that is about 102 mm, or 10 pulses per wheel with our disc. Replace the timed turn in the obstacle-avoiding robot with "spin until both wheels reach 10 pulses", and turns stay consistent as the battery drains.

Driving straight

To keep the robot straight, compare the two counts and slow down the wheel that is ahead:

const int BASE = 160;
const float K = 4.0;

void loop() {
  noInterrupts();
  long diff = leftCount - rightCount;   // positive: left is ahead
  interrupts();
  int correction = K * diff;
  drive(BASE - correction, BASE + correction);
  delay(20);
}

This is a proportional controller. Add derivative and integral terms and you have a full PID loop, which can also hold a target speed on each wheel regardless of battery level or load.

Troubleshooting

  • Counts jump in bursts or double count: the signal is noisy or bouncing. Use the module's digital output, keep wires short, or accept only pulses that come at least a millisecond apart.
  • One wheel never counts: check that the disc actually passes through the sensor slot, and that the pin is 2 or 3.
  • Counts go up when reversing: single-channel optical encoders cannot sense direction. Use the motor direction you commanded, or switch to quadrature encoders.
  • Slip: encoders count wheel rotation, not motion over the ground. On smooth floors, fast starts make wheels slip, so accelerate gently.

— TheSolderBench · found a mistake? tell me

More pages from this notebook

How a Line-Following Robot Works

The sensors, logic and code behind a line follower, from a simple two-sensor bang-bang robot to smoother proportional steering.

→ wobbling? lower the gain

PID Control Explained Without Heavy Math

What the P, I and D terms actually do, shown with a robot that follows a line, plus Arduino code and a step-by-step tuning method.

→ tune P first, then D, I last

Choosing a Chassis for Your First Robot

Two-wheel, four-wheel, tracked or DIY? Compare robot chassis types by steering, terrain and difficulty, and learn how to check motor and battery fit.

→ pick the battery before the chassis

Using the MPU6050 Accelerometer and Gyroscope

Read tilt and rotation from the MPU6050, convert raw values to g and degrees per second, and combine them with a complementary filter.

→ gyro drifts, accel is noisy, blend both