Drive exactly 1 metre with a wheel encoder and a stop-and-turn routine on Arduino: design worksheet and code

A ₹1,640 design worksheet for driving a 2WD robot a set distance and turning a set angle using slot-type wheel encoders, with ticks-per-millimetre maths, rounding error and a square-path test routine.

About the author · Sources, testing and AI policy

  • Parts₹1,640
  • Bench time6–9 h
  • LevelIntermediate
  • ReviewedNov 2026

A ₹1,640 robot that drives a measured distance and turns a measured angle, so it can trace a 500 mm square. Slot-type encoders count wheel pulses, and the sketch turns ticks into millimetres and degrees. The test at the end measures how far the robot misses the start point, which is the honest measure of dead reckoning.

Scope: intermediate. This is a design worksheet with illustrative costs. It has not been bench-tested by this site. The tick table was computed by script for 65 mm wheels, 20-slot encoder discs and a 130 mm wheel track. Real wheels slip, tyres squash and the effective track is not exactly what you measured, so the numbers give a starting point that you correct by measurement. This is the same dead-reckoning idea as the odometry worksheet, applied to fixed moves.

How it works

One tick moves the wheel π × 65 / 20 = 10.21 mm. A 1,000 mm drive is therefore 97.9 ticks, which rounds to 98, giving 1,000.5 mm, so the rounding error is under a millimetre here. The coarse resolution matters more for short moves: 100 mm is 9.8 ticks, which rounds to 10 and gives 102.1 mm.

Turning on the spot, each wheel travels an arc of π × 130 × (angle / 360). A full 360° spin needs 40.0 ticks per wheel, so 90° needs 10.0 ticks per wheel, one wheel forward and the other back. While driving straight, the sketch counts ticks per wheel on interrupts and slows the leading wheel to keep the two counts equal.

Bill of materials

PartQtyUnitTotal
Arduino Uno R3 (or compatible)1₹550₹550
2WD chassis kit with gear motors and 65 mm wheels1₹450₹450
L298N motor driver board1₹180₹180
Slot-type speed sensor module (LM393) with encoder disc2₹90₹180
Battery holder with two 18650 cells1₹210₹210
Switch1₹20₹20
Jumper wires1₹50₹50
Parts total₹1,640

Scroll the table sideways to see every column.

Prices checked Nov 2026. Shipping and tools are not included.

Prices are illustrative figures for Indian sellers in November 2026, before shipping, and have not been checked against current listings.

The numbers

DistanceTicks (10.21 mm each)Distance after rounding to whole ticks
100 mm9.8 ticks102.1 mm
500 mm49.0 ticks500.3 mm
1000 mm97.9 ticks1000.6 mm
Ticks = distance / (π × 65 / 20). Whole-tick rounding limits resolution to about ±5 mm.

A 500 mm square side is 49.0 ticks, and each 90° turn is 10 ticks per wheel. If the effective track is 3 percent bigger than you measured, each turn is about 3 percent short, about 2.7°, which adds up to roughly 11° of error after four turns. Calibrate the turn first, by measuring one 360° spin.

Power and wiring

PartConnects toNote
Left encoder OUTD2Interrupt 0
Right encoder OUTD3Interrupt 1
Encoder VCC and GND5V and GNDCheck the module’s range
L298N ENA / IN1 / IN2D5 / D7 / D8Left motor
L298N ENB / IN3 / IN4D6 / D9 / D4Right motor
D2 and D3 are the Uno’s two hardware interrupt pins, so they are kept for the encoders.

The sketch

// Drive a distance and turn an angle: Uno + L298N + two slot encoders
// Design worksheet: calibrate MM_PER_TICK and TRACK_MM by measurement.

const int L_EN = 5, L_IN1 = 7, L_IN2 = 8;
const int R_EN = 6, R_IN3 = 9, R_IN4 = 4;
void drive(int left, int right) {   // -255..255 per side
  digitalWrite(L_IN1, left >= 0);  digitalWrite(L_IN2, left < 0);
  digitalWrite(R_IN3, right >= 0); digitalWrite(R_IN4, right < 0);
  analogWrite(L_EN, constrain(abs(left), 0, 255));
  analogWrite(R_EN, constrain(abs(right), 0, 255));
}
void motorsInit() {
  pinMode(L_EN, OUTPUT); pinMode(L_IN1, OUTPUT); pinMode(L_IN2, OUTPUT);
  pinMode(R_EN, OUTPUT); pinMode(R_IN3, OUTPUT); pinMode(R_IN4, OUTPUT);
}

