ESP32 PWM Tutorial: Control LED Brightness and Motor Speed with Arduino IDE
Oct 27th, 2026

ESP32 PWM (Pulse Width Modulation) allows you to control LED brightness, DC motor speed, buzzer tones, and other electronic devices using digital signals.
Instead of producing a continuously variable voltage, the ESP32 rapidly switches a GPIO pin between HIGH and LOW. By adjusting the ESP32 PWM duty cycle, you can change the average power delivered to a connected load.
This ESP32 PWM tutorial uses examples targeting the ESP32-DevKitC V4 with an ESP32-WROOM-32E module and the Espressif Arduino-ESP32 core 3.3.7 API. This is the specified reproducible configuration, not a claim of physical hardware testing.
What Is PWM?
ESP32 PWM generates a repeating sequence of digital pulses. Each cycle contains an ON period and an OFF period.
Example PWM pulse train with a 50% duty cycle.

PWM Duty Cycle
The duty cycle represents the percentage of each PWM period that the signal stays HIGH.
Duty cycle (%) = (ON time ÷ Period) × 100
PWM duty cycles from 0% (always LOW) to 100% (always HIGH).

For ESP32 LED brightness control, a 25% duty cycle produces lower brightness than 75%. For a DC motor, increasing the duty cycle generally increases speed, although the relationship is not perfectly linear.
PWM Frequency
ESP32 PWM frequency is the number of complete ON/OFF cycles per second, measured in hertz (Hz). For example, a frequency of 1,000 Hz means the signal completes 1,000 cycles every second.

Choosing the right ESP32 PWM frequency depends on the connected load. LEDs commonly operate at several kilohertz, while DC motors may use frequencies above the audible range.
How ESP32 PWM Works
The ESP32 LEDC (LED Control) peripheral generates PWM signals without requiring the processor to toggle GPIO pins continuously.
LEDC uses timers to establish frequency and resolution, while channels control duty cycles. The number of channels and supported features varies across ESP32 variants.For a deeper understanding of timer and channel configuration, see Espressif's ESP32 LEDC peripheral documentation.
The ESP32's GPIO Matrix allows PWM signals to be assigned to different output-capable pins. This flexibility helps you select suitable ESP32 PWM pins for your circuit.
Which ESP32 Pins Support PWM?
On the original ESP32, many output-capable GPIOs can be used as ESP32 PWM pins through the LEDC peripheral.
For the reference board, GPIO18 and GPIO19 are suitable choices.
When selecting ESP32 PWM pins, avoid GPIO34–GPIO39 because they are input-only. GPIO6–GPIO11 are normally reserved for SPI flash, and boot-strapping pins require special care.
Always consult the pinout of the specific development board because available GPIOs differ between ESP32, ESP32-S3, ESP32-C3, and other variants. For detailed GPIO functions and pin restrictions, check our ESP32 pinout guide.
ESP32 PWM Resolution
ESP32 PWM resolution determines the number of available duty-cycle levels.
At 8-bit resolution:
2^8=256
Duty values range from 0 to 255.
With 10-bit ESP32 PWM resolution, the duty-cycle range becomes 0–1023, providing finer control.
A higher ESP32 PWM frequency leaves less timer capacity for high resolution. Consequently, frequency and resolution must be selected together.
Arduino IDE LEDC Configuration
When configuring ESP32 PWM in Arduino IDE with Arduino-ESP32 3.x, use the pin-based LEDC API. For API details, consult the official Arduino-ESP32 LEDC documentation
ledcAttach(18, 5000, 8);
ledcWrite(18, 128);
In this ESP32 PWM example, the first function attaches GPIO18 to a PWM channel at 5 kHz with 8-bit resolution. The second sets approximately 50% duty.
Unlike Arduino-ESP32 2.x, the 3.x API does not require the older ledcSetup() and ledcAttachPin() calls.
Project 1: Fade an LED
This ESP32 LED brightness control project gradually increases and decreases LED brightness using PWM on GPIO18.
Components: ESP32-DevKitC V4, LED, 220 Ω resistor, breadboard, and jumper wires.
Wiring Diagram
GPIO18 → 220 Ω resistor → LED anode (+); LED cathode (−) → GND.

