
开源协议
:GPL 3.0
描述
项目简介
此项目是基于esp32开发,是一个桌面宠物狗,成本比较低,制作简单
项目功能
此处可填写项目的相关功能及应用场景,示例:
只是一只机器狗,不挑食,不拆家
主要功能
- 各种动作(手机遥控,语音控制等多种方式)
- 表情显示(手机控制,动作跟随)
- 时钟,天气
- 大模型语音交互
项目参数
硬件信息:
- 项目采用立创的ESP32开发板作为主控
- ASRPRO核心板作为语音模块
- 0.96寸OLED(SSD1306或SSD1315)
- SG90舵机四只作为机器狗的四条腿
- 电池部分采用7.4V电池包
- IP2326充电模块
软件信息:
- 此项目基于Arduino开发
- 使用VsCode、PlatformIO作为开发工具
- 使用U8g2,NTPClient,ArduinoJson,ESPAsyncWebServer,SPIFFS,Servo 等依赖库
原理解析(硬件说明)
这个机器狗的硬件包括5个部分:
主控电路
主控使用立创的ESP32开发板
机器狗的核心控制单元,负责处理按键输入,驱动显示屏,给舵机发送pwm信号,与ASRPRO语音模块通讯,检测电池状态,以及通过内置Wi-Fi模块实现网络连接,用于获取实时信息和远程更新数据

舵机单路
作为项目的运动部分,负责控制舵机,确保电机正常驱动。
舵机采用5V供电,建议PWM信号脚增加下拉电阻,在没有信号时下拉到低电平,确保电机停转和起始状态稳定。

屏幕电路
IIC驱动屏幕,确保屏幕正常显示。
值得注意的是,由于IIC是开漏输出,这意味着它们可以吸收电流,但无法提供电流,所以我们需要添加上拉电阻。至于为什么不能设置为推挽输出,由于IIC是一主多从,所以当连接多个设备时,一个设备信号高电平(P-MOS导通),一个设备信号低电平(N-MOS导通),在一根信号线上就会形成短路

语音电路
ASRPRO模块实现语音交互功功能,连接麦克风咪头以及喇叭,以及和主控通讯
该模块通过串口与主控连接


电源电路
电池是7.4V软包电池,舵机和主控以及语音模块都是5V供电,所以使用LDO实现5V输出,由于设备比较多,所以使用了3个1117来确保有足够的电流

软件代码
舵机与屏幕部分教学文档里有详细介绍,这里重点介绍下大模型的接入与语音控制
接入大模型
流程是这样的

进入deepseek官网(https://www.deepseek.com/),API开放平台

创建API Key

连接信息
const char* api_key = "your api key"; // 刚申请的
const char* host = "api.deepseek.com";
const int httpsPort = 443;
String inputString = "";
bool stringComplete = false;
创建Payload
String buildPayload(const String& prompt) {
JsonDocument doc;
doc["model"] = "deepseek-chat";
doc["stream"] = false;
JsonArray messages = doc["messages"].to();
JsonObject systemMsg = messages.add();
systemMsg["role"] = "system";
systemMsg["content"] = "You are a helpful assistant";
JsonObject userMsg = messages.add();
userMsg["role"] = "user";
userMsg["content"] = prompt;
String payload;
serializeJson(doc, payload);
return payload;
}
发送请求
String sendToAPI(const String& prompt) {
WiFiClientSecure client;
client.setInsecure();
client.setTimeout(15000);
String cleanedPrompt = cleanInput(prompt);
Serial.println("Cleaned Prompt: " + cleanedPrompt);
String payload = buildPayload(cleanedPrompt);
Serial.println("Request Payload:");
Serial.println(payload);
u8g2.clearBuffer();
u8g2.drawStr(0, 10, "waiting for response...");
if (!client.connect(host, httpsPort)) {
return "Connection failed";
}
String request = String("POST /v1/chat/completions HTTP/1.1\r\n") +
"Host: " + host + "\r\n" +
"Authorization: Bearer " + api_key + "\r\n" +
"Content-Type: application/json\r\n" +
"Content-Length: " + payload.length() + "\r\n\r\n" +
payload;
client.print(request);
String response;
while (client.connected() || client.available()) {
if (client.available()) {
response += client.readStringUntil('\n');
}
}
int jsonStart = response.indexOf('{');
if (jsonStart == -1) {
return "No JSON found";
}
return parseResponse(response.substring(jsonStart));
}
响应
String parseResponse(const String& json) {
// 创建一个 JsonDocument 对象,用于存储解析后的 JSON 数据
JsonDocument doc;
// 使用 deserializeJson 函数将输入的 JSON 字符串解析到 doc 对象中
// 返回一个 DeserializationError 对象,表示解析过程中是否发生错误
DeserializationError error = deserializeJson(doc, json);
// 检查解析过程中是否发生错误
if (error) {
// 如果发生错误,返回一个包含错误信息的字符串
return "Parse Error: " + String(error.c_str());
}
// 检查解析后的 JSON 数据中是否包含 "choices" 键
if (!doc.containsKey("choices")) {
// 如果不包含 "choices" 键,返回 "Invalid Response" 字符串
return "Invalid Response";
}
// 从解析后的 JSON 数据中获取 "choices" 键下的第一个元素中的 "message" 键下的 "content" 键的值
// 该值是一个 const char* 类型的指针
const char* content = doc["choices"][0]["message"]["content"];
// 检查获取到的 content 是否为空
return content ? String(content) : "Empty Content";
}
注意事项
1.请勿强行掰动舵机,避免电机损坏
2.购买电池注意输出电流,过小带不动四只舵机
3.充电时注意观察,避免劣质电池起火
4.断电前建议进入回收模式,防止碰撞导致损毁
5.语音交互功能需要自定义音色等功能请选择4M以上的ASR_PRO模块
实物图





设计图
未生成预览图,请在编辑器重新保存一次BOM
暂无BOM
克隆工程工程成员
知识产权声明&复刻说明
本项目为开源硬件项目,其相关的知识产权归创作者所有。创作者在本平台上传该硬件项目仅供平台用户用于学习交流及研究,不包括任何商业性使用,请勿用于商业售卖或其他盈利性的用途;如您认为本项目涉嫌侵犯了您的相关权益,请点击上方“侵权投诉”按钮,我们将按照嘉立创《侵权投诉与申诉规则》进行处理。
请在进行项目复刻时自行验证电路的可行性,并自行辨别该项目是否对您适用。您对复刻项目的任何后果负责,无论何种情况,本平台将不对您在复刻项目时,遇到的任何因开源项目电路设计问题所导致的直接、间接等损害负责。


评论