The humble LED is where most electronics journeys begin, and for good reason. It is the simplest actuator you can connect to an ESP32 -- just a resistor, an LED, and two wires. In this guide, you will learn how to turn an LED on and off with digital output, and then take it further by controlling brightness using PWM (Pulse Width Modulation). Along the way, you will understand why a current-limiting resistor is essential and how to calculate the right value.
This guide uses the ESP32-WROOM-32 DevKit. Pin labels and GPIO numbers may differ on your board -- always check your board's pinout diagram.
Need more than one colour? See RGB LEDs for mixing colours with a single LED, or WS2812B NeoPixels for addressable LED strips where each pixel can be a different colour.
🔗Key Specs
| Parameter | Typical Value (5 mm LED) |
|---|---|
| Forward voltage ($V_f$) | Red: $2.0\,\text{V}$, Green: $2.2\,\text{V}$, Blue: $3.0\,\text{V}$ |
| Forward current ($I_f$) | $20\,\text{mA}$ (typical max) |
| ESP32 GPIO output | $3.3\,\text{V}$, max $\approx 12\,\text{mA}$ per pin |
Important: GPIOs 34--39 on the ESP32 are input-only and cannot be used to drive an LED. Stick to other GPIO pins for output.
🔗Why You Need a Resistor
An LED will happily draw as much current as it can get until it burns out. A current-limiting resistor placed in series with the LED restricts the current to a safe level. The formula is:
$$R = \frac{V_{supply} - V_f}{I}$$
Where:
- $V_{supply}$ is the GPIO output voltage ($3.3\,\text{V}$ on ESP32)
- $V_f$ is the LED's forward voltage
- $I$ is your target forward current
For a red LED ($V_f = 2.0\,\text{V}$) at $10\,\text{mA}$:
$$R = \frac{3.3 - 2.0}{0.010} = 130\,\Omega$$
The nearest standard value is $150\,\Omega$, but a $220\,\Omega$ resistor is a safe, commonly available choice that works well for most LEDs across all colors. It will draw a bit less current and the LED will be slightly dimmer, but it will still be clearly visible and well within safe limits for the ESP32 GPIO.
🔗Common Resistor Values by LED Color
| LED Color | $V_f$ (typical) | Resistor at $10\,\text{mA}$ | Safe general choice |
|---|---|---|---|
| Red | $2.0\,\text{V}$ | $130\,\Omega$ | $220\,\Omega$ |
| Green | $2.2\,\text{V}$ | $110\,\Omega$ | $220\,\Omega$ |
| Blue | $3.0\,\text{V}$ | $30\,\Omega$ | $220\,\Omega$ |
| White | $3.0\,\text{V}$ | $30\,\Omega$ | $220\,\Omega$ |
🔗What You'll Need
| Component | Qty | Notes | Buy |
|---|---|---|---|
| ESP32 dev board | 1 | AliExpress | Amazon.de .co.uk .com | |
| LED (red) | 1 | Any standard 5mm LED | AliExpress | Amazon.de .co.uk .com |
| 220 ohm resistor | 1 | Current-limiting resistor | AliExpress | Amazon.de .co.uk .com |
| Breadboard | 1 | AliExpress | Amazon.de .co.uk .com | |
| Jumper wires | 2 | AliExpress | Amazon.de .co.uk .com |
Links marked Amazon/AliExpress are affiliate links. We may earn a small commission at no extra cost to you.
🔗Wiring
| Component | Connection |
|---|---|
| GPIO 25 | One leg of $220\,\Omega$ resistor |
| Other leg of resistor | LED anode (longer leg, +) |
| LED cathode (shorter leg, -) | GND |
The signal path is: GPIO 25 --> resistor --> LED anode --> LED cathode --> GND.
Tip: The longer leg of an LED is the anode (+). If the legs have been trimmed, look for the flat edge on the LED housing -- that side is the cathode (-).
🔗Code Example: Digital On/Off
This sketch turns the LED on for one second, then off for one second, repeating forever.
#define LED_PIN 25
void setup() {
pinMode(LED_PIN, OUTPUT);
}
void loop() {
digitalWrite(LED_PIN, HIGH); // LED on
delay(1000);
digitalWrite(LED_PIN, LOW); // LED off
delay(1000);
}🔗Code Example: PWM Brightness Control
The ESP32 does not have analogWrite() like Arduino Uno boards. Instead, it uses its built-in LED Control (LEDC) peripheral for PWM output. With the current ESP32 Arduino core, the simplified API uses ledcAttach() and ledcWrite().
This sketch smoothly fades the LED in and out.
#define LED_PIN 25
void setup() {
// Attach GPIO 25 to LEDC with 5000 Hz frequency and 8-bit resolution (0-255)
ledcAttach(LED_PIN, 5000, 8);
}
void loop() {
// Fade in
for (int dutyCycle = 0; dutyCycle <= 255; dutyCycle++) {
ledcWrite(LED_PIN, dutyCycle);
delay(5);
}
// Fade out
for (int dutyCycle = 255; dutyCycle >= 0; dutyCycle--) {
ledcWrite(LED_PIN, dutyCycle);
delay(5);
}
}🔗How It Works
Digital output sets the GPIO pin to either $3.3\,\text{V}$ (HIGH) or $0\,\text{V}$ (LOW). The LED is either fully on or fully off.
PWM (Pulse Width Modulation) rapidly switches the pin between HIGH and LOW at a fixed frequency (5000 Hz in our example). The duty cycle determines what fraction of each cycle the pin stays HIGH. At 8-bit resolution, the duty cycle ranges from 0 (always off) to 255 (always on). A value of 127 means the pin is HIGH roughly half the time, making the LED appear about half brightness. The switching is too fast for your eyes to see, so the LED appears to dim smoothly.
The relationship between duty cycle and perceived brightness is:
$$\text{Brightness} \; (\%) = \frac{\text{dutyCycle}}{255} \times 100$$
Note: Human eyes perceive brightness logarithmically, not linearly. A duty cycle of 127 (50%) will look much brighter than "half brightness" to you. For perceptually smooth fading, use an exponential curve instead of a linear one.
🔗Troubleshooting
| Problem | Possible Cause | Solution |
|---|---|---|
| LED does not light up | Reversed polarity | Flip the LED around (swap anode and cathode) |
| LED does not light up | Wrong GPIO pin or using GPIO 34--39 | Verify the pin in code matches your wiring; use an output-capable GPIO |
| LED burned out | Missing or wrong resistor value | Replace the LED and add a $220\,\Omega$ resistor |
| LED is very dim | Resistor value too high | Use a lower resistance (but not below the calculated minimum) |
| PWM fading not working | Using analogWrite() instead of LEDC | ESP32 does not support analogWrite() -- use ledcAttach() and ledcWrite() |
| LED flickers at low brightness | PWM frequency too low | Increase the frequency in ledcAttach() (try 5000 Hz or higher) |
🔗Next Steps
- Control multiple LEDs independently on different GPIO pins
- Try an RGB LED to mix colors
- Use a button or potentiometer to control LED brightness interactively
- Combine with a sensor to create a visual indicator (for example, LED brightness that changes with temperature)