小智AI xiaozhi-esp32 自定义开发板移植指南:从管脚映射到固件编译的完整实战
发布时间:2026/9/11 20:33:55 作者:尧图编辑部 阅读量:1,286

小智AI xiaozhi-esp32 自定义开发板移植指南从管脚映射到固件编译的完整实战【免费下载链接】xiaozhi-esp32An MCP-based chatbot | 一个基于MCP的聊天机器人项目地址: https://gitcode.com/GitHub_Trending/xia/xiaozhi-esp32小智AIxiaozhi-esp32是一个基于 MCP 的 ESP32 系列 AI 语音聊天机器人项目内置对 70 多种 ESP32 开发板的支持。本指南以仓库 docs/custom-board_zh.md 为核心完整讲解如何为项目新增一块自定义开发板从创建板级目录、编写config.h管脚映射与config.json构建配置到实现板级初始化 C 类、接入 Kconfig 与 CMake最后用scripts/build.py一键编译出可烧录固件。读完本文你将掌握一套可直接复制执行的开发板移植方法论并理解固件上报标识与 OTA 升级通道的耦合关系。一、重要提示为什么不能直接覆盖现有开发板配置在开始动手前必须理解一个关键约束。当自定义开发板的 IO 配置与某个已有开发板不同时切勿直接覆盖原有开发板的配置去编译固件。正确做法是创建全新的开发板类型或者通过config.json中builds数组配置不同的name与 sdkconfig 宏定义来区分。原因在于 OTA 升级机制每个开发板有唯一的标识和对应的固件升级通道固件上报的type/name直接决定了设备未来从哪个通道拉取升级包。如果直接覆盖原有配置OTA 升级时自定义固件可能被原有开发板的标准固件覆盖导致设备无法正常工作。保持开发板标识的唯一性至关重要。二、开发板目录结构与文件职责每个开发板在main/boards/下拥有独立目录通常包含四类文件文件职责xxx_board.cc主要的板级初始化代码实现板子相关的初始化和功能config.h板级配置文件定义硬件管脚映射和其他配置项config.json上报开发板类型及发布配置供 CMake 和scripts/build.py使用README.md开发板相关的说明文档仓库中大量真实示例遵循这一约定例如 bread-compact-wifi 目录 下同时存在config.h、config.json、compact_wifi_board.cc带厂商子目录的板卡则按厂商/板型两级组织如 waveshare/esp32-s3-touch-amoled-1.8 目录。三、定制开发板完整六步流程第 1 步创建新的开发板目录在main/boards/下创建新目录命名采用[品牌名]-[开发板类型]形式mkdir main/boards/my-custom-board若你的板卡归属于某个厂商应放入main/boards/厂商/板型/两级目录。从 scripts/build.py 的校验逻辑_collect_variants可以看出板卡位于厂商子目录时config.json中的manufacturer必须与目录名一致否则构建脚本会直接报错拒绝。第 2 步编写 config.h硬件管脚映射config.h定义全部硬件配置包括音频采样率与 I2S 管脚、编解码芯片 I2C 地址与管脚、按钮与 LED 管脚、显示屏参数与管脚等。以下示例来自 lichuang-c3-dev与文档一致#ifndef _BOARD_CONFIG_H_ #define _BOARD_CONFIG_H_ #include driver/gpio.h // 音频配置 #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 // 按钮配置 #define BOOT_BUTTON_GPIO GPIO_NUM_9 // 显示屏配置 #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通常为 24000项目音频管线的默认采样率。若板卡使用无编解码芯片的直连方案可在板级.cc中改用NoAudioCodec参考 compact_wifi_board.cc 中的NoAudioCodecDuplex/NoAudioCodecSimplex用法。显示屏相关宏会直接影响esp_lcd_panel_swap_xy、esp_lcd_panel_mirror与背光输出极性DISPLAY_BACKLIGHT_OUTPUT_INVERT控制背光电平是否反相。第 3 步编写 config.json上报标识与构建配置config.json定义兼容性上报类型与自动化编译配置{ type: my-custom-board, // 固件上报的开发板类型发布后应保持稳定 target: esp32s3, // 目标芯片型号: esp32, esp32s3, esp32c3, esp32c6, esp32p4等 builds: [ { name: my-custom-board, // 开发板名称用于生成固件包 sdkconfig_append: [ // 特别 Flash 大小配置 CONFIG_ESPTOOLPY_FLASHSIZE_8MBy, // 特别分区表配置 CONFIG_PARTITION_TABLE_CUSTOM_FILENAME\partitions/v2/8m.csv\ ] } ] }配置项说明manufacturer: 厂商子目录名称使用厂商目录时必须填写并作为board.manufacturer上报。平铺社区板可以省略上报为空字符串。真实示例见 waveshare/esp32-s3-touch-amoled-1.8/config.json。type: 固件上报的开发板系列类型发布后应保持稳定。target: 目标芯片型号必须与硬件匹配。name: release 构建上报的固件变体名称通常与type一致。sdkconfig_append: 额外的 sdkconfig 配置项数组会追加到默认配置中。命名约束与源码校验对应type和name只能包含小写字母、数字、点.和连字符-不允许使用下划线、空格或大写字母。这条规则在 scripts/build.py 的_validate_reported_identifier中有硬性校验正则^[a-z0-9.-]$build.py在列出板卡或编译前会检查所有板卡的type/name合法性违规板卡会被整体拒绝。常用的 sdkconfig_append 配置// Flash 大小 CONFIG_ESPTOOLPY_FLASHSIZE_4MBy // 4MB Flash CONFIG_ESPTOOLPY_FLASHSIZE_8MBy // 8MB Flash // 分区表 CONFIG_PARTITION_TABLE_CUSTOM_FILENAME\partitions/v2/4m.csv\ // 4MB 分区表 CONFIG_PARTITION_TABLE_CUSTOM_FILENAME\partitions/v2/8m.csv\ // 8MB 分区表 // 音频处理 CONFIG_USE_DEVICE_AECy // 启用设备端 AEC分区表 CSV 文件均位于仓库 partitions/v2/含 4m、8m、16m、32m 及 esp32c3 专用表。两条重要约定不要重复默认值对于适用的目标芯片项目默认使用 16MB Flash 和partitions/v2/16m.csv。若板卡配置与项目及目标芯片的有效默认值相同不要在sdkconfig_append中重复填写只保留板卡确实需要的覆盖项。各芯片的默认配置见仓库根目录的 sdkconfig.defaults 及sdkconfig.defaults.esp32s3等系列文件。不要选语言和唤醒词这些属于用户构建选项应统一通过menuconfig或构建脚本参数配置方便 CLI、Agent 和在线编译接口共用同一套参数。多变体Multi-build机制一个目录可以在builds数组下声明多个变体每个变体拥有独立的name与sdkconfig_append。典型示例见 bread-compact-wifi/config.json它同时声明了bread-compact-wifiSSD1306 128x32与bread-compact-wifi-128x64SSD1306 128x64两个固件变体编译时通过--name选择。这正是文档强调的通过 config.json 中的 builds 配置不同 name 来区分的落地方式。第 4 步编写板级初始化代码创建my_custom_board.cc实现开发板的所有初始化逻辑。一个基本的开发板类包含四部分类定义继承自WifiBoard或Ml307Board初始化函数包括 I2C、显示屏、按钮、IoT 等组件的初始化虚函数重写如GetAudioCodec()、GetDisplay()、GetBacklight()等注册开发板使用DECLARE_BOARD宏注册开发板完整示例与文档一致#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_; // I2C初始化 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_)); } // SPI初始化用于显示屏 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(); }); } // 显示屏初始化以ST7789为例 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); } // MCP Tools 初始化 void InitializeTools() { // 参考 MCP 文档 } 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);源码级补充解读DECLARE_BOARD宏定义在 main/boards/common/board.h 中展开后生成全局工厂函数void* create_board()返回new BOARD_CLASS_NAME()Board::GetInstance()通过static_castBoard*(create_board())获取单例。每个固件只能注册一个板级类。Board基类board.h声明的关键纯虚函数包括GetBoardType()、GetAudioCodec()、GetNetwork()、StartNetwork()、GetNetworkStateIcon()、SetPowerSaveLevel()、GetBoardJson()等GetDisplay()、GetLed()、GetBacklight()等为可选的默认实现默认返回nullptr。WifiBoardwifi_board.h在Board之上封装了 Wi-Fi 连接管理、EnterWifiConfigMode()配网模式、网络事件回调等能力如果你使用 4G 模组如 ML307则应继承Ml307Board或DualNetworkBoard。按钮回调中app.GetDeviceState() kDeviceStateStarting表示设备处于启动配网状态此时单击进入 Wi-Fi 配网模式EnterWifiConfigMode()否则切换对话状态app.ToggleChatState()。真实项目中的更复杂按钮触摸对讲、音量加减、长按最大/静音可参考 compact_wifi_board.cc 的InitializeButtons()。InitializeTools()用于注册 MCP 工具如灯控LampController可参考 MCP 协议文档 与 MCP 使用指南。第 5 步接入构建系统Kconfig CMakeLists在 main/Kconfig.projbuild 中添加开发板选项打开 main/Kconfig.projbuild在choice BOARD_TYPE中添加新配置项choice BOARD_TYPE prompt Board Type default BOARD_TYPE_BREAD_COMPACT_WIFI help Board type. 开发板类型 # ... 其他开发板选项 ... config BOARD_TYPE_MY_CUSTOM_BOARD bool My Custom Board (我的自定义开发板) depends on IDF_TARGET_ESP32S3 # 根据你的目标芯片修改 endchoice注意事项BOARD_TYPE_MY_CUSTOM_BOARD是配置项名称需要全大写使用下划线分隔。depends on指定目标芯片类型如IDF_TARGET_ESP32S3、IDF_TARGET_ESP32C3等。仓库真实示例见 Kconfig.projbuild 中BOARD_TYPE_BREAD_COMPACT_WIFI等配置的写法默认项带if IDF_TARGET_ESP32S3约束。描述文字可以使用中英文。在 main/CMakeLists.txt 中添加开发板配置打开 main/CMakeLists.txt在板型判断链中添加分支# 在 elseif 链中添加你的开发板配置 elseif(CONFIG_BOARD_TYPE_MY_CUSTOM_BOARD) set(BOARD_DIR my-custom-board) # 相对于 main/boards 的完整路径 set(BUILTIN_TEXT_FONT font_puhui_basic_20_4) # 根据屏幕大小选择合适的字体 set(BUILTIN_ICON_FONT font_awesome_20_4) set(DEFAULT_EMOJI_COLLECTION twemoji_64) # 可选如果需要表情显示 endif()仓库真实分支可参考 CMakeLists.txt 第 93 行起的if(CONFIG_BOARD_TYPE_BREAD_COMPACT_WIFI)分支其中set(BOARD_DIR bread-compact-wifi)同时设置了该板默认字体。字体和表情配置说明按屏幕分辨率选择屏幕规格文本字体图标字体小屏幕128x64 OLEDfont_puhui_basic_14_1font_awesome_14_1中小屏幕240x240font_puhui_basic_16_4font_awesome_16_4中等屏幕240x320font_puhui_basic_20_4font_awesome_20_4大屏幕480x320font_puhui_basic_30_4font_awesome_30_4表情集合选项twemoji_3232x32小屏幕、twemoji_6464x64大屏幕。第 6 步配置与编译方法一使用 idf.py 手动配置# 1. 设置目标芯片首次配置或更换芯片时 idf.py set-target esp32s3 # 或 esp32c3 / esp32 / esp32p4 等 # 2. 清理旧配置 idf.py fullclean # 3. 进入配置菜单导航到 Xiaozhi Assistant - Board Type选择自定义开发板 idf.py menuconfig # 4. 编译和烧录 idf.py build idf.py flash monitor方法二使用 build.py 脚本推荐只要开发板目录下有config.json即可用 scripts/build.py 一键完成配置与编译python scripts/build.py my-custom-board语言和唤醒词属于用户构建参数python scripts/build.py my-custom-board \ --language en-US \ --wake-word wn9_jarvis_tts参数取值规则--language接受 main/assets/locales/ 下已有的 locale如zh-CN、en-US大小写与-/_会被归一化处理。--wake-word接受 ESP-SR 模型名、nihaoxiaozhi自动选择与目标芯片兼容的模型或disabled。ESP32-C3/C5/C6 仅支持 WakeNet9swn9s_*模型ESP32-S3/P4/S31 构建会自动使用 AFE 唤醒引擎。这一约束与 scripts/build.py 中的_LITE_WAKE_WORD_TARGETS {esp32c3, esp32c5, esp32c6}和_AFE_WAKE_WORD_TARGETS {esp32s3, esp32p4, esp32s31}完全对应。可以用文本或 JSON 格式查询可用值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/espressif__esp-sr/Kconfig.projbuild。如果尚未生成managed_components/请先运行idf.py reconfigure。build.py的自动化行为与源码实现对应不传参数时打印帮助使用--list-boards列出所有开发板类型和变体。如果开发板有多个变体builds数组交互式提示选择非交互环境使用--name 变体如python scripts/build.py bread-compact-wifi --name bread-compact-wifi-128x64。读取config.json中的target仅当已有 build 目录的目标芯片不同时才执行fullclean然后通过一次idf.py reconfigure同时配置目标芯片、开发板名称、defaults 和所选变体的sdkconfig_append该片段被写入build/xiaozhi-build.sdkconfig.defaults随后的idf.py build复用这份配置。将所选 build 的name作为固件上报的变体名称。默认生成build/merged-binary.bin不创建 ZIP指定--zip时会重新生成releases/v版本_名称.zip版本号来自根 CMakeLists.txt 的PROJECT_VER。另外还支持--build-options-json传入语义化构建选项如aec_mode、wifi_provisioning、display_style等具体键值由--list-boards --json对所选变体输出供 CLI、Agent 与在线编译接口共用。第 7 步创建 README.md在开发板目录中编写README.md说明开发板特性、硬件要求、编译和烧录步骤便于其他开发者复现与维护。四、常见开发板组件一览1. 显示屏项目支持多种显示屏驱动包括ST7789SPI、ILI9341SPI、SH8601QSPI、SSD1306/SSD1315I2C OLED见 bread-compact-wifi 的 SSD1306 初始化示例、SH1106 等。SPI 屏走SpiLcdDisplay如本文示例OLED 屏走OledDisplay均在 main/display/ 的lcd_display.h/oled_display.h中定义。2. 音频编解码器支持的编解码器包括ES8311常用、ES7210麦克风阵列、AW88298功放、ES8374/ES8388/ES8389 等实现均位于 main/audio/codecs/统一继承AudioCodec基类audio_codec.h。3. 电源管理部分开发板使用电源管理芯片如 AXP2101实现见 main/boards/common/axp2101.cc等 PMIC用于电池充电管理与电量读取。4. MCP 设备控制可以添加各种 MCP 工具让 AI 能够使用Speaker扬声器控制、Screen屏幕亮度调节、Battery电池电量读取、Light灯光控制等。MCP 服务端实现位于 main/mcp_server.cc协议细节见 MCP 协议文档。五、开发板类继承关系板级类统一继承自Board抽象基类main/boards/common/board.hBoard- 基础板级类WifiBoard- Wi-Fi 连接的开发板Ml307Board- 使用 4G 模块的开发板DualNetworkBoard- 支持 Wi-Fi 与 4G 网络切换的开发板选择继承哪个基类取决于硬件网络方案Wi-Fi 板卡选WifiBoard4G 模组如 ML307选Ml307Board双网卡选DualNetworkBoard实现分别见 wifi_board.cc、ml307_board.cc、dual_network_board.cc。六、开发技巧参考相似的开发板新开发板与现有开发板相似时优先参考现有实现——仓库main/boards/下有 70 多种板卡可作为抄作业模板从中挑选芯片型号、屏幕类型、编解码芯片最接近的一块作为起点。分步调试先实现基础功能如显示再添加更复杂的功能如音频。管脚映射确保在config.h中正确配置所有管脚映射逐一核对 I2S、I2C、SPI、按钮、背光管脚与原理图一致。检查硬件兼容性确认所有芯片和驱动程序的兼容性特别是编解码芯片地址与屏幕驱动型号。七、可能遇到的问题现象排查方向显示屏不正常检查 SPI 配置、镜像设置DISPLAY_MIRROR_X/Y和颜色反转设置esp_lcd_panel_invert_color音频无输出检查 I2S 配置、PA 使能引脚AUDIO_CODEC_PA_PIN和编解码器地址无法连接网络检查 Wi-Fi 凭据和网络配置确认type/name标识唯一且未被其他板卡占用无法与服务器通信检查 MQTT 或 WebSocket 配置参考 MQTT/UDP 协议文档 与 WebSocket 文档八、参考资料本指南英文版docs/custom-board.md自定义开发板接线与硬件方案docs/v0/wiring.jpg、docs/v1/wiring2.jpg板级公共组件源码main/boards/common/构建脚本scripts/build.py分区表定义partitions/v2/语言资源目录main/assets/locales/音频编解码器实现main/audio/codecs/显示驱动实现main/display/MCP 协议与使用docs/mcp-protocol_zh.md、docs/mcp-usage_zh.mdESP-IDF 相关开发细节可参考仓库内 esp-idf-6-migration.md 迁移文档【免费下载链接】xiaozhi-esp32An MCP-based chatbot | 一个基于MCP的聊天机器人项目地址: https://gitcode.com/GitHub_Trending/xia/xiaozhi-esp32创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考