//----------------------------------------------
//EDA-Robot(180度舵机版本)
//CodeVersion:V1.1
//---------------导入库--------------------------
#include <Arduino.h>
#include <Arduino.h>
#include <Servo.h>
#include <U8g2lib.h>
#include <Wire.h>
#include <FS.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncWebServer.h>
#include <ESP8266HTTPClient.h>
#include <NTPClient.h>
#include <ArduinoJson.h>
#include <WiFiUdp.h>
#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;//声明舵机1
Servo servo2;//声明舵机2
Servo servo3;//声明舵机3
Servo servo4;//声明舵机4
int engine1 = 14; // 舵机1引脚
int engine2 = 16; // 舵机2引脚
int engine3 = 12; // 舵机3引脚
int engine4 = 13; // 舵机4引脚
//---------------屏幕部分--------------------------
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE, /* clock=*/5, /* data=*/4); // 使用SSD1306屏幕驱动,时钟引脚5,数据引脚4
//---------------网络部分--------------------------
const char *ssid = "EDA-Robot";//WIFI名称
const char *password = ""; // 无密码
AsyncWebServer server(80);//设置服务器端口
WiFiUDP ntpUDP;//声明UDP
NTPClient timeClient(ntpUDP, "ntp1.aliyun.com", 8 * 3600, 60000);//配置NTP服务器
//---------------API部分--------------------------
const char *weatherAPI = "http://api.seniverse.com/v3/weather/daily.json?key=";//心知天气API地址
String temperature = "";//天气温度
String humidity = "";//天气湿度
String weather = "";//天气
String cityname = "";//城市名称
String weatherapi = "";//心知天气API密钥
//---------------标签部分--------------------------
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) // 检查按钮1是否满足去抖条件(避免机械抖动导致的误触发)
{
buttonPressed = true; // 设置按钮1按下标志位
lastPressTime = currentTime; // 更新按钮1的最后有效按下时间
}
unsigned long currentTime2 = millis();// 获取当前时间
if (currentTime2 - lastPressTime2 > debounceDelay2)// 检查按钮2的去抖条件(使用独立的去抖时间和记录变量)
{
buttonPressed2 = true; // 设置按钮2按下标志位
lastPressTime2 = currentTime2; // 更新按钮2的最后有效按下时间
}
}
//---------------配置页面路由--------------------------
void handleWiFiConfig()
{
server.on("/front", HTTP_GET, [](AsyncWebServerRequest *request) {// 当访问 /front 路径时触发舵机动作
actionstate = 1; // 更新全局动作状态标志(1通常表示前进/前方动作)
request->send(200, "text/plain", "Front function started"); // 立即响应客户端,避免阻塞(状态码200,返回纯文本确认信息)
});
//以下函数相同,不再注释
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("/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("/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、city、api
String ssid = request->getParam("ssid", true)->value();
String pass = request->getParam("pass", 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["city"] = city;
doc["api"] = api;
fs::File file = SPIFFS.open(ssidFile, "w"); // 打开文件进行写入
if (file) {
serializeJson(doc, file); // 将JSON内容写入文件
file.close(); // 关闭文件
}
// 更新全局变量
cityname = city;
weatherapi = api;
// 开始连接WiFi
WiFi.begin(ssid.c_str(), pass.c_str());
// 发送HTML响应,告知用户正在连接
// 发送带UTF-8编码声明的HTML响应
request->send(200, "text/html; charset=UTF-8",
"<!DOCTYPE html>"
"<html>"
"<head>"
" <meta charset='UTF-8'>"
" <title>状态</title>"
"</head>"
"<body>"
" <h1>请返回使用在线功能,如果能正常获取则配置成功!</h1>"
"</body>"
"</html>"
);}
);
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()
{
// 初始化SPIFFS文件系统(存储WiFi配置等信息)
if (SPIFFS.begin()) // 成功挂载文件系统
{
// 尝试打开存储WiFi配置的JSON文件(需提前创建)
fs::File file = SPIFFS.open(ssidFile, "r"); // "r"表示只读模式
if (file) // 文件存在且可访问
{
// 创建动态JSON文档(容量需根据实际配置数据调整)
DynamicJsonDocument doc(1024); // 建议至少1024字节存储配置参数
// 反序列化JSON数据(将文件内容解析为JSON对象)
DeserializationError error = deserializeJson(doc, file);
if (!error) // JSON解析成功
{
// 从JSON对象中提取配置参数
String ssid = doc["ssid"]; // WiFi名称字段
String pass = doc["pass"]; // WiFi密码字段
String city = doc["city"]; // 城市代码字段
String api = doc["api"]; // 天气API密钥字段
// 将配置参数赋给全局变量
cityname = city; // 存储城市代码
weatherapi = api; // 存储API密钥
// 使用存储的凭证尝试连接WiFi
WiFi.begin(ssid.c_str(), pass.c_str()); // 转换为C风格字符串
// 设置5秒连接超时(5000ms)
unsigned long startAttemptTime = millis();
while (WiFi.status() != WL_CONNECTED &&
millis() - startAttemptTime < 5000)
{
delay(500); // 等待连接,每0.5秒检测一次
}
// 连接状态检测
if (WiFi.status() != WL_CONNECTED)
{
Serial.println("WiFi connection failed, starting captive portal...");
handleWiFiConfig(); // 启动强制配置门户(如AP模式)
}
else
{
Serial.println("WiFi connected");
timeClient.begin(); // 初始化NTP时间客户端
}
}
file.close(); // 关闭文件释放资源
}
}
}
void fetchWeather()
{ // 天气捕捉
// 天气数据初始化模块(首次运行或需要更新时触发)
if (initweather == false)
{
// 检测WiFi连接状态(确保网络可用性)
if (WiFi.status() == WL_CONNECTED)
{
WiFiClient client; // 创建TCP客户端
HTTPClient http; // 初始化HTTP客户端
// 构建带参数的API请求URL(包含动态参数)
String apiUrl = weatherAPI + weatherapi + "&location=" + cityname + "&language=zh-Hans&unit=c&start=0&days=1";
// 发起HTTPS连接(注意:实际需确认weatherAPI是否支持SSL)
if (http.begin(client, apiUrl))
{
int httpCode = http.GET(); // 发送GET请求
// 成功接收响应(httpCode 200表示成功)
if (httpCode > 0)
{
String payload = http.getString(); // 获取完整响应数据
// 调试输出原始JSON数据(建议在开发阶段开启)
Serial.println("JSON Response:");
Serial.println(payload);
// 创建JSON文档并解析数据
DynamicJsonDocument doc(2048); // 建议扩大至2048字节防止数据截断
DeserializationError error = deserializeJson(doc, payload);
if (!error)
{
// 提取天气数据(注意字段路径需与API响应结构匹配)
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.println("JSON解析失败: " + String(error.c_str()));
}
}
else
{
Serial.printf("HTTP请求失败,错误代码: %d,详情: %s\n",
httpCode, http.errorToString(httpCode).c_str());
}
http.end(); // 必须释放资源
}
else
{
Serial.println("服务器连接失败,请检查API地址");
}
}
}
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) + " 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());
}
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());
}
else
{
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());
}
}
void front()
{
servo2.write(140); //舵机2旋转至140度
servo3.write(40); //舵机旋转至40度
delay(100);//延时100s
servo1.write(40); //内容同上
servo4.write(140);
delay(100);
servo2.write(90);
servo3.write(90);
delay(100);
servo1.write(90);
servo4.write(90);
delay(100);
servo1.write(140);
servo4.write(40);
delay(100);
servo2.write(40);
servo3.write(140);
delay(100);
servo1.write(90);
servo4.write(90);
delay(100);
servo2.write(90);
servo3.write(90);
}
void back()
{
servo3.write(140);
servo2.write(40);
delay(100);
servo4.write(40);
servo1.write(140);
delay(100);
servo3.write(90);
servo2.write(90);
delay(100);
servo4.write(90);
servo1.write(90);
delay(100);
servo4.write(140);
servo1.write(40);
delay(100);
servo3.write(40);
servo2.write(140);
delay(100);
servo4.write(90);
servo1.write(90);
delay(100);
servo3.write(90);
servo2.write(90);
}
void right()
{
int num = 0;
while (num < 3)//调用一次执行3次
{
servo1.write(100);
servo4.write(100);
delay(100);
servo3.write(60);
servo2.write(60);
delay(100);
servo1.write(140);
servo4.write(140);
delay(100);
servo3.write(40);
servo2.write(40);
delay(100);
servo3.write(90);
servo2.write(90);
servo1.write(90);
servo4.write(90);
delay(100);
servo1.write(80);
servo4.write(80);
delay(100);
servo3.write(120);
servo2.write(120);
delay(100);
servo1.write(90);
servo4.write(90);
delay(100);
servo3.write(140);
servo2.write(140);
delay(100);
servo3.write(90);
servo2.write(90);
num++;
}
}
void left()
{
int num = 0;
while (num < 3)
{
servo1.write(80);
servo4.write(80);
delay(100);
servo3.write(120);
servo2.write(120);
delay(100);
servo1.write(40);
servo4.write(40);
delay(100);
servo3.write(140);
servo2.write(140);
delay(100);
servo3.write(90);
servo2.write(90);
servo1.write(90);
servo4.write(90);
delay(100);
servo1.write(100);
servo4.write(100);
delay(100);
servo3.write(60);
servo2.write(60);
delay(100);
servo1.write(90);
servo4.write(90);
delay(100);
servo3.write(40);
servo2.write(40);
delay(100);
servo3.write(90);
servo2.write(90);
num++;
}
}
void sitdown()
{
servo2.write(140);
servo4.write(40);
delay(3000);
servo2.write(90);
servo4.write(90);
}
void lie()
{
servo1.write(180);
servo3.write(0);
servo2.write(0);
servo4.write(180);
delay(3000);
servo1.write(90);
servo3.write(90);
servo2.write(90);
servo4.write(90);
}
void toplefthand()
{
int num = 0;
while (num < 3)
{
servo3.write(0);
delay(100);
servo3.write(30);
delay(100);
num++;
}
servo3.write(90);
}
void toprighthand()
{
int num = 0;
while (num < 3)
{
servo1.write(180);
delay(100);
servo1.write(150);
delay(100);
num++;
}
servo1.write(90);
}
void dosleep()
{
servo1.write(0);
servo3.write(180);
servo2.write(180);
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 serialListen(){
// 读取完整字符串(直到换行符)
String receivedString = Serial.readStringUntil('\n');
// 去掉可能的回车符或空格
receivedString.trim();
// 处理接收到的字符串
Serial.print("Received: ");
Serial.println(receivedString);
if(receivedString=="front"){//当接收到“front”时
front();//执行前进
};
if(receivedString=="back"){
back();
};
if(receivedString=="toplefthand"){
toplefthand();
};
if(receivedString=="toprighthand"){
toprighthand();
};
if(receivedString=="left"){
left();
};
if(receivedString=="right"){
right();
};
if(receivedString=="sitdown"){
sitdown();
};
if(receivedString=="lie"){
lie();
};
if(receivedString=="dosleep"){
dosleep();
};
if(receivedString=="kaixin"){
emojiState=0;
};
if(receivedString=="shengqi"){
emojiState=1;
};
if(receivedString=="nanshou"){
emojiState=5;
};
if(receivedString=="haoqi"){
emojiState=3;
};
if(receivedString=="xihuan"){
emojiState=4;
};
if(receivedString=="cuowu"){
emojiState=2;
};
if(receivedString=="yun"){
emojiState=6;
};
if(receivedString=="shijian"){
emojiState=8;
};
if(receivedString=="tianqi"){
emojiState=7;
};
if(receivedString=="logo"){
emojiState=9;
};
}
/// @brief
void setup()
{
u8g2.begin();
u8g2.setDisplayRotation(U8G2_R2);
// OLED 显示初始化与按钮中断配置
u8g2.firstPage(); // 启动U8g2页面缓冲绘制
do {
// 设置显示字体(14像素高度,适合128x64屏幕)
u8g2.setFont(u8g2_font_ncenB14_tr);
// 绘制LOGO位图(居中显示计算)
// 参数说明:X坐标0(左对齐),Y坐标(64/2 -22/2)=21(垂直居中)
// 位图尺寸:宽度128px,高度22px,数据源为logo数组
u8g2.drawXBMP(0, (64 / 2 - 22 / 2), 128, 22, logo);
} while (u8g2.nextPage()); // 循环刷新直至完成全帧绘制
// 按钮1配置(通常对应GPIO2)
// 硬件要求:按钮接地触发,内置上拉保持高电平
pinMode(BUTTON_PIN, INPUT_PULLUP);
// 配置下降沿中断(物理按下时产生低电平)
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), handleButtonPress, FALLING);
// 按钮2配置(通常对应GPIO15)
// 硬件要求:需外接上拉电阻,按钮接3.3V触发
pinMode(BUTTON_PIN2, INPUT); // 无内部上拉模式
// 配置上升沿中断(物理按下时产生高电平)
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN2), handleButtonPress, RISING);
//启用SPIFFS文件系统
SPIFFS.begin();
servo1.attach(engine1, 500, 2500); // 配置舵机PWM,500µs=0度,2500µs=180度
servo2.attach(engine2, 500, 2500);
servo3.attach(engine3, 500, 2500);
servo4.attach(engine4, 500, 2500);
servo1.write(90);//舵机旋转到90度
servo3.write(90);
servo2.write(90);
servo4.write(90);
// 初始化串口
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)//当WIFI未连接时
{
Serial.println("Starting captive portal...");//串口输出
handleWiFiConfig();//加载WIFI配置
}
else
{
handleWiFiConfig();//加载WIFI配置
Serial.println("WiFi connected");
timeClient.begin();//NTP服务初始化
timeClient.update(); // 获取初始时间
}
delay(5000);
u8g2.clearDisplay();//清屏
do
{
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawXBMP(0, 0, 64, 64, ipip);
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()
{
if (Serial.available() > 0) {
serialListen();
}
// 对 ADC 数据多次采样并求平均
float adcVoltage = getAverageAdcVoltage();
// 将采样的 ADC 电压转换为实际电池电压
batteryVoltage = adcVoltage * voltageDividerRatio; // 计算电池电压
// 根据电池电压计算电量百分比
batteryPercentage = mapBatteryPercentage(batteryVoltage);
if (buttonPressed)//按键1按下时
{
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;
}
}
这是我的整段代码。请你使IO2和en在Visual Studio Code变成高电平,放入这段代码中并且不报错