xiaozhi-esp32 自定义开发板接入指南:从 config.json 到固件构建的完整流程
【免费下载链接】xiaozhi-esp32An MCP-based chatbot | 一个基于MCP的聊天机器人项目地址: https://gitcode.com/GitHub_Trending/xia/xiaozhi-esp32
本指南以 docs/custom-board.md 为核心骨架,讲解如何在 xiaozhi-esp32 开源项目中从零新增一款自定义开发板:包括目录结构约定、config.h/config.json配置要点、板级 C++ 类的实现、Kconfig 与 CMake 的构建系统接入,以及通过scripts/build.py一键配置编译的完整流程。读完本文,你将具备把任意基于 ESP32 系列芯片的硬件(音频编解码器、屏幕、按键、4G 模块等组合)接入该项目,并生成带独立 OTA 固件通道的完整能力。
适用前提:本文基于当前仓库的实际代码(
main/boards/下 70+ 款板级实现、scripts/build.py、main/Kconfig.projbuild 与 main/CMakeLists.txt)编写;文中涉及的 ESP-IDF、LVGL 等外部工具链的使用以其官方文档为准。
一、为什么需要“自定义开发板”机制
xiaozhi-esp32 是一个基于 ESP32 系列芯片的 AI 语音助手项目,支持 70 多款官方与社区开发板,每一款都在main/boards/下拥有自己的独立目录。新增硬件支持的本质,就是为你的板卡建立一套"身份 + 配置 + 实现"三件套:
- 身份(identity):由
config.json中的type、name字段决定,固件会向服务端上报该标识,OTA 升级也依赖它定位固件; - 配置(configuration):
config.h中的引脚、采样率、屏幕参数等硬件级设定; - 实现(implementation):
xxx_board.cc中继承基类的板级初始化与虚函数实现。
最重要的一条警告:永远不要覆盖已有板卡的配置
Warning:对于 IO 配置与现有板卡不同的自定义硬件,绝不要直接覆盖原有板卡的配置文件。必须新建一种板型,或利用
config.json的builds数组生成固件名不同、sdkconfig宏不同的独立变体。构建请使用python scripts/build.py [board-directory]。
原因在 docs/custom-board.md 中有明确说明:覆盖已有板卡配置是危险的,因为 OTA 更新可能用原板卡的官方固件替换你的自定义固件。每一款板卡都必须拥有唯一身份和自己的固件更新通道——这与 scripts/build.py 中_collect_variants()对type/name全局唯一性的严格校验(重复即报错)是相互印证的。
二、目录布局约定
一个板卡目录通常包含以下文件:
| 文件 | 作用 |
|---|---|
xxx_board.cc | 板级初始化和胶水代码(板级类、虚函数覆写、DECLARE_BOARD注册) |
config.h | 引脚分配与板级设置 |
config.json | 上报的板卡类型与发布配置,供 CMake 和scripts/build.py消费 |
README.md | 板卡专属说明(硬件要求、烧录方法、特殊注意事项) |
板卡可以平铺在main/boards/下,也可以按厂商分组放在main/boards/<manufacturer>/<board>/下(详见后文"厂商子目录"一节)。当前仓库两种布局都有实例,例如平铺的 main/boards/bread-compact-wifi/ 与分组存放的 main/boards/waveshare/esp32-p4-nano/。
三、第一步:创建板卡目录
在main/boards/下按[厂商]-[型号]的命名风格新建目录(示例:m5stack-tab5):
mkdir main/boards/my-custom-board命名约定:目录名使用小写字母与连字符(如waveshare、lceda-course-examples),便于与 Kconfig 符号、构建产物名称保持一致。
四、第二步:编写配置文件
4.1 config.h:引脚与硬件参数
config.h集中定义全部硬件设定,主要包括:
- 音频采样率与 I2S 引脚映射;
- 音频编解码器 I2C 地址与引脚;
- 按键与 LED 引脚;
- 显示参数与引脚。
以下示例取自 docs/custom-board.md(参照lichuang-c3-dev风格),AUDIO_I2S_GPIO_*系列宏被 main/boards/common/wifi_board.h 等基类通过GetAudioCodec()间接消费:
#ifndef _BOARD_CONFIG_H_ #define _BOARD_CONFIG_H_ #include <driver/gpio.h> // Audio #define AUDIO_INPUT_SAMPLE_RATE 24000 #define AUDIO_OUTPUT_SAMPLE_RATE 24000 #define AUDIO_I2S_GPIO_MCLK GPIO_NUM_10 #define AUDIO_I2S_GPIO_WS GPIO_NUM_12 #define AUDIO_I2S_GPIO_BCLK GPIO_NUM_8 #define AUDIO_I2S_GPIO_DIN GPIO_NUM_7 #define AUDIO_I2S_GPIO_DOUT GPIO_NUM_11 #define AUDIO_CODEC_PA_PIN GPIO_NUM_13 #define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_0 #define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_1 #define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR // Buttons #define BOOT_BUTTON_GPIO GPIO_NUM_9 // Display #define DISPLAY_SPI_SCK_PIN GPIO_NUM_3 #define DISPLAY_SPI_MOSI_PIN GPIO_NUM_5 #define DISPLAY_DC_PIN GPIO_NUM_6 #define DISPLAY_SPI_CS_PIN GPIO_NUM_4 #define DISPLAY_WIDTH 320 #define DISPLAY_HEIGHT 240 #define DISPLAY_MIRROR_X true #define DISPLAY_MIRROR_Y false #define DISPLAY_SWAP_XY true #define DISPLAY_OFFSET_X 0 #define DISPLAY_OFFSET_Y 0 #define DISPLAY_BACKLIGHT_PIN GPIO_NUM_2 #define DISPLAY_BACKLIGHT_OUTPUT_INVERT true #endif // _BOARD_CONFIG_H_参数说明与取值提示:
AUDIO_INPUT/OUTPUT_SAMPLE_RATE:语音链路采样率,项目常见取值为 16000 或 24000,需与所选音频编解码器及服务端协议匹配;AUDIO_CODEC_PA_PIN:功放(PA)使能引脚,若硬件无独立 PA 使能,可留空或不定义;DISPLAY_MIRROR_X/Y与DISPLAY_SWAP_XY:屏幕镜像与横竖屏交换开关,与后文esp_lcd_panel_mirror()/esp_lcd_panel_swap_xy()调用一一对应;DISPLAY_BACKLIGHT_OUTPUT_INVERT:背光 PWM 极性是否反转,取决于背光驱动电路是共阳还是共阴;DISPLAY_WIDTH/HEIGHT/OFFSET_*:分辨率与偏移,用于SpiLcdDisplay的初始化参数。
4.2 config.json:兼容敏感的上报身份与构建配置
config.json定义了对兼容性敏感的上报类型,并驱动scripts/build.py:
{ "type": "my-custom-board", "target": "esp32s3", "builds": [ { "name": "my-custom-board", "sdkconfig_append": [ "CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y", "CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v2/8m.csv\"" ] } ] }字段含义:
| 字段 | 说明 |
|---|---|
type | 兼容敏感的板卡家族标识,固件上报给服务端。发布后应保持稳定,变更会导致 OTA 通道失效 |
target | 目标芯片,必须与真实硬件一致(esp32、esp32s3、esp32c3、esp32c6、esp32p4等) |
name | 发布构建上报的固件变体名,通常与type一致 |
sdkconfig_append | 追加到默认配置中的额外 sdkconfig 行 |
命名硬性约束:type与name只能包含小写字母、数字、句点(.)与连字符(-),不允许下划线、空格和大写字母。这条规则在 scripts/build.py 中由_REPORTED_IDENTIFIER_PATTERN = re.compile(r"^[a-z0-9.-]+$")与_validate_reported_identifier()强制校验,违规会直接抛出ValueError。
常用的sdkconfig_append条目:
// Flash size "CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y" "CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y" // Partition table "CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v2/4m.csv\"" "CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v2/8m.csv\"" // Audio pipeline "CONFIG_USE_DEVICE_AEC=y" // enable on-device AEC项目在适用目标上默认使用 16MB Flash 与partitions/v2/16m.csv。不要重复写入与项目/目标默认值一致的条目,只在确有板卡级差异时才使用sdkconfig_append覆盖。分区表定义见 partitions/v2/。
不要在板卡config.json中选择语言或指定唤醒词。这些属于用户构建选项,必须统一通过menuconfig或构建脚本参数配置,以保证 CLI、Agent 与在线构建共用同一套接口(这也正是 scripts/build.py 提供--language/--wake-word参数的原因)。
多变体(builds 数组)示例:以仓库现有板卡 main/boards/bread-compact-wifi/config.json 为例,同一块板通过两个 build 条目分别产出 128x32 与 128x64 OLED 的固件变体:
{ "type": "bread-compact-wifi", "target": "esp32s3", "builds": [ { "name": "bread-compact-wifi", "sdkconfig_append": ["CONFIG_OLED_SSD1306_128X32=y"] }, { "name": "bread-compact-wifi-128x64", "sdkconfig_append": ["CONFIG_OLED_SSD1306_128X64=y"] } ] }此外,builds条目还可选配idf_version字段(如"<6.0")做 ESP-IDF 版本门控,由 scripts/build.py 的_get_builds_for_idf()处理,便于在同一板卡下兼容不同 IDF 版本。
五、第三步:实现板级类
创建my_custom_board.cc,一个基础板级类包含四部分:
- 类声明:继承
WifiBoard(WiFi 板)或Ml307Board(4G 板); - 初始化辅助方法:I2C、显示、按键、IoT/MCP 工具等;
- 虚函数覆写:
GetAudioCodec()、GetDisplay()、GetBacklight()等; - 板卡注册:
DECLARE_BOARD(ClassName)。
完整示例(来自 docs/custom-board.md):
#include "wifi_board.h" #include "codecs/es8311_audio_codec.h" #include "display/lcd_display.h" #include "application.h" #include "button.h" #include "config.h" #include "mcp_server.h" #include <esp_log.h> #include <driver/i2c_master.h> #include <driver/spi_common.h> #define TAG "MyCustomBoard" class MyCustomBoard : public WifiBoard { private: i2c_master_bus_handle_t codec_i2c_bus_; Button boot_button_; LcdDisplay* display_; void InitializeI2c() { i2c_master_bus_config_t i2c_bus_cfg = { .i2c_port = I2C_NUM_0, .sda_io_num = AUDIO_CODEC_I2C_SDA_PIN, .scl_io_num = AUDIO_CODEC_I2C_SCL_PIN, .clk_source = I2C_CLK_SRC_DEFAULT, .glitch_ignore_cnt = 7, .intr_priority = 0, .trans_queue_depth = 0, .flags = { .enable_internal_pullup = 1, }, }; ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &codec_i2c_bus_)); } void InitializeSpi() { spi_bus_config_t buscfg = {}; buscfg.mosi_io_num = DISPLAY_SPI_MOSI_PIN; buscfg.miso_io_num = GPIO_NUM_NC; buscfg.sclk_io_num = DISPLAY_SPI_SCK_PIN; buscfg.quadwp_io_num = GPIO_NUM_NC; buscfg.quadhd_io_num = GPIO_NUM_NC; buscfg.max_transfer_sz = DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(uint16_t); ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO)); } void InitializeButtons() { boot_button_.OnClick([this]() { auto& app = Application::GetInstance(); if (app.GetDeviceState() == kDeviceStateStarting) { EnterWifiConfigMode(); return; } app.ToggleChatState(); }); } void InitializeDisplay() { esp_lcd_panel_io_handle_t panel_io = nullptr; esp_lcd_panel_handle_t panel = nullptr; esp_lcd_panel_io_spi_config_t io_config = {}; io_config.cs_gpio_num = DISPLAY_SPI_CS_PIN; io_config.dc_gpio_num = DISPLAY_DC_PIN; io_config.spi_mode = 2; io_config.pclk_hz = 80 * 1000 * 1000; io_config.trans_queue_depth = 10; io_config.lcd_cmd_bits = 8; io_config.lcd_param_bits = 8; ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI2_HOST, &io_config, &panel_io)); esp_lcd_panel_dev_config_t panel_config = {}; panel_config.reset_gpio_num = GPIO_NUM_NC; panel_config.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB; panel_config.bits_per_pixel = 16; ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(panel_io, &panel_config, &panel)); esp_lcd_panel_reset(panel); esp_lcd_panel_init(panel); esp_lcd_panel_invert_color(panel, true); esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY); esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y); display_ = new SpiLcdDisplay(panel_io, panel, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY); } void InitializeTools() { // Register MCP tools here; see docs/mcp-usage.md. } public: MyCustomBoard() : boot_button_(BOOT_BUTTON_GPIO) { InitializeI2c(); InitializeSpi(); InitializeDisplay(); InitializeButtons(); InitializeTools(); GetBacklight()->SetBrightness(100); } virtual AudioCodec* GetAudioCodec() override { static Es8311AudioCodec audio_codec( codec_i2c_bus_, I2C_NUM_0, AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE, AUDIO_I2S_GPIO_MCLK, AUDIO_I2S_GPIO_BCLK, AUDIO_I2S_GPIO_WS, AUDIO_I2S_GPIO_DOUT, AUDIO_I2S_GPIO_DIN, AUDIO_CODEC_PA_PIN, AUDIO_CODEC_ES8311_ADDR); return &audio_codec; } virtual Display* GetDisplay() override { return display_; } virtual Backlight* GetBacklight() override { static PwmBacklight backlight(DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT); return &backlight; } }; DECLARE_BOARD(MyCustomBoard);实现要点:
- 基类
WifiBoard定义于 main/boards/common/wifi_board.h,它继承自Board,默认的GetAudioCodec()返回nullptr,并提供EnterWifiConfigMode()等辅助方法;板级类只需覆写自己需要的虚函数; - 按键回调中
kDeviceStateStarting状态下进入 WiFi 配网模式(EnterWifiConfigMode()),否则切换对话状态(app.ToggleChatState()),这是项目通用的按键交互范式; InitializeTools()留空即支持 MCP 工具注册(如喇叭控制、亮度调节、电量读取等),详见 MCP 使用指南;- 若板卡带摄像头,可参考 main/boards/common/esp32_camera.h 或 main/boards/common/esp_video.h 接入。
六、第四步:接入构建系统
6.1 在 Kconfig 中添加条目
打开 main/Kconfig.projbuild,在choice BOARD_TYPE("Target Board" 选择块)中追加条目:
choice BOARD_TYPE prompt "Target Board" default BOARD_TYPE_BREAD_COMPACT_WIFI help Board type. # ... other entries ... config BOARD_TYPE_MY_CUSTOM_BOARD bool "My Custom Board" depends on IDF_TARGET_ESP32S3 # pick the matching target endchoice注意(与 main/Kconfig.projbuild 中现有 70+ 条目保持一致):
- 符号标识必须大写、下划线分隔(如
BOARD_TYPE_MY_CUSTOM_BOARD); depends on将条目限定到正确目标芯片(IDF_TARGET_ESP32S3、IDF_TARGET_ESP32C3等);bool后的标签文本可以是中英文或本地化描述;- 该 choice 为每个目标芯片配置了默认板型(如 ESP32-S3 默认
BOARD_TYPE_BREAD_COMPACT_WIFI)。
6.2 在 CMakeLists.txt 中添加分支
打开 main/CMakeLists.txt,在板型条件链中追加:
elseif(CONFIG_BOARD_TYPE_MY_CUSTOM_BOARD) set(BOARD_DIR "my-custom-board") set(BUILTIN_TEXT_FONT font_puhui_basic_20_4) # pick a font for the display set(BUILTIN_ICON_FONT font_awesome_20_4) set(DEFAULT_EMOJI_COLLECTION twemoji_64) # optional, for emoji display构建系统从main/boards/${BOARD_DIR}/加载源码,并从该目录的config.json读取上报板型。若config.json或其顶层type缺失,则回退使用完整BOARD_DIR(将/替换为-)作为板型。当前仓库 main/CMakeLists.txt 中的真实分支(如CONFIG_BOARD_TYPE_ESP32_S3_BOX_3对应set(BOARD_DIR "espressif/esp32-s3-box-3"))可直接对照参考。
字体与 emoji 选择建议(按屏幕分辨率匹配):
- 小屏(128x64 OLED):
font_puhui_basic_14_1/font_awesome_14_1 - 中小屏(240x240):
font_puhui_basic_16_4/font_awesome_16_4 - 中屏(240x320):
font_puhui_basic_20_4/font_awesome_20_4 - 大屏(480x320 及以上):
font_puhui_basic_30_4/font_awesome_30_4
emoji 集合:twemoji_32(32x32 像素,适合小屏)、twemoji_64(64x64 像素,适合大屏)。仓库中的高分辨率板型(如 main/boards/espressif/esp32-s3-box-3/)实际使用noto-color-emoji_128等更高规格集合,可按需取用。
七、第五步:构建与烧录
方案 A:手动使用 idf.py
- 设置目标芯片(首次或切换目标时):
idf.py set-target esp32s3 # ESP32-S3 idf.py set-target esp32c3 # ESP32-C3 idf.py set-target esp32 # ESP32 - 清理过期配置:
idf.py fullclean - 通过 menuconfig 选择板型:
idf.py menuconfig导航到
Xiaozhi Assistant -> Target Board,选择你的板卡。 - 构建并烧录:
idf.py build idf.py flash monitor
方案 B:使用 build.py(推荐)
只要板卡目录包含config.json,即可一键配置并构建:
python scripts/build.py my-custom-board语言与唤醒词属于用户构建选项,可显式指定:
python scripts/build.py my-custom-board \ --language en-US \ --wake-word wn9_jarvis_tts参数取值规则(与 scripts/build.py 的_normalize_language()、_wake_word_sdkconfig_options()实现一致):
--language接受 main/assets/locales/ 下列出的任意 locale;--wake-word接受 ESP-SR 模型名、nihaoxiaozhi(按目标芯片自动选择兼容模型)或disabled;- ESP32-C3/C5/C6 目标仅支持 WakeNet9s 模型(
wn9s_*);ESP32-S3/P4/S31 构建自动使用 AFE 唤醒词引擎;ESP32 使用 ESP 唤醒词引擎——目标不匹配会被显式拒绝(见_WAKE_WORD_MODEL_PATTERN与目标判定逻辑)。
以文本或机器可读形式查询可选值:
python scripts/build.py --list-languages python scripts/build.py --list-languages --json python scripts/build.py --list-wake-words python scripts/build.py --list-wake-words --json注意:唤醒词列表来自当前解析的 ESP-SR 组件。若
managed_components/尚未生成,请先执行idf.py reconfigure再查询。
build.py的完整行为(见 scripts/build.py):
- 无参数运行时打印帮助;用
--list-boards列出所有板型与变体(含厂商前缀的发布名,如waveshare-esp32-p4-nano-10.1-a); - 所选板卡存在多个 builds 变体时,会交互式提示选择;非交互环境需传
--name <variant>; - 从
config.json读取target,仅当已有构建目录的目标芯片不同才执行清理,然后通过一次idf.py reconfigure完成目标芯片、板名、默认配置与所选 build 的sdkconfig_append配置(写入build/xiaozhi-build.sdkconfig.defaults片段);随后的idf.py build直接复用该配置; - 将所选 build 的
name作为上报的固件变体名(-DBOARD_NAME=...); - 默认只生成
build/merged-binary.bin而不打包 ZIP;传--zip才会生成releases/v<version>_<name>.zip; - 对上报标识(
type/name)做全局唯一性校验,重复会直接报错,防止 OTA 身份冲突; - 若
sdkconfig_append中写了CONFIG_USE_ESP_BLUFI_WIFI_PROVISIONING=y,会自动补全其 Kconfig select 依赖(蓝牙相关宏),模拟 menuconfig 行为。
八、第六步:编写 README
在板卡目录的README.md中描述:板卡硬件组成、硬件要求、构建与烧录说明、以及任何特殊注意事项(例如仓库中 main/boards/sensecap-watcher/README.md 这类板级文档的写法)。
九、厂商子目录布局
当同一厂商有多款变体时,推荐使用main/boards/<manufacturer>/<board>/分组布局,例如 main/boards/waveshare/esp32-p4-nano/ 或 main/boards/lceda-course-examples/eda-tv-pro/。
厂商子目录下的板卡,需在config.json中声明同样值的字段,例如"manufacturer": "waveshare"。固件会将其作为board.manufacturer与board.type、board.name一并上报;平铺的社区板卡不声明该字段,上报空厂商字符串。同时在 CMake 分支中将BOARD_DIR设为相对main/boards/的完整路径:
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_NANO) set(BOARD_DIR "waveshare/esp32-p4-nano") set(BUILTIN_TEXT_FONT font_puhui_basic_30_4) set(BUILTIN_ICON_FONT font_awesome_30_4) set(DEFAULT_EMOJI_COLLECTION twemoji_64)一致性约束(由 scripts/build.py 的_collect_variants()强制校验):板卡位于某厂商子目录下时,config.json必须声明且只能声明与该子目录同名的manufacturer,否则报错;反之,平铺板卡声明manufacturer也会报错并提示移动目录。
布局经验法则:
- 同一厂商有两款以上共享驱动、资源或文档的板卡时,使用厂商布局;
- 一次性板卡与社区示例使用平铺布局;
- 目录名使用小写与连字符。
十、可复用的公共组件
main/boards/common/提供了大量可直接复用的组件,板级类可直接包含使用:
显示驱动
支持的 LCD 系列包括 ST7789(SPI)、ILI9341(SPI)、SH8601(QSPI)等,完整驱动见 main/display/lcd_display.h 及相关面板代码。
音频编解码器(main/audio/codecs/)
Es8311AudioCodec(最常见)Es8374AudioCodecEs8388AudioCodecEs8389AudioCodecBoxAudioCodec(ES7210 麦克风阵列 + 编解码器组合,用于 ESP-Box 系列板卡)NoAudioCodec(无外部编解码器,直接 I2S)DummyAudioCodec(无音频板卡的占位实现)
电源管理
Axp2101:电源管理 IC 辅助类;Sy6970:电池充电器辅助类;AdcBatteryMonitor:基于 ADC 的简单电池电压监测;PowerSaveTimer/SleepTimer:浅睡眠调度辅助。
以上实现在 main/boards/common/ 下均有对应文件(如 main/boards/common/axp2101.cc、main/boards/common/adc_battery_monitor.cc)。
网络基类
WifiBoard:纯 WiFi 基类;Ml307Board/Nt26Board:4G 模组基类;DualNetworkBoard:WiFi/4G 可切换基类;RndisBoard:RNDIS-over-USB 网络(ESP32-S3 / ESP32-P4);EspVideo:ESP32-S3 / ESP32-P4 上的 ESP-Video 辅助。
输入辅助
Button:标准按键(单击、长按、多击);Knob:旋转编码器封装;PressToTalkMcpTool:按键通话工具,通过 MCP 自注册;SystemReset:开机长按按键执行安全出厂重置。
MCP 集成
任何板卡都可以注册自定义 MCP 工具——喇叭控制、屏幕亮度、电池读数、灯光控制等,详见 MCP 使用指南。
十一、板级类继承体系
从 main/boards/common/board.h 与 main/boards/common/wifi_board.h 的源码结构可以梳理出如下继承关系:
Board:基类WifiBoard:WiFi 连接板卡Ml307Board/Nt26Board:4G 模组板卡DualNetworkBoard:WiFi + 4G 可切换板卡RndisBoard:RNDIS-over-USB 板卡
选择基类时:纯 WiFi 板继承WifiBoard;4G 模组板继承Ml307Board/Nt26Board;两者皆有可能的板继承DualNetworkBoard。
十二、开发技巧与排错指南
技巧
- 从相似板卡开始:复制并修改现有板卡通常比从零开始更快——比如你的硬件用 ES8311 + ST7789,就直接参考 main/boards/espressif/esp32-s3-box-lite/ 这类板卡;
- 增量点亮:先让屏幕正常显示,再调音频,最后跑完整业务栈;
- 逐脚核对:
config.h中每个引脚都必须与原理图一致; - 检查硬件兼容性:尤其注意编解码器/PMIC/触摸控制器的组合。
排错
- 显示异常:检查 SPI 配置、镜像方向与颜色反转(
esp_lcd_panel_invert_color)是否正确; - 无音频:检查 I2S 接线、PA 使能引脚与编解码器 I2C 地址;
- 无法连接 WiFi:复查 WiFi 凭据与配网方式;
- 无法连接服务器:验证 WebSocket / MQTT 端点配置。
十三、参考链接
- ESP-IDF 官方文档:https://docs.espressif.com/projects/esp-idf/
- LVGL 官方文档:https://docs.lvgl.io/
- ESP-SR 仓库:https://github.com/espressif/esp-sr
延伸阅读:本文关联的原始指南见 docs/custom-board.md(另有 docs/custom-board_zh.md 中文版);MCP 工具注册细节见 MCP 使用指南;板卡上报协议可参考 docs/websocket.md 与 docs/mqtt-udp.md。
【免费下载链接】xiaozhi-esp32An MCP-based chatbot | 一个基于MCP的聊天机器人项目地址: https://gitcode.com/GitHub_Trending/xia/xiaozhi-esp32
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考