LED Basics and Why Resistors are Mandatory
An LED (Light Emitting Diode) has no internal current limiting. Without a resistor, Ohm’s law gives infinite current through a forward-biased diode until it burns out — typically in under a second. Every LED circuit must include a current-limiting resistor between the GPIO pin and the LED anode (positive leg).
LEDs have two important parameters: forward voltage (Vf) — the voltage drop across the LED when conducting — and forward current (If) — the current needed for the desired brightness.
| LED Color | Typical Vf | Recommended If | Resistor (3.3V supply) |
|---|---|---|---|
| Red | 1.8–2.2 V | 10–20 mA | 68–150 Ω |
| Yellow / Orange | 2.0–2.2 V | 10–20 mA | 56–130 Ω |
| Green | 2.0–3.0 V | 10–20 mA | 15–130 Ω |
| Blue | 3.0–3.4 V | 5–10 mA | 0–33 Ω (use 33 Ω min) |
| White | 3.0–3.4 V | 5–10 mA | 0–33 Ω (use 33 Ω min) |
R = (Vsupply - Vf) / If Red LED on 3.3V at 15 mA: R = (3.3 - 2.0) / 0.015 = 86.7 Ω → use 100 Ω Blue LED on 3.3V at 8 mA: R = (3.3 - 3.2) / 0.008 = 12.5 Ω → use 33 Ω (safer margin)
Simple LED Wiring
| Component | Connection |
|---|---|
| LED Anode (+) long leg | 100 Ω resistor → ESP32 GPIO |
| LED Cathode (–) short leg | ESP32 GND |
#define LED_PIN 16
void setup() {
pinMode(LED_PIN, OUTPUT);
}
void loop() {
digitalWrite(LED_PIN, HIGH); // LED on
delay(500);
digitalWrite(LED_PIN, LOW); // LED off
delay(500);
}
PWM Brightness Control with LEDC
ESP32 has a dedicated LEDC peripheral with 16 channels capable of high-frequency PWM. This is the correct way to control LED brightness:
#define LED_PIN 16
#define CHANNEL 0
#define FREQ 5000 // 5 kHz
#define BITS 8 // 8-bit → 0–255
void setup() {
ledcSetup(CHANNEL, FREQ, BITS); // Configure channel
ledcAttachPin(LED_PIN, CHANNEL); // Bind GPIO to channel
}
void loop() {
// Fade in
for (int duty = 0; duty <= 255; duty++) {
ledcWrite(CHANNEL, duty);
delay(8); // 256 steps × 8ms ≈ 2 second fade
}
// Fade out
for (int duty = 255; duty >= 0; duty--) {
ledcWrite(CHANNEL, duty);
delay(8);
}
}
RGB LED Control
A common-cathode RGB LED has 4 pins: GND (common), R, G, B. Connect each color pin through a 100 Ω resistor to a separate GPIO. Use LEDC on all three channels for full color mixing:
#define R_PIN 16
#define G_PIN 17
#define B_PIN 18
void setupLEDC(int pin, int ch) {
ledcSetup(ch, 5000, 8);
ledcAttachPin(pin, ch);
}
void setColor(int r, int g, int b) {
ledcWrite(0, r); // R channel
ledcWrite(1, g); // G channel
ledcWrite(2, b); // B channel
}
void setup() {
setupLEDC(R_PIN, 0);
setupLEDC(G_PIN, 1);
setupLEDC(B_PIN, 2);
}
void loop() {
setColor(255, 0, 0); delay(1000); // Red
setColor( 0, 255, 0); delay(1000); // Green
setColor( 0, 0, 255); delay(1000); // Blue
setColor(255, 100, 0); delay(1000); // Orange
setColor(128, 0, 128); delay(1000); // Purple
setColor(255, 255, 255); delay(1000); // White
setColor( 0, 0, 0); delay(500); // Off
}
WS2812B NeoPixel LEDs
NeoPixels are individually addressable RGB LEDs with a built-in driver chip. One GPIO pin can control a strip of 60, 100, or even 300 LEDs via a high-speed serial protocol. Install the Adafruit NeoPixel library from the Arduino Library Manager.
#include <Adafruit_NeoPixel.h>
#define LED_PIN 16
#define NUM_LEDS 12 // Number of pixels in strip
Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
void setup() {
strip.begin();
strip.setBrightness(80); // 0–255, limit for power safety
strip.show();
}
void loop() {
for (int i = 0; i < NUM_LEDS; i++) {
strip.setPixelColor(i, strip.Color(255, 0, 0)); // Red
strip.show();
delay(50);
strip.setPixelColor(i, strip.Color(0, 0, 0)); // Off
}
}
Power note: Each WS2812B LED can draw up to 60 mA at full white. A strip of 30 LEDs = 1.8 A. Always power LED strips from a dedicated 5V supply, not from the ESP32 5V pin.
Driving High-Power LEDs via MOSFET
12V LED strip (+) ──── 12V PSU (+) 12V LED strip (–) ──── MOSFET Drain (IRLZ44N) MOSFET Source ──── GND (common with ESP32 GND) ESP32 GPIO16 ──── 10kΩ ──── MOSFET Gate Use PWM on GPIO16 to dim a 12V LED strip via IRLZ44N logic-level MOSFET. IRLZ44N can handle 47A — vast overkill, but it runs cool at low currents.
Summary
Always use a current-limiting resistor with individual LEDs. Use the LEDC peripheral (ledcSetup / ledcAttachPin / ledcWrite) for smooth brightness control. Drive RGB LEDs with three LEDC channels for 16 million colors. For addressable strips, use the Adafruit NeoPixel or FastLED library. Power any high-current load from a separate supply, not the GPIO pin.