ESP32 — ESP-IDF v6

Your First ESP-IDF Project

Now that your ESP-IDF environment is set up, let's build your first real project: a Hello World that prints to the serial monitor and blinks an LED using a FreeRTOS task.


Prerequisites

Before continuing, make sure you have completed the setup guide for your OS:

And that idf.py --version returns ESP-IDF v6.x.x in your terminal.


Step 1 — Activate the ESP-IDF Environment

Every time you open a new terminal, activate the environment first.

Windows (PowerShell):

powershell
. $env:USERPROFILE\.espressif\esp-idf-v6.0\export.ps1

Ubuntu / macOS:

shell
get_idf   # If you set up the alias
# OR
. $HOME/.espressif/esp-idf-v6.0/export.sh

Step 2 — Create a New Project

shell
# Create a new minimal project
idf.py create-project hello_world
cd hello_world

This creates the following structure:

text
hello_world/
├── CMakeLists.txt          # Top-level build file
├── main/
│   ├── CMakeLists.txt      # Component build file
│   └── hello_world_main.c  # Your application code
└── sdkconfig               # (created after first build)

Step 3 — Write the Application

Open main/hello_world_main.c in VS Code and replace its contents with:

c
1#include <stdio.h>
2#include "freertos/FreeRTOS.h"
3#include "freertos/task.h"
4#include "esp_log.h"
5#include "driver/gpio.h"
6
7static const char *TAG = "hello_world";
8
9// Change this to your board's LED pin
10// ESP32 DevKit v1: GPIO 2
11// ESP32-S3 DevKitC: GPIO 48
12#define BLINK_GPIO 2
13
14void app_main(void)
15{
16 // Configure LED GPIO as output
17 gpio_reset_pin(BLINK_GPIO);
18 gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
19
20 ESP_LOGI(TAG, "Hello from Analog Data!");
21 ESP_LOGI(TAG, "ESP-IDF version: %s", esp_get_idf_version());
22
23 int count = 0;
24 while (1) {
25 ESP_LOGI(TAG, "Blink count: %d", count++);
26
27 // LED ON
28 gpio_set_level(BLINK_GPIO, 1);
29 vTaskDelay(pdMS_TO_TICKS(500));
30
31 // LED OFF
32 gpio_set_level(BLINK_GPIO, 0);
33 vTaskDelay(pdMS_TO_TICKS(500));
34 }
35}

Step 4 — Set the Target Chip

shell
# For standard ESP32 DevKit
idf.py set-target esp32

# For ESP32-S3
idf.py set-target esp32s3

# For ESP32-C6
idf.py set-target esp32c6

This creates or updates the sdkconfig file with chip-specific settings.


Step 5 — (Optional) Configure the Project

shell
idf.py menuconfig

This opens a terminal-based configuration menu. For now, you can leave everything at defaults and press Q then Y to save and exit.

Useful settings to explore later:

  • Component config → ESP System Settings — CPU frequency, logging level
  • Partition Table — Flash partitioning
  • Serial flasher config — Default flash size and port

Step 6 — Build the Project

shell
idf.py build

The first build takes 3–10 minutes as it compiles all ESP-IDF libraries. Subsequent builds are incremental and much faster.

A successful build ends with:

text
[100%] Linking CXX executable hello_world.elf
esptool.py v4.x.x
...
Project build complete. To flash, run:
 idf.py flash
or
 idf.py -p PORT flash

Step 7 — Flash and Monitor

Connect your ESP32 via USB and identify the port:

OSFind port with
WindowsDevice Manager → COM ports
Ubuntuls /dev/ttyUSB*
macOSls /dev/cu.*

Flash and open the serial monitor:

shell
# Windows
idf.py -p COM3 flash monitor

# Ubuntu / macOS
idf.py -p /dev/ttyUSB0 flash monitor

You should see the LED on your board blinking and the serial output:

text
I (342) hello_world: Hello from Analog Data!
I (342) hello_world: ESP-IDF version: v6.0.0
I (352) hello_world: Blink count: 0
I (852) hello_world: Blink count: 1
I (1352) hello_world: Blink count: 2
...

Exit the monitor: Press Ctrl + ]


Understanding the Project Structure

CMakeLists.txt (root)

cmake
1cmake_minimum_required(VERSION 3.24)
2include($ENV{IDF_PATH}/tools/cmake/project.cmake)
3project(hello_world)

This tells CMake where to find ESP-IDF and sets your project name.

main/CMakeLists.txt

cmake
1idf_component_register(SRCS "hello_world_main.c"
2 INCLUDE_DIRS ".")

This registers your source files as a component in the build system.

app_main() — The Entry Point

Unlike Arduino's setup() and loop(), ESP-IDF uses app_main() as the entry point. It runs in a FreeRTOS task with a configurable stack size.

c
1void app_main(void) {
2 // Your code here
3 // This function can create FreeRTOS tasks and return
4}

Next Steps

Previous
macOS Setup