iotdata, by Data Frontieriot
data
Block coding

SG90 mini servo: how PWM controls the angle

How the SG90 mini servo turns the width of a pulse into an angle, why the signal repeats 50 times per second, how to calibrate the end points and what the Arduino Servo library and MicroPython do under the hood.

Data Frontier Engineering· Published October 22, 2026

The SG90 mini servo is the most common joint in workbench robot arms. Inside, it has a small motor, a gearbox, a potentiometer attached to the output shaft and a control circuit. The circuit compares the requested position with the position read from the potentiometer and turns the motor until the two match.

The three wires

  • Brown: GND.
  • Red: power, around 5 V.
  • Orange: command signal.

The signal wire powers nothing: it only tells the servo where to go. That is why the red wire can, and with four servos should, come from a separate supply, as long as its GND is shared with the board’s.

The pulse that becomes an angle

The command is a pulse repeated 50 times per second, that is, one every 20 ms. What matters is not the frequency but how long the pulse stays high:

Pulse widthPosition (datasheet reference)
about 1 msone end
1.5 mscenter (90 degrees)
about 2 msthe other end

This is PWM, pulse width modulation. In practice, many SG90s only reach the full 180 degrees with pulses between about 0.5 ms and 2.4 ms, and each unit varies a little. That is why calibrating pays off.

Calibrating the end points

  1. Send the servo to 90 degrees and mount the joint’s arm aligned in that position.
  2. Lower the angle step by step until the joint reaches its mechanical limit or the servo starts to buzz. Note the last angle without buzzing.
  3. Do the same on the upper side.

The buzzing is the servo pushing against something that will not give. In that situation it draws its maximum current and heats up. A servo that buzzes while standing still is a sign of a badly set limit.

The SG90 numbers

According to the manufacturer’s datasheet, at 4.8 V: torque of about 1.8 kgf·cm and a speed of 0.1 s to turn 60 degrees. In practical terms: it easily holds a light gripper and an object of a few grams at the tip of a short arm. A long arm or a heavy object calls for a bigger servo, such as the MG90S, with metal gears.

On the Arduino Nano

The Servo library, which comes with the Arduino IDE, generates the pulses using a timer interrupt. That is why the servo works on any digital pin, not just the ones marked PWM. The write command takes the angle, and the library converts it into pulses that range, by default, from 544 µs to 2,400 µs.

An important side effect: on the Nano, using the Servo library disables PWM (analogWrite) on pins 9 and 10, even if no servo is connected to them.

On the ESP32 with MicroPython

The ESP32 has hardware PWM on almost all of its output pins. In MicroPython:

from machine import Pin, PWM

servo = PWM(Pin(13), freq=50)
servo.duty_ns(1_500_000)   # 1.5 ms: center

The block coding IDE does this math for you: the card "move servo to 120 degrees" becomes the matching pulse, within the pulse limits you set for that joint.

What to look at in the IDE

In the block coding IDE, each servo card asks for the pin and the angle, and the code panel shows the Arduino write or the MicroPython duty it generated. To power the arm’s four servos, see the article on the robot arm with four mini servos.