An SG90 needs a 50 Hz pulse between roughly 0.5 and 2.4 ms. On the ESP32 the ESP32Servo library produces that from the LEDC peripheral, so the sketch looks just like an Arduino Uno servo sketch.
The wiring is three wires, but power is the real lesson: the servo’s current spikes are the commonest cause of ESP32 resets, so give it its own 5 V supply as soon as it carries any load.
Wiring
| ESP32 DevKit V1 | SG90 micro servo (9 g) | Wire | Notes |
|---|---|---|---|
| D18 | SIG | orange | A 3.3 V signal is enough for the SG90. |
| VIN | VCC | red | One unloaded SG90 on a good USB port only. For real loads use a separate 5 V 1 A supply and join its GND to the ESP32 GND. |
| GND | GND | brown |
4.8–6 V (several hundred mA while moving or stalled) part, 3.3 V board. The signal input is happy with 3.3 V logic, so ESP32 and Pico drive it directly. On ESP32 use the ESP32Servo library — the standard Servo library is for AVR and RP2040.
Board quirks
- The core
Servolibrary is AVR-only; on ESP32 use ESP32Servo, which drives the pulse from an LEDC PWM channel. - A stalled servo can pull several hundred mA. Powered from VIN that drops the board’s supply and causes brownout resets.
- Any output-capable pin works; GPIO18 is free at boot and not a strapping pin.
Code
// SG90 micro servo on an ESP32 DevKit V1 with ESP32Servo (uses an LEDC channel).
// Signal (orange) → D18, red → 5 V supply, brown → GND (shared with the ESP32).
#include <ESP32Servo.h>
constexpr uint8_t SERVO_PIN = 18;
Servo servo;
void setup() {
Serial.begin(115200);
servo.setPeriodHertz(50); // standard 50 Hz servo frame
servo.attach(SERVO_PIN, 500, 2400); // SG90 pulse range in µs
Serial.println("Sweeping 0° ↔ 180°");
}
void loop() {
for (int angle = 0; angle <= 180; angle += 30) {
servo.write(angle);
Serial.printf("Angle %d°\n", angle);
delay(500);
}
for (int angle = 180; angle >= 0; angle -= 30) {
servo.write(angle);
Serial.printf("Angle %d°\n", angle);
delay(500);
}
}// SG90 micro servo on an ESP32 DevKit V1 with ESP32Servo (uses an LEDC channel).
// Signal (orange) → D18, red → 5 V supply, brown → GND (shared with the ESP32).
#include <ESP32Servo.h>
constexpr uint8_t SERVO_PIN = 18;
Servo servo;
void setup() {
Serial.begin(115200);
servo.setPeriodHertz(50); // standard 50 Hz servo frame
servo.attach(SERVO_PIN, 500, 2400); // SG90 pulse range in µs
Serial.println("Sweeping 0° ↔ 180°");
}
void loop() {
for (int angle = 0; angle <= 180; angle += 30) {
servo.write(angle);
Serial.printf("Angle %d°\n", angle);
delay(500);
}
for (int angle = 180; angle >= 0; angle -= 30) {
servo.write(angle);
Serial.printf("Angle %d°\n", angle);
delay(500);
}
}Library: Servo (Arduino) — compiled with ESP32Servo 3.2.1.
Expected serial output
Sweeping 0° ↔ 180°
Angle 0°
Angle 30°
Angle 60°
Angle 90°Sweeping 0° ↔ 180° Angle 0° Angle 30° Angle 60° Angle 90°
Common errors
Servo jitters or twitches
Usually supply noise: give the servo its own 5 V supply and a 470 µF capacitor across it, with a common ground.
ESP32 resets when the servo moves
Brownout from servo current on the USB supply. Power the servo separately.