
eda机器狗
简介
本项目是复刻嘉立创的智能机器狗,本项目基于ESP8266构建,是一个可以手机控制的桌面宠物,电路大部分使用插件封装,易于新手焊接,方便焊接。 本项目不含电池成本大致为30元,项目功能多样
简介:本项目是复刻嘉立创的智能机器狗,本项目基于ESP8266构建,是一个可以手机控制的桌面宠物,电路大部分使用插件封装,易于新手焊接,方便焊接。 本项目不含电池成本大致为30元,项目功能多样开源协议
:GPL 3.0
描述
项目简介
本项目是复刻嘉立创的智能机器狗,本项目基于ESP8266构建,是一个可以手机控制的桌面宠物,电路大部分使用插件封装,易于新手焊接,方便焊接。 本项目不含电池成本大致为30元,项目功能多样
项目功能
通过ESP8266 wifi 192.168.4.1 连接配置WIFI参数及心知天气api可以在局域网用网页遥控器功能控制机器狗做相应动作及显示不同的表情、时间,和天气。
项目参数
本设计采用ESP8266主控,内置WIFI功能,通过AP模式遥控
屏幕支持0.96寸SSD1306/SSD1315驱动OLED显示屏,可显示表情、时钟、天气等相关信息
选用AMS1117 LDO线性稳压器, 负责将8.4V和5V电压分别转换成5V和3.3V,为舵机及主控提供电源
项目支持SG-90/MG-90 180度及360度版本,推荐使用180度版本,自带限位器,无需电机校准
原理解析(硬件说明)
2.1 电路图原理分析
该项目在硬件设计中涉及的主要电路模块包括:
(1) 主控电路:作为项目的核心控制单元,负责处理按键输入、驱动显示屏,并通过内置Wi-Fi模块实现网络连接,用于获取实时信息和远程更新数据。
(2) 舵机电路:作为项目的运动部分,负责控制舵机,确保电机正常驱动。
(3) 电源电路:为整个电路系统提供稳定的电源输出,确保所有模块在合适的电压下稳定运行。
该项目的硬件部分设计较为简单,主要通过软硬件结合实现信息显示与运动交互控制。
2.2.1 主控电路
(1)ESP8266模组外部电路
img.png项目采用了乐鑫科技的ESP8266芯片模组作为主控。ESP8266芯片以其高集成度、优异的RF性能和低功耗著称,集成了完整的Wi-Fi功能,非常适合用于物联网应用的开发。其内置的Tensilica L106 32位RISC处理器,最高工作频率可达160 MHz,支持实时操作系统(RTOS)和Wi-Fi协议栈,使其在处理数据传输及任务管理时具备极强的灵活性和高效性。img_101.png该模组的电路设计遵循官方推荐的设计规范。在GPIO0、GPIO2、RST、EN等引脚上拉10K电阻,以确保这些引脚在芯片启动时保持高电平,从而确保芯片能够正常工作。GPIO15引脚在SDK中定义为CS片选引脚,同时参考模式启动要求则通过10K下拉电阻保持低电平。由于项目空间有限,并且使用了LDO稳压器,我们简化了电路设计,去除了部分外围电容(如10uF和100nF电容),但这些修改并不影响模组的正常运行(规范而言最好加上,起到滤波作用)。通过参考ESP8266的数据手册和官方的应用设计图,在嘉立创EDA中我们设计的电路最终如下图所示:img_63.png
(2)硬件IIC通信
img.png
另外,在ESP8266的库函数定义中我们可以知道SDK定义的IIC引脚分别为GPIO4-SDA、GPIO5-SCL为了方便调试,我们选用SDK提供的IIC为屏幕通信,当然你可以自定义,不影响。
(3)模式切换电路
img_4.pngESP8266的启动模式包括运行模式和下载模式,为确保能够顺利烧录固件并调试程序,电路设计中引出了TX、RX、GND和3.3V引脚作为串口通信接口,同时增加了IO0和GND引脚用于进入下载模式的跳线设计。用户可以通过简单的跳线操作快速切换工作模式,从而实现程序烧录与正常运行。在嘉立创EDA中我们设计的电路最终如下图所示:
img_64.png至此,我们的主控电路就基本设计完成
2.2.2 交互电路
(1)IIC屏幕显示电路
该项目的显示屏采用了OLED屏幕模组,该模组本身已经集成了驱动电路,因此不需要额外设计复杂的外围电路。img_5.png
img_5.png通过直接将屏幕模组与主控模块的IIC接口相连,即可完成图像和信息的显示。我们通过查阅屏幕模组的技术规格,确认了引脚排列、接口类型以及屏幕的功耗需求,确保其与主控电路的匹配性。在电路设计中,屏幕模组的连接端口采用标准插座接口,简化了电路设计并提高了可维护性和模块化程度。在嘉立创EDA中我们设计的电路最终如下图所示:img_65.png
(2)舵机驱动电路
在本项目中为了使用易焊接的插件器件并确保体积较小,所以没有在舵机的PWM信号引脚接下拉电阻等信号优化措施,建议可以增加下拉电阻,确保电机停转和起始状态稳定,在后续的软件调试中也会更方便。img_5.png
(3)按键电路
按键部分因为接口有限选择的是GPIO15和GPIO2接口,由于ESP12F在开机时GPIO15必须下拉,并接有下拉电阻,所以这里开关选择接入3.3V,当GPIO15输入高电平时触发开关。GPIO2为正常接入GND。img_5.png
(3)ADC电量检测电路
img_54.png修改分压器适配8.4V到1V 现在需要适配新的输入电压范围(最大8.4V)到ESP8266的1.0VADC输入。分压比计算如下:
假设保持100k,计算:
对于,输出电压:
对于电压较低时(如4.2V),输出电压为:
分压电路成功将8.4V的输入电压压缩到0-1V范围内。
2.2.3 电源电路
(1)舵机电源供电电路
依照舵机的数据手册,我们可以知道SG90舵机的驱动电压是4.8-6V,当然,在测试中其实3.3V也能驱动舵机,但是低电压驱动会导致舵机扭矩不足,动力较差,甚至用手轻挡就会让电机停止转动,由于我们的EDA-Robot是需要背负2节14500电池,需要承载相应的重量,所以这里我们选择使用5V驱动img_5.png既然选择5V驱动,所以我们选择了AMS1117-5V固定输出LDO,我们只要确保输入LDO的电压在5-18V即可img_5.pngimg_5.png
(2)电池选择
前面我们选择了AMS1117-5V版本,所以我们要确保输入电压5-18V,为了简化电路,缩小体积,提升安全性,这里我们选取14500钢壳锂电池。单节电池范围在4.2-2.75V,那么由串联分压可知我们可以串联2节14500电池,这样电池范围就在5.5-8.4V,在AMS1117-5V输入范围内,这样既能得到高容量,又能省去升压电路,极为方便。img_5.png由于商城没有14500电池,所以这里我们使用在线公开库的14500电池,当然你也可以自己设计一个符号和封装。img_5.png
(3)主控电源供电电路
由esp8266的数据手册可知,主控工作电压在3.3V,那么我们选择AMS1117-3.3V固定输出LDO就可以了img_5.pngimg_5.png这里我们选择了AMS1117-3.3V,支持3.3V-18V输出,在ESP8266的数据手册中要求供电电流要高于500mA,为了确保电流足够,我们直接从14500取电,确保舵机和主控的供电互不干涉。img_5.pngimg_5.png
3.1 原理图与PCB设计
在了解完机器狗的硬件电路原理后接下来进行原理图与PCB设计环节,原理图设计部分包含了元器件选型、元器件搜索以及原理图整理的内容;PCB设计部分包含边框设计、元器件分类布局、PCB走线与设计检查、PCB生产与打样等内容。
3.1.1 原理图设计
(1)工程创建
打开嘉立创EDA专业版软件(https://pro.lceda.cn/editor ),登录账号后选择创建工程, 输入工程名称:EDA-Robot机器狗,系统会自动创建一个工程项目,接下来就在该项目中完成EDA-Robot机器狗的原理图与PCB、3D外壳与面板设计的内容。img_16.png创建好工程后打开左侧工程列表中的Schematic1图页,在右侧画布区域放置元器件进行连接,底下的PCB1是用来绘制PCB图的页面,设计流程是先完成原理图的设计后再到PCB设计。img_17.png
(2)元器件搜索
元器件搜索的方式有三种途径,第一种可以在左侧的常用库中找到官方提供的参考库进行放置,优点是比较方便,缺点是库种类较少;第二种方式是通过顶部菜单栏中的放置按钮选项,可以看到器件实物图与参考价格、数据手册等信息;第三种方式是在软件底部面板中搜索放置器件,这种方式的好处是可以看到所选器件的符号、封装与3D模型效果图。img_18.pngimg_19.png
(3)元器件放置
为了方便初学者学习,该项目提供了完成电路图中所需的元器件清单,可以直接根据器件清单中提供的器件编号及备注信息进行元器件的搜索和放置。以底部面板搜索器件为例,在原理图工作区打开底部库面板,选择器件类目,在搜索栏中输入器件编号,进行搜索找到对应元器件点击放置在画布中即可。里面的M3铜柱在EDA左侧常用库的最后一项可以找到并放置。img_20.pngEDA-Robot项目物料清单
序号 器件名称 参数 立创编号(优先选) 备选料编号 数量 单价 起购数 总价 备注
1 直插电阻 10K C410695 C119347 9 0.05966 50 2.98
2 直插电容 10uf C43351 C503219 4 0.0819 50 4.1
3 独石电容 100nf C5632430 C263184 1 0.1881 10 1.88
4 主控芯片 ESP-12F C82891 无 1 14.7 1 14.7
5 轻触按键 66 C7528706 C7528712 2 0.11951 10 1.2
6 直插电阻 75K C410583 电商平台 1 0.0613 50 3.07
7 排针 13Pin C27985188 C42431836 5 0.0546 50 2.73 可不买,使用6P掰开就行
8 降压芯片 3.3V C347222 C6186 1 0.259 5 1.3
9 降压芯片 5.0V C5345984 C6068482 1 0.22509 10 2.25
10 OLED屏幕 HS96L03W2C03 C5248080 电商平台 1 13.14 1 13.14
11 排针 1*6Pin C37208 C5156615 1 0.190475 20 3.81
12 开关 SK12D07VG5 C431548 C3020419 1 0.135375 20 2.71
13 电池盒 5号2节 电商平台 无 1 无 无 无 除了电池盒、还有电池
14 舵机 SG90-180° 电商平台 无 1 无 无 无
(4)原理图整理
img_66.png
软件代码
// 导入头文件
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "image.cpp"
#define BUTTON_PIN 2 // GPIO2 引脚 (D4)
#define BUTTON_PIN2 15
volatile bool buttonPressed = false; // 按键标志
volatile bool buttonPressed2 = false; // 按键标志
unsigned long lastPressTime = 0; // 上次按键时间
const unsigned long debounceDelay = 50; // 消抖时间 (ms)
unsigned long lastPressTime2 = 0; // 上次按键时间
const unsigned long debounceDelay2 = 50; // 消抖时间 (ms)
// 定义
// 分压器比例(输入电压到 ADC 电压的比例)
const float voltageDividerRatio = 8.4; // 分压比(8.4倍缩小)
// 电压范围(电池电压)
const float minVoltage = 6.4; // 电压为0%时
const float maxVoltage = 8.4; // 电压为100%时
// 采样次数
const int numSamples = 10;
float batteryVoltage = 0; // 计算电池电压
int batteryPercentage = 0;
Servo servo1;
Servo servo2;
Servo servo3;
Servo servo4;
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=/U8X8_PIN_NONE, / clock=/5, / data=*/4); // ESP32 Thing, HW I2C with pin remapping
// 设置WiFi热点名称和密码
const char *ssid = "EDA-Robot";
const char *password = ""; // 无密码
AsyncWebServer server(80);
// random(minValue, maxValue)
// 微调参数值,舵机1
WiFiUDP ntpUDP;
const char *weatherAPI = "http://api.seniverse.com/v3/weather/daily.json?key=";
NTPClient timeClient(ntpUDP, "ntp1.aliyun.com", 8 * 3600, 60000);
String temperature = "";
String humidity = "";
String weather = "";
String useruid = "";
String cityname = "";
String weatherapi = "";
int engine1 = 14; // 舵机引脚
// int engine1offsetleftpwm = -93; // 舵机左转补偿
// int engine1offsetrightpwm = -87; // 舵机左转补偿-40
int engine2 = 16; // 舵机引脚
// int engine2offsetleftpwm = -120; // 舵机左转补偿
// int engine2offsetrightpwm = -122; // 舵机左转补偿-60
int engine3 = 12; // 舵机引脚
// int engine3offsetleftpwm = -3; // 舵机左转补偿
// int engine3offsetrightpwm = -57; // 舵机左转补偿
int engine4 = 13; // 舵机引脚
// int engine4offsetleftpwm = -78; // 舵机左转补偿
// int engine4offsetrightpwm = -109; // 舵机左转补偿-71
int speed = 300; // 舵机转速
int runtime = 100; // 运动延时预留变量,用于控制动作连贯性,如果你不知道这是什么不建议修改
static bool initweather = false; // 天气初始化
bool freestate = false;
// 定义一个标志来检查是否需要执行动作
int prevEmojiState = -1; // 用于跟踪之前的 emojiState
int actionstate = 0;
int emojiState = 0; // 表情状态
const char *ssidFile = "/ssid.json";
void ICACHE_RAM_ATTR handleButtonPress()
{
unsigned long currentTime = millis();
if (currentTime - lastPressTime > debounceDelay)
{
buttonPressed = true;
lastPressTime = currentTime;
}
unsigned long currentTime2 = millis();
if (currentTime2 - lastPressTime2 > debounceDelay2)
{
buttonPressed2 = true;
lastPressTime2 = currentTime2;
}
}
void handleWiFiConfig()
{
// 启动服务器
server.on("/front", HTTP_GET, [](AsyncWebServerRequest *request)
{
actionstate = 1; // 设置标志,执行舵机动作
request->send(200, "text/plain", "Front function started"); });
server.on("/back", HTTP_GET, [](AsyncWebServerRequest *request)
{
actionstate = 4; // 设置标志,执行舵机动作
request->send(200, "text/plain", "Front function started"); });
server.on("/left", HTTP_GET, [](AsyncWebServerRequest *request)
{
actionstate = 2; // 设置标志,执行舵机动作
request->send(200, "text/plain", "Front function started"); });
server.on("/right", HTTP_GET, [](AsyncWebServerRequest *request)
{
actionstate = 3; // 设置标志,执行舵机动作
request->send(200, "text/plain", "Front function started"); });
server.on("/toplefthand", HTTP_GET, [](AsyncWebServerRequest *request)
{
actionstate = 5; // 设置标志,执行舵机动作
request->send(200, "text/plain", "Front function started"); });
server.on("/toprighthand", HTTP_GET, [](AsyncWebServerRequest *request)
{
actionstate = 6; // 设置标志,执行舵机动作
request->send(200, "text/plain", "Front function started"); });
server.on("/sitdown", HTTP_GET, [](AsyncWebServerRequest *request)
{
actionstate = 8; // 设置标志,执行舵机动作
request->send(200, "text/plain", "Front function started"); });
server.on("/lie", HTTP_GET, [](AsyncWebServerRequest *request)
{
actionstate = 7;
request->send(200, "text/plain", "Front function started"); });
server.on("/sleep", HTTP_GET, [](AsyncWebServerRequest *request)
{
actionstate = 10;
request->send(200, "text/plain", "Front function started"); });
// server.on("/dance", HTTP_GET, [](AsyncWebServerRequest *request)
// {
// actionstate = 7; // 设置标志,执行舵机动作
// request->send(200, "text/plain", "Front function started"); });
server.on("/free", HTTP_GET, [](AsyncWebServerRequest *request)
{
freestate=true;
request->send(200, "text/plain", "Front function started"); });
server.on("/offfree", HTTP_GET, [](AsyncWebServerRequest *request)
{
freestate=false;
request->send(200, "text/plain", "Front function started"); });
server.on("/histate", HTTP_GET, [](AsyncWebServerRequest *request)
{
emojiState = 0; // 设置标志,执行舵机动作
request->send(200, "text/plain", "Front function started"); });
server.on("/angrystate", HTTP_GET, [](AsyncWebServerRequest *request)
{
emojiState = 1; // 设置标志,执行舵机动作
request->send(200, "text/plain", "Front function started"); });
server.on("/edastate", HTTP_GET, [](AsyncWebServerRequest *request)
{
emojiState = 9; // 设置标志,执行舵机动作
request->send(200, "text/plain", "Front function started"); });
server.on("/errorstate", HTTP_GET, [](AsyncWebServerRequest *request)
{
emojiState = 2; // 设置标志,执行舵机动作
request->send(200, "text/plain", "Front function started"); });
server.on("/batteryVoltage", HTTP_GET, [](AsyncWebServerRequest *request)
{ request->send(200, "text/plain", String(batteryVoltage)); });
server.on("/batteryPercentage", HTTP_GET, [](AsyncWebServerRequest *request)
{ request->send(200, "text/plain", String(batteryPercentage)); });
server.on("/speed", HTTP_GET, [](AsyncWebServerRequest *request)
{ request->send(200, "text/plain", String(speed)); });
server.on("/speedup", HTTP_GET, [](AsyncWebServerRequest *request)
{
speed++; // 设置标志,执行舵机动作
request->send(200, "text/plain", "Front function started"); });
server.on("/speeddown", HTTP_GET, [](AsyncWebServerRequest *request)
{
speed--;
request->send(200, "text/plain", "Front function started"); });
server.on("/speedup", HTTP_GET, [](AsyncWebServerRequest *request)
{
speed++; // 设置标志,执行舵机动作
request->send(200, "text/plain", "Front function started"); });
server.on("/speeddown", HTTP_GET, [](AsyncWebServerRequest *request)
{
speed--;
request->send(200, "text/plain", "Front function started"); });
server.on("/dowhatstate", HTTP_GET, [](AsyncWebServerRequest *request)
{
emojiState = 3; // 设置标志,执行舵机动作
request->send(200, "text/plain", "Front function started"); });
server.on("/lovestate", HTTP_GET, [](AsyncWebServerRequest *request)
{
emojiState = 4; // 设置标志,执行舵机动作
request->send(200, "text/plain", "Front function started"); });
server.on("/sickstate", HTTP_GET, [](AsyncWebServerRequest *request)
{
emojiState = 5; // 设置标志,执行舵机动作
request->send(200, "text/plain", "Front function started"); });
server.on("/yunstate", HTTP_GET, [](AsyncWebServerRequest *request)
{
emojiState = 6;
request->send(200, "text/plain", "Front function started"); });
server.on("/time", HTTP_GET, [](AsyncWebServerRequest *request)
{
emojiState = 8;
request->send(200, "text/plain", "Front function started"); });
server.on("/weather", HTTP_GET, [](AsyncWebServerRequest *request)
{
emojiState = 7; // 设置标志,执行舵机动作
request->send(200, "text/plain", "Front function started"); });
server.on("/connect", HTTP_POST, [](AsyncWebServerRequest *request)
{
// 获取POST参数:ssid、pass、uid、city、api
String ssid = request->getParam("ssid", true)->value();
String pass = request->getParam("pass", true)->value();
String uid = request->getParam("uid", true)->value();
String city = request->getParam("city", true)->value();
String api = request->getParam("api", true)->value();
// 打印接收到的参数
Serial.println(ssid);
Serial.println(pass);
// 保存WiFi信息到JSON文件
DynamicJsonDocument doc(1024);
doc["ssid"] = ssid;
doc["pass"] = pass;
doc["uid"] = uid;
doc["city"] = city;
doc["api"] = api;
fs::File file = SPIFFS.open(ssidFile, "w"); // 打开文件进行写入
if (file) {
serializeJson(doc, file); // 将JSON内容写入文件
file.close(); // 关闭文件
}
// 更新全局变量
useruid = uid;
cityname = city;
weatherapi = api;
// 开始连接WiFi
WiFi.begin(ssid.c_str(), pass.c_str());
// 发送HTML响应,告知用户正在连接
request->send(200, "text/html", "<h1>Connecting...</h1>"); });
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
{
// 检查SPIFFS文件系统中是否存在index.html文件
if (SPIFFS.exists("/index.html")) {
fs::File file = SPIFFS.open("/index.html", "r"); // 打开index.html文件
if (file) {
size_t fileSize = file.size(); // 获取文件大小
String fileContent;
// 逐字节读取文件内容
while (file.available()) {
fileContent += (char)file.read();
}
file.close(); // 关闭文件
// 返回HTML内容
request->send(200, "text/html", fileContent);
return;
}
}
// 如果文件不存在,返回404错误
request->send(404, "text/plain", "File Not Found"); });
server.on("/control.html", HTTP_GET, [](AsyncWebServerRequest *request)
{
// 检查SPIFFS文件系统中是否存在index.html文件
if (SPIFFS.exists("/control.html")) {
fs::File file = SPIFFS.open("/control.html", "r"); // 打开index.html文件
if (file) {
size_t fileSize = file.size(); // 获取文件大小
String fileContent;
// 逐字节读取文件内容
while (file.available()) {
fileContent += (char)file.read();
}
file.close(); // 关闭文件
// 返回HTML内容
request->send(200, "text/html", fileContent);
return;
}
}
// 如果文件不存在,返回404错误
request->send(404, "text/plain", "File Not Found"); });
server.on("/engine.html", HTTP_GET, [](AsyncWebServerRequest *request)
{
// 检查SPIFFS文件系统中是否存在index.html文件
if (SPIFFS.exists("/engine.html")) {
fs::File file = SPIFFS.open("/engine.html", "r"); // 打开index.html文件
if (file) {
size_t fileSize = file.size(); // 获取文件大小
String fileContent;
// 逐字节读取文件内容
while (file.available()) {
fileContent += (char)file.read();
}
file.close(); // 关闭文件
// 返回HTML内容
request->send(200, "text/html", fileContent);
return;
}
}
// 如果文件不存在,返回404错误
request->send(404, "text/plain", "File Not Found"); });
server.on("/setting.html", HTTP_GET, [](AsyncWebServerRequest *request)
{
// 检查SPIFFS文件系统中是否存在index.html文件
if (SPIFFS.exists("/setting.html")) {
fs::File file = SPIFFS.open("/setting.html", "r"); // 打开index.html文件
if (file) {
size_t fileSize = file.size(); // 获取文件大小
String fileContent;
// 逐字节读取文件内容
while (file.available()) {
fileContent += (char)file.read();
}
file.close(); // 关闭文件
// 返回HTML内容
request->send(200, "text/html", fileContent);
return;
}
}
// 如果文件不存在,返回404错误
request->send(404, "text/plain", "File Not Found"); });
// 启动服务器
server.begin();
};
void loadWiFiConfig()
{
if (SPIFFS.begin())
{
fs::File file = SPIFFS.open(ssidFile, "r");
if (file)
{
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, file);
if (!error)
{
String ssid = doc["ssid"];
String pass = doc["pass"];
String uid = doc["uid"];
String city = doc["city"];
String api = doc["api"];
useruid = uid;
cityname = city;
weatherapi = api;
WiFi.begin(ssid.c_str(), pass.c_str());
// 尝试连接WiFi,最多等待10秒
unsigned long startAttemptTime = millis();
while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < 5000)
{
delay(500);
}
// 如果连接失败,打印状态
if (WiFi.status() != WL_CONNECTED)
{
Serial.println("WiFi connection failed, starting captive portal...");
handleWiFiConfig(); // 启动强制门户
}
else
{
Serial.println("WiFi connected");
timeClient.begin();
}
}
file.close();
}
}
}
void fetchWeather()
{ // 天气捕捉
if (initweather == false)
{
// mylcd.fillScreen(TFT_BLACK);
if (WiFi.status() == WL_CONNECTED)
{
WiFiClient client;
HTTPClient http;
if (http.begin(client, weatherAPI + weatherapi + "&location=" + cityname + "&language=zh-Hans&unit=c&start=0&days=1"))
{
int httpCode = http.GET();
if (httpCode > 0)
{
String payload = http.getString();
Serial.println("JSON Response:");
Serial.println(payload);
DynamicJsonDocument doc(1024);
deserializeJson(doc, payload);
String temperature2 = doc["results"][0]["daily"][0]["high"];
String humidity2 = doc["results"][0]["daily"][0]["humidity"];
String weathe2r = doc["results"][0]["daily"][0]["text_day"];
temperature = temperature2;
humidity = humidity2;
weather = weathe2r;
initweather = true;
Serial.print("Data received: ");
Serial.println(temperature);
Serial.println(humidity);
Serial.println(weather);
}
else
{
Serial.printf("HTTP GET request failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
}
else
{
Serial.println("Failed to connect to server");
}
// mylcd.fillScreen(TFT_BLACK);
}
}
if (weather == "阴" || weather == "多云")
{
do
{
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawXBMP(0, 0, 64, 64, cloud);
u8g2.drawStr(64, 20, "Temp");
String temperatureString = String(temperature) + " C";
u8g2.drawStr(64, 30, temperatureString.c_str());
u8g2.drawStr(64, 50, "Humidity");
String humidityString = String(humidity) + " %";
u8g2.drawStr(64, 60, humidityString.c_str());
} while (u8g2.nextPage());
// mylcd.pushImage(mylcd.width() / 2-(99/2), 10, 99, 99,cloud);
}
else if (weather == "小雨" || weather == "大雨" || weather == "暴雨" || weather == "雨")
{
do
{
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawXBMP(0, 0, 64, 64, rain);
u8g2.drawStr(64, 20, "Temp");
String temperatureString = String(temperature) + " %";
u8g2.drawStr(64, 30, temperatureString.c_str());
u8g2.drawStr(64, 50, "Humidity");
String humidityString = String(humidity) + " %";
u8g2.drawStr(64, 60, humidityString.c_str());
} while (u8g2.nextPage());
}
else if (weather == "晴")
{
do
{
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(64, 20, "Temp");
u8g2.drawXBMP(0, 0, 64, 64, sun);
String temperatureString = String(temperature) + " %";
u8g2.drawStr(64, 30, temperatureString.c_str());
u8g2.drawStr(64, 50, "Humidity");
String humidityString = String(humidity) + " %";
u8g2.drawStr(64, 60, humidityString.c_str());
} while (u8g2.nextPage());
}
}
void front()
{
servo2.write(140); // 旋转到 90 度
servo3.write(40); // 旋转到 90 度
delay(100);
servo1.write(40); // 旋转到 90 度
servo4.write(140); // 旋转到 90 度
delay(100);
servo2.write(90); // 旋转到 90 度
servo3.write(90); // 旋转到 90 度
delay(100);
servo1.write(90); // 旋转到 90 度
servo4.write(90); // 旋转到 90 度
delay(100);
servo1.write(140); // 旋转到 90 度
servo4.write(40); // 旋转到 90 度
delay(100);
servo2.write(40); // 旋转到 90 度
servo3.write(140); // 旋转到 90 度
delay(100);
servo1.write(90); // 旋转到 90 度
servo4.write(90); // 旋转到 90 度
delay(100);
servo2.write(90); // 旋转到 90 度
servo3.write(90); // 旋转到 90 度
// //+30C 2/3
// servo2.writeMicroseconds(1500 + speed + engine2offsetleftpwm);
// servo3.writeMicroseconds(1500 - speed - engine3offsetleftpwm);
// delay(500-runtime);
// servo2.writeMicroseconds(1500);
// servo3.writeMicroseconds(1500);
// //-30C 1/4
// servo1.writeMicroseconds(1500 - speed - engine1offsetrightpwm);
// servo4.writeMicroseconds(1500 + speed + engine4offsetrightpwm);
// delay(500-runtime);
// servo1.writeMicroseconds(1500);
// servo4.writeMicroseconds(1500);
// // 0C 2/3
// servo2.writeMicroseconds(1500 - speed - engine2offsetrightpwm);
// servo3.writeMicroseconds(1500 + speed + engine3offsetrightpwm);
// delay(500-runtime);
// servo2.writeMicroseconds(1500);
// servo3.writeMicroseconds(1500);
// // 0C 1/4
// servo1.writeMicroseconds(1500 + speed + engine1offsetleftpwm);
// servo4.writeMicroseconds(1500 - speed - engine4offsetleftpwm);
// delay(500-runtime);
// servo1.writeMicroseconds(1500);
// servo4.writeMicroseconds(1500);
// //+30C 1/4
// servo1.writeMicroseconds(1500 + speed + engine1offsetleftpwm);
// servo4.writeMicroseconds(1500 - speed - engine4offsetleftpwm);
// delay(500-runtime);
// servo1.writeMicroseconds(1500);
// servo4.writeMicroseconds(1500);
// //-30C 2/3
// servo2.writeMicroseconds(1500 - speed - engine2offsetrightpwm);
// servo3.writeMicroseconds(1500 + speed + engine3offsetrightpwm);
// delay(500-runtime);
// servo2.writeMicroseconds(1500);
// servo3.writeMicroseconds(1500);
// // 0C 1/4
// servo1.writeMicroseconds(1500 - speed - engine1offsetrightpwm);
// servo4.writeMicroseconds(1500 + speed + engine4offsetrightpwm);
// delay(500-runtime);
// servo1.writeMicroseconds(1500);
// servo4.writeMicroseconds(1500);
// // 0C 2/3
// servo2.writeMicroseconds(1500 + speed + engine2offsetleftpwm);
// servo3.writeMicroseconds(1500 - speed - engine3offsetleftpwm);
// delay(500-runtime);
// servo2.writeMicroseconds(1500);
// servo3.writeMicroseconds(1500);
}
void back()
{
servo3.write(140); // 旋转到 90 度
servo2.write(40); // 旋转到 90 度
delay(100);
servo4.write(40); // 旋转到 90 度
servo1.write(140); // 旋转到 90 度
delay(100);
servo3.write(90); // 旋转到 90 度
servo2.write(90); // 旋转到 90 度
delay(100);
servo4.write(90); // 旋转到 90 度
servo1.write(90); // 旋转到 90 度
delay(100);
servo4.write(140); // 旋转到 90 度
servo1.write(40); // 旋转到 90 度
delay(100);
servo3.write(40); // 旋转到 90 度
servo2.write(140); // 旋转到 90 度
delay(100);
servo4.write(90); // 旋转到 90 度
servo1.write(90); // 旋转到 90 度
delay(100);
servo3.write(90); // 旋转到 90 度
servo2.write(90); // 旋转到 90 度
// //+30C 2/3
// servo2.writeMicroseconds(1500 - speed - engine2offsetrightpwm);
// servo3.writeMicroseconds(1500 + speed + engine3offsetrightpwm);
// delay(500-runtime);
// servo2.writeMicroseconds(1500);
// servo3.writeMicroseconds(1500);
// //-30C 1/4
// servo1.writeMicroseconds(1500 + speed + engine1offsetleftpwm);
// servo4.writeMicroseconds(1500 - speed - engine4offsetleftpwm);
// delay(500-runtime);
// servo1.writeMicroseconds(1500);
// servo4.writeMicroseconds(1500);
// // 0C 2/3
// servo2.writeMicroseconds(1500 + speed + engine2offsetleftpwm);
// servo3.writeMicroseconds(1500 - speed - engine3offsetleftpwm);
// delay(500-runtime);
// servo2.writeMicroseconds(1500);
// servo3.writeMicroseconds(1500);
// // 0C 1/4
// servo1.writeMicroseconds(1500 - speed - engine1offsetrightpwm);
// servo4.writeMicroseconds(1500 + speed + engine4offsetrightpwm);
// delay(500-runtime);
// servo1.writeMicroseconds(1500);
// servo4.writeMicroseconds(1500);
// //+30C 1/4
// servo1.writeMicroseconds(1500 - speed - engine1offsetrightpwm);
// servo4.writeMicroseconds(1500 + speed + engine4offsetrightpwm);
// delay(500-runtime);
// servo1.writeMicroseconds(1500);
// servo4.writeMicroseconds(1500);
// //-30C 2/3
// servo2.writeMicroseconds(1500 + speed + engine2offsetleftpwm);
// servo3.writeMicroseconds(1500 - speed - engine3offsetleftpwm);
// delay(500-runtime);
// servo2.writeMicroseconds(1500);
// servo3.writeMicroseconds(1500);
// // 0C 1/4
// servo1.writeMicroseconds(1500 + speed + engine1offsetleftpwm);
// servo4.writeMicroseconds(1500 - speed - engine4offsetleftpwm);
// delay(500-runtime);
// servo1.writeMicroseconds(1500);
// servo4.writeMicroseconds(1500);
// // 0C 2/3
// servo2.writeMicroseconds(1500 - speed - engine2offsetrightpwm);
// servo3.writeMicroseconds(1500 + speed + engine3offsetrightpwm);
// delay(500-runtime);
// servo2.writeMicroseconds(1500);
// servo3.writeMicroseconds(1500);
}
void left()
{
int num = 0;
while (num < 4)
{
servo1.write(100); // 旋转到 90 度
servo4.write(100); // 旋转到 90 度
delay(100);
servo3.write(60); // 旋转到 90 度
servo2.write(60); // 旋转到 90 度
delay(100);
servo1.write(140); // 旋转到 90 度
servo4.write(140); // 旋转到 90 度
delay(100);
servo3.write(40); // 旋转到 90 度
servo2.write(40); // 旋转到 90 度
delay(100);
servo3.write(90); // 旋转到 90 度
servo2.write(90); // 旋转到 90 度
servo1.write(90); // 旋转到 90 度
servo4.write(90); // 旋转到 90 度
delay(100);
servo1.write(80); // 旋转到 90 度
servo4.write(80); // 旋转到 90 度
delay(100);
servo3.write(120); // 旋转到 90 度
servo2.write(120); // 旋转到 90 度
delay(100);
servo1.write(90); // 旋转到 90 度
servo4.write(90); // 旋转到 90 度
delay(100);
servo3.write(140); // 旋转到 90 度
servo2.write(140); // 旋转到 90 度
delay(100);
servo3.write(90); // 旋转到 90 度
servo2.write(90); // 旋转到 90 度
num++;
}
// +10 1/4
// servo1.writeMicroseconds(1500 + speed + engine1offsetleftpwm);
// servo4.writeMicroseconds(1500 + speed + engine4offsetrightpwm);
// delay(200);
// +30
// servo3.writeMicroseconds(1500 - speed - engine3offsetleftpwm);
// servo2.writeMicroseconds(1500 - speed - engine2offsetrightpwm);
// delay(300);
// +0 1/4
// servo1.writeMicroseconds(1500);
// servo4.writeMicroseconds(1500);
// +20 2/3
// delay(200);
// servo2.writeMicroseconds(1500);
// servo3.writeMicroseconds(1500);
// delay(100);
// //-30
// servo1.writeMicroseconds(1500 - speed - engine1offsetrightpwm);
// servo4.writeMicroseconds(1500 - speed - engine4offsetleftpwm);
// delay(200);
// servo3.writeMicroseconds(1500 + speed + engine3offsetrightpwm);
// servo2.writeMicroseconds(1500 + speed + engine2offsetleftpwm);
// delay(300);
// servo1.writeMicroseconds(1500);
// servo4.writeMicroseconds(1500);
// delay(200);
// servo2.writeMicroseconds(1500);
// servo3.writeMicroseconds(1500);
}
void right()
{
int num = 0;
while (num < 3)
{
servo1.write(80); // 旋转到 90 度
servo4.write(80); // 旋转到 90 度
delay(100);
servo3.write(120); // 旋转到 90 度
servo2.write(120); // 旋转到 90 度
delay(100);
servo1.write(40); // 旋转到 90 度
servo4.write(40); // 旋转到 90 度
delay(100);
servo3.write(140); // 旋转到 90 度
servo2.write(140); // 旋转到 90 度
delay(100);
servo3.write(90); // 旋转到 90 度
servo2.write(90); // 旋转到 90 度
servo1.write(90); // 旋转到 90 度
servo4.write(90); // 旋转到 90 度
delay(100);
servo1.write(100); // 旋转到 90 度
servo4.write(100); // 旋转到 90 度
delay(100);
servo3.write(60); // 旋转到 90 度
servo2.write(60); // 旋转到 90 度
delay(100);
servo1.write(90); // 旋转到 90 度
servo4.write(90); // 旋转到 90 度
delay(100);
servo3.write(40); // 旋转到 90 度
servo2.write(40); // 旋转到 90 度
delay(100);
servo3.write(90); // 旋转到 90 度
servo2.write(90); // 旋转到 90 度
// // +30
// servo1.writeMicroseconds(1500 - speed - engine1offsetrightpwm);
// servo4.writeMicroseconds(1500 - speed - engine4offsetleftpwm);
// delay(200);
// servo3.writeMicroseconds(1500 + speed + engine3offsetrightpwm);
// servo2.writeMicroseconds(1500 + speed + engine2offsetleftpwm);
// delay(300);
// servo1.writeMicroseconds(1500);
// servo4.writeMicroseconds(1500);
// delay(200);
// servo2.writeMicroseconds(1500);
// servo3.writeMicroseconds(1500);
// delay(100);
// //-30
// servo1.writeMicroseconds(1500 + speed + engine1offsetleftpwm);
// servo4.writeMicroseconds(1500 + speed + engine4offsetrightpwm);
// delay(200);
// servo3.writeMicroseconds(1500 - speed - engine3offsetleftpwm);
// servo2.writeMicroseconds(1500 - speed - engine2offsetrightpwm);
// delay(300);
// servo1.writeMicroseconds(1500);
// servo4.writeMicroseconds(1500);
// delay(200);
// servo2.writeMicroseconds(1500);
// servo3.writeMicroseconds(1500);
num++;
}
}
void sitdown()
{
servo2.write(140); // 旋转到 90 度
servo4.write(40); // 旋转到 90 度
delay(3000);
servo2.write(90); // 旋转到 90 度
servo4.write(90); // 旋转到 90 度
// servo2.writeMicroseconds(1500 + speed + engine3offsetleftpwm);
// servo4.writeMicroseconds(1500 - speed - engine3offsetleftpwm);
// delay(1000);
// servo2.writeMicroseconds(1500);
// servo4.writeMicroseconds(1500);
// delay(3000);
// servo2.writeMicroseconds(1500 - speed - engine3offsetrightpwm);
// servo4.writeMicroseconds(1500 + speed + engine3offsetrightpwm);
// delay(1000);
// servo2.writeMicroseconds(1500);
// servo4.writeMicroseconds(1500);
}
void lie()
{
servo1.write(180); // 旋转到 90 度
servo3.write(0); // 旋转到 90 度
servo2.write(0); // 旋转到 90 度
servo4.write(180); // 旋转到 90 度
delay(3000);
servo1.write(90); // 旋转到 90 度
servo3.write(90); // 旋转到 90 度
servo2.write(90); // 旋转到 90 度
servo4.write(90); // 旋转到 90 度
// servo1.writeMicroseconds(1500 + speed + engine1offsetleftpwm);
// servo3.writeMicroseconds(1500 - speed - engine3offsetleftpwm);
// servo2.writeMicroseconds(1500 - speed - engine2offsetrightpwm);
// servo4.writeMicroseconds(1500 + speed + engine4offsetrightpwm);
// delay(1000);
// servo1.writeMicroseconds(1500);
// servo2.writeMicroseconds(1500);
// servo3.writeMicroseconds(1500);
// servo4.writeMicroseconds(1500);
// delay(3000);
// servo1.writeMicroseconds(1500 - speed - engine1offsetrightpwm);
// servo3.writeMicroseconds(1500 + speed + engine3offsetrightpwm);
// servo2.writeMicroseconds(1500 + speed + engine2offsetleftpwm);
// servo4.writeMicroseconds(1500 - speed - engine4offsetleftpwm);
// delay(1000);
// servo1.writeMicroseconds(1500);
// servo2.writeMicroseconds(1500);
// servo3.writeMicroseconds(1500);
// servo4.writeMicroseconds(1500);
}
void toplefthand()
{
int num = 0;
while (num < 3)
{
servo3.write(0); // 旋转到 90 度
delay(100);
servo3.write(30);
delay(100);
// 旋转到 90 度
// servo3.writeMicroseconds(1500 - speed - engine3offsetleftpwm);
// delay(1000);
// servo3.writeMicroseconds(1500);
// delay(500);
// servo3.writeMicroseconds(1500 + speed + engine3offsetrightpwm);
// delay(1000);
// servo3.writeMicroseconds(1500);
num++;
}
servo3.write(90);
}
void toprighthand()
{
int num = 0;
while (num < 3)
{
servo1.write(180); // 旋转到 90 度
delay(100);
servo1.write(150);
delay(100);
// servo1.writeMicroseconds(1500 + speed + engine1offsetleftpwm);
// delay(1000);
// servo1.writeMicroseconds(1500);
// delay(500);
// servo1.writeMicroseconds(1500 - speed - engine1offsetrightpwm);
// delay(1000);
// servo1.writeMicroseconds(1500);
num++;
}
servo1.write(90);
}
void dosleep()
{
servo1.write(0); // 旋转到 90 度
servo3.write(180); // 旋转到 90 度
servo2.write(180); // 旋转到 90 度
servo4.write(0);
}
// 对 ADC 数据多次采样并计算平均值
float getAverageAdcVoltage()
{
long totalAdcValue = 0;
// 多次采样
for (int i = 0; i < numSamples; i++)
{
totalAdcValue += analogRead(A0); // 读取 ADC 数据
delay(10); // 每次采样间隔 10ms
}
// 计算平均 ADC 值
float averageAdcValue = totalAdcValue / (float)numSamples;
// 将 ADC 值转换为电压
return (averageAdcValue / 1023.0) * 1.0; // ESP8266 的参考电压为 1.0V
}
// 计算电池电量百分比的函数
int mapBatteryPercentage(float voltage)
{
if (voltage <= minVoltage)
return 0; // 小于等于最小电压时,电量为 0%
if (voltage >= maxVoltage)
return 100; // 大于等于最大电压时,电量为 100%
// 根据线性比例计算电量百分比
return (int)((voltage - minVoltage) / (maxVoltage - minVoltage) * 100);
}
void setup()
{
servo1.attach(engine1, 500, 2500); // 引脚 D1,500µs=0度,2500µs=180度
servo2.attach(engine2, 500, 2500); // 引脚 D1,500µs=0度,2500µs=180度
servo3.attach(engine3, 500, 2500); // 引脚 D1,500µs=0度,2500µs=180度
servo4.attach(engine4, 500, 2500); // 引脚 D1,500µs=0度,2500µs=180度
servo1.write(90); // 旋转到 90 度
servo3.write(90); // 旋转到 90 度
servo2.write(90); // 旋转到 90 度
servo4.write(90); // 旋转到 90 度
// servo1.write(0); // 旋转到 90 度
// servo3.write(180); // 旋转到 90 度
// servo2.write(180); // 旋转到 90 度
// servo4.write(0); // 旋转到 90 度
// servo1.attach(engine1);
// servo2.attach(engine2);
// servo3.attach(engine3);
// servo4.attach(engine4);
SPIFFS.begin();
u8g2.begin();
pinMode(BUTTON_PIN, INPUT_PULLUP); // GPIO2 设置为输入并启用内部上拉电阻
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), handleButtonPress, FALLING);
pinMode(BUTTON_PIN2, INPUT); // GPIO15 设置为输入(无需内部上拉)
// 监听上升沿触发中断
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN2), handleButtonPress, RISING); // 设置下降沿中断
u8g2.setDisplayRotation(U8G2_R2);
u8g2.firstPage();
do
{
u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.drawXBMP(0, (64 / 2 - 22 / 2), 128, 22, logo);
} while (u8g2.nextPage());
// 初始化串口
Serial.begin(115200);
// 设置WiFi为热点模式
WiFi.softAP(ssid, password);
Serial.println("热点已启动");
// 访问的IP地址是 ESP8266 的默认IP:192.168.4.1
Serial.print("访问地址: ");
Serial.print(WiFi.softAPIP());
// 加载WiFi配置
loadWiFiConfig();
if (WiFi.status() != WL_CONNECTED)
{
Serial.println("Starting captive portal...");
handleWiFiConfig();
}
else
{
handleWiFiConfig();
Serial.println("WiFi connected");
timeClient.begin();
timeClient.update(); // 获取初始时间
}
delay(5000);
u8g2.clearDisplay();
do
{
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawXBMP(0, 0, 64, 64, wifi);
u8g2.drawStr(64, 10, "WIFI_AP");
u8g2.drawStr(64, 27, "EDA-Robot");
u8g2.drawStr(64, 43, "192.168.4.1");
u8g2.drawStr(64, 60, "WIFI CTRL");
} while (u8g2.nextPage());
delay(5000);
u8g2.clearDisplay();
}
void loop()
{
// 对 ADC 数据多次采样并求平均
float adcVoltage = getAverageAdcVoltage();
// 将采样的 ADC 电压转换为实际电池电压
batteryVoltage = adcVoltage * voltageDividerRatio; // 计算电池电压
// 根据电池电压计算电量百分比
batteryPercentage = mapBatteryPercentage(batteryVoltage);
if (buttonPressed)
{
buttonPressed = false; // 清除按键标志
front();
}
if (buttonPressed2)
{
buttonPressed2 = false; // 清除按键标志
back();
}
if (emojiState != prevEmojiState)
{
u8g2.clearDisplay(); // 状态变化时清屏
prevEmojiState = emojiState; // 更新状态
}
if (freestate)
{
delay(3000);
actionstate = random(0, 10);
}
// 可以使用switch优化效率
switch (actionstate)
{
case 0 /* constant-expression */:
/* code */
break;
case 1:
front(); // 执行一次舵机动作
actionstate = 0;
break;
case 2:
left(); // 执行一次舵机动作
actionstate = 0;
break;
case 3:
right(); // 执行一次舵机动作
actionstate = 0;
break;
case 4:
back(); // 执行一次舵机动作
actionstate = 0;
break;
case 5:
toplefthand(); // 执行一次舵机动作
actionstate = 0;
break;
case 6:
toprighthand(); // 执行一次舵机动作
actionstate = 0;
break;
case 10:
dosleep(); // 执行一次舵机动作
actionstate = 0;
break;
case 7:
lie(); // 执行一次舵机动作
actionstate = 0;
break;
case 8:
sitdown(); // 执行一次舵机动作
actionstate = 0;
break;
case 9:
emojiState = random(0, 7); // 执行一次舵机动作
actionstate = 0;
break;
default:
break;
}
switch (emojiState)
{
case 0: // 首页
u8g2.setFont(u8g2_font_ncenB14_tr);
do
{
u8g2.drawXBMP(0, 0, 128, 64, hi);
} while (u8g2.nextPage());
break;
case 1: // 第二页
u8g2.setFont(u8g2_font_ncenB14_tr);
do
{
u8g2.drawXBMP(0, 0, 128, 64, angry);
} while (u8g2.nextPage());
break;
case 2: // 第三页
do
{
u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.drawXBMP(0, 0, 128, 64, error);
} while (u8g2.nextPage());
break;
case 3: // 第四页
do
{
u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.drawXBMP(0, 0, 128, 64, dowhat);
} while (u8g2.nextPage());
break;
case 4: // 第四页
do
{
u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.drawXBMP(0, 0, 128, 64, love);
} while (u8g2.nextPage());
break;
case 5: // 第四页
do
{
u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.drawXBMP(0, 0, 128, 64, sick);
} while (u8g2.nextPage());
break;
case 6: // 第四页
do
{
u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.drawXBMP(0, 0, 128, 64, yun);
} while (u8g2.nextPage());
break;
case 7: // 第四页
if (WiFi.status() != WL_CONNECTED)
{
do
{
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawXBMP(0, 0, 64, 64, wifi);
u8g2.drawStr(64, 20, "IP:");
u8g2.drawStr(64, 40, "192.168.4.1");
u8g2.drawStr(64, 60, "Need NET");
} while (u8g2.nextPage());
}
else
{
fetchWeather();
}
break;
break;
case 8: // 第四页
if (WiFi.status() != WL_CONNECTED)
{
do
{
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawXBMP(0, 0, 64, 64, wifi);
u8g2.drawStr(64, 20, "IP:");
u8g2.drawStr(64, 40, "192.168.4.1");
u8g2.drawStr(64, 60, "Need NET");
} while (u8g2.nextPage());
}
else
{
do
{
timeClient.update(); // 更新时间
u8g2.setFont(u8g2_font_ncenB14_tr);
timeClient.update();
u8g2.drawXBMP(0, 0, 64, 64, timeimage);
// 获取当前时间
// 显示时间到 OLED
int currentHour = timeClient.getHours();
int currentMinute = timeClient.getMinutes();
String timeToDisplay = String(currentHour) + ":" + String(currentMinute);
u8g2.drawStr(64, 30, "TIME");
u8g2.setCursor(64, 50);
u8g2.print(timeToDisplay);
} while (u8g2.nextPage());
}
break;
case 9: // 第四页
do
{
u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.drawXBMP(0, (64 / 2 - 22 / 2), 128, 22, logo);
} while (u8g2.nextPage());
break;
default:
// 添加默认 case 来处理其他情况
break;
}
delay(100);
}
注意事项
OLED显示屏为0.96寸SSD1306或SSD1315驱动
电池为2节14500锂电池,单节电压3.7-4.2v
电池盒为2节串联14500电池盒
预留开关和外部充电接口,外部充电模块应当为2串锂电池充电模块,满电电压8.1-8.4V。
切勿强行掰动舵机,避免电机损坏,也可购买金属齿轮的MG90舵机
设计图

BOM


评论