cotestatnt / esp-fs-webserver

ESP32/ESP8266 webserver, WiFi manager and web editor Arduino library
MIT License
120 stars 29 forks source link

How to update variable value on custom html example? #28

Closed arimukhlas closed 1 year ago

arimukhlas commented 1 year ago

Hi @cotestatnt

  1. I implement your sugestion on this and its work perfectly, so I try to other value like sensor DHT22. I want to show its value on custom_html, and the value update every 5 second without refresh webpage. I try to add some code to HTML
setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      document.getElementById("label1").innerHTML = this.responseText;
      }
    };
    xhttp.open("GET", "/temperature", true);
    xhttp.send();
}, 5000 ) ;

but fail. Can you give me a hint to do that??

  1. How to erase input previous settings every time I upload sketch?
  2. When I try examples/simpleServer >> upload littleFS data, and then I try examples/customHTML, the previous HTML simpleServer still exist. How to reset it?

Thanks

cotestatnt commented 1 year ago

Hi @arimukhlas

Your piece of code has no particular problems. I tested it quickly on a test web page and it does what it's supposed to: it sends a GET /temperature request every 5 seconds.

1 - Are you sure you have implemented the response to the request on the "web server" side

2, 3 - When you ulpoad a new sketch, set the option "Erase All Flash Before Sketch Upload" to enabled

arimukhlas commented 1 year ago

Hi @arimukhlas

Your piece of code has no particular problems. I tested it quickly on a test web page and it does what it's supposed to: it sends a GET /temperature request every 5 seconds.

1 - Are you sure you have implemented the response to the request on the "web server" side

2, 3 - When you ulpoad a new sketch, set the option "Erase All Flash Before Sketch Upload" to enabled

  1. I'm not sure, i just add code
    myWebServer.addHandler("/temperature", HTTP_GET, handleTemp);

and function

void handleTemp() {
  WebServerClass* webRequest = myWebServer.getRequest();

  String reply = "45";
  webRequest->send(200, "text/plain", reply);
}

is that wrong? or what should i add in the code?

thanks

cotestatnt commented 1 year ago

It should work. What happens if you open the dev tools on browser (F12)? Check the log section, maybe there is some error which block the Javascript code execution

arimukhlas commented 1 year ago

no error,

this is HTML

static const char custom_html[] PROGMEM = R"EOF(
<main class="container">
      <article class="grid">
        <div>
          <hgroup>
            <h1>ESP FS WebServer - LED Switcher</h1>
          </hgroup>
          <label for="remember">
            <input type="checkbox" role="switch" id="toggle-led" name="toggle-led">
            Toggle built-in LED
          </label>
          <br>
          <p id="esp-response"></p>
        </div>
      </article>
    </main>

<p id="label1"></p>

)EOF";

static const char custom_css[] PROGMEM = R"EOF(
pre{
    font-family: Monaco,Menlo,Consolas,'Courier New',monospace;
    color: #333;
    line-height: 20px;
    background-color: #f5f5f5;
    border: 1px solid rgba(0,0,0,0.15);
    border-radius: 6px;
    overflow-y: scroll;
    min-height: 350px;
    font-size: 85%;
}
.select{
  width: 25%;
  height:40px;
  padding-top: 10px;
  padding-left: 20px;
  border:1px solid #ccc;
  border-radius: 6px;
  box-shadow: 0 1px 2px 0 rgba(220, 220, 230, 0.5);
}
.body{
  background-color: cadetblue;
}
)EOF";

