Home > Industry Insights >BLDC
TECHNICAL SUPPORT

Product Support

How to Control a Servo Motor with ESP32 Using Python: A Complete Guide

Published 2026-04-05

This guide provides a step‑by‑step method to control a standardservomotor with an ESP32 board using Python. Whether you are building a robotic arm, a pan‑tilt camera mount, or a simple moving prop, you will learn the exact wiring, the Python code, and the calibration steps to make yourservomove precisely. All examples are based on common, real‑world situations so you can immediately apply them to your own projects.

01What You Need

One ESP32 development board (any common variant)

One standard 5Vservomotor (e.g., SG90 or MG995 type)

External 5V power supply (if using a high‑torque servo)

Jumper wires (female‑to‑female)

A computer with MicroPython firmware installed on the ESP32

> Important: Never power a servo directly from the ESP32’s 3.3V pin. Most servos require 5V and can draw more current than the board can safely provide. Use a separate 5V power source for the servo, and connect only the signal wire to the ESP32.

02Hardware Connection – A Real Case

Imagine you are building a simple robotic claw that opens and closes. You have a common MG995 servo. Connect it as follows:

Servo Wire Color (typical) Connect to
Power Red External 5V positive terminal
Ground Brown or Black External 5V groundandESP32 GND
Signal Orange or Yellow ESP32 GPIO pin 15 (any PWM pin)

Critical point: The ground of the external power supplymustbe connected to the ESP32 GND. Without this common ground, the signal will be unstable and the servo will jitter or not move.

03Python Code – Step by Step

The ESP32 controls a servo by generating a 50 Hz PWM signal (period = 20 ms). The servo’s position is determined by the pulse width: 0.5 ms for 0°,1.5 ms for 90°, and 2.5 ms for 180°.

Below is a complete, tested MicroPython script. Save it asmain.pyon your ESP32.

from machine import Pin, PWM import time # Use GPIO15 – you can change to any pin that supports PWM SERVO_PIN = 15 # Standard servo frequency: 50 Hz PWM_FREQ = 50 # Duty cycle values for 0°, 90°, 180° (calculated for 50 Hz) # For a 16‑bit PWM resolution (0–65535): # Duty = (pulse_width_ms / 20ms)65535 # 0.5 ms -> 1638 (0°) # 1.5 ms -> 4915 (90°) # 2.5 ms -> 8192 (180°) DUTY_0 = 1638 DUTY_90 = 4915 DUTY_180 = 8192 # Initialize PWM on the servo pin servo = PWM(Pin(SERVO_PIN), freq=PWM_FREQ, duty_u16=DUTY_90) # start at 90° def set_angle(angle): """ Set servo angle (0° to 180°). Linearly maps angle to duty cycle. """ # Clamp angle to valid range angle = max(0, min(180, angle)) # Linear mapping: duty = DUTY_0 + (angle/180)(DUTY_180 - DUTY_0) duty = int(DUTY_0 + (angle / 180) * (DUTY_180 - DUTY_0)) servo.duty_u16(duty) # Example: sweep back and forth while True: for angle in range(0, 181, 10): set_angle(angle) time.sleep_ms(50) for angle in range(180, -1, -10): set_angle(angle) time.sleep_ms(50)

04How to Use the Code

1. Flash MicroPython firmware to your ESP32 (if not already done).

2. Connect the servo as described.

3. Copy the script to the board using a tool likempremoteor Thonny.

4. Run the script. The servo will sweep from 0° to 180° and back repeatedly.

05Calibration – Why Your Servo May Need Different Values

Different servo models have slightly different pulse width ranges. For example, a cheap SG90 might work with 0.5 ms to 2.4 ms, while a high‑torque MG996R uses 0.6 ms to 2.4 ms. The common 0.5–2.5 ms range works for most, but you should always calibrate.

Calibration method(a real‑world case from a robotic arm project):

Set the duty cycle to 1638 and note the actual angle.

If the servo does not reach 0°, increase the duty slightly (e.g., 1700) until it stops moving.

Do the same for 180° by decreasing from 8192.

Use your measured DUTY_MIN and DUTY_MAX in theset_angle()function.

06Common Problems and Solutions

Problem Most Likely Cause Fix
Servo does not move at all Missing common ground between ESP32 and servo power Connect GND of external supply to ESP32 GND
Servo jitters or twitches PWM frequency is not 50 Hz; or unstable power Setfreq=50; use a separate 5V supply with adequate current
Servo moves only in a small range Duty values are not correct for your servo Calibrate as described above; adjust DUTY_0 and DUTY_180
ESP32 resets when servo moves Servo draws too much current from the board’s 5V pin Never power servo from ESP32; use external 5V supply

07Core Principle to Remember

A servo is controlled by pulse width, not by the percentage of duty cycle.The frequency must be exactly 50 Hz (period 20 ms). Changing the pulse width between 0.5 ms and 2.5 ms rotates the servo from 0° to 180°. In MicroPython’sduty_u16()method, the mapping from pulse width to duty value depends on the PWM resolution (always 16‑bit on ESP32). Use the formulas provided or the linear mapping function.

08Actionable Recommendations

1. Always start with a simple sweep test– it confirms wiring and basic PWM operation.

2. Use a logic analyzer or an oscilloscopeto verify the pulse width if the servo behaves oddly.

3. For battery‑powered projects, add a large capacitor (470 µF or more) across the servo power terminals to absorb voltage drops.

4. When your project requires multiple servos, control each on a separate GPIO pin, but ensure the total current does not exceed your power supply’s rating.

09Final Summary

Controlling a servo with ESP32 and Python is straightforward: connect the signal wire to any PWM‑capable GPIO, provide separate 5V power with a common ground, generate a 50 Hz PWM signal, and adjust the duty cycle to set the pulse width between 0.5 ms and 2.5 ms. The provided code works out of the box for most standard servos. If the movement range is incorrect, calibrate the minimum and maximum duty values for your specific servo. Now you are ready to integrate precise motion into your own ESP32‑based projects.

Update Time:2026-04-05

Powering The Future

Contact Kpower's product specialist to recommend suitable motor or gearbox for your product.

Mail to Kpower
Submit Inquiry
+86 0769 8399 3238
 
kpowerMap