lehrkraefte:blc:informatik:ffprg2-2020:webdev

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
lehrkraefte:blc:informatik:ffprg2-2020:webdev [2021/01/07 13:44]
Ivo Blöchliger created
lehrkraefte:blc:informatik:ffprg2-2020:webdev [2021/01/13 11:35] (current)
Ivo Blöchliger [Basic JavaScript]
Line 1: Line 1:
-=== Einrichten ===+===== 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. [[https://www.putty.org/|putty]])   * **Nur Windows**: Installation eines ssh-clients (z.B. [[https://www.putty.org/|putty]])
   * Passwort ändern:   * Passwort ändern:
Line 25: Line 33:
 Ihre Seite sollte dann online unter https://ofi.tech-lab.ch/2021/FF361/ in Ihrem Ordner sichtbar sein. Ihre Seite sollte dann online unter https://ofi.tech-lab.ch/2021/FF361/ in Ihrem Ordner sichtbar sein.
  
 +Rousourcen für Web-Entwicklung:
 +  * https://wiki.selfhtml.org/
 +  * https://www.w3schools.com/
 +  * https://developer.mozilla.org/en-US/docs/Web
 +  * 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)
 +
 +{{ :lehrkraefte:blc:informatik:ffprg2-2020:screenshot_20201224-105441.png?direct&200 |}}
 +
 +<code html 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>
 +
 +</code>
 +
 +<code css 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;
 +}
 +</code>
 +
 +<code c++ 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 += " &nbsp; ";
 +      }
 +    }
 +    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+=" &nbsp; ";
 +      }
 +    }
 +    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+=" &nbsp; ";
 +      }
 +    }
 +    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+=" &nbsp; ";
 +      }
 +    }
 +    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++;
 +  }
 +}
 +</code>
  • lehrkraefte/blc/informatik/ffprg2-2020/webdev.1610023470.txt.gz
  • Last modified: 2021/01/07 13:44
  • by Ivo Blöchliger