Introduction: EPS8266 Webserver Group LED Control UI + Read & Write Files + Show ESP Chip Infos + DHT22 + PWM + I2C + JSON

About: esp8266 projects

Im letzten 4. Teil können die PWM Werte in einer Gruppe eingestellt werden, die Speicherung aller PWM Werte (Preset) erfolgt in einer Datei und die Anzeige von Temperatur und Luftfeuchtigkeit sind als neue Features hinzugekommen. Zusätzlich kann der I2C Bus neu gestartet und die ESP Chip Infos ausgelesen werden.

Für eine bessere Übersicht der Verarbeitung von einzelnen Benutzereingaben gibt es am Ende einen Flowchart. Darin kann der „Klick-Weg“ von der Weboberfläche zu der Verarbeitung im ESP und die Rückmeldung in die Weboberfläche leicht nachvollzogen werden.

In den vorigen Teilen ist eine Weboberfläche für die Steuerung aller 16 LED mit einer Visualisierung über einen Raumplan entstanden. (Part 2) Diese wurde mit zusätzlichen Features wie z.B. dimmen einzelner LEDs (Part 3) und anzeigen verschiedener Systeminfos schrittweise erweitert.




Step 1: Erwiterungen in CaptivePortalAdvanced.ino

+ server.on("/LEDpart", handleLEDpart);

+ server.on("/LEDdata", handleLEDdata);

+ server.on("/statusDHT", handleStatusDHT);

+ server.on("/i2cRST", handleRstI2C);

+ server.on("/status", handleStatusESP);

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <DNSServer.h>
#include <ESP8266mDNS.h>
#include <EEPROM.h>
#include <Wire.h>

/*
   This example serves a "hello world" on a WLAN and a SoftAP at the same time.
   The SoftAP allow you to configure WLAN parameters at run time. They are not setup in the sketch but saved on EEPROM.

   Connect your computer or cell phone to wifi network ESP_ap with password 12345678. A popup may appear and it allow you to go to WLAN config. If it does not then navigate to  http://192.168.4.1/wifi  and config it there.
   Then wait for the module to connect to your wifi and take note of the WLAN IP it got. Then you can disconnect from ESP_ap and return to your regular WLAN.

   Now the ESP8266 is in your network. You can reach it through  http://192.168.4.1/wifi  (the IP you took note of) or maybe at  http://192.168.4.1/wifi  too.

   This is a captive portal because through the softAP it will redirect any http request to  http://192.168.4.1/wifi 
*/

//////////////////////////// PINs /////////////////////////////////

// LED
#define BLINKPIN D8       // Status LED indicates a wifi / user interaction 
#define BLINKPIN_TOGGLE() (digitalWrite(BLINKPIN, !digitalRead(BLINKPIN)))   
#define BLINKPIN_ON()     (digitalWrite(BLINKPIN, HIGH))
#define BLINKPIN_OFF()    (digitalWrite(BLINKPIN, LOW))

//I2C TLC59116
#define TLC59116_SCL D1
#define TLC59116_SDA D2 

//////////////////////////// DHT /////////////////////////////////
#include "DHT.h"
#define DHTPIN D4       // what digital pin we're connected to
#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321
DHT dht(DHTPIN, DHTTYPE);

  float Humidity = 0;
  float Temperature = 0;

  
//////////////////////////// TLC59116  /////////////////////////////////
// Demo für I2C-LED-Treiber mit TLC59116
// ELV-AG Leer
// letzte Änderung 21.4.2011
// I2C -Adresse = 0C hex (alle Jumper A0 bis A3 offen)
// Hinweis zur Slaveadresse:
// Da bei der "Wire"-Funktion entfällt das letzte Bit, das für Lesen oder Schreiben (R/W) steht.
// Die Adresse hat somit nur noch 7 Bit, und sieht am Beispiel von C0hex so aus : 1100000b (60hex)
// 8Bit : 11000000 = C0h
// 7Bit : 1100000  = 60h

#define BRIGHTNESS 32  // init max. 255
    int BRIGHTNESS_var = 8;
    int BRIGHTNESS_Array[16]={64,64,64,64, 64,64,64,64, 64,64,64,64, 64,64,64,64}; 
 String BRIGHTNESS_string = "NULL";  //html <=> js

//////////////////////////// SPIFFS / fsUploadFile /////////////////////////////////

#include <FS.h>  
    unsigned long Speicherbelegung = 0;
    File fsUploadFile;              // a File object to temporarily store the received file
    String getContentType(String filename); // convert the file extension to the MIME type
    bool handleFileRead(String path);       // send the right file to the client (if it exists)
    void handleFileUpload();                // upload a new file to the SPIFFS

    String Page404upload = "<html><head></head><body><form method='post' enctype='multipart/form-data'><input type='file' name='name'><input class='button' type='submit' value='Upload'></form></body></html>";    



/* Set these to your desired softAP credentials. They are not configurable at runtime */
#ifndef APSSID
#define APSSID "ESP_LED_TLC59116"
#define APPSK  "12345678"
#endif

const char *softAP_ssid = APSSID;
const char *softAP_password = APPSK;

/* hostname for mDNS. Should work at least on windows. Try  http://192.168.4.1/wifi  */
const char *myHostname = "ESP_LED";  //esp8266

/* Don't set this wifi credentials. They are configurated at runtime and stored on EEPROM */
char ssid[32] = "";
char password[32] = "";

// DNS server
const byte DNS_PORT = 53;
DNSServer dnsServer;

// Web server
ESP8266WebServer server(80);

/* Soft AP network parameters */
IPAddress apIP(172, 217, 28, 1);
IPAddress netMsk(255, 255, 255, 0);


/** Should I connect to WLAN asap? */
boolean connect;

/** Last time I tried to connect to WLAN */
unsigned long lastConnectTry = 0;

/** Current WLAN status */
unsigned int status = WL_IDLE_STATUS;

