Working with ESP-IDF
This chapter includes the following sections, please read as needed:
ESP-IDF Getting Started
New to ESP32 ESP-IDF development and looking to get started quickly? We have prepared a general Getting Started Tutorial for you.
- Section 1: Environment Setup
- Section 2: Running Examples
- Section 3: Creating a Project
- Section 4: Using Components
- Section 5: Debugging
- Section 6: FreeRTOS
- Section 7: Peripherals
- Section 8: Wi-Fi Programming
- Section 9: BLE Programming
Please Note: This tutorial uses the ESP32-S3-Zero as a teaching example, and all hardware code is based on its pinout. Before you start, it is recommended that you check the pinout of your development board to ensure the pin configuration is correct.
Setting Up the Development Environment
The ESP32-C5-Touch-LCD-1.69 example project requires ESP-IDF v5.3 or newer.
The following guide uses Windows as an example, demonstrating development using VS Code + the ESP-IDF extension. macOS and Linux users should refer to the official documentation.
The screenshots in this section use ESP-IDF V5.5.2 as an example. When installing, please select the ESP-IDF version that matches your board's example.
Install the ESP-IDF Development Environment
-
Download the installation manager from the ESP-IDF Installation Manager page. This is Espressif's latest cross-platform installer. The following steps demonstrate how to use its offline installation feature.
Click the Offline Installer tab on the page, then select Windows as the operating system and the ESP-IDF version you need (the version shown in the screenshot is for reference only — choose the version that fits your actual needs).

After confirming your selection, click the download button. The browser will automatically download two files: the ESP-IDF Offline Package (.zst) and the ESP-IDF Installer (.exe).

Please wait for both files to finish downloading.
-
Once the download is complete, double-click to run the ESP-IDF Installer (eim-gui-windows-x64.exe).
The installer will automatically detect if the offline package exists in the same directory. Click Install from archive.

Next, select the installation path. We recommend using the default path. If you need to customize it, ensure the path does not contain Chinese characters or spaces. Click Start installation to proceed.

-
When you see the following screen, the ESP-IDF installation is successful.

-
We recommend installing the drivers as well. Click Finish installation, then select Install driver.

Install Visual Studio Code and the ESP-IDF Extension
-
Download and install Visual Studio Code.
-
During installation, it is recommended to check Add "Open with Code" action to Windows Explorer file context menu to facilitate opening project folders quickly.
-
In VS Code, click the Extensions icon
in the Activity Bar on the side (or use the shortcut Ctrl + Shift + X) to open the Extensions view.
-
Enter ESP-IDF in the search box, locate the ESP-IDF extension, and click Install.

