⚡ ESP32 GPIO Output & LED Blinking

A structured guide to understanding digital output pins, voltage logic levels, pin numbering, and how to drive one or several LEDs from your ESP32 board.

1. Introduction: What Does "Output" Mean on the ESP32?

Every GPIO (General Purpose Input/Output) pin on the ESP32 can be configured in software to behave either as an input (reading a signal) or an output (generating a signal). When a pin is configured as an output, the microcontroller can actively drive that pin to one of two voltage levels, which in turn can switch an LED on or off, or trigger any small electronic component.

You can use the ESP32 GPIO pins in output mode just to control anything ranging from a small LED all the way up to high-power devices through relays, SSR, thyristors, etc. In this guide we begin with the simplest case — blinking a single LED — before scaling up to controlling several LEDs simultaneously.

2. Pin Numbering: GPIO Number vs. Board Silkscreen Number

One of the most common points of confusion for beginners is that an ESP32 development board often has two different numbering systems printed near the pins:

For the ESP32, the safest and most reliable approach is to always program using the native GPIO number. The pin numbers are those indicated in the grey "GPIO" insert on the board's pinout, and even if there is a link between the number of GPIO pins of the ESP32 and those usually used on the Arduino, to avoid confusion, it is better not to use them and to use the native pins number of ESP32 exclusively — you should therefore avoid using labels like A0, MOSI, SCK, SDA.

Rule of thumb: When your code says pinMode(13, OUTPUT), the number 13 refers to GPIO13 on the chip — not necessarily the position of the pin on the board edge.

Not every GPIO can be freely used, though. For the ESP32 DEVKIT V1 board, GPIO6 to GPIO11 are connected to the integrated SPI flash and are not recommended for other uses. All GPIOs can be used as outputs except GPIOs 6 to 11 (connected to the integrated SPI flash) and GPIOs 34, 35, 36 and 39 (input‑only GPIOs).

3. Understanding Voltage Logic Levels (HIGH / LOW)

Digital electronics work with only two meaningful voltage states. High level (HIGH) represents the logical "1" or "true", and Low level (LOW) represents logical "0" or "false".

On the ESP32 specifically: on the ESP32 board, HIGH typically means that the pin output is close to 3.3V, and Low level typically means the pin outputs about 0 volts, which is connected to ground (GND).

Important: The ESP32 GPIO pins are not 5V tolerant — they are 3.3V for logic HIGH, and you cannot directly connect them to a 5V source such as an Arduino UNO without extra protection.
StateWritten in CodeApprox. VoltageEffect on LED
HIGHdigitalWrite(pin, HIGH)≈ 3.3 VLED on
LOWdigitalWrite(pin, LOW)≈ 0 VLED off

4. The Basic LED Circuit: Resistor + LED on a GPIO Pin

To light an LED safely from a GPIO pin, a current-limiting resistor is always placed in series with the LED. Below is a simplified schematic of the circuit connected to GPIO13, matching the typical layout found on many ESP32 dev boards.

ESP32 GPIO13 Resistor (220–330Ω) LED GND
Fig. 1 — GPIO13 drives current through a series resistor and an LED back to GND.

How Current Flows

When the GPIO outputs a high level (3.3V), current flows out from the pin, through the current-limiting resistor, through the LED, and back to the GND pin of the ESP32, forming a complete circuit loop.

Why the Resistor Is Necessary

The resistor is a current-limiting resistor that protects the LED by preventing excessive current from burning it out, and protects the ESP32 by preventing the GPIO pin from outputting excessive current. If a specific value isn't available, a resistor in the 220Ω–1kΩ range can be used instead.

Resistor placement doesn't matter electrically: Whether the resistor is placed before or after the LED in the series path, the same current flows through the entire loop when the circuit is closed. What truly matters is that the LED and resistor are both included somewhere in that single series path between the GPIO pin and GND.
Absolute current limit: The absolute maximum current drawn per GPIO is 40mA according to the "Recommended Operating Conditions" section in the ESP32 datasheet. Always size your resistor so the LED current stays safely below this limit.

5. Code Example: Blinking a Single LED

The Arduino-style workflow for any digital output pin always follows the same two-step pattern: first declare the pin as an output inside setup(), then toggle it HIGH or LOW inside loop().

You need to define the GPIO pin to operate in output mode in the setup() function using the pinMode() Arduino function, then you can drive the pin HIGH or LOW to change the digital state of that pin.