void setup() {
  
  pinMode(BLINKPIN, OUTPUT);  // Set pin as output
  BLINKPIN_ON();
  
  delay(1000);
  Serial.begin(9600);
  Serial.println();
  Serial.println("Configuring access point...");
  /* You can remove the password parameter if you want the AP to be open. */
  WiFi.softAPConfig(apIP, apIP, netMsk);
  WiFi.softAP(softAP_ssid, softAP_password);
  delay(500); // Without delay I've seen the IP address blank
  Serial.print("AP IP address: ");
  Serial.println(WiFi.softAPIP());

  /* Setup the DNS server redirecting all the domains to the apIP */
  dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
  dnsServer.start(DNS_PORT, "*", apIP);

  /*  Init SPIFFS  */
  if (!SPIFFS.begin()) { 
    Serial.println("SPIFFS nicht initialisiert! Stop!");
    while (1) yield();
  }
  else {
      Dir dir = SPIFFS.openDir("/");
    while (dir.next()) {
      String fileName = dir.fileName();
      size_t fileSize = dir.fileSize();
      Speicherbelegung += fileSize;
      Serial.printf("FS File: %s, size: %s\n", fileName.c_str(), formatBytes(fileSize).c_str());
    }
    Serial.printf("\n");
    Serial.printf("Speicherbelegung:  %s\n", formatBytes(Speicherbelegung).c_str());
    Serial.printf("\n");
  }

     server.serveStatic("/",    SPIFFS, "/index.html");
     server.serveStatic("/img", SPIFFS, "/img");
     server.serveStatic("/js",  SPIFFS, "/js");
     server.serveStatic("/css", SPIFFS, "/css");  
     server.serveStatic("/dat", SPIFFS, "/data");

  /* Setup web page: file uploads */ 
  server.on("/upload", HTTP_GET, []() {                 // if the client requests the upload page
    if (!handleFileRead("/upload.html"))                // send it if it exists        
     server.send(404, "text/html", Page404upload);  
  }
  );

  server.on("/upload", HTTP_POST,                       // if the client posts to the upload page
    [](){ server.send(200); },                          // Send status 200 (OK) to tell the client we are ready to receive
    handleFileUpload                                    // Receive and save the file
  );

  /* Setup web pages: LED on/off + js LED status + LED PWM*/ 
  server.on("/readLED",   handleStatusLED);
  server.on("/LEDswitch", handleLED_switch);
  server.on("/LEDpwm",    handleLED_pwm);
  
  server.on("/LEDpart",   handleLEDpart);
  server.on("/LEDdata",   handleLEDdata);    

  /* Setup web pages: DHT info */  
  server.on("/statusDHT", handleStatusDHT);
    
  /* Setup web pages: I2C info on COM port */
  server.on("/i2c", handleStatusI2C);
  server.on("/i2cRST", handleRstI2C);

  /* Setup web pages: Reboot ESP*/
  server.on("/reboot", handleReboot);
  server.on("/status", handleStatusESP);
      
  /* Setup web pages: root, wifi config pages, SO captive portal detectors and not found. */
  //server.on("/", handleRoot); //old
  server.on("/",HTTP_GET,handleRoot);   // Files SPIFFS  
  server.on("/wifi", handleWifi);
  server.on("/wifisave", handleWifiSave);
  server.on("/generate_204", handleRoot);  //Android captive portal. Maybe not needed. Might be handled by notFound handler.
  server.on("/fwlink", handleRoot);  //Microsoft captive portal. Maybe not needed. Might be handled by notFound handler.
  server.onNotFound(handleNotFound);
  server.begin(); // Web server start
  Serial.println("HTTP server started");
  loadCredentials(); // Load WLAN credentials from network
  connect = strlen(ssid) > 0; // Request WLAN connect if there is a SSID

  /* start DHT sensor */ 
  dht.begin();
  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
   Humidity = dht.readHumidity();
  // Read temperature as Celsius (the default)
   Temperature = dht.readTemperature();
  // Read temperature as Fahrenheit (isFahrenheit = true)
  //float f = dht.readTemperature(true);

   // Check if any reads failed and exit early (to try again).
  if (isnan(Humidity) || isnan(Temperature)) {
    Serial.println("Failed to read from DHT sensor!");
    return;
  }

  /* start TLC59116 LED + set PWM*/
  init_TLC59116();
  
  //Set_LED_ALL(BRIGHTNESS);  // 0-255 max
  readDatafile();  // read preset data from file
  Set_LED_ALL_Array(); 


   
  BLINKPIN_OFF();  
}

void connectWifi() {
  Serial.println("Connecting as wifi client...");
  WiFi.disconnect();
  WiFi.begin(ssid, password);
  int connRes = WiFi.waitForConnectResult();
  Serial.print("connRes: ");
  Serial.println(connRes);
}

void loop() {
  if (connect) {
    Serial.println("Connect requested");
    connect = false;
    connectWifi();
    lastConnectTry = millis();
  }
  {
    unsigned int s = WiFi.status();
    if (s == 0 && millis() > (lastConnectTry + 60000)) {
      /* If WLAN disconnected and idle try to connect */
      /* Don't set retry time too low as retry interfere the softAP operation */
      connect = true;
    }
    if (status != s) { // WLAN status change
      Serial.print("Status: ");
      Serial.println(s);
      status = s;
      if (s == WL_CONNECTED) {
        /* Just connected to WLAN */
        Serial.println("");
        Serial.print("Connected to ");
        Serial.println(ssid);
        Serial.print("IP address: ");
        Serial.println(WiFi.localIP());

        // Setup MDNS responder
        if (!MDNS.begin(myHostname)) {
          Serial.println("Error setting up MDNS responder!");
        } else {
          Serial.println("mDNS responder started");
          // Add service to MDNS-SD
          MDNS.addService("http", "tcp", 80);
        }
      } else if (s == WL_NO_SSID_AVAIL) {
        WiFi.disconnect();
      }
    }
    if (s == WL_CONNECTED) {
      MDNS.update();
    }
  }
  // Do work:
  //DNS
  dnsServer.processNextRequest();
  //HTTP
  server.handleClient();
}

Step 2: Erwiterungen in Credentials.ino

keine

/** Load WLAN credentials from EEPROM */
void loadCredentials() {
  EEPROM.begin(512);
  EEPROM.get(0, ssid);
  EEPROM.get(0 + sizeof(ssid), password);
  char ok[2 + 1];
  EEPROM.get(0 + sizeof(ssid) + sizeof(password), ok);
  EEPROM.end();
  if (String(ok) != String("OK")) {
    ssid[0] = 0;
    password[0] = 0;
  }
  Serial.println("Recovered credentials:");
  Serial.println(ssid);
  Serial.println(strlen(password) > 0 ? "********" : "");
}

/** Store WLAN credentials to EEPROM */
void saveCredentials() {
  EEPROM.begin(512);
  EEPROM.put(0, ssid);
  EEPROM.put(0 + sizeof(ssid), password);
  char ok[2 + 1] = "OK";
  EEPROM.put(0 + sizeof(ssid) + sizeof(password), ok);
  EEPROM.commit();
  EEPROM.end();
}

Step 3: Erwiterungen in HandleHttp.ino

+ void handleLEDpart()

+ void handleLEDdata()

+ void handleStatusDHT()

+ void handleRstI2C()

+ void handleStatusESP()

/** Handle root or redirect to captive portal */
//void handleRoot() {
//  if (captivePortal()) { // If caprive portal redirect instead of displaying the page.
//    return;
//  }
//  server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
//  server.sendHeader("Pragma", "no-cache");
//  server.sendHeader("Expires", "-1");
//
//  String Page;
//  Page += F(
//            "<html><head></head><body>"
//            "<h1>HELLO WORLD!!</h1>");
//  if (server.client().localIP() == apIP) {
//    Page += String(F("<p>You are connected through the soft AP: ")) + softAP_ssid + F("</p>");
//  } else {
//    Page += String(F("<p>You are connected through the wifi network: ")) + ssid + F("</p>");
//  }
///** Handle LED Control*/
//
//  Page +=
//    String(F(
//             "\r\n<br />"
//             "<table><tr><th align='left'>LED Control</th></tr>"
//             "<tr><td>"));
//  Page += String(F("\r\n<tr><td><a href='/LEDswitch'> all LED </a></td></tr>")); 
//            
//    for (int i = 1; i < 17; i++) {
//      Page += String(F("\r\n<tr><td><a href='/LEDswitch?LED=")) + i + (F("'>LED")) + i + (F("</a></td></tr>"));
//    }
//
//    Page +=
//    String(F(
//      "</td></tr>"
//      "</table>"
//      "\r\n<br />"));
//
//  Page += F(
//            "<p>You may want to test the <a href='/i2c'>I2C status</a>. (COM port)</p>");
//              
//  Page += F(
//            "<p>You may want to <a href='/wifi'>config the wifi connection</a>.</p>"
//            "</body></html>");
//
//  server.send(200, "text/html", Page);
//}

