lehrkraefte:blc:informatik:ffprg2-2020:webdev

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/

  • 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:

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 += " &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++;
  }
}
  • lehrkraefte/blc/informatik/ffprg2-2020/webdev.txt
  • Last modified: 2021/01/13 11:35
  • by Ivo Blöchliger