Arduino Code
Expected result: The LED gradually brightens and dims in a continuous cycle. Each ramp takes approximately 2.56 seconds.
Project 2: Control DC Motor Speed
For ESP32 DC motor speed control, a DC motor requires more current than an ESP32 GPIO can supply. Use an N-channel MOSFET to switch the motor’s external power supply, with a flyback diode connected across the motor.To learn more about different motor types and their control methods, read our ESP32 motor control guide.
Components:
ESP32-DevKitC V4, small 5 V DC motor, N-channel MOSFET suitable for 3.3 V gate drive, 100 Ω gate resistor, 10 kΩ gate-to-source pull-down resistor, appropriately rated flyback diode, and external 5 V power supply.
Choosing the MOSFET
For ESP32 DC motor speed control using a GPIO pin, choose a MOSFET whose datasheet specifies a sufficiently low maximum on-resistance, RDS(on), at a gate-to-source voltage of 2.5 V or 3.0 V.Its current rating and thermal performance must also suit the motor’s starting and stall current. The gate-threshold voltage alone does not indicate that a MOSFET is fully on.
The IRLZ44N shown in the diagram does not have a guaranteed RDS(on) specification at 3.3 V gate drive. It may operate with some small motors, but should only be retained after testing the actual motor, power supply, and 20 kHz ESP32 PWM frequency for reliable starting and acceptable MOSFET temperature. Otherwise, use a MOSFET specified for lower gate voltages.
Check the replacement device’s physical pinout before wiring; its gate, drain, and source arrangement may differ from the illustration.
Wiring Diagram
Conceptual low-side MOSFET circuit. Connect the flyback diode directly across the motor: cathode to +5 V, anode to the MOSFET drain. Verify the MOSFET's physical pinout before wiring.
The motor supply negative terminal and ESP32 GND must be connected. Do not power the motor from the ESP32's 3.3 V pin. Choose a supply, diode, and MOSFET rated for the motor's stall current.

