An SG90 makes a NodeMCU move things: a door latch, a fish feeder, a pointer on a dashboard. The signal is a 3.3 V pulse from D5; the power must come from VIN or, better, its own 5 V supply.
Unlike the ESP32, the ESP8266 needs no extra library — the Servo that comes with the core works. The catch is its default pulse width, which is why the sketch sets 500–2400 µs explicitly.
Wiring
| NodeMCU ESP8266 | SG90 micro servo (9 g) | Wire | Notes |
|---|---|---|---|
| D5 | SIG | orange | D5 is GPIO14. Orange wire on most SG90s. A 3.3 V signal is enough. |
| VIN | VCC | red | Red wire. One unloaded SG90 on a good USB port only; for anything more use a separate 5 V 1 A supply with its GND joined to the NodeMCU GND. |
| GND | GND | brown | Brown wire. |
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 ESP8266 core ships its own
Servolibrary — don’t install ESP32Servo or the AVR one. Its default pulse range is 1000–2000 µs, which turns an SG90 only about 90°; the sketch callsattach(D5, 500, 2400)for the full sweep. - Never power the servo from 3V3. The NodeMCU’s regulator feeds the ESP8266, and a moving servo pulls hundreds of mA — the 3.3 V rail sags and the board resets or the Wi-Fi drops.
- D0 (GPIO16) can’t generate servo pulses. Avoid D3, D4 and D8 too: the servo’s input can upset the boot levels, and D4 toggles at start-up, which makes the arm twitch every reset.
- The pulses are made by the core’s software waveform generator, not a hardware timer. Heavy Wi-Fi traffic can add a little jitter; a separate PCA9685 board fixes that for robot arms.
Code
// SG90 micro servo on a NodeMCU (ESP8266), using the Servo library that ships with the core.
// SIG → D5 (GPIO14), VCC → VIN (5 V from USB, one unloaded SG90 only), GND → GND.
#include <Servo.h>
const uint8_t SERVO_PIN = D5; // GPIO14 — PWM-capable, no boot role (D0 can't do servo pulses)
Servo servo;
void setup() {
Serial.begin(115200);
Serial.println();
// The ESP8266 Servo library defaults to 1000–2000 µs, which only moves an SG90 about 90°.
// 500–2400 µs gives the full 0–180°. Narrow it if your servo buzzes at the ends.
servo.attach(SERVO_PIN, 500, 2400);
Serial.println("Sweeping 0 <-> 180 deg on D5");
}
void loop() {
for (int a = 0; a <= 180; a += 30) {
servo.write(a);
Serial.printf("Angle %d deg\n", a);
delay(600);
}
for (int a = 150; a >= 30; a -= 30) {
servo.write(a);
Serial.printf("Angle %d deg\n", a);
delay(600);
}
}// SG90 micro servo on a NodeMCU (ESP8266), using the Servo library that ships with the core.
// SIG → D5 (GPIO14), VCC → VIN (5 V from USB, one unloaded SG90 only), GND → GND.
#include <Servo.h>
const uint8_t SERVO_PIN = D5; // GPIO14 — PWM-capable, no boot role (D0 can't do servo pulses)
Servo servo;
void setup() {
Serial.begin(115200);
Serial.println();
// The ESP8266 Servo library defaults to 1000–2000 µs, which only moves an SG90 about 90°.
// 500–2400 µs gives the full 0–180°. Narrow it if your servo buzzes at the ends.
servo.attach(SERVO_PIN, 500, 2400);
Serial.println("Sweeping 0 <-> 180 deg on D5");
}
void loop() {
for (int a = 0; a <= 180; a += 30) {
servo.write(a);
Serial.printf("Angle %d deg\n", a);
delay(600);
}
for (int a = 150; a >= 30; a -= 30) {
servo.write(a);
Serial.printf("Angle %d deg\n", a);
delay(600);
}
}Library: Servo (Arduino).
Expected serial output
Sweeping 0 <-> 180 deg on D5
Angle 0 deg
Angle 30 deg
Angle 60 deg
Angle 90 degSweeping 0 <-> 180 deg on D5 Angle 0 deg Angle 30 deg Angle 60 deg Angle 90 deg
Common errors
Servo only turns about half way
You used servo.attach(pin) with the default 1000–2000 µs. Use attach(pin, 500, 2400).
Board resets when the servo moves
The USB supply sags under servo current. Give the servo its own 5 V supply and a 470 µF capacitor across it, with GND shared.
Servo buzzes or jitters at rest
Supply noise or pushing past its end stops. Narrow the range (for example 600–2300 µs). Full fix.