Controlling a Single LED

How to control an LED with ESP32 using digital output

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

ParameterTypical 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

ComponentQtyNotesBuy
ESP32 dev board1AliExpress | Amazon.de .co.uk .com
LED (red)1Any standard 5mm LEDAliExpress | Amazon.de .co.uk .com
220 ohm resistor1Current-limiting resistorAliExpress | Amazon.de .co.uk .com
Breadboard1AliExpress | Amazon.de .co.uk .com
Jumper wires2AliExpress | 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

ComponentConnection
GPIO 25One leg of $220\,\Omega$ resistor
Other leg of resistorLED 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

ProblemPossible CauseSolution
LED does not light upReversed polarityFlip the LED around (swap anode and cathode)
LED does not light upWrong GPIO pin or using GPIO 34--39Verify the pin in code matches your wiring; use an output-capable GPIO
LED burned outMissing or wrong resistor valueReplace the LED and add a $220\,\Omega$ resistor
LED is very dimResistor value too highUse a lower resistance (but not below the calculated minimum)
PWM fading not workingUsing analogWrite() instead of LEDCESP32 does not support analogWrite() -- use ledcAttach() and ledcWrite()
LED flickers at low brightnessPWM frequency too lowIncrease 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)