The ESP32 has multiple pins that support PWM functionality, giving us a wide flexibility to control various devices.
Each ESP32 model has a different number 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 of analogWrite
used in Arduino to generate a PWM, in the case of the ESP32 would be the modulation they call ledc
.
Although the function is called ledc
, it does not mean it is only for LEDs. It can be used for any device, including motors, transistors, etc.
If you don’t know what a PWM analog output is or need more help, check these entries:
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 configured 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 high.
A duty cycle of 0 means that the pin will always be low, while a duty cycle of 255 will keep the pin always high.
To set the duty cycle, we use the ledcWrite()
function. Here is 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
Below is 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 brighten the LED
for (int dutyCycle = 0; dutyCycle <= 255; dutyCycle++) {
ledcWrite(ledChannel, dutyCycle);
delay(10);
}
// Gradually decrease the duty cycle to dim the LED
for (int dutyCycle = 255; dutyCycle >= 0; dutyCycle--) {
ledcWrite(ledChannel, dutyCycle);
delay(10);
}
}
In this example, the LED connected to pin 5 will gradually brighten from the minimum to the maximum duty cycle and then dim back down.
Alternative with a Library
Many people have become accustomed to using analogWrite
as part of their programs, due to the convenience and simplicity it offers (you may even have many programs that use it and would like to use them on the ESP32).
Fortunately, you are not alone in this. Various authors have created libraries to allow using PWM like in a “classic” Arduino.
An example of this library is 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);
delay(10);
}
By using this library, we will lose some of the flexibility we can achieve with ledc
, so it is preferable to use this.
However, as a transition, to adapt your programs, or if it is easier for teaching purposes (for example), using a library is not a bad option.
References: