ESP8266 Webserver -> Replace somthing with a string

Hi Friends,

I have a question to programming a wWebserver. The complete code on the bottom.

My basic question is:

I wanna replace words, with strings in the index_html-String. For example:

%ReadTemperatur% replace it with String = analogRead(A0);

or

%ReadPinstatus% replace it with String = digitalRead(4);

I hope you understand what I mean :smiley: My aim is:

everytime I refresh the index_html, I need the Pin-Status.

Or is there another way ? For example, put a Sting between the index_html-String ?

Notice: I am using the <ESP8266WebServer.h> library.

Here is the full Code I am using:

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

char * ssid_ap = "MYSSID";
char * password_ap = "11111111";

IPAddress ip(192,168,11,4); // arbitrary IP address (doesn't conflict w/ local network)
IPAddress gateway(192,168,11,1);
IPAddress subnet(255,255,255,0);

ESP8266WebServer server;

const char index_html[] PROGMEM = R"rawliteral(<html>
  <body>
   <p>
    <form action="/action_page"><input type="hidden" name="input" value="1"><input type="submit" value="Sensor On"></form>
   </p>
   <p>
    <form action="/action_page"><input type="hidden" name="input" value="2"><input type="submit" value="Sensor On"></form>
   </p>
   <p>%FOR-EXAMPLE-THIS-COULD-BE-REPLACED%</p>
  </body></html>)rawliteral";

String PageNotFound = "<center><p style=\"font-size:30px;\">Page not found!</p>";
String UnknownCommand = "<center><p style=\"font-size:30px;\">Unknown command</p>";
String InputReaktion1 = "<center><p style=\"font-size:30px;\">yeaaaaah.</p>";
String InputReaktion2= "<center><p style=\"font-size:30px;\">noooooo.</p>";

void notFound() {
  server.send(200,"text/html",String(PageNotFound));
  }

void handleForm() {
 String CheckField = server.arg("input"); 
 if(CheckField  == "1") {
  server.send(200, "text/html", InputReaktion1);
  }
  if(CheckField  == "2") {
  server.send(200, "text/html", InputReaktion2);
  }
 }

void setup() {
  WiFi.mode(WIFI_AP);
  WiFi.softAPConfig(ip,gateway,subnet);
  WiFi.softAP(ssid_ap,password_ap);
  Serial.begin(115200);

  server.on("/", HTTP_GET, []{server.send(200,"text/html",String(index_html));});
  server.on("/action_page", handleForm);
  server.onNotFound(notFound);
  server.begin();
}

void loop() {
  server.handleClient();
}

The code that you are using is defining the characters as a big array of char stored in progmem (the flash-memory of the ESP8266.

based on this principle you would have to find the exact place where to put your data into the array - and in case the used array-length would change to move the data into RAM and then re-arrange the characters.

This would be a big hassle.

It is much more convenient to use a different principle to send the html-data by using the command client.print()

random nerd tutorials has an example for this

best regards Stefan

Sorry, but your suggestion code does not work, because it has to work as an AP.

OK so you can try this code.

/*********
  Rui Santos
  Complete project details at http://randomnerdtutorials.com
*********/

// Load Wi-Fi library
#include <ESP8266WiFi.h>

// Replace with your network credentials
//const char* ssid     = "FRITZ!Box 7490";
//const char* password = "80841232631090916208";


char * ssid_ap = "MYSSID";
char * password_ap = "11111111";

IPAddress ip(192,168,11,4); // arbitrary IP address (doesn't conflict w/ local network)
IPAddress gateway(192,168,11,1);
IPAddress subnet(255,255,255,0);

// Set web server port number to 80
WiFiServer server(80);

// Variable to store the HTTP request
String header;

// Auxiliar variables to store the current output state
String output5State = "off";
String output4State = "off";

// Assign output variables to GPIO pins
const int output5 = 5;
const int output4 = 4;

// Current time
unsigned long currentTime = millis();
// Previous time
unsigned long previousTime = 0;
// Define timeout time in milliseconds (example: 2000ms = 2s)
const long timeoutTime = 2000;

void setup() {
  Serial.begin(115200);
  // Initialize the output variables as outputs
  pinMode(output5, OUTPUT);
  pinMode(output4, OUTPUT);
  // Set outputs to LOW
  digitalWrite(output5, LOW);
  digitalWrite(output4, LOW);

  WiFi.mode(WIFI_AP);
  WiFi.softAPConfig(ip, gateway, subnet);
  WiFi.softAP(ssid_ap, password_ap);
  /*
    // Connect to Wi-Fi network with SSID and password
    Serial.print("Connecting to ");
    Serial.println(ssid);
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
    }
    // Print local IP address and start web server
    Serial.println("");
    Serial.println("WiFi connected.");
  */
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  server.begin();
}

void loop() {
  WiFiClient client = server.available();   // Listen for incoming clients

  if (client) {                             // If a new client connects,
    Serial.println("New Client.");          // print a message out in the serial port
    String currentLine = "";                // make a String to hold incoming data from the client
    currentTime = millis();
    previousTime = currentTime;
    while (client.connected() && currentTime - previousTime <= timeoutTime) { // loop while the client's connected
      currentTime = millis();
      if (client.available()) {             // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        Serial.write(c);                    // print it out the serial monitor
        header += c;
        if (c == '\n') {                    // if the byte is a newline character
          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();

            // turns the GPIOs on and off
            if (header.indexOf("GET /5/on") >= 0) {
              Serial.println("GPIO 5 on");
              output5State = "on";
              digitalWrite(output5, HIGH);
            } else if (header.indexOf("GET /5/off") >= 0) {
              Serial.println("GPIO 5 off");
              output5State = "off";
              digitalWrite(output5, LOW);
            } else if (header.indexOf("GET /4/on") >= 0) {
              Serial.println("GPIO 4 on");
              output4State = "on";
              digitalWrite(output4, HIGH);
            } else if (header.indexOf("GET /4/off") >= 0) {
              Serial.println("GPIO 4 off");
              output4State = "off";
              digitalWrite(output4, LOW);
            }

            // Display the HTML web page
            client.println("<!DOCTYPE html><html>");
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");
            // CSS to style the on/off buttons
            // Feel free to change the background-color and font-size attributes to fit your preferences
            client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
            client.println(".button { background-color: #195B6A; border: none; color: white; padding: 16px 40px;");
            client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
            client.println(".button2 {background-color: #77878A;}</style></head>");

            // Web Page Heading
            client.println("<body><h1>ESP8266 Web Server</h1>");

            // Display current state, and ON/OFF buttons for GPIO 5
            client.println("<p>GPIO 5 - State " + output5State + "</p>");
            // If the output5State is off, it displays the ON button
            if (output5State == "off") {
              client.println("<p><a href=\"/5/on\"><button class=\"button\">ON</button></a></p>");
            } else {
              client.println("<p><a href=\"/5/off\"><button class=\"button button2\">OFF</button></a></p>");
            }

            // Display current state, and ON/OFF buttons for GPIO 4
            client.println("<p>GPIO 4 - State " + output4State + "</p>");
            // If the output4State is off, it displays the ON button
            if (output4State == "off") {
              client.println("<p><a href=\"/4/on\"><button class=\"button\">ON</button></a></p>");
            } else {
              client.println("<p><a href=\"/4/off\"><button class=\"button button2\">OFF</button></a></p>");
            }
            client.println("</body></html>");

            // The HTTP response ends with another blank line
            client.println();
            // Break out of the while loop
            break;
          } else { // if you got a newline, then clear currentLine
            currentLine = "";
          }
        } else if (c != '\r') {  // if you got anything else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine
        }
      }
    }
    // Clear the header variable
    header = "";
    // Close the connection
    client.stop();
    Serial.println("Client disconnected.");
    Serial.println("");
  }
}

I just added your lines of code that setup the ESP8266

best regards Stefan

Hey mate,

I have just resolved it (see below).

The only concern I have, is that what you menstioned: the RAM.

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>


char * ssid_ap = "testing";
char * password_ap = "11111111";


IPAddress ip(192,168,11,4); // arbitrary IP address (doesn't conflict w/ local network)
IPAddress gateway(192,168,11,1);
IPAddress subnet(255,255,255,0);
ESP8266WebServer server;


String index_html = R"rawliteral(<html>
  <body>
   <p>
    <form action="/action_page"><input type="hidden" name="input" value="1"><input type="submit" value="Sensor On"></form>
   </p>
   <p>
    <form action="/action_page">aa<input type="hidden" name="input" value="2"><input type="submit" value="Sensor Off"></form>
   </p>
   <p>aa <-- this will be replaced</p>
  </body></html>)rawliteral";


String PageNotFound = "<center><p style=\"font-size:30px;\">Page not found!</p>";
String UnknownCommand = "<center><p style=\"font-size:30px;\">Unknown command</p>";
String InputReaktion1 = "<center><p style=\"font-size:30px;\">yeaaaaah.</p>";
String InputReaktion2= "<center><p style=\"font-size:30px;\">noooooo.</p>";


void notFound() {
  server.send(200,"text/html",String(PageNotFound));
  }


void handleForm() {
 String CheckField = server.arg("input"); 
 if(CheckField  == "1") {
  server.send(200, "text/html", InputReaktion1);
  }
  if(CheckField  == "2") {
  server.send(200, "text/html", InputReaktion2);
  }
 }



void setup() {
  WiFi.mode(WIFI_AP);
  WiFi.softAPConfig(ip,gateway,subnet);
  WiFi.softAP(ssid_ap,password_ap);
  Serial.begin(115200);

  server.on("/", HTTP_GET, []{server.send(200,"text/html",String(index_html));});
  server.on("/action_page", handleForm);
  server.onNotFound(notFound);
  server.begin();
}