/** Handle root or redirect to captive portal (Files SPIFFS)*/
void handleRoot() {
  Serial.printf("handleRoot");
  if (!handleFileRead(server.uri()))  server.send(404, "text/plain", "FileNotFound");
}

/** Redirect to captive portal if we got a request for another domain. Return true in that case so the page handler do not try to handle the request again. */
boolean captivePortal() {
  if (!isIp(server.hostHeader()) && server.hostHeader() != (String(myHostname) + ".local")) {
   // Serial.println("Request redirected to captive portal");
    server.sendHeader("Location", String("http://") + toStringIp(server.client().localIP()), true);
    server.send(302, "text/plain", "");   // Empty content inhibits Content-length header so we have to close the socket ourselves.
    server.client().stop(); // Stop is needed because we sent no content length
    return true;
  }
  return false;
}

/** Wifi config page handler */
void handleWifi() {
  server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
  server.sendHeader("Pragma", "no-cache");
  server.sendHeader("Expires", "-1");

  String Page;
  Page += F(
            "<html><head></head><body>"
            "<h1>Wifi config</h1>");
  if (server.client().localIP() == apIP) {
    Page += String(F("<p>You are connected through the soft AP: ")) + softAP_ssid + F("</p>");
  } else {
    Page += String(F("<p>You are connected through the wifi network: ")) + ssid + F("</p>");
  }
  Page +=
    String(F(
             "\r\n<br />"
             "<table><tr><th align='left'>SoftAP config</th></tr>"
             "<tr><td>SSID ")) +
    String(softAP_ssid) +
    F("</td></tr>"
      "<tr><td>IP ") +
    toStringIp(WiFi.softAPIP()) +
    F("</td></tr>"
      "</table>"
      "\r\n<br />"
      "<table><tr><th align='left'>WLAN config</th></tr>"
      "<tr><td>SSID ") +
    String(ssid) +
    F("</td></tr>"
      "<tr><td>IP ") +
    toStringIp(WiFi.localIP()) +
    F("</td></tr>"
      "</table>"
      "\r\n<br />"
      "<table><tr><th align='left'>WLAN list (refresh if any missing)</th></tr>");
  Serial.println("scan start");
  int n = WiFi.scanNetworks();
  Serial.println("scan done");
  if (n > 0) {
    for (int i = 0; i < n; i++) {
      Page += String(F("\r\n<tr><td>SSID ")) + WiFi.SSID(i) + ((WiFi.encryptionType(i) == ENC_TYPE_NONE) ? F(" ") : F(" *")) + F(" (") + WiFi.RSSI(i) + F(")</td></tr>");
    }
  } else {
    Page += F("<tr><td>No WLAN found</td></tr>");
  }
  Page += F(
            "</table>"
            "\r\n<br /><form method='POST' action='wifisave'><h4>Connect to network:</h4>"
            "<input type='text' placeholder='network' name='n'/>"
            "<br /><input type='password' placeholder='password' name='p'/>"
            "<br /><input type='submit' value='Connect/Disconnect'/></form>"
            "<p>You may want to <a href='/'>return to the home page</a>.</p>"
            "</body></html>");
  server.send(200, "text/html", Page);
  server.client().stop(); // Stop is needed because we sent no content length
}

/** Handle the WLAN save form and redirect to WLAN config page again */
void handleWifiSave() {
  Serial.println("wifi save");
  server.arg("n").toCharArray(ssid, sizeof(ssid) - 1);
  server.arg("p").toCharArray(password, sizeof(password) - 1);
  server.sendHeader("Location", "wifi", true);
  server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
  server.sendHeader("Pragma", "no-cache");
  server.sendHeader("Expires", "-1");
  server.send(302, "text/plain", "");    // Empty content inhibits Content-length header so we have to close the socket ourselves.
  server.client().stop(); // Stop is needed because we sent no content length
  saveCredentials();
  connect = strlen(ssid) > 0; // Request WLAN connect with new credentials if there is a SSID
}

void handleNotFound() {
  if (captivePortal()) { // If caprive portal redirect instead of displaying the error page.
    return;
  }
  String message = F("File Not Found\n\n");
  message += F("URI: ");
  message += server.uri();
  message += F("\nMethod: ");
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += F("\nArguments: ");
  message += server.args();
  message += F("\n");

  for (uint8_t i = 0; i < server.args(); i++) {
    message += String(F(" ")) + server.argName(i) + F(": ") + server.arg(i) + F("\n");
  }
  server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
  server.sendHeader("Pragma", "no-cache");
  server.sendHeader("Expires", "-1");
  server.send(404, "text/plain", message);
}


/** Handle files SPIFFS */
String getContentType(String filename) { // convert the file extension to the MIME type
  if (filename.endsWith(".html")) return "text/html";
  else if (filename.endsWith(".css")) return "text/css";
  else if (filename.endsWith(".js")) return "application/javascript";
  else if (filename.endsWith(".ico")) return "image/x-icon";
  else if (filename.endsWith(".jpg")) return "image/jpeg";
  else if (filename.endsWith(".gif")) return "image/gif";  
  else if (filename.endsWith(".png")) return "image/png";    
  return "text/plain";
}

bool handleFileRead(String path) { // send the right file to the client (if it exists)
  Serial.println("handleFileRead");
  if (path.endsWith("/")) path += "index.html";          // If a folder is requested, send the index file
  
  String contentType = getContentType(path);             // Get the MIME type
  String pathWithGz = path + ".gz";
  if (SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)) { // If the file exists, either as a compressed archive, or normal
    if (SPIFFS.exists(pathWithGz))                         // If there's a compressed version available
      path += ".gz";                                       // Use the compressed verion
    File file = SPIFFS.open(path, "r");                    // Open the file
    size_t sent = server.streamFile(file, contentType);    // Send it to the client
    file.close();                                          // Close the file again
    Serial.println(String("\tSent file: ") + path);
    return true;
  }
  Serial.println(String("\tFile Not Found: ") + path);   // If the file doesn't exist, return false
  return false;
}

