2. 固件编译与首次烧录:Hello World工程创建、编译流程解析、使用esptool.py进行首次烧录、串口监视器查看日志
好,咱们正式开始动手了。这一章的目标很明确——让ESP32跑起来,在串口里看到“Hello World”。听起来简单,但这里面藏着不少坑。我当年第一次烧录时,折腾了整整一个下午才看到那行字。嗯,咱们争取半小时搞定。
2.1 创建Hello World工程
我个人习惯用ESP-IDF的命令行工具来创建工程。别怕敲命令,其实就一行的事。
idf.py create-project hello_world
这条命令会在当前目录下生成一个 hello_world 文件夹。进去看看,结构很简单:
- main/ — 存放源代码的目录
- CMakeLists.txt — 顶层构建文件
- main/CMakeLists.txt — 组件构建文件
- main/hello_world_main.c — 主程序文件
打开 hello_world_main.c,把默认代码替换成下面这个:
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
void app_main(void)
{
printf("Hello World!\n");
/* 打印芯片信息 */
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
printf("芯片型号: %s, 核心数: %d\n",
CONFIG_IDF_TARGET,
chip_info.cores);
/* 循环打印,方便观察 */
for (int i = 10; i >= 0; i--) {
printf("倒计时: %d 秒\n", i);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
printf("重启中...\n");
}
这里有个小细节:app_main 是ESP32的入口函数,不是 main。我第一次写的时候直接写了 main(),编译报错半天没反应过来。
2.2 编译流程解析
编译ESP32固件,说白了就是执行一条命令:
idf.py build
但这条命令背后干了什么?我拆开给你看:
| 步骤 | 工具 | 作用 |
|---|---|---|
| 1. 配置 | cmake | 读取CMakeLists.txt,生成构建系统文件 |
| 2. 编译 | gcc / xtensa-esp32-elf-gcc | 将 .c 文件编译为 .o 目标文件 |
| 3. 链接 | ld | 将所有 .o 文件和库链接成 .elf 文件 |
| 4. 生成二进制 | esptool.py / objcopy | 将 .elf 转换为 .bin 烧录文件 |
你可能会问:为什么要有这么多步骤?直接生成 .bin 不行吗?
其实每个步骤都有它的意义。比如链接阶段,ESP-IDF会把FreeRTOS、WiFi协议栈、蓝牙协议栈全部链接进来。我曾经遇到一个坑:编译通过了,但烧录后芯片不停重启。查了半天,发现是链接时某个库版本不匹配。
build/ 目录下你会看到:
hello_world.elf— 带调试信息的可执行文件hello_world.bin— 纯二进制固件,烧录用flash_args— 烧录参数文件,后面会用到
2.3 使用esptool.py进行首次烧录
esptool.py 是乐鑫官方提供的烧录工具。其实 idf.py flash 底层调用的就是它。但我建议你直接学esptool.py,因为后面做OTA升级时,你大概率需要手动调用它。
先确认一下你的ESP32连接状态。把开发板通过USB连到电脑,然后:
esptool.py --port /dev/ttyUSB0 flash_id
如果返回芯片型号和Flash大小,说明连接正常。如果报错,八成是驱动没装好,或者端口号不对。Windows用户一般是 COM3、COM4 这样的。
接下来正式烧录:
esptool.py --chip esp32 \
--port /dev/ttyUSB0 \
--baud 921600 \
write_flash \
0x1000 build/bootloader/bootloader.bin \
0x8000 build/partition_table/partition-table.bin \
0x10000 build/hello_world.bin
这三个地址分别对应:
- 0x1000 — Bootloader,引导程序
- 0x8000 — 分区表,告诉芯片Flash怎么划分
- 0x10000 — 应用程序,我们的Hello World
2.4 串口监视器查看日志
烧录完成后,芯片会自动重启。如果没看到输出,别急,打开串口监视器:
idf.py monitor
或者直接用 screen 或 putty:
screen /dev/ttyUSB0 115200
波特率是 115200,这是ESP32默认的日志输出速率。你应该会看到类似这样的输出:
Hello World!
芯片型号: esp32, 核心数: 2
倒计时: 10 秒
倒计时: 9 秒
...
倒计时: 0 秒
重启中...
看到这行字,恭喜你,ESP32已经跑起来了!
Ctrl+] 退出monitor。如果日志刷得太快看不清,可以在 printf 里加个延时,或者用 ESP_LOGI 替代 printf,它自带时间戳和日志级别过滤。
2.5 本章知识结构图
下面这张图把整个流程串起来了,你可以对照着回顾一下:
2.6 避坑指南
最后,分享几个我踩过的坑:
- 波特率不匹配: 烧录时用921600,监视时用115200。这两个不一样,别搞混了。
- Flash加密: 如果你买的模组开启了Flash加密,直接用esptool.py烧录会失败。需要先关闭加密,或者用加密烧录模式。
- 电源不足: ESP32瞬间电流能到500mA。我曾经用电脑USB口供电,结果烧录到一半掉线了。换个带外接电源的USB Hub就好了。
- 日志乱码: 如果看到一堆乱码,八成是波特率设错了。检查一下你的串口工具配置。
好了,到这里你已经完成了ESP32的首次烧录。从创建工程到编译,再到烧录和查看日志,整个流程都走了一遍。后面我们会在这个基础上,一步步加入OTA升级的功能。