Zbotic Logo Zbotic Logo
  • Home
  • Shop
  • Sale
  • 3D Print Service
  • PCB Service
  • B2B
  • Blogs
  • Contact Us
0 0

View Wishlist Add all to cart

0 0
0 Shopping Cart
Shopping cart (0)
Subtotal: ₹0.00

View cartCheckout

  • Shop
  • About Us
  • Contact Us
  • Reseller
  • Blogs
020 69134444
1800 209 0998
[email protected]
Help Desk
Facebook Twitter Instagram Linkedin YouTube
Zbotic Logo Zbotic Logo
0 0

View Wishlist Add all to cart

0 0
0 Shopping Cart
Shopping cart (0)
Subtotal: ₹0.00

View cartCheckout

All departments
  • 3D Print Service
  • 3D Printer
  • Batteries & Chargers
  • Development Boards
  • Drone Parts
  • EBike parts
  • Sensor Modules
  • Electronic Components
  • Electronic Modules
  • IoT and Wireless
  • Mechanical Parts and Workbench Tools
  • Motors & Drivers & Pumps & Actuators
  • DIY and Robot Kits
  • Show more
  • Home
  • Shop
  • Sale
  • 3D Print Service
  • PCB Service
  • B2B
  • Blogs
  • Contact Us
Return to previous page
Home IoT & Smart Home

ESP32 PWM LED Dimmer: Smooth Fading with LEDC Channel

ESP32 PWM LED Dimmer: Smooth Fading with LEDC Channel

March 11, 2026 /Posted byJayesh Jain / 0

ESP32 PWM LED dimmer LEDC channel fading is one of the most popular beginner-to-intermediate projects in the Indian electronics community, and for good reason — it demonstrates a critical embedded systems concept while producing immediately satisfying visual results. Unlike the Arduino Uno’s limited analogWrite(), the ESP32’s LEDC (LED Control) peripheral is a dedicated, highly configurable hardware PWM engine that can drive multiple LED channels simultaneously with independent frequencies, resolutions, and duty cycles. This tutorial covers everything from the basic theory to advanced smooth fading algorithms and multi-channel RGB LED control.

Table of Contents

  1. ESP32 LEDC Peripheral Overview
  2. Basic PWM Setup: ledcSetup and ledcAttachPin
  3. Smooth LED Fading: Linear vs Gamma-Corrected
  4. Driving Multiple LEDs with Independent LEDC Channels
  5. RGB LED Control: Full Colour Mixing
  6. PWM LED Dimmer Circuit: MOSFETs for High-Power LEDs
  7. Advanced Fading Patterns: Breathing, Strobe and Chase
  8. Frequently Asked Questions

ESP32 LEDC Peripheral Overview

The ESP32’s LEDC peripheral was originally designed for LED brightness control but is a fully general-purpose hardware PWM controller. Understanding its architecture is key to using it effectively:

  • 16 LEDC channels total — 8 “high-speed” channels (hardware-supported auto-fade) and 8 “low-speed” channels
  • 4 independent timers — channels are grouped; each timer can have a different frequency and resolution
  • Configurable resolution: 1 to 16 bits (default 8-bit gives 256 steps; 13-bit gives 8192 steps for ultra-smooth fading)
  • Frequency range: From 1 Hz to 40 MHz depending on resolution — perfect for both visible LED flicker-free dimming (>1 kHz) and motor/servo control
  • Any GPIO can be a LEDC output — unlike Arduino PWM which is restricted to specific pins marked with ~
Resolution Steps Max Frequency Best Use
8-bit 256 312 kHz Simple LED dimming, compatible with analogWrite
10-bit 1024 78 kHz Smooth dimming for displays
12-bit 4096 19.5 kHz High-quality audio PWM, precision dimming
13-bit 8192 9.7 kHz Ultra-smooth fading, imperceptible steps
Ai Thinker NodeMCU-32S ESP32 Development Board

Ai Thinker NodeMCU-32S ESP32 Development Board – IPEX Version

All 16 LEDC PWM channels are available on this NodeMCU-32S board. It is the perfect starting platform for ESP32 LED dimming and PWM experiments, widely available across India.

