Getting started with ESP32: blink an LED

Set up your toolchain, understand the pinout and write your first ESP32 program by blinking an LED. With Arduino and with ESP-IDF.

Also available in: EN, ES

Series Introduction to the ESP32 Part 19 View the series →
Diagram of an LED connected to an ESP32

The hardware Hello World is blinking an LED. It is a simple ritual, but it touches every important part of a new board: power, toolchain, GPIO and flashing.

What you need

ItemDetail
BoardESP32 DevKitC, ESP32-WROOM-32 or similar
LEDAny color, ~2 V forward drop
Resistor220 Ω to 330 Ω
SoftwarePlatformIO (recommended) or Arduino IDE 2.x

Tip: many DevKits already ship an LED on GPIO2. You can use it to test without wiring anything, though it is usually inverted.

The pinout in two minutes

The ESP32 has GPIOs numbered 0 to 39, but not all are usable:

  • GPIO6GPIO11 are wired to the SPI flash. Do not touch them.
  • GPIO34GPIO39 are input only (no internal pull-up).
  • GPIO1 (TX0) and GPIO3 (RX0) are used by the serial port.
  • GPIO0 selects the boot mode; avoid using it as an output.

For an LED, any output GPIO works. We will use GPIO2.

Arduino code

The Arduino core makes startup trivial:

#include <Arduino.h>

constexpr int LED_PIN = 2;

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

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

delay() blocks (and sometimes that matters)

delay() halts the whole CPU. For a blink it is fine, but as soon as you add Wi-Fi or several sensors you will want millis():

unsigned long last = 0;
constexpr unsigned long INTERVAL = 1000;

void loop() {
  if (millis() - last >= INTERVAL) {
    last = millis();
    digitalWrite(LED_PIN, !digitalRead(LED_PIN));
  }
}

The same in ESP-IDF (Rust)

If you prefer Rust, the esp-idf-hal crate offers a typed API. A blink with esp-idf-svc looks like this:

use esp_idf_svc::hal::delay::FreeRtos;
use esp_idf_svc::hal::gpio::PinDriver;
use esp_idf_svc::hal::peripherals::Peripherals;

fn main() -> anyhow::Result<()> {
    esp_idf_svc::sys::link_patches();
    let peripherals = Peripherals::take()?;
    let mut led = PinDriver::output(peripherals.pins.gpio2)?;

    loop {
        led.set_high()?;
        FreeRtos::delay_ms(1000);
        led.set_low()?;
        FreeRtos::delay_ms(1000);
    }
}

The big difference: in Rust the compiler forces you to declare the pin as an output before writing to it. Many hardware mistakes vanish at compile time.

PlatformIO setup

A minimal platformio.ini for the Arduino core:

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200

And for Rust with esp-idf:

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = espidf
monitor_speed = 115200

Troubleshooting

  1. It will not flash / board not found. Install the cp210x or ch34x drivers matching your board’s USB chip.
  2. It flashes but does not blink. Check the LED polarity and the resistor.
  3. It reboots in a loop. Usually a power issue: use a USB port that can deliver at least 500 mA.

Next step

With GPIO under control you have the essentials. The natural next jump is connectivity (Wi-Fi, MQTT), which we will cover on the blog. For now you can move on to serial monitor and debugging or go back to the series hub.

Related posts

↑↓ navigate ↵ open Esc close