void handleFileUpload(){ // upload a new file to the SPIFFS
 
 String Page;
  Page += F(
            "<html><head>"
            "</head><body>"
            "<h1>FileUpload</h1>"); 
  
  HTTPUpload& upload = server.upload();
  
  if(upload.status == UPLOAD_FILE_START){
    
    String filename = upload.filename;
 // if(!filename.startsWith("/")) filename = "/"+filename;
    
    Serial.print("handleFileUpload Name: "); 
    Serial.println(filename);
    Page += String(F("<p>handleFileUpload Name: ")) + filename + F("</p>");    
/*                
  // Opens a file. path should be an absolute path starting with a slash (e.g. /dir/filename.txt). 
  //  https://circuits4you.com/2018/01/31/example-of-es...
*/  
     if (filename.endsWith(".jpg")) filename = "/img/"+filename; 
     else if (filename.endsWith(".gif")) filename = "/img/"+filename;
     else if (filename.endsWith(".png")) filename = "/img/"+filename;     
     else if (filename.endsWith(".css")) filename = "/css/"+filename;  
     else if (filename.endsWith(".js" ))  filename = "/js/"+filename;
          
     if (!filename.startsWith("/")) filename = "/"+filename;
        
     fsUploadFile = SPIFFS.open(filename, "w"); // Open the file for writing in SPIFFS (create if it doesn't exist)´     
    
     filename = String();
  }
  
  else if(upload.status == UPLOAD_FILE_WRITE){
    if(fsUploadFile)
      fsUploadFile.write(upload.buf, upload.currentSize); // Write the received bytes to the file
  }
   
  else if(upload.status == UPLOAD_FILE_END){
    if(fsUploadFile) {                                    // If the file was successfully created
      fsUploadFile.close();                               // Close the file again 
      Serial.print("handleFileUpload Size: "); 
      Serial.println(formatBytes(upload.totalSize).c_str());
      Serial.println("-------------------------\n");

      Page += String(F("<p>handleFileUpload Size:  ")) + formatBytes(upload.totalSize).c_str() + F("</p>");  
      Page += F(
            "<p>[ <a href='/'>RoomInfo Info</a> ]  [ <a href='/wifi'>wifi Info</a> ]  [<a href='/upload'> upload </a>]</p>"
            "</body></html>");       
   
      server.send(303, "text/html", Page);


 // Terminal
      {
      Dir dir = SPIFFS.openDir("/");
        while (dir.next()) {
        String fileName = dir.fileName();
        size_t fileSize = dir.fileSize();
        Speicherbelegung += fileSize;
        Serial.printf("FS File: %s, size: %s\n", fileName.c_str(), formatBytes(fileSize).c_str());
        }
       Serial.printf("\n");
       Serial.printf("Speicherbelegung:  %s\n", formatBytes(Speicherbelegung).c_str());
       Serial.printf("\n");
     }
    } else {
      server.send(500, "text/plain", "500: couldn't create file");
    }
  }
}

/** Handle the WLAN LED PWM link and redirect to WLAN root page again */
void handleLEDpart() {
  BLINKPIN_ON();
    //LEDpart?from=xx&to=yy&pwm=zzz
    String erg = "handleLEDpart ERROR";
    
    int arg1 = 0; 
    int arg2 = 0;    
    int arg3 = 0;       
    arg1 =  server.arg(0).toInt();//von LED
    arg2 =  server.arg(1).toInt();//bis LED
    arg3 =  server.arg(2).toInt();//pwm LEDs   
    
    if (server.args() > 2){ // LED group and PWM OK?             
     for (uint8_t i = arg1; i <= arg2; i++){
      BRIGHTNESS_Array[i] = arg3;
      Set_LED_PWM(i, arg3);
      }
          
      send_LED_ALL_Array();
      erg = BRIGHTNESS_string;
    }
    
    server.send( 200, "text/plain", erg );   
  BLINKPIN_OFF();
}

/** Handle the WLAN LED PWM link and redirect to WLAN root page again */
void handleLED_pwm() {
  BLINKPIN_ON();
    // ./LEDpwm?LED=xx&PWM=yy  
    String erg = "handleLEDpart ERROR";
      
    int arg1 = 0; 
    int arg2 = 0;    
    arg1 =  server.arg(0).toInt();
    arg2 =  server.arg(1).toInt();
    
    if (server.args() > 1){ // LEDnr and PWM OK?            
      BRIGHTNESS_Array[arg1] = arg2;
      Set_LED_PWM(arg1, arg2);

      send_LED_ALL_Array();
      erg = BRIGHTNESS_string;
    }
    
    server.send( 200, "text/plain", erg );     
  BLINKPIN_OFF();
}


/** Handle the WLAN LED link and redirect to WLAN root page again */
void handleLED_switch() {
  BLINKPIN_ON();
 // ./LEDswitch?LED=xx
 //   server.argName(i) => server.arg(i)  
    int arg1 = 0;    

    if (server.args() > 0){ // LEDnr OK? 
    arg1 =  server.arg(0).toInt();
        
    if ((arg1 <= 16) && (arg1 > 0)) {
        handleLED_switch_fkt(arg1);
      }
                               
    } // server.argName = ""
    else  {
          if (BRIGHTNESS_var > 127)
            BRIGHTNESS_var = 0;    
          else if (BRIGHTNESS_var < 128)  
            BRIGHTNESS_var = 255; 
            
          Set_LED_ALL(BRIGHTNESS_var);         
   } 
      
  String message = F("URI: ");
  message += server.uri();
  message += F("\nMethod: ");
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += F("\nArguments: ");
  message += server.args();  
  message += F("\n");
  Serial.print(message);

  for (uint8_t i = 0; i < server.args(); i++) {
    message += String(F("> ")) + server.argName(i) + F(": ") + server.arg(i) + F(" <\n");
  }
    
    server.sendHeader("Location", "/",true);   //redirect to WLAN root page
    server.send(302, "text/plane","");     
  BLINKPIN_OFF();
}


/** send LED PWM Array  */
void handleStatusLED() {
  BLINKPIN_ON();
    send_LED_ALL_Array(); 
    server.send(200, "text/plane", BRIGHTNESS_string); // LED value - JSON to client ajax request
  BLINKPIN_OFF();
}

/** save LED PWM in file LEDpwm.dat */
void handleLEDdata(){
  BLINKPIN_ON();
  
    writeDatafile();
    delay(100); 
    readDatafile();  

    server.sendHeader("Location", "/",true);   //Weiterleitung auf die eigene HTML-Seite
    server.send(302, "text/plane",""); 
  BLINKPIN_OFF();   
}

/** send DHT sensor data */
void handleStatusDHT(){        
  BLINKPIN_ON();
   ReadDHT();  
   String DHT_string = "NULL";
          DHT_string  = "{\"Temperature\":\""+ String(Temperature) + "\", ";
          DHT_string += " \"Humidity\":\"" + String(Humidity) + "\"}";          
    
    server.send( 200, "text/plain", DHT_string );  
  BLINKPIN_OFF();   
}
  
void handleRstI2C() {
  BLINKPIN_ON();
    init_TLC59116();
    readDatafile();  // read preset data from file
    Set_LED_ALL_Array();
    
    server.sendHeader("Location", "/",true);   //redirect to WLAN root page
    server.send(302, "text/plane","");      
  BLINKPIN_OFF();      
}