View on Zbotic

Basic PWM Setup: ledcSetup and ledcAttachPin

The Arduino ESP32 core provides three key LEDC functions. Note: in ESP32 Arduino core v3.x, the API was updated — we cover both the classic (v2.x) and new (v3.x) API:

Classic API (Arduino ESP32 core v2.x)

const int LED_PIN    = 2;   // GPIO 2 (built-in LED)
const int LEDC_CH    = 0;   // LEDC channel 0 (0-15)
const int FREQ_HZ    = 5000; // PWM frequency: 5 kHz
const int RESOLUTION = 8;   // 8-bit = 0–255

void setup() {
  ledcSetup(LEDC_CH, FREQ_HZ, RESOLUTION); // Configure channel
  ledcAttachPin(LED_PIN, LEDC_CH);         // Attach GPIO to channel
}

void loop() {
  // Fade in
  for (int duty = 0; duty <= 255; duty++) {
    ledcWrite(LEDC_CH, duty);
    delay(10);
  }
  // Fade out
  for (int duty = 255; duty >= 0; duty--) {
    ledcWrite(LEDC_CH, duty);
    delay(10);
  }
}

New API (Arduino ESP32 core v3.x)

In ESP32 Arduino core v3.0+, channels are assigned automatically:

const int LED_PIN    = 2;
const int FREQ_HZ    = 5000;
const int RESOLUTION = 8;

void setup() {
  // New API: attach and configure in one call
  ledcAttach(LED_PIN, FREQ_HZ, RESOLUTION);
}

void loop() {
  for (int duty = 0; duty <= 255; duty++) {
    ledcWrite(LED_PIN, duty); // Pass GPIO, not channel number
    delay(10);
  }
  for (int duty = 255; duty >= 0; duty--) {
    ledcWrite(LED_PIN, duty);
    delay(10);
  }
}

Check your core version: Arduino IDE → Tools → Board → Boards Manager → Search “esp32” by Espressif Systems → see version number.

Smooth LED Fading: Linear vs Gamma-Corrected

A naive linear fade from 0 to 255 does not look smooth to the human eye. The human eye perceives brightness on a logarithmic scale — the difference between 0 and 10 is very visible, but the difference between 245 and 255 is nearly imperceptible. This is why a linear ramp starts fast and then barely seems to change near the top.

The solution is gamma correction, which applies a power curve (gamma ≈ 2.2 for most LED colours) to map linear duty values to perceptually uniform brightness steps:

// Pre-computed gamma correction table for 8-bit (256 entries)
// gamma = 2.2, input 0-255 → output 0-255
const uint8_t gamma8[] = {
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  1,  1,  1,
    1,  1,  1,  1,  1,  1,  1,  1,  1,  2,  2,  2,  2,  2,  2,  2,
    2,  3,  3,  3,  3,  3,  3,  3,  4,  4,  4,  4,  4,  5,  5,  5,
    5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  9,  9,  9, 10,
   10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
   17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
   25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
   37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
   51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
   69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
   90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
  115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
  144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
  177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
  215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255
};

void loop() {
  // Gamma-corrected fade — looks perfectly smooth!
  for (int i = 0; i <= 255; i++) {
    ledcWrite(LED_PIN, gamma8[i]);
    delay(8);
  }
  for (int i = 255; i >= 0; i--) {
    ledcWrite(LED_PIN, gamma8[i]);
    delay(8);
  }
}

The difference is dramatic — gamma correction makes LED dimming look professional and natural.

Driving Multiple LEDs with Independent LEDC Channels

The ESP32’s 16 LEDC channels are a major advantage over Arduino. You can independently control up to 16 LEDs at different brightness levels simultaneously:

// Classic API example: 4 LEDs on independent channels
#define LED1 13
#define LED2 12
#define LED3 14
#define LED4 27

void setup() {
  // Setup 4 channels: same frequency, 8-bit resolution
  for (int ch = 0; ch < 4; ch++) {
    ledcSetup(ch, 5000, 8);
  }
  ledcAttachPin(LED1, 0);
  ledcAttachPin(LED2, 1);
  ledcAttachPin(LED3, 2);
  ledcAttachPin(LED4, 3);
}

