Zbotic Logo Zbotic Logo
  • Home
  • Shop
  • Sale
  • 3D Print Service
  • PCB Service
  • B2B
  • Blogs
  • Contact Us
0 0

View Wishlist Add all to cart

0 0
0 Shopping Cart
Shopping cart (0)
Subtotal: ₹0.00

View cartCheckout

  • Shop
  • About Us
  • Contact Us
  • Reseller
  • Blogs
020 69134444
1800 209 0998
[email protected]
Help Desk
Facebook Twitter Instagram Linkedin YouTube
Zbotic Logo Zbotic Logo
0 0

View Wishlist Add all to cart

0 0
0 Shopping Cart
Shopping cart (0)
Subtotal: ₹0.00

View cartCheckout

All departments
  • 3D Print Service
  • 3D Printer
  • Batteries & Chargers
  • Development Boards
  • Drone Parts
  • EBike parts
  • Sensor Modules
  • Electronic Components
  • Electronic Modules
  • IoT and Wireless
  • Mechanical Parts and Workbench Tools
  • Motors & Drivers & Pumps & Actuators
  • DIY and Robot Kits
  • Show more
  • Home
  • Shop
  • Sale
  • 3D Print Service
  • PCB Service
  • B2B
  • Blogs
  • Contact Us
Return to previous page
Home Motors & Actuators

ESP32 Servo Control: Multiple Servos & Smooth PWM Guide

ESP32 Servo Control: Multiple Servos & Smooth PWM Guide

March 11, 2026 /Posted byJayesh Jain / 1

Table of Contents

  • Why Use ESP32 for Servo Control?
  • Servo Motor Basics: PWM and Angles
  • ESP32 LEDC PWM API Explained
  • Wiring a Single Servo to ESP32
  • Code: Single Servo Control
  • Controlling Multiple Servos Simultaneously
  • Smooth Motion: Interpolation and Easing
  • Wi-Fi Web Interface for Remote Servo Control
  • Power Supply and Current Considerations
  • SG90 vs MG90 vs MG996: Choosing the Right Servo
  • Recommended Products
  • Frequently Asked Questions
  • Conclusion

Why Use ESP32 for Servo Control?

The ESP32 microcontroller has become the preferred platform for servo control projects that go beyond simple position commands. Here is why makers and engineers choose ESP32 over Arduino Uno for servo applications:

  • 16 independent hardware PWM channels (LEDC): The ESP32’s LED Control (LEDC) peripheral generates up to 16 hardware-accurate PWM signals simultaneously. Unlike Arduino’s timer-based approach that shares timers between pins, each ESP32 LEDC channel runs independently, enabling truly simultaneous multi-servo control with precise timing.
  • Higher PWM resolution: Up to 16-bit resolution (65,535 steps) vs. Arduino’s 8-bit (255 steps). This translates to much finer angular positioning — approximately 0.003° per step vs. Arduino’s 0.7° per step.
  • Wi-Fi and Bluetooth built-in: Control servos remotely via web browser, smartphone app, or MQTT without any additional hardware modules.
  • Dual-core processing: Run servo motion on one core and Wi-Fi/Bluetooth on the other, eliminating latency conflicts.
  • 3.3V logic: Note — standard servo signal lines work with 3.3 V logic (the PWM signal), though servo power requires 5 V (or higher for MG996-class servos).

Servo Motor Basics: PWM and Angles

A standard hobby servo expects a PWM signal with these parameters:

  • Frequency: 50 Hz (one pulse every 20 ms)
  • Pulse width at 0°: ~500–600 µs
  • Pulse width at 90°: ~1,500 µs (centre position)
  • Pulse width at 180°: ~2,400–2,500 µs

The servo’s internal circuit compares the received pulse width to its current shaft position (measured by a potentiometer) and drives a small DC motor to reduce the error to zero. The result is a closed-loop position control system in a compact package.

Important: Pulse width ranges vary between servo brands and models. The SG90, for example, has a usable range of approximately 500–2,400 µs. Sending pulses outside this range will drive the servo into its mechanical stop, causing buzzing and heat buildup. Always test with conservative ranges (600–2,400 µs) first and expand if needed.

