- ❤️ 博客主页 单片机菜鸟哥,一个野生非专业硬件IOT爱好者 ❤️
- ❤️ 本篇创建记录 2025-08-31 ❤️
- ❤️ 本篇更新记录 2025-08-31 ❤️
- 🎉 欢迎关注 🔎点赞 👍收藏 ⭐️留言📝
- 🙏 此博客均由博主单独编写,不存在任何商业团队运营,如发现错误,请留言轰炸哦!及时修正!感谢支持!
快速导读
1.前言
在前面我们成功运行了 ESP32-IDF开发之旅 基础篇⑤ 第一个基础代码:ESP32S3 Blink点灯 (当然,如果不成功,那就再运行一遍),作为初学者,可能对整个代码结构以及构建过程需要有一点了解,以便后续更加复杂的功能能够得心应手。所以我们直接看一下官方文档。
任意一个ESP-IDF 项目可以看作是多个不同组件的集合:
ESP-IDF 基础库,包括 libc、ROM bindings 等Wi-Fi驱动TCP/IP协议栈FreeRTOS操作系统- 项目相关的
传感器驱动(比如DHT11、DS18B20、OLED等等),根据自己的项目进行添加 - 负责将上述组件整合到一起的主程序,也叫做
main
注意:
- 项目 特指一个目录,其中包含了构建 应用程序 (即可执行文件)所需的
全部文件和配置,还包含了其他支持型文件,例如分区表、数据分区或文件系统分区,以及引导加载程序。- 项目配置 保存在项目根目录下名为
sdkconfig的文件中,可以通过idf.py menuconfig进行修改,且一个项目只能包含一个项目配置。
- 应用程序 是由 ESP-IDF 构建得到的
可执行文件。一个项目通常会构建两个应用程序:项目应用程序(可执行的主文件,即用户自定义的固件)和引导加载程序(启动并初始化项目应用程序)。
- 组件 是模块化且独立的代码,会被编译成静态库(.a 文件)并链接到应用程序。部分组件由 ESP-IDF 官方提供,其他组件则来源于其它开源项目。
- 目标 特指运行构建后应用程序的硬件设备。运行
idf.py --list-targets可以查看当前 ESP-IDF 版本中支持目标的完整列表。
idf.py 命令行工具提供了一个前端,可以帮助你轻松管理项目的构建过程,它管理了以下工具:
CMake,配置待构建的项目,负责配置Ninja,用于构建项目,负责编译生成esptool.py,烧录目标硬件设备,负责烧录
可通过 idf.py 配置构建系统,具体可参考 IDF 前端工具。
2. 项目的目录树结构
- myProject/
- CMakeLists.txt
- sdkconfig
- dependencies.lock
- bootloader_components/ - boot_component/ - CMakeLists.txt
- Kconfig
- src1.c
- components/ - component1/ - CMakeLists.txt
- Kconfig
- src1.c
- component2/ - CMakeLists.txt
- Kconfig
- src1.c
- include/ - component2.h
- managed_components/ - namespace__component-name/ - CMakelists.txt
- src1.c
- idf_component.yml
- include/ - src1.h
- main/ - CMakeLists.txt
- src1.c
- src2.c
- idf_component.yml
- build/
组成部分:
- 顶层项目
CMakeLists.txt文件,这是 CMake 用于学习如何构建项目的主要文件,可以在这个文件中设置项目全局的 CMake 变量。顶层项目 CMakeLists.txt 文件会导入 /tools/cmake/project.cmake 文件,由它负责实现构建系统的其余部分。该文件最后会设置项目的名称,并定义该项目。 sdkconfig项目配置文件,执行idf.py menuconfig时会创建或更新此文件,文件中保存了项目中所有组件(包括 ESP-IDF 本身)的配置信息。 sdkconfig 文件可能会也可能不会被添加到项目的源码管理系统中。后续我们也会学习自定义menuconfig。- “
dependencies.lock” 文件包含项目中当前使用的所有托管的组件及其版本。使用 IDF 组件管理器添加或更新项目组件时,会自动生成或更新 dependencies.lock 文件。因此,请勿手动编辑此文件!如果项目中没有组件包含idf_component.yml文件,则不会创建 dependencies.lock 文件。 - “
idf_component.yml” 是可选文件,里面包含组件的元数据及其依赖项。IDF 组件管理器使用该文件下载和解析这些依赖项。更多信息,请参阅 idf_component.yml。 - “
bootloader_components” 是可选目录,里面包含了需要在引导加载项目中进行编译和链接的组件。并不是每个项目都需要这种自定义组件,但此类组件在引导加载程序需要修改以嵌入新功能时可能很有用。 - “
components” 是可选目录,里面包含了项目的部分自定义组件,并不是每个项目都需要这种自定义组件,但它有助于构建可复用的代码或者导入第三方(不属于 ESP-IDF)的组件。或者,你也可以在顶层 CMakeLists.txt 中设置EXTRA_COMPONENT_DIRS变量以查找其他指定位置处的组件。 - “
managed_components” 目录由 IDF 组件管理器创建,用于存储由 IDF 组件管理器管理的组件。每个托管组件通常包含 idf_component.yml 清单文件,定义了包括版本和依赖项在内的组件元数据。但对于那些来自 Git 仓库的组件,清单文件是可选的。请勿手动修改 “managed_components” 目录下的内容。如果需要进行更改,可以将组件复制到 components 目录下。“managed_components” 目录通常不在 Git 中进行版本控制,也不会与项目源代码一起分发。 main目录是一个特殊的组件,它包含项目本身的源代码。”main” 是默认名称,CMake 变量 COMPONENT_DIRS 默认包含此组件,但您可以修改此变量。build目录是存放构建输出的地方,如果没有此目录,idf.py 会自动创建。CMake 会配置项目,并在此目录下生成临时的构建文件。随后,在主构建进程的运行期间,该目录还会保存临时目标文件、库文件以及最终输出的二进制文件。此目录通常不会添加到项目的源码管理系统中,也不会随项目源码一同发布。
每个组件目录都包含一个 CMakeLists.txt 文件,里面会定义一些变量以控制该组件的构建过程,以及其与整个项目的集成。
每个组件还可以包含一个 Kconfig 文件,它用于定义 menuconfig 时展示的 组件配置 选项。某些组件可能还会包含 Kconfig.projbuild 和 project_include.cmake 特殊文件,它们用于 覆盖项目的部分设置。
2.1 项目 CMakeLists 文件
每个项目都有一个顶层 CMakeLists.txt 文件,包含整个项目的构建设置。默认情况下,项目 CMakeLists 文件会非常小。
cmake_minimum_required(VERSION 3.22)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(myProject)
每个项目都要按照上面显示的顺序添加上述三行代码:
- cmake_minimum_required(VERSION 3.22) 必须放在 CMakeLists.txt 文件的第一行,它会告诉 CMake 构建该项目所需要的最小版本号。ESP-IDF 支持 CMake 3.22 或更高的版本。
- include($ENV{IDF_PATH}/tools/cmake/project.cmake) 会导入 CMake 的其余功能来完成配置项目、检索组件等任务。
- project(myProject) 会创建项目本身,并指定项目名称。该名称会作为最终输出的二进制文件的名字,即 myProject.elf 和 myProject.bin。每个 CMakeLists 文件只能定义一个项目。
上面的内容为必要部分,除此之外,还有一些可选部分。以下这些变量都有默认值,用户可以覆盖这些变量值以自定义构建行为。
-
COMPONENT_DIRS:
组件的搜索目录,默认为IDF_PATH/components、 PROJECT_DIR/components、和 EXTRA_COMPONENT_DIRS。如果你不想在这些位置搜索组件,请覆盖此变量。 -
EXTRA_COMPONENT_DIRS:
用于搜索组件的其它可选目录列表。路径可以是相对于项目目录的相对路径,也可以是绝对路径。 -
COMPONENTS:
用于指定要构建到项目中的组件名称列表,默认为 COMPONENT_DIRS 目录下检索到的所有组件。使用此变量可以“精简”项目,从而缩短构建时间。请注意,如果一个组件通过 COMPONENT_REQUIRES 指定了它依赖的另一个组件,则会自动将其添加到 COMPONENTS 中,所以 COMPONENTS 列表可能会非常短。另外,还可以通过设置 MINIMAL_BUILD 构建属性 来指定 COMPONENTS 中的 main 组件。 -
BOOTLOADER_IGNORE_EXTRA_COMPONENT:可选组件列表,位于 bootloader_components/ 目录中,引导加载程序编译时会忽略该列表中的组件。使用这一变量可以将一个组件有条件地包含在项目中。
-
BOOTLOADER_EXTRA_COMPONENT_DIRS:可选的附加路径列表,引导加载程序编译时将从这些路径中搜索要编译的组件。注意,这是一个构建属性。
以上变量中的路径可以是绝对路径,或者是相对于项目目录的相对路径。
2.2 组件 CMakeLists 文件
每个项目都包含一个或多个组件,这些组件可以是 ESP-IDF 的一部分,可以是项目自身组件目录的一部分,也可以从自定义组件目录添加。
组件是 COMPONENT_DIRS 列表中包含 CMakeLists.txt 文件的任何目录。
构建 或 编译 时,ESP-IDF 搜索所有待构建的组件的顺序为COMPONENT_DIRS 指定的顺序依次进行,会按照以下优先级搜索组件目录(从高到低):。
- 项目目录下的组件
- EXTRA_COMPONENT_DIRS 中的组件
- 项目目录下 managed_components 目录中的组件。这些组件由 IDF Component Manager 下载并管理。(除非 IDF Component Manager 被禁用)
- IDF_PATH/components 目录下的组件
如果有两个及以上同名组件,构建系统会使用优先级更高的组件。这使得我们可以在项目中覆盖 ESP-IDF 提供的组件。只需要复制 ESP-IDF 组件到项目目录下,然后修改它。这样可以在修改组件的同时,不修改 ESP-IDF 的源代码。
如果在现有项目中通过将组件移动到一个新位置来覆盖它,项目不会自动看到新组件的路径。请运行
idf.py reconfigure命令后(或删除项目构建文件夹)再重新构建。
最小组件 CMakeLists.txt 文件通过使用 idf_component_register 将组件添加到构建系统中。
idf_component_register(SRCS "foo.c" "bar.c"
INCLUDE_DIRS "include" REQUIRES mbedtls)
- SRCS 是源文件列表(.c、.cpp、.cc、.S),里面所有的源文件都将会编译进组件库中。
- INCLUDE_DIRS 是目录列表,里面的路径会被添加到所有需要该组件的组件(包括 main 组件)全局 include 搜索路径中。
- REQUIRES 实际上并不是必需的,但通常需要它来声明该组件需要使用哪些其它组件,请参考 组件依赖。
3. menuconfig项目配置
为什么会用到menuconfig,它有什么好处?
- menuconfig可以将配置内容独立出代码,通过配置的形式来新增复用性,比如WiFi账号密码。
- menuconfig生成的宏定义,配合C语言预处理,可以兼容不同的代码处理逻辑。
- menuconfig还可以被CMake访问,可以根据配置执行不同的构建方案,比如SPIFFS构建web页面路径文件。后续有案例可以学习。
3.1 Blink项目配置
还是打开示例工程。
examples/get-started/blink
还记得我们说需要配置IO口。

