#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
// WiFi配置
const char* ssid = "6xxx";
const char* password = "12345678";
// API配置
const String apiUrl = "https://spark-api-open.xf-yun.com/v1/chat/completions";
const String apiPassword = "xxxxxpsrxk";
const String appId = "6cxxf";
String userId = "exxxp";
// GPIO配置
const int D2_PIN = 2; // ESP32的D2引脚对应GPIO2
// 内存优化配置
const size_t MAX_RESPONSE_SIZE = 150000; // 最大响应长度
const size_t JSON_BUFFER_SIZE = 225000; // JSON缓冲区大小
const size_t LINE_LENGTH = 200; // 每行显示字符数
bool isProcessing = false; // 请求标记 正在发送请求为true 请求结束为flase
bool promptDisplayed = false; // 输入提示标记 正在输入true,输入结束为false
// 函数前向声明
void handleSerialInput();
void processRequest(String &input);
void addMessage(JsonArray &arr, const char* role, const char* content);
void handleResponse(HTTPClient &http);
void processResponseBuffer(String &buffer, bool &isComplete);
void extractContent(JsonDocument &doc);
String cleanResponse(String text);
void checkForGPIOCommands(String &text);
void setup() {
Serial.begin(115200);
// 初始化D2引脚
pinMode(D2_PIN, OUTPUT);
digitalWrite(D2_PIN, LOW); // 默认设为低电平
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected");
Serial.println("请输入您的问题(以回车结束):"); // 初始提示
}
void loop() {
handleSerialInput();
}
// 串口输入处理(优化输入提示)
void handleSerialInput() {
static String inputBuffer;
while (Serial.available()) {
char c = Serial.read();
// 显示输入提示
if (!promptDisplayed && inputBuffer.length() == 0) {
Serial.print("\n你的提问为:");
promptDisplayed = true;
}
// 同时检查CR和NL作为有效的行结束符
if (c == '\n' || c == '\r') {
if (!isProcessing && inputBuffer.length() > 0) {
Serial.println(); // 结束输入行
processRequest(inputBuffer); //调用HTTP请求函数 并携带提问的问题
inputBuffer = ""; //提问完成后将,缓存清空。
promptDisplayed = false;//输入结束标记为false
}
} else {
inputBuffer += c;
Serial.print(c); // 回显输入字符
}
}
}
// HTTP请求处理(增加超时设置)
void processRequest(String &input) {
isProcessing = true;
JsonDocument doc;
doc["model"] = "4.0Ultra";
doc["user"] = userId;
JsonArray messages = doc.createNestedArray("messages");
// 简化system提示,仅包含高低电平控制
addMessage(messages, "system", "你是知识渊博的助理,也能控制硬件。如果用户要求控制D2引脚,请明确在回复中包含以下指令词之一:'D2引脚高电平'或'D2引脚低电平'。");
addMessage(messages, "user", input.c_str());
HTTPClient http;//创建HTTP客户端
http.begin(apiUrl);//配置发送HTTP请求的目标apiUrl
http.addHeader("Content-Type", "application/json");//设置HTTP 请求头 的方法 告诉服务器,客户端发送的请求体(payload)是 JSON 格式的数据。
http.addHeader("Authorization", "Bearer " + apiPassword);//向服务器提供身份认证凭证
// 增加超时设置(60秒)
http.setTimeout(60000);
String payload;
serializeJson(doc, payload);//序列化为JSON格式的字符串,并将结果存储在`payload`中
int httpCode = http.POST(payload);//客户端POST方法用于向服务器发送请求 并payload作为请求体,
//返回值httpCode是服务器返回的HTTP状态码,比如200表示成功,404表示未找到等。
if (httpCode > 0) {
handleResponse(http);
} else {
Serial.print("HTTP Error: ");
Serial.println(httpCode);
if(httpCode == -11){
Serial.println("可能原因:");
Serial.println("1. 网络连接不稳定");
Serial.println("2. 响应数据过大(建议缩短请求内容)");
Serial.println("3. 服务器响应超时");
}
}
http.end();
isProcessing = false;
}
// 添加消息到JSON数组
void addMessage(JsonArray &arr, const char* role, const char* content) {
JsonObject obj = arr.createNestedObject();
obj["role"] = role;
obj["content"] = content;
}
// 响应处理(优化错误处理)
void handleResponse(HTTPClient &http) {
WiFiClient *stream = http.getStreamPtr();
String responseBuffer;//用于存储 从 HTTP 响应流中逐步读取的原始数据片段
unsigned long timeout = millis() + 30000; // 延长超时到30秒
bool isComplete = false;//用于标记是否超时 未超时为 false,超时为true
while (!isComplete && (millis() < timeout)) {
if (stream->available()) {
char c = stream->read();
responseBuffer += c;
// 实时处理流数据
if (responseBuffer.length() >= 512 || !stream->available()) {
processResponseBuffer(responseBuffer, isComplete);
}
}
delay(10);
}
// 处理剩余数据
if (!isComplete && responseBuffer.length() > 0) {
processResponseBuffer(responseBuffer, isComplete);
if (!isComplete) {
Serial.println("\n[警告] 响应数据不完整");
}
}
}
// 处理响应缓冲区
void processResponseBuffer(String &buffer, bool &isComplete) {
JsonDocument doc;
DeserializationError error = deserializeJson(doc, buffer);
if (!error) {
extractContent(doc);// 提取内容
isComplete = doc["is_end"] | false; // 判断是否结束
buffer = ""; // 清空已处理数据
}
else if (error.code() != DeserializationError::IncompleteInput) {
// 过滤EmptyInput错误
if(error.code() != DeserializationError::EmptyInput){
Serial.print("JSON解析错误: ");
Serial.println(error.c_str());
}
buffer = "";
}
}
// 内容提取(简化版)
void extractContent(JsonDocument &doc) {
static String fullResponse;
if (doc.containsKey("choices")) {
const char* content = doc["choices"][0]["message"]["content"];
if (content) {
String cleaned = cleanResponse(content);
fullResponse += cleaned;
// 分页显示
while (fullResponse.length() > LINE_LENGTH) {
Serial.println(fullResponse.substring(0, LINE_LENGTH));
fullResponse = fullResponse.substring(LINE_LENGTH);
}
Serial.print(fullResponse); // 显示剩余部分
// 检查是否包含GPIO控制命令
checkForGPIOCommands(fullResponse);
}
}
if (doc["is_end"] == true) {
// 响应结束时再次检查完整回复
checkForGPIOCommands(fullResponse);
Serial.println("\n[END OF RESPONSE]");
fullResponse = "";
}
}
// 响应清洗(优化中文处理)
String cleanResponse(String text) {
// 保留中文标点
text.replace("。", "。");
text.replace(",", ",");
// 处理其他字符
text.replace("\r\n", " ");
text.replace('\n', ' ');
text.trim();
// 合并空格
int newLength = text.length();
do {
newLength = text.length();
text.replace(" ", " ");
} while (newLength != text.length());
return text;
}
// 检查GPIO控制指令(简化版,只有高电平和低电平)
void checkForGPIOCommands(String &text) {
// 检查高电平指令
if (text.indexOf("D2引脚高电平") >= 0 ||
(text.indexOf("D2") >= 0 &&
(text.indexOf("通高电平") >= 0 || text.indexOf("设为高") >= 0 ||
text.indexOf("打开") >= 0 || text.indexOf("HIGH") >= 0 ||
text.indexOf("点亮") >= 0 || text.indexOf("通电") >= 0))) {
digitalWrite(D2_PIN, HIGH);
Serial.println("\n[系统] D2引脚已设置为高电平");
}
// 检查低电平指令
else if (text.indexOf("D2引脚低电平") >= 0 ||
(text.indexOf("D2") >= 0 &&
(text.indexOf("通低电平") >= 0 || text.indexOf("设为低") >= 0 ||
text.indexOf("关闭") >= 0 || text.indexOf("LOW") >= 0 ||
text.indexOf("熄灭") >= 0 || text.indexOf("断电") >= 0))) {
digitalWrite(D2_PIN, LOW);
Serial.println("\n[系统] D2引脚已设置为低电平");
}
}
【ESP32】【LLM API】LLM Control ESP32 and GPIO
最新推荐文章于 2025-06-04 03:00:30 发布