static const char custom_script[] PROGMEM = R"EOF(
function toggleLed() {
        const pars = new URLSearchParams({
          val:  document.getElementById('toggle-led').checked ? '1' : '0'
        });

        fetch('/led?' + pars )                // Do the request
        .then(response => response.text())    // Parse the response 
        .then(text => {                       // DO something with response
          console.log(text);
          document.getElementById('esp-response').innerHTML = text + ' <i>(Builtin LED is ON with a low signal)</i>';
        });
      }

      // Add event listener to the LED checkbox (the function will be called on every change)
      document.getElementById('toggle-led').addEventListener('change', toggleLed );

      setInterval(function ( ) {
                var xhttp = new XMLHttpRequest();
                xhttp.onreadystatechange = function() {
                    if (this.readyState == 4 && this.status == 200) {
                        document.getElementById("label1").innerHTML = this.responseText;
                    }
                };
                xhttp.open("GET", "/temperature", true);
                xhttp.send();
            }, 5000 ) ;

)EOF";

and the sketch

#include <esp-fs-webserver.h>   // https://github.com/cotestatnt/esp-fs-webserver

#include <FS.h>
#include <LittleFS.h>
#define FILESYSTEM LittleFS

#ifndef LED_BUILTIN
#define LED_BUILTIN 2
#endif

// Set this to 1 if you want clear the /config.json file at startup
#define CLEAR_OTIONS 0

struct tm sysTime;

// Test "options" values
uint8_t ledPin = LED_BUILTIN;
bool boolVar = true;
uint32_t longVar = 1234567890;
float floatVar = 15.5F;
String stringVar = "Test option String";

// ThingsBoard varaibles
String tb_deviceToken = "xxxxxxxxxxxxxxxxxxx";
String tb_device_key = "xxxxxxxxxxxxxxxxxxx";
String tb_secret_key = "xxxxxxxxxxxxxxxxxxx";
String tb_serverIP = "192.168.1.1";
uint16_t tb_port = 8181;

// Var labels (in /setup webpage)
#define LED_LABEL "The LED pin number"
#define BOOL_LABEL "A bool variable"
#define LONG_LABEL "A long variable"
#define FLOAT_LABEL "A float variable"
#define STRING_LABEL "A String variable"

#define TB_SERVER "ThingsBoard server address"
#define TB_PORT "ThingsBoard server port"
#define TB_DEVICE_TOKEN "ThingsBoard device token"
#define TB_DEVICE_KEY "Provisioning device key"
#define TB_SECRET_KEY "Provisioning secret key"

// Timezone definition to get properly time from NTP server
#define MYTZ "CET-1CEST,M3.5.0,M10.5.0/3"
struct tm Time;

#ifdef ESP8266
ESP8266WebServer server(80);
#elif defined(ESP32)
WebServer server(80);
#endif

FSWebServer myWebServer(FILESYSTEM, server);

/*
* Include the custom HTML, CSS and Javascript to be injected in /setup webpage.
* HTML code will be injected according to the order of options declaration.
* CSS and JavaScript will be appended to the end of body in order to work properly.
* In this manner, is also possible override the default element styles
* like for example background color, margins, paddings etc etc
*/
#include "customElements.h"

////////////////////////////////  Filesystem  /////////////////////////////////////////
void startFilesystem() {
  // FILESYSTEM INIT
  if ( FILESYSTEM.begin()) {
    File root = FILESYSTEM.open("/", "r");
    File file = root.openNextFile();
    while (file) {
      const char* fileName = file.name();
      size_t fileSize = file.size();
      Serial.printf("FS File: %s, size: %lu\n", fileName, (long unsigned)fileSize);
      file = root.openNextFile();
    }
    Serial.println();
  }
  else {
    Serial.println(F("ERROR on mounting filesystem. It will be formmatted!"));
    FILESYSTEM.format();
    ESP.restart();
  }
}

