Servo end-point calibration rig with two buttons and EEPROM on Arduino: design worksheet and code

A ₹1,010 design worksheet for a small rig that finds the real pulse widths for 0° and 180° on your own servo, using buttons and a protractor, then stores them in EEPROM for any project to reuse.

About the author · Sources, testing and AI policy

  • Parts₹1,010
  • Bench time2–3 h
  • LevelBeginner
  • ReviewedNov 2026

A ₹1,010 rig that finds out what your servo really does. Two buttons nudge the pulse width up and down, you read the angle off a protractor, and the sketch saves the pulse widths for 0°, 90° and 180° in EEPROM. The values then go straight into any robot arm or gripper sketch, so the angles on screen match the angles in the world.

Scope: beginner. This is a design worksheet with illustrative costs. It has not been bench-tested by this site. The table was computed by script from the common assumption of 500 to 2,500 µs for 0 to 180°, which is a loose convention: many hobby servos use about 1,000 to 2,000 µs for a smaller range, and cheap ones vary from unit to unit. That variation is exactly why the rig exists. Do not drive a servo past its mechanical stop; stop pushing when it buzzes.

How it works

A hobby servo reads a pulse every 20 ms and turns to match the pulse width. If 500 to 2,500 µs covered 0 to 180°, each microsecond would be 0.09° of travel, and 1,500 µs would be the 90° centre. Real servos often reach their end stops before 500 or 2,500 µs, or need more than that, so a command that is too far makes the servo hum against the stop.

The rig steps the pulse by 10 µs per press, with a long press repeating. You step to the physical 0° mark, press a save button, repeat for 90° and 180°, and the three pulse widths are stored. Later sketches read them and use a straight line between them, with an optional correction for the 90° point if the servo is not linear.

Bill of materials

PartQtyUnitTotal
Arduino Uno R3 (or compatible)1₹550₹550
Micro servo (SG90) for testing1₹120₹120
Push buttons1₹10₹10
Protractor or printed angle scale1₹20₹20
External 5 V supply for the servo (optional)1₹200₹200
Breadboard1₹60₹60
Jumper wires1₹50₹50
Parts total₹1,010

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

Pulse widthAngle if 500 to 2,500 µs = 0 to 180°
500 µs0°
1000 µs45°
1500 µs90°
2000 µs135°
2500 µs180°
Angle = (pulse − 500) / 2000 × 180. A convention, not a property of your servo.

If your measured 0° and 180° pulses are a and b microseconds, a later sketch maps an angle θ to a pulse with pulse = a + (b − a) × θ / 180. For example if 0° needs 600 µs and 180° needs 2,350 µs, 90° is 1,475 µs, not 1,500. This takes one line of code and removes the error.

Power and wiring

PartConnects toNote
Servo signalD9
Servo power5V from a separate supply if it resets the UnoCommon GND
Up buttonD2 to GNDInternal pull-up
Down buttonD3 to GNDInternal pull-up
Save buttonD4 to GNDInternal pull-up
Hold a protractor against the servo with its centre on the horn pivot and read the angle where the horn points.

The sketch

// Servo end-point calibration rig: Uno + servo + 3 buttons + EEPROM
// Design worksheet: find the pulse widths for 0, 90 and 180 degrees.

#include <Servo.h>
#include <EEPROM.h>

const int SERVO_PIN = 9, UP = 2, DOWN = 3, SAVE = 4;
const int STEP_US = 10;
const int MIN_US = 400, MAX_US = 2600;      // hard limits for safety
const int ADDR = 0;                          // three ints stored from here

Servo s;
int pulse = 1500;
int stage = 0;                               // 0: 0 deg, 1: 90, 2: 180
int saved[3] = {500, 1500, 2500};

void setup() {
  pinMode(UP, INPUT_PULLUP); pinMode(DOWN, INPUT_PULLUP); pinMode(SAVE, INPUT_PULLUP);
  Serial.begin(9600);
  s.attach(SERVO_PIN);
  s.writeMicroseconds(pulse);
  Serial.println("Step to the 0 degree mark, then press SAVE");
}

void loop() {
  bool moved = false;
  if (digitalRead(UP) == LOW)   { pulse = min(pulse + STEP_US, MAX_US); moved = true; }
  if (digitalRead(DOWN) == LOW) { pulse = max(pulse - STEP_US, MIN_US); moved = true; }
  if (moved) { s.writeMicroseconds(pulse); Serial.println(pulse); delay(120); }

  if (digitalRead(SAVE) == LOW) {
    delay(30);
    if (digitalRead(SAVE) == LOW) {
      saved[stage] = pulse;
      Serial.print("Saved stage "); Serial.print(stage); Serial.print(": "); Serial.println(pulse);
      stage++;
      if (stage == 3) {
        for (int i = 0; i < 3; i++) EEPROM.put(ADDR + i * sizeof(int), saved[i]);
        Serial.println("All saved to EEPROM");
        stage = 0;
      } else {
        Serial.println(stage == 1 ? "Now the 90 degree mark" : "Now the 180 degree mark");
      }
      while (digitalRead(SAVE) == LOW) {}
    }
  }
}

To use the stored values in another sketch, read three ints with EEPROM.get from the same addresses and map the angle to a pulse with the formula above. EEPROM has a limited number of writes (commonly listed at about 100,000 per cell), which is plenty here because it is written only when you finish a calibration. The sketch has been proofread but not compiled on this site.

Test plan

  1. Centre check: see where 1,500 µs actually points.
  2. Zero mark: step down until the horn lines up with 0° and note the pulse.
  3. End stops: step toward each end and stop the moment you hear the servo strain.
  4. Repeat: calibrate twice and compare the pulses.
  5. Second servo: repeat with a different servo and compare.
  6. Readback: reboot and print the stored values from EEPROM.

What usually goes wrong

SymptomLikely causeFix
Servo hums at the endPulse beyond its mechanical stopBack off 20 to 30 µs and save that
Jumps when attachedStarting position differsWrite the pulse before attaching
Different each timeBacklash in the gearsApproach each mark from the same side
Values lostSaved before all three stagesComplete all three presses
Arduino resets on moveServo draws too much from USBUse an external 5 V supply

Upgrades

  • Per-joint table: store a calibration for each servo of a robot arm.
  • Serial menu: select the servo number and save over Serial.
  • Limits: use the end values as software limits.
  • Feedback servo: use a servo with a position output to automate this.

Cost and parts notes

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

Common questions

Why is 90° not always 1,500 µs?

Real servos differ from the 1,500 µs convention, sometimes by tens of microseconds.

Does this work for continuous-rotation servos?

No. Those turn at a speed set by the pulse, not to an angle.

Why a hard limit of 400 and 2,600 µs?

To stop a held button from driving the servo far past its stop.

How many times can EEPROM be written?

Datasheets commonly list about 100,000 writes per cell; this rig writes rarely.

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.