Wire an LED to an ESP32 the right way: sizing the resistor with Ohm's law
Why an LED always needs a resistor, the step-by-step sum for an ESP32 (3.3 V) and an Arduino Uno (5 V), which GPIO pins are safe, and blink and button code.
- An LED always needs a current-limiting resistor, because the LED itself limits nothing.
- One formula: R = (supply voltage − LED forward voltage) ÷ the current you want.
- The ESP32 runs at 3.3 V, its pins are not 5 V tolerant, and some pins should not drive an LED.
- Measure the voltage across the resistor and you know the real current without breaking the circuit.
Almost everyone’s first microcontroller project is a blinking LED. It sounds trivial, but wire the LED straight to a GPIO pin with no resistor and you can damage both the LED and the pin. This article explains why, works out the resistor value step by step, and finishes with code that works.
The examples use an ESP32 DevKit board (ESP32-WROOM-32 module), programmed from Arduino IDE 2 with the arduino-esp32 core installed.
Why an LED cannot go straight on a pin
An LED is a kind of diode. Below a certain voltage almost no current flows. Once the voltage reaches that point, called the forward voltage (Vf), the current rises very steeply, and the LED does nothing to limit it.
Wired directly, the only thing limiting the current is the internal resistance of the GPIO pin. The LED runs above its rating and the chip’s pin supplies more than it was designed for. Sometimes nothing fails at once; the pin just degrades until one day it stops working.
The resistor in series with the LED is what sets the current. Without it, nothing does.
The one formula you need
The supply voltage splits in two: part across the LED, the rest across the resistor. With Ohm’s law (V = I × R):
R = (Vsupply − Vf) ÷ I
Vf depends on the LED colour. These are typical values for 5 mm LEDs; always check the datasheet of the LED you actually use.
| LED colour | Typical Vf |
|---|---|
| Red | 1.8–2.2 V |
| Yellow, orange | 2.0–2.2 V |
| Bright green, blue, white | 2.8–3.3 V |
A typical 5 mm LED is rated around 20 mA continuous, but an indicator only needs 5–10 mA to be clearly bright, and that is much kinder to the GPIO pin.
A worked example, step by step
The task: a red LED on an ESP32 GPIO pin, which outputs 3.3 V, at about 10 mA.
- Voltage left for the resistor = 3.3 − 2.0 = 1.3 V
- R = 1.3 ÷ 0.010 = 130 Ω
- 130 Ω is not in the common E12 series of standard values, so pick the next larger one: 150 Ω
- Actual current = 1.3 ÷ 150 ≈ 8.7 mA, a little under target, on the safe side
- Power in the resistor P = I² × R = 0.0087² × 150 ≈ 0.011 W; an ordinary 1/4 W resistor has plenty of margin
On an Arduino Uno, whose pins output 5 V, the numbers change:
| Board | Pin voltage | Calculated R (10 mA) | Chosen | Actual current |
|---|---|---|---|---|
| ESP32 | 3.3 V | 130 Ω | 150 Ω | ≈ 8.7 mA |
| Arduino Uno | 5 V | 300 Ω | 330 Ω | ≈ 9.1 mA |
ESP32 GPIO facts worth knowing
- The ESP32 runs at 3.3 V and its pins are not 5 V tolerant. Never feed a 5 V signal straight into a pin.
- The datasheet gives roughly 40 mA per pin at the highest drive strength. That is a ceiling, not a working value; about 10 mA is plenty for an LED.
- GPIO 6–11 connect to the flash inside WROOM modules. Do not use them.
- GPIO 34–39 are input-only and cannot drive an LED.
- GPIO 0, 2, 5, 12, 15 are strapping pins that affect how the board boots. Usable with care; beginners should avoid them for now.
This article uses GPIO18 for the LED and GPIO4 for the button. Neither has any of the limits above.
Wiring
- Unplug the USB cable before every change.
- GPIO18 → 150 Ω resistor → the LED’s long leg (anode, +).
- The LED’s short leg (cathode, −), on the side where the rim is flattened → GND.
- Button: one leg to GPIO4, the other to GND. No extra resistor: the code turns on the chip’s internal pull-up.
Code
Blink every half second:
const int LED_PIN = 18;
void setup() {
pinMode(LED_PIN, OUTPUT);
}
void loop() {
digitalWrite(LED_PIN, HIGH);
delay(500);
digitalWrite(LED_PIN, LOW);
delay(500);
}
Add the button: press for light, release for dark.
const int LED_PIN = 18;
const int BUTTON_PIN = 4;
void setup() {
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP); // released = HIGH, pressed = LOW
}
void loop() {
bool pressed = digitalRead(BUTTON_PIN) == LOW;
digitalWrite(LED_PIN, pressed ? HIGH : LOW);
}
INPUT_PULLUP switches on a resistor inside the chip that pulls the pin up to 3.3 V while the button is open, so it reads HIGH. Pressing connects the pin to ground and it reads LOW. The logic is inverted from what many people expect.
Measure the real current with a multimeter
No need to break the circuit for an ammeter. Set the multimeter to DC volts and measure across the resistor while the LED is on. If it reads 1.25 V, the current is 1.25 ÷ 150 ≈ 8.3 mA.
The reading is usually a little below the calculation, because the GPIO output voltage sags slightly under load and no two LEDs have exactly the same Vf. That is normal.
Common mistakes
- No resistor because “it lit up fine”. It does light, but the pin and the LED are over their ratings.
- LED the wrong way round. It stays dark but is not damaged. Flip it and try again.
- Using pins 34–39. The code is perfect and nothing lights, because those pins are input-only.
- Resistor in the wrong breadboard row. Both legs in the same row means the current bypasses the resistor entirely.
Next, the ESP32 reads a sensor over two I2C wires, and in the third article we print a case for it.
FAQ
Can I use 220 Ω instead of 150 Ω?
Yes. The current drops to about (3.3 − 2.0) ÷ 220 ≈ 5.9 mA, slightly dimmer but still clearly visible. A larger value is always the safer side.
Can several LEDs in parallel share one resistor?
Better not. Each LED has a slightly different forward voltage, and the one with the lowest takes almost all the current. Give each LED its own resistor.
Why are blue and white LEDs so dim on 3.3 V?
Blue and white LEDs drop about 2.8–3.3 V, which leaves almost nothing for the resistor. Drive them from 5 V through a transistor for steady brightness.
Circuits and code were tested on sample boards. Boards differ in pin layout, so check your board's pinout before you power anything.