详情

首页手游攻略 两天时间:我和 kimi k3 一起把小智"整容"了:从零烧录固件到 Minecraft 小僵尸

两天时间:我和 kimi k3 一起把小智"整容"了:从零烧录固件到 Minecraft 小僵尸

佚名 2026-07-20 17:41:57

如何让AI助手主动开口说话?本文通过修改小智固件、集成MCP语音工具,带你从零实现Minecraft小僵尸在屏幕上“活”起来。
核心内容:
1. 改造小智固件,增加MCP语音工具实现外部推送
2. 搭建ESP-IDF编译环境,解决Windows命令行长度限制
3. 烧录调试与头像定制,最终让“绿崽”在设备上成功运行

 

一台 M5Stack CoreS3、一个开源的小智(xiaozhi-esp32)固件、一个自己搭的服务端,再加一个 AI 搭子(Kimi Work,K3 模型)。两天时间,我们踩了七八个坑,最后让屏幕上长出了一只会眨眼的 Minecraft 小僵尸——「绿崽」。

这篇文章是整个过程的完整复盘,希望能给同样想折腾小智的你省点时间。

起因:我想让外部系统主动「推消息」给机器人

一切的起点其实是个功能需求:我希望外部系统能主动推送信息给小智,让它开口说话,而不只是人喊它才应答。

翻完 xiaozhi-esp32 的源码后发现:MQTT 模式下设备空闲常连,服务端的 alert 消息可以在屏幕上弹文字,但空闲时无法开口——没有音频通道、没有 speak 工具,固件里 mcp_server.cc 还会把 MCP notifications 直接丢掉。

所以方案定了:改固件,加一个 speak 的 MCP 工具。要改固件,就得能自己编译烧录;要自编译,最好服务端也自己搭。于是有了这条完整的折腾链路:

   自搭服务端 → 配 Kimi K3 大模型 → 搭 ESP-IDF 编译环境 → 编译固件
→ 烧录 → 修启动 bug → 设备上线 → 改写默认头像 → 修崩溃 → 上岸

第一关:服务端跑起来(最顺利的一步)

服务端用的是官方配套的 xiaozhi-esp32-server,Docker 一把梭:

  • 容器跑起来后,OTA 接口 http://<局域网IP>:8003/xiaozhi/ota/ 返回正常
  • LLM 配的 Kimi(KimiLLM,OpenAI 兼容协议),模型 K3,填上 API Key 后实测 200 连通

这一步意外顺滑,让我产生了「今天能搞定」的错觉。

第二关:ESP-IDF 编译环境,被 Windows 命令行长度背刺

小智 v2.4.0 官方要求 ESP-IDF v6.0+,我装了 v6.0.2。然后遇到了一个非常 Windows 的坑:

Windows 命令行有 32767 字符上限。 这个项目编译时链接命令带了几百个 -I 头文件路径,实测拼出来 36081 个字符——直接超限失败。

解法很土但有效:把 ESP-IDF 和工程全部物理复制到超短路径(比如 C:efesp-idf-v6.0.2 和 C:pfxiaozhi-esp32),路径短了,命令行就够长了。junction、subst 这些花活都试过,CMake 会把路径还原回去,别浪费时间和它斗智斗勇。

第三关:编译成功,烧录成功,然后设备「死」了

改好三个配置(板型 M5STACK_CORE_S3、OTA 地址指向自己的服务器、控制台改到 USB)后编译通过,烧录也显示 Hash verified。

然后设备黑屏,电源键没反应,连正常的 COM 口都不见了。

第一天的最大悬疑就此开始。

真相一:它根本没在运行,而是一直躺在下载模式

长按底侧 RST 键 3 秒亮绿灯可以进下载模式,COM 口回来了。但奇怪的是:刷完机 esptool 明明做了 “Hard resetting via RTS pin”,设备却从不启动。

后来用 esptool 的 --before no-reset 模式一探,发现芯片一直停在 ROM 下载模式——它从来就没跑过我们的固件。黑屏不是「跑了但显示挂了」,是压根没跑。

真相二:DTR 陷阱,一个自己坑自己的经典案例

更诡异的是,我用 pyserial 写脚本抓启动日志,每次打开串口设备反而更「死」。

最后定位到原因:Windows 上打开串口时,DTR 线默认会被拉高,而 ESP32-S3 的 USB-Serial-JTAG 外设里 DTR 连着 GPIO0(BOOT 选择脚)。GPIO0 低电平 = 任何复位都进下载模式。

也就是说,我每次「打开串口准备抓日志」的动作本身,就把设备按回了下载模式。自己成了自己最大的干扰源。

解法:打开串口后立刻 dtr = False(GPIO0 拉高),再脉冲 RTS 复位,设备立刻正常启动,ROM 日志哗哗地流出来。

真相三:PSRAM 模式错了,但这错误藏得极深

拿到启动日志后,死因一目了然:

   E (34) octal_psram: PSRAM chip is not connected, or wrong PSRAM line mode
E cpu_start: Failed to init external RAM!
abort() was called

固件按八线(Octal)PSRAM 初始化,然后反复 probe 失败、崩溃、重启。设备进入「启动→崩溃→重启」的死循环,USB 每 10 秒重新枚举一次——这就是之前 COM 口时有时无的原因。

可是 sdkconfig 里明明写着 CONFIG_SPIRAM_MODE_OCT=y,这还是 esp32s3 默认配置里的。直到我翻了官方仓库里 CoreS3 的 main/boards/m5stack-core-s3/config.json:

   json"sdkconfig_append": [
    "CONFIG_SPIRAM_MODE_QUAD=y",
    ...
]