/** Handle the WLAN I2C link and redirect to WLAN root page again */
void handleStatusI2C() {
  BLINKPIN_ON();
byte error, address;
  int nDevices;

  Serial.println("Scanning...");

  nDevices = 0;
  for(address = 1; address < 127; address++ ) 
  {
    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(address);
    error = Wire.endTransmission();

    if (error == 0)
    {
      Serial.print("I2C device found at address 0x");
      if (address<16) 
        Serial.print("0");
      Serial.print(address,HEX);
      Serial.println("  !");

      nDevices++;
    }
    else if (error==4) 
    {
      Serial.print("Unknown error at address 0x");
      if (address<16) 
        Serial.print("0");
      Serial.println(address,HEX);
    }    
  }
  if (nDevices == 0)
    Serial.println("No I2C devices found\n");
  else
    Serial.println("done\n");    
    server.send( 200, "text/plain", String( nDevices ));     
  BLINKPIN_OFF();
}

/** Handle the WLAN ESP status task on COM and redirect to WLAN root page again */
/**  https://circuits4you.com/2018/01/31/example-of-es... */
void handleStatusESP() {
  Speicherbelegung = 0;
  //  Größe des ESP8266 Flash-Speicher sowie Chip ID ausgeben und mit der Konfiguration überprüfen
    uint32_t realSize = ESP.getFlashChipRealSize();
    uint32_t ideSize = ESP.getFlashChipSize();
    FlashMode_t ideMode = ESP.getFlashChipMode();
    Serial.printf("Reale Flash ID:   %08X\n", ESP.getFlashChipId());
    Serial.printf("Reale Flash groesse: %u", realSize);
    Serial.print(" Byte\n\n");
    Serial.printf("In der IDE hinterlegte Flash groesse: %u", ideSize);
    Serial.print(" Byte\n");
    Serial.printf("In der IDE hinterlegte Flash geschwindigkeit: %u\n", ESP.getFlashChipSpeed());
    Serial.printf("In der IDE hinterlegter  Flash Modus:  %s\n\n", (ideMode == FM_QIO ? "QIO" : ideMode == FM_QOUT ? "QOUT" : ideMode == FM_DIO ? "DIO" : ideMode == FM_DOUT ? "DOUT" : "UNKNOWN"));
    if(ideSize != realSize) {
        Serial.println("Flash konfiguration ist falsch!\n");
    } else {
        Serial.println("Flash konfigguration ist korrekt.\n");
    }

  Dir dir = SPIFFS.openDir("/");
        while (dir.next()) {
        String fileName = dir.fileName();
        size_t fileSize = dir.fileSize();
        Speicherbelegung += fileSize;
        Serial.printf("FS File: %s, size: %s\n", fileName.c_str(), formatBytes(fileSize).c_str());
        }
       Serial.printf("\n");
       Serial.printf("Speicherbelegung:  %s\n", formatBytes(Speicherbelegung).c_str());
       Serial.printf("\n");
 
   server.sendHeader("Location", "/",true);   //redirect to WLAN root page
   server.send(302, "text/plane","");      
  }
/** Handle the WLAN Reboot link and redirect to WLAN root page again */
void handleReboot(){
  BLINKPIN_ON();  
    Serial.println("Rebooting...");
    Serial.flush(); // end output on serial 

   server.sendHeader("Location", "/",true);   //redirect to WLAN root page
   server.send(302, "text/plane","");     
   delay(1000);
  BLINKPIN_OFF();   
   ESP.restart();  // start reset ESP
}

Step 4: Erwiterungen in Tools.ino

+ void ReadDHT()

+ void readDatafile()

+ void writeDatafile()

/** Is this an IP? */
boolean isIp(String str) {
  for (size_t i = 0; i < str.length(); i++) {
    int c = str.charAt(i);
    if (c != '.' && (c < '0' || c > '9')) {
      return false;
    }
  }
  return true;
}

/** IP to String? */
String toStringIp(IPAddress ip) {
  String res = "";
  for (int i = 0; i < 3; i++) {
    res += String((ip >> (8 * i)) & 0xFF) + ".";
  }
  res += String(((ip >> 8 * 3)) & 0xFF);
  return res;
}

//format bytes / SPIFFS
String formatBytes(size_t bytes) {
  if (bytes < 1024) {
    return String(bytes) + "B";
  } else if (bytes < (1024 * 1024)) {
    return String(bytes / 1024.0) + "KB";
  } else if (bytes < (1024 * 1024 * 1024)) {
    return String(bytes / 1024.0 / 1024.0) + "MB";
  } else {
    return String(bytes / 1024.0 / 1024.0 / 1024.0) + "GB";
  }
}

//////////////////////////// TLC59116  /////////////////////////////////

void init_TLC59116() {

  Wire.begin(TLC59116_SDA, TLC59116_SCL); // (SDA), (SCL)  
  Wire.beginTransmission(B1100000); // TLC59116 Slave Adresse ->C0 hex
  Wire.write(0x80);  // autoincrement ab Register 0h

  Wire.write(0x00);  // Register 00 /  Mode1  
  Wire.write(0x00);  // Register 01 /  Mode2 

  Wire.write(0x00);  // Register 02 /  PWM LED 1    // Default alle PWM auf 0
  Wire.write(0x00);  // Register 03 /  PWM LED 2    
  Wire.write(0x00);  // Register 04 /  PWM LED 3
  Wire.write(0x00);  // Register 05 /  PWM LED 4
  Wire.write(0x00);  // Register 06 /  PWM LED 5
  Wire.write(0x00);  // Register 07 /  PWM LED 6
  Wire.write(0x00);  // Register 08 /  PWM LED 7
  Wire.write(0x00);  // Register 09 /  PWM LED 8
  Wire.write(0x00);  // Register 0A /  PWM LED 9
  Wire.write(0x00);  // Register 0B /  PWM LED 10
  Wire.write(0x00);  // Register 0C /  PWM LED 11
  Wire.write(0x00);  // Register 0D /  PWM LED 12
  Wire.write(0x00);  // Register 0E /  PWM LED 13
  Wire.write(0x00);  // Register 0F /  PWM LED 14
  Wire.write(0x00);  // Register 10 /  PWM LED 15
  Wire.write(0x00);  // Register 11 /  PWM LED 16  // Default alle PWM auf 0

  Wire.write(0xFF);  // Register 12 /  Group duty cycle control
  Wire.write(0x00);  // Register 13 /  Group frequency
  Wire.write(0xAA);  // Register 14 /  LED output state 0  // Default alle LEDs auf PWM
  Wire.write(0xAA);  // Register 15 /  LED output state 1  // Default alle LEDs auf PWM
  Wire.write(0xAA);  // Register 16 /  LED output state 2  // Default alle LEDs auf PWM
  Wire.write(0xAA);  // Register 17 /  LED output state 3  // Default alle LEDs auf PWM
  Wire.write(0x00);  // Register 18 /  I2C bus subaddress 1
  Wire.write(0x00);  // Register 19 /  I2C bus subaddress 2
  Wire.write(0x00);  // Register 1A /  I2C bus subaddress 3
  Wire.write(0x00);  // Register 1B /  All Call I2C bus address
  Wire.write(0xFF);  // Register 1C /  IREF configuration  
  Wire.endTransmission();  // I2C-Stop
}