////////////////////  Load application options from filesystem  ////////////////////
/*
* Unlike what was done in customOptions.ino, in this example
* the variables are read (and written) all at once using the ArduinoJon library
*/
bool loadOptions() {
  if (FILESYSTEM.exists(myWebServer.configFile())) {
    File file = FILESYSTEM.open(myWebServer.configFile(), "r");
    DynamicJsonDocument doc(file.size() * 1.33);
    if (!file)
      return false;

    DeserializationError error = deserializeJson(doc, file);
    if (error)
      return false;

    ledPin = doc[LED_LABEL];
    boolVar = doc[BOOL_LABEL];
    longVar = doc[LONG_LABEL];
    floatVar = doc[FLOAT_LABEL]["value"];
    stringVar = doc[STRING_LABEL].as<String>();

    tb_deviceToken = doc[TB_DEVICE_TOKEN].as<String>();
    tb_device_key = doc[TB_DEVICE_KEY].as<String>();
    tb_secret_key = doc[TB_SECRET_KEY].as<String>();
    tb_serverIP = doc[TB_SERVER].as<String>();
    tb_port = doc[TB_PORT];

    file.close();

    Serial.println();
    Serial.printf("LED pin value: %d\n", ledPin);
    Serial.printf("Bool value: %d\n", boolVar);
    Serial.printf("Long value: %ld\n",longVar);
    Serial.printf("Float value: %d.%d\n", (int)floatVar, (int)(floatVar*1000)%1000);
    Serial.println(stringVar);
    return true;
  }
  else
    Serial.println(F("Configuration file not exist"));
  return false;
}

//   Call this if you need to save parameters from the sketch side
// bool saveOptions() {
//   if (FILESYSTEM.exists(myWebServer.configFile())) {
//     File file = FILESYSTEM.open(myWebServer.configFile(), "w");
//     if (!file)
//       return false;

//     DynamicJsonDocument doc(file.size() * 1.33);
//     // Deserialize first, otherwise unhandled or hidden options will be lost
//     DeserializationError error = deserializeJson(doc, file);
//     if (error)
//       return false;

//     doc[LED_LABEL] = ledPin;
//     doc[BOOL_LABEL] = boolVar;
//     doc[LONG_LABEL] = longVar;
//     doc[FLOAT_LABEL]["value"] = floatVar;
//     doc[STRING_LABEL] = stringVar;
//     serializeJsonPretty(doc, file);
//     file.close();
//     return true;
//   }
//   else
//     Serial.println(F("Configuration file not exist"));
//   return false;
// }

void setup() {
  Serial.begin(115200);

  // FILESYSTEM INIT
  startFilesystem();

  // Load configuration (if not present, default will be created when webserver will start)
#if CLEAR_OPTIONS
  if (myWebServer.clearOptions())
    ESP.restart();
#endif
  if (loadOptions())
    Serial.println(F("Application option loaded\n\n"));
  else
    Serial.println(F("Application options NOT loaded!\n\n"));

  // Configure /setup page and start Web Server

  // Add a new options box
  myWebServer.addOptionBox("My Options");
  myWebServer.addOption(LED_LABEL, ledPin);
  myWebServer.addOption(LONG_LABEL, longVar);
  // Float fields can be configured with min, max and step properties
  myWebServer.addOption(FLOAT_LABEL, floatVar, 0.0, 100.0, 0.01);
  myWebServer.addOption(STRING_LABEL, stringVar);
  myWebServer.addOption(BOOL_LABEL, boolVar);

  // Add a new options box
  myWebServer.addOptionBox("ThingsBoard");
  myWebServer.addOption(TB_SERVER, tb_serverIP);
  myWebServer.addOption(TB_PORT, tb_port);
  myWebServer.addOption(TB_DEVICE_KEY, tb_device_key);
  myWebServer.addOption(TB_SECRET_KEY, tb_secret_key);
  myWebServer.addOption(TB_DEVICE_TOKEN, tb_deviceToken);

  // Add a new options box with custom code injected
  myWebServer.addOptionBox("Custom HTML");
  // How many times you need (for example one in different option box)
  myWebServer.addHTML(custom_html, "fetch-test", /*overwite*/ true);
  // Only once (CSS and Javascript will be appended to head and body)
  myWebServer.addCSS(custom_css, /*overwite*/ false);
  myWebServer.addJavascript(custom_script, /*overwite*/ false);

  // Try to connect to stored SSID, start AP if fails after timeout
  IPAddress myIP = myWebServer.startWiFi(15000, "ESP_AP", "123456789" );
  myWebServer.addHandler("/led", HTTP_GET, handleLed);
  myWebServer.addHandler("/temperature", HTTP_GET, handleTemp);

  // Start webserver
  if (myWebServer.begin()) {
    Serial.print(F("\nESP Web Server started on IP Address: "));
    Serial.println(myIP);
    Serial.println(F("Open /setup page to configure optional parameters"));
    Serial.println(F("Open /edit page to view and edit files"));
    Serial.println(F("Open /update page to upload firmware and filesystem updates\n\n"));
  }
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  myWebServer.run();
}
void handleLed() {
  WebServerClass* webRequest = myWebServer.getRequest();

  // http://xxx.xxx.xxx.xxx/led?val=1
  if(webRequest->hasArg("val")) {
    int value = webRequest->arg("val").toInt();
    digitalWrite(ledPin, value);
  }

  String reply = "LED is now ";
  reply += digitalRead(ledPin) ? "OFF" : "ON";
  webRequest->send(200, "text/plain", reply);
}

