Basic JavaScript
Ein Crash-Course in 4 html-Dateien, siehe https://ofi.tech-lab.ch/2021/teach/d0571f1e/javascript-basics/
Bei Nachfrage oder konkreten Fragen liefere ich gerne mehr und konkrete Beispiele.
Une ein JavaScript-Spielerei von mir: https://glf.tech-lab.ch/teach/deadbeef/javascript/mancala/proto/mancala.html, oder ein Anfang von Tic-Tac-Toe, zum selber ausbauen: https://glf.tech-lab.ch/teach/deadbeef/javascript/tictactoe-generated/
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.
Rousourcen für Web-Entwicklung:
- Und natürlich Google, Stackoverflow etc…
- und ja, nur weil es in einem Browser läuft, heisst noch nicht, dass es in anderen Browsern auch läuft… Aber Explorer ist ja nicht mehr so sehr im Gebrauch.
Mein Christbaum hat ein Web-Interface
- ESP32 mit WS2812b LED-Streifen
- Webserver auf dem ESP (erreichbar im meinem Heimnetzwerk unter http://baum/)
- OTA-Update-Möglichkeit (Over the air, d.h. direkt via Wireless, kein Gefummel mit Laptop und USB-Kabel unter dem Christbaum)
- 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++; } }