This is an old revision of the document!
Einrichten
- Nur Windows: Installation eines ssh-clients (z.B. putty)
- Passwort ändern:
- Sich per ssh verbinden, das Kommand
passwd
eingeben und bestätigen (es wird bei der Eingabe der Passwörter gar nichts angezeigt). - (Mac, Linux, direkt auf der Konsole: ssh -p 40199 user.name@tech-lab.ch, Windows putty oder anderer SSH-client installieren (sollte jetzt auch native gehen, habe ich gehört)).
- FileZilla installieren (auf Linux und Mac nicht nötig, aber möglich).
- Datei index.html umbenennen, z.B. in start.html (enthält ihren Namen)
- z.B. mit Filezilla oder auf der Konsole mit
mv index.html start.html
- Datei .htaccess umbennen oder löschen, damit Ihre Webseite von ausserhalb der KSBG erreichbar wird. Das kann via Konsole (ssh) oder mit Filezilla erfolgen.
- z.B. mit Filezilla oder auf der Konsole mit
mv .htaccess .htaccess.bak
Erste Webseite
Text-Editor, entweder lokal, oder direkt auf dem Server mit nano
oder mit vim
.
Erste html-Datei mit Namen index.html erstellen (und auf den Server laden)
<!DOCTYPE html> <html> <body> <h1>Titel</h1> Text. </body> </html>
Ihre Seite sollte dann online unter https://ofi.tech-lab.ch/2021/FF361/ in Ihrem Ordner sichtbar sein.
Mein Christbaum hat ein Web-Interface
- ESP32 mit WS2812b LED-Streifen
- Webserver auf dem ESP (Dateien unten)
- OTA-Update-Möglichkeit (Over the air, d.h. direkt via Wireless)
- index.html
<!DOCTYPE html> <html> <head> <title>Xmas Tree Web Server</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" href="data:,"> <link rel="stylesheet" type="text/css" href="style.css"> </head> <body> <script> function setProgram() { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { // Typical action to be performed when the document is ready: document.getElementById("status").innerHTML = xhttp.responseText; } }; var url = '/prg'; var sep = "?"; for (const [key, value] of Object.entries(state)) { url+=sep+key+"="+value sep="&"; } console.log(url); xhttp.open("GET", url, true); xhttp.send(); } state = { 'nr':%NR%, 'brightness':%BRIGHTNESS%, 'speed':%SPEED%, 'color1':'%COLOR1%', 'color2':'%COLOR2%' }; function mod(a,b) { return a - Math.floor(a/b)*b; } function makeColor(img, event, name) { var h = (event.clientX-img.offsetLeft)/400; console.log(h); var x = (1-Math.abs(mod(h*6,2) - 1)); rgb = [0,0,0]; if (h<1/6) rgb = [1,x,0]; else if (h<2/6) rgb = [x,1,0]; else if (h<3/6) rgb = [0,1,x]; else if (h<4/6) rgb = [0,x,1]; else if (h<5/6) rgb = [x,0,1]; else rgb = [1,0,x]; v = Math.floor(rgb[2]*255)+256*(Math.floor(rgb[1]*255)+256*Math.floor(rgb[0]*255)); s = v.toString(16); while (s.length<6) { s = "0"+s } console.log(s); state[name] = s; document.getElementById(name).value = "#"+s; setProgram(); } </script> <h1>Xmas Tree Web Server</h1> <!-- <p>Active Programm: <strong> %STATE%</strong></p> --> <p><button class="button" onclick="state['nr']=0;setProgram();">Rainbow</button></p> <p><button class="button" onclick="state['nr']=1;setProgram();">Running</button></p> <p><button class="button" onclick="state['nr']=2;setProgram();">Sparkling</button></p> <p><button class="button" onclick="state['nr']=3;setProgram();">Fading</button></p> <div><input type="range" id="brightness" name="brightness" min="0" max="255" value="%BRIGHTNESS%" onchange="state['brightness']=this.value;setProgram();"> <label for="brightness">Brightness</label> </div> <div><input type="range" id="speed" name="speed" min="0" max="255" value="%SPEED%" onchange="state['speed']=this.value;setProgram();"> <label for="brightness">Speed</label> </div> <div> <img src="hsv400.png" width="400px" height="50px" onclick="makeColor(this, event, 'color1');"> </div> <div> <img src="hsv400.png" width="400px" height="50px" onclick="makeColor(this, event, 'color2');"> </div> <div> <input type="color" id="color1" name="color1" value="#%COLOR1%" onchange="state['color1']=this.value.substr(1);setProgram();"> <label for="color1">Color 1</label> <input type="color" id="color2" name="color2" value="#%COLOR2%" onchange="state['color2']=this.value.substr(1);setProgram();"> <label for="color2">Color 2</label> </div> <div class="status" id="status"></div> </body> </html>
- style.css
html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center; background-color: black; color: #aabbaa; } h1{ color: #aaaaff; padding: 2vh; } p{ font-size: 1.5rem; } .button { display: inline-block; background-color: #008CBA; border: none; border-radius: 4px; color: white; padding: 10px 40px; text-decoration: none; font-size: 20px; margin: -2px; cursor: pointer; } .button2 { background-color: #f44336; }
- xmastree.ino
/********* Rui Santos Complete project details at https://randomnerdtutorials.com https://randomnerdtutorials.com/esp32-web-server-spiffs-spi-flash-file-system/ Zusätzliche Libraries für diesen Sketch: https://github.com/me-no-dev/AsyncTCP/archive/master.zip https://github.com/me-no-dev/ESPAsyncWebServer/archive/master.zip Entpacken, umbenennen (ohne -master) und nach ~/Arduino/libraries/. speichern. Zusätzliche Tools für diesen Sketch: https://github.com/me-no-dev/arduino-esp32fs-plugin/releases/tag/1.0 https://github.com/me-no-dev/arduino-esp32fs-plugin/releases/download/1.0/ESP32FS-1.0.zip In den Ordner ~/Arudino/tools/. entpacken (den Ordner tools anlegen, falls nötig). *********/ // Import required libraries #include "WiFi.h" #include "ESPAsyncWebServer.h" #include "SPIFFS.h" #include <ESPmDNS.h> #include <esp_wifi.h> // To set MAC-Adress (to avoid new MAC-Adresses every time). // OTA #include <WiFiUdp.h> #include <ArduinoOTA.h> // Replace with your network credentials const char* ssid = "tech-lab"; const char* password = "rech-lab"; const char* hostname = "baum"; #include <Adafruit_NeoPixel.h> #define PIN 26 #define NUMPIXEL 113 Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXEL, PIN, NEO_GRB + NEO_KHZ800); // Create AsyncWebServer object on port 80 AsyncWebServer server(80); int activeProgram = 0; int newProgram = 2; int numPrograms = 4; int brightness = 255; int speed = 35; uint32_t color1 = 0xff0000; uint32_t color2 = 0x0000ff; // Replaces placeholder with LED state value String processor(const String& var){ Serial.println(var); if(var == "STATE"){ return String(activeProgram); } if (var == "BRIGHTNESS") { return String(brightness); } if (var == "SPEED") { return String(speed); } if (var == "NR") { return String(activeProgram); } if (var == "COLOR1") { char buf[20]; sprintf(buf, "%06x", color1); return String(buf); } if (var == "COLOR2") { char buf[20]; sprintf(buf, "%06x", color2); return String(buf); } return String(); } void setup(){ // Serial port for debugging purposes Serial.begin(115200); // Initialize SPIFFS if(!SPIFFS.begin(true)){ Serial.println("An Error has occurred while mounting SPIFFS"); return; } // Connect to Wi-Fi WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); Serial.print("Connecting to WiFi "); Serial.println(ssid); while (WiFi.waitForConnectResult() != WL_CONNECTED) { Serial.println("Connection Failed! Rebooting..."); delay(2000); ESP.restart(); } // Print ESP32 Local IP Address Serial.println(WiFi.localIP()); ArduinoOTA.setPort(3232); ArduinoOTA.setHostname(hostname); ArduinoOTA .onStart([]() { String type; if (ArduinoOTA.getCommand() == U_FLASH) type = "sketch"; else {// U_SPIFFS type = "filesystem"; SPIFFS.end(); } Serial.println("Start updating " + type); }) .onEnd([]() { Serial.println("\nEnd"); }) .onProgress([](unsigned int progress, unsigned int total) { Serial.printf("Progress: %u%%\r", (progress / (total / 100))); }) .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(); // Set up Hostname if(!MDNS.begin(hostname)) { Serial.println("Error starting mDNS"); } else { Serial.print("MDNS started with name "); Serial.println(hostname); // nicht sicher, ob die folgende Zeile nötig ist, macht aber Sinn... // mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0); MDNS.addService("_http", "_tcp", 80); } // Route for root / web page server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ request->send(SPIFFS, "/index.html", String(), false, processor); }); // Route to load style.css file server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){ request->send(SPIFFS, "/style.css", "text/css"); }); // Route to load hsv400.png file server.on("/hsv400.png", HTTP_GET, [](AsyncWebServerRequest *request){ request->send(SPIFFS, "/hsv400.png", "image/png"); }); // Route to load favicon.ico file server.on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest *request){ request->send(SPIFFS, "/favicon.ico", "image/x-icon"); }); // Route to set Programms server.on("/prg", HTTP_GET, [](AsyncWebServerRequest *request){ String status = String("Status: "); if(request->hasParam("brightness")) { int b = atoi(request->getParam("brightness")->value().c_str()); b = (b<0 ? 0 : (b>255 ? 255 : b)); if (b!=brightness) { brightness = b; pixels.setBrightness(brightness); Serial.printf("Brightness=%d\n", brightness); status += "Brightness="; status += brightness; status += " "; } } if(request->hasParam("speed")) { int s = atoi(request->getParam("speed")->value().c_str()); s = (s<0 ? 0 : (s>255 ? 255 : s)); if (s!=speed) { speed = s; Serial.printf("Speed=%d\n", speed); status+="speed="; status+=speed; status+=" "; } } if(request->hasParam("color1")) { int c1 = strtol(request->getParam("color1")->value().c_str(), NULL, 16); if (c1!=color1) { color1=c1; Serial.printf("Color1=%06x\n", color1); char buf[20]; sprintf(buf, "%06x", color1); status+="color1="; status+=buf; status+=" "; } } if(request->hasParam("color2")) { int c2 = strtol(request->getParam("color2")->value().c_str(), NULL, 16); if (c2!=color2) { color2=c2; Serial.printf("Color2=%06x\n", color2); char buf[20]; sprintf(buf, "%06x", color2); status+="color2="; status+=buf; status+=" "; } } if(request->hasParam("nr")) { int p = atoi(request->getParam("nr")->value().c_str()); p = (p<0) ? 0 : (p>=numPrograms ? numPrograms-1 : p); if (p!=activeProgram) { newProgram = p; status += " program="; status += newProgram; } } request->send(200, "text/plain", status); }); // Start server server.begin(); Serial.println("WebServer up and running!"); } float fmap(float v, float fromMin, float fromMax, float toMin, float toMax) { return (v-fromMin)*(toMax-toMin)/(fromMax-fromMin)+toMin; } void cycleHSV(unsigned int &counter) { float f = (speed>100) ? fmap(speed, 100,255,2,0.2) : fmap(speed, 0,100,10,2); if (counter>=f*NUMPIXEL) { counter = 0; } for (int i=0; i<NUMPIXEL; i++) { pixels.setPixelColor(i, pixels.ColorHSV( ((int)((i+counter/f)*65535/NUMPIXEL))%0xffff )); } pixels.show(); } void stars(unsigned int &counter) { float f = (speed>100) ? fmap(speed, 100,255, 1,10) : fmap(speed, 0,100, 0.2, 1); if (counter>=1000/f) { counter = 0; } for (int i=0; i<NUMPIXEL; i++) { float v = 0.1*abs(10-abs(i%20 - counter*0.02*f)); v = 4*v-3.0; if (v<0) v=0.0; /* if (i==0) { Serial.printf("counter=%d, v=%f -> v=%d\n", counter, v, (int)(v*v*255)); } */ v = pow(v,8); int r = (int)(v * ((color1 >> 16) & 255)); int g = (int)(v * ((color1 >> 8) & 255)); int b = (int)(v * (color1 & 255)); /* if (i==0 && r!=0) { Serial.printf("r=%d, g=%d, b=%d, color1=%06x, v=%f\n", r,g,b, color1, v); } */ pixels.setPixelColor(i, pixels.Color(r,g,b)); } pixels.show(); } int mix(float f) { int r = 0; for (int i=0; i<3; i++) { int c1 = color1 & (0xff << (8*i)); int c2 = color2 & (0xff << (8*i)); int c3 = (1.0-f)*c1+f*c2; r += c3 & (0xff<<(8*i)); } //Serial.printf("mix %f of color1=%06x and color2=%06x results in %06x\n", f, color1, color2, r); return r; } int scale(float f, int color) { int r = 0; for (int i=0; i<3; i++) { r |= ((int)(f*(color & (0xff<<(8*i))))) & (0xff<<(8*i)); } return r; } #define NUMFUNK 20 void funkeln(int counter) { static int pos[NUMFUNK]; static int colors[NUMFUNK]; static int count[NUMFUNK]; static int len[NUMFUNK]; static bool start=true; if (counter==0) { for (int i=0; i<NUMPIXEL; i++) { pixels.setPixelColor(i,0); } } if (start) { start = false; // Delay to connect to wifi will be random enough randomSeed(micros()); for (int i=0; i<NUMFUNK; i++) { pos[i] = random(NUMPIXEL); colors[i] = mix(random(10001)/10000.0); //Serial.printf("colors[%d]=%06x\n",i,colors[i]); count[i] = 0; len[i] = (70+random(60))*(speed+20)/120.0; } } else { for (int i=0; i<NUMFUNK; i++) { count[i]++; float t = ((float)count[i])/len[i]; t = t<0.5 ? 2.0*t : 2.0-2*t; t = pow(t,4); int c = scale(t,colors[i]); /*if (i==0) { Serial.printf("count=%d, len=%d, t=%f, colors[0]=%06x, c=%06x\n", count[i], len[i], t, colors[i], c); }*/ pixels.setPixelColor(pos[i], c); if (count[i]>=len[i]) { pos[i] = random(NUMPIXEL); colors[i] = mix(random(10001)/10000.0); count[i] = 0; len[i] = (70+random(60))*(speed+20)/120.0; } } pixels.show(); } } void fade(unsigned int &counter) { if (counter>=speed*5+20) { counter=0; } float t = ((float)(counter))/(speed*5+20); t = cos(t*2*PI)/2+0.5; int c = mix(t); for (int i=0; i<NUMPIXEL; i++) { pixels.setPixelColor(i,c); } pixels.show(); } unsigned int counter = 0; unsigned long nextStep = millis(); void loop() { ArduinoOTA.handle(); // init if (newProgram!=activeProgram) { activeProgram = newProgram; nextStep = millis(); counter = 0; } if (millis()>nextStep) { switch(activeProgram) { case 0: cycleHSV(counter); nextStep += 40; // 25fps break; case 1: stars(counter); nextStep += 40; // 25fps break; case 2: funkeln(counter); nextStep += 40; // 25fps break; case 3: fade(counter); nextStep += 40; // 25fps break; } counter++; } }