A server on an ESP32 board.

· · 个人记录

#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();
  }
}