添加 main.ino
This commit is contained in:
commit
0756b0b4f3
681
main.ino
Normal file
681
main.ino
Normal file
@ -0,0 +1,681 @@
|
|||||||
|
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include <ESP8266WebServer.h>
|
||||||
|
#include <WiFiClient.h>
|
||||||
|
#include <EEPROM.h>
|
||||||
|
#include <DNSServer.h>
|
||||||
|
|
||||||
|
#define AP_TIMEOUT 300000 // 5分钟AP模式超时(毫秒)
|
||||||
|
|
||||||
|
const byte DNS_PORT = 53;
|
||||||
|
DNSServer dnsServer;
|
||||||
|
ESP8266WebServer server(80);
|
||||||
|
|
||||||
|
// 配置结构体
|
||||||
|
struct Config {
|
||||||
|
char wifiSSID[32];
|
||||||
|
char wifiPass[64];
|
||||||
|
char mqttServer[40];
|
||||||
|
char mqttPort[6];
|
||||||
|
char clientID[24];
|
||||||
|
char mqttUser[24];
|
||||||
|
char mqttPass[24];
|
||||||
|
char subTopic[32];
|
||||||
|
char pubTopic[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
Config config;
|
||||||
|
bool isAPMode = false;
|
||||||
|
|
||||||
|
// 函数前置声明
|
||||||
|
void handleRoot();
|
||||||
|
void handleCustom();
|
||||||
|
void handleWiFi();
|
||||||
|
void handleInfo();
|
||||||
|
void handleReset();
|
||||||
|
void handleReboot();
|
||||||
|
void handleSave();
|
||||||
|
void handleConnect();
|
||||||
|
void handleScan();
|
||||||
|
void handleCheckWiFi();
|
||||||
|
void handleWiFiStatus();
|
||||||
|
#include <PubSubClient.h>
|
||||||
|
WiFiClient espClient;
|
||||||
|
PubSubClient mqttClient(espClient);
|
||||||
|
|
||||||
|
// MQTT消息回调函数
|
||||||
|
void callback(char* topic, byte* payload, unsigned int length) {
|
||||||
|
Serial.write(payload, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long connectStartTime = 0;
|
||||||
|
unsigned long lastMqttReconnectAttempt = 0;
|
||||||
|
unsigned long lastMqttPing = 0;
|
||||||
|
const unsigned long mqttPingInterval = 60000; // 每60秒发送一次心跳包
|
||||||
|
bool connecting = false;
|
||||||
|
String pendingSSID = "";
|
||||||
|
String pendingPass = "";
|
||||||
|
bool shouldConnectWiFi = false;
|
||||||
|
int wifiConnectAttempts = 0;
|
||||||
|
const int maxWifiConnectAttempts = 5; // WiFi最大连接尝试次数
|
||||||
|
int mqttReconnectAttempts = 0;
|
||||||
|
const int maxMqttReconnectAttempts =20; // MQTT最大重连尝试次数
|
||||||
|
|
||||||
|
void enableTransparentMode() {
|
||||||
|
// 检查模块是否响应
|
||||||
|
Serial.println("AT");
|
||||||
|
if (waitForResponse("OK", 1000)) {
|
||||||
|
Serial.println("AT+CWMODE=1");
|
||||||
|
if (waitForResponse("OK", 1000)) {
|
||||||
|
Serial.println("AT+CIPMODE=1");
|
||||||
|
if (waitForResponse("OK", 1000)) {
|
||||||
|
Serial.println("AT+CIPSEND");
|
||||||
|
waitForResponse(">", 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助函数,等待模块响应
|
||||||
|
bool waitForResponse(const char* response, unsigned long timeout) {
|
||||||
|
unsigned long startTime = millis();
|
||||||
|
while (millis() - startTime < timeout) {
|
||||||
|
if (Serial.find(response)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reconnect() {
|
||||||
|
if (mqttClient.connect(config.clientID, config.mqttUser, config.mqttPass)) {
|
||||||
|
mqttClient.subscribe(config.subTopic);
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
enableTransparentMode();
|
||||||
|
}
|
||||||
|
return mqttClient.connected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void startAPMode() {
|
||||||
|
isAPMode = true;
|
||||||
|
WiFi.mode(WIFI_AP);
|
||||||
|
String mac = WiFi.macAddress();
|
||||||
|
mac.replace(":", "");
|
||||||
|
String apName = "znkg_" + mac.substring(mac.length() - 6);
|
||||||
|
WiFi.softAP(apName.c_str(), "12345678");
|
||||||
|
|
||||||
|
// DNS重定向到配网页面
|
||||||
|
dnsServer.start(DNS_PORT, "*", WiFi.softAPIP());
|
||||||
|
|
||||||
|
startWebServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
|
||||||
|
|
||||||
|
Serial.begin(9600);
|
||||||
|
EEPROM.begin(sizeof(Config));
|
||||||
|
loadConfig();
|
||||||
|
|
||||||
|
// 设置MQTT回调函数
|
||||||
|
mqttClient.setCallback(callback);
|
||||||
|
|
||||||
|
// 首次通电或配置为空时进入AP模式
|
||||||
|
if (strlen(config.wifiSSID) == 0 || strlen(config.mqttServer) == 0) {
|
||||||
|
WiFi.mode(WIFI_AP);
|
||||||
|
startAPMode();
|
||||||
|
} else {
|
||||||
|
// 设置MQTT服务器
|
||||||
|
mqttClient.setServer(config.mqttServer, atoi(config.mqttPort));
|
||||||
|
|
||||||
|
// 尝试连接保存的WiFi
|
||||||
|
WiFi.mode(WIFI_AP_STA);
|
||||||
|
WiFi.begin(config.wifiSSID, config.wifiPass);
|
||||||
|
connectStartTime = millis();
|
||||||
|
wifiConnectAttempts++;
|
||||||
|
|
||||||
|
// 等待15秒连接
|
||||||
|
for (int i = 0; i < 15; i++) {
|
||||||
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
Serial.println("WiFi连接成功");
|
||||||
|
wifiConnectAttempts = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
if (WiFi.status() != WL_CONNECTED) {
|
||||||
|
wifiConnectAttempts++; // 递增尝试次数
|
||||||
|
Serial.printf("WiFi连接失败,当前尝试次数: %d/%d\n", wifiConnectAttempts, maxWifiConnectAttempts);
|
||||||
|
|
||||||
|
if (wifiConnectAttempts >= maxWifiConnectAttempts) {
|
||||||
|
Serial.println("已达到最大WiFi连接尝试次数,进入AP模式");
|
||||||
|
WiFi.mode(WIFI_AP);
|
||||||
|
startAPMode();
|
||||||
|
} else {
|
||||||
|
delay(5000); // 等待5秒后重试
|
||||||
|
WiFi.begin(config.wifiSSID, config.wifiPass); // 重新尝试连接
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
startWebServer();
|
||||||
|
}
|
||||||
|
// 未连接则判断是否超过最大尝试次数
|
||||||
|
// 原代码(直接重启)
|
||||||
|
|
||||||
|
|
||||||
|
// 修改后代码(递增尝试次数)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
static unsigned long apStartTime = millis();
|
||||||
|
|
||||||
|
// 处理AP模式超时
|
||||||
|
if (isAPMode && millis() - apStartTime > AP_TIMEOUT) {
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理DNS请求
|
||||||
|
if (isAPMode) {
|
||||||
|
dnsServer.processNextRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理STC串口数据转发到MQTT
|
||||||
|
if (Serial.available() > 0 && mqttClient.connected()) {
|
||||||
|
String data = Serial.readStringUntil('\n');
|
||||||
|
if (data == "rest") {
|
||||||
|
memset(&config, 0, sizeof(config));
|
||||||
|
saveConfig();
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
mqttClient.publish(config.pubTopic, data.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// MQTT重连机制
|
||||||
|
if (!mqttClient.connected()) {
|
||||||
|
unsigned long now = millis();
|
||||||
|
if (now - lastMqttReconnectAttempt > 5000) {
|
||||||
|
lastMqttReconnectAttempt = now;
|
||||||
|
mqttReconnectAttempts++;
|
||||||
|
if (reconnect()) {
|
||||||
|
Serial.println("MQTT连接成功");
|
||||||
|
lastMqttPing = now;
|
||||||
|
mqttReconnectAttempts = 0;
|
||||||
|
} else {
|
||||||
|
Serial.printf("MQTT重连失败,当前尝试次数: %d/%d\n", mqttReconnectAttempts, maxMqttReconnectAttempts);
|
||||||
|
if (mqttReconnectAttempts >= maxMqttReconnectAttempts) {
|
||||||
|
|
||||||
|
Serial.println("已达到最大MQTT重连尝试次数,设备即将自动重启");
|
||||||
|
startAPMode();
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
startWebServer();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unsigned long now = millis();
|
||||||
|
if (now - lastMqttPing > mqttPingInterval) {
|
||||||
|
if (mqttClient.loop()) {
|
||||||
|
lastMqttPing = now;
|
||||||
|
} else {
|
||||||
|
// ping失败,断开连接触发重连
|
||||||
|
mqttClient.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mqttClient.loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
server.handleClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
void startWebServer() {
|
||||||
|
server.on("/", handleRoot);
|
||||||
|
server.on("/custom", handleCustom);
|
||||||
|
server.on("/wifi", handleWiFi);
|
||||||
|
server.on("/info", handleInfo);
|
||||||
|
server.on("/reset", handleReset);
|
||||||
|
server.on("/reboot", handleReboot);
|
||||||
|
server.on("/save", handleSave);
|
||||||
|
server.on("/connect", handleConnect);
|
||||||
|
server.on("/scan", handleScan);
|
||||||
|
server.on("/check_wifi", handleCheckWiFi);
|
||||||
|
server.on("/wifi_status", handleWiFiStatus);
|
||||||
|
|
||||||
|
// 添加强制门户重定向
|
||||||
|
server.on("/generate_204", handleRoot); // 安卓设备
|
||||||
|
server.on("/fwlink", handleRoot); // 微软设备
|
||||||
|
server.on("/hotspot-detect.html", handleRoot); // 苹果设备
|
||||||
|
server.on("/ncsi.txt", []() {
|
||||||
|
server.send(200, "text/plain", "OK");
|
||||||
|
});
|
||||||
|
|
||||||
|
// 未知请求重定向到首页
|
||||||
|
server.onNotFound([]() {
|
||||||
|
server.sendHeader("Location", "http://" + WiFi.softAPIP().toString(), true);
|
||||||
|
server.send(302, "text/plain", "");
|
||||||
|
});
|
||||||
|
|
||||||
|
server.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 网页处理函数
|
||||||
|
void handleRoot() {
|
||||||
|
String html = "<!DOCTYPE html><html><head>"
|
||||||
|
"<meta charset=\"UTF-8\">"
|
||||||
|
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
|
||||||
|
"<title>配网页面</title>"
|
||||||
|
"<style>"
|
||||||
|
"* {box-sizing: border-box; margin: 0; padding: 0; font-family: Arial, sans-serif;}"
|
||||||
|
"body {background-color: #f5f5f5; padding: 20px; color: #333;}"
|
||||||
|
".container {max-width: 500px; margin: 0 auto; background: white; border-radius: 10px; padding: 25px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);}"
|
||||||
|
"h1 {text-align: center; margin-bottom: 30px; color: #2c3e50; font-size: 28px;}"
|
||||||
|
".btn {display: block; width: 100%; padding: 15px; margin: 12px 0; background-color: #3498db; color: white; border: none; border-radius: 6px; font-size: 16px; cursor: pointer; transition: background-color 0.3s;}"
|
||||||
|
".btn:hover {background-color: #2980b9;}"
|
||||||
|
".btn:nth-child(odd) {background-color: #2ecc71;}"
|
||||||
|
".btn:nth-child(odd):hover {background-color: #27ae60;}"
|
||||||
|
".btn-danger {background-color: #e74c3c;}"
|
||||||
|
".btn-danger:hover {background-color: #c0392b;}"
|
||||||
|
"</style></head>"
|
||||||
|
"<body>"
|
||||||
|
"<div class=\"container\">"
|
||||||
|
"<h1>配网页面</h1>"
|
||||||
|
"<button class=\"btn\" onclick=\"location.href='/custom'\">自定义设置</button>"
|
||||||
|
"<button class=\"btn\" onclick=\"location.href='/wifi'\">WiFi设置</button>"
|
||||||
|
"<button class=\"btn\" onclick=\"location.href='/info'\">网络信息</button>"
|
||||||
|
"<button class=\"btn\" onclick=\"location.href='/reset'\">重 置</button>"
|
||||||
|
"<button class=\"btn\" onclick=\"location.href='/reboot'\">重 启</button>"
|
||||||
|
"<button class=\"btn\" onclick=\"window.close()\">退 出</button>"
|
||||||
|
"</div>"
|
||||||
|
"</body></html>";
|
||||||
|
server.send(200, "text/html", html);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleCustom() {
|
||||||
|
String html = "<!DOCTYPE html><html><head>"
|
||||||
|
"<meta charset=\"UTF-8\">"
|
||||||
|
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
|
||||||
|
"<title>自定义设置</title>"
|
||||||
|
"<style>"
|
||||||
|
"* {box-sizing: border-box; margin: 0; padding: 0; font-family: Arial, sans-serif;}"
|
||||||
|
"body {background-color: #f5f5f5; padding: 20px; color: #333;}"
|
||||||
|
".container {max-width: 500px; margin: 0 auto; background: white; border-radius: 10px; padding: 25px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);}"
|
||||||
|
"h1 {text-align: center; margin-bottom: 30px; color: #2c3e50; font-size: 24px;}"
|
||||||
|
".input-group {margin-bottom: 20px;}"
|
||||||
|
".input-group label {display: block; margin-bottom: 8px; font-weight: bold; color: #555;}"
|
||||||
|
".input-group input {width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 16px;}"
|
||||||
|
".btn {display: block; width: 100%; padding: 15px; margin: 15px 0; background-color: #3498db; color: white; border: none; border-radius: 6px; font-size: 16px; cursor: pointer; transition: background-color 0.3s;}"
|
||||||
|
".btn:hover {background-color: #2980b9;}"
|
||||||
|
".btn-back {background-color: #95a5a6;}"
|
||||||
|
".btn-back:hover {background-color: #7f8c8d;}"
|
||||||
|
"</style></head>"
|
||||||
|
"<body>"
|
||||||
|
"<div class=\"container\">"
|
||||||
|
"<h1>自定义设置</h1>"
|
||||||
|
"<form action=\"/save\">"
|
||||||
|
"<div class=\"input-group\">"
|
||||||
|
"<label>MQTT地址:</label>"
|
||||||
|
"<input type=\"text\" name=\"mqtt_server\" value=\""
|
||||||
|
+ String(config.mqttServer) + "\">"
|
||||||
|
"</div>"
|
||||||
|
"<div class=\"input-group\">"
|
||||||
|
"<label>MQTT端口:</label>"
|
||||||
|
"<input type=\"text\" name=\"mqtt_port\" value=\""
|
||||||
|
+ String(config.mqttPort) + "\">"
|
||||||
|
"</div>"
|
||||||
|
"<div class=\"input-group\">"
|
||||||
|
"<label>客户端ID:</label>"
|
||||||
|
"<input type=\"text\" name=\"client_id\" value=\""
|
||||||
|
+ String(config.clientID) + "\">"
|
||||||
|
"</div>"
|
||||||
|
"<div class=\"input-group\">"
|
||||||
|
"<label>用户名称:</label>"
|
||||||
|
"<input type=\"text\" name=\"mqtt_user\" value=\""
|
||||||
|
+ String(config.mqttUser) + "\">"
|
||||||
|
"</div>"
|
||||||
|
"<div class=\"input-group\">"
|
||||||
|
"<label>输入密码:</label>"
|
||||||
|
"<input type=\"password\" name=\"mqtt_pass\" value=\""
|
||||||
|
+ String(config.mqttPass) + "\">"
|
||||||
|
"</div>"
|
||||||
|
"<div class=\"input-group\">"
|
||||||
|
"<label>发布主题:</label>"
|
||||||
|
"<input type=\"text\" name=\"pub_topic\" value=\""
|
||||||
|
+ String(config.pubTopic) + "\">"
|
||||||
|
"</div>"
|
||||||
|
"<div class=\"input-group\">"
|
||||||
|
"<label>订阅主题:</label>"
|
||||||
|
"<input type=\"text\" name=\"sub_topic\" value=\""
|
||||||
|
+ String(config.subTopic) + "\">"
|
||||||
|
"</div>"
|
||||||
|
"<button type=\"submit\" class=\"btn\">保 存</button>"
|
||||||
|
"</form>"
|
||||||
|
"<button onclick=\"window.location.replace('/');\" class=\"btn btn-back\">返 回</button>"
|
||||||
|
"</div>"
|
||||||
|
"</body></html>";
|
||||||
|
server.send(200, "text/html", html);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleWiFi() {
|
||||||
|
String html = "<!DOCTYPE html><html><head>"
|
||||||
|
"<meta charset=\"UTF-8\">"
|
||||||
|
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
|
||||||
|
"<title>WiFi设置</title>"
|
||||||
|
"<style>"
|
||||||
|
"* {box-sizing: border-box; margin: 0; padding: 0; font-family: Arial, sans-serif;}"
|
||||||
|
"body {background-color: #f5f5f5; padding: 20px; color: #333;}"
|
||||||
|
".container {max-width: 500px; margin: 0 auto; background: white; border-radius: 10px; padding: 25px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);}"
|
||||||
|
"h1 {text-align: center; margin-bottom: 20px; color: #2c3e50; font-size: 24px;}"
|
||||||
|
"#networks {margin-bottom: 20px; max-height: 300px; overflow-y: auto; border: 1px solid #eee; border-radius: 6px; padding: 10px;}"
|
||||||
|
"#networks div {padding: 12px; border-bottom: 1px solid #eee; cursor: pointer; transition: background-color 0.2s;}"
|
||||||
|
"#networks div:hover {background-color: #f9f9f9;}"
|
||||||
|
"form {margin-bottom: 20px;}"
|
||||||
|
"input {width: 100%; padding: 12px; margin-bottom: 15px; border: 1px solid #ddd; border-radius: 6px; font-size: 16px;}"
|
||||||
|
".btn {display: block; width: 100%; padding: 15px; margin: 15px 0; background-color: #3498db; color: white; border: none; border-radius: 6px; font-size: 16px; cursor: pointer; transition: background-color 0.3s;}"
|
||||||
|
".btn:hover {background-color: #2980b9;}"
|
||||||
|
".btn-back {background-color: #95a5a6;}"
|
||||||
|
".btn-back:hover {background-color: #7f8c8d;}"
|
||||||
|
"</style>"
|
||||||
|
"<script>"
|
||||||
|
"function loadNetworks() {"
|
||||||
|
" fetch('/scan')"
|
||||||
|
" .then(r => r.json())"
|
||||||
|
" .then(data => {"
|
||||||
|
" let networksDiv = document.getElementById('networks');"
|
||||||
|
" networksDiv.innerHTML = '';"
|
||||||
|
" data.forEach(net => {"
|
||||||
|
" let div = document.createElement('div');"
|
||||||
|
" div.textContent = net;"
|
||||||
|
" div.style.cursor = 'pointer';"
|
||||||
|
" div.onclick = function() {"
|
||||||
|
" document.getElementById('ssid').value = net;"
|
||||||
|
" };"
|
||||||
|
" networksDiv.appendChild(div);"
|
||||||
|
" });"
|
||||||
|
" });"
|
||||||
|
"}"
|
||||||
|
"window.onload = loadNetworks;"
|
||||||
|
"</script></head>"
|
||||||
|
"<body>"
|
||||||
|
"<div class=\"container\">"
|
||||||
|
"<h1>WiFi设置</h1>"
|
||||||
|
"<div id=\"networks\"></div>"
|
||||||
|
"<form action=\"/connect\">"
|
||||||
|
"<input type=\"text\" id=\"ssid\" name=\"ssid\" placeholder=\"WiFi名称\">"
|
||||||
|
"<input type=\"password\" name=\"pass\" placeholder=\"WiFi密码\">"
|
||||||
|
"<button type=\"submit\" class=\"btn\">连 接</button>"
|
||||||
|
"</form>"
|
||||||
|
"<button onclick=\"window.location.href='/';\" class=\"btn btn-back\">返 回</button>"
|
||||||
|
"</div>"
|
||||||
|
"</body></html>";
|
||||||
|
server.send(200, "text/html", html);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleInfo() {
|
||||||
|
String html = "<!DOCTYPE html><html><head>"
|
||||||
|
"<meta charset=\"UTF-8\">"
|
||||||
|
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
|
||||||
|
"<title>网络信息</title>"
|
||||||
|
"<style>"
|
||||||
|
"* {box-sizing: border-box; margin: 0; padding: 0; font-family: Arial, sans-serif;}"
|
||||||
|
"body {background-color: #f5f5f5; padding: 20px; color: #333;}"
|
||||||
|
".container {max-width: 500px; margin: 0 auto; background: white; border-radius: 10px; padding: 25px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);}"
|
||||||
|
"h1 {text-align: center; margin-bottom: 30px; color: #2c3e50; font-size: 24px;}"
|
||||||
|
".info {margin-bottom: 15px; padding: 15px; background-color: #f9f9f9; border-radius: 6px; font-size: 16px;}"
|
||||||
|
".info strong {color: #2c3e50;}"
|
||||||
|
".connected {color: #27ae60; font-weight: bold;}"
|
||||||
|
".disconnected {color: #c0392b; font-weight: bold;}"
|
||||||
|
".btn {display: block; width: 100%; padding: 15px; margin: 20px 0 0; background-color: #3498db; color: white; border: none; border-radius: 6px; font-size: 16px; cursor: pointer; transition: background-color 0.3s;}"
|
||||||
|
".btn:hover {background-color: #2980b9;}"
|
||||||
|
"</style></head>"
|
||||||
|
"<body>"
|
||||||
|
"<div class=\"container\">"
|
||||||
|
"<h1>网络信息</h1>"
|
||||||
|
"<div class=\"info\"><strong>IP地址:</strong> "
|
||||||
|
+ WiFi.localIP().toString() + "</div>"
|
||||||
|
"<div class=\"info\"><strong>MAC地址:</strong> "
|
||||||
|
+ WiFi.macAddress() + "</div>"
|
||||||
|
"<div class=\"info\"><strong>工作模式:</strong> "
|
||||||
|
+ (WiFi.getMode() == WIFI_AP ? "AP" : "STA") + "</div>"
|
||||||
|
"<div class=\"info\"><strong>信号强度:</strong> "
|
||||||
|
+ String(WiFi.RSSI()) + " dBm</div>"
|
||||||
|
"<div class=\"info\"><strong>MQTT连接状态:</strong> <span class=\"" + (mqttClient.connected() ? "connected" : "disconnected") + "\">" + (mqttClient.connected() ? "已连接" : "未连接") + "</span></div>"
|
||||||
|
"<div class=\"info\"><strong>MQTT地址:</strong> "
|
||||||
|
+ String(config.mqttServer) + "</div>"
|
||||||
|
"<div class=\"info\"><strong>MQTT端口:</strong> "
|
||||||
|
+ String(config.mqttPort) + "</div>"
|
||||||
|
"<div class=\"info\"><strong>客户端ID:</strong> "
|
||||||
|
+ String(config.clientID) + "</div>"
|
||||||
|
"<div class=\"info\"><strong>发布主题:</strong> "
|
||||||
|
+ String(config.pubTopic) + "</div>"
|
||||||
|
"<div class=\"info\"><strong>订阅主题:</strong> "
|
||||||
|
+ String(config.subTopic) + "</div>"
|
||||||
|
"<button onclick=\"window.location.href='/';\" class=\"btn\">返 回</button>"
|
||||||
|
"</div>"
|
||||||
|
"</body></html>";
|
||||||
|
server.send(200, "text/html", html);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void handleReboot() {
|
||||||
|
String html = "<!DOCTYPE html><html><head>"
|
||||||
|
"<meta charset=\"UTF-8\">"
|
||||||
|
"<title>重启</title>"
|
||||||
|
"<style>"
|
||||||
|
"body {background:#000;color:#fff;font-size:40px;text-align:center;}"
|
||||||
|
"</style></head>"
|
||||||
|
"<body>"
|
||||||
|
"<h1>设备重启中...</h1>"
|
||||||
|
"<script>"
|
||||||
|
"setTimeout(function() {"
|
||||||
|
" window.location.href = '/';"
|
||||||
|
"}, 3000);"
|
||||||
|
"</script>"
|
||||||
|
"</body></html>";
|
||||||
|
server.send(200, "text/html", html);
|
||||||
|
delay(1000);
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleSave() {
|
||||||
|
if (server.method() == HTTP_GET) {
|
||||||
|
// 保存MQTT配置
|
||||||
|
strncpy(config.mqttServer, server.arg("mqtt_server").c_str(), sizeof(config.mqttServer));
|
||||||
|
strncpy(config.mqttPort, server.arg("mqtt_port").c_str(), sizeof(config.mqttPort));
|
||||||
|
strncpy(config.clientID, server.arg("client_id").c_str(), sizeof(config.clientID));
|
||||||
|
strncpy(config.mqttUser, server.arg("mqtt_user").c_str(), sizeof(config.mqttUser));
|
||||||
|
strncpy(config.mqttPass, server.arg("mqtt_pass").c_str(), sizeof(config.mqttPass));
|
||||||
|
strncpy(config.subTopic, server.arg("sub_topic").c_str(), sizeof(config.subTopic));
|
||||||
|
strncpy(config.pubTopic, server.arg("pub_topic").c_str(), sizeof(config.pubTopic));
|
||||||
|
|
||||||
|
saveConfig();
|
||||||
|
|
||||||
|
// 设置MQTT服务器
|
||||||
|
mqttClient.setServer(config.mqttServer, atoi(config.mqttPort));
|
||||||
|
|
||||||
|
// 尝试连接MQTT
|
||||||
|
bool connected = mqttClient.connect(config.clientID, config.mqttUser, config.mqttPass);
|
||||||
|
|
||||||
|
String html;
|
||||||
|
if (connected) {
|
||||||
|
html = "<!DOCTYPE html><html><head><title>连接成功</title>";
|
||||||
|
html += "<meta http-equiv='refresh' content='3;url=/'>";
|
||||||
|
html += "</head><body><h1>配置保存成功,MQTT连接成功</h1>";
|
||||||
|
html += "<p>设备将在 3 秒后跳转...</p></body></html>";
|
||||||
|
enableTransparentMode();
|
||||||
|
} else {
|
||||||
|
html = "<!DOCTYPE html><html><head><title>连接失败</title>";
|
||||||
|
html += "<meta http-equiv='refresh' content='5;url=/custom'>";
|
||||||
|
html += "</head><body><h1>配置保存成功,但MQTT连接失败</h1>";
|
||||||
|
html += "<p>请检查MQTT配置,页面将在 5 秒后跳转至配置页面...</p></body></html>";
|
||||||
|
}
|
||||||
|
|
||||||
|
server.send(200, "text/html", html);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleConnect() {
|
||||||
|
if (server.hasArg("ssid") && server.hasArg("pass") && !connecting) {
|
||||||
|
pendingSSID = server.arg("ssid");
|
||||||
|
pendingPass = server.arg("pass");
|
||||||
|
strncpy(config.wifiSSID, pendingSSID.c_str(), sizeof(config.wifiSSID));
|
||||||
|
strncpy(config.wifiPass, pendingPass.c_str(), sizeof(config.wifiPass));
|
||||||
|
saveConfig();
|
||||||
|
|
||||||
|
connecting = true;
|
||||||
|
connectStartTime = millis();
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
WiFi.begin(pendingSSID, pendingPass);
|
||||||
|
}
|
||||||
|
|
||||||
|
String html = "<!DOCTYPE html><html><head>"
|
||||||
|
"<meta charset=\"UTF-8\">"
|
||||||
|
"<title>WiFi连接状态</title>"
|
||||||
|
"<style>"
|
||||||
|
"body {background:#000;color:#fff;font-size:40px;text-align:center;}"
|
||||||
|
" .info {margin:15px 0;font-size:40px;}"
|
||||||
|
" button {background:#0066cc;color:#fff;border:none;padding:15px 30px;margin:15px;cursor:pointer; font-size: 40px;}"
|
||||||
|
"</style></head>"
|
||||||
|
"<body>"
|
||||||
|
"<h1>WiFi连接状态</h1>";
|
||||||
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
html += "</head><body>"
|
||||||
|
"<h1 style=\"color:green\">连接成功</h1>"
|
||||||
|
"<p>SSID: "
|
||||||
|
+ WiFi.SSID() + "</p>"
|
||||||
|
"<p>IP地址: "
|
||||||
|
+ WiFi.localIP().toString() + "</p>"
|
||||||
|
"<button onclick=\"window.location.href='/';\">返回首页</button>";
|
||||||
|
connecting = false;
|
||||||
|
} else if (connecting && millis() - connectStartTime < 30000) {
|
||||||
|
html += "</head><body>"
|
||||||
|
"<h1>正在连接中...</h1>"
|
||||||
|
"<meta http-equiv=\"refresh\" content=\"5\">";
|
||||||
|
} else {
|
||||||
|
html += "</head><body>"
|
||||||
|
"<h1 style=\"color:red\">连接失败</h1>"
|
||||||
|
"<button onclick=\"window.location.href='/wifi';\">重新选择WiFi</button>";
|
||||||
|
connecting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
html += "</body></html>";
|
||||||
|
server.send(200, "text/html", html);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleScan() {
|
||||||
|
String json = "[";
|
||||||
|
int n = WiFi.scanNetworks();
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
if (i) json += ",";
|
||||||
|
json += "\"" + WiFi.SSID(i) + "\"";
|
||||||
|
}
|
||||||
|
json += "]";
|
||||||
|
server.send(200, "application/json", json);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存配置到EEPROM
|
||||||
|
void saveConfig() {
|
||||||
|
EEPROM.put(0, config);
|
||||||
|
EEPROM.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从EEPROM加载配置
|
||||||
|
void loadConfig() {
|
||||||
|
EEPROM.get(0, config);
|
||||||
|
// 检查是否为首次使用
|
||||||
|
if (config.mqttPort[0] == 0) {
|
||||||
|
strcpy(config.mqttPort, "1883");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleCheckWiFi() {
|
||||||
|
String html = "<!DOCTYPE html><html><head>"
|
||||||
|
"<meta charset=\"UTF-8\">"
|
||||||
|
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
|
||||||
|
"<title>WiFi连接状态</title>"
|
||||||
|
"<style>"
|
||||||
|
"* {box-sizing: border-box; margin: 0; padding: 0; font-family: Arial, sans-serif;}"
|
||||||
|
"body {background-color: #f5f5f5; padding: 20px; color: #333;}"
|
||||||
|
".container {max-width: 500px; margin: 0 auto; background: white; border-radius: 10px; padding: 25px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);}"
|
||||||
|
"h1 {text-align: center; margin-bottom: 20px; font-size: 24px;}"
|
||||||
|
".connected {color: #27ae60;}"
|
||||||
|
".disconnected {color: #c0392b;}"
|
||||||
|
".info {margin: 15px 0; padding: 15px; background-color: #f9f9f9; border-radius: 6px; font-size: 16px;}"
|
||||||
|
".btn {display: block; width: 100%; padding: 15px; margin: 20px 0 0; background-color: #3498db; color: white; border: none; border-radius: 6px; font-size: 16px; cursor: pointer; transition: background-color 0.3s;}"
|
||||||
|
".btn:hover {background-color: #2980b9;}"
|
||||||
|
"</style></head>"
|
||||||
|
"<body>"
|
||||||
|
"<div class=\"container\">";
|
||||||
|
|
||||||
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
html += "<h1 class=\"connected\">连接成功</h1>"
|
||||||
|
"<div class=\"info\"><strong>SSID:</strong> "
|
||||||
|
+ WiFi.SSID() + "</div>"
|
||||||
|
"<div class=\"info\"><strong>IP地址:</strong> "
|
||||||
|
+ WiFi.localIP().toString() + "</div>"
|
||||||
|
"<div class=\"info\"><strong>信号强度:</strong> "
|
||||||
|
+ String(WiFi.RSSI()) + " dBm</div>";
|
||||||
|
} else {
|
||||||
|
html += "<h1 class=\"disconnected\">未连接</h1>";
|
||||||
|
}
|
||||||
|
|
||||||
|
html += "<button onclick=\"window.location.href='/';\" class=\"btn\">返回首页</button>"
|
||||||
|
"</div>"
|
||||||
|
"</body></html>";
|
||||||
|
server.send(200, "text/html", html);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleWiFiStatus() {
|
||||||
|
String html = "<!DOCTYPE html><html><head>"
|
||||||
|
"<meta charset=\"UTF-8\">"
|
||||||
|
"<title>WiFi连接状态</title>"
|
||||||
|
"<style>"
|
||||||
|
"body {background:#000;color:#fff;font-size:40px;text-align:center;}"
|
||||||
|
" .info {margin:15px 0;font-size:40px;}"
|
||||||
|
" button {background:#0066cc;color:#fff;border:none;padding:15px 30px;margin:15px;cursor:pointer; font-size: 40px;}"
|
||||||
|
"h1 {font-size:24px;}"
|
||||||
|
"p {margin: 15px 0;}"
|
||||||
|
"</style></head>"
|
||||||
|
"<body>";
|
||||||
|
|
||||||
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
html += "<h1 style=\"color:green\">连接成功</h1>"
|
||||||
|
"<p>SSID: "
|
||||||
|
+ WiFi.SSID() + "</p>"
|
||||||
|
"<p>IP地址: "
|
||||||
|
+ WiFi.localIP().toString() + "</p>"
|
||||||
|
"<p>信号强度: "
|
||||||
|
+ String(WiFi.RSSI()) + " dBm</p>";
|
||||||
|
} else {
|
||||||
|
html += "<h1 style=\"color:red\">未连接</h1>";
|
||||||
|
}
|
||||||
|
|
||||||
|
html += "<button onclick=\"window.location.href='/';\">返回首页</button>"
|
||||||
|
"</body></html>";
|
||||||
|
server.send(200, "text/html", html);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleReset() {
|
||||||
|
memset(&config, 0, sizeof(config));
|
||||||
|
saveConfig();
|
||||||
|
|
||||||
|
String html = "<!DOCTYPE html><html><head>"
|
||||||
|
"<meta charset=\"UTF-8\">"
|
||||||
|
"<title>重置成功</title>"
|
||||||
|
"<style>"
|
||||||
|
"body {background:#000;color:#fff;text-align:center;}"
|
||||||
|
"</style></head>"
|
||||||
|
"<body>"
|
||||||
|
"<h1>重置成功</h1>"
|
||||||
|
"<p>WiFi配置已清除,设备将重启进入AP模式</p>"
|
||||||
|
"<script>"
|
||||||
|
"setTimeout(function() {"
|
||||||
|
" window.location.href = '/reboot';"
|
||||||
|
"}, 3000);"
|
||||||
|
"</script>"
|
||||||
|
"</body></html>";
|
||||||
|
server.send(200, "text/html", html);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user