void loop() {
  // Create a flowing "chaser" effect
  for (int i = 0; i <= 255; i++) {
    ledcWrite(0, i);          // LED1 fading in
    ledcWrite(1, 255 - i);    // LED2 fading out
    ledcWrite(2, (i * 2) % 256); // LED3 double speed
    ledcWrite(3, (255 - i * 2 + 256) % 256); // LED4 opposite
    delay(5);
  }
}
Waveshare ESP32-S3 1.47inch LCD Display Development Board

Waveshare ESP32-S3 1.47inch 172×320 LCD Display Development Board

Combine ESP32-S3’s 16 LEDC PWM channels with the onboard display to build a professional LED dimmer controller with a real-time brightness readout and colour picker UI.

View on Zbotic

RGB LED Control: Full Colour Mixing

RGB LEDs have three elements (red, green, blue) that you drive independently to mix any colour. Use 3 LEDC channels for perfect colour control:

#define RED_PIN   25
#define GREEN_PIN 26
#define BLUE_PIN  27

// Classic API: channels 0, 1, 2
void setupRGB() {
  ledcSetup(0, 5000, 8); ledcAttachPin(RED_PIN,   0);
  ledcSetup(1, 5000, 8); ledcAttachPin(GREEN_PIN, 1);
  ledcSetup(2, 5000, 8); ledcAttachPin(BLUE_PIN,  2);
}

void setColor(uint8_t r, uint8_t g, uint8_t b) {
  ledcWrite(0, r);
  ledcWrite(1, g);
  ledcWrite(2, b);
}

// HSV to RGB converter for rainbow cycling
void hsvToRgb(float h, float s, float v,
              uint8_t* r, uint8_t* g, uint8_t* b) {
  int i = int(h * 6);
  float f = h * 6 - i;
  float p = v * (1 - s);
  float q = v * (1 - f * s);
  float t = v * (1 - (1 - f) * s);
  switch (i % 6) {
    case 0: *r=v*255; *g=t*255; *b=p*255; break;
    case 1: *r=q*255; *g=v*255; *b=p*255; break;
    case 2: *r=p*255; *g=v*255; *b=t*255; break;
    case 3: *r=p*255; *g=q*255; *b=v*255; break;
    case 4: *r=t*255; *g=p*255; *b=v*255; break;
    case 5: *r=v*255; *g=p*255; *b=q*255; break;
  }
}

void setup() { setupRGB(); }

void loop() {
  // Rainbow cycle over 5 seconds
  for (int i = 0; i < 360; i++) {
    uint8_t r, g, b;
    hsvToRgb(i / 360.0, 1.0, 1.0, &r, &g, &b);
    setColor(r, g, b);
    delay(14); // 360 * 14ms ≈ 5 seconds
  }
}

PWM LED Dimmer Circuit: MOSFETs for High-Power LEDs

The ESP32’s GPIO can source only 40 mA per pin (120 mA total for all pins). For high-power LEDs (1W, 3W, 10W LED modules used in Indian market LED bulbs and strips), you need a MOSFET driver circuit.

Basic N-Channel MOSFET Dimmer Circuit:

  • ESP32 GPIO (3.3V PWM) → Gate of IRLZ44N or IRL520N (logic-level N-MOSFET)
  • Add a 100Ω resistor between GPIO and Gate to limit current
  • Add a 10kΩ resistor between Gate and GND to pull Gate low when ESP32 boots (prevents random full-power flash)
  • Drain → LED negative (cathode) terminal
  • Source → GND (shared with ESP32 GND)
  • LED positive (anode) → External 12V supply through current-limiting resistor
  • Add a flyback diode (1N4007) across the LED in reverse for inductive loads like LED drivers

Important note for Indian 12V LED strips: The popular 5050 RGB LED strips sold in India use 12V. For these, use three N-MOSFET circuits (one per colour channel). The IRLZ44N or the much cheaper IRF520 (available at most Indian component shops) work well for strip currents up to 5A.

For 230V AC LED dimming (mains voltage), never use GPIO-driven MOSFETs directly. Use a commercial TRIAC dimmer module like the RobotDyn AC dimmer module with opto-isolation — safety first for mains-connected projects.