For continuous rotation servos (modified for robotics drive wheels), the 1,500 µs pulse is the stop position, below 1,500 µs rotates one direction, and above 1,500 µs rotates the other direction.

ESP32 LEDC PWM API Explained

The ESP32 Arduino Core provides the LEDC (LED Control) API for PWM generation. For servo control, you work with three key functions:

  • ledcSetup(channel, frequency, resolution_bits) — Configure a LEDC channel
  • ledcAttachPin(pin, channel) — Attach a GPIO pin to a LEDC channel
  • ledcWrite(channel, duty) — Set the duty cycle (0 to 2^resolution – 1)

For servo control at 50 Hz with 16-bit resolution:

  • Period = 1/50 Hz = 20 ms = 20,000 µs
  • Total steps at 16-bit = 65,535
  • Each µs = 65,535 / 20,000 = 3.28 steps
  • To set 1,500 µs: duty = 1500 × 3.28 = 4,915

This gives you positioning resolution of approximately 1/3.28 µs = 0.3 µs per step — far finer than any hobby servo can physically resolve, but useful when combining multiple servo motions programmatically.

Note for ESP32 Arduino Core v3.x users: The API changed significantly. In v3.x, use ledcAttach(pin, frequency, resolution) and ledcWrite(pin, duty) instead of the separate setup/attach functions. Check your ESP32 core version with ESP.getSdkVersion().

Wiring a Single Servo to ESP32

Hobby servos have a 3-wire connector:

  • Brown/Black wire: GND
  • Red wire: Power (5 V for SG90, 6 V for MG996)
  • Orange/Yellow/White wire: Signal (PWM input)

Wiring to ESP32 DevKit:

Servo Wire Connect To Notes
Brown/Black (GND) ESP32 GND + Power supply GND Common ground is critical
Red (Power) External 5V supply (NOT ESP32 5V pin) Servos draw up to 1A — don’t power from ESP32
Orange/Yellow (Signal) ESP32 GPIO (e.g., GPIO 13) Any GPIO except 6, 7, 8, 9, 10, 11 (flash)

Critical wiring note: Never power multiple servos from the ESP32’s onboard 5 V pin or 3.3 V regulator. Even one SG90 can draw 250 mA stall current, which will exceed the ESP32 devboard’s onboard regulator limit and crash the microcontroller. Use a dedicated 5 V supply (phone charger, buck converter, or USB power bank) with its GND connected to ESP32 GND.

SG90 Servo Motor

Servo SG90 9g 180 Degree

The classic 9g micro servo — ultra-light, 180° range, and compatible with any ESP32 GPIO. Perfect for robotics, pan-tilt mechanisms, and automation prototypes.

View on Zbotic

Code: Single Servo Control

Complete ESP32 sketch for a single servo with degree-based positioning:

// ESP32 Servo Control using LEDC
// Compatible with ESP32 Arduino Core v2.x

#define SERVO_PIN  13
#define LEDC_CHAN  0
#define LEDC_FREQ  50       // 50 Hz for servo
#define LEDC_RES   16       // 16-bit resolution

// Pulse width in microseconds
#define SERVO_MIN_US  500
#define SERVO_MAX_US  2400

void setup() {
  ledcSetup(LEDC_CHAN, LEDC_FREQ, LEDC_RES);
  ledcAttachPin(SERVO_PIN, LEDC_CHAN);
  Serial.begin(115200);
  Serial.println("ESP32 Servo Control");
  setServoDeg(90); // Centre position on startup
  delay(1000);
}

// Convert microseconds to LEDC duty
uint32_t usToDuty(int us) {
  return (uint32_t)(us * 65535.0 / 20000.0);
}

// Set servo position by angle (0-180 degrees)
void setServoDeg(int deg) {
  deg = constrain(deg, 0, 180);
  int us = map(deg, 0, 180, SERVO_MIN_US, SERVO_MAX_US);
  ledcWrite(LEDC_CHAN, usToDuty(us));
}

void loop() {
  // Sweep 0 to 180 and back
  for (int pos = 0; pos = 0; pos -= 5) {
    setServoDeg(pos);
    delay(50);
  }
  delay(500);
}

Controlling Multiple Servos Simultaneously

ESP32 can control up to 16 servos simultaneously using 16 independent LEDC channels. Here is a clean object-oriented approach:

