代码拉取完成,页面将自动刷新
/*
说明:ESP32 接入DeepSeek API,实现AI大模型对话
注意:因为使用非流式输出,获取完全部才输出,自己调整缓冲区和响应长度max_tokens,不然内容太多会响应失败。
Arduino IDE版本为2.3.4, esp32包版本为2.0.17
B站:星汇极客
个人网站获取更多资源:https://www.mqtttwj.online
创建时间:2025.01.28
更新时间:2025.01.28
*/
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <ArduinoJson.h> //记得先安装库
// 配置信息
const char* ssid = "ChinaNet-4tRa"; //wifi名字
const char* password = "88888888"; //wifi密码
const char* host = "api.deepseek.com"; //DeepSeek的网址
const int port = 443;
const char* apiKey = "sk-dd287464e97c467f8b6db8a036561343"; //先注册,创建key
// 分块解析状态机
enum ChunkState { PARSE_CHUNK_SIZE, PARSE_CHUNK_DATA, PARSE_COMPLETE };
//类似main()函数
void setup() {
Serial.begin(115200);
WiFi.setAutoReconnect(true);
connectToWiFi();
}
//类似while()循环
void loop() {
if (WiFi.status() != WL_CONNECTED) { //重新连接机制
Serial.println("\n[WARN] WiFi连接丢失,尝试重连...");
connectToWiFi();
delay(1000);
return;
}
if (Serial.available() > 0) { //获取串口字符串
String userInput = Serial.readStringUntil('\n');
userInput.trim();
if (userInput.length() > 0) {
Serial.printf("\n[ESP32] 发送消息:%s\r\n", userInput.c_str());
String response = sendDeepSeekRequest(userInput);
if (response.length() > 0) {
parseApiResponse(response);
}
}
Serial.println("\r\n[DeepSeek] 请输入问题给我(按回车发送):");
}
}
// WiFi连接函数
void connectToWiFi() {
WiFi.mode(WIFI_STA); //STA模式
Serial.println("\n[ESP32] 正在连接WiFi...");
WiFi.begin(ssid, password);
unsigned long startTime = millis();
while (WiFi.status() != WL_CONNECTED && millis() - startTime < 20000) {
delay(500);
Serial.print(".");
}
if (WiFi.status() == WL_CONNECTED) {
Serial.printf("\n[ESP32] 连接成功! IP地址: %s\n", WiFi.localIP().toString().c_str());
Serial.println("\r\n[DeepSeek] 请输入问题给我(按回车发送):");
} else {
Serial.println("\n[ERROR] 连接超时,请检查配置");
}
}
// API请求函数
String sendDeepSeekRequest(String userContent) {
WiFiClientSecure client; // 在这里正确声明客户端
client.setInsecure(); // 不验证证书
client.setTimeout(15000); // 设置整体超时时间
Serial.println("\r\n[ESP32] 正在连接API服务器...");
if (!client.connect(host, port)) {
Serial.println("[ERROR] API服务器连接失败");
return "";
}
// 构建JSON请求
StaticJsonDocument<512> requestDoc;
requestDoc["model"] = "deepseek-chat"; //deepseek-chat
requestDoc["max_tokens"] = 5000; // 限制响应长度,注意:这个是一次性返回多少个字,最好多一点,不然获取不完整!!!
JsonArray messages = requestDoc.createNestedArray("messages");
JsonObject message = messages.createNestedObject();
message["role"] = "user";
message["content"] = userContent;
String payload;
serializeJson(requestDoc, payload);
// 构建HTTP请求
String request = String("POST /v1/chat/completions HTTP/1.1\r\n") +
"Host: " + host + "\r\n" +
"Authorization: Bearer " + apiKey + "\r\n" +
"Content-Type: application/json\r\n" +
"Connection: close\r\n" +
"Content-Length: " + payload.length() + "\r\n\r\n" +
payload;
// 发送请求
Serial.println("[ESP32] 正在发送请求...");
client.print(request);
// 跳过响应头
while (client.connected() && client.available() == 0) delay(10);
while (client.available()) {
String line = client.readStringUntil('\n');
if (line == "\r") break;
}
// 处理分块响应
String responseBody = parseChunkedResponse(client);
client.stop();
return responseBody;
}
// 分块响应解析
String parseChunkedResponse(WiFiClientSecure &client) {
String body;
ChunkState state = PARSE_CHUNK_SIZE;
int chunkSize = 0;
unsigned long lastData = millis();
Serial.println("[ESP32] 开始接收分块数据...");
while (client.connected() && millis() - lastData < 60000) { // 60秒超时,注意:如果你的内容很多,超时时间要加长,不然还没思考完就超时了!!!
if (client.available()) {
lastData = millis();
switch (state) {
case PARSE_CHUNK_SIZE: {
String hexLine = client.readStringUntil('\r');
client.read(); // 跳过\n
chunkSize = strtol(hexLine.c_str(), NULL, 16);
Serial.printf("[ESP32] 块大小: %d\n", chunkSize);
if (chunkSize <= 0) {
state = PARSE_COMPLETE;
Serial.println("[ESP32] 接收到结束块");
} else {
state = PARSE_CHUNK_DATA;
}
break;
}
case PARSE_CHUNK_DATA: {
Serial.println("[ESP32] 接收块数据中...");
while (chunkSize > 0) {
if (client.available()) {
char c = client.read();
body += c;
chunkSize--;
lastData = millis(); // 每次接收重置超时
} else {
delay(10); // 避免CPU占用过高,自己看情况吧
}
}
client.read(); // 跳过\r
client.read(); // 跳过\n
state = PARSE_CHUNK_SIZE;
Serial.println("[ESP32] 块数据接收完成");
break;
}
case PARSE_COMPLETE:
Serial.printf("[ESP32] 接收完成,总长度: %d\n", body.length());
return body;
}
} else {
delay(50); // 降低CPU占用
}
}
Serial.println("[WARN] 分块接收超时");
return body;
}
// 响应解析
void parseApiResponse(String response) {
if (response.length() == 0) {
Serial.println("[ERROR] 收到空响应");
return;
}
Serial.println("[ESP32] 原始响应数据:");
Serial.println(response);
DynamicJsonDocument doc(8192); // 增大缓冲区,注意:这个是esp32能获取的数据,自己看情况!!!
DeserializationError error = deserializeJson(doc, response);
if (error) {
Serial.print("[ERROR] JSON解析失败: ");
Serial.println(error.c_str());
return;
}
if (doc.containsKey("error")) {
Serial.print("[ERROR] API错误: ");
serializeJson(doc["error"], Serial);
return;
}
if (doc.containsKey("choices")) {
const char* content = doc["choices"][0]["message"]["content"];
Serial.println("\n---------- DeepSeek回复 ----------");
Serial.println(content);
JsonObject usage = doc["usage"];
Serial.printf("\nToken使用: 总%d (输入%d + 输出%d)\n",
usage["total_tokens"].as<int>(),
usage["prompt_tokens"].as<int>(),
usage["completion_tokens"].as<int>());
Serial.println("--------------------------");
} else {
Serial.println("[ERROR] 响应格式异常");
}
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。