在ESP32S3开发板上,我们需要修改为48引脚。然后在代码中,我们可以看到
#define BLINK_GPIO CONFIG_BLINK_GPIO
然后追溯一下文件位置。

可以发现,它来源于build目录下的sdkconfig.h文件。
注意:
- build目录是构建编译产物,直接修改这里的话会被覆盖掉。
- sdkconfig.h文件:项目配置菜单借助sdkconfig,应用程序构建目标将在构建目录中生成 sdkconfig.h 文件,并使得 sdkconfig 选项可用于项目构建系统和源文件。
为了能修改sdkconfig.h,ESP-IDF框架为我们提供了高效的配置方式,那就是Menuconfig。
- 方式1:运行命令 idf.py menuconfig 打开
- 方式2:插件打开menuconfig,点击快捷键小齿轮(推荐)
3.2 自定义配置
menuconfig菜单里看到的关于LED配置的内容来源于main组件下的Kconfig.projbuild 文件。

这部分内容刚好对应上我们之前看到的配置界面。
注意:
ESP-IDF 使用基于 kconfiglib 的 esp-idf-kconfig 包,而 kconfiglib 是 Kconfig 系统的 Python 扩展。Kconfig 提供了编译时的项目配置机制,以及多种类型的配置选项(如整数、字符串和布尔值等)。Kconfig 文件指定了选项之间的依赖关系、默认值、组合方式等。可以参考文档:Documentation of esp-idf-kconfig
ESP-IDF默认组件也使用了大量的Kconfig配置,通过这些配置,我们可以修改ESP-IDF的很多默认配置项,比如系统分区表、FreeRTOS 配置(任务调度器的行为、栈大小、系统 tick 时间)、驱动程序设置、日志输出级别等。
依葫芦画瓢,我们在它基础上改成这样:
menu "Blink Configuration"
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
choice BLINK_LED
prompt "Blink LED type"
default BLINK_LED_GPIO
help
Select the LED type. A normal level controlled LED or an addressable LED strip.
The default selection is based on the Espressif DevKit boards.
You can change the default selection according to your board.
config BLINK_LED_GPIO
bool "GPIO"
config BLINK_LED_STRIP
bool "LED strip"
endchoice
choice BLINK_LED_STRIP_BACKEND
depends on BLINK_LED_STRIP
prompt "LED strip backend peripheral"
default BLINK_LED_STRIP_BACKEND_RMT if SOC_RMT_SUPPORTED
default BLINK_LED_STRIP_BACKEND_SPI
help
Select the backend peripheral to drive the LED strip.
config BLINK_LED_STRIP_BACKEND_RMT
depends on SOC_RMT_SUPPORTED
bool "RMT"
config BLINK_LED_STRIP_BACKEND_SPI
bool "SPI"
endchoice
config BLINK_GPIO
int "Blink GPIO number"
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
default 8
help
GPIO number (IOxx) to blink on and off the LED.
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to blink.
config BLINK_GPIO1
int "Blink GPIO1 number"
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
default 48
help
GPIO number (IOxx) to blink on and off the LED.
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to blink.
config BLINK_PERIOD
int "Blink period in ms"
range 10 3600000
default 1000
help
Define the blinking period in milliseconds.
endmenu
效果如下:


