使用ESP调用Coze智能体实现串口对话
·
硬件准备
- ESP32或ESP8266开发板
- USB转串口模块(如CH340)
- 连接线
软件准备
- Arduino IDE或PlatformIO
- Coze API密钥
- 串口通信库(如SoftwareSerial)
配置ESP开发环境
安装必要的库文件,包括WiFi库和HTTP客户端库。在Arduino IDE中,通过库管理器搜索并安装这些库。
修改开发板的串口通信设置,确保波特率匹配。通常使用115200波特率进行调试。
连接Coze API
获取Coze API的访问密钥,并在代码中配置API端点。确保网络连接正常,ESP能够访问互联网。
编写HTTP请求代码,构造符合Coze API要求的请求格式。包括设置请求头、请求体和必要的认证信息。
实现串口通信
初始化串口通信,设置正确的波特率。在代码中实现串口数据的读取和发送功能。
处理接收到的串口数据,将其转换为Coze API能够理解的格式。发送到Coze智能体后,接收返回结果并通过串口输出。
示例代码片段
#include <WiFi.h>
#include <HTTPClient.h>
const char* ssid = "your_SSID";
const char* password = "your_PASSWORD";
const char* cozeApiUrl = "https://api.coze.com/v1/chat";
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
}
void loop() {
if (Serial.available()) {
String input = Serial.readString();
sendToCoze(input);
}
}
void sendToCoze(String message) {
HTTPClient http;
http.begin(cozeApiUrl);
http.addHeader("Content-Type", "application/json");
http.addHeader("Authorization", "Bearer your_API_KEY");
String payload = "{\"message\":\"" + message + "\"}";
int httpCode = http.POST(payload);
if (httpCode > 0) {
String response = http.getString();
Serial.println(response);
}
http.end();
}
调试与优化
测试串口通信的稳定性,确保数据传输没有丢失或错误。调整缓冲区大小和超时设置以获得最佳性能。
优化网络请求,添加错误处理和重试机制。考虑实现异步请求以避免阻塞主循环。
监控内存使用情况,确保长时间运行不会导致内存泄漏或崩溃。定期重启可能有助于维持系统稳定性。
安全注意事项
保护API密钥,避免将其硬编码在代码中。考虑使用安全存储方式或动态获取机制。
实现输入验证,防止恶意数据导致系统异常。限制输入长度和内容类型,确保系统安全。
使用HTTPS协议进行通信,确保数据传输加密。验证服务器证书,防止中间人攻击。
完整代码:
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
// ------------------- 必须修改这5行(按Coze文档配置) -------------------
const char* WIFI_SSID = "123"; // 你的WiFi名称
const char* WIFI_PASS = "12345678"; // 你的WiFi密码
const char* COZE_API_KEY = "pat_DFxxx"; // 确保无多余空格
const char* COZE_BOT_ID = "xxxx"; // 你的Coze机器人ID
const char* COZE_USER_ID = "123"; // 自定义用户ID(固定不变)
// -----------------------------------------------------------------------
const char* COZE_API_DOMAIN = "api.coze.cn"; // Coze v3 API域名
const int COZE_API_PORT = 443; // HTTPS固定端口
WiFiClientSecure client; // ESP8266 HTTPS客户端
String inputBuffer = ""; // 串口输入缓冲区
// 解析最终回复(适配ArduinoJson 7.x + Coze v3响应格式)
String processAnswer(DynamicJsonDocument& resDoc) {
// ArduinoJson 7.x:用[]直接访问键,无需getMember()
if (resDoc["code"].as<int>() != 0) {
return "❌ 应答异常:" + resDoc["msg"].as<String>();
}
JsonArray data = resDoc["data"].as<JsonArray>();
String aiReply = "无回复内容";
String followUps = "";
int count = 0;
// ArduinoJson 7.x:遍历数组用auto(无需&,避免引用绑定错误)
for (auto item : data) {
String type = item["type"].as<String>();
if (type == "answer") {
aiReply = item["content"].as<String>();
} else if (type == "follow_up") {
count++;
followUps += "☆ 问题" + String(count) + ":" + item["content"].as<String>() + "\n";
}
}
String result = "✅ AI:" + aiReply + "\n";
if (count > 0) {
result += "参考提问:\n" + followUps;
}
return result;
}
// 轮询获取聊天结果(GET /v3/chat/retrieve + GET /v3/chat/message/list)
String getChatResult(String conversationId, String chatId) {
String retrieveUrl = "/v3/chat/retrieve?conversation_id=" + conversationId + "&chat_id=" + chatId;
String msgListUrl = "/v3/chat/message/list?chat_id=" + chatId + "&conversation_id=" + conversationId;
String params = "bot_id=" + String(COZE_BOT_ID) + "&task_id=" + chatId;
int maxRetries = 20; // 最大轮询次数(20秒超时)
int retryCount = 0;
while (retryCount < maxRetries) {
retryCount++;
Serial.printf("🤔 轮询中(%d/%d)...", retryCount, maxRetries);
// 1. 发送GET请求查询状态(/retrieve)
if (client.connect(COZE_API_DOMAIN, COZE_API_PORT)) {
client.print("GET " + retrieveUrl + " HTTP/1.1\r\n");
client.print("Host: " + String(COZE_API_DOMAIN) + "\r\n");
client.print("Authorization: Bearer " + String(COZE_API_KEY) + "\r\n");
client.print("Connection: close\r\n\r\n");
// 读取状态响应
String retrieveResp = "";
while (client.connected() || client.available()) {
if (client.available()) {
retrieveResp += client.readString();
}
}
client.stop();
// 解析状态响应
int jsonStart = retrieveResp.indexOf("{");
if (jsonStart != -1) {
String jsonStr = retrieveResp.substring(jsonStart);
DynamicJsonDocument resDoc(1024); // 适配ESP8266内存
DeserializationError error = deserializeJson(resDoc, jsonStr);
if (!error && resDoc["code"].as<int>() == 0) {
// 嵌套键访问:data.status
String status = resDoc["data"]["status"].as<String>();
Serial.println("状态:" + status);
if (status == "completed") {
// 2. 状态完成,获取消息列表(/message/list)
if (client.connect(COZE_API_DOMAIN, COZE_API_PORT)) {
client.print("GET " + msgListUrl + "&" + params + " HTTP/1.1\r\n");
client.print("Host: " + String(COZE_API_DOMAIN) + "\r\n");
client.print("Authorization: Bearer " + String(COZE_API_KEY) + "\r\n");
client.print("Connection: close\r\n\r\n");
// 读取消息响应
String msgResp = "";
while (client.connected() || client.available()) {
if (client.available()) {
msgResp += client.readString();
}
}
client.stop();
// 解析消息响应
int msgJsonStart = msgResp.indexOf("{");
if (msgJsonStart != -1) {
String msgJsonStr = msgResp.substring(msgJsonStart);
DynamicJsonDocument msgDoc(2048); // 适配回复长度
DeserializationError msgError = deserializeJson(msgDoc, msgJsonStr);
if (!msgError) {
return processAnswer(msgDoc); // 解析并返回回复
} else {
return "❌ 消息解析错误:" + String(msgError.c_str());
}
}
return "❌ 消息响应无JSON";
} else {
return "❌ 连接消息接口失败";
}
} else if (status == "failed") {
String errMsg = resDoc["data"]["error_msg"].as<String>();
return "❌ 任务失败:" + errMsg;
}
} else {
Serial.println("状态解析错误");
}
} else {
Serial.println("状态响应无JSON");
}
} else {
Serial.println("连接状态接口失败");
}
delay(1000); // 每秒轮询一次
}
return "❌ 轮询超时(20秒)";
}
// 核心函数:调用Coze v3 API(完整流程)
void callCozeAI(String userInput) {
Serial.print("AI:思考中...");
String postBody = "";
String response = "";
// 1. 构建Coze v3 POST请求体(ArduinoJson 7.x语法)
DynamicJsonDocument reqDoc(1024); // 适配ESP8266内存
reqDoc["bot_id"] = COZE_BOT_ID;
reqDoc["user_id"] = COZE_USER_ID;
reqDoc["stream"] = false;
reqDoc["auto_save_history"] = true;
JsonArray messages = reqDoc.createNestedArray("additional_messages");
JsonObject userMsg = messages.createNestedObject();
userMsg["role"] = "user";
userMsg["content"] = userInput;
userMsg["content_type"] = "text";
serializeJson(reqDoc, postBody); // 转换为JSON字符串
Serial.printf("\n📤 请求体:%s\n", postBody.c_str());
// 2. 发送POST请求创建对话(/v3/chat)
if (client.connect(COZE_API_DOMAIN, COZE_API_PORT)) {
client.print("POST /v3/chat HTTP/1.1\r\n");
client.print("Host: " + String(COZE_API_DOMAIN) + "\r\n");
client.print("Authorization: Bearer " + String(COZE_API_KEY) + "\r\n");
client.print("Content-Type: application/json\r\n");
client.print("Content-Length: " + String(postBody.length()) + "\r\n");
client.print("Connection: close\r\n\r\n");
client.print(postBody);
// 读取POST响应
while (client.connected() || client.available()) {
if (client.available()) {
response += client.readString();
}
}
client.stop();
} else {
Serial.println("\n❌ 连接Coze失败!检查网络和API Key");
return;
}
// 3. 解析POST响应,提取chat_id和conversation_id(ArduinoJson 7.x语法)
String chatId = "";
String conversationId = "";
int jsonStart = response.indexOf("{");
if (jsonStart != -1) {
String jsonStr = response.substring(jsonStart);
DynamicJsonDocument resDoc(1024);
DeserializationError error = deserializeJson(resDoc, jsonStr);
if (!error && resDoc["code"].as<int>() == 0) {
JsonObject data = resDoc["data"];
chatId = data["id"].as<String>();
conversationId = data["conversation_id"].as<String>();
Serial.printf("\n✅ 对话创建成功(chat_id:%s)\n", chatId.c_str());
} else {
Serial.printf("\n❌ POST响应解析错误:%s\n", error.c_str());
Serial.printf("响应内容:%s\n", jsonStr.c_str());
return;
}
} else {
Serial.println("\n❌ POST响应无JSON");
return;
}
// 4. 轮询获取结果并打印
if (chatId != "" && conversationId != "") {
String aiReply = getChatResult(conversationId, chatId);
Serial.println("\n" + aiReply + "\n");
Serial.println("💬 输入下一条消息(输入q退出)~");
} else {
Serial.println("\n❌ 未获取到对话ID");
}
}
void setup() {
Serial.begin(115200);
Serial.println("\n📡 ESP8266 Coze AI启动中...");
// 连接WiFi(带重连)
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.printf("\n✅ WiFi已连接!IP:%s\n", WiFi.localIP().toString().c_str());
Serial.println("💬 请输入文字按回车对话(输入q退出)~");
// ESP8266 HTTPS必须关闭证书验证
client.setInsecure();
}
void loop() {
// 读取串口输入
while (Serial.available() > 0) {
char c = Serial.read();
if (c == '\n' || c == '\r') {
if (inputBuffer.length() > 0) {
Serial.printf("\n🗣️ 你:%s\n", inputBuffer.c_str());
if (inputBuffer == "q") {
Serial.println("❌ 退出对话,重启后可重新连接~");
while (1);
}
callCozeAI(inputBuffer);
inputBuffer = "";
}
} else {
inputBuffer += c;
}
}
// WiFi断线自动重连
if (WiFi.status() != WL_CONNECTED) {
Serial.println("\n⚠️ WiFi断线,正在重连...");
WiFi.reconnect();
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.printf("\n✅ WiFi重连成功!IP:%s\n", WiFi.localIP().toString().c_str());
}
delay(100);
}
更多推荐

所有评论(0)