Sketch: LED on GPIO13

const int ledPin = 13;

void setup() {
  pinMode(ledPin, OUTPUT);
}

void loop() {
  digitalWrite(ledPin, HIGH);
  delay(1000);
  digitalWrite(ledPin, LOW);
  delay(1000);
}

Upload steps in the Arduino IDE, performed one action at a time:

Select the correct ESP32 board from Tools > Board
Select the correct COM port from Tools > Port
Save the sketch (File > Save)
Click the Upload button
Hold the BOOT button if prompted during flashing
Release BOOT once uploading begins
Wait for "Done uploading" in the console

This matches the standard flashing procedure: choose the board and COM port, hold down the BOOT button, click upload and keep the button pressed, then release it once the IDE starts sending the code and wait for flashing to complete.

6. Scaling Up: Controlling Multiple LEDs

Once a single LED works, the same principle extends naturally to several LEDs at once — each one simply needs its own GPIO pin, its own current-limiting resistor, and a shared connection back to GND.

A commonly used and readily accessible set of GPIOs for this kind of experiment on ESP32 dev boards is GPIO12, GPIO27, GPIO33 and GPIO15 — all confirmed usable as digital outputs. Similar four-LED demonstrations appear frequently in ESP32 tutorials, for example wiring four LEDs connected to pins D4, D0, D2, and D15 of ESP32.

ESP32 Multiple GPIOs GPIO12 GPIO27 GPIO33 GPIO15 Common GND
Fig. 2 — Four LEDs, each on its own GPIO with its own resistor, sharing one common ground return path to the ESP32.
Key wiring rule: Each LED's resistor connects to a different GPIO, but every LED cathode can be tied to the same ground rail, which then returns to a single GND pin on the ESP32.

7. Code Example: Blinking Multiple LEDs Together

To manage several pins cleanly, it is good practice to store the pin numbers in named variables (or an array) rather than repeating raw numbers throughout the code. This mirrors the standard multi-pin configuration pattern used in ESP32 tutorials, where several GPIOs are declared as outputs together:

void setup() { pinMode(0, OUTPUT); pinMode(4, OUTPUT); pinMode(2, OUTPUT); pinMode(15, OUTPUT); } is a typical way multiple pins are initialized before use.

Sketch: Four LEDs Blinking Together

const int led1 = 12;
const int led2 = 27;
const int led3 = 33;
const int led4 = 15;

void setup() {
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  pinMode(led3, OUTPUT);
  pinMode(led4, OUTPUT);
}

void loop() {
  digitalWrite(led1, HIGH);
  digitalWrite(led2, HIGH);
  digitalWrite(led3, HIGH);
  digitalWrite(led4, HIGH);
  delay(500);
  digitalWrite(led1, LOW);
  digitalWrite(led2, LOW);
  digitalWrite(led3, LOW);
  digitalWrite(led4, LOW);
  delay(500);
}

Every pin follows the exact same two rules used earlier for the single LED: it must be declared as OUTPUT in setup(), and it is switched using digitalWrite() in loop(). digitalWrite() sets an output pin to either HIGH (3.3V) or LOW (0V), exactly as it does for a single LED — simply repeated once per pin.

Variation: Different Blink Speed

Changing only the delay() value changes the blink rate for all four LEDs simultaneously, since they share the same timing in this simple synchronous loop:

delay(500);   // 500 ms ON
delay(500);   // 500 ms OFF
Beginner tip: Avoid retyping the same four lines repeatedly by hand for every pin — use copy‑paste carefully, or better, refactor the code later using an array and a for loop once you're comfortable with the basics.

8. Using a Prototyping / Breadboard Setup

For repeated experiments, many learners build a small reusable prototyping board that hosts an ESP32 alongside common peripherals — switches, a buzzer, an LCD, a real-time clock module, and a motor driver — all pre-wired to accessible header pins. Mounting the ESP32 on a breadboard makes it easy to pull individual GPIO wires out to LEDs, resistors, and other components without redoing the core wiring each time.

Practical workflow: Insert the ESP32 into a breadboard, connect a common ground rail to the board's GND pin, then run individual jumper wires from each output GPIO (e.g. 12, 27, 33, 15) to its own resistor‑LED pair on the breadboard.

9. Summary

With these fundamentals in place, the same digital output technique can be extended in future lessons to drive buzzers, relays, motor drivers, and other components — all following the identical HIGH/LOW output logic covered here.