-
For ESP-IDF extension versions ≥ 2.0, the extension will automatically detect and recognize the ESP-IDF environment installed in the previous steps, requiring no manual configuration.
If installation fails or a reinstall is needed, you can try deleting the C:\Users\%Username%\esp and C:\Users\%Username%\.espressif folders and then retry.
Building and Flashing
Navigate to the ESP-IDF example project directory and run:
cd example/esp-idf
idf.py build flash monitor
If you need to specify a serial port, replace COMx with the actual port, for example COM5:
idf.py -p COMx build flash monitor
Example
ESP-IDF examples are located in the example/esp-idf/main/examples directory, and the project entry is example/esp-idf/main/main.c. At any time, keep only one example or application macro set to 1, and set all others to 0.
#define EXAMPLE_RGB_TEST 1
#define EXAMPLE_MIC_SPEAKER_TEST 0
#define EXAMPLE_IMU_TEST 0
#define EXAMPLE_BAT_TEST 0
#define EXAMPLE_RTC_TEST 0
#define EXAMPLE_LVGL_DEMO_TEST 0
#define EXAMPLE_Brookesia_TEST 0
#define APPS_WIFI_Connect 0
#define APPS_Clock 0
#define APPS_Honeycomb_Demo 0
By default, the RGB color cycling test runs. To run another example, change the corresponding macro to 1 and set all others to 0.
| Example | Basic Description |
|---|---|
| 01_RGB_Test | Screen color cycling test |
| 02_Mic_Speaker_Test | Microphone and speaker test |
| 03_IMU_Test | IMU test |
| 04_Bat_Test | Battery detection test |
| 05_RTC_Test | RTC test |
| 06_LVGL_Demo_Test | LVGL example test |
| 07_Brookesia_Test | Brookesia example test |
| 08_WIFI_Connect | Wi-Fi provisioning application |
| 09_Clock_Display | Clock display application |
| 10_Honeycomb_Demo | Honeycomb icon interactive demonstration |
01_RGB_Test
Hardware Connection
- Connect the development board to a computer using a USB cable.
Code Analysis
RGB color cycling test code
void rgb_test_run(void)
{
static const uint16_t colors[] = { RGB565_RED, RGB565_GREEN, RGB565_BLUE };
static const char *color_names[] = { "red", "green", "blue" };
bsp_display_cfg_t display_cfg = {0};
esp_lcd_panel_handle_t panel = NULL;
uint16_t *draw_buffer = NULL;
size_t color_index = 0;
ESP_ERROR_CHECK(bsp_display_new(&display_cfg, &panel, NULL));
ESP_ERROR_CHECK(bsp_display_brightness_init());
ESP_ERROR_CHECK(bsp_display_brightness_set(100));
draw_buffer = heap_caps_malloc(BSP_LCD_H_RES * RGB_TEST_BLOCK_LINES * sizeof(uint16_t), MALLOC_CAP_DMA);
while (true) {
ESP_LOGI(TAG, "show color: %s", color_names[color_index]);
rgb_test_draw_color(panel, draw_buffer, colors[color_index]);
vTaskDelay(pdMS_TO_TICKS(RGB_TEST_DELAY_MS));
color_index++;
if (color_index >= (sizeof(colors) / sizeof(colors[0]))) {
color_index = 0;
}
}
}
Code Explanation
bsp_display_new(&display_cfg, &panel, NULL): Initializes the LCD panel and returns a panel handle.bsp_display_brightness_init()/bsp_display_brightness_set(100): Initializes and sets backlight brightness to 100%.heap_caps_malloc(..., MALLOC_CAP_DMA): Allocates a DMA‑compatible draw buffer for bulk pixel transfers.rgb_test_draw_color(panel, draw_buffer, colors[color_index]): Writes the specified color to the LCD in 20‑line blocks, refreshing the entire screen block by block.vTaskDelay(pdMS_TO_TICKS(RGB_TEST_DELAY_MS)): Each color stays for 1 second.
Expected Behavior
- The screen displays red, green, and blue in sequence, each for 1 second.
- The serial port outputs the current color name every second.
02_Mic_Speaker_Test
Hardware Connection
- Connect the development board to a computer using a USB cable.
Code Analysis
Microphone loopback playback code
void mic_speaker_test_run(void)
{
uint8_t audio_buffer[MIC_SPEAKER_TEST_FRAME_BYTES];
esp_codec_dev_handle_t speaker = NULL;
esp_codec_dev_handle_t microphone = NULL;
esp_codec_dev_sample_info_t codec_fs = {
.sample_rate = BSP_AUDIO_OUTPUT_SAMPLE_RATE_HZ,
.bits_per_sample = 16,
.channel = 1,
};
ESP_ERROR_CHECK(bsp_audio_init(NULL));
speaker = bsp_audio_codec_speaker_init();
microphone = bsp_audio_codec_microphone_init();
ESP_ERROR_CHECK(esp_codec_dev_open(speaker, &codec_fs));
ESP_ERROR_CHECK(esp_codec_dev_set_out_vol(speaker, MIC_SPEAKER_TEST_VOLUME));
ESP_ERROR_CHECK(esp_codec_dev_set_in_gain(microphone, MIC_SPEAKER_TEST_GAIN));
ESP_LOGI(TAG, "microphone loopback to speaker");
while (true) {
ESP_ERROR_CHECK(esp_codec_dev_read(microphone, audio_buffer, sizeof(audio_buffer)));
ESP_ERROR_CHECK(esp_codec_dev_write(speaker, audio_buffer, sizeof(audio_buffer)));
}
}
Code Explanation
bsp_audio_init(NULL): Initializes the I2S bus and ES8311 codec.bsp_audio_codec_speaker_init()/bsp_audio_codec_microphone_init(): Initializes the speaker and microphone codec devices, respectively.esp_codec_dev_open(speaker, &codec_fs): Opens the speaker device in 16‑bit mono format.esp_codec_dev_set_out_vol(speaker, 70): Sets speaker volume to 70.esp_codec_dev_set_in_gain(microphone, 18): Sets microphone gain to 18dB.esp_codec_dev_read(...)/esp_codec_dev_write(...): Reads 1024 bytes from the microphone and writes them to the speaker for real‑time loopback.
Expected Behavior
- Serial output shows
microphone loopback to speaker. - Speaking into the microphone allows real-time playback from the speaker.
03_IMU_Test
Hardware Connection
- Connect the development board to a computer using a USB cable.
Code Analysis
QMI8658 data reading code
void imu_test_run(void)
{
qmi8658_data_t data = {0};
ESP_ERROR_CHECK(bsp_i2c_init());
ESP_ERROR_CHECK(bsp_qmi8658_init());
while (true) {
ESP_ERROR_CHECK(bsp_qmi8658_get_data(&data));
ESP_LOGI(TAG, "accel: %.2f %.2f %.2f m/s2", data.accelX, data.accelY, data.accelZ);
ESP_LOGI(TAG, "gyro: %.2f %.2f %.2f rad/s", data.gyroX, data.gyroY, data.gyroZ);
ESP_LOGI(TAG, "temp: %.2f C", data.temperature);
vTaskDelay(pdMS_TO_TICKS(IMU_TEST_DELAY_MS));
}
}
Code Explanation
bsp_i2c_init(): Initializes the I2C bus (SDA=GPIO8, SCL=GPIO9, 400kHz).bsp_qmi8658_init(): Initializes the QMI8658 6‑axis sensor at I2C address 0x6B.bsp_qmi8658_get_data(&data): Reads accelerometer, gyroscope, and temperature data into theqmi8658_data_tstructure.ESP_LOGI(TAG, ...): Outputs accelerometer (m/s²), gyroscope (rad/s), and temperature (°C) via serial.vTaskDelay(pdMS_TO_TICKS(IMU_TEST_DELAY_MS)): Reads data at the interval defined by the macro.
Expected Behavior
- The serial port outputs accelerometer, gyroscope, and temperature data every 500ms.
- Tilting or rotating the board causes the data to change accordingly.

