How to embed variables in html text for a website [solved]

Dear colleagues.
I am busy with a weather station, where values of sensors connected to ESP-01S are sent via my wifi network to an ESP8266, which in turn transmits data to an Arduino Uno/ESP-01S for display on a website.
My problem: How to embed the varying sensor values within the html text of the website (e.g. variables tmain, tmainmin, etc. in the code below).
I would appreciate your help. If this question has been answered already, please, point me to the relevant posts.
Addition: I realize now that I cannot use variables if the entire html string is a constant. Maybe I must divide the html code into constants and variables and concatenate them?


```cpp
#include <SoftwareSerial.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
const char* ssid = "***"; 
const char* password = "!!!";
float tmain = 24.0; // just an example, the value will later be retrieved 
float tmainmin;
float tmainmax;
float hmain;
float pmain;
float t2av;
float t2min;
float t2max;
String t2avS;
String t2minS;
String t2maxS;
IPAddress local_IP(192,168,68,107); // to fix the url
IPAddress gateway(192,168,68,1);
IPAddress subnet(255,255,255,0);

ESP8266WebServer server(80);
// Your entire HTML content embedded as a raw string literal
const char htmlContent[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>House in Ruimsig</title>
</head>
<body>
<h2>House in Ruimsig</h2>
<h3>Mainstation</h3>
<table>
  <tr>
    <td>current:</td>
    <td style="text-align: right;">tmain<span>&#176;</span>C</td> // the variable tmain needs to be displayed/updated
    </tr>
  <tr>
    <td>minimum:</td>
    <td style="text-align: right;">tmainmin<span>&#176;</span>C</td>
   </tr>
  <tr>
    <td>maximum:</td>
    <td style="text-align: right;">tmainmax<span>&#176;</span>C</td>
   </tr>
  <tr><td>--------------</td><td>--------</td></tr>
  <tr>
    <td>humidity:</td>
    <td style="text-align: right;">47 %</td>
   </tr>
  <tr>
    <td>pressure:</td>
    <td style="text-align: right;">850 hPa</td>
   </tr>
</table>

<h3>Outside</h3>
<table>
  <tr>
    <td>current:</td>
    <td style="text-align: right;">25.3 <span>&#176;</span>C</td>
    </tr>
  <tr>
    <td>minimum:</td>
    <td style="text-align: right;">5.3 <span>&#176;</span>C</td>
    </tr>
  <tr>
    <td>maximum:</td>
    <td style="text-align: right;">26.0 <span>&#176;</span>C</td>
   </tr>
</table>
    <!-- Your content goes here -->
</body>
</html>  
)rawliteral";

// Function to handle root path
void handleRoot()
{
  server.send_P(200, "text/html", htmlContent);
}


void setup()
{
  Serial.begin(115200);
  delay(10);
  WiFi.config(local_IP, gateway, subnet); // configure a fixed IP address for the server
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.print("Connected! IP address: ");
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot);
  server.begin();
  Serial.println("HTTP server started");
}

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

have a look at esp32-web-server-sent-events-sse or esp8266-nodemcu-web-server-sent-events-sse

There are several techniques for this.
One is to embed place holders in the HTML for the variables in the static HTML then replace the place holders with actual values by editing them before sending. This code uses such a trick: arduino/esp8266-ledclock at master · buxtronix/arduino · GitHub
Another is to embed javascript in the static HTML which, when loaded in the browser, makes a return trip to the server and fetches the variables and populates the HTML fields.
Yet another is to stream out the HTML in interleaved chunks alternating between HTML code and variables.

The "related topics" section appended at the end of this thread also has a few suggestions.

Welcome to the forum

You can do things like this


const char* htmlHeader = R"(
  <html>
    <head><title>ESP32 Server</title></head>
    <body>
      <h1>Hello World!</h1>
      <p>X is %d</p>
      <p>Y is %d</p>
    </body>
  </html>
)";

void setup()
{
    Serial.begin(115200);
    int X = 123;
    int Y = -123;
    char buffer[250];
    snprintf(buffer, sizeof(buffer), htmlHeader, X, Y);
    Serial.println(buffer);
}

void loop() {}

This is what I do. I also use String reserve() for the final string to minimize memory fragmentation.

As noted above there are other options. To me this is the simplest, even if it is a bit more code.

EDIT: For an example see the handleRoot() function in this code.

This is actually something that AI would probably get right the first time ;-)

I use relative javascript files to read data or perform functions.

<script src="script.js"></script>

I use HTML for formatting in a simple, static template.

The current ESPAsyncWebServer, which should work on ESP8266 and ESP32, supports templates. Their Templates example has the web page with placeholder

static const char *htmlContent PROGMEM = R"(
<!DOCTYPE html>
<html>
<body>
    <h1>Hello, %USER%</h1>
</body>
</html>
)";

Then you get a callback later

  server.serveStatic("/dynamic.html", LittleFS, "/template.html").setTemplateProcessor([](const String &var) -> String {
    if (var == "USER") {
      return String("Bob ") + millis();
    }
    return asyncsrv::emptyString;
  });

The example shows a few different ways to handle result caching.

Thank you, horace, for the links! Unfortunately, the events route is above my iq grade :-).
I got another ide from UKHeliBob that was easy enough for me to modify and implement.

Thanks for the suggestions 6v6gt! UKHeliBob had a similar idea and provided some code, which I could use for my purpose after modification.

Thanks for the idea and the code, UKHeliBob!
I have used it in my sketch after a bit of modification... actually only using "%s" for a string placeholder since "%d" did not work for my float values.

I am glad that you got it working and that my example was helpful.

Thank you, oldcurmudgeon! I came right with a code from UKHeliBOB with only few modifications.

Yes, it helped a lot. Exactly what I was looking for as a newcomer. It was easy enough to understand and integrate in my sketch. Constantly updating via events would maybe be better, but just to difficult for me right now.
I will post the solution later.

Hi xfpd.
Thanks for your reply. I came right with the sketch of another colleague.

I am sure there are some clever ways to do it but I have never been comfortable with the workings of server/client code used by web pages so I kept it simple. Using raw text also helped because it allowed the text to be set out sensibly and using snprintf() was already familiar to me.

Hi kenb4.
Thanks for your suggestion! It looks easy enough for me, and I might try it in future. For now, I took a sketch from UKHeliBob and integrated it into my sketch.

Dear all.
Firstly, thanks for all you replies, much appreciated.
I got a solution using a sketch from UKHeliBob, where placeholders (%s) are used for the temperature values/strings, which then get updated/defined.
See the new sketch below with comments on the changes.

#include <SoftwareSerial.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
const char* ssid = "***"; // fill in your own credentials
const char* password = "!!!";
float tmain = 24.0; // just examples, the values will later be retrieved 
float tmainmin = 5.3;
float tmainmax = 30.1;
String tmainS;
String tmainminS;
String tmainmaxS;
float hmain = 47;
float pmain = 850;
String hmainS;
String pmainS;
float t2av = 23.5;
float t2min = 4.2;
float t2max = 27.9;
String t2avS;
String t2minS;
String t2maxS;
IPAddress local_IP(192,168,68,107);
IPAddress gateway(192,168,68,1);
IPAddress subnet(255,255,255,0);

char htmlfullContent[1250];

ESP8266WebServer server(80);
// preliminary html content embedded as a raw string literal with placeholders
const char htmlpreContent[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>House in Ruimsig</title>
</head>
<body>
<h2>House in Ruimsig</h2>
<h3>Mainstation</h3>
<table>
  <tr>
    <td>current:</td>
    <td style="text-align: right;">%s <span>&#176;</span>C</td> // %s is placeholder for a string (each time a different one)
    </tr>
  <tr>
    <td>minimum:</td>
    <td style="text-align: right;">%s <span>&#176;</span>C</td> // see above
   </tr>
  <tr>
    <td>maximum:</td>
    <td style="text-align: right;">%s <span>&#176;</span>C</td>
   </tr>
  <tr><td>--------------</td><td>--------</td></tr>
  <tr>
    <td>humidity:</td>
    <td style="text-align: right;">%s <span>&#37</span></td>
   </tr>
  <tr>
    <td>pressure:</td>
    <td style="text-align: right;">%s hPa</td>
   </tr>
</table>

<h3>Outside</h3>
<table>
  <tr>
    <td>current:</td>
    <td style="text-align: right;">%s <span>&#176;</span>C</td>
    </tr>
  <tr>
    <td>minimum:</td>
    <td style="text-align: right;">%s <span>&#176;</span>C</td>
    </tr>
  <tr>
    <td>maximum:</td>
    <td style="text-align: right;">%s <span>&#176;</span>C</td>
   </tr>
</table>
</body>
</html>  
)rawliteral";

// Function to handle root path
void handleRoot()
{
  server.send_P(200, "text/html", htmlfullContent); 
}


void setup()
{
  Serial.begin(115200);
  delay(10);
  WiFi.config(local_IP, gateway, subnet); // configure a fixed IP address for the server
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.print("Connected! IP address: ");
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot);
  server.begin();
  Serial.println("HTTP server started");

//char buffer[1250];
tmainS = String(tmain,1);
tmainminS = String(tmainmin,1);
tmainmaxS = String(tmainmax,1);
hmainS = String(hmain,0);
pmainS = String(pmain,0);
t2avS = String(t2av,1);
t2minS = String(t2min,1);
t2maxS = String(t2max,1);
 // generate the full html content by sequentially replacing all placeholders (%s) with the respective strings
snprintf(htmlfullContent, sizeof(htmlfullContent), htmlpreContent, tmainS, tmainminS, tmainmaxS, hmainS, pmainS, t2avS, t2minS, t2maxS);
Serial.println(htmlfullContent); // just to check that all placeholders are now filled
}

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