30Pin ESP32 Expansion Board

30Pin ESP32 Expansion Board with Type-C and Micro USB

Break out all 30 GPIO pins easily for your multi-channel LEDC PWM LED projects. This expansion board simplifies wiring multiple MOSFET driver circuits for high-power LED dimming.

View on Zbotic

Advanced Fading Patterns: Breathing, Strobe and Chase

Now that you understand the basics, here are production-ready implementations of popular LED effects:

Breathing / Heartbeat Effect

The breathing effect uses a sine wave for an organic, natural pulsing appearance:

#include <math.h>

void breathingEffect(int ledPin, float period_ms, int iterations) {
  float step = 0;
  unsigned long start = millis();
  while (millis() - start < period_ms * iterations) {
    // Sine wave from 0..1 (output range 5..255 to avoid fully off)
    float brightness = (sin(step) + 1.0) / 2.0;
    uint8_t duty = (uint8_t)(5 + brightness * 250);
    ledcWrite(ledPin, gamma8[duty]); // Apply gamma correction
    step += (2 * M_PI) / (period_ms / 10); // 10ms per step
    delay(10);
  }
}

Strobe Effect

void strobe(int ledPin, int flashes, int onMs, int offMs) {
  for (int i = 0; i < flashes; i++) {
    ledcWrite(ledPin, 255);
    delay(onMs);
    ledcWrite(ledPin, 0);
    delay(offMs);
  }
}

Non-blocking Fading with millis()

For real-world projects that need to fade LEDs while also handling Wi-Fi, sensors, or other tasks, use a non-blocking pattern:

class LEDFader {
public:
  int pin;
  int duty = 0;
  int step = 1;
  int minDuty = 0, maxDuty = 255;
  unsigned long stepInterval = 10;
  unsigned long lastUpdate = 0;

  void begin(int p, int freq = 5000, int res = 8) {
    pin = p;
    ledcSetup(p, freq, res); // Classic API
    ledcAttachPin(p, p % 16);
  }

  void update() {
    if (millis() - lastUpdate >= stepInterval) {
      lastUpdate = millis();
      duty += step;
      if (duty >= maxDuty || duty <= minDuty) step = -step;
      ledcWrite(pin % 16, gamma8[duty]);
    }
  }
};

LEDFader led1, led2, led3;

void setup() {
  led1.begin(13); led1.stepInterval = 8;
  led2.begin(12); led2.stepInterval = 12;
  led3.begin(14); led3.stepInterval = 15;
}

void loop() {
  led1.update();
  led2.update();
  led3.update();
  // Other tasks can run here without blocking the faders
}

Frequently Asked Questions

What is the difference between analogWrite() and ledcWrite() on ESP32?

On Arduino Uno and Mega, analogWrite(pin, value) uses the AVR timer hardware for 8-bit PWM on specific pins. On ESP32, analogWrite() is a compatibility wrapper that internally calls the LEDC functions — it works but limits you to 8-bit and a fixed frequency. Using ledcWrite() directly gives you full control over frequency (1 Hz to 40 MHz), resolution (1–16 bits), and channel assignment. Always use the native LEDC API for professional ESP32 projects.

Why does my LED flicker when dimmed at low brightness?

Visible flicker at low duty cycles usually indicates the PWM frequency is too low. Human eyes can detect flicker below about 50–100 Hz. Increase the LEDC frequency to 1 kHz or higher (5 kHz is the most common recommendation). Note that increasing frequency requires reducing resolution to maintain the clock constraints — at 5 kHz, 8-bit (256 steps) is fine. Also, some cheap LED bulbs have non-linear drivers that flicker more — use high-quality LEDs for smooth dimming.

Can I use LEDC PWM for servo motor control?

Yes! Standard RC servos use a 50 Hz PWM signal with 1–2 ms pulse width. Set LEDC to 50 Hz with 16-bit resolution (65536 steps). Map 0–180 degrees to pulse widths of 1ms–2ms: duty = map(angle, 0, 180, 1638, 3277) for 16-bit at 50 Hz (1ms = 1/20 × 32768 = 1638 steps, 2ms = 3277 steps). The ESP32Servo library automates this for you.

