
很多人一开始接触ESP32的开发第一反应就是翻出Arduino IDE把板子插上选好开发板和端口然后点那个上传按钮。这确实是最容易上手的方式。但当你开始面对一个稍微像点样子的项目——比如要跑音频处理、要接入几个外设传感器、还要做低功耗管理的时候Arduino那套基于直觉的工程组织方式就会开始让你难受。文件多了之后找不到依赖关系库版本一升级整个项目编译不过代码量一上来编译速度也让人抓狂。这个时候把开发环境切到VSCode加CMake搭配官方提供的ESP-IDF框架是一个很自然的技术演进方向。这篇文章里的内容就是我从几年前开始接触这套VSCode加CMake开发方式以来踩过的坑、沉淀下来的流程和一些个人觉得很有用的工程化经验。写的都是实际操作不会讲太多空洞的理论。如果你正打算从Arduino这类简易环境迁移到更专业的ESP32开发方式或者你已经在用ESP-IDF但还在用命令行加文本编辑器的方式硬撑那这篇文章应该能给你一些切实的帮助。环境搭建部分可以无脑跟着做后面讲到的工程组织方式是我个人认为让ESP32项目从“能跑”走向“能维护”的关键思路。1. 为什么绕开Arduino IDE选择VSCode加CMake的组合1.1 Arduino IDE到底卡在哪里Arduino IDE的优势是开箱即用对于点亮一个LED、读个温湿度、驱动一下OLED屏幕这种场景它确实是效率最高的选择。但它的短板也相当明显所有代码只有一个主循环加若干回调函数整个项目就是一堆.ino文件拼接在一起。一旦代码规模上来这种平铺结构就会导致命名冲突、逻辑混乱而且很难做单元测试和模块化复用。另一个关键问题是Arduino IDE对ESP32这种复杂芯片的支持本质上是通过第三方板级支持包实现的这一层封装隐藏了很多底层的调度细节比如任务切换、内存分配、中断处理等你都很难直接干预。当你想跑FreeRTOS任务、想用双核并行、想做深度的低功耗优化时Arduino那套封装反而成了束缚。更重要的一点是ESP32原厂推出的ESP-IDF框架本身就推荐使用CMake构建系统而且IDF 5.0之后新增的很多组件、中间件和新版工具链都在持续往CMake这条路上倾斜。长期来看Arduino路线在ESP32上的维护力度和更新速度已经很难追上IDF主线。所以如果你打算认真把ESP32这个平台吃透直接上手IDF加CMake反而是更省时间的路径。1.2 VSCode在这套组合里解决了什么问题VSCode本身是一个编辑器它对于嵌入式项目最大的价值不是代码编写本身而是它把代码补全、语法检查、版本管理、终端操作、串口监视、插件扩展这些开发环节统一到了一个界面里。用VSCode搭配C/C扩展和ESP-IDF扩展等于是在编辑器内部完成了“编辑、编译、烧录、串口监视”这条完整的迭代链路不再需要频繁切换到独立的命令行窗口去敲idf.py命令。更重要的是CMake在配置阶段会生成compile_commands.json文件VSCode的C/C扩展读取这个文件之后可以提供非常准确的智能提示和跳转这种精度是Arduino IDE那种靠猜的方式没法比的。1.3 CMake的角色不只是一个“构建工具”很多从单片机开发转过来的人一听到CMake就觉得恐惧觉得这玩意复杂语法又晦涩。但用在ESP32开发里你其实不需要完全掌握CMake的方方面面只需要理解它的核心思想CMake负责描述“源文件和依赖关系”然后生成本地构建系统在ESP-IDF默认是Ninja或Make最后由构建系统完成编译和链接。用生活化的类比来说CMake就像是一个总包工头它不亲自砌墙但清楚整个工程要分几个工种、每个工种需要什么材料、谁依赖谁、按照什么顺序进场。在ESP-IDF框架里每一个组件比如驱动SSD1306屏幕、驱动MPU6050传感器都有自己的CMakeLists.txt文件这个文件声明了组件本身需要编译哪些源文件、依赖哪些其他组件。头部的顶层CMakeLists.txt声明了整个项目要引入哪些组件由CMake将整个依赖树展开成具体的编译任务。理解这一步ESP32项目中的很多编译疑惑就能解开。比如你总是搞不明白为什么明明加了某个头文件却报“找不到文件”又或者是某个库的宏定义全部失效这些问题的根源往往就是你忽略了CMake文件里的依赖声明。2. 搭建设备前的准备理解ESP-IDF的组件化架构2.1 什么是组件ComponentESP-IDF把项目组织方式约定得很明确一个项目由main组件加上若干自定义组件组成每个组件都放在独立的子目录中。组件内部拥有自己的源文件、头文件、依赖声明和链接配置。这种组织方式和PC端软件开发的“模块化”非常像只不过在嵌入式场景里所有代码最终都要静态链接成一个烧录镜像所以模块化更多体现在源码头文件组织层面。举个例子你平时放在一个文件夹里的三个.c文件加两个.h文件在ESP-IDF的项目结构下它们应该被放成一个组件。这个组件的CMakeLists.txt里会写清楚需要编译哪些文件是否要把头文件目录暴露给其他组件。如果你顺着这个约定去组织代码项目里数百个文件的依赖关系会变得一目了然。2.2 顶层目录、组件目录与依赖关系的约定一个标准ESP-IDF项目目录长这样project/ ├── CMakeLists.txt ├── sdkconfig ├── main/ │ ├── CMakeLists.txt │ └── main.c ├── components/ │ ├── sensor_driver/ │ │ ├── CMakeLists.txt │ │ ├── sensor_driver.c │ │ └── include/ │ │ └── sensor_driver.h │ └── screen_driver/ │ ├── CMakeLists.txt │ └── ...顶层CMakeLists.txt的内容其实非常简单通常就是一句指向ESP-IDF核心构建脚本的include命令。但就是这个顶层文件负责把整个工程的构建流程交给ESP-IDF的框架去处理。不同的组件之间依赖关系的声明方式是写在组件自己的CMakeLists.txt里比如主组件依赖传感器驱动组件就写一行REQUIRES sensor_driver。这里有一个很关键的细节如果组件A依赖组件B而B又反过来依赖A这就是循环依赖ESP-IDF的构建系统会直接报错。所以依赖关系必须是一个有向无环图。实际操作中划分组件的边界要清楚避免把互相牵扯太深的代码塞进两个互不熟悉的组件里。2.3 为什么项目规模上来之后组件化是不可免的我最早在做ESP32项目的时候也觉得“组件”这个机制很繁琐反正代码量不大全部塞进main文件夹里不就行了。后来项目膨胀到接近两万行代码才发现不拆组件的痛苦main目录里堆了几十个C文件有些文件之间通过extern引用共享全局变量改一个数据结构半天编译不过测试也几乎没法写。后来我花了两个晚上把项目拆成了六个组件驱动层、协议层、业务逻辑层、用户交互层、电源管理、网络服务。拆完之后编译明显更快因为增量编译的粒度细了而且每一个组件都能拿单独的测试代码去验证。比如电源管理组件我单独做了一个测试入口只编译这个组件加一个模拟主函数专门验证各种低功耗模式的切换逻辑这在之前那种大锅烩的工程里根本没法做。3. 从零搭建VSCode加CMake开发环境3.1 基础工具链准备搭建这套环境之前先把基础工具装好。以下是我整理的最精简清单按顺序执行基本不会出错安装Python要求3.8以上版本注意勾选“Add Python to PATH”选项否则后面执行idf.py时会找不到Python解释器。安装GitESP-IDF的获取和版本更新完全依赖Git。安装CMake新版ESP-IDF要求CMake 3.16以上。Windows环境直接去下载安装包Linux环境通过包管理器安装即可。安装NinjaNinja是一个极快的构建工具ESP-IDF默认生成的构建系统就是Ninja。安装VSCode以及C/C扩展、ESP-IDF扩展、串口监视扩展。这里有一个我踩过的坑如果你在Linux环境下一定要确认CMake和Ninja的安装版本。某些旧版本Ubuntu自带的CMake是3.10ESP-IDF用不了。解决方法是手动编译安装新版CMake或者启用官方仓库的更新源。不要图省事用旧版本硬扛后果是编译过程中出现各种诡异的语法错误排查起来非常浪费时间。3.2 获取并配置ESP-IDFESP-IDF本身是一套很大的代码库里面包含了各种组件、工具链和构建脚本。获取方式是用Git克隆官方仓库。在这里要给一个个人建议不要直接克隆最新master分支而是选择一个稳定版本比如拉取某个release标签。主分支虽然新功能多但经常会有接口变动和编译兼容性问题对于做实际项目的开发者来说稳定优先。获取到之后在ESP-IDF的根目录下运行安装脚本。这个脚本的职责是下载工具链和编译依赖比如交叉编译器、调试器、各类命令行工具。整个过程会持续一段时间根据网络状况可能需要十几分钟甚至更久。安装完之后还需要执行环境导出脚本。这一步会把一系列环境变量写入当前终端会话最关键的是让系统能找到idf.py这个核心命令。这个命令是ESP-IDF的命令行入口编译、烧录、监视串口都通过它来执行。3.3 VSCode插件的精细配置VSCode里的ESP-IDF扩展是一个大集合它包含了ESP-IDF的调试支持、代码片段、配置界面等功能。第一次使用时扩展会引导你指定ESP-IDF的安装路径。这里有一个容易出问题的点一定要在扩展设置里把idf.espIdfPath设置成你实际存放ESP-IDF目录的位置并且把idf.toolsPath设置成工具链目录的上一级。配置完成后在VSCode的命令面板里输入ESP-IDF: Show Examples Projects可以浏览ESP-IDF自带的官方示例直接选择其中一个示例创建新项目。这时构建系统会自动按照CMake的规则去配置项目VSCode右下角的状态栏会出现“Build”“Flash”“Monitor”等按钮。从这里开始你基本告别了命令行正常开发流程就是在编辑器里改代码、点Build、点Flash、点Monitor一条链路下来非常顺畅。有一个实用的小技巧VSCode的CMake插件和ESP-IDF扩展可以协同工作。打开一个已配置好的工程后在命令面板运行CMake: Scan for KitsCMake插件会扫描系统里的编译器并生成一个编译器套件列表。然后在CMake: Select a Kit里选择ESP-IDF对应的交叉编译器。这样配置之后CMake插件的“配置、构建、调试”按钮也能正常工作等于多了一套图形化操作的保障。3.4 验证环境是否OK环境搭好之后不要急于写复杂代码先创建一个最简单的hello world示例。在VSCode命令面板选择ESP-IDF: Create Project from Extension模板选hello_world创建完成之后点击Build。第一次构建如果一切顺利会在项目目录下生成build文件夹里面包含了编译出的ELF文件、map文件、烧录镜像等。看到这个目录结构说明整套工具链已经打通了。接下来把开发板连上电脑点击Flash等进度条走完打开Monitor如果串口输出“Hello world!”字符串那整个环境搭建就算圆满完成。4. 实操要点CMakeLists.txt的编写思路与常见配置4.1 顶层CMakeLists.txt其实很简单顶层CMakeLists.txt在多数项目里只需要三行内容指定CMake最低版本、引入ESP-IDF的构建入口、为项目本身命名。以某开源智能家居网关项目为例顶层文件长这样cmake_minimum_required(VERSION 3.16) include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(smart_gateway)这里的IDF_PATH环境变量就是ESP-IDF的安装根目录由之前的环境导出脚本写入。如果这个变量不存在CMake就会直接报错提示找不到project.cmake。遇到这个错误时先不要怀疑CMake配置问题优先检查环境变量是否在当前VSCode终端会话中生效。4.2 组件CMakeLists.txt的核心语法组件内部的CMakeLists.txt更关键因为它直接决定了编译哪些文件、暴露哪些头文件、依赖哪些组件。最基本的写法如下idf_component_register( SRCS sensor_driver.c sensor_driver_utils.c INCLUDE_DIRS include REQUIRES driver esp_timer )这个宏是整个ESP-IDF构建系统的核心几乎所有组件配置都围绕它展开。SRCS字段列出该组件需要编译的源文件INCLUDE_DIRS指定对外暴露的头文件目录REQUIRES声明依赖的其他组件。有一个细节需要特别说明INCLUDE_DIRS里列出的目录会被其他依赖了这个组件的组件自动加入编译参数。也就是说如果屏幕驱动组件依赖了传感器组件屏幕驱动代码里就可以直接#include sensor_driver.h因为在CMake层面这个头文件路径已经被传过去了。还有一种情况是组件A只需要在编译时知道组件B的头文件位置但不希望被链接到组件B的实现这个时候需要用PRIV_REQUIRES字段。它与REQUIRES的区别在于PRIV_REQUIRES声明的依赖不会传导给依赖它的上层组件适合用来隐藏内部实现细节。4.3 添加一个第三方库到项目里使用CMake加ESP-IDF组织项目最大的方便之处在于引入第三方库几乎不费劲。大多数ESP32生态下的开源库都会在仓库里自带CMakeLists.txt并把目录结构组织成一个或多个组件。你要做的就是把整个库目录放到项目根目录下的components文件夹里然后在主组件的CMakeLists.txt里把库对应的组件名写进REQUIRES字段。如果第三方库没有按ESP-IDF的项目规范组织而是只有一堆源文件和头文件就需要手动给它补充一个CMakeLists.txt把这个库包装成一个标准组件。包装过程并不难在库目录下新建一个CMakeLists.txt用idf_component_register把源文件和头文件目录声明好即可。只要没有特别复杂的跨平台宏判断一般一两分钟就能搞定。一个经验之谈引入第三方库之后如果编译报错先去看它的CMakeLists.txt里写了什么很多嵌入式的库会用手动指定宏定义的方式控制某些功能比如是否启用调试输出、是否优化体积、是否启用某个硬件平台这些宏一般写在编译选项里如果你项目里的其他组件和它冲突集成就会非常痛苦。解决思路是在包装组件时单独指定编译选项尽量不影响其他组件。4.4 分区表与自定义构建选项ESP32的Flash容量一般有4MB、8MB、16MB等几个规格而默认的分区表可能不能满足你的需求。比如你要跑一个比较大的固件还要做OTA升级并且要挂一个文件系统来存配置和日志这个时候默认分区表显然不够用就得自己定义分区表。自定义分区表的过程也是通过CMake配置完成的。通常在项目根目录下放一个partitions.csv文件然后在顶层CMakeLists.txt里加上一行set(PARTITION_TABLE_CUSTOM_CSV partitions.csv)这个变量会被ESP-IDF的构建系统读取在生成烧录镜像时使用指定的分区表。分区表的内容格式也很直观每一行定义分区名称、类型、子类型、偏移地址、大小、标志等。关于偏移地址有一个必须注意的约束烧录镜像的引导程序地址通常是固定的0x1000而分区表中第一个分区的偏移地址要避开引导程序占用的空间所以我个人习惯在第一个分区之前保留至少64KB的空白区域。如果在实际烧录过程中遇到擦除失败或者启动循环重启的问题优先怀疑分区表定义和实际连接方式是否冲突。5. 编译、烧录、调试全流程的实操细节5.1 编译命令背后的执行逻辑当你点击VSCode的Build按钮时实际上执行的是idf.py build。这个命令本身的执行分两个阶段。第一阶段是CMake配置阶段会扫描项目结构、收集所有组件的信息、检查依赖关系然后生成构建规则文件。这个过程在你第一次构建时尤其重要因为很多缓存在这里产生后续的增量编译都是基于这套缓存的。第二阶段是Ninja执行阶段按照CMake生成的规则去编译每个源文件最后链接成最终的固件。Ninja在这里的效率非常高因为它在内部维护了精确的依赖关系只要某个头文件变化它自动就知道哪些源文件需要重新编译那些无关的文件根本不会碰。所以当你觉得编译慢的时候真正能提效的办法是尽量减少全局头文件的修改尽量把不常变动的代码拆成独立组件避免头文件修改波及整个工程。这就是为什么组件化在工程规模变大之后几乎是一个必然的选项。5.2 烧录方式的区别ESP32支持串口烧录和JTAG烧录两种方式但日常调试首选还是串口。串口烧录的原理是在引导程序阶段通过串口协议把固件写入Flash。点击VSCode的Flash按钮后扩展会自动选择可用的串口然后开始烧录流程。烧录过程中你最好不要拔插USB线否则会直接导致失败。串口烧录有一个比较经典的问题如果你的开发板使用了USB转串口芯片那么每次点击Flash之前开发板可能处于正常运行状态它会把串口TX引脚在某些时刻拉低干扰烧录握手。解决办法很简单绝大多数ESP32开发板上都有一个BOOT按键也有标注为IO0在烧录之前按住这个按键不放同时按一下EN复位键然后松开BOOT按键这时候芯片就进入了下载模式。VSCode的ESP-IDF扩展在Flash按钮旁边会自动判断是否需要手动进入下载模式但不同开发板的自动下载电路差异很大。如果遇到烧录超时手动按BOOT键进入下载模式是最稳妥的路径。5.3 串口监视与日志输出烧录完成之后下一步就是打开Monitor窗口看日志输出。VSCode的ESP-IDF扩展内置了串口监视器会把芯片通过串口输出的所有内容展示在终端面板里。它可以配置波特率默认是115200这是ESP-IDF日志模块的默认输出速率。看日志这个环节有一个很多初学者容易忽略的细节ESP-IDF的日志系统分为多个等级分别是错误、警告、信息、调试、冗长。默认情况下编译参数里的日志等级是“信息”也就是说调试级别和冗长级别的日志输出都会被编译器过滤掉不会编译进固件里。这带来的一个问题是你想用ESP_LOGD输出一段调试日志结果串口终端上什么都没显示你以为是代码没执行到那段实际上是日志等级不够。想要显示调试日志可以在项目配置菜单里调整日志等级也可以直接在CMake配置中添加编译选项。这个排查点在调试一些行为诡异的BUG时特别有用。5.4 调试和断点的使用VSCode加ESP-IDF支持真机调试通过JTAG调试器比如某常见调试模块或者芯片自带的USB调试接口可以在VSCode里直接打断点、查看变量值、单步执行。我个人在使用过程中发现对于大多数逻辑类问题串口日志加断言的组合已经能排查掉九成问题。JTAG调试适合那些时间敏感、数据量大的场景比如追踪某个中断是否被触发、查看某个缓冲区在哪个时刻被写坏等。配置调试功能的时候需要注意打开调试会重置芯片而且调试时如果开启了看门狗可能会在等待断点触发时不断产生溢出复位这种情况需要关闭相应的看门狗功能。6. 实战中的工程化经验与常见错误排查6.1 编译阶段的高频问题找不到头文件。这类错误的常见原因有三个一是某个组件的INCLUDE_DIRS没有正确设置二是依赖组件没有写进REQUIRES三是组件目录名称写错。排查思路是按顺序检查CMakeLists.txt里的声明以及组件目录是否存在于components文件夹下。链接错误符号未定义。常见原因是链接时没有包含实现某个函数的目标文件。在ESP-IDF里如果源文件没有出现在组件的SRCS列表里即使它存在于组件目录中也不会被编译进固件。还有一个隐蔽的情况如果某个源文件内容全被宏定义包裹而宏定义又没有开启那这个文件编译出来就是一个空对象相关符号自然缺失也会导致链接错误。编译选项冲突。ESP-IDF每个组件可以单独设置编译选项但跨组件的全局宏定义冲突极难排查。举一个真实场景网络协议栈组件会定义CONFIG_xxx这类宏你的业务代码里如果也定义了同名的宏通过头文件间接引用时行为会变得非常不可控。建议所有的自定义宏都加项目专属前缀比如项目代号缩写加下划线避免和系统级配置宏冲突。6.2 烧录与运行阶段的高频问题烧录成功后复位重启但没有任何输出。这个现象多半是Flash启动模式设置错误或者是分区表配置有问题。先检查串口选择是否正确再看开发板是否真的进入了运行模式。有些开发板带有电源指示灯但运行和下载共用同一个LED会很容易被误导。设备反复重启日志里出现看门狗超时。这个情况十有八九是某个任务卡死了。ESP-IDF默认会在任务卡死时打印出任务栈回溯和任务列表。看到这类日志第一时间去查是哪个任务占用了CPU或阻塞时间过长。如果在日志里发现task_wdt相关的字样说明是任务看门狗被触发可以检查代码中是否有长时间关中断、死循环或者内存越界改写的情况。串口数据乱码。优先考虑波特率是否正确因为ESP32芯片的时钟校准参数可能在出厂时就存在偏差串口波特率稍微不匹配就会导致首个字节乱码。解决办法是在串口监视器里尝试几个典型波特率。6.3 环境层面的“玄学”问题这里说的“玄学”其实背后都有原因。比如某一次明明没有任何代码改动Build点击后却全量重新编译。这种情况多半是CMake缓存或构建目录状态错乱了直接将build目录删除重新构建一次通常能解决。ESP-IDF的构建系统非常依赖文件时间戳某些文件系统在挂载时修改了时间戳导致依赖判断失败删除构建缓存是最可靠的恢复手段。另一个常见问题是换了ESP-IDF版本之后老项目编译报出一堆兼容性错误。这里给一个建议每个项目在创建时建议把ESP-IDF的版本号记录在README里最好用Git子模块或版本管理工具锁定你使用的IDF版本避免某天更新了整个IDF之后所有项目的构建环境悄然发生变化。7. 从能跑到工程化的关键转变7.1 单元测试与最小复现当项目规模上升到一定程度很多人会发现一个小改动引发的bug比预期多得多。根源在于嵌入式项目缺乏系统的回归测试机制。ESP-IDF为每个组件提供了单元测试的接入方式你可以在组件内部添加测试代码也可以通过一个特殊的测试固件工程把多个组件拉进来跑断言测试。我在一个智能网关项目里就做过这个实践把网络协议解析模块做成一个独立组件并在组件里写了一套模拟输入数据的测试函数。每次改动协议解析逻辑我就在本地跑一遍测试十几秒就能确认功能是否有退化。对于嵌入式项目来说这样的测试密度已经能产生非常可观的收益。7.2 代码组织与命名规范工程化转变的另一个重要方面是代码组织规范。组件命名尽量用功能而非人名内部函数的静态化程度要高。ESP-IDF构建系统在编译每个组件时默认会开启一些编译警告如果代码中存在未使用的函数或变量会在编译日志中显示警告。最好把警告当作错误处理强迫自己保持代码整洁不然等到代码量上去了再回头清理会被动得多。7.3 版本管理与协作流程多人协作开发ESP32项目时建议使用Git子模块或Manifest工具管理ESP-IDF本身的版本同时把不同组件拆成不同的Git仓库。这样可以做到组件独立演进主项目只记录组件的版本号。实际操作中分支策略尽量简化比如只保留一个开发分支和一个发布分支每个组件维护自己的语义化版本标签。这种模式搭配CMake的组件依赖管理整体协作流程会非常清晰。8. 一些值得尝试的进阶优化8.1 优化编译速度编译速度是嵌入式开发者最在意的事情之一尤其是项目代码量增长之后。除了前面说的组件化之外还有几个切实可行的手段。第一个是使用ccache。ccache是一个编译器缓存工具可以缓存编译器的输出结果当同一份源文件的哈希没有变化时直接复用之前的编译产物这一步节省的时间非常可观。在ESP-IDF的配置选项里可以启用编译缓存启用之后第二次构建的速度甚至能快出好几倍。第二个是给Ninja设置并行任务数。默认并行数是CPU核心数但在内存紧张的情况下多个编译任务同时跑会导致内存溢出反而降低速度。可以手动控制并行任务数找出自己机器上的最优值。8.2 自定义组件模板当你创建过多个ESP32项目之后会发现很多代码骨架是高度相似的。比如网络模块、传感器扫描模块、OLED显示模块几乎是每个项目都会用到。把常用的组件整理成模板让新项目通过模板直接生成是一个效率提升很大的手法。维护一份包含常用配置和默认CMakeLists.txt的组件模板后续新项目只需要复制修改业务逻辑即可。8.3 深入使用sdkconfigsdkconfig这个文件本质上是项目构建配置的集中地所有和ESP-IDF相关的功能开关都在这里。它在CMake构建过程中扮演了非常重要的角色很多编译器宏都是从这个文件生成的。做低功耗项目的人会反复调整sdkconfig里的休眠选项做音频项目的人会纠结音频时钟配置做网络项目的人可能要调MAC地址和WiFi缓冲区大小。这个文件生成的*_config.h头文件会在全部组件中生效所以修改后经常引发大规模重新编译因此最好在项目初期就把需要调整的配置确定好避免后期频繁改动导致构建缓存反复失效。以我个人经验而言从Arduino切换到这个组合之后前两周确实有点不适应各种环境问题、构建报错让人怀疑自己是不是在浪费时间。但坚持一个项目做完之后你会明显感觉到这套工作流带来的掌控感代码结构清晰依赖一目了然调试手段丰富构建效率也达到可接受的范围。特别是项目迭代到后期功能频繁变更时这种工程化方式的优势会体现得非常明显。最后分享一个小习惯我在每次创建一个新的ESP32项目时都会先花十分钟把项目目录结构和CMakeLists.txt搭好哪怕当前只有一个空的main函数。这个框架一旦定下来后面所有的代码添加都是往框架里填充内容整个过程会顺畅很多。这也算是我在做了好几个ESP32项目之后觉得最值得养成的一个开发习惯。