04_Bat_Test
Hardware Connection
- Connect the development board to a computer using a USB cable.
- Connect a Lithium battery to the development board.
Code Analysis
Battery information reading and display code
void bat_test_run(uint16_t battery_capacity_mah)
{
bsp_bat_info_t bat_info = {0};
lv_display_t *display = NULL;
lv_obj_t *label = NULL;
display = bsp_display_start();
ESP_ERROR_CHECK(bsp_display_brightness_set(100));
ESP_ERROR_CHECK(bsp_bat_init(battery_capacity_mah));
ESP_ERROR_CHECK(bsp_display_lock(0));
label = lv_label_create(lv_screen_active());
lv_obj_align(label, LV_ALIGN_TOP_LEFT, 10, 10);
bsp_display_unlock();
while (true) {
ret = bsp_get_bat_info(&bat_info);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "battery info update failed: %s", esp_err_to_name(ret));
continue;
}
battery_state = (bat_info.ma > 0) ? "Charging" : ((bat_info.ma < 0) ? "Discharging" : "Idle");
ESP_ERROR_CHECK(bsp_display_lock(0));
lv_label_set_text_fmt(label,
"Battery Test\n"
"State: %s\n"
"Voltage: %u mV\n"
"Current: %d mA\n"
"SOC: %u %%\n"
"Temp: %u C\n"
"Capacity: %u mAh\n"
"%s",
battery_state,
bat_info.mv,
bat_info.ma,
bat_info.soc,
bat_info.tc,
battery_capacity_mah,
battery_note);
bsp_display_unlock();
vTaskDelay(pdMS_TO_TICKS(BAT_TEST_DELAY_MS));
}
}
Code Explanation
bsp_display_start(): Starts the LVGL display.bsp_bat_init(battery_capacity_mah): Initializes the BQ27220 fuel gauge with the battery capacity (passed as parameter, default 1000mAh).bsp_get_bat_info(&bat_info): Reads battery information (voltage/current/SOC/temperature/capacity, etc.).bsp_display_lock(0)/bsp_display_unlock(): Acquires/releases the LVGL mutex for thread‑safe UI operations.lv_label_set_text_fmt(label, ...): Formats and updates the battery information label, including state, voltage, current, SOC, temperature, and capacity.
Expected Behavior
- The screen displays battery status information: State (Charging/Discharging/Idle), Voltage (mV), Current (mA), SOC (%), Temperature (°C), Capacity (mAh).
- Serial output also shows battery data.
- When a battery is connected and current is non-zero, the state shows Charging or Discharging; when no battery is connected or fully charged, it shows Idle.