void handleTemp() {
  WebServerClass* webRequest = myWebServer.getRequest();

  String reply = "45";
  webRequest->send(200, "text/plain", reply);
}
cotestatnt commented 1 year ago

@arimukhlas I've tried your sketch and it works...

Are you sure about flash erase on upload? If some wrong configuration file is still in flash, it can cause problems

image

arimukhlas commented 1 year ago

@arimukhlas I've tried your sketch and it works...

Are you sure about flash erase on upload? If some wrong configuration file is still in flash, it can cause problems

image

Oh, I'm sorry... when I implement on examples/simpleServer, its work perfectly... I can show value, but when I implement on examples/customHTML, it can not show value, I dont know why...

are you use my code above?

cotestatnt commented 1 year ago

Yes of course.

Paste & copy directly from your post (all in one tab) and then uploaded on a ESP32 dev board erasing all the flash content.

static const char custom_html[] PROGMEM = R"EOF(
<main class="container">
      <article class="grid">
        <div>
          <hgroup>
            <h1>ESP FS WebServer - LED Switcher</h1>
          </hgroup>
          <label for="remember">
            <input type="checkbox" role="switch" id="toggle-led" name="toggle-led">
            Toggle built-in LED
          </label>
          <br>
          <p id="esp-response"></p>
        </div>
      </article>
    </main>
<p id="label1"></p>
)EOF";

static const char custom_css[] PROGMEM = R"EOF(
pre{
    font-family: Monaco,Menlo,Consolas,'Courier New',monospace;
    color: #333;
    line-height: 20px;
    background-color: #f5f5f5;
    border: 1px solid rgba(0,0,0,0.15);
    border-radius: 6px;
    overflow-y: scroll;
    min-height: 350px;
    font-size: 85%;
}
.select{
  width: 25%;
  height:40px;
  padding-top: 10px;
  padding-left: 20px;
  border:1px solid #ccc;
  border-radius: 6px;
  box-shadow: 0 1px 2px 0 rgba(220, 220, 230, 0.5);
}
.body{
  background-color: cadetblue;
}
)EOF";

static const char custom_script[] PROGMEM = R"EOF(
function toggleLed() {
  const pars = new URLSearchParams({
    val:  document.getElementById('toggle-led').checked ? '1' : '0'
  });

  fetch('/led?' + pars )                // Do the request
  .then(response => response.text())    // Parse the response 
  .then(text => {                       // DO something with response
    console.log(text);
    document.getElementById('esp-response').innerHTML = text + ' <i>(Builtin LED is ON with a low signal)</i>';
  });
}

// Add event listener to the LED checkbox (the function will be called on every change)
document.getElementById('toggle-led').addEventListener('change', toggleLed );

setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      document.getElementById("label1").innerHTML = this.responseText;
    }
  };
  xhttp.open("GET", "/temperature", true);
  xhttp.send();
}, 5000 ) ;
)EOF";