How many LEDC channels can run simultaneously on ESP32?

All 16 channels can run simultaneously. Each high-speed channel (0–7) is linked to one of 4 high-speed timers. Each low-speed channel (8–15) is linked to one of 4 low-speed timers. Channels sharing a timer must use the same frequency and resolution but can have independent duty cycles. For maximum flexibility with different frequencies, assign channels to different timers.

Does ledcFade() work on ESP32 for hardware-assisted fading?

Yes! The ESP32’s high-speed LEDC channels (0–7) support hardware auto-fade via ledcFade(channel, targetDuty, maxFade, time) and ledcFadeWithInterrupt(). Hardware fading runs entirely in the LEDC peripheral with no CPU intervention, freeing your code for other tasks. Only use this with the classic API (v2.x); the v3.x API does not yet expose ledcFade directly — use ledc_set_fade_with_time() from the ESP-IDF driver directly in that case.

Build Your ESP32 LED Projects with Zbotic

Shop ESP32 development boards, expansion shields, and sensor modules for your PWM LED dimmer and IoT projects. Zbotic delivers fast across India — order today and start building tomorrow.

Shop ESP32 & IoT Components

Tags: ESP32 LED fading, ESP32 PWM, LED dimmer, LEDC channel, PWM Arduino ESP32
Share Post
  • Facebook
  • Linkedin
  • Whatsapp
Best ESP32 Development Boards ...
blog best esp32 development boards in india 2026 buyers guide 595605
blog esp32 micropython getting started setup code and projects 595612
ESP32 MicroPython Getting Star...

Related posts

Svg%3E
Read more

IoT Home Insurance Sensor Kit: Leak, Smoke, and Motion

April 1, 2026 0
Table of Contents IoT and Home Insurance Water Leak Detection Smoke and Fire Detection Motion and Intrusion Sensing Building the... Continue reading
Svg%3E
Read more

IoT Pet Tracker: GPS Collar with Geofencing Alerts

April 1, 2026 0
Table of Contents Introduction and Overview Hardware Components Required GPS Module Integration with ESP32 Cloud Platform Setup Real-Time Tracking Dashboard... Continue reading
Svg%3E
Read more

IoT Aquaponics Controller: Fish and Plant Automation

April 1, 2026 0
Table of Contents The Water Monitoring Challenge in India Sensor Technologies for Water Building the Sensor Node Data Transmission and... Continue reading
Svg%3E
Read more

IoT Composting Monitor: Temperature and Moisture Tracking

April 1, 2026 0
Table of Contents Why Temperature Monitoring Matters Sensor Selection Guide Hardware Assembly and Wiring Firmware Development Cloud Data Logging Alert... Continue reading
Svg%3E
Read more

IoT Beehive Monitor: Weight, Temperature, and Humidity

April 1, 2026 0
Table of Contents Why Monitor Beehives Weight Measurement System Temperature and Humidity Sensing Building the Monitor Data Analysis for Bee... Continue reading

Add comment Cancel reply

Your email address will not be published. Required fields are marked

Facebook Twitter Instagram Pinterest Linkedin Youtube

Get the latest deals and more.

Download on Google Play Download on the App Store

Call us: 020 69134444 / 1800 209 0998

Monday - Saturday 09:30 AM - 06:00 PM
For Technical Supports Email: [email protected]
For Sales / Enquiries Email: [email protected]

  • My Account

    • Cart

    • Wishlist

    • Checkout

    • My Orders

    • Track Order

    • My Account

  • Information

    • FAQs

    • Blogs

    • Career

    • About Us

    • Contact Us

    • Payment Options

  • Policies

    • Privacy Policy

    • Terms & Conditions

    • GST Input Tax Credit

    • Shipping Return Policy

    • E-Waste Collection Points

    • Our Sitemap

© Zbotic.in is registered trademark of Moxie Supply Pvt Ltd – All Rights Reserved
Login
Use Phone Number
Use Email Address
Not a member yet? Register Now
Reset Password
Use Phone Number
Use Email Address
Register
Already a member? Login Now