Language: EN


How to use PWM analog outputs on an ESP32

The ESP32 has multiple pins that support PWM functionality, giving us great flexibility to control various devices.

Each ESP32 model has a certain amount of available PWM outputs. For example, the ESP32 has 16, while the ESP32-S3 has 8.

However, the operation is somewhat different from what we are used to in a “conventional” Arduino. This is because the ESP32 has more than one way to generate PWM outputs.

The equivalent to analogWrite used in Arduino to generate PWM, in the case of the ESP32 would be the modulation called ledc.

Although the function is called ledc, it does not mean that it is only used for LEDs. It can be used for any device, including motors, transistors, etc.

How to use a PWM output on the ESP32

Before using a PWM output, we need to configure its parameters, such as the PWM channel, the associated pin, and the modulation frequency.

To do this, we will use the ledcSetup() function

const int ledChannel = 0;    // PWM channel, can be from 0 to 15
const int ledPin = 5;        // Pin to which the device is connected
const int frequency = 5000;  // Frequency in Hz
const int resolution = 8;    // Resolution in bits (from 1 to 15)

ledcSetup(ledChannel, frequency, resolution);

Once we have set the parameters, we can set the duty cycle to control the intensity of the output signal.

The duty cycle is defined as a value between 0 and 255 (or the maximum value defined by the resolution) that represents the percentage of time the pin will remain in the high state.

A duty cycle of 0 means that the pin will always be in the low state, while a duty cycle of 255 will keep the pin always in the high state.

To set the duty cycle, we use the ledcWrite() function. Here’s an example of how to control the intensity of the LED connected to the PWM pin:

int dutyCycle = 128; // Duty cycle value (0 to 255)

ledcWrite(ledChannel, dutyCycle);

With a dutyCycle value of 128, the LED will receive approximately half of the maximum voltage, resulting in a brightness of 50%.

Code Example

Next, we show you a complete example of how to control the intensity of an LED connected to the PWM pin of the ESP32:

const int ledChannel = 0;
const int ledPin = 5;
const int frequency = 5000;
const int resolution = 8;

void setup() {
  ledcSetup(ledChannel, frequency, resolution);
  ledcAttachPin(ledPin, ledChannel);

void loop() {
  // Gradually increase the duty cycle to increase the LED intensity
  for (int dutyCycle = 0; dutyCycle <= 255; dutyCycle++) {
    ledcWrite(ledChannel, dutyCycle);

  // Gradually decrease the duty cycle to decrease the LED intensity
  for (int dutyCycle = 255; dutyCycle >= 0; dutyCycle--) {
    ledcWrite(ledChannel, dutyCycle);

In this example, the LED connected to pin 5 will gradually illuminate from the minimum to the maximum duty cycle and then dim again.

Alternative with a library

Many people have become accustomed to using analogWrite as part of their programs, for convenience.

In fact, you may even have many programs that use it, and you would like to use it on the ESP32.

Fortunately, you are not the only one. So different authors have created libraries to allow PWM to be used as

An example of this library GitHub - erropix/ESP32_AnalogWrite: Provides an analogWrite polyfill for ESP32 using the LEDC functions

#include <Arduino.h>
#include <analogWrite.h>

int brightStep = 1;
int brightness = 0;

void setup() {
  // Set resolution for a specific pin
  analogWriteResolution(LED_BUILTIN, 12);

void loop() {
  brightness += brightStep;
  if (brightness == 0 || brightness == 255) {
    brightStep = -brightStep;

  analogWrite(LED_BUILTIN, brightness);


Using this library, we will lose some of the flexibility that we can use with ledc, so it is preferable that you use this.

But, as a transition, to adapt your programs, or if it is easier for you in teaching (for example), using a library is not a bad option.