8266控制l298n电机模块

This commit is contained in:
2024-10-17 21:46:53 +08:00
commit 9026528747

755
wifi-config_a.ino Normal file
View File

@@ -0,0 +1,755 @@
#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);
}