WS2812 网页控制

截屏2025-03-03 16.48.13.png

#include <ESP8266WiFi.h>
#include <ESPAsyncWebServer.h>
#include <Adafruit_NeoPixel.h>

// 配置wifi信息
const char* ssid = "山东酷影";
const char* password = "sdky86400265";

AsyncWebServer server(80);

#define DEFAULT_PIN 12
#define DEFAULT_LED_COUNT 30

Adafruit_NeoPixel *strip;
bool runningEffect = false;
unsigned long previousMillis = 0;
int effectInterval = 100;

struct Config {
  int pin;
  int ledCount;
  uint8_t brightness;
  uint32_t color;
  bool marquee;
  bool randomEffect; // 新增随机效果标志
};
Config config;

void setup() {
  Serial.begin(115200);
  
  // 使用更安全的初始化方式
  config.pin = DEFAULT_PIN;
  config.ledCount = DEFAULT_LED_COUNT;
  config.brightness = 50;
  config.color = Adafruit_NeoPixel::Color(255, 255, 255); // 使用静态方法
  config.marquee = false;
  config.randomEffect = false; // 初始化随机效果标志
  
  strip = new Adafruit_NeoPixel(config.ledCount, config.pin, NEO_GRB + NEO_KHZ800);
  strip->begin();
  strip->setBrightness(config.brightness);
  strip->show();

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nConnected! IP: " + WiFi.localIP().toString());

  // 使用自定义分隔符的原始字符串
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    String html = R"=====(
      <!DOCTYPE html>
      <html><head>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <style>
          body {font-family: Arial; margin: 20px;}
          input, button {margin: 5px;}
          #colorPicker {width: 200px; height: 200px; border: 1px solid #000; cursor: crosshair;}
        </style>
      </head>
      <body>
        <h1>WS2812 controller</h1>
        <form id="configForm">
          LED NUMBER: <input type="number" name="ledCount" value=")=====";
    html += String(config.ledCount);
    html += R"=====("><br>
          PIN NUMBER: <input type="number" name="pin" value=")=====";
    html += String(config.pin);
    html += R"=====("><br>
          Brightness(0-255): <input type="range" name="brightness" min="0" max="255" value=")=====";
    html += String(config.brightness);
    html += R"=====("><br>
          Marquee: <input type="checkbox" name="marquee" )=====";
    html += config.marquee ? "checked" : "";
    html += R"=====(><br>
          Random Effect: <input type="checkbox" name="randomEffect" )=====";
    html += config.randomEffect ? "checked" : "";
    html += R"=====(><br>
        </form>
        <h3>Color Picker</h3>
        <canvas id="colorPicker" width="200" height="200"></canvas>
        <script>
          // 创建色盘
          const canvas = document.getElementById('colorPicker');
          const ctx = canvas.getContext('2d');
          const gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
          gradient.addColorStop(0, 'red');
          gradient.addColorStop(0.16, 'orange');
          gradient.addColorStop(0.32, 'yellow');
          gradient.addColorStop(0.48, 'green');
          gradient.addColorStop(0.64, 'blue');
          gradient.addColorStop(0.80, 'indigo');
          gradient.addColorStop(1, 'violet');
          ctx.fillStyle = gradient;
          ctx.fillRect(0, 0, canvas.width, canvas.height);

          // 监听色盘点击事件
          canvas.addEventListener('click', function(event) {
            const rect = canvas.getBoundingClientRect();
            const x = event.clientX - rect.left;
            const y = event.clientY - rect.top;
            const pixel = ctx.getImageData(x, y, 1, 1).data;
            const color = '#' + ((1 << 24) + (pixel[0] << 16) + (pixel[1] << 8) + pixel[2]).toString(16).slice(1);
            
            // 发送颜色值到服务器
            fetch('/config', {
              method: 'POST',
              headers: {'Content-Type': 'application/x-www-form-urlencoded'},
              body: 'color=' + encodeURIComponent(color)
            });
          });

          // 监听表单变化
          const form = document.getElementById('configForm');
          form.addEventListener('change', function() {
            let formData = new FormData(form);
            fetch('/config', {
              method: 'POST',
              body: new URLSearchParams(formData)
            });
          });
        </script>
      </body></html>
    )=====";
    request->send(200, "text/html", html);
  });

  server.on("/config", HTTP_POST, [](AsyncWebServerRequest *request){
    // 处理配置更新
    if(request->hasParam("ledCount", true)) 
      config.ledCount = request->getParam("ledCount", true)->value().toInt();
    if(request->hasParam("pin", true)) 
      config.pin = request->getParam("pin", true)->value().toInt();
    if(request->hasParam("brightness", true)) 
      config.brightness = request->getParam("brightness", true)->value().toInt();
    if(request->hasParam("color", true)){
      String colorStr = request->getParam("color", true)->value().substring(1);
      config.color = strtoul(colorStr.c_str(), NULL, 16);
    }
    config.marquee = request->hasParam("marquee", true);
    config.randomEffect = request->hasParam("randomEffect", true); // 更新随机效果标志

    // 安全重建灯带
    delete strip;
    strip = new Adafruit_NeoPixel(config.ledCount, config.pin, NEO_GRB + NEO_KHZ800);
    strip->begin();
    strip->setBrightness(config.brightness);
    strip->clear();
    strip->show();
    
    request->send(200, "text/plain", "OK");
  });

  server.begin();
}

void loop() {
  static int pos = 0;
  if(config.marquee) {
    if(millis() - previousMillis >= effectInterval) {
      previousMillis = millis();
      strip->clear();
      strip->setPixelColor(pos, config.color);
      strip->show();
      pos = (pos + 1) % config.ledCount;
    }
  } else if (config.randomEffect) {
    if(millis() - previousMillis >= effectInterval) {
      previousMillis = millis();
      strip->clear();
      for(int i = 0; i < config.ledCount; i++) {
        if(random(2) == 1) { // 随机决定是否点亮
          strip->setPixelColor(i, Adafruit_NeoPixel::Color(random(256), random(256), random(256))); // 随机颜色
        }
      }
      strip->show();
    }
  } else {
    strip->fill(config.color);
    strip->show();
  }
}

本文链接:

http://152.69.194.25:22380/index.php/archives/13/
1 + 2 =
快来做第一个评论的人吧~