//////////////////////////// DHT /////////////////////////////////
// handleStatusDHT();
void ReadDHT(){
  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
   Humidity = dht.readHumidity();
  // Read temperature as Celsius (the default)
   Temperature = dht.readTemperature();
  // Read temperature as Fahrenheit (isFahrenheit = true)
  //float f = dht.readTemperature(true);
}



// Diese Funktion setzt die Helligkeit für ein LED-Register 
// Voraussetzung ist, das im entsprechende Register 14 bis 17 die LED aktiviert ist
// Übergabeparameter: LED = Nummer der LED / PWM = Helligkeitswert 0 -255

void Set_LED_PWM(int LED, int PWM)
{
  Wire.begin();             //I2C-Start
  Wire.beginTransmission(B1100000); // TLC59116 Slave Adresse ->C0 hex
  Wire.write(0x01 + LED);    // Register LED-Nr
  Wire.write(PWM);
  Wire.endTransmission();   // I2C-Stop
  BRIGHTNESS_Array[LED] = PWM;
}

// Diese Funktion setzt die Helligkeit für alle LED-Register gleichzeitig
// Voraussetzung ist, das im entsprechende Register 14 bis 17 die LED aktiviert ist
// Übergabeparameter: PWM = Helligkeitswert 0 -255

void Set_LED_ALL(int PWM)
{
  Wire.begin();                     // I2C-Start
  Wire.beginTransmission(B1100000); // TLC59116 Slave Adresse ->C0 hex
  Wire.write(0x82);                 // Startregister 02h 
  for (int i=1 ; i < 17; i++){      // 16Bytes (Register 02h bis 11h) schreiben
    Wire.write(PWM);
    BRIGHTNESS_Array[i] = PWM;
  }
  Wire.endTransmission();           // I2C-Stop
}

void Set_LED_ALL_Array()
{  
  Wire.begin();                     // I2C-Start
  Wire.beginTransmission(B1100000); // TLC59116 Slave Adresse ->C0 hex
  Wire.write(0x82);                 // Startregister 02h 
  for (int i=1 ; i < 17; i++){      // 16Bytes (Register 02h bis 11h) schreiben
    Wire.write(BRIGHTNESS_Array[i]);
  }
  Wire.endTransmission();           // I2C-Stop
  send_LED_ALL_Array();
}

void send_LED_ALL_Array()
{  
        BRIGHTNESS_string  = "{\"LEDpwm\": [\""+ String(BRIGHTNESS_Array[1]) ;
        for (int i=2 ; i < 17; i++){      
          BRIGHTNESS_string += "\", \""+ String(BRIGHTNESS_Array[i]);
        }  
        BRIGHTNESS_string += "\"]}";        
        //Serial.println(BRIGHTNESS_string);
}


//////////////////////////// TLC59116 WLAN /////////////////////////////////
void handleLED_switch_fkt(int arg1) {

   if (BRIGHTNESS_Array[arg1] > 127)
      BRIGHTNESS_Array[arg1] = 0;    
   else if (BRIGHTNESS_Array[arg1] < 128)  
      BRIGHTNESS_Array[arg1] = 255; 
  
   Set_LED_PWM(arg1,BRIGHTNESS_Array[arg1]); 
   Serial.print(arg1);  
   Serial.println(" done\n");           
}


//////////////////////////// LEDdata /////////////////////////////////
void readDatafile(){
 // int BRIGHTNESS_Array[16]={64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64};    
 // ./data/LEDpwm.dat  
  File f = SPIFFS.open("/LEDpwm.dat", "r");
  delay(10); 
  
  if (!f) {
    Serial.println("open file faild");
  }
  else
  {
      Serial.println("data file contents:");
      for (int i=1; i<=16; i++){
        String s=f.readStringUntil(',');
        Serial.print(i);
        Serial.print(":");
        Serial.print(s);
        Serial.print(" => ");
        BRIGHTNESS_Array[i] = s.toInt();
        Serial.println(BRIGHTNESS_Array[i]);
      }
      f.close();  // close file
      Serial.println("file closed");
  }
}

void writeDatafile(){
  File f = SPIFFS.open("/LEDpwm.dat", "w");
  
  if (!f) {
    Serial.println("open file faild");
  }
  else
  {
        BRIGHTNESS_string  = String(BRIGHTNESS_Array[1]) ;
        for (int i=2 ; i < 17; i++){      
          BRIGHTNESS_string += ","+ String(BRIGHTNESS_Array[i]);
        }  
      
      f.print(BRIGHTNESS_string);      //Write data to file
      f.close();  // close file

      Serial.println("written data: " + BRIGHTNESS_string);      
  }


}

Step 5: Die Weboberfläche (index.html) Part 4

+ LEDgroupSet()

+ LEDgroupSetSlider(slider, value)

+ getDataDHT()

+ getI2Cdevices()

<!DOCTYPE html>
<html>
<title>LED Info</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Cache-Control" content="no-cache">
<meta http-equiv="Cache-control" content="no-store">
<meta http-equiv="expires" content="-1">

<style>

	rect {  
		fill:white;
		stroke:grey;
		stroke-width:5;
		fill-opacity:0.1;
		stroke-opacity:0.9
	  }

    rect:hover {
	    fill: white;
	    opacity:0.2;		
	  }

	/* unvisited link */
	a:link {
		color: black;
		text-decoration: none;		
	}

	/* visited link */
	a:visited {
		color: black;
		text-decoration: none;		
	}

	/* mouse over link */
	a:hover {
		color: blue;
		text-decoration: none;		
	}

	/* selected link */
	a:active {
		color: blue;
		text-decoration: none;		
	} 

/* Basics & Browser Support, Grids, Headings  http://mincss.com  */

	body,textarea,input,select{background:0;border-radius:0;font:16px sans-serif;margin:0}
	.addon,.btn-sm,.nav,textarea,input,select{outline:0;font-size:14px}
	.smooth{transition:all .2s}
	.btn,.nav a{text-decoration:none}

	
	h2{font-size:1.5em; text-align: center; margin: 0.2em 0; margin-bottom: 0.2em; border-bottom: 2px solid; margin-bottom: 15px; padding-bottom: 5px;}	
	h1{font-size:3em; margin: 0.2em 0; padding-left: 15px; margin-bottom: 0.2em; border-bottom: 5px solid; margin-bottom: 15px; padding-bottom: 5px;}
	
	.row{margin:1% 0;overflow:auto}
	.col{float:left}
	.table,.c12{width:100%}
	.c11{width:91.66%}
	.c10{width:83.33%}
	.c9{width:75%}
	.c8{width:66.66%}
	.c7{width:58.33%}
	.c6{width:50%}
	.c5{width:41.66%}
	.c4{width:33.33%}
	.c3{width:25%}
	.c2{width:16.66%}
	.c1{width:8.33%}
	
	@media(max-width:870px){.row .col{width:100%}}


/*tutorial @  http://mincss.com  */