#include <esp-fs-webserver.h>  // https://github.com/cotestatnt/esp-fs-webserver

#include <FS.h>
#include <LittleFS.h>
#define FILESYSTEM LittleFS

#ifndef LED_BUILTIN
#define LED_BUILTIN 2
#endif

// Set this to 1 if you want clear the /config.json file at startup
#define CLEAR_OTIONS 0

struct tm sysTime;

// Test "options" values
uint8_t ledPin = LED_BUILTIN;
bool boolVar = true;
uint32_t longVar = 1234567890;
float floatVar = 15.5F;
String stringVar = "Test option String";

// ThingsBoard varaibles
String tb_deviceToken = "xxxxxxxxxxxxxxxxxxx";
String tb_device_key = "xxxxxxxxxxxxxxxxxxx";
String tb_secret_key = "xxxxxxxxxxxxxxxxxxx";
String tb_serverIP = "192.168.1.1";
uint16_t tb_port = 8181;

// Var labels (in /setup webpage)
#define LED_LABEL "The LED pin number"
#define BOOL_LABEL "A bool variable"
#define LONG_LABEL "A long variable"
#define FLOAT_LABEL "A float variable"
#define STRING_LABEL "A String variable"

#define TB_SERVER "ThingsBoard server address"
#define TB_PORT "ThingsBoard server port"
#define TB_DEVICE_TOKEN "ThingsBoard device token"
#define TB_DEVICE_KEY "Provisioning device key"
#define TB_SECRET_KEY "Provisioning secret key"

// Timezone definition to get properly time from NTP server
#define MYTZ "CET-1CEST,M3.5.0,M10.5.0/3"
struct tm Time;

#ifdef ESP8266
ESP8266WebServer server(80);
#elif defined(ESP32)
WebServer server(80);
#endif

FSWebServer myWebServer(FILESYSTEM, server);

////////////////////////////////  Filesystem  /////////////////////////////////////////
void startFilesystem() {
  // FILESYSTEM INIT
  if (FILESYSTEM.begin()) {
    File root = FILESYSTEM.open("/", "r");
    File file = root.openNextFile();
    while (file) {
      const char* fileName = file.name();
      size_t fileSize = file.size();
      Serial.printf("FS File: %s, size: %lu\n", fileName, (long unsigned)fileSize);
      file = root.openNextFile();
    }
    Serial.println();
  } else {
    Serial.println(F("ERROR on mounting filesystem. It will be formmatted!"));
    FILESYSTEM.format();
    ESP.restart();
  }
}

////////////////////  Load application options from filesystem  ////////////////////
/*
* Unlike what was done in customOptions.ino, in this example
* the variables are read (and written) all at once using the ArduinoJon library
*/
bool loadOptions() {
  if (FILESYSTEM.exists(myWebServer.configFile())) {
    File file = FILESYSTEM.open(myWebServer.configFile(), "r");
    DynamicJsonDocument doc(file.size() * 1.33);
    if (!file)
      return false;

    DeserializationError error = deserializeJson(doc, file);
    if (error)
      return false;

    ledPin = doc[LED_LABEL];
    boolVar = doc[BOOL_LABEL];
    longVar = doc[LONG_LABEL];
    floatVar = doc[FLOAT_LABEL]["value"];
    stringVar = doc[STRING_LABEL].as<String>();

    tb_deviceToken = doc[TB_DEVICE_TOKEN].as<String>();
    tb_device_key = doc[TB_DEVICE_KEY].as<String>();
    tb_secret_key = doc[TB_SECRET_KEY].as<String>();
    tb_serverIP = doc[TB_SERVER].as<String>();
    tb_port = doc[TB_PORT];

    file.close();

    Serial.println();
    Serial.printf("LED pin value: %d\n", ledPin);
    Serial.printf("Bool value: %d\n", boolVar);
    Serial.printf("Long value: %d\n", longVar);
    Serial.printf("Float value: %d.%d\n", (int)floatVar, (int)(floatVar * 1000) % 1000);
    Serial.println(stringVar);
    return true;
  } else
    Serial.println(F("Configuration file not exist"));
  return false;
}