官方发布固件用的是 QUAD(四线)模式! 默认配置的 OCT 在这台机器上根本不工作,官方在发布脚本里悄悄覆盖成了 QUAD。改成 QUAD 重新编译烧录,设备「叮」的一声亮了。

教训:玩 xiaozhi 自编译,一定要看你那个板子 config.json 里的 sdkconfig_append,那里有官方没写进文档的板级秘方。

第四关:设备连不上服务器,errno 113 指向防火墙

设备正常启动、连上家里 WiFi(拿到 IP),但 OTA 请求一直失败:

   E EspTcp: Failed to connect to 192.168.1.21:8003, code=0x71

0x71 = 113 = EHOSTUNREACH。排查链路:

  1. 电脑上 curl 自己的局域网 IP,OTA 接口正常 ✅
  2. ping 设备,通 ✅
  3. Docker 端口监听在 0.0.0.0 ✅
  4. 手机浏览器访问 OTA 地址,打不开 ❌

手机也不通,说明问题在电脑这一侧,与设备无关。Windows 防火墙加了两条入站放行规则(8003/8000),没用;整个防火墙临时关掉,立刻通了

最终结论:防火墙层面的拦截(如果 Windows 防火墙规则明明放行了还不行,大概率是 360/电脑管家/火绒这类安全软件接管了网络过滤,它们会无视 Windows 规则)。

设备随即上线,服务器日志里出现了它的身影:

   OTA请求设备ID: 44:1b:f6:df:59:6c
OTA请求ClientID: 8991a3ae-4017-4533-83a6-337021944701
收到hello消息:{"type":"hello","features":{"mcp":true},...}

第五关:给小智「整容」,一只 Minecraft 小僵尸的诞生

功能跑通后,看默认头像就越来越不顺眼了。小智默认显示一个 FontAwesome 的芯片图标,多少有点敷衍。

我先让 AI 出了 10 个简洁几何风候选(白团子、猫咪、饭团、小云……),最后选了 Minecraft 小僵尸:像素风、僵尸绿、大头宝宝比例、方块大眼带高光、一颗小虎牙、MC 标志性的青色小衣服。取名「绿崽」。

固件实现:比想象中简单

小智 CoreS3 用的是 LcdDisplay(LVGL),中央表情区原本是 emoji 图片/字体图标。改造方式:

  1. 像素脸:12×14 的像素图,每像素 11 物理像素,用约 20 个 LVGL 矩形对象拼出头、高光、阴影、眼睛、嘴、虎牙、小衣服——不用任何图片资源,纯代码绘制
  2. 眨眼动画:一个 4.2 秒的 LVGL 定时器,眼睛矩形从 3 像素行缩到 1 像素行、130ms 后恢复
  3. 说话动嘴:进入 kDeviceStateSpeaking 状态时,180ms 定时器让嘴在开合之间切换;回到 idle 就停
  4. 表情拦截:SetEmotion 不再切 emoji 图片,只跟踪「说/不说」

然后又是一个崩溃循环

第一版烧进去,设备又开始「初始化→联网→重启」无限循环。抓串口日志:

   Guru Meditation Error: Core 1 panic'ed (LoadProhibited)
EXCVADDR: 0x00000028

0x28 这种地址,典型的空指针偏移访问。用 xtensa-esp32s3-elf-addr2line 解码调用栈,三秒定位:

   lv_obj_set_style_text_color  ← lv_obj_style_gen.c:635
LcdDisplay::SetTheme         ← lcd_display.cc:1332
Assets::Apply                ← assets.cc:54
Application::CheckAssetsVersion

我把 emoji_label_ 置空后,SetTheme() 里还有一处没判空就直接给它设颜色。主题在「激活」阶段一刷新,当场暴毙。加个 if (emoji_label_ != nullptr),世界清净了。

最终效果

  • 待机:绿崽安安静静待着,每 4 秒左右眨一次眼
  • 喊「你好小智」:正常应答
  • 说话的时候:嘴巴一张一合,配合语音非常有喜感

这两天踩过的坑(速查清单)

解法
Windows 命令行 32767 字符上限ESP-IDF 和工程复制到 C:efC:pf 短路径
刷完机设备黑屏「死了」其实一直停在下载模式,用 esptool --before no-reset 探测
打开串口反而抓不到日志DTR 陷阱:开串口后立刻 dtr=False,否则 GPIO0 被拉低
octal_psram 初始化失败看板子 config.json 的 sdkconfig_append,CoreS3 要用 QUAD
设备 OTA 报 errno 113电脑侧防火墙/安全软件拦截,手机实测可快速定位
LoadProhibited 崩溃循环addr2line 解码 backtrace,空指针判空修复
刷机后 WiFi 要重配erase-flash 会清 NVS,日常刷机别加 erase

下一步

「绿崽」只是开始。最初的目标——让外部系统主动推送消息给小智开口说话——还没做:接下来要在固件里加 self.audio_speaker.speak 这个 MCP 工具,服务端加一个 /push/speak 接口。方案已经设计好,就等动手了。

如果你也在玩小智,欢迎抄作业;如果踩到新坑,也欢迎来交流。

工具链:xiaozhi-esp32 v2.4.0 / xiaozhi-esp32-server(Docker)/ ESP-IDF v6.0.2 / Kimi Work(K3 模型,负责全部源码分析、调试和固件改写)

 

登录查看剩余 70% 内容

点击查看更多
推荐专题
热门阅读