.container { border: 3px solid #eee; margin: 10px; padding:10px; float:left; text-align: center; max-width: 25%;}

/*customised range*/ 
input[type="range"].range
{
     cursor: pointer;
     width: 100px !important;
     -webkit-appearance: none;
     z-index: 200;
     width:50px;
     border: 1px solid #e6e6e6;
     background-color: #e6e6e6;
     background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#e6e6e6), to(#d2d2d2));
     background-image: -webkit-linear-gradient(right, #e6e6e6, #d2d2d2);
     background-image: -moz-linear-gradient(right, #e6e6e6, #d2d2d2);
     background-image: -ms-linear-gradient(right, #e6e6e6, #d2d2d2);
     background-image: -o-linear-gradient(right, #e6e6e6, #d2d2d2);
}

/*customised range when focusing on input */ 
input[type="range"].range:focus
{
     border: 0 !imporant;
	outline: none !important;
}

/*customised range slider icon*/ input[type="range"]
.range::-webkit-slider-thumb
{
     -webkit-appearance: none;
     width: 10px;
     height: 10px;
	 background-color: #555;
     background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#4DDBFF), to(#00CCFF));
     background-image: -webkit-linear-gradient(right, #4DDBFF, #00CCFF);
     background-image: -moz-linear-gradient(right, #4DDBFF, #00CCFF);
     background-image: -ms-linear-gradient(right, #4DDBFF, #00CCFF);
     background-image: -o-linear-gradient(right, #4DDBFF, #00CCFF);
}

/*setting round corners to the range */ 
input[type="range"].round {
     -webkit-border-radius: 20px;
     -moz-border-radius: 20px;
     border-radius: 20px;
}

/*setting round corners to the range slider icon*/ 
input[type="range"].round::-webkit-slider-thumb {
    -webkit-border-radius: 5px;
	-moz-border-radius: 5px;
	-o-border-radius: 5px;
}

/* set range from 1 - 0 vertically (highest on top) */ 
.vhf /*.vertical-heighest-first */
{
     -webkit-transform:rotate(270deg);
     -moz-transform:rotate(270deg);
     -o-transform:rotate(270deg);
     -ms-transform:rotate(270deg);
     transform:rotate(270deg); 
}

.dataSensor{
	color: blue;
}	 

</style>
<body >
<h2>
    [ <span class="dataSensor" id="dataTemperature"> 0.00</span> °C ] [ <span class="dataSensor" id="dataHumidity">0.00</span> % ]  
	[ <a onclick="LEDswitch()">LED on/off </a> ]    
	[ <a href="./LEDdata">save PWM LEDs</a> ]	
	[ <a onclick="getI2Cdevices()">I2C scanner</a> ]		
	[ <a href="./i2cRST">I2C reset</a> ]	
	[ <a href="./upload">Upload</a> ]
	[ <a href="./wifi">WIFI info</a> ]	
	[ <a href="./status">ESP info</a> ]	
	[ <a href="./reboot">reboot ESP</a> ]	
</h2>

<div class="row">
	<div class="col c12"><h1>Group</h1></div>	
</div>	
<div class="row">
		<div class="container col " style="margin-right: 0px">start<br><br><br><br>
		<input oninput="LEDgroupSetSlider('dataminGroup', this.value)" id="minGroup" type="range" class="range vhf round" round min="1" max="16" step="1">
		<br><br><br><span  id="dataminGroup">00</span>
		</div>
		<div class="container  col " style="margin-right: 0px">end<br><br><br><br>
		<input oninput="LEDgroupSetSlider('datamaxGroup', this.value)" id="maxGroup" type="range" class="range vhf round" round min="1" max="16" step="1">
		<br><br><br><span  id="datamaxGroup">00</span>
		</div>
		<div class="container  col " style="margin-right: 0px">PWM<br><br><br><br>
		<input oninput="LEDgroupSetSlider('datapwmGroup', this.value)" id="pwmGroup" type="range" class="range vhf round" round min="0" max="255" step="1">
		<br><br><br><span id="datapwmGroup">000</span>
		</div>
		<div class="container  col " style="margin-right: 0px"><br><br>
		<strong><a onclick="LEDgroupSet()" >set LED</a></strong>
		<br><br><br>
		</div>
</div>	

 <div class="row">
    <div class="col c12"><h1>Map</h1></div>

	<div class="col c12">		
<figure id="imageMapRoom">
<svg viewBox="0 0 462 286" width="462" height="286">
  <image id="imageMapRoomPic" width="462" height="286" xlink:href="./img/living_room_demo.png" >
    <title>Living Room LEDs</title>
  </image>	
  
  <a onclick="UpdateLED(1)" >
    <rect id="Feld_LED_K1" x="20" y="20"  width="50" height="50"  rx="20" ry="20" />
  </a>
   <a onclick="UpdateLED(2)">
    <rect id="Feld_LED_K2" x="80" y="20"  width="50" height="50"  rx="20" ry="20" />
  </a>
   <a onclick="UpdateLED(3)">
	<rect id="Feld_LED_K3" x="140" y="20"  width="50" height="50" rx="20" ry="20" />
  </a>
  <a onclick="UpdateLED(4)">
	<rect id="Feld_LED_K4" x="200" y="20"  width="50" height="50" rx="20" ry="20" />
  </a>
  
  <a onclick="UpdateLED(5)">
	<rect id="Feld_LED_K5" x="20" y="80"  width="50" height="50" rx="20" ry="20" />
  </a>
    <a onclick="UpdateLED(6)">
	<rect id="Feld_LED_K6" x="80" y="80"  width="50" height="50" rx="20" ry="20" />
  </a>
  <a onclick="UpdateLED(7)">
	<rect id="Feld_LED_K7" x="140" y="80"  width="50" height="50" rx="20" ry="20" />
  </a>
    <a onclick="UpdateLED(8)">
	<rect id="Feld_LED_K8" x="200" y="80"  width="50" height="50" rx="20" ry="20" />
  </a>
  
  <a onclick="UpdateLED(9)">
	<rect id="Feld_LED_K9" x="20" y="140"  width="50" height="50" rx="20" ry="20" />
  </a>
    <a onclick="UpdateLED(10)">
	<rect id="Feld_LED_K10" x="80" y="140"  width="50" height="50" rx="20" ry="20" />
  </a>
  <a onclick="UpdateLED(11)">
	<rect id="Feld_LED_K11" x="140" y="140"  width="50" height="50" rx="20" ry="20" />
  </a>
    <a onclick="UpdateLED(12)">
	<rect id="Feld_LED_K12" x="200" y="140"  width="50" height="50" rx="20" ry="20" />
  </a>
  
    <a onclick="UpdateLED(13)">
	<rect id="Feld_LED_K13" x="20" y="200"  width="50" height="50" rx="20" ry="20" />
  </a>
    <a onclick="UpdateLED(14)">
	<rect id="Feld_LED_K14" x="80" y="200"  width="50" height="50" rx="20" ry="20" />
  </a>
  <a onclick="UpdateLED(15)">
	<rect id="Feld_LED_K15" x="140" y="200"  width="50" height="50" rx="20" ry="20" />
  </a>
    <a onclick="UpdateLED(16)">
	<rect id="Feld_LED_K16" x="200" y="200"  width="50" height="50" rx="20" ry="20" />
  </a> 
</svg>
  <figcaption> auf die Felder klicken ...</figcaption>
</figure>		
		</div>
	</div>
		
<div class="row">
	<div class="col c12"><h1>Slider</h1></div>	
</div>	

<div class="row" id="sliderHTML">
</div>  

		
	
<script> 
//////////////////////////////////////////////////////

// silder container
	var txtHTML = "";

    for (var i=1;i<17;i++) {
	txtHTML += "<div class=\"container\" style=margin-right: 0px><span>";
	txtHTML += i ;
    txtHTML += "</span><br><br><br><br><input id=\"LEDsilder";
	txtHTML += i ;	
	txtHTML += "\" oninput=\"UpdateLEDdim(";
	txtHTML += i ;
	txtHTML += ",this.value)\" ";
	//txtHTML += i ;	
	txtHTML += " type=\"range\" class=\"range vhf round\" min=\"0\" max=\"255\" step=\"1\">";
	txtHTML += " <br><br><br><br><span id=\"wertLED_K";
	txtHTML += i ;	
	txtHTML += "\">000</span></div>";
    }
 
    document.getElementById("sliderHTML").innerHTML = txtHTML;


  
function LEDswitch(){  // onClick= "UpdateLEDdim();"
  var xhttp = new XMLHttpRequest();
  xhttp.open("GET", "./LEDswitch" , true); //Handle LED on/off ESP8266 server
  xhttp.send();
  getData();
}

function UpdateLED(LEDnr) {  // onClick= "UpdateLED(xx);" 
  var xhttp = new XMLHttpRequest();
  xhttp.open("GET", "./LEDswitch?LED=" + LEDnr, true); //Handle LED on/off on ESP8266 server
  xhttp.send();
  getData();
}

function UpdateLEDdim(LEDnr,LEDpwm) {  // onClick= "UpdateLEDdim(xx,yyy);"
  var xhttp = new XMLHttpRequest();
  xhttp.open("GET", "./LEDpwm?LED=" + LEDnr + "&PWM=" + LEDpwm, true); //Handle LED PWM on ESP8266 server
  xhttp.send();
  getData();
}

//tutorial:  http://mincss.com  
// {"LEDpwm": ["32", "32", "32", "32", "32", "32", "32", "32", "32", "32", "32", "32", "32", "32", "32", "32"]}
function getData() {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
   if (this.readyState == 4 && this.status == 200) {
     //Push the data in array
  var txt = this.responseText;
  var obj = JSON.parse(txt); 
	
  var txtID = ""; 
  var objID = "";
  var feldID = "";
   
		for (var i=1;i<17;i++){
			 txtID = "wertLED_K"  + i; 
			 objID = "LEDsilder"  + i;
			feldID = "Feld_LED_K" + i;
		
			document.getElementById(txtID).innerHTML = obj["LEDpwm"][i-1]; 
			document.getElementById(objID).value = obj["LEDpwm"][i-1];
		
		if ( parseInt(obj["LEDpwm"][i-1]) > 48 )    
			document.getElementById(feldID).style.stroke = "rgb(0,"+ parseInt(obj["LEDpwm"][i-1]) +",0)";
	    else  
			document.getElementById(feldID).style.stroke = "red";
	
		}

    }
	
  };
  xhttp.open("GET", "./readLED", true); //Handle readLED on ESP8266 server
  xhttp.send();
}

function LEDgroupSetSlider(slider, value){
	
	document.getElementById(slider).innerHTML = value; // Update werte
			
var min = parseInt(document.getElementById("dataminGroup").innerHTML);
var max = parseInt(document.getElementById("datamaxGroup").innerHTML);

  if (min > max) { // Wert min <= max 
        // Wert min > max => maxGroup anpassen
		document.getElementById("datamaxGroup").innerHTML = document.getElementById("dataminGroup").innerHTML; 
  }
  if (max < min) { // Wert min <= max 
        // Wert max < min => minGroup anpassen
		document.getElementById("dataminGroup").innerHTML = document.getElementById("datamaxGroup").innerHTML; 
  }
}


function LEDgroupSet() {
//minGroup maxGroup  LED
//pwmGroup PWM
//LEDpart?von=xx&bis=yy&pwm=zzz
var min3web = parseInt(document.getElementById("dataminGroup").innerHTML);
var max4web = parseInt(document.getElementById("datamaxGroup").innerHTML);
var min4web = parseInt(document.getElementById("datapwmGroup").innerHTML);

  var xhttp = new XMLHttpRequest();
  xhttp.open("GET", "./LEDpart?from=" + min3web + "&to=" + max4web + "&pwm=" + min4web, true); //Handle readADC server on ESP8266
  xhttp.send();
  getData();
}


function getDataDHT(){
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
   if (this.readyState == 4 && this.status == 200) {
     //Push the data in array
  var txt = this.responseText;
  //var txt = '{"Temperature":"20.20",  "Humidity":"54.90"}';
  var obj = JSON.parse(txt); //Ref:  http://mincss.com 
	
	document.getElementById("dataTemperature").innerHTML = obj.Temperature;
	document.getElementById("dataHumidity").innerHTML = obj.Humidity;
    }
  };

  xhttp.open("GET", "./statusDHT", true); //Handle readLED server on ESP8266
  xhttp.send();  
  
}

function getI2Cdevices(){
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
   if (this.readyState == 4 && this.status == 200) {
     //Push the data in array
  var txt = this.responseText;
   alert('I2C devices found: '+txt); 
    }
  };

  xhttp.open("GET", "./i2c", true); //Handle readLED server on ESP8266
  xhttp.send();  
  
}

//////////////////////////////////////////////////////
setInterval(function() {
  // Call a function repetatively with 60 Second interval

  getDataDHT();

}, 60000); // 60.000 Seconds/ 1 min. update rate


 //////////////////////////////////////////////////////
	getData();
	getDataDHT();
</script>

</body>
</html>

Step 6:

Durch verschieden Erweiterung in der Weboberfläche und der ESP Programmierung können nun einzelne LEDs oder LEDs in einer Gruppe verändert und angezeigt werden. Die LED Positionen im Raumplan können frei in der index.html Datei angepasst werden. Auch der Raumplan selber kann leicht über den Upload ausgetauscht werden. Die Startwerte aller LEDs können nach eigenen Vorstellungen abgespeichert werden.

Beim ersten Verbinden über den AP können die Zugangsdaten des eigenen WLANs eingetragen und die Weboberfläche kann ab jetzt über eine IP Adresse im WLAN aufgerufen werden.

Wer lieber ohne das eigene WLAN den ESP betreiben möchte, kann die Weboberfläche weiter über den AP aufgerufen.

Der Datenaustausch erfolgt über JSON (JavaScript). Es werden über XMLHttpRequest von der html Seite im ESP verschiedene URLs mit Positionen und PWM Werten aufgerufen. Die PWM Werte werden über JSON.parse und innerHTML Befehle wieder in die Weboberfläche zurückgespielt. Durch diese JS „Manipulation“ der index.html Seite wird ein neues Aufbauen vermieden. Die ganzen Abfragen erfolgen im Hintergrund. =)

Für die Fehlersuche sind Abfragen für den ESP8266 (Chip Info / WLAN / Reboot) und I2C Bus (I2C scanner / I2C reset) vorhanden

und nun viel Spaß beim Nachbauen