05_RTC_Test
Hardware Connection
- Connect the development board to a computer using a USB cable.
Code Analysis
RTC initialization and time reading code
void rtc_test_run(void)
{
char datetime_str[32];
pcf85063a_datetime_t time = {
.year = 2026,
.month = 1,
.day = 1,
.dotw = 4,
.hour = 12,
.min = 0,
.sec = 0,
};
ESP_ERROR_CHECK(bsp_rtc_init());
ESP_ERROR_CHECK(bsp_set_rtc_time_date(time));
while (true) {
ESP_ERROR_CHECK(bsp_get_rtc_time_date(&time));
ESP_ERROR_CHECK(bsp_datetime_to_str(datetime_str, sizeof(datetime_str), time));
ESP_LOGI(TAG, "rtc time: %s", datetime_str);
vTaskDelay(pdMS_TO_TICKS(RTC_TEST_DELAY_MS));
}
}
Code Explanation
bsp_rtc_init(): Initializes the PCF85063A RTC chip.bsp_set_rtc_time_date(time): Sets the initial RTC time (example sets 2026‑01‑01 12:00:00).bsp_get_rtc_time_date(&time): Reads the current time from the RTC into thepcf85063a_datetime_tstructure.bsp_datetime_to_str(datetime_str, ...): Converts the time structure to a string for display.vTaskDelay(pdMS_TO_TICKS(RTC_TEST_DELAY_MS)): Reads time at the interval defined by the macro.
Expected Behavior
- The serial port outputs
rtc time: YYYY-MM-DD HH:MM:SSevery second.

06_LVGL_Demo_Test
Hardware Connection
- Connect the development board to a computer using a USB cable.
Code Analysis
LVGL Widgets Example Code
void lvgl_test_run(void)
{
lv_display_t *display = NULL;
display = bsp_display_start();
ESP_ERROR_CHECK(display ? ESP_OK : ESP_FAIL);
ESP_ERROR_CHECK(bsp_display_brightness_set(100));
ESP_ERROR_CHECK(bsp_display_lock(0));
/* Running Widgets Demo */
lv_demo_widgets();
bsp_display_unlock();
ESP_LOGI(TAG, "LVGL demo started");
while (true) {
vTaskDelay(pdMS_TO_TICKS(LVGL_TEST_DELAY_MS));
}
}
Code Explanation
bsp_display_start(): Starts the LVGL display, initializing the LCD panel and LVGL core.bsp_display_brightness_set(100): Sets backlight brightness to 100%.bsp_display_lock(0)/bsp_display_unlock(): Acquires/releases the LVGL mutex for thread‑safe UI operations.lv_demo_widgets(): Starts the LVGL Widgets example, showcasing common widgets such as buttons, sliders, switches, and charts.
Expected Behavior
- The screen displays the LVGL Widgets example interface with buttons, sliders, switches, charts, and other widgets.
- Touch interaction with the widgets is supported.

07_Brookesia_Test
Hardware Connection
- Connect the development board to a computer using a USB cable.
Code Analysis
Brookesia Phone initialization code
bool init_phone_system(void)
{
ESP_Brookesia_PhoneStylesheet_t *stylesheet = nullptr;
bsp_display_lock(0);
phone = new (std::nothrow) ESP_Brookesia_Phone(display);
stylesheet = new (std::nothrow) ESP_Brookesia_PhoneStylesheet_t(ESP_BROOKESIA_PHONE_DEFAULT_DARK_STYLESHEET());
stylesheet->core.manager.flags.enable_app_save_snapshot = 0;
stylesheet->core.manager.app.max_running_num = 1;
stylesheet->home.flags.enable_recents_screen = 0;
phone->addStylesheet(stylesheet);
phone->activateStylesheet(stylesheet);
phone->setTouchDevice(bsp_display_get_input_dev());
phone->registerLvLockCallback(phone_lvgl_lock, 0);
phone->registerLvUnlockCallback(phone_lvgl_unlock);
phone->begin();
phone->installApp(&minimal_app);
bsp_display_unlock();
return true;
}
Code Explanation
ESP_Brookesia_Phone(display): Creates a Brookesia Phone instance bound to the LVGL display.ESP_Brookesia_PhoneStylesheet_t(...): Uses the default dark theme stylesheet.phone->addStylesheet(stylesheet)/phone->activateStylesheet(stylesheet): Adds and activates the stylesheet.phone->setTouchDevice(bsp_display_get_input_dev()): Sets the touch input device.phone->registerLvLockCallback(...)/phone->registerLvUnlockCallback(...): Registers LVGL lock callbacks for thread safety.phone->begin(): Starts the Phone UI framework.phone->installApp(&minimal_app): Installs a minimal example app (displays "Hello Brookesia" text).
Expected Behavior
- The screen shows the Brookesia Phone interface with a status bar and app launcher.
- Tapping the app icon opens the "Hello Brookesia Minimal App" page.