void setup() {
  Serial.begin(115200);

  // FILESYSTEM INIT
  startFilesystem();

  // Load configuration (if not present, default will be created when webserver will start)
#if CLEAR_OPTIONS
  if (myWebServer.clearOptions())
    ESP.restart();
#endif
  if (loadOptions())
    Serial.println(F("Application option loaded\n\n"));
  else
    Serial.println(F("Application options NOT loaded!\n\n"));

  // Configure /setup page and start Web Server

  // Add a new options box
  myWebServer.addOptionBox("My Options");
  myWebServer.addOption(LED_LABEL, ledPin);
  myWebServer.addOption(LONG_LABEL, longVar);
  // Float fields can be configured with min, max and step properties
  myWebServer.addOption(FLOAT_LABEL, floatVar, 0.0, 100.0, 0.01);
  myWebServer.addOption(STRING_LABEL, stringVar);
  myWebServer.addOption(BOOL_LABEL, boolVar);

  // Add a new options box
  myWebServer.addOptionBox("ThingsBoard");
  myWebServer.addOption(TB_SERVER, tb_serverIP);
  myWebServer.addOption(TB_PORT, tb_port);
  myWebServer.addOption(TB_DEVICE_KEY, tb_device_key);
  myWebServer.addOption(TB_SECRET_KEY, tb_secret_key);
  myWebServer.addOption(TB_DEVICE_TOKEN, tb_deviceToken);

  // Add a new options box with custom code injected
  myWebServer.addOptionBox("Custom HTML");
  // How many times you need (for example one in different option box)
  myWebServer.addHTML(custom_html, "fetch-test", /*overwite*/ true);
  // Only once (CSS and Javascript will be appended to head and body)
  myWebServer.addCSS(custom_css, /*overwite*/ false);
  myWebServer.addJavascript(custom_script, /*overwite*/ false);

  // Try to connect to stored SSID, start AP if fails after timeout
  IPAddress myIP = myWebServer.startWiFi(15000, "ESP_AP", "123456789");
  myWebServer.addHandler("/led", HTTP_GET, handleLed);
  myWebServer.addHandler("/temperature", HTTP_GET, handleTemp);

  // Start webserver
  if (myWebServer.begin()) {
    Serial.print(F("\nESP Web Server started on IP Address: "));
    Serial.println(myIP);
    Serial.println(F("Open /setup page to configure optional parameters"));
    Serial.println(F("Open /edit page to view and edit files"));
    Serial.println(F("Open /update page to upload firmware and filesystem updates\n\n"));
  }
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  myWebServer.run();
}
void handleLed() {
  WebServerClass* webRequest = myWebServer.getRequest();

  // http://xxx.xxx.xxx.xxx/led?val=1
  if (webRequest->hasArg("val")) {
    int value = webRequest->arg("val").toInt();
    digitalWrite(ledPin, value);
  }

  String reply = "LED is now ";
  reply += digitalRead(ledPin) ? "OFF" : "ON";
  webRequest->send(200, "text/plain", reply);
}

void handleTemp() {
  WebServerClass* webRequest = myWebServer.getRequest();

  String reply = "45";
  webRequest->send(200, "text/plain", reply);
}
arimukhlas commented 1 year ago

Oh oke, I upload on wemos D1 mini, html can not show value but when I upload on my ESP32 Dev Kit V1, it show the value

is there any different config between esp32 and esp8266?

arimukhlas commented 1 year ago

Oh oke, I upload on wemos D1 mini, html can not show value but when I upload on my ESP32 Dev Kit V1, it show the value

is there any different config between esp32 and esp8266?

Done, I dont know why, but I try flash all and reupload on my wemos, custom html show the value