Arduino Code
Expected result: In this ESP32 DC motor speed control project, the motor accelerates, reaches maximum commanded duty, slows down, and stops before repeating. At low duty cycles, the motor may remain stationary until sufficient starting torque develops.
•PWM for Servo Motors
When using ESP32 PWM to control servo motors, a specific type of pulse signal is required to set their position. Unlike LEDs or regular DC motors, servos respond to the length of each control pulse.
Many hobby servos expect pulses approximately every 20 milliseconds, with pulse widths commonly around 1–2 milliseconds indicating position.
Use a servo-compatible library or carefully configured LEDC timing. An ESP32 PWM frequency of 5 kHz with a 50% duty cycle won't work for a typical hobby servo. Most servos expect a signal around 50 Hz, with pulses usually lasting between 1 and 2 milliseconds.
•PWM for Buzzers
With a passive buzzer, changing the ESP32 PWM frequency changes the pitch of the sound. Higher frequencies produce higher-pitched tones, while lower frequencies produce deeper tones.
For example:
This ESP32 PWM example produces an approximately 1 kHz tone for half a second. Active buzzers contain their own oscillator and behave differently.
Common PWM Problems
| Problem | Possible cause | Suggested fix |
|---|---|---|
| LED flickering | PWM frequency is too low | Try the LED example’s 5 kHz, 8-bit configuration and check whether ledcAttach() succeeds. |
| No output | Wrong GPIO, input-only pin, or incorrect wiring | Confirm the GPIO number against your board’s pinout and check the circuit connections. |
| Motor whining | Audible vibration caused by PWM switching | Try the example’s 20 kHz setting, provided the driver supports it, and check motor operation and MOSFET temperature. |
| Motor not starting | Insufficient starting torque, inadequate supply current, or insufficient MOSFET gate drive | Test with a higher duty cycle, check the supply against the motor’s starting current, and verify MOSFET suitability for 3.3 V control. |
| Compilation errors | Code uses Arduino-ESP32 2.x functions with a 3.x core | Replace ledcSetup() and ledcAttachPin() with ledcAttach(), and use the GPIO pin in ledcWrite(). |
| Unexpected duty levels | Duty values do not match the configured resolution | Use values from 0–255 for 8-bit resolution or 0–1023 for 10-bit resolution. |
| PWM setup | Unsupported frequency/resolution combination or unavailable LEDC | Check the return value of ledcAttach(). Try the example |
| fails | resources | settings and check whether other outputs or libraries already use LEDC resources. |
Choosing Frequency and Resolution
| Application | Starting frequency | Resolution |
|---|---|---|
| LED brightness | 5 kHz | 8-bit |
| DC motor | 20 kHz | 8-bit |
| Passive buzzer | Audible tone frequency | 8-bit |
| Hobby servo | Usually 50 Hz | 14–16-bit, where supported |
These ESP32 PWM frequency and resolution settings are starting points rather than universal recommendations. Motor drivers, LEDs, and servo models may require different configurations.
Conclusion
ESP32 PWM provides a practical way to control LED brightness, DC motor speed, and other devices using digital pulses. By understanding duty cycle, frequency, and resolution, you can configure the LEDC peripheral with the Arduino-ESP32 3.x functions ledcAttach() and ledcWrite().Start with the ESP32 LED brightness control project, then explore ESP32 DC motor speed control using a suitable driver and external power supply. For more advanced lighting projects, explore our ESP32 WS2812B LED guide to learn how to create individually addressable LED effects.Always check your board’s GPIO restrictions and match the PWM settings and circuit components to the connected device.
Frequently Asked Questions (FAQ)
Which ESP32 pins support PWM?
ESP32 PWM pins include many output-capable GPIOs that support the LEDC peripheral. On the original ESP32, GPIO34–GPIO39 are input-only and cannot output PWM.Other pins may be reserved for flash memory or connected to onboard components. Always check the pinout for your specific board, as ESP32, ESP32-S3, and ESP32-C3 pin restrictions differ.
How do I generate PWM with Arduino-ESP32 3.x?
To generate ESP32 PWM in Arduino IDE with Arduino-ESP32 3.x, use ledcAttach(pin, frequency, resolution) to configure the output, then call ledcWrite(pin, duty) to set the duty value. For example, ledcAttach(18, 5000, 8) configures GPIO18 for 5 kHz PWM with 8-bit resolution. Check the return value of ledcAttach() to confirm that configuration succeeded.
Why do ledcSetup() and ledcAttachPin() cause compilation errors?
When working with ESP32 PWM in Arduino IDE, note that these functions were removed in Arduino-ESP32 3.0. Replace them with ledcAttach(). Also check your ledcWrite() calls: the 3.x function takes a GPIO pin number rather than the channel number used in older examples.
What frequency and resolution should I use?
For ESP32 PWM frequency and resolution settings in this tutorial, start with 5 kHz and 8-bit resolution for the LED, or 20 kHz and 8-bit resolution for the DC motor. These are starting points, not universal settings. Check the requirements of your load and driver, and remember that frequency and resolution must form a supported combination.
Does 50% PWM produce a steady 1.65 V output?
No. The GPIO still switches between approximately 0 V and 3.3 V. At 50% duty cycle, it spends half of each period HIGH.Its time-average voltage is approximately 1.65 V, but producing a smooth analog voltage requires filtering and may require additional circuitry.
Can I connect a DC motor directly to an ESP32 GPIO?
No. Use a suitable MOSFET or motor driver, an appropriately rated external supply, and flyback protection. For the non-isolated circuit in this tutorial, connect the external supply’s negative terminal to ESP32 GND. Verify that the MOSFET is suitable for 3.3 V gate drive and the motor’s starting and stall current.
Can I use the LED PWM settings for a servo motor?
No. Many hobby servos use a signal around 50 Hz and respond to pulse width, commonly around 1–2 ms. Follow your servo’s specifications and use a compatible library or carefully configured LEDC output.The 5 kHz ESP32 PWM frequency used in the LED example is unsuitable for a typical hobby servo.