// Multi-servo control on ESP32
// Supports up to 16 servos

struct Servo {
  int pin;
  int channel;
  int minUs;
  int maxUs;
  int currentDeg;
};

// Define your servos
Servo servos[] = {
  {13, 0, 500, 2400, 90},  // Servo 0: GPIO13, Channel 0
  {12, 1, 500, 2400, 90},  // Servo 1: GPIO12, Channel 1
  {14, 2, 600, 2300, 90},  // Servo 2: GPIO14, Channel 2
  {27, 3, 500, 2400, 90},  // Servo 3: GPIO27, Channel 3
};

const int NUM_SERVOS = 4;

void initServos() {
  for (int i = 0; i < NUM_SERVOS; i++) {
    ledcSetup(servos[i].channel, 50, 16);
    ledcAttachPin(servos[i].pin, servos[i].channel);
    setServoDeg(i, 90); // All to centre
  }
}

uint32_t usToDuty(int us) {
  return (uint32_t)(us * 65535.0 / 20000.0);
}

void setServoDeg(int idx, int deg) {
  deg = constrain(deg, 0, 180);
  servos[idx].currentDeg = deg;
  int us = map(deg, 0, 180, servos[idx].minUs, servos[idx].maxUs);
  ledcWrite(servos[idx].channel, usToDuty(us));
}

void setup() {
  Serial.begin(115200);
  initServos();
  delay(1000);
}

void loop() {
  // Wave: each servo offsets by 45 degrees in phase
  unsigned long t = millis();
  for (int i = 0; i < NUM_SERVOS; i++) {
    float phase = (float)i * 45.0;
    int deg = 90 + (int)(80.0 * sin((t / 1000.0) * 2 * PI + phase * PI / 180.0));
    setServoDeg(i, deg);
  }
  delay(20); // 50 Hz update rate
}

This code makes four servos perform a smooth wave motion with each servo 45° out of phase — useful for animatronic tentacles, robotic fingers, and hexapod leg segments.

TowerPro SG90 Servo

TowerPro SG90 180 Degree Rotation Servo Motor

Industry-standard TowerPro SG90 — precise, durable, and widely tested with ESP32. Ideal for 4-servo wave arrays, pan-tilt systems, and robotic grippers.

View on Zbotic

Smooth Motion: Interpolation and Easing

Jumping a servo directly from 0° to 180° causes mechanical shock, noise, and stress on the gears. Smooth motion requires interpolation — moving the servo through intermediate positions over time.

Linear Interpolation (Lerp)

void sweepTo(int idx, int targetDeg, int stepDelayMs) {
  int startDeg = servos[idx].currentDeg;
  int steps = abs(targetDeg - startDeg);
  for (int i = 0; i  startDeg ? i : -i);
    setServoDeg(idx, deg);
    delay(stepDelayMs);
  }
}

Ease-In-Out (Sinusoidal)

void easeServeTo(int idx, int targetDeg, int durationMs) {
  int startDeg = servos[idx].currentDeg;
  int steps = durationMs / 20; // 50 Hz update
  for (int i = 0; i <= steps; i++) {
    // Sinusoidal ease-in-out
    float t = (float)i / steps;
    float ease = 0.5 - 0.5 * cos(t * PI);
    int deg = startDeg + (int)((targetDeg - startDeg) * ease);
    setServoDeg(idx, deg);
    delay(20);
  }
}

The sinusoidal ease creates a natural deceleration at the target position, eliminating the bounce and stress that linear sweeps create. Call it like: easeServeTo(0, 45, 800); to move servo 0 to 45° over 800 ms with smooth acceleration and deceleration.

Wi-Fi Web Interface for Remote Servo Control

One of ESP32’s killer features for servo projects is built-in Wi-Fi. Here’s a minimal web server that lets you control servo angle from any browser on the same network:

#include <WiFi.h>
#include <WebServer.h>

const char* ssid = "YourWiFi";
const char* password = "YourPassword";

WebServer server(80);

void handleRoot() {
  String html = "<html><body>";
  html += "<h2>ESP32 Servo Control</h2>";
  html += "<input type='range' min='0' max='180' value='90' ";
  html += "oninput="fetch('/set?deg=' + this.value)" style='width:300px'>";
  html += "</body></html>";
  server.send(200, "text/html", html);
}