const int ENC_L = 2, ENC_R = 3;
const float MM_PER_TICK = 10.21;            // pi * 65 / 20
const float TRACK_MM = 130.0;
volatile long ticksL = 0, ticksR = 0;

void isrL() { ticksL++; }
void isrR() { ticksR++; }

void stopMotors() { drive(0, 0); delay(300); }

void driveMm(float mm, int speed) {
  long target = (long)(mm / MM_PER_TICK + 0.5);
  noInterrupts(); ticksL = 0; ticksR = 0; interrupts();
  while (true) {
    noInterrupts(); long l = ticksL, r = ticksR; interrupts();
    if ((l + r) / 2 >= target) break;
    int corr = constrain((int)(l - r) * 8, -40, 40);   // slow the leading wheel
    drive(speed - corr, speed + corr);
  }
  stopMotors();
}

void turnDeg(float deg, int speed) {         // positive = clockwise
  long target = (long)(PI * TRACK_MM * fabs(deg) / 360.0 / MM_PER_TICK + 0.5);
  noInterrupts(); ticksL = 0; ticksR = 0; interrupts();
  int s = deg > 0 ? 1 : -1;
  while (true) {
    noInterrupts(); long l = ticksL, r = ticksR; interrupts();
    if ((l + r) / 2 >= target) break;
    drive(s * speed, -s * speed);
  }
  stopMotors();
}

void setup() {
  motorsInit();
  pinMode(ENC_L, INPUT_PULLUP); pinMode(ENC_R, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(ENC_L), isrL, RISING);
  attachInterrupt(digitalPinToInterrupt(ENC_R), isrR, RISING);
  delay(3000);
  for (int i = 0; i < 4; i++) {              // 500 mm square
    driveMm(500, 150);
    turnDeg(90, 130);
  }
}

void loop() {}

The turn uses the same speed on both sides, so the two wheel counts may differ slightly; averaging them is a simple fix, not a precise one. Motors coast after power is cut, so the real stopping point is a tick or two beyond the target; lowering speed near the end, or subtracting a measured overshoot from the target, improves accuracy. The sketch has been proofread but not compiled on this site.

Test plan

  1. Encoder check: spin each wheel by hand for one turn and print the count; it should be 20 ticks per turn.
  2. Straight line: drive 1 m three times and measure the miss.
  3. Spin test: command a 360° turn, mark the start heading, and adjust TRACK_MM until it comes back to the mark.
  4. Square test: run the 500 mm square and measure the gap between start and finish.
  5. Surface test: repeat on carpet and a hard floor.
  6. Speed test: repeat at a lower speed and compare overshoot.

What usually goes wrong

SymptomLikely causeFix
Counts double or erraticContact bounce or noisy sensorAdjust the module threshold and debounce in software
Drifts to one sideWheels differKeep the tick correction loop and check motor wiring
Turns shortEffective track wider than measuredRaise or lower TRACK_MM from the spin test
OvershootsCoasting after power offSlow down near the target
Square does not closeAccumulated small errorsCalibrate the turn first; expect some gap

Upgrades

  • Speed ramp: ease in and out of each move.
  • Gyro heading: add an MPU6050 for turns.
  • Path list: store a list of moves and replay it.
  • Odometry: combine with the odometry worksheet to track the pose.

Cost and parts notes

The ₹1,640 total is an illustration and does not include shipping. Use the cost calculator to compare sellers.

Common questions

How far is one encoder tick?

For a 65 mm wheel and 20 slots, about 10.2 mm.

Why does the square not close?

Small turn and distance errors add up; encoders reduce this but cannot remove it.

Why interrupts for the encoders?

They catch every pulse even while the loop does other work.

What is dead reckoning?

Working out position from measured movement alone, without an outside reference.

Last reviewed: November 2026. This is a design worksheet with illustrative costs. It has not been bench-tested by this site; library names, pins and limits must be checked against your board and the current library versions, and measurements from your build are welcome.

More builds

Next builds to try

Free: the final-year project pack

A shortlist with BOMs, a report outline that matches the usual chapter scheme, a costing sheet and 40 viva questions. Built for the February–April submission season.

The Build Note

One short email when something is worth your bench time.

A new build sheet, a part price that moved, or a mistake worth avoiding. No daily drip, no recycled news.

One short email when a build or a price is worth your time. Unsubscribe in one click.