A server on an ESP32 board.
NOUSERNAMEERIC · · 个人记录
#include <WiFi.h>
#include <WebServer.h>
#include <SPIFFS.h>
#include <ArduinoJson.h>
WebServer server(80);
// 配置参数
#define CONNECTION_TIMEOUT 15000
#define DEFAULT_SSID "Eric"
#define DEFAULT_PSK "Eric120513"
#define STATUS_LED_PIN 2
#define SERIAL_BAUDRATE 115200
// 有效数字GPIO列表(根据ESP32实际可用引脚)
const uint8_t validPins[] = {0,2,4,5,12,13,14,15,16,17,18,19,21,22,23,25,26,27,32,33};
struct PinState {
uint8_t mode; // 0=INPUT, 1=OUTPUT
uint8_t value;
};
// 全局变量
PinState pinStates[34]; // 仅存储GPIO0-33的状态
uint32_t serialTxCount = 0;
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="UTF-8">
<title>ESP32控制面板</title>
<style>
body { font-family: Arial; margin: 20px; }
.card { padding: 15px; border: 1px solid #ddd; margin: 10px; }
.pin-item { margin: 5px 0; padding: 5px; background: #f5f5f5; }
</style>
</head>
<body>
<div class="card">
<h2>系统状态</h2>
<div id="sysinfo"></div>
</div>
<div class="card">
<h2>GPIO控制</h2>
<div id="pins"></div>
</div>
<div class="card">
<h2>串口通信</h2>
<input type="text" id="message">
<button onclick="sendMessage()">发送</button>
<div id="serialLog"></div>
</div>
<script>
function updateStatus() {
fetch('/sysinfo').then(r => r.json()).then(data => {
document.getElementById('sysinfo').innerHTML = `
<div>固件: ${data.version}</div>
<div>IP: ${data.ip}</div>
<div>内存: ${data.heap} bytes</div>
<div>发送计数: ${data.txCount}</div>`;
});
fetch('/pins').then(r => r.json()).then(data => {
let html = '';
data.forEach(pin => {
html += `<div class="pin-item">
GPIO ${pin.num}:
<select onchange="setMode(${pin.num}, this.value)">
<option value="0" ${pin.mode==0?'selected':''}>输入</option>
<option value="1" ${pin.mode==1?'selected':''}>输出</option>
</select>
状态: ${pin.value}
${pin.mode==1 ? `<button onclick="toggle(${pin.num})">切换</button>` : ''}
</div>`;
});
document.getElementById('pins').innerHTML = html;
});
}
function setMode(pin, mode) {
fetch(`/pin/mode?num=${pin}&mode=${mode}`);
updateStatus();
}
function toggle(pin) {
fetch(`/pin/toggle?num=${pin}`);
updateStatus();
}
function sendMessage() {
const msg = document.getElementById('message').value;
fetch(`/serial/send?msg=${encodeURIComponent(msg)}`);
document.getElementById('message').value = '';
}
setInterval(updateStatus, 5000);
</script>
</body>
</html>
)rawliteral";
void initGPIO() {
// 初始化有效GPIO
for(auto pin : validPins) {
if(pin <= 33) { // 确保不超出数组范围
pinMode(pin, INPUT);
pinStates[pin] = {0, digitalRead(pin)};
}
}
pinMode(STATUS_LED_PIN, OUTPUT);
}
void handleSysInfo() {
DynamicJsonDocument doc(256);
doc["version"] = ESP.getSdkVersion();
doc["ip"] = WiFi.localIP().toString();
doc["heap"] = ESP.getFreeHeap();
doc["txCount"] = serialTxCount;
String json;
serializeJson(doc, json);
server.send(200, "application/json", json);
}
void handlePinStatus() {
DynamicJsonDocument doc(512);
JsonArray array = doc.to<JsonArray>();
for(auto pin : validPins) {
if(pin > 33) continue;
JsonObject obj = array.createNestedObject();
obj["num"] = pin;
obj["mode"] = pinStates[pin].mode;
obj["value"] = digitalRead(pin);
}
String json;
serializeJson(doc, json);
server.send(200, "application/json", json);
}
void handlePinMode() {
uint8_t pin = server.arg("num").toInt();
uint8_t mode = server.arg("mode").toInt();
// 验证引脚有效性
bool valid = false;
for(auto p : validPins) {
if(p == pin && pin <= 33) {
valid = true;
break;
}
}
if(valid) {
pinMode(pin, mode ? OUTPUT : INPUT);
pinStates[pin].mode = mode;
server.send(200, "text/plain", "OK");
} else {
server.send(400, "text/plain", "无效引脚");
}
}
void handlePinToggle() {
uint8_t pin = server.arg("num").toInt();
if(pin <= 33 && pinStates[pin].mode == 1) {
digitalWrite(pin, !digitalRead(pin));
server.send(200, "text/plain", "切换成功");
} else {
server.send(400, "text/plain", "操作失败");
}
}
void handleSerialSend() {
String msg = server.arg("msg");
if(msg.length() > 0 && msg.length() <= 128) {
Serial.println(msg);
serialTxCount++;
server.send(200, "text/plain", "发送成功");
} else {
server.send(400, "text/plain", "消息长度无效");
}
}
void setup() {
Serial.begin(SERIAL_BAUDRATE);
Serial.println("ESP32 GPIO 服务器");
Serial.println("-|By lyc122|-");
Serial.println("----------");
Serial.println("系统启动中......");
delay(1000);
Serial.println("初始化存储...");
// 初始化SPIFFS(带重试机制)
for(int i=0; i<3; i++) {
if(SPIFFS.begin(true)) break;
delay(1000);
}
Serial.println("初始化引脚状态...");
initGPIO();
pinMode(2,OUTPUT);
digitalWrite(2,HIGH);
Serial.println("连接网络...");
// 连接WiFi(带超时处理)
WiFi.begin(DEFAULT_SSID, DEFAULT_PSK);
uint32_t start = millis();
while(WiFi.status() != WL_CONNECTED && millis()-start < CONNECTION_TIMEOUT) {
delay(500);
digitalWrite(STATUS_LED_PIN, !digitalRead(STATUS_LED_PIN));
}
digitalWrite(STATUS_LED_PIN, LOW);
delay(100);
digitalWrite(STATUS_LED_PIN, HIGH);
delay(100);
digitalWrite(STATUS_LED_PIN, LOW);
delay(100);
digitalWrite(STATUS_LED_PIN, HIGH);
delay(100);
digitalWrite(STATUS_LED_PIN, LOW);
delay(100);
digitalWrite(STATUS_LED_PIN, HIGH);
delay(100);
digitalWrite(STATUS_LED_PIN, LOW);
delay(100);
digitalWrite(STATUS_LED_PIN, HIGH);
delay(100);
digitalWrite(STATUS_LED_PIN, LOW);
delay(100);
Serial.println("配置路由...");
// 配置路由
server.on("/", []{ server.send(200, "text/html", index_html); });
server.on("/sysinfo", handleSysInfo);
server.on("/pins", handlePinStatus);
server.on("/pin/mode", handlePinMode);
server.on("/pin/toggle", handlePinToggle);
server.on("/serial/send", handleSerialSend);
Serial.println("启动服务器...");
server.begin();
Serial.println("系统启动完成");
Serial.println("->当前IP地址: "+WiFi.localIP().toString());
}
void loop() {
server.handleClient();
// 更新输入状态
static uint32_t lastUpdate = 0;
if(millis() - lastUpdate > 100) {
for(auto pin : validPins) {
if(pin <= 33 && pinStates[pin].mode == 0) {
pinStates[pin].value = digitalRead(pin);
}
}
lastUpdate = millis();
}
}