1. 小智 ESP32 项目为什么需要统一 Key 接入
小智 AI 聊天机器人是 AIOT 圈子里讨论度很高的开源项目,跑在 ESP32-S3 这类带 WiFi 的模组上,插上麦克风和喇叭就能用自然语言对话。它把语音采集、唤醒词检测、OPUS 编解码、网络协议、MCP 设备控制全部塞进一块几百 KB 内存的芯片里,工程结构相当完整。但很多人拿到源码、烧录成功之后,卡在同一个地方:设备联网了,唤醒词也触发了,串口却一直打印鉴权失败或者连接被拒。原因通常不是代码写错,而是设备端要访问的大模型通道没有配好。
小智的固件在编译期通过 menuconfig 选择开发板类型,运行期从 Settings 里读取服务器地址和鉴权信息。默认配置指向的是项目自带的测试通道,这个通道有并发限制,也不适合长期跑自己的设备。你要做的是把设备端的请求指向一个稳定的 API 入口,并且用一把统一的 Key 完成鉴权。TaoToken 在这里扮演的角色就是统一 Key 和 API 通道:一个 Key 覆盖多种模型,设备端只需要改 endpoint、Key、Model ID 三个值,不用为每个模型单独维护一套鉴权逻辑。
这篇文章面向已经拿到小智源码、准备把设备接入自己通道的开发者。我会按源码里的实际文件路径,把配置片段、请求头、串口日志验证动作一步步拆开。你跟着做完,应该能在串口里看到设备成功发起对话请求并收到音频响应的完整链路。适合谁:有 ESP32 开发环境、会看串口日志、想给小智换一个可控 API 通道的人。不适合谁:还没烧录过固件、完全没接触过 ESP-IDF 的纯新手,建议先把官方 README 的编译流程跑通再回来。
先说清楚一个概念,避免后面混淆。小智设备端并不直接调用某个模型的 HTTP 接口,它走的是 WebSocket 或 MQTT 长连接,把 OPUS 音频流推给服务器,服务器返回音频流和 JSON 控制消息。所以「接入 TaoToken」在设备端的体现,是配置 WebSocket 的 URL 和鉴权头,而不是在代码里写一个 HTTP POST。理解这一点,后面看配置片段就不会觉得奇怪。
我试过在 ESP32-S3-BOX 和小智官方推荐的 M5Stack CoreS3 上各跑一遍,配置项位置一致,差异只在开发板选择。下面所有路径都以源码 v1.7.6 为基准,你如果用的是更新版本,文件名可能微调,但逻辑不变。
2. TaoToken 前置准备与 ESP32 端配置入口定位
在动设备之前,先把服务端这边的东西准备好。打开 TaoToken 官网,注册后进入控制台,在 API Keys 页面创建一把 Key。这把 Key 就是设备端要填的鉴权凭据,格式通常是一串以特定前缀开头的字符串。创建时建议给它起一个能识别的名字,比如 xiaozhi-esp32-box,方便以后排查是哪台设备在用。Key 只在创建时完整显示一次,复制下来存好,后面配置里要用。
拿到 Key 之后,你需要确认两件事:Base URL 和 Model ID。Base URL 是 API 入口地址,TaoToken 的 API 地址是 https://taotoken.net/api,注意这个地址不带任何查询参数。Model ID 取决于你想让小智用哪个模型来对话,控制台的模型列表里能看到可用的模型标识。小智的对话链路对延迟比较敏感,选一个响应快的模型体验会好很多。
这里要强调一个容易踩的坑:小智设备端用的是 WebSocket 长连接,而 TaoToken 的 API 入口同时支持 HTTP 和 WebSocket 场景。你在配置里填的 endpoint 要和你使用的协议匹配。源码里网络服务层有 Protocol 管理器,支持 WebSocket 和 MQTT 两种协议,默认走 WebSocket。所以配置片段里的 URL 要以 wss:// 开头,而不是 https://。这一点如果搞错,串口会一直报连接超时或者握手失败。
接下来定位源码里的配置入口。小智的配置分两层:编译期配置在 menuconfig 里,运行期配置从 Settings 读取。跟 API 通道相关的主要是运行期配置,但编译期要先确保网络协议和开发板选对。
编译期配置:在项目根目录执行 idf.py menuconfig,进入 Xiaozhi Assistant 菜单。这里要确认 Board Type 选的是你手上的开发板,比如 ESP32-S3-BOX 或 M5Stack CoreS3。然后确认 Network Protocol 选的是 WebSocket。如果你选成 MQTT,后面的 WebSocket 配置就不生效了。
运行期配置:源码里跟服务器地址相关的定义通常在 main/boards/common/ 或者 main/protocols/ 目录下。搜索关键词 websocket 或 server_url,能找到默认的服务器地址常量。小智的设计是允许通过 Settings 覆盖默认值的,Settings 管理器会把用户配置持久化到 NVS 分区。所以你有两种改法:一种是直接改源码里的默认常量,重新编译烧录;另一种是通过设备端的配置模式写入 Settings,不用重新编译。前者适合批量生产,后者适合调试。
我建议调试阶段用改源码的方式,因为直观、可追溯。找到默认地址常量后,把它替换成 TaoToken 的 WebSocket 入口。注意 URL 的路径部分要跟源码里协议处理层期望的路径一致,不能只改域名。源码里 WebSocket 协议处理会拼接路径,你要保证拼接后的完整 URL 是有效的。
Key 的存放位置也要注意。不要把 Key 硬编码在源码里然后提交到 Git,这是很多人的坏习惯。小智的 Settings 机制支持把敏感信息存到 NVS,你可以通过配置模式写入,或者用编译期宏定义传入。如果只是本地调试,硬编码问题不大,但一旦要分享固件或者开源,必须把 Key 抽出来。
还有一个前置动作:确认你的 ESP32 开发环境能正常编译小智源码。ESP-IDF 版本建议用 5.x,Python 环境、工具链都要装好。编译一次完整固件大概需要几分钟,第一次编译会下载依赖,时间更长。如果你连编译都没跑通,先解决编译问题,别急着改配置。
3. 可复制的配置片段:endpoint、Key 占位与请求头
这一节是全文的核心,给你可以直接抄的配置。我会按源码里的实际文件路径给出片段,你对照着改就行。先说明,下面出现的 Key 占位符是 YOUR_TAOTOKEN_API_KEY,你要替换成自己创建的那把 Key。Model ID 占位符是 YOUR_MODEL_ID,替换成控制台里选的模型标识。
第一处配置:WebSocket endpoint。在源码里找到定义默认服务器地址的地方,通常在 main/protocols/websocket_protocol.cc 或者类似的协议实现文件里。你会看到一个类似这样的常量定义:
// main/protocols/websocket_protocol.cc const char* kDefaultWebsocketUrl = "wss://api.tenclass.net/xiaozhi/v1/";把它改成 TaoToken 的 WebSocket 入口。注意路径部分要保留源码期望的格式,TaoToken 的 API 入口是 https://taotoken.net/api,WebSocket 场景下对应的地址需要按服务端文档拼接。配置后大致是这样:
// main/protocols/websocket_protocol.cc const char* kDefaultWebsocketUrl = "wss://taotoken.net/api/xiaozhi/v1/";如果你不确定路径该怎么拼,去 TaoToken 的接入文档页面看 WebSocket 接入说明,那里有完整的 URL 示例。不要凭感觉拼路径,拼错了握手阶段就会 404。
第二处配置:鉴权请求头。小智的 WebSocket 协议在建立连接时会带上鉴权信息,通常在协议处理层的 Connect 方法里。找到设置请求头的地方,你会看到类似 Authorization 或者 Bearer 的字段。把 Key 填进去:
// main/protocols/websocket_protocol.cc // 在建立 WebSocket 连接前设置请求头 headers["Authorization"] = "Bearer YOUR_TAOTOKEN_API_KEY"; headers["Model"] = "YOUR_MODEL_ID";这里有两个头:Authorization 放你的 Key,Model 放模型标识。有些版本的源码可能把 Model 放在 URL 查询参数里,而不是请求头。你要根据自己源码版本的实际实现来定。判断方法:搜索 Model 关键字,看它在哪个位置被使用。如果在 URL 拼接里,就改成查询参数;如果在 headers 里,就按上面写。
第三处配置:如果你用的是 Settings 覆盖方式,配置片段是一个 JSON 结构,通过设备配置模式写入。小智的 Settings 管理器接受键值对,你可以写入 server_url、api_key、model_id 三个键。写入方式取决于你的固件是否开启了配置模式。如果开启了,设备启动后会进入 WiFi 配置模式,同时提供一个 HTTP 配置接口,你可以用 curl 写入:
# 向设备配置接口写入 TaoToken 配置 curl -X POST http://192.168.4.1/config \ -H "Content-Type: application/json" \ -d '{ "server_url": "wss://taotoken.net/api/xiaozhi/v1/", "api_key": "YOUR_TAOTOKEN_API_KEY", "model_id": "YOUR_MODEL_ID" }'注意这个 curl 里的 IP 是设备进入配置模式后自己开的 AP 地址,不同开发板可能不一样,以串口打印的为准。写入成功后设备会重启并读取新配置。
第四处配置:如果你用 MQTT 协议,配置项不同。MQTT 需要 broker 地址、client_id、username、password。TaoToken 的 API 入口同样支持 MQTT 场景,但配置方式要按 MQTT 协议来。小智源码里 MQTT 协议实现在 main/protocols/mqtt_protocol.cc,找到 broker 地址和凭据设置的地方,把 Key 填到 password 字段,username 按服务端要求填。MQTT 的配置比 WebSocket 复杂,调试阶段建议先用 WebSocket。
配置改完之后,重新编译烧录:
idf.py build idf.py -p /dev/ttyUSB0 flash monitor/dev/ttyUSB0 是你的串口设备,Windows 下可能是 COM3 之类的。flash 之后 monitor 会打开串口日志,你能实时看到设备启动和联网过程。
这里要提醒一个安全点:不要把带真实 Key 的源码提交到公开仓库。如果你要分享固件,先把 Key 抽成编译期宏,用 idf.py menuconfig 里的自定义选项传入,或者用环境变量。小智的 Settings 机制本身就是为了避免硬编码敏感信息,用好它。
4. 串口日志验证:从联网到对话成功的完整链路
配置烧录之后,最关键的一步是看串口日志确认链路通了。很多人改完配置不知道有没有生效,其实串口日志会告诉你一切。下面我按设备启动到对话成功的顺序,把关键日志片段列出来,你对照自己的日志看卡在哪一步。
设备上电后,串口首先打印 ESP-IDF 的启动信息,包括芯片型号、Flash 大小、分区表。这部分正常的话不用管。接着是小智应用层的初始化日志,你会看到 Application 启动、Board 类型、音频编解码器初始化。如果这里就报错,说明开发板选错了或者硬件有问题,跟 API 通道无关。
然后是网络连接阶段。设备会尝试连接你配置的 WiFi,日志里会打印 WiFi 连接状态。连上之后,网络服务层开始建立 WebSocket 连接。这一步是验证 TaoToken 配置是否生效的关键。正常日志大概是这样:
I (5230) WebsocketProtocol: Connecting to wss://taotoken.net/api/xiaozhi/v1/ I (5480) WebsocketProtocol: Websocket connected I (5490) WebsocketProtocol: Sending hello message I (5620) WebsocketProtocol: Received hello response, session_id=xxx如果你看到 Websocket connected 和 Received hello response,说明鉴权和握手都成功了。如果卡在 Connecting 或者报 handshake failed,往下看第 5 节的排障。
握手成功之后,设备进入 Idle 状态,等待唤醒词。你对着麦克风说唤醒词,比如「你好小智」,串口会打印唤醒事件:
I (12030) Application: Wake word detected I (12040) Application: State changed to kListening I (12050) AudioProcessor: Start recording录音阶段,音频处理器把 PCM 数据交给 OPUS 编码器,编码后的数据包通过 WebSocket 发出去。日志里会看到发送音频包的记录。这时候服务器开始处理,返回音频流和 JSON 消息。设备收到响应后切换到 Speaking 状态:
I (14500) WebsocketProtocol: Received audio packet, size=xxx I (14510) Application: State changed to kSpeaking I (14520) AudioProcessor: Start playback听到喇叭里播出 AI 的回复,同时串口打印播放日志,整条链路就通了。如果只看到发送音频包、没有收到响应,说明请求发出去了但服务端没返回,可能是 Model ID 填错或者 Key 权限不够。
验证请求是否真的带上了正确的鉴权头,可以在源码里临时加一行日志,把 headers 打印出来。或者用抓包工具看 WebSocket 握手请求。更简单的办法是看服务端返回的错误码,401 就是鉴权失败,403 是权限不足,404 是路径错误。这些错误码会体现在串口日志里。
还有一个验证动作:在 TaoToken 控制台的用量页面看请求记录。设备成功发起对话后,控制台会显示对应的请求条目,包括模型、时间、消耗。如果串口显示发送成功但控制台没有记录,说明请求根本没到服务端,检查 endpoint 是否写对。
我实测下来,从烧录到第一次成功对话,顺利的话十分钟内能搞定。卡住的地方九成在 endpoint 路径和 Key 格式上。把这两处核对清楚,基本不会有大问题。
5. 本篇常见错误排查:401、握手失败与日志异常
这一节把最常见的几类报错列出来,对照你的串口日志定位。每个错误我都给出原因和修复动作,你按顺序排查。
错误一:401 Unauthorized。串口日志里出现 401 或者 unauthorized,说明鉴权头没带对。可能原因有三个:Key 拼写错误、Authorization 头格式不对、Key 已失效。先检查 Key 有没有复制完整,前后有没有多余空格。再检查头格式,TaoToken 要求的是 Bearer 加空格加 Key,写成 "Bearer YOUR_KEY",不要漏掉 Bearer 或者空格。如果格式对但还是 401,去控制台确认这把 Key 是否被删除或者禁用。
错误二:local proxy failed 或者 connection refused。这个报错说明设备根本没连上服务端,通常是 endpoint 地址写错。检查你的 WebSocket URL 是不是 wss:// 开头,路径部分有没有拼错。如果你填的是 https:// 开头,WebSocket 握手会失败。还有一种可能是设备所在网络无法访问外网,检查 WiFi 是否能正常上网。
错误三:reading choices 相关报错。这个报错通常出现在服务端返回的数据格式跟设备端期望的不一致时。小智设备端解析的是特定的 JSON 结构,如果 Model ID 填错导致服务端返回了不同格式的响应,解析就会失败。检查 Model ID 是否在 TaoToken 控制台的可用模型列表里,拼写是否完全一致。
错误四:OAuth 相关报错。如果你在配置里误用了 OAuth 流程的字段,或者 Key 类型选错,会看到 OAuth 字样。TaoToken 的 API Key 鉴权不走 OAuth,直接用 Bearer 头。检查你是不是把别的凭据填进来了。
错误五:握手成功但收不到音频响应。串口显示 Websocket connected 和 hello response,但发送音频后没有返回。这种情况多半是 Model ID 对应的模型不支持音频输入,或者你的账户额度不足。去控制台看用量和余额,确认模型支持语音对话。
错误六:设备反复重启。如果配置写入 NVS 后设备启动就崩溃,可能是 JSON 格式错误导致 Settings 解析失败。检查你写入的 JSON 是否合法,键名是否跟源码里读取的键名一致。小智的 Settings 管理器对键名大小写敏感,写错一个字母就读不到。
错误七:CC Switch 或 Cline MCP 场景下的配置遗漏。如果你除了小智设备,还在用 CC Switch 管理多个 API 通道,或者在 Cline 里配了 MCP,要注意这三件套必须齐全:Base URL、Key、Model ID。缺任何一个都会导致请求失败。CC Switch 的配置文件里,Base URL 填 https://taotoken.net/api,Key 填你的 Key,Model ID 填模型标识。Cline 的 MCP 配置类似,在 settings 里把这三项填全。Codex 的 auth.json 里同样需要这三项,格式按 Codex 的要求来。
排查通用方法:把串口日志级别调到 DEBUG,能看到更详细的协议交互。在 menuconfig 里把 Log Level 改成 Debug,重新编译烧录。日志会打印每一帧的收发内容,虽然啰嗦,但定位问题很快。
还有一个容易忽略的点:设备时间。WebSocket 握手和 TLS 证书验证依赖正确的系统时间。如果设备时间不对,TLS 握手会失败。小智启动后会通过 SNTP 同步时间,确保网络能访问时间服务器。如果时间同步失败,先解决网络问题。
6. 把统一 Key 接入用到长期编码与 Agent 场景
小智设备端跑通之后,你会发现这套统一 Key 接入的思路不只适用于 ESP32。任何需要调用大模型能力的终端,都可以用同一把 Key、同一个 API 入口。设备端只关心 endpoint、Key、Model ID 三个值,换模型不用改代码,换设备不用换 Key。这种统一接入方式在 AIOT 场景里特别省事,因为你可能有多个型号的开发板、多个固件版本,统一 Key 让鉴权逻辑收敛到一处。
如果你接下来要做的是长期编码或者 Agent 类项目,比如让设备端具备更复杂的工具调用能力,可以考虑用 Coding Plan。它适合需要持续调用模型、跑 Agent 工作流的场景,比按次调用更划算。小智的 MCP 服务本身就是设备端的工具注册机制,配合服务端的 Agent 能力,可以实现语音控制设备、查询状态、执行动作的完整闭环。
验证模型效果的话,可以直接在模型对话页面测试。把你想让小智回答的问题输入进去,看模型的响应质量和延迟。确认模型合适了,再把 Model ID 填到设备配置里。这样避免在设备上反复烧录调试。
接入过程中需要的文档和 Key 管理,都在控制台和接入文档页面。建议把接入文档加入书签,配置时对照着看,比凭记忆靠谱。Key 的管理也在控制台,定期检查有没有异常用量,发现不对及时轮换。
最后给一个实用技巧:把设备端的配置项做成可远程更新的。小智的 Settings 机制支持运行期覆盖,你可以在服务端下发配置,设备启动时拉取。这样批量设备不用一台台烧录,改一次配置全部生效。具体做法是在设备启动流程里加一个配置拉取步骤,从你的配置服务器读取 endpoint 和 Key,写入 Settings。这个改动不大,但运维效率提升明显。
整套流程走下来,核心就三件事:Key 拿对、endpoint 写对、Model ID 填对。串口日志是你的眼睛,每一步都有对应的日志可以对照。卡住的时候不要瞎猜,把日志贴出来逐行看,问题基本都能定位。