void handleSet() {
  if (server.hasArg("deg")) {
    int deg = server.arg("deg").toInt();
    setServoDeg(0, deg);
    server.send(200, "text/plain", "OK");
  }
}

void setup() {
  // ... servo init ...
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) delay(500);
  Serial.println(WiFi.localIP());
  server.on("/", handleRoot);
  server.on("/set", handleSet);
  server.begin();
}

void loop() {
  server.handleClient();
}

Open the ESP32’s IP address in a browser to see a slider that controls the servo in real time from anywhere on your Wi-Fi network.

Servo MG996 13KG

Servo MG996 13KG 180 Degree High Quality

Heavy-duty metal-geared servo with 13 kg-cm torque — the go-to choice for ESP32 robotic arms, steering mechanisms, and high-load multi-servo systems.

View on Zbotic

Power Supply and Current Considerations

Power is the most commonly overlooked aspect of multi-servo projects:

  • SG90 at stall: ~700 mA per servo
  • MG996 at stall: ~2,500 mA per servo
  • Rule of thumb: For N servos, your supply should deliver at least N × stall_current × 0.5 (practical average)

For 4 SG90 servos: 4 × 700 mA × 0.5 = 1.4 A minimum supply. Use a 3 A supply for headroom.

Use a dedicated 5V regulated supply (not the ESP32’s USB power) and connect the supply GND directly to ESP32 GND. Add 100–470 µF electrolytic capacitors across the servo power rail to absorb current transients when servos start moving.

SG90 vs MG90 vs MG996: Choosing the Right Servo

Servo Torque Weight Gears Best For
SG90 1.8 kg-cm 9 g Plastic Light payloads, rapid prototyping
MG90S 2.2 kg-cm 13 g Metal Higher loads, longer life
MG996 13 kg-cm 55 g Metal Robotic arms, heavy steering

Recommended Products

Build your ESP32 multi-servo project with these components from Zbotic:

Servo MG996 normal quality

Servo MG996 13KG 180 Degree

Metal-geared 13 kg-cm servo at a budget price — ideal for ESP32 robotic arm builds and steering mechanisms where SG90 torque isn’t sufficient.

View on Zbotic

Servo Mount Holder Bracket

Servo Mount Holder Bracket for SG90/MG90 (Pack of 2)

Plastic mounting brackets for SG90 and MG90 servos — makes it easy to mechanically mount servos at precise angles in your multi-servo ESP32 frame.

View on Zbotic

Aluminum Servo Horn 25T

Aluminum Servo Horn/Arm 25T Round Type Disc MG995 MG996

Metal servo horn disc for MG996 servos — much stronger than plastic horns for load-bearing robotic arm joints driven by your ESP32 system.

View on Zbotic

Frequently Asked Questions

Can I use the Arduino Servo library on ESP32?

Yes. The ESP32 Arduino Core includes a Servo.h library that wraps the LEDC peripheral. However, it only supports up to 12 servos per core (limited by available LEDC channels after other peripherals). The direct LEDC API shown in this guide gives you more control and is preferred for professional projects.

Why does my ESP32 reset when servos start moving?

The ESP32 is drawing too much power. Servo startup current spikes cause the 3.3 V or 5 V rail to sag below the ESP32’s minimum operating voltage, triggering a brownout reset. Add a 470 µF–1,000 µF capacitor across the servo 5 V power rail and ensure your power supply is rated for adequate current.

How many servos can I control with one ESP32?

The LEDC hardware supports 16 channels (8 high-speed + 8 low-speed timers), so technically 16 servos. Practically, power limits the number: at 5 V with a 5 A supply, you can drive approximately 7 SG90 servos at moderate load. For more servos, use a PCA9685 16-channel I2C servo driver board which offloads PWM generation to dedicated hardware and supports virtually unlimited cascading.

What GPIO pins are safe to use for servo signals on ESP32?

Avoid GPIO 6–11 (used for internal flash). GPIO 34, 35, 36, 39 are input-only. Best servo signal pins: GPIO 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 27. Note that GPIO 12 affects boot mode if pulled high — safer to use GPIO 13, 14, or higher for the first servo to avoid boot issues.