08_WIFI_Connect
Hardware Connection
- Connect the development board to a computer using a USB cable.
Code Analysis
Wi-Fi AP and Captive Portal code
void wifi_connect_test_run(void)
{
lv_display_t *display = NULL;
wifi_connect_init_stack();
wifi_connect_start_network();
ESP_ERROR_CHECK(bsp_display_set_partial_mode(true, 40));
display = bsp_display_start();
ESP_ERROR_CHECK(bsp_display_brightness_set(100));
ESP_ERROR_CHECK(bsp_display_lock(0));
wifi_connect_create_screen();
bsp_display_unlock();
while (true) {
vTaskDelay(pdMS_TO_TICKS(WIFI_CONNECT_IDLE_MS));
}
}
Code Explanation
wifi_connect_init_stack(): Initializes NVS, network interface, and the default event loop.wifi_connect_start_network(): Creates an AP (SSID based on MAC address), starts HTTP and DNS servers for Captive Portal.bsp_display_set_partial_mode(true, 40): Sets LVGL to partial refresh mode to reduce memory usage.wifi_connect_create_screen(): Creates a provisioning screen with a QR code and status bar; the QR code contains the AP's SSID and password.- After the user's phone connects to the AP, the provisioning page automatically appears; after entering home Wi‑Fi credentials, the device connects automatically.
Expected Behavior
- The screen displays a QR code and AP information; scanning the QR code and connecting to the AP automatically opens the provisioning page.
- Entering the home Wi‑Fi SSID and password on the provisioning page allows the device to connect to Wi‑Fi and display connection status and IP address on the screen.

09_Clock_Display
Hardware Connection
- Connect the development board to a computer using a USB cable.
Code Analysis
Clock application initialization code
void clock_test_run(void)
{
lv_display_t *display = NULL;
ESP_ERROR_CHECK(bsp_i2c_init());
ESP_ERROR_CHECK(bsp_rtc_init());
ESP_ERROR_CHECK(bsp_display_set_partial_mode(true, 40));
display = bsp_display_start();
ESP_ERROR_CHECK(bsp_display_brightness_set(100));
ESP_ERROR_CHECK(bsp_display_lock(0));
clock_app_create_screen();
bsp_display_unlock();
while (true) {
vTaskDelay(pdMS_TO_TICKS(CLOCK_APP_IDLE_MS));
}
}
Code Explanation
bsp_i2c_init()/bsp_rtc_init(): Initializes the I2C bus and the PCF85063A RTC, providing the time source for the clock.bsp_display_set_partial_mode(true, 40): Sets LVGL to partial refresh mode to reduce memory usage.bsp_display_start(): Starts the LVGL display.bsp_display_brightness_set(100): Sets backlight brightness to 100%.bsp_display_lock(0)/bsp_display_unlock(): Acquires/releases the LVGL mutex for thread‑safe UI operations.clock_app_create_screen(): Creates the clock interface, loads the Classic watch face, and starts a timer to refresh the hands.vTaskDelay(pdMS_TO_TICKS(CLOCK_APP_IDLE_MS)): Idles at the interval defined by the macro.
Expected Behavior
- The screen displays an analog clock face with hour, minute, and second hands moving in real time according to the RTC.
10_Honeycomb_Demo
Hardware Connection
- Connect the development board to a computer using a USB cable.
Code Analysis
Honeycomb icon layout code
void honeycomb_demo_run(void)
{
lv_display_t *display = NULL;
display = bsp_display_start();
ESP_ERROR_CHECK(bsp_display_brightness_set(100));
ESP_ERROR_CHECK(bsp_display_lock(0));
honeycomb_demo_create_screen();
bsp_display_unlock();
while (true) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
static void honeycomb_demo_drag_event_cb(lv_event_t *event)
{
lv_indev_t *indev = lv_indev_active();
lv_point_t vector = {0};
lv_indev_get_vect(indev, &vector);
honeycomb_offset.x += vector.x;
honeycomb_offset.y += vector.y;
honeycomb_demo_refresh_layout();
}
Code Explanation
bsp_display_start(): Starts the LVGL display.honeycomb_demo_create_screen(): Creates a honeycomb layout with 25 circular icons arranged in a 5‑column honeycomb grid.honeycomb_demo_drag_event_cb(...): Touch‑drag callback that updates icon positions based on finger sliding offset.honeycomb_demo_refresh_layout(): Refreshes the icon layout, scaling each icon according to its distance from the screen center (closer = larger).honeycomb_demo_calculate_scale(distance): Calculates the icon scale using a quadratic decay function to achieve a fisheye magnifying effect.
Expected Behavior
- The screen displays colorful circular icons in a honeycomb arrangement, with the center icon enlarged and edge icons shrunk.
- Dragging with a finger pans the icon array; icons scale dynamically with position, creating a fisheye interaction effect.
