Files
esp8266-l298n/wifi-config_a.ino
2024-10-17 21:46:53 +08:00

756 lines
19 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <EEPROM.h>
#include <Arduino.h>
#include <WebSocketsServer_Generic.h>
#include <ArduinoJson.h>
int ENAPin = 14; // D5 ENA
int ENBPin = 12; // D6 ENB
int IN1Pin = 4; // D2 IN1
int IN2Pin = 0; // D3 IN2
int IN3Pin = 13; // D7 IN3
int IN4Pin = 15; // D8 IN4
// 停止所有电机
void stopMotors() {
digitalWrite(IN1Pin, LOW);
digitalWrite(IN2Pin, LOW);
stopMotorB();
}
unsigned long startTime = 0; // 记录电机启动时间
unsigned long runDuration = 0; // 电机运行的总时长
bool motorRunning = false; // 电机是否在运行
// 启动副电机
void startMotorB() {
// 启动电机 正转
digitalWrite(IN3Pin, LOW);
digitalWrite(IN4Pin, HIGH);
motorRunning = true;
startTime = millis(); // 记录启动时间
}
// 停止副电机
void stopMotorB() {
// 停止电机
digitalWrite(IN3Pin, LOW);
digitalWrite(IN4Pin, LOW);
motorRunning = false;
runDuration = 0;
startTime = 0;
}
//AP名称
const char* AP_NAME = "tiktok-wifi";
String nets;
// 前端HTML页面内容定义为字符串
const char* webpage PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<style>
body {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
}
.button {
background-color: #4CAF50;
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 10px;
cursor: pointer;
border-radius: 5px;
width: 80%;
/* 按钮宽度适应手机屏幕 */
}
@media (min-width: 600px) {
.button {
width: 200px;
/* 在大屏幕上设置固定宽度 */
}
}
</style>
<script>
function startCommand(action) {
fetch('/start', {
method: 'POST',
body: JSON.stringify({ action: action }),
headers: {
'Content-Type': 'application/json'
}
});
}
document.addEventListener('DOMContentLoaded', function () {
const buttons = document.querySelectorAll('button');
buttons.forEach(button => {
button.addEventListener('click', () => {
startCommand(button.dataset.action);
});
});
});
</script>
</head>
<body>
<button class="button" data-action="0">全部电机停止</button>
<button class="button" data-action="1">主电机关闭</button>
<button class="button" data-action="2">主电机开启</button>
<button class="button" data-action="3">副电机关闭</button>
<button class="button" data-action="4">副电机开启</button>
<button class="button" data-action="5">副电机+5秒</button>
<button class="button" data-action="6">副电机+10秒</button>
<button class="button" data-action="7">副电机+20秒</button>
</body>
</html>)rawliteral";
const char* config_wifi_success_tip_html = "\
<!DOCTYPE html>\r\n\
<html lang='en'>\r\n\
<head>\r\n\
<meta charset='UTF-8'>\r\n\
<meta name='viewport' content='width=device-width, initial-scale=1.0'>\r\n\
<title>网络配置</title>\r\n\
<style>\r\n\
body {\r\n\
font-family: Arial, sans-serif;\r\n\
background-color: #f4f4f4;\r\n\
margin: 0;\r\n\
padding: 20px;\r\n\
}\r\n\
\r\n\
h1,p {\r\n\
text-align: center;\r\n\
color: #333;\r\n\
}\r\n\
</style>\r\n\
</head>\r\n\
<body>\r\n\
<div>\r\n\
<h1>网络配置保存成功</h1>\r\n\
<p>设备IP地址: %s</p>\r\n\
<p>WS地址: ws://%s:81</p>\r\n\
</div>\r\n\
</body>\r\n\
</html>\r\n\
";
//POST参数有误
const char* config_wifi_error_tip_html = "\
<!DOCTYPE html>\r\n\
<html lang='en'>\r\n\
<head>\r\n\
<meta charset='UTF-8'>\r\n\
<meta name='viewport' content='width=device-width, initial-scale=1.0'>\r\n\
<title>网络配置</title>\r\n\
<style>\r\n\
body {\r\n\
font-family: Arial, sans-serif;\r\n\
background-color: #f4f4f4;\r\n\
margin: 0;\r\n\
padding: 20px;\r\n\
}\r\n\
\r\n\
h1 {\r\n\
text-align: center;\r\n\
color: #333;\r\n\
}\r\n\
</style>\r\n\
</head>\r\n\
<body>\r\n\
<h1>POST参数有误</h1>\r\n\
</body>\r\n\
</html>\r\n\
";
//重置wifi配置
const char* reset_wifi_tip_html = "\
<!DOCTYPE html>\r\n\
<html lang='en'>\r\n\
<head>\r\n\
<meta charset='UTF-8'>\r\n\
<meta name='viewport' content='width=device-width, initial-scale=1.0'>\r\n\
<title>网络配置</title>\r\n\
<style>\r\n\
body {\r\n\
font-family: Arial, sans-serif;\r\n\
background-color: #f4f4f4;\r\n\
margin: 0;\r\n\
padding: 20px;\r\n\
}\r\n\
\r\n\
h1 {\r\n\
text-align: center;\r\n\
color: #333;\r\n\
}\r\n\
</style>\r\n\
</head>\r\n\
<body>\r\n\
<h1>清除WIFI配置信息成功</h1>\r\n\
</body>\r\n\
</html>\r\n\
";
//配网页面代码
const char* config_wifi_page_html = "\
<!DOCTYPE html>\r\n\
<html lang='en'>\r\n\
<head>\r\n\
<meta charset='UTF-8'>\r\n\
<meta name='viewport' content='width=device-width, initial-scale=1.0'>\r\n\
<title>网络配置</title>\r\n\
<style>\r\n\
body {\r\n\
font-family: Arial, sans-serif;\r\n\
background-color: #f4f4f4;\r\n\
margin: 0;\r\n\
padding: 20px;\r\n\
}\r\n\
\r\n\
h1 {\r\n\
text-align: center;\r\n\
color: #333;\r\n\
}\r\n\
\r\n\
form {\r\n\
background: white;\r\n\
padding: 20px;\r\n\
border-radius: 8px;\r\n\
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\r\n\
margin: 20px auto;\r\n\
max-width: 400px;\r\n\
}\r\n\
\r\n\
label {\r\n\
display: block;\r\n\
margin-bottom: 8px;\r\n\
font-weight: bold;\r\n\
color: #555;\r\n\
}\r\n\
\r\n\
input[type='text'] {\r\n\
width: 100%;\r\n\
padding: 10px;\r\n\
margin-bottom: 15px;\r\n\
border: 1px solid #ccc;\r\n\
border-radius: 4px;\r\n\
box-sizing: border-box;\r\n\
}\r\n\
\r\n\
input[type='submit'] {\r\n\
background-color: #28a745;\r\n\
color: white;\r\n\
padding: 10px;\r\n\
border: none;\r\n\
border-radius: 4px;\r\n\
cursor: pointer;\r\n\
width: 100%;\r\n\
font-size: 16px;\r\n\
}\r\n\
\r\n\
input[type='submit']:hover {\r\n\
background-color: #218838;\r\n\
}\r\n\
\r\n\
.reconfigure-button {\r\n\
background-color: #007bff;\r\n\
margin-top: 10px;\r\n\
}\r\n\
\r\n\
.reconfigure-button:hover {\r\n\
background-color: #0056b3;\r\n\
}\r\n\
</style>\r\n\
</head>\r\n\
<body>\r\n\
<h1>网络配置</h1>\r\n\
<form name='input' action='/' method='POST'>\r\n\
<label for='ssid'>Wi-Fi名称:</label>\r\n\
<input type='text' name='ssid' id='ssid' required>\r\n\
\r\n\
<label for='password'>Wi-Fi密码:</label>\r\n\
<input type='text' name='password' id='password' required>\r\n\
\r\n\
<input type='submit' value='保存'>\r\n\
</form>\r\n\
<form action='/reconfigure' method='GET'>\r\n\
<input type='submit' class='reconfigure-button' value='重新配置Wi-Fi'>\r\n\
</form>\r\n\
<form action='/reset' method='GET'>\r\n\
<input type='submit' class='reconfigure-button' value='清除已配置Wi-Fi'>\r\n\
</form>\r\n\
</body>\r\n\
</html>\r\n\
";
//DNS端口号
const byte DNS_PORT = 53;
//esp8266-AP-IP地址
IPAddress apIP(192, 168, 4, 1);
//创建dnsServer实例
DNSServer dnsServer;
//创建WebServer
ESP8266WebServer server(80);
//创建webSocketServer
WebSocketsServer webSocketServer = WebSocketsServer(81);
void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) {
IPAddress ip;
switch (type) {
case WStype_DISCONNECTED:
Serial.println("对端关闭");
ip = webSocketServer.remoteIP(num);
nets = String(ip[0]) + String(".") + String(ip[1]) + String(".") + String(ip[2]) + String(".") + String(ip[3]);
Serial.println(nets);
break;
case WStype_CONNECTED:
{
Serial.println("对端连接");
ip = webSocketServer.remoteIP(num);
nets = String(ip[0]) + String(".") + String(ip[1]) + String(".") + String(ip[2]) + String(".") + String(ip[3]);
Serial.println(nets);
}
break;
case WStype_TEXT:
Serial.println("收到消息");
showcpu(payload);
break;
}
}
void showcpu(uint8_t* payload) {
StaticJsonDocument<200> root;
deserializeJson(root, (const char*)payload);
String type = root["type"];
Serial.println("type-----" + String(type));
int typeAsInt = type.toInt();
switch (typeAsInt) {
case 1:
// 增加运行时间
// 增加运行时间
runDuration += 10000; // 增加10秒的运行时间
if (!motorRunning) {
startMotorB(); // 如果电机未运行,则启动电机
}
break;
default:
// 如果收到的action不是预期的停止所有电机
// stopMotors();
break;
}
}
//访问主页回调函数
void handleRoot() {
server.send(200, "text/html", config_wifi_page_html);
}
void handleReset() {
for (int i = 0; i < 96; ++i) {
EEPROM.write(2048 + i, 0);
}
EEPROM.commit();
server.send(200, "text/html", reset_wifi_tip_html);
delay(2000);
Serial.println("准备重启中...");
Serial.println();
//重启
ESP.reset();
}
void handleReconfigure() {
Serial.println("正在重新配置Wi-Fi...");
if (WiFi.status() == WL_CONNECTED) { //如果连接上 就输出IP信息 防止未连接上break后会误输出
// 断开当前的Wi-Fi连接
WiFi.disconnect();
// 启动AP模式
initSoftAP();
initDNS();
}
}
//Post回调函数
void handleRootPost() {
Serial.println("接收POST数据");
String qsid = server.arg("ssid");
String qpass = server.arg("password");
Serial.println("POST-WIFI名称:");
Serial.println(qsid);
Serial.println("POST-WIFI密码:");
Serial.println(qpass);
Serial.println("");
if (qsid.length() > 0 && qpass.length() > 0) {
Serial.println("清除之前的账号密码开始");
for (int i = 0; i < 96; ++i) {
EEPROM.write(2048 + i, 0);
}
Serial.println("清除之前的账号密码完毕");
Serial.println("写入新的WIFI名称:");
for (int i = 0; i < qsid.length(); ++i) {
EEPROM.write(2048 + i, qsid[i]);
}
Serial.println("写入新的WIFI密码:");
for (int i = 0; i < qpass.length(); ++i) {
EEPROM.write(2048 + 32 + i, qpass[i]);
}
EEPROM.commit();
// 连接新的Wi-Fi
connectNewWifi();
// 返回保存成功页面和IP地址
// String response = "<meta charset='UTF-8'>网络配置保存成功<br>";
// response += "设备IP地址: " + WiFi.localIP().toString();
char html_buffer[1024]; // 确保数组足够大以容纳字符串
snprintf(html_buffer, sizeof(html_buffer), config_wifi_success_tip_html,
WiFi.localIP().toString().c_str(), // 插入设备 IP 地址
WiFi.localIP().toString().c_str() // 插入设备 IP 地址
);
// 打印生成的 HTML
Serial.println(html_buffer);
server.send(200, "text/html", html_buffer);
} else {
Serial.println("POST参数有误");
server.send(200, "text/html", config_wifi_error_tip_html);
return;
}
//返回保存成功页面
// server.send(200, "text/html", "<meta charset='UTF-8'>保存成功");
//重启
delay(2000);
Serial.println("准备重启中...");
Serial.println();
ESP.reset();
}
//初始化基础
void initBasic(void) {
Serial.begin(115200);
//设置设备名
// WiFi.hostname("HOMEKIT");
// 断开之前链接的WIFI
WiFi.disconnect();
// 初始化 EEPROM
// EEPROM = EEPROMClass(0x3FB);
EEPROM.begin(4096);
}
//初始化AP模式
void initSoftAP(void) {
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
if (WiFi.softAP(AP_NAME)) {
Serial.println("AP模式设置成功");
}
}
//初始化WebServer
void initWebServer(void) {
//设置主页回调函数
server.on("/", HTTP_GET, handleRoot);
//设置Post请求回调函数
server.on("/", HTTP_POST, handleRootPost);
server.on("/reset", HTTP_GET, handleReset);
// 添加重新配置路由
server.on("/reconfigure", HTTP_GET, handleReconfigure);
server.on("/md", HTTP_GET, handleGet);
// server.on("/command", HTTP_POST, handlePost); // 短按命令
server.on("/start", HTTP_POST, handleStart); // 开始运动的请求
// server.on("/stop", HTTP_POST, handleStop); // 停止运动的请求
//设置无法响应的http请求的回调函数
server.onNotFound(handleRoot);
//启动WebServer
server.begin();
Serial.println();
Serial.println("HTTP服务启动成功!");
}
// 处理GET请求以返回HTML页面
void handleGet() {
if (server.method() == HTTP_GET) {
server.send(200, "text/html", webpage);
}
}
// 处理POST请求以控制电机
// void handlePost() {
// if (server.method() == HTTP_POST) {
// StaticJsonDocument<200> doc;
// deserializeJson(doc, server.arg("plain"));
// int action = doc["action"].as<int>();
// bool longPress = doc["longPress"].as<bool>(); // 长按标志
// setMotorState(action, longPress);
// if (!longPress) {
// delay(1000); // 短按持续1秒
// stopMotors(); // 停止电机
// }
// server.send(200, "text/plain", "Action received.");
// }
// }
// 处理开始运动的请求
void handleStart() {
if (server.method() == HTTP_POST) {
StaticJsonDocument<200> doc;
deserializeJson(doc, server.arg("plain"));
int action = doc["action"].as<int>();
setMotorState(action, true); // 开始运动
server.send(200, "text/plain", "Start action received.");
}
}
// 处理停止运动的请求
// void handleStop() {
// if (server.method() == HTTP_POST) {
// stopMotors(); // 停止所有电机
// server.send(200, "text/plain", "Stop action received.");
// }
// }
void setMotorState(int action, bool continuous) {
Serial.println("如下:");
Serial.println(action);
// 根据action设置电机状态
switch (action) {
case 0: // 全部电机停止
stopMotors();
break;
case 1: // 主电机关闭
digitalWrite(IN1Pin, LOW);
digitalWrite(IN2Pin, LOW);
break;
case 2: // 主电机开启
// 正转
digitalWrite(IN1Pin, LOW);
digitalWrite(IN2Pin, HIGH);
break;
case 3: // 副电机关闭
// 停止
stopMotorB();
break;
case 4: // 副电机开启
startMotorB();
break;
case 5: // 副电机反向
// 增加运行时间
runDuration += 5000; // 增加5秒的运行时间
if (!motorRunning) {
startMotorB(); // 如果电机未运行,则启动电机
}
break;
case 6:
// 增加运行时间
runDuration += 10000; // 增加5秒的运行时间
if (!motorRunning) {
startMotorB(); // 如果电机未运行,则启动电机
}
break;
case 7:
// 增加运行时间
runDuration += 20000; // 增加5秒的运行时间
if (!motorRunning) {
startMotorB(); // 如果电机未运行,则启动电机
}
break;
default:
// 如果收到的action不是预期的停止所有电机
// stopMotors();
break;
}
// 如果不是长按,不需要在这里停止电机,因为会有单独的停止请求
}
//初始化DNS服务器
void initDNS(void) {
//判断将所有地址映射到esp8266的ip上是否成功
if (dnsServer.start(DNS_PORT, "*", apIP)) {
Serial.println("DNS服务器启动成功.");
} else {
Serial.println("DNS服务器启动失败.");
}
}
void connectNewWifi(void) {
Serial.println("读取本地缓存的WIFI名称");
String esid;
for (int i = 0; i < 32; ++i) {
esid += char(EEPROM.read(2048 + i));
}
Serial.println(esid);
Serial.println("读取本地缓存的WIFI密码");
String epass = "";
for (int i = 32; i < 96; ++i) {
epass += char(EEPROM.read(2048 + i));
}
Serial.println(epass);
// WiFi.hostname("esp8266_test");
WiFi.begin(esid, epass); // 链接WIFI
Serial.println("");
Serial.print("正在连接WIFI中");
int count = 0;
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
count++;
if (count > 10) { //如果5秒内没有连上就开启Web配网 可适当调整这个时间
initSoftAP();
initDNS();
break; // 跳出 防止无限初始化
}
Serial.print(".");
}
Serial.println("");
if (WiFi.status() == WL_CONNECTED) { //如果连接上 就输出IP信息 防止未连接上break后会误输出
Serial.println("WIFI链接成功!");
Serial.println("IP地址如下:");
Serial.println(WiFi.localIP());
// server.stop();
}
}
void setPinsOutput(void) {
// 设置电机驱动引脚为输出模式
pinMode(ENAPin, OUTPUT);
pinMode(ENBPin, OUTPUT);
pinMode(IN1Pin, OUTPUT);
pinMode(IN2Pin, OUTPUT);
pinMode(IN3Pin, OUTPUT);
pinMode(IN4Pin, OUTPUT);
// 最大动力旋转
digitalWrite(ENAPin, HIGH);
digitalWrite(ENBPin, HIGH);
}
void setup() {
initBasic();
initWebServer();
connectNewWifi();
webSocketServer.begin();
webSocketServer.onEvent(webSocketEvent);
Serial.println("webSocketServer start");
// 设置电机驱动引脚为输出模式
setPinsOutput();
}
unsigned long previousMillis = 0; // 上次执行的时间
const long interval = 1000; // 每秒执行一次1000毫秒
void loop() {
server.handleClient();
dnsServer.processNextRequest();
webSocketServer.loop();
unsigned long currentMillis = millis(); // 获取当前时间
// 每秒执行一次电机状态检查
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis; // 更新上次执行的时间
// 如果电机正在运行,检查运行时间
if (motorRunning) {
Serial.println("电机运行中");
Serial.println(millis() - startTime);
Serial.println(runDuration);
if (millis() - startTime >= runDuration) { // 检查是否达到运行时间
stopMotorB(); // 停止电机
// motorRunning = false; // 更新电机状态
}
} else { // 如果电机未运行
Serial.println("电机未运行");
Serial.println(millis() - startTime);
Serial.println(runDuration);
if (millis() - startTime < runDuration) { // 检查是否达到运行时间
startMotorB(); // 停止电机
}
}
}
// // 如果电机正在运行,检查运行时间
// if (motorRunning) {
// Serial.println("电机运行中");
// Serial.println(millis() - startTime);
// Serial.println(runDuration);
// if (millis() - startTime >= runDuration) { // 检查是否达到运行时间
// stopMotorB(); // 停止电机
// }
// }
// if (!motorRunning) {
// Serial.println("电机未运行");
// Serial.println(millis() - startTime);
// Serial.println(runDuration);
// if (millis() - startTime < runDuration) { // 检查是否达到运行时间
// startMotorB(); // 停止电机
// }
// }
delay(5);
}