ESP32C3开发指南(基于IDF):console控制台命令行交互功能
一、使用以下指令,创建一个工程console,意思就是控制台交互例程
idf.py create-project console

二、进到console工程,使用以下指令对工程进行编译
idf.py build

三、在主函数文件中,写入如下代码

/* Basic console example (esp_console_repl API)This example code is in the Public Domain (or CC0 licensed, at your option.)Unless required by applicable law or agreed to in writing, thissoftware is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES ORCONDITIONS OF ANY KIND, either express or implied.
*/#include <stdio.h>// 标准输入输出(如 printf)
#include <string.h> // 字符串处理(如 strcmp)
#include "esp_system.h"// ESP 系统基础功能(如复位、版本)
#include "esp_log.h" // ESP 日志模块(打印调试信息)
#include "esp_console.h"// ESP 控制台核心 API(命令注册、REPL 交互)
#include "esp_vfs_dev.h" // ESP VFS(虚拟文件系统)设备适配(如串口、USB)
#include "esp_vfs_fat.h"// ESP VFS-FAT 文件系统适配(用于存储命令历史)
#include "nvs.h"// NVS(非易失性存储)基础接口
#include "nvs_flash.h"// NVS Flash 初始化与操作
#include "cmd_system.h"// 系统相关命令(如重启、查看版本,官方预定义命令)
#include "cmd_wifi.h"// WiFi 相关命令(如连接热点、查看状态,官方预定义命令)
#include "cmd_nvs.h"// NVS 相关命令(如读写 NVS 数据,官方预定义命令)/* * 警告:若启用了「次级串口控制台」,会打印此提示。* 原因:次级串口控制台仅支持「输出」,不支持「输入」,而交互式控制台需要输入(如敲命令),因此次级控制台无实际用途。* 解决:若看到此警告,在 menuconfig 中禁用次级串口控制台(路径:Component config → ESP System Settings → Secondary console)。*/
#if SOC_USB_SERIAL_JTAG_SUPPORTED// 判断芯片是否支持 USB Serial/JTAG(如 ESP32-C3、S3)
#if !CONFIG_ESP_CONSOLE_SECONDARY_NONE// 判断是否未禁用次级控制台
#warning "A secondary serial console is not useful when using the console component. Please disable it in menuconfig."
#endif
#endifstatic const char* TAG = "example";// 日志标签(打印日志时用于区分模块)
#define PROMPT_STR CONFIG_IDF_TARGET// 控制台提示符前缀,值为芯片型号(如 "esp32c3",由 CONFIG_IDF_TARGET 自动定义)/* * 控制台命令历史功能:可将历史命令存储到文件、从文件加载(避免重启后丢失历史)。* 实现方式:基于 Wear Levelling(磨损均衡)库,在 Flash 上挂载 FATFS 文件系统,用于存储历史文件。* 注:此功能需在 menuconfig 中启用(CONFIG_CONSOLE_STORE_HISTORY)。*/
#if CONFIG_CONSOLE_STORE_HISTORY // 若启用了「命令历史存储」配置#define MOUNT_PATH "/data" // FATFS 文件系统挂载路径(后续访问文件需用此路径前缀)
#define HISTORY_PATH MOUNT_PATH "/history.txt"// 命令历史文件路径(历史命令会写入此文件)/*** @brief 初始化文件系统(用于存储命令历史)* 功能:在 Flash 的 "storage" 分区上挂载 FATFS,开启读写权限,支持磨损均衡。*/
static void initialize_filesystem(void)
{static wl_handle_t wl_handle;// 磨损均衡句柄(静态变量,确保生命周期与程序一致)// FATFS 挂载配置:最大支持 4 个同时打开的文件,若挂载失败则自动格式化分区const esp_vfs_fat_mount_config_t mount_config = {.max_files = 4,.format_if_mount_failed = true};// 执行挂载:参数依次为「挂载路径」「Flash 分区名」「挂载配置」「磨损均衡句柄」esp_err_t err = esp_vfs_fat_spiflash_mount_rw_wl(MOUNT_PATH, "storage", &mount_config, &wl_handle);if (err != ESP_OK) {// 若挂载失败,打印错误日志(不终止程序,仅禁用历史功能)ESP_LOGE(TAG, "Failed to mount FATFS (%s)", esp_err_to_name(err));return;}
}
#endif // CONFIG_STORE_HISTORY/*** @brief 初始化 NVS(非易失性存储)* 功能:NVS 用于存储设备配置(如 WiFi 密码、用户参数),控制台部分命令(如 cmd_nvs)依赖 NVS 工作。* 处理逻辑:若 NVS 无空闲页或版本不匹配,先擦除 NVS 再初始化。*/
static void initialize_nvs(void)
{esp_err_t err = nvs_flash_init();// 初始化 NVS Flash// 处理两种常见错误:1. NVS 无空闲页;2. NVS 版本更新(需擦除旧数据)if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {ESP_ERROR_CHECK( nvs_flash_erase() );// 擦除 NVS 所有数据err = nvs_flash_init();// 重新初始化 NVS}ESP_ERROR_CHECK(err);// 若仍有错误,终止程序(NVS 初始化失败会影响后续功能)
}
//打印HELLOWORLD
static int helloworld_cmd(int argc, char **argv)
{ESP_LOGI(TAG,"you enter the helloworld cmd");return 0;
}
//LED 控制命令的处理函数(输入 "led on" 或 "led off" 时执行)
static int cmd_led_control(int argc, char **argv)
{// 检查参数数量:必须输入 "led on" 或 "led off"(argc=2)if (argc != 2) {ESP_LOGI(TAG,"用法错误!正确用法:led on/off\n");return ESP_ERR_INVALID_ARG; // 返回错误码,控制台会提示}// 处理 "on" / "off" 命令if (strcmp(argv[1], "on") == 0) {ESP_LOGI(TAG,"LED 已点亮(GPIO15)\n");} else if (strcmp(argv[1], "off") == 0) {ESP_LOGI(TAG,"LED 已熄灭(GPIO15)\n");} else {ESP_LOGI(TAG,"参数错误!仅支持 on/off\n");return ESP_ERR_INVALID_ARG;}return ESP_OK; // 返回成功,控制台无额外提示
}//注册命令
void set_cmd_init(void)
{//打印helloworld命令const esp_console_cmd_t cmd1 = {.command = "helloworld",.help = "this command just for test helloworld",.hint = NULL,.func = helloworld_cmd,};esp_console_cmd_register(&cmd1);const esp_console_cmd_t cmd_led = {.command = "led", // 命令名(用户输入的关键词).help = "控制 LED 开关,用法:led on/off", // 帮助信息(输入 "help led" 时显示).hint = "<on/off>", // 命令提示(输入 "led " 按 Tab 键会显示).func = cmd_led_control // 绑定命令的处理函数};ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_led)); // 注册到控制台}void app_main(void)
{esp_console_repl_t *repl = NULL;// REPL 句柄(指向控制台交互实例,后续用于控制控制台生命周期)//初始化 REPL 配置:使用默认配置(可后续修改提示符、命令长度等)esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();/* 自定义控制台提示符:格式为「芯片型号>」(如 "esp32c3>")* 作用:提示用户可输入命令,可根据需求修改(如改为 "cmd>")*/repl_config.prompt = PROMPT_STR ">";// 最大命令行长度:由配置项 CONFIG_CONSOLE_MAX_COMMAND_LINE_LENGTH 定义(默认 256 字符)repl_config.max_cmdline_length = CONFIG_CONSOLE_MAX_COMMAND_LINE_LENGTH;// 3. 初始化基础依赖模块initialize_nvs();// 先初始化 NVS(控制台命令可能依赖 NVS)// 若启用命令历史存储,初始化文件系统并配置历史文件路径
#if CONFIG_CONSOLE_STORE_HISTORYinitialize_filesystem(); // 挂载 FATFSrepl_config.history_save_path = HISTORY_PATH; // 指定历史命令存储文件ESP_LOGI(TAG, "Command history enabled");// 打印日志:历史功能已启用
#elseESP_LOGI(TAG, "Command history disabled");
#endif// 4. 注册控制台命令(用户输入的命令需先注册才能被识别)esp_console_register_help_command();// 注册 "help" 命令(查看所有支持的命令)register_system_common(); // 注册系统通用命令(如 "restart" 重启、"version" 查看版本)//register_system_sleep();
#if CONFIG_ESP_WIFI_ENABLED // 若启用了 WiFi 配置,注册 WiFi 相关命令register_wifi();// 注册 WiFi 命令(如 "wifi connect" 连接热点、"wifi scan" 扫描热点
#endifregister_nvs();// 注册 NVS 命令(如 "nvs get" 读取 NVS、"nvs set" 写入 NVS)//注册自己的命令set_cmd_init();// 5. 根据配置选择控制台硬件接口(UART / USB CDC / USB Serial/JTAG)// 逻辑:通过 menuconfig 配置的「控制台类型」,初始化对应硬件并创建 REPL 实例
#if defined(CONFIG_ESP_CONSOLE_UART_DEFAULT) || defined(CONFIG_ESP_CONSOLE_UART_CUSTOM)// 情况1:使用 UART 作为控制台(默认 UART 或自定义 UART)esp_console_dev_uart_config_t hw_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); // UART 默认配置(波特率 115200 等)// 创建 UART 类型的 REPL 实例:参数为「UART 配置」「REPL 配置」「REPL 句柄指针」ESP_ERROR_CHECK(esp_console_new_repl_uart(&hw_config, &repl_config, &repl));#elif defined(CONFIG_ESP_CONSOLE_USB_CDC)// 情况2:使用 USB CDC 作为控制台(如 ESP32-S3 的 USB 虚拟串口)esp_console_dev_usb_cdc_config_t hw_config = ESP_CONSOLE_DEV_CDC_CONFIG_DEFAULT();// USB CDC 默认配置ESP_ERROR_CHECK(esp_console_new_repl_usb_cdc(&hw_config, &repl_config, &repl));#elif defined(CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG)// 情况3:使用 USB Serial/JTAG 作为控制台(如 ESP32-C3 的原生 USB 接口)esp_console_dev_usb_serial_jtag_config_t hw_config = ESP_CONSOLE_DEV_USB_SERIAL_JTAG_CONFIG_DEFAULT();ESP_ERROR_CHECK(esp_console_new_repl_usb_serial_jtag(&hw_config, &repl_config, &repl));#else
#error Unsupported console type
#endif// 6. 启动交互式控制台(REPL:Read-Eval-Print Loop,读-评-印循环)// 功能:启动后控制台会持续等待用户输入→解析命令→执行命令→打印结果,直到程序退出ESP_ERROR_CHECK(esp_console_start_repl(repl));
}
四、尝试编译一下,发现如下报错
报错核心原因是 找不到 cmd_system.h 头文件—— 这个文件是 ESP-IDF 官方控制台示例中 “系统命令模块” 的头文件

实际上这三个函数都找不到

五、在main目录下创建一个文件夹,叫做idf_component.yml, 这个文件是 ESP-IDF 工程的 组件依赖配置文件(通常命名为 idf_component.yml),核心作用是 告诉 ESP-IDF 编译器:工程需要依赖 cmd_system、cmd_nvs、cmd_wifi 这 3 个自定义组件,以及去哪里找到这些组件的源代码

六、在idf_component.yml文件下,写入如下代码

dependencies: # 固定关键字:表示“工程依赖的组件列表”cmd_system: # 组件名:自定义的组件标识(需和组件目录名一致)path: ${IDF_PATH}/examples/system/console/advanced/components/cmd_system # 组件路径:告诉编译器去哪里找这个组件的源代码cmd_nvs: # 第二个依赖组件:NVS 命令相关代码path: ${IDF_PATH}/examples/system/console/advanced/components/cmd_nvscmd_wifi: # 第三个依赖组件:WiFi 命令相关代码path: ${IDF_PATH}/examples/system/console/advanced/components/cmd_wifi
七、添加完后再编译一下,报如下错误,说是CONFIG_CONSOLE_MAX_COMMAND_LINE_LENGTH 这个配置宏未定义

八、输入以下指令,将工程配置为ESP32C3芯片
idf.py set-target esp32c3

九、输入以下指令,打开配置菜单
idf.py menuconfig

十、在这个位置,把控制台输出配置成USB模式

十一、先保存退出,回到代码,创建一个这个文件,然后在这个文件里面写入如下代码。这段代码是 ESP-IDF 中用于定义 menuconfig 配置菜单的 Kconfig 语法片段,作用是在配置界面中添加自定义选项,让用户可以可视化地配置控制台相关功能

# 定义一个名为 "Example Configuration" 的配置菜单(在 menuconfig 中会作为一个选项卡显示)
# 所有后续配置项都会包含在这个菜单下,直到 endmenu 结束
menu "Example Configuration"# 定义第一个配置项:是否在 Flash 中存储命令历史# "config" 是关键字,后面跟配置项名称(CONSOLE_STORE_HISTORY)config CONSOLE_STORE_HISTORYbool "Store command history in flash" # 配置项类型为 bool(布尔值,复选框),显示名称为"在 Flash 中存储命令历史"default y # 默认值为 y(yes,启用),即默认勾选此选项help # 帮助说明(用户按 ? 键时显示)Linenoise line editing library provides functions to save and loadcommand history. If this option is enabled, initalizes a FAT filesystemand uses it to store command history.# 中文含义:Linenoise 行编辑库提供了保存和加载命令历史的功能。# 若启用此选项,会初始化 FAT 文件系统,并用于存储命令历史(重启后不丢失)。# 定义第二个配置项:最大命令行长度config CONSOLE_MAX_COMMAND_LINE_LENGTHint "Maximum command line length" # 配置项类型为 int(整数),显示名称为"最大命令行长度"default 1024 # 默认值为 1024(单位:字符)help # 帮助说明This value marks the maximum length of a single command line. Once it isreached, no more characters will be accepted by the console.# 中文含义:此值表示单个命令行的最大长度。达到该长度后,控制台将不再接受更多字符。# 标志 "Example Configuration" 菜单定义结束
endmenu
十二、再编译一下,编译成功

十三、输入以下指令,进行编译、下载、调试

十四、下载程序,后发现有个报错,这是因为工程启用了 命令历史存储功能(CONFIG_CONSOLE_STORE_HISTORY=y),该功能需要在 Flash 中预留一个标签为 storage 的 FAT 格式分区,用于保存命令历史文件(history.txt),但当前工程的 分区表(partition table)中没有定义这个分区,因此挂载失败,但其实这个功能可有可无,它的作用就是板子重启后也能读取上你上一次敲的命令行

十五、回到代码,我们在工程目录下新建一个.csv文件,这个是分区表

十六、在分区表中写入如下代码
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
storage, data, fat, , 1M,

十七、我们的历史命令就存储在这个分区下,保存文件

十八、在终端输入以下指令,进入配置菜单
idf.py menuconfig

十九、进到这个路径,选择这个选项,意思就是自定义分区表 CSV 文件,保存并退出

二十、输入以下指令,再编译、下载程序
idf.py flash monitor

二十一、发现编译没过,报错, 说工程中缺少 partitions.csv 文件

二十二、发现原来是文件名不一致导致的

二十三、将文件名改成一致

二十四、再编译,还是报错, 说是分区表定义的总大小超过了配置的 Flash 容量:分区表总大小需要 2.1MB,但当前配置的 Flash 容量是 2MB(2048KB),空间不足导致编译失败,但是我的ESP32C3的FLASH有4M,所以我们修改以下menuconfig

二十五、在这个路径下,把flash修改成4M

二十六、再编译下载,成功了,可以敲出命令行了

二十七、可以输入help看看有什么命令

二十八、至此,命令行就可以用了

二十九、但是如果使用了历史命令存储,因为要写入文件的原因,敲命令行响应会比不使用历史命令存储要慢许多,大家可以两个都感受一下,就是连续敲击空格,看看新行的出现速度,但是不影响使用,只是有点不爽,这个功能也比较鸡肋,用处不大,大家按需开启

