Learn how to control an SG90 servo motor using Arduino with 0°–180° precision. This advanced guide explains PWM theory, servo internals, wiring, timing diagrams, control techniques, and working Arduino codes with diagrams.
1. Introduction
Servo motors are among the most commonly used electro-mechanical actuators in robotics, RC vehicles, automation, and embedded systems. The SG90 micro servo is lightweight, inexpensive, and capable of rotating approximately 0°–180° with good accuracy.
Controlling a servo with an Arduino seems simple, but understanding what happens internally—and how the PWM control signal actually works—lets you build more stable, precise, and responsive systems.
In this article, we go beyond the typical beginner-level wiring and code. You’ll learn:
- How the SG90 servo works internally
- True PWM theory for servo control
- Why Arduino’s Servo library doesn’t use standard PWM
- Timing diagrams and pulse-width explanation
- Control strategies for smooth and stable movement
- Wiring diagram (included)
- Basic and advanced Arduino codes
2. How an SG90 Servo Motor Works Internally
The SG90 is a closed-loop position control actuator built with:
✔ DC Motor
Small brushed DC motor providing rotation.
✔ Gearbox
Plastic gear train reduces speed and increases torque.
✔ Rotary Potentiometer
Connected to output shaft; provides real-time angular feedback.
✔ Control PCB
Contains:
- Error amplifier
- PID-like control loop
- H-bridge driver for motor direction
- Pulse-width decoder
✔ Output Shaft (0°–180° range)
Mechanically limited; cannot rotate 360°.


3. How Servos Understand PWM (Pulse-Width Modulation)
Important:
Servos do not use frequency modulation; they use pulse width modulation at a fixed frequency.
✔ Signal Specs (SG90):
- Frequency: 50 Hz (period = 20 ms)
- Pulse width:
- 1.0 ms → 0°
- 1.5 ms → 90°
- 2.0 ms → 180°
📊 Pulse Timing Table
| Position | Pulse Width |
|---|---|
| 0° | ~1.0 ms |
| 45° | ~1.25 ms |
| 90° | ~1.5 ms |
| 135° | ~1.75 ms |
| 180° | ~2.0 ms |

✔ Arduino PWM vs Servo PWM: Key Difference
The Arduino’s built-in PWM pins output 490/980 Hz, which is not suitable for servo control.
That is why the Servo.h library uses internal timers (Timer1) to generate the correct 50 Hz servo pulses.
4. Wiring Diagram (SG90 to Arduino UNO)
Connections
| SG90 Servo | Arduino UNO |
|---|---|
| Brown (GND) | GND |
| Red (VCC) | 5V |
| Orange (Signal) | D9 |
⚠️ Use separate power supply for multiple servos.



5. Basic Arduino Code (0°–180° Sweep)
#include <Servo.h>
Servo myservo;
void setup() {
myservo.attach(9); // Signal connected to pin D9
}
void loop() {
for (int pos = 0; pos <= 180; pos++) {
myservo.write(pos);
delay(15);
}
for (int pos = 180; pos >= 0; pos--) {
myservo.write(pos);
delay(15);
}
}

6. Advanced PWM Timing Code (No Servo Library)
This code manually generates the 1ms–2ms pulses.
int servoPin = 9;
void setup() {
pinMode(servoPin, OUTPUT);
}
void writeServo(int angle) {
int pulseWidth = map(angle, 0, 180, 1000, 2000); // microseconds
digitalWrite(servoPin, HIGH);
delayMicroseconds(pulseWidth);
digitalWrite(servoPin, LOW);
delay(20 - pulseWidth/1000); // complete 20ms frame
}
void loop() {
for (int pos=0; pos<=180; pos++) {
writeServo(pos);
}
for (int pos=180; pos>=0; pos--) {
writeServo(pos);
}
}
This manually implements servo PWM timing. Useful when:
- You need custom timing
- You avoid conflicts with Servo library timers
- You want deeper control
7. Smoother Motion (Interpolation Control)
For robotics/arms, jumping directly between angles causes jitter.
Use eased motion:
void smoothMove(int startPos, int endPos, int speedDelay) {
if (startPos < endPos) {
for (int p = startPos; p <= endPos; p++) {
myservo.write(p);
delay(speedDelay);
}
} else {
for (int p = startPos; p >= endPos; p--) {
myservo.write(p);
delay(speedDelay);
}
}
}
8. Common SG90 Servo Issues & Fixes
1. Jitter / Vibration
Cause:
- Weak power supply
- USB-only powering
Fix:
- Use 5V 1A supply
- Add 100–470 µF capacitor across VCC–GND
2. Servo moves but doesn’t reach 180°
Cause:
- Mechanical limits vary
- Pulse width range differs slightly
Fix:
myservo.writeMicroseconds(500); // try min
myservo.writeMicroseconds(2500); // try max
3. Servo gets hot
Cause:
- Stall position / overloaded
Fix: - Reduce load
- Avoid keeping servo commanded at extreme ends