SG90 Mini Servo
The SG90 is a compact micro servo motor ideal for robotics and DIY projects. Operating on 4.8V to 6V, it delivers 1.8 kg·cm torque, with a 0° to 180° range controlled via PWM signals. Weighing 9g, its nylon gears and dimensions (22.8mm x 12.2mm x 28.5mm) make it perfect for lightweight, precise applications.

On this page
SG90 pinout
The SG90 is a 3-wire servo motor with GND (brown), +5V (red), and DATA (orange) wires.
| Pin | Type | Description | Notes |
|---|---|---|---|
| GND | Power | Ground connection. Connect to ESP32 GND or external power supply ground. | Completes the electrical circuit. |
| +5V | Power | Power supply input (4.8V-6V). Can be powered from ESP32 5V pin for light loads. | Use external power supply for multiple servos. |
| DATA | PWM | PWM control signal. Pulse width: 1ms (0°), 1.5ms (90°), 2ms (180°). | Connect to a PWM-capable GPIO pin (e.g., GPIO 18). |
Operating voltage: 4.8V-6V
Torque: 1.8 kg·cm at 4.8V
Angular range: 0° to 180°
Weight: 9g
Nylon gears (less durable than metal gears)
Speed: 0.1 sec/60° at 4.8V
Wiring the SG90 to ESP32
To control the SG90 servo with an ESP32, connect the brown wire to GND, red wire to 5V power, and orange wire to a PWM GPIO pin.
| SG90 pin | ESP32 pin | Purpose |
|---|---|---|
| GND (brown) | GND | Ground connection for both power and signal reference. |
| +5V (red) | 5V or External Supply | Power supply (4.8V-6V). Use external supply for multiple servos. |
| DATA (orange) | GPIO 18 | PWM control signal (50Hz, 1-2ms pulse width). |
PWM frequency: 50Hz (standard servo)
Current draw: ~100-400mA depending on load
For single servo: ESP32 5V pin can power it
For multiple servos: use external 5V power supply with shared GND
Add 100µF capacitor across power supply to reduce noise
Nylon gears: suitable for light loads only
For heavier loads: upgrade to MG90S (metal gears)
Use ESP32Servo library or ledc functions for control
SG90 code examples
SG90 Arduino example
Copy// Requires library: "ESP32Servo"
#include <ESP32Servo.h> // The classic Arduino Servo library does not support the ESP32
Servo myServo; // Create a Servo object
void setup() {
myServo.attach(18); // Servo signal on GPIO18, matches the wiring above
}
void loop() {
myServo.write(0); // Move the servo to 0 degrees
delay(1000);
myServo.write(90); // Move the servo to 90 degrees
delay(1000);
myServo.write(180); // Move the servo to 180 degrees
delay(1000);
}On the ESP32 the classic Arduino Servo library does not work - install ESP32Servo instead, which provides the same familiar attach()/write() API on top of the ESP32's LEDC PWM hardware. The servo's signal wire connects to GPIO18 as shown above, and the sketch sweeps between 0, 90 and 180 degrees. Power the servo from 5V (or an external supply for stronger servos) - not from the 3.3V pin. The SG90 is a small 9 g micro servo - it runs happily from a 5V USB supply for light loads.
SG90 ESP-IDF example
Copy#include "driver/ledc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_err.h"
#define SERVO_PIN GPIO_NUM_18 // GPIO pin for the servo signal
#define SERVO_MIN_PULSEWIDTH 500 // Minimum pulse width in microseconds (0°)
#define SERVO_MAX_PULSEWIDTH 2500 // Maximum pulse width in microseconds (180°)
#define SERVO_MAX_DEGREE 180 // Maximum angle in degrees
// Convert an angle to an LEDC duty value: pulse width in us -> timer ticks
// (16-bit resolution at 50 Hz means the 20000 us period spans 65535 ticks)
uint32_t calculate_duty(uint32_t angle) {
uint32_t pulse_us = SERVO_MIN_PULSEWIDTH + ((SERVO_MAX_PULSEWIDTH - SERVO_MIN_PULSEWIDTH) * angle) / SERVO_MAX_DEGREE;
return (uint32_t)((uint64_t)pulse_us * 65535 / 20000);
}
void app_main() {
// Configure the LEDC timer
ledc_timer_config_t ledc_timer = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.timer_num = LEDC_TIMER_0,
.duty_resolution = LEDC_TIMER_16_BIT,
.freq_hz = 50, // Frequency for servos
.clk_cfg = LEDC_AUTO_CLK
};
ledc_timer_config(&ledc_timer);
// Configure the LEDC channel
ledc_channel_config_t ledc_channel = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.channel = LEDC_CHANNEL_0,
.timer_sel = LEDC_TIMER_0,
.intr_type = LEDC_INTR_DISABLE,
.gpio_num = SERVO_PIN,
.duty = 0, // Initial duty cycle
.hpoint = 0
};
ledc_channel_config(&ledc_channel);
while (1) {
// Move servo to 0°
uint32_t duty = calculate_duty(0);
ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, duty);
ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);
vTaskDelay(pdMS_TO_TICKS(1000));
// Move servo to 90°
duty = calculate_duty(90);
ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, duty);
ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);
vTaskDelay(pdMS_TO_TICKS(1000));
// Move servo to 180°
duty = calculate_duty(180);
ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, duty);
ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}This code controls a servo using ESP-IDF's LEDC PWM driver. The calculate_pulse_width function computes the pulse width for a given angle (0° to 180°). The LEDC timer is set to 50 Hz, and the GPIO pin (e.g., GPIO_NUM_18) is configured as the output for the PWM signal. The servo's position is adjusted by updating the PWM duty cycle in the loop. The SG90 is a small 9 g micro servo - it runs happily from a 5V USB supply for light loads.
SG90 ESPHome example
Copyoutput:
- platform: ledc
id: pwm_output
pin: GPIO18 # servo signal, matches the wiring above
frequency: 50 Hz
servo:
- id: my_servo
output: pwm_output
number:
- platform: template
name: "SG90 Position"
min_value: -100
max_value: 100
step: 1
optimistic: true
set_action:
- servo.write:
id: my_servo
level: !lambda 'return x / 100.0;'On the ESP32 the PWM output platform is ledc (the esp8266_pwm platform seen in older examples is ESP8266-only). The servo consumes the 50 Hz LEDC output on GPIO18, and the template number entity maps -100..100 to the servo range so you can slide it from Home Assistant; servo.write takes -1.0..1.0. The SG90 is a small 9 g micro servo - it runs happily from a 5V USB supply for light loads.
SG90 PlatformIO example
Copy[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
lib_deps =
madhephaestus/ESP32Servo @ ^3.0.5#include <Arduino.h>
#include <ESP32Servo.h> // The classic Arduino Servo library does not support the ESP32
Servo myServo; // Create a Servo object
void setup() {
myServo.attach(18); // Servo signal on GPIO18, matches the wiring above
}
void loop() {
myServo.write(0); // Move the servo to 0 degrees
delay(1000);
myServo.write(90); // Move the servo to 90 degrees
delay(1000);
myServo.write(180); // Move the servo to 180 degrees
delay(1000);
}This code demonstrates how to control a servo in PlatformIO using the Arduino framework. The servo is connected to GPIO 18, and its position is controlled using PWM signals generated by the myServo.attach() and myServo.write() methods. No additional libraries are needed as the Arduino Servo library is built into the framework. The setup() function initializes the servo, while the loop() moves it between 0°, 90°, and 180° with delays. The SG90 is a small 9 g micro servo - it runs happily from a 5V USB supply for light loads.
SG90 MicroPython example
Copyfrom machine import Pin, PWM
from time import sleep
# Configure PWM on GPIO18
servo = PWM(Pin(18))
servo.freq(50) # Set frequency to 50 Hz
# Function to move the servo to a specific angle (0° to 180°)
def set_servo_angle(angle):
# Convert angle to duty cycle (pulse width in microseconds)
duty = int(40 + (angle / 180) * 115) # Duty cycle range: 40-155 (approx. 500-2500 μs)
servo.duty(duty)
# Main loop
while True:
set_servo_angle(0) # Move to 0°
sleep(1) # Wait 1 second
set_servo_angle(90) # Move to 90°
sleep(1) # Wait 1 second
set_servo_angle(180) # Move to 180°
sleep(1) # Wait 1 secondThis MicroPython code controls a servo motor using PWM on GPIO 18. The PWM object sets a 50 Hz frequency for the servo. The function set_servo_angle(angle) converts an angle (0° to 180°) into a duty cycle to position the servo. In the loop, the servo moves between 0°, 90°, and 180° with a 1-second delay between movements. The SG90 is a small 9 g micro servo - it runs happily from a 5V USB supply for light loads.
SG90 specifications
About the SG90
The SG90 is TowerPro’s small analog micro servo, the one most ESP32 tutorials reach for first: 9g, plastic (nylon) gears, and a rated stall torque of 1.8 kg-cm at 4.8V, running anywhere from 4.8V to 6V. It takes the standard 50Hz servo pulse, with the datasheet’s usable pulse-width window running roughly 500-2400us across the full swing and the common 1000-2000us band covering 0-180 degrees - the same signal every other servo on this site expects, so the wiring and code carry over directly.
The genuine TowerPro part is a known quantity, but the SG90 name is also one of the most cloned in the hobby servo market - a lot of what ships under that label from budget listings is an unbranded clone with looser tolerances, and quality varies a lot below a certain price point. There is no fully reliable way to tell a clone from a resistor and a multimeter, but the practical upshot is the same either way: treat the 1.8 kg-cm torque figure as a ceiling, not a guarantee, especially under load or after months of use.
Power is the other place people get caught out: even a single SG90’s stall current can spike past 200 mA, and pulling that from the ESP32 board’s own 5V regulator alongside the MCU is a common way to trigger a brownout reset mid-motion. A dedicated 5V supply with a shared ground avoids it. For more torque in the same footprint, the metal-gear MG90S is a drop-in upgrade; for driving several servos at once without burning through GPIOs, see the PCA9685 PWM controller and the general PWM servo control notes.
SG90 troubleshooting
Servo Not Responding or Moving Erratically
›
Issue: The SG90 servo does not move as expected or exhibits erratic behavior.
Possible causes include insufficient power supply, incorrect wiring, or improper PWM signal configuration.
Solution: Ensure the servo is powered by an adequate external power source, as the Arduino's 5V pin may not supply sufficient current. Verify that the control signal is connected to the correct PWM-capable pin on the microcontroller. Confirm that the PWM signal parameters match the servo's specifications, typically a 50Hz frequency with pulse widths between 1ms and 2ms corresponding to 0° to 180° positions.
Continuous Rotation Instead of Positional Movement
›
Issue: The SG90 servo rotates continuously instead of moving to a specified position.
Possible causes include the use of a continuous rotation servo variant or incorrect pulse width parameters.
Solution: Determine whether the servo is a standard positional servo or a continuous rotation model. For standard servos, ensure that the control pulses correspond to the correct positional commands. If using the Servo.attach() function in Arduino, specify appropriate minimum and maximum pulse widths to match the servo's requirements.
Servo Jittering or Twitching
›
Issue: The SG90 servo jitters or twitches when holding a position.
Possible causes include electrical noise, unstable power supply, or interference from other components.
Solution: Use a stable and adequately rated external power supply for the servo. Implement proper grounding and consider adding decoupling capacitors to filter out electrical noise. Ensure that the control signal is clean and free from interference, and avoid running servo wires parallel to high-power lines to minimize electromagnetic interference.
Servo Overheating
›
Issue: The SG90 servo becomes excessively hot during operation.
Possible causes include overloading the servo, continuous operation under high torque, or mechanical binding.
Solution: Check for any mechanical obstructions or excessive loads that may cause the servo to work harder than intended. Ensure that the servo is operating within its specified torque range and duty cycle. If the application requires continuous rotation under load, consider using a servo designed for such purposes or a geared motor with appropriate specifications.
Where to buy the SG90

Resources
Similar sensors





