Embedded Systems

ESP32 Beginner Course #4: PWM Explained by Controlling LED Brightness

Learn PWM on the ESP32 using LEDC, duty cycle, frequency, 8-bit resolution, and a fading LED brightness example.

ESP32PWMLEDCLEDDuty Cycle

Introduction

So far, we have turned an LED completely on or completely off. But many real systems need more control than that. LED dimmers, motor speed controllers, cooling fans, and power converters often need variable power.

PWM, or Pulse Width Modulation, is one of the most useful techniques for controlling average power with a digital pin.

What is PWM?

PWM rapidly switches a digital signal between HIGH and LOW. Instead of changing the voltage level directly, it changes how long the signal stays HIGH during each cycle.

If the signal is HIGH for a small part of the cycle, the load receives less average power. If the signal is HIGH for most of the cycle, the load receives more average power.

For a visual explanation, open the interactive lab: PWM Signal.

PWM is not true analog output

PWM does not create a smooth analog voltage by itself. The ESP32 pin is still switching between 0V and 3.3V.

The useful effect comes from speed and averaging:

  • An LED appears dimmer or brighter because our eyes average the fast switching
  • A motor changes speed because its mechanical inertia averages the pulses
  • A filter circuit can smooth PWM into a more analog-like voltage

Duty cycle

Duty cycle is the percentage of time the PWM signal stays HIGH during one cycle.

Examples:

  • 0 percent: always LOW
  • 25 percent: HIGH for one quarter of the cycle
  • 50 percent: HIGH for half of the cycle
  • 100 percent: always HIGH

For LED brightness, higher duty cycle usually means brighter light.

Frequency

Frequency tells us how many PWM cycles happen per second.

If the frequency is too low, the LED may visibly flicker. If the frequency is high enough, the LED looks stable and smooth.

In this project, we use 5000 Hz, which is a good beginner value for LED dimming.

Resolution

PWM resolution controls how many duty cycle steps are available.

With 8-bit resolution, the duty value can range from 0 to 255:

  • 0 means off
  • 128 is about 50 percent
  • 255 means fully on

Higher resolution gives finer control, but the available frequency and resolution depend on the PWM hardware.

Hardware required

  • ESP32 DevKit
  • LED
  • 220 ohm resistor
  • Breadboard
  • Jumper wires
  • USB cable

Wiring

Use the same LED circuit from the GPIO lesson:

GPIO2
  |
 LED
  |
220 ohm
  |
 GND

The hardware stays simple. The important change is in software.

Source code

const int ledPin = 2;

const int frequency = 5000;
const int resolution = 8;

void setup()
{
    ledcAttach(ledPin, frequency, resolution);
}

void loop()
{
    for (int duty = 0; duty <= 255; duty++)
    {
        ledcWrite(ledPin, duty);
        delay(10);
    }

    for (int duty = 255; duty >= 0; duty--)
    {
        ledcWrite(ledPin, duty);
        delay(10);
    }
}

Code explanation

First, the LED pin is stored in a variable:

const int ledPin = 2;

Then the PWM frequency and resolution are defined:

const int frequency = 5000;
const int resolution = 8;

Inside setup(), PWM is attached to the LED pin:

ledcAttach(ledPin, frequency, resolution);

Inside loop(), the first for loop increases the duty cycle from 0 to 255. This fades the LED up.

The second for loop decreases the duty cycle from 255 back to 0. This fades the LED down.

Common mistakes

If the LED does not fade, check:

  • LED polarity
  • Resistor value and wiring
  • GPIO number in the code
  • Arduino ESP32 package version
  • Whether old code uses ledcSetup() and ledcAttachPin()

In newer Arduino ESP32 versions, ledcAttach() and ledcWrite() are commonly used together.

Real engineering use cases

PWM is used in:

  • LED dimmers
  • RGB LED strips
  • DC motor speed control
  • Fan control
  • Audio generation
  • Servo-style control signals
  • Power electronics

The same concept becomes more powerful when combined with MOSFETs, motor drivers, filters, and feedback control.

Engineering challenge

Modify the program so the LED fades up in exactly 2 seconds and fades down in exactly 2 seconds. Then try changing the PWM frequency and observe whether the LED appears different.

Next lesson

Next, we will learn how to read analog voltages using the ESP32 ADC. We will use a potentiometer and control LED brightness based on knob position.