• Moin Leute,

    ich habe von der Schule aus einen Infinity Mirror gemacht. Diesen habe ich mit einiger Hilfe von @cooper realisieren können. Er ist kreisförmig mit einem ⌀9,3cm. Das ganze wurde mit einer ws2812b LED Streifen umgesetzt und einem ESP8266 welcher einen Webserver erzeugt über welchen man die LED Streifen steuern kann. Der Webserver funktioniert hierbei mit .html, .js und .css Dateien, welche sich alle im SPIFFS (Serial Peripheral Interface Flash File System) befinden. Bei folgendem Video könnt ihr euch einen Einblick ergattern.

    Dort seht ihr zum Einen den Aufbau der Webseite, als auch am Ende das fertige Produkt und seine Funktionsweise.
    Im folgenden werde ich den Code zeigen und zudem eine .zip File zur Verfügung stellen, falls sich das einer in einem Bearbeitungsprogramm anschauen möchte oder selbst etwas mit einem Webserver oder einer LED Streifen machen möchte und davon nicht so viel Ahnung hat. In der Zip Datei befindet sich auch eine Powerpoint Datei, welche einige Dinge näher erklärt.

    Zuerst der Aufbau des Projekts mit dem Programm Fritzing

    Elektronik:

    ¹Produktempfehlungen

    Produkte zum Warenkorb hinzufügen


    Baumaterialien:

    ¹Produktempfehlungen

    Produkte zum Warenkorb hinzufügen


    Danach der Arduino Code, sieht nach einer Menge aus. Ist es auch, aber an sich gar nicht so schwer zu verstehen.

    #include <ESP8266WiFi.h>
    #include <ESPAsyncTCP.h>
    #include <ESPAsyncWebServer.h>
    #include <Adafruit_NeoPixel.h>
    #include <ArduinoOTA.h>
    
    #define LED_PIN 2
    #define LED_COUNT 14
    Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
    
    bool gerade = true;
    int id = 0;
    int idsafe = -1;
    int color1[3] = {0,0,0};
    int color2[3] = {0,0,0};
    int brightness;
    int parameter = 0;
    
    // ÄNDERN, FALLS MAN ES NACHMACHEN MÖCHTE!
    const char * ssid = "YOUR_WLAN_SSID";
    const char * password = "YOUR_WLAN_PASSWORD";
    
    
    AsyncWebServer server(80);
    
    void rainbow(int wait) {
      for (long firstPixelHue = 0; firstPixelHue < 5 * 65536; firstPixelHue += 256) {
        for (int i = 0; i < strip.numPixels(); i++) {
          if (id != 2) break;
          int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
          strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
        }
        if (id != 2) break;
        strip.show();
        delay(wait);
      }
    }
    
    void farbenUebergang() {
      strip.clear();
      for (int k = 1; k <= 2; k++) {
        for (int i = 0; i <= 255; i++) {
          if (id != 3) break;
          switch (k) {
            case 1:
              strip.fill(strip.Color((int)(i * ((double)color1[0] / 255)), (int)(i * ((double)color1[1] / 255)), (int)(i * ((double)color1[2] / 255))), 0, LED_COUNT);
              break;
            case 2:
              strip.fill(strip.Color((int)(i * ((double)color2[0] / 255)), (int)(i * ((double)color2[1] / 255)), (int)(i * ((double)color2[2] / 255))), 0, LED_COUNT);
              break;
          }
          strip.show();
          delay(3);
        }
        if (id != 3) break;
        for (int i = 255; i >= 0; i--) {
          if (id != 3) break;
          switch (k) {
            case 1:
              strip.fill(strip.Color((int)(i * ((double)color1[0] / 255)), (int)(i * ((double)color1[1] / 255)), (int)(i * ((double)color1[2] / 255))), 0, LED_COUNT);
              break;
            case 2:
              strip.fill(strip.Color((int)(i * ((double)color2[0] / 255)), (int)(i * ((double)color2[1] / 255)), (int)(i * ((double)color2[2] / 255))), 0, LED_COUNT);
              break;
          }
          strip.show();
          delay(3);
        }
        if (id != 3) break;
      }
    }
    
    void farbenVerfolgung() {
      strip.clear();
      for (int k = 1; k <= 2; k++) {
        for (int i = 0; i < 15; i++) {
          if (id != 4) break;
          switch (k) {
            case 1:
              strip.setPixelColor(i, strip.Color(color1[0], color1[1], color1[2]));
              break;
            case 2:
              strip.setPixelColor(i, strip.Color(color2[0], color2[1], color2[2]));
              break;
          }
          strip.show();
          delay(100);
        }
        if (id != 4) break;
        strip.clear();
      }
    }
    
    void farbenVerfolgungZweiFarben(uint32_t color1, uint32_t color2) {
      strip.clear();
      for (int k = 0; k < 1; k++) {
        for (int i = 0; i < LED_COUNT; i++) {
          if (id != 5) break;
          if (gerade == true) {
            strip.setPixelColor(i, color1);
          } else {
            strip.setPixelColor(i, color2);
          }
          gerade = !gerade;
        }
        if (id != 5) break;
        strip.show();
        delay(200);
        if (LED_COUNT % 2 == 0) {
          gerade = !gerade;
        }
      }
    }
    
    void flashing() {
      strip.clear();
      for (int k = 0; k < 1; k++) {
        for (int i = 0; i < LED_COUNT; i++) {
          if (id != 6) break;
          if (gerade == true) {
            strip.setPixelColor(i, strip.Color(0, 0, 0));
          } else {
            strip.setPixelColor(i, strip.Color(255, 255, 255));
          }
          gerade = !gerade;
        }
        if (id != 6) break;
        strip.show();
        delay(50);
        if (LED_COUNT % 2 == 0) {
          gerade = !gerade;
        }
      }
    }
    
    void randomPixel() {
      for (parameter = 0; parameter < LED_COUNT; parameter++) {
        strip.setPixelColor(parameter, strip.Color(random(255), random(255), random(255)));
        strip.show();
        if (id != 8) break;
        delay(100);
      }
    }
    
    void setup() {
    
      Serial.begin(115200);
    
      strip.begin();
      strip.show();
      strip.setBrightness(5);
    
      if (!SPIFFS.begin()) {
        Serial.println("An Error has occurred while mounting SPIFFS");
        return;
      }
    
      WiFi.mode(WIFI_STA);
      WiFi.begin(ssid, password);
      while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.println("Connecting to WiFi..");
      }
    
    // zeigt IP Adresse vom EPS8266 im Seriellen Monitor an mit welcher man auf den Webserver gelangt
      Serial.println(WiFi.localIP());
    
    // Ermöglicht es updates Over The Air zu machen -> Controller muss nicht angeschlossen sein
      ArduinoOTA.onStart([]() {
        String type;
        if (ArduinoOTA.getCommand() == U_FLASH)
          type = "sketch";
        else // U_SPIFFS
          type = "filesystem";
        // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
        Serial.println("Start updating " + type);
      });
      ArduinoOTA.onEnd([]() {
        Serial.println("\nEnd");
      });
      ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
        Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
      });
      ArduinoOTA.onError([](ota_error_t error) {
        Serial.printf("Error[%u]: ", error);
        if (error == OTA_AUTH_ERROR)
          Serial.println("Auth Failed");
        else if (error == OTA_BEGIN_ERROR)
          Serial.println("Begin Failed");
        else if (error == OTA_CONNECT_ERROR)
          Serial.println("Connect Failed");
        else if (error == OTA_RECEIVE_ERROR)
          Serial.println("Receive Failed");
        else if (error == OTA_END_ERROR)
          Serial.println("End Failed");
      });
      ArduinoOTA.begin();
    
      DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
    
      server.on("/set", HTTP_GET, [](AsyncWebServerRequest * request) {
        int params = request -> params();
    
        for (int i = 0; i < params; i++) {
          AsyncWebParameter * param = request -> getParam(i);
          if (param -> name() == "1red") {
            color1[0] = param -> value().toInt();
          }
          if (param -> name() == "1green") {
            color1[1] = param -> value().toInt();
          }
          if (param -> name() == "1blue") {
            color1[2] = param -> value().toInt();
          }
          if (param -> name() == "2red") {
            color2[0] = param -> value().toInt();
          }
          if (param -> name() == "2green") {
            color2[1] = param -> value().toInt();
          }
          if (param -> name() == "2blue") {
            color2[2] = param -> value().toInt();
          }
          if (param -> name() == "brightness") {
            brightness = param -> value().toInt();
            strip.setBrightness(brightness);
            strip.show();
          }
        }
        request -> send(SPIFFS, "/index.html", String());
      });
    
      // Route for root / web page
      server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
        request -> send(SPIFFS, "/index.html", String());
      });
    
      server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest * request) {
        request -> send(SPIFFS, "/style.css", "text/css");
      });
    
      server.on("/script.js", HTTP_GET, [](AsyncWebServerRequest * request) {
        request -> send(SPIFFS, "/script.js", "text/javascript");
      });
    
      server.on("/data.json", HTTP_GET, [](AsyncWebServerRequest * request) {
        request -> send(SPIFFS, "/data.json", "text/plain");
      });
    
      server.on("/wled_logo.png", HTTP_GET, [](AsyncWebServerRequest * request) {
        request -> send(SPIFFS, "/wled_logo.png", String());
      });
    
      server.on("/JetBrainsMono-Regular.woff", HTTP_GET, [](AsyncWebServerRequest * request) {
        request -> send(SPIFFS, "/JetBrainsMono-Regular.woff", "text/plain");
      });
    
      server.on("/favicon.png", HTTP_GET, [](AsyncWebServerRequest * request) {
        request -> send(SPIFFS, "/favicon.png", String());
      });
    
      server.onNotFound([](AsyncWebServerRequest * request) {
        request->send(404);
      });
    
      server.on("/on", HTTP_GET, [](AsyncWebServerRequest * request) {
        request -> send(SPIFFS, "/index.html", String());
        id = idsafe;
      });
    
      server.on("/off", HTTP_GET, [](AsyncWebServerRequest * request) {
        request -> send(SPIFFS, "/index.html", String());
        if (idsafe == -1) {
          idsafe = 1;
        } else {
          idsafe = id;
        }
        id = 0;
      });
    
      server.on("/solid", HTTP_GET, [](AsyncWebServerRequest * request) {
        request -> send(SPIFFS, "/index.html", String());
        id = 1;
      });
    
      server.on("/rainbow", HTTP_GET, [](AsyncWebServerRequest * request) {
        request -> send(SPIFFS, "/index.html", String());
        id = 2;
      });
    
      server.on("/farbenUebergang", HTTP_GET, [](AsyncWebServerRequest * request) {
        request -> send(SPIFFS, "/index.html", String());
        id = 3;
      });
    
      server.on("/farbenVerfolgung", HTTP_GET, [](AsyncWebServerRequest * request) {
        request -> send(SPIFFS, "/index.html", String());
        id = 4;
      });
    
      server.on("/farbenVerfolgungZweiFarben", HTTP_GET, [](AsyncWebServerRequest * request) {
        request -> send(SPIFFS, "/index.html", String());
        id = 5;
      });
    
      server.on("/flashing", HTTP_GET, [](AsyncWebServerRequest * request) {
        request -> send(SPIFFS, "/index.html", String());
        id = 6;
      });
    
      server.on("/randomFill", HTTP_GET, [](AsyncWebServerRequest * request) {
        request -> send(SPIFFS, "/index.html", String());
        id = 7;
      });
    
      server.on("/randomPixel", HTTP_GET, [](AsyncWebServerRequest * request) {
        request -> send(SPIFFS, "/index.html", String());
        strip.clear();
        parameter = -1;
        id = 8;
      });
    
      // Start Webserver
      server.begin();
    }
    
    void loop() {
      ArduinoOTA.handle();
      switch (id) {
        case 0:
          strip.fill(strip.Color(0, 0, 0), 0, LED_COUNT);
          strip.show();
          delay(10);
          break;
        case 1:
          strip.fill(strip.Color(color1[0], color1[1], color1[2]), 0, LED_COUNT);
          strip.show();
          break;
        case 2:
          rainbow(10);
          break;
        case 3:
          farbenUebergang();
          break;
        case 4:
          farbenVerfolgung();
          break;
        case 5:
          farbenVerfolgungZweiFarben(strip.Color(color1[0], color1[1], color1[2]), strip.Color(color2[0], color2[1], color2[2]));
          break;
        case 6:
          flashing();
          break;
        case 7:
          strip.fill(strip.Color(random(255), random(255), random(255)), 0, LED_COUNT);
          strip.show();
          delay(400);
          break;
        case 8:
          randomPixel();
          break;
      }
    }
    
    

    Hierbei nutze ich die Library von Adafruit um die LED Leiste zu steuern. Alle Methoden über dem void setup() sind dafür da um die LED Streifen zu steuern. Hierbei sind die Effekte, welche dann auf dem Streifen angezeigt, alle selbstgemacht bis auf den in der Raindbow Methode. Hierbei sollte man also nicht zu viel von mir erwarten, da es nur so paar basic Effekte sind.
    In der setup Methode wird dann erst einmal der ganze spaß initialisiert was quasi copy paste aus dem Internet ist.
    Mit server.on kann man bestimmen, was passieren soll, wenn man die IP Adresse im Browser aufruft mit der Endung, welche man in die Anführungszeichen setzt, aufruft. Mit der “/set” Methode werden hierbei die Variablen mit den neuen Werten aus der Javascript file überschrieben. Beispielsweise werden hierbei die rgb Werte der Farbe 1 mittels eines Colorpickers (iro Colorpicker) überschrieben oder die Helligkeit mittels Schiebereglers verändert.
    Die folgende Zeile Code findet man öfters und stellt dar, welche html Datei aufgerufen werden soll, wenn man die unterschiedlichen Webseitenendungen aufruft.

    request -> send(SPIFFS, "/index.html", String());
    

    Beim Aufruf der Webseitenendungen, welche den gleichen Namen haben wie die Methoden der Effekte, wird eine Variable “id” überschrieben. Diese sagt dem ESP8266, welche Methode er in der void loop() Methode aufrufen soll. Dies funktioniert mittels einer Switch Case Abfrage.

    Naja kommen wir mal zur weiteren wichtigen Datei undzwar die Javascript Datei.

    var values = document.getElementById("values");
    var hexInput = document.getElementById("hexInput");
    
    var color1 = document.getElementById("color1");
    var color2 = document.getElementById("color2");
    var color1Wert = "#0099FF";
    var color2Wert = "#" + invertHex(color1Wert);
    
    function invertHex(hex) {
      hex = hex.replace("#", "");
      return (Number(`0x1${hex}`) ^ 0xffffff).toString(16).substr(1).toUpperCase();
    }
    
    // Initialisierung des Colorpickers
    var colorPicker = new iro.ColorPicker(".colorPicker", {
      width: 440,
      color: color1Wert,
      borderWidth: 1,
      borderColor: "#fff",
    });
    
    // colorPicker
    colorPicker.on(["color:init", "input:end"], function (color) {
      hexInput.value = color.hexString;
      farbVeraenderung(color);
    });
    
    hexInput.addEventListener("change", function () {
      colorPicker.color.hexString = this.value;
      var color = colorPicker.color;
      farbVeraenderung(color);
    });
    
    function farbVeraenderung(color) {
      values.innerHTML = [
        "hex: " + color.hexString,
        "rgb: " + color.rgbString,
        "hsl: " + color.hslString,
      ].join("<br>");
    
      if (color1.style.border === "7px solid white") {
        color1.style.background = color.hexString;
        $.get("/set?1red=" + color.red, function (data) {});
        $.get("/set?1green=" + color.green, function (data) {});
        $.get("/set?1blue=" + color.blue, function (data) {});
      } else {
        color2.style.background = color.hexString;
        $.get("/set?2red=" + color.red, function (data) {});
        $.get("/set?2green=" + color.green, function (data) {});
        $.get("/set?2blue=" + color.blue, function (data) {});
      }
    }
    
    // Wechselt die URL ohne Seite reloaden (URL Methode wird dennoch im Sketch aufgerufen)
    function getURL(url) {
      $.get("/" + url, function (data) {});
    }
    
    // Text unter Helligkeitsregler wird geupdated
    function updateTextInput(val) {
      document.getElementById("schiebereglerValue").value = val;
    }
    
    / /Helligkeit verändert
    function updateBrightness(val) {
      $.get("/set?brightness=" + val, function (data) {});
    }
    
    function updateTextColorPicker() {
      values.innerHTML = [
        "hex: " + colorPicker.color.hexString,
        "rgb: " + colorPicker.color.rgbString,
        "hsl: " + colorPicker.color.hslString,
      ].join("<br>");
      hexInput.value = colorPicker.color.hexString;
    }
    
    function swap1() {
      if (color2.style.border === "7px solid white") {
        color2.style.border = "2px solid white";
        color1.style.border = "7px solid white";
        colorPicker.color.rgbString = color1.style.background;
        updateTextColorPicker();
      }
    }
    
    function swap2() {
      if (color1.style.border === "7px solid white") {
        color1.style.border = "2px solid white";
        color2.style.border = "7px solid white";
        colorPicker.color.rgbString = color2.style.background;
        updateTextColorPicker();
      }
    }
    
    // Startup
    $(document).ready(function () {
      color1.style.border = "7px solid white";
      color2.style.border = "2px solid white";
      color1.style.background = color1Wert;
      color2.style.background = color2Wert;
      colorPicker.color.hexString = color2Wert;
      var color = colorPicker.color;
      $.get("/set?2red=" + color.red, function (data) {});
      $.get("/set?2green=" + color.green, function (data) {});
      $.get("/set?2blue=" + color.blue, function (data) {});
      colorPicker.color.hexString = color1Wert;
      color = colorPicker.color;
      $.get("/set?1red=" + color.red, function (data) {});
      $.get("/set?1green=" + color.green, function (data) {});
      $.get("/set?1blue=" + color.blue, function (data) {});
      var val = document.getElementById("schieberegler").value;
      document.getElementById("schiebereglerValue").value = val;
      $.get("/set?brightness=" + val, function (data) {});
      $.get("/off", function (data) {});
      console.log("ready");
    });
    

    Hier sieht man oben wieder Initialisierungen von beispielsweise der Anfangsfarben. Diese können beliebig gewählt werden. In meinem Beispiel kann ich nur die erste Farbe wählen, die zweite wird automatisch erstellt indem es den Gegenwert der ersten Farbe auswählt.
    Bei der Methode farbVeraenderung(color) wird, wenn man entweder den Hexcode im Textfeld ändert oder den Colorpicker bewegt, der Text neben dem Colorpicker verändert, als auch die jeweilige Farbe an den ESP8266 weitergegeben. Dies habe ich mittels jQuery und dessen get Methode gemacht. Dies sorgt dafür, dass die Webseite quasi aufgerufen wird, aber sich die Seite bei dem Benutzer nicht reloaded. Hierbei werden die rgb Werte getrennt übergeben, da dies leichter zum Umsetzen war 🙂 Das Programm weiß, welche Farbe ausgewählt ist, das die ausgewählte Farbe eine weiße Umrandung auf der Webseite hat, welche beim Anklicken der anderen Farbe sich verkleinert und bei der angeklickten Farbe vergrößert.
    Die weiteren Methoden benutzen auch die get Methode um die Helligkeit zu ändern oder aber um die unterschiedlichen Effekte aufzurufen.
    Beim refreshen/erstmaligen Laden des Webservers werden die Standardwerte an den Mikrocontroller übertragen, sodass dieser schon einmal etwas hat, womit er arbeiten kann.

    Hier der Link zu der .zip File https://www.dropbox.com/s/el15nvrszrh2lyg/CT_Projekt_Makesmart.zip?dl=0 (hoffe der klappt)
    Ehm hier sieht man den Webserver vielleicht etwas besser.
    Bild Text

    Das wärs soweit mit meinem Projekt, falls jemand das Ganze genauer erklärt haben möchte schreibt mich gerne auf Discord oder so an. Ich bin auf dem Community Discord von cooper. Ich antworte auch recht schnell und bin oft online.
    Wer bis hier hin gescrollt hat, dem wünsche ich noch einen schönen Tag/Abend.

    ¹Affiliate Link. Affiliate Links sind Referenzen des Autors. Bei Kauf wird eine Provision ausgeschüttet. Mehr Informationen.


  • Man kann ja fast sagen, dass ich seit Beginn an dabei war und dein Projekt mitverfolgt hab, … und ich muss sagen: das Ergebnis ist echt nice geworden! :programmingparrot: Also was du da gezaubert hast ist echt wahnsinn!

    Das hat mich aufjeden Fall inspiriert und ich würde das ganze gerne in etwas größer bauen an die Wand, oder in einen Tisch oder ähnliches. 🙉 Mal sehen. Finds auch echt hammer, dass du so viel Material zur Verfügung stellst. 🙂

    Wäre definitiv auch noch was für #WS2812!
    Mach weiter so, bin gespannt was du noch so alles tolles zauberst :pepenote:


  • Hallöchen aus Wien,
    Danke mal für die Guten Ideen, ich bin auf der Suche nach einer Steuerung für was Größeres 😉
    Und habe Deine Schaltung geklaut und nachgebaut…
    Nach vielen Problemen mit dem ES8266 unter Windows konnte ich die Webseite auch installieren und jetzt bin ich mal wieder am Verzweifeln 😉
    Die Webpage wird aufgerufen, und ich läuft auch (ich habe mal Debug ausgaben auf manche Menüitems drangehängt)
    Kann es sein das auf Deiner Seite was nicht zusammenpasst?
    Im Code Steht
    #define LED_PIN 2
    Soweit ich das Fritzing nachgebaut habe, ist dort aber D4 als Ausgang angeschaltet…
    Das passt nicht…
    Gehört hier
    #define LED_PIN 4 ???

    Dann schaltet sich bei mir zumindest die LEDs ein, Farbwechsel etc funktioniert aber immer noch nicht, ich fürchte die 3.3 V des Rechners sind zu wenig für das Steuersignal…
    LG
    Christof


  • @chris Pin D4 auf dem D1 Mini ist GPIO 2 vom ESP8266

    Der D1 Mini mit seinen 3,3V Betriebsspannung müsste eigentlich den WS2812 Streifen ohne Probleme antreiben können :heh:


  • @chris Moin aus Baden-Württemberg,

    das ganze liegt daran, dass die Beschriftung auf dem ESP8266 nicht mit der GPIO Beschriftung übereinstimmt. Das ganze kann man im Datenblatt des Controllers nachlesen.
    Hinter dem D4 Port steckt in Wirklichkeit der GPIO2. Deshalb auch die 2 anstelle der 4.Bild Text
    Hoffe das wird mittels dieses Bildes klar.

    LG
    Jonas

Ähnliche Themen