void loop() {

String aa="aa";
String bb="bb";
index_html.replace(aa, bb);
server.handleClient();
}

I don't use variable-type String
the reason is this

if it has a function like "replace" pretty good.
Some users say an ESP8266 has enough RAM to deal with it.

Well you could do a test:
with a loop that will modify the string with function replaces something inside the String over and over again and let it run for a day testing if the code will crash or not

best regards Stefan

Okay, that's what I am concerned of :confused:

How should I do it ? Or is there a way to clean up the RAM from day to day ?
Yes, the ESP has to run for years. I'm gonna read your article above later this evening.

maybe I should put the string into a file via SPIFFS ? Or using a char, instead of String ?

Take a look at the source code for the various Arduino ESP8266 core libraries, you'll see that the String class is used extensively. I wouldn't worry too much about OP's incremental use of it.

@gfvalvo

did you do a real hard test of million times repeated new assinging different content to a variable of type String??

There is an alternative that not every user would recommend and is arguing against it.

SafeString

I'm very sure that with with SafeString a code-crash caused by too much RAM usage through a string can never occur: because assigning too many characters to a SafeString the too many characters will just be dropped.

best regards Stefan

okay, is "Safestring" the solution ?

Regards, from Germany

I'm simply saying the String class is used throughout the ESP8266 core code. In fact, the ESP8266 would not exist without that class so you wouldn't even be able to use it in the Arduino ecosystem. Given that it's used EVERYWHERE in the core, what do you think the chances are that @hello_freinds's dinky little code will bring the whole thing crashing down?

I don't know exactly because I haven't studied the core-code.
I remember that some users said if the String is defined inside a function that is entered and left again the used RAM will be freed. If the core-code does it always this way everything is fine.
But what happens if a newcomer defines a String globally?
If the String is defined globally the RAM used for this String will never be freed.

I don't have a decent understanding how it works but this made me careful and made me using SafeString.

I say yes

Okay friend. I will try it. But I'm gonna definitevly read the article you posted before. There are core working methods of our code on RAMs.

Good mornig. I have tried SafeString. It worked. But when make larger Stringe which are spreaded over multiple lines, then it did not work.

cSF(msgStr, 300, "Hello World!"); // this works

cSF(msgStr, 300, "
Hello World!
"); // this does not work, because it is spread on multiple lines

And my second problem is: I still can not replace some characters with another variable from a String. It is not easy as it is on Arduinos replace() function.

Arduino works like this:

MyString = replace("toBeReplaced", "replacement");

But I guess, in SafeString, it works a little different :smiley:

The variable-type String is based on changeable length of the string. This is the reason why String occupies always new RAM on each change of the variables content.

SafeString is based on a fixed length. ==> usage of RAM stays constant.

Somehow the reference is not easy to find
https://www.forward.com.au/pfod/ArduinoProgramming/SafeString/docs/html/class_safe_string.html#a57cec86ff5ada9de33835d6b7042175c

the basic syntax is

#include "SafeString.h"
createSafeString(stringOne, 35);
stringOne.replace(arguments);

best regards Stefan

sounds logical :slight_smile:

Hey guys :smiley:

First of all: thanks for helping me. I just have found the solution for "not hurting the RAM by huge Strings".

I am happy. I will post ist here in a few minutes :smiley:

yayyyy

The solution will be marked by comments in my code itself :smiley:

Here is my solution:

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

char * ssid_ap = "testing";
char * password_ap = "11111111";


/////////////////////////////////////////////////////////Hey falks !!! Thanks again!
/////////////////////////////////////////////////////////here are 6 STringe (relatively short)
/////////////////////////////////////////////////////////it is that easy :D now just implement
/////////////////////////////////////////////////////////this 6 strings, into the server.send() below;
String index_html1 = R"rawliteral(<html><head>
  <body><p>test1
  </p>)rawliteral";
  
String index_html2 = R"rawliteral(<p>test3
  </p>)rawliteral";
  
String index_html3 = R"rawliteral(<p>test4
  </p>)rawliteral";
  
String index_html4 = R"rawliteral(<p>test4
  </p>)rawliteral";
  
String index_html5 = R"rawliteral(<p>test5
  </p>)rawliteral";
  
String index_html6 = R"rawliteral(<p>test6</p>
  </body></html>)rawliteral";


void setup() {
  WiFi.mode(WIFI_AP);
  WiFi.softAP(ssid_ap,password_ap);


/////////////////////////////////////////////////////////so as you can see, here are 6 Strings seperately inserted into the server.send();
/////////////////////////////////////////////////////////it is that easy... now the RAM is going to have a good time 
  server.on("/", HTTP_GET, []{server.send(200,"text/html",String(index_html1 + index_html2 + index_html3 + index_html4 + index_html5 + index_html6));});
  server.begin();
}

void loop() {
server.handleClient();
}