Does the servo jitter when ESP32 is doing Wi-Fi operations?

With the direct LEDC API, servo jitter during Wi-Fi is minimal because LEDC generates PWM in hardware independently of the CPU. Unlike software PWM (used by some libraries), hardware LEDC continues generating precise pulses even when the CPU is handling Wi-Fi interrupts. For sensitive applications, assign servo updates to Core 0 and Wi-Fi to Core 1 using FreeRTOS tasks.

Conclusion

ESP32 is an exceptional platform for multi-servo projects thanks to its 16 hardware PWM channels, high PWM resolution, and built-in Wi-Fi for remote control. The LEDC API gives you direct, precise control that surpasses Arduino’s timer-based approach, and the dual-core architecture eliminates conflicts between servo motion and wireless communication.

Start with a single SG90 and the basic sketch, then expand to multiple servos with the struct-based multi-servo code. Add smooth easing interpolation for professional-quality motion, and integrate the Wi-Fi web server for remote control via any browser.

Find the right servos, mounting hardware, and extension cables for your ESP32 project in Zbotic’s motors and actuators collection.

Tags: ESP32 servo, LEDC PWM servo, multiple servo ESP32, servo motor control, SG90 ESP32
Share Post
  • Facebook
  • Linkedin
  • Whatsapp
Smart Gate Opener with DC Moto...
blog smart gate opener with dc motor ir remote arduino project guide 596920
blog piezoelectric buzzer active vs passive and how to drive them 596923
Piezoelectric Buzzer: Active v...

Related posts

Svg%3E
Read more

Gear Motor Guide: N20, JGB37, and Planetary Motors Compared

April 1, 2026 0
When your project needs more torque than a bare DC motor can provide, a gear motor is the answer. By... Continue reading
Svg%3E
Read more

Miniature Pump Hydroponics: Automated Nutrient Dosing System

April 1, 2026 0
Hydroponics grows plants in nutrient-rich water instead of soil, and automating the nutrient dosing process with peristaltic pumps and Arduino... Continue reading
Svg%3E
Read more

Drone Motor Testing: Thrust Stand Build and KV Measurement

April 1, 2026 0
If you are building a drone, selecting the right motor-propeller combination is critical for flight performance. A drone motor thrust... Continue reading
Svg%3E
Read more

Pump Selection Guide: Peristaltic, Submersible, and Diaphragm

April 1, 2026 0
When your Arduino project needs to move liquid — whether for automated plant watering, hydroponics, aquarium management, or a coffee... Continue reading
Svg%3E
Read more

Solenoid Guide: Door Locks, Valves, and Automation Projects

April 1, 2026 0
A solenoid is an electromechanical device that converts electrical energy into linear motion. When you energise the coil, a plunger... Continue reading

One comment

  • Avatar of David De la Haye

    David De la Haye

    March 21, 2026 - 6:03 pm

    This is a very useful and complete explanation, thank you.

    However, are you aware that this may be impacted by recent migration?
    https://docs.espressif.com/projects/arduino-esp32/en/latest/migration_guides/2.x_to_3.0.html

    Reply

Add comment Cancel reply

Your email address will not be published. Required fields are marked

Facebook Twitter Instagram Pinterest Linkedin Youtube

Get the latest deals and more.

Download on Google Play Download on the App Store

Call us: 020 69134444 / 1800 209 0998

Monday - Saturday 09:30 AM - 06:00 PM
For Technical Supports Email: [email protected]
For Sales / Enquiries Email: [email protected]

  • My Account

    • Cart

    • Wishlist

    • Checkout

    • My Orders

    • Track Order

    • My Account

  • Information

    • FAQs

    • Blogs

    • Career

    • About Us

    • Contact Us

    • Payment Options

  • Policies

    • Privacy Policy

    • Terms & Conditions

    • GST Input Tax Credit

    • Shipping Return Policy

    • E-Waste Collection Points

    • Our Sitemap

© Zbotic.in is registered trademark of Moxie Supply Pvt Ltd – All Rights Reserved
Login
Use Phone Number
Use Email Address
Not a member yet? Register Now
Reset Password
Use Phone Number
Use Email Address
Register
Already a member? Login Now