ESP32: Pure ESP-IDF Blink is a runnable microcontroller simulation example on SensBench — open the wired circuit, inspect the code, and compile real firmware in your browser, no install needed. Last updated Aug 14, 2026.

FREE SIMULATOR EXAMPLE · INTERMEDIATE

ESP32: Pure ESP-IDF Blink

This example blinks the ESP32’s onboard LED on GPIO2 with zero Arduino code: a pure ESP-IDF project built on app_main(), the GPIO driver API and FreeRTOS delays. Press play and the LED toggles once a second while tagged, leveled log lines scroll through the monitor. It is the smallest possible program that proves you can live without setup() and loop().

Board ESP32 DevKit (Xtensa LX6, 240 MHz)
Difficulty intermediate
Category basics
Libraries None — pure ESP-IDF project, no Arduino core
Runs in Sensbench browser simulator — free, no account
ESP32 DevKit blinking its onboard LED on GPIO2 from a pure ESP-IDF C project in the Sensbench simulator

The exact circuit you get when you open this example — already wired, ready to run.

The sketch

// Pure ESP-IDF blink — no Arduino core, just IDF APIs.
// app_main() is the ESP-IDF entry point (instead of setup()/loop()).
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"

#define LED_PIN GPIO_NUM_2

static const char *TAG = "blink";

void app_main(void)
{
    ESP_LOGI(TAG, "ESP-IDF blink starting");
    gpio_reset_pin(LED_PIN);
    gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);

    int level = 0;
    while (1) {
        level = !level;
        gpio_set_level(LED_PIN, level);
        ESP_LOGI(TAG, "LED %s", level ? "on" : "off");
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

How it works

Start at the entry point, because that is the whole difference. app_main() is where ESP-IDF hands you control — it runs as a FreeRTOS task and is even allowed to return. Arduino’s setup() and loop() are not something the silicon needs; the Arduino core defines its own app_main() and calls your two functions from it. Writing IDF means taking that layer back: no hidden loop is calling you, so this sketch makes its own with while (1).

The GPIO calls are the un-wrapped versions of pinMode() and digitalWrite(): gpio_reset_pin() returns GPIO2 to its power-on state, clearing whatever the bootloader left configured, gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT) sets the pad direction, and gpio_set_level() drives it. More typing, but every decision is now explicit — pull-ups, open-drain and drive strength become options you choose instead of defaults you never saw.

Two more IDF habits worth stealing. vTaskDelay(pdMS_TO_TICKS(1000)) yields the task for a second — it is what Arduino’s delay() calls underneath, except here you say so and the scheduler is free to run something else meanwhile. And ESP_LOGI(TAG, ...) replaces Serial.println() with leveled logging: every line carries a severity and the “blink” tag, and IDF can filter output by tag and level at runtime — try doing that with print statements.

Before you build it for real

The simulator blinks happily where real silicon argues. GPIO2 is a strapping pin: the bootloader samples it at reset when picking the boot mode, and the safe rule is that nothing external may hold it low — an LED is a harmless load, but a button wired to GND is the classic way to make a board refuse to boot with an error message that never mentions the pin. Second, vTaskDelay() takes ticks, not milliseconds: always wrap the number in pdMS_TO_TICKS() exactly like this sketch does, because the tick rate is a sdkconfig option and code passing a raw 1000 blinks at a different speed on every differently-configured project.

Get the parts

Related examples

FAQ

Where did setup() and loop() go?

They were never part of the chip — they are Arduino core conventions, called from an app_main() the core defines for you. In ESP-IDF you write app_main() yourself and structure the program however you like: one loop, a set of FreeRTOS tasks, or code that runs once and returns.

Can I mix IDF calls into an Arduino sketch?

Yes. Arduino-ESP32 is built on top of ESP-IDF, so gpio_set_level(), ESP_LOGI() and the rest of the IDF API are all callable from a .ino file. Plenty of projects start as Arduino and drop in IDF calls exactly where they need the control.