3.3 组件配置相关文件
在 ESP-IDF 构建系统中,有这么几种文件和组件配置相关。

它们分别是:
- Kconfig文件
- Kconfig.projbuild文件
- sdkconfig文件
- sdkconfig.default文件
- sdkconfig.old文件
- sdkconfig.ci文件
- sdkconfig.old文件
其中Kconfig 和 Kconfig.projbuild 文件用于定义菜单,其余各种“sdkconfig”文件用于保存我们配置的值。
| 文件名 | 作用 |
|---|---|
| sdkconfig | 用于保存 Kconfig 设置的键值对形式,不建议手动更改 |
| sdkconfig.default | 用于保存 Kconfig 设置的键值对形式,不建议手动更改 |
| sdkconfig | 用于保存 Kconfig 设置的键值对形式,不建议手动更改 |
| sdkconfig.old | 当保存旧的 menuconfig 设置时,自动备份原来的 sdkconfig 文件 |
| sdkconfig.default. TARGET_NAME | 对于 esp32 这个目标芯片,sdkconfig 的默认值会首先 从 sdkconfig.defaults 获取,然后再从 sdkconfig.defaults.esp32 获取。 |
4. 构建自定义组件
在项目的目录树结构中,components文件夹就是我们自定义的组件。
4.1 新建自定义组件
命令格式:
idf.py -C components create-component [myComponents]
- 其中 [myComponents] 就是对应的组件名字。
比如,我这里新建一个myComponent组件。

其中,为了测试,代码如下:
- myComponent.c
#include <stdio.h>
#include "esp_log.h"
#include "myComponent.h"
static const char *TAG = "myComponent";
void func(void)
{
ESP_LOGI(TAG, "func");
}
- myComponent.h
void func(void);
#ifndef MYCOMPONENT_H
#define MYCOMPONENT_H
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdint.h>
// Function declarations
void func(void);
#ifdef __cplusplus
}
#endif
#endif /* MYCOMPONENT_H */
4.2 main组件引入自定义组件
在main组件里引入模块使用即可。
/* Blink Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "led_strip.h"
#include "sdkconfig.h"
#include "myComponent.h" // 引入头文件
static const char *TAG = "example";
/* Use project configuration menu (idf.py menuconfig) to choose the GPIO to blink,
or you can edit the following line and set a number here.
*/
#define BLINK_GPIO CONFIG_BLINK_GPIO
static uint8_t s_led_state = 0;
#ifdef CONFIG_BLINK_LED_STRIP
static led_strip_handle_t led_strip;
static void blink_led(void)
{
/* If the addressable LED is enabled */
if (s_led_state) {
/* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */
led_strip_set_pixel(led_strip, 0, 16, 16, 16);
/* Refresh the strip to send data */
led_strip_refresh(led_strip);
} else {
/* Set all LED off to clear all pixels */
led_strip_clear(led_strip);
}
}
static void configure_led(void)
{
ESP_LOGI(TAG, "Example configured to blink addressable LED!");
/* LED strip initialization with the GPIO and pixels number*/
led_strip_config_t strip_config = {
.strip_gpio_num = BLINK_GPIO,
.max_leds = 1, // at least one LED on board
};
#if CONFIG_BLINK_LED_STRIP_BACKEND_RMT
led_strip_rmt_config_t rmt_config = {
.resolution_hz = 10 * 1000 * 1000, // 10MHz
.flags.with_dma = false,
};
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
#elif CONFIG_BLINK_LED_STRIP_BACKEND_SPI
led_strip_spi_config_t spi_config = {
.spi_bus = SPI2_HOST,
.flags.with_dma = true,
};
ESP_ERROR_CHECK(led_strip_new_spi_device(&strip_config, &spi_config, &led_strip));
#else
#error "unsupported LED strip backend"
#endif
/* Set all LED off to clear all pixels */
led_strip_clear(led_strip);
}
#elif CONFIG_BLINK_LED_GPIO
static void blink_led(void)
{
/* Set the GPIO level according to the state (LOW or HIGH)*/
gpio_set_level(BLINK_GPIO, s_led_state);
}
static void configure_led(void)
{
ESP_LOGI(TAG, "Example configured to blink GPIO LED!");
gpio_reset_pin(BLINK_GPIO);
/* Set the GPIO as a push/pull output */
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
}
#else
#error "unsupported LED type"
#endif
void app_main(void)
{
func(); // 执行新组件方法
/* Configure the peripheral according to the LED type */
configure_led();
while (1) {
ESP_LOGI(TAG, "Turning the LED %s!", s_led_state == true ? "ON" : "OFF");
blink_led();
/* Toggle the LED state */
s_led_state = !s_led_state;
vTaskDelay(CONFIG_BLINK_PERIOD / portTICK_PERIOD_MS);
}
}
4.3 编译测试

按照预期执行。
4.4 给自定义组件新增menuconfig

直接把main组件的Kconfig.probuild文件复制到自定义组件的根目录下,并修改内容如下:
menu "myComponent"
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
config BLINK_GPIO
int "myComponent GPIO number"
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
default 8
help
GPIO number (IOxx) to blink on and off the LED.
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to blink.
endmenu
直接看menuconfig配置页面:

5. 使用官方组件
对应目录就是 managed_components

就有点像是Arduino Libray Manager,我们也会去使用别人写好的库。
5.1 引入组件
输入命令添加依赖
idf.py add-dependency “zorxx/dht”
编译项目
idf.py build
idf会自动下载我们依赖的组件,存放到managed_components文件夹中


到这里就是基本上引入了,后面就是根据具体情况来使用而已。







4454

被折叠的 条评论
为什么被折叠?



