Multiple Webserver Pages in Master WebServer Page (problem with iframe)

For my project I have a page on 1 master web server in which I access multiple web servers via inline frames (iframe html object).

Everything works fine but when a device is turned off, I get a "page not found" kind of error in the corresponding iframe.
I actually want to check before I put an iframe in the page whether the device (web server) is turned on. If the device is turned off I want to show some text. Can anyone help me with that?

Underneath my .ino (file)

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

#include "htmSource.h" //Our external HTML webpage

//SSID and Password of your WiFi router
const char* ssid = "xxxxx";
const char* password = "xxxxxx";

ESP8266WebServer server(80); //Server on port 80

//===============================================================
// This routine is executed when you open its IP in browser
//===============================================================
void handleRoot() {
  String s = FPSTR(extHTML); //Read HTML contents
  server.send(200, "text/html", s); //Send web page
}
void setup(void){
  Serial.begin(9600);
  
  WiFi.begin(ssid, password);     //Connect to your WiFi router
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  //If connection successful show IP address in serial monitor
    Serial.println("");
    Serial.print("Connected to ");
    Serial.println(ssid);
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());  //IP address assigned to your ESP
  
    server.on("/", handleRoot);      //Which routine to handle at root location

    server.begin();                  //Start server
    Serial.println("HTTP server started");
}
void loop(void){
  server.handleClient();          //Handle client requests
}

// end script!!

and htmSource.h (file)

const char extHTML[] PROGMEM = R"=====(
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { background-color: #1f2c34}
iframe{ overflow: hidden; height: 40px; width: 90px; border: 1px solid white; }
</style>
</head>
<body>

<iframe src="http://192.168.178.25" scrolling="no" ></iframe><br><br>
<iframe src="http://192.168.178.68" scrolling="no"></iframe><br><br>
<iframe src="http://192.168.178.72" scrolling="no"></iframe><br>

</body>
</html> 
)=====";

I tried that but somehow I can't get libraries installed that need to be installed with a zip file. Just installing libraries via the library manager is possible, but I don't think ESP8266Ping is listed there.

You could try one of these

1 Like

Many thank Z80,

ESPping did the trick!!!!
I believe after some fidling around, I would be able to make my project work with this jumpstart.
To prefent fidling: any further tips???

ping_succes

Using this inside standard ping example code, gave me the above ping result :

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

etc....

So now I've tweaked my code with the following snippets.

Inside .ino (file):

//===============================================================
// This routine is executed when you open its IP in browser
//===============================================================
const IPAddress ipToPing(192, 168, xxx, xxx);

void handleRoot() {
  String s = FPSTR(extHTMLstart); //Read HTML contents
  
  if(Ping.ping(ipToPing)) { // check if device is active (ip should be ping-able)
    s += "<iframe src=\"http://192.168.xxx.xxx\" scrolling=\"no\"></iframe>"; // embed device page to iframe
  } else { // if device not active
    s += "Device is not active, or some other error ocurred."; // show some information
  }
  
  s += "</body></html>"; // wrap the webpage up
  server.send(200, "text/html", s); //Send web page
}

And my htmSource.h (file) now looks like this:

const char extHTMLstart[] PROGMEM = R"=====(
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { background-color: #1f2c34}
iframe{ overflow: hidden; height: 40px; width: 90px; border: 1px solid white; }
</style>
</head>
<body>
)=====";

Only one more thing to do...
I've to make a for loop somehow to loop trough my 5 devices.
Sugestions ??? :blush:

Pinging 5 addresses before sending a response is probably going to take to long...
So maybe doing it periodically (15 mins for example) and have the html ready in advance would be better.
It is not ideal and there are probably better solutions but it's the best I can do. :slightly_smiling_face:

two alternatives

a) Why is the ping necessary at all? Just request a (small) page from the ESP - if that fails (404) or doesn't get a valid 200/204 HTTP response - the device is not reachable.

b) instead of iframing the content of the other ESPs you could just request the plain data from them and integrate the content in the main page

Noiasca, can you give me some hints about alternative a??

At first I was looking for some kind of sneak peek to test whether device is active or not. Later I came to ping and ZX80 suggested that too so that's why I followed that path.

Anyway: Can you give me a hint on how to program such a small page request?

This works, not very fast (using ESPping library)

#include <ESPping.h>
const char *ips[] = {"192.168.1.5", "192.168.1.77", "192.168.1.88"};
void setup()
{
  Serial.begin(115200);
  delay(100);
  WiFi.begin("b_____", "g________");
  Serial.print("\nConnection");
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nConnection OK, IP: ");
  Serial.println(WiFi.localIP());
}

void loop()
{
  for (int i = 0; i < 3 ; i++) {  //3 ip_________________________________
    Serial.println(ips[i]);
    if (Ping.ping(ips[i]) > 0) {
      Serial.printf(" response time : %d/%.2f/%d ms\n", Ping.minTime(), Ping.averageTime(), Ping.maxTime());
    } else {
      Serial.println(" Error !");
    }
  }
  delay(1000);
}

Really slow

14:37:52.820 -> 192.168.1.5
14:37:57.930 ->  response time : 0/23.28/0 ms
14:37:57.930 -> 192.168.1.77
14:38:02.445 ->  response time : 0/98.33/0 ms
14:38:02.445 -> 192.168.1.88
14:38:06.460 ->  response time : 0/5.19/0 ms
14:38:07.453 -> 192.168.1.5
14:38:11.732 ->  response time : 0/53.89/0 ms
14:38:11.732 -> 192.168.1.77
14:38:16.050 ->  response time : 0/61.18/0 ms
14:38:16.050 -> 192.168.1.88
14:38:20.101 ->  response time : 0/4.75/0 ms

Okey not fast, yet for my purpose kind off okey. Could be beter please Noiasca (post #8) give some hint on opition (a)

image

If I want to do small requests, as mentionted in post #8.
Can you please give some hints to start from?

see the Example

ESP8266HTTPClient / BasicHTTPClient

this will give you the information if your resource was reachable or not:

/**
   Check web sites for availability
   https://forum.arduino.cc/t/multiple-webserver-pages-in-master-webserver-page-problem-with-iframe/1101573/12
   by noiasca
   2023-03-18
*/

#include <Arduino.h>

#ifdef ARDUINO_ARCH_ESP8266            // libraries if you compile for a ESP8266
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>          // for the webserver
//#include <ESP8266mDNS.h>               // Bonjour/multicast DNS, finds the device on network by name
//#include <ArduinoOTA.h>                // OTA Upload via ArduinoIDE
//#include <Preferences.h>               // "preferences" for ESP8266 V2.0.0 by Volodymyr Shymanskyy https://github.com/vshymanskyy/Preferences/
#include <ESP8266HTTPClient.h>
#endif

//#include <credentials5582.h>           // if you have an external file with your credentials you can use it - comment or remove it - if not available

#ifndef STASSID                        // either use an external .h file containing STASSID and STAPSK - or 
//                                     // add defines to your boards - or
#define STASSID "your-ssid"            // ... modify these line to your SSID
#define STAPSK  "your-password"        // ... and set your WIFI password
#endif


// connect to the wifi
void  wifiConnect() {
  const byte wifiActivityPin = 2;
  WiFi.mode(WIFI_STA);
  WiFi.begin(STASSID, STAPSK);
  // Wait for connection
  int8_t i = 0;
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print('.');
    if (wifiActivityPin < 255) digitalWrite(wifiActivityPin, !digitalRead(wifiActivityPin)); // blink one pin as indicator
    i++;
    if (i >= 80) {
      Serial.println();
      i = 0;
    }
  }
  Serial.println();
}

struct Site {
  String resource;            // a web resource
  uint32_t previousMillis;    // timestamp of last check
  int result;                 // result of last check
};

// define the sites to be checked
Site site[] {
  {"http://172.18.67.66", 0, 0},
  {"http://172.18.67.68", 0, 0},
  {"http://172.18.67.100", 0, 0},       // in my testcase this site will fail
};
constexpr size_t noOfSites = sizeof(site) / sizeof(site[0]);

// this function checks each site one by one
// and stores the result in the global structure
void checkOneOfManySites() {
  static uint32_t previousMillis = 0; // time management
  static size_t currentSite = 0;      //index of site to be tested
  WiFiClient client;
  HTTPClient http;
  
  if (millis() - previousMillis > 5000) {
    Serial.print("[HTTP] check "); Serial.println(site[currentSite].resource);
    if (http.begin(client, site[currentSite].resource)) {  // HTTP
      http.setTimeout(1000); // must be set after http.begin! read https://github.com/espressif/arduino-esp32/issues/1433
      int httpCode = http.GET();
      Serial.printf("[HTTP] GET... code: %d\n", httpCode);
      // httpCode will be negative on error
      http.end();
      site[currentSite].previousMillis = millis();
      site[currentSite].result = httpCode;
      currentSite++;
      if (currentSite >= noOfSites) currentSite = 0;
    }
    previousMillis = millis(); // this 
  }
}

// checks all sites if they are available
void checkMySites() {
  WiFiClient client;
  HTTPClient http;

  for (auto & i : site) {
    Serial.print("[HTTP] check "); Serial.println(i.resource);
    if (http.begin(client, i.resource)) {  // HTTP
      int httpCode = http.GET();
      Serial.printf("[HTTP] GET... code: %d\n", httpCode);
      // httpCode will be negative on error
      http.end();
      i.previousMillis = millis();
      i.result = httpCode;
    }
  }
}

// check one site and print the result
void checkOneSite() {
  WiFiClient client;
  HTTPClient http;

  if (http.begin(client, "http://172.18.67.66")) {  // HTTP
    int httpCode = http.GET();
    Serial.printf("[HTTP] GET... code: %d\n", httpCode);
    http.end();
  }
}

// just from the example BasicHttpClient
void checkExampleSite() {
  WiFiClient client;
  HTTPClient http;

  Serial.print("[HTTP] begin...\n");
  if (http.begin(client, "http://jigsaw.w3.org/HTTP/connection.html")) {  // HTTP
    Serial.print("[HTTP] GET...\n");
    // start connection and send HTTP header
    int httpCode = http.GET();
    // httpCode will be negative on error
    if (httpCode > 0) {
      // HTTP header has been send and Server response header has been handled
      Serial.printf("[HTTP] GET... code: %d\n", httpCode);
      // file found at server
      if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
        String payload = http.getString();
        Serial.println(payload);
      }
    } else {
      Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
    }
    http.end();
  } else {
    Serial.printf("[HTTP} Unable to connect\n");
  }
}

// this is just a quick debug print of the (previous) state of each site
void debugPrint() {
  static uint32_t previousMillis = 0; // time management
  if (millis() - previousMillis > 1000) {
    previousMillis = millis();
    for (auto &i : site) {
      Serial.print(i.resource); Serial.print("\t");
      Serial.print(i.result); Serial.print("\t");
      Serial.print(i.previousMillis / 1000); Serial.print("\n");
    }
  }
}

void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.println(F("check webpages"));
  wifiConnect();
  checkMySites(); // optional - just to get a first result
}

void loop() {
  checkOneOfManySites();
  debugPrint();
  // don't block code with dirty delays
}

This will print out something like:

09:28:12.305 -> [HTTP] check http://172.18.67.68
09:28:12.359 -> [HTTP] GET... code: 200
09:28:12.359 -> http://172.18.67.66	200	10
09:28:12.359 -> http://172.18.67.68	200	15
09:28:12.359 -> http://172.18.67.100	-1	10
09:28:13.361 -> http://172.18.67.66	200	10
09:28:13.361 -> http://172.18.67.68	200	15
09:28:13.361 -> http://172.18.67.100	-1	10
09:28:14.363 -> http://172.18.67.66	200	10
09:28:14.363 -> http://172.18.67.68	200	15
09:28:14.363 -> http://172.18.67.100	-1	10
09:28:15.352 -> http://172.18.67.66	200	10
09:28:15.352 -> http://172.18.67.68	200	15
09:28:15.352 -> http://172.18.67.100	-1	10
09:28:16.373 -> http://172.18.67.66	200	10
09:28:16.373 -> http://172.18.67.68	200	15
09:28:16.373 -> http://172.18.67.100	-1	10
09:28:17.328 -> [HTTP] check http://172.18.67.100
09:28:22.650 -> [HTTP] GET... code: -1
09:28:22.650 -> http://172.18.67.66	200	10
09:28:22.650 -> http://172.18.67.68	200	15
09:28:22.650 -> http://172.18.67.100	-1	25

you see either a 200 for a good resource or a negative value if the connection fails.

PS.: the default timeout of a page is 5000ms, so I reduced it to 1000ms. It could be even less for a local resource.

How I came to that code
this will describe how I came up with that code:

I started with the Example "BasicHTTPClient".
Cleaning it up a little bit and use functions for things belonging together. For example the wifiConnect() or the check function checkExampleSite()

Then play around with a single hardcoded site --> checkOneSite()
Put several sites in an array of structure and loop through that array --> checkMySites()
Get rid of the delays(). Make a function based on "Blink Without Delay" to check one site after the other. --> checkOneOfManySites()
and finally, add a simple print output. debugPrint()

all in all it took me around 90 minutes including to google how to reduce that weired http timeout and writing all that text. TL;DR :wink:

1 Like

Nice solution,

I am convinced that I can continue from here. Thank you very much with the extensive elaboration of Example "BasicHTTPClient"

I did a quick test and added 12 more local ips for fun. The script took a total of 15ms!!!

Many thanxs.

just to be clear on that:
between each check in checkOneOfManySites is a 5 seconds interval.
if you have 15 sites each site will be checked in n*5 seconds --> every 45 seconds.
one check might be very fast (probably these 15ms you measured) - but if it fails - it could be one second also.
That's the reason why I jump through the sites in a slower pace.
For fast access should used the data in the structure (like shown in the debugPrint) - worst case the information is 45 seconds old. Imho that should be acceptable.

I chose to just go for your checkOneSite feature. Changed it a little bit, after everything works I don't need serial.print anymore. I don't think too much about the checking time anymore, by using http.GET() I can get away with everything with ease. My application is not exact for real-time information, but the ping method was just not convenient enough. In my loop function I don't use delays anymore, I saw your comment about that and see what you mean. I just let my loop go on constantly. On the client side, I put a regular javascript refresh every 20 seconds. It all works perfectly now.

Here is a small piece of the most essential code that I had to change:

int checkThisSite(String ipAdress) {
  WiFiClient client;
  HTTPClient http;
  if (http.begin(client, ipAdress)) {  // HTTP
    int httpCode = http.GET(); // send Get request and store result in variable
    http.end(); // close HTTP 
    return httpCode; // like expected return the Get result
  } 
  else { 
    return 0; // no empty return inside a int function.
  }
}

void handleRoot() {
  String s = FPSTR(extHTMLstart); //Read HTML contents
  
  for (int i = 0; i < 5; i++){
    if(checkThisSite(ipsToHost[i])  > 0 ) { // check if device is active (ip should be ping-able)
      s += "\t<iframe src=\""; // embed device page to iframe (first part of string)
      s += ipsToHost[i]; // add ip to string
      s += "\" scrolling=\"no\"></iframe><br><br>\n"; // close the embeding thml code
    } else { // if device not active
      s += "<br>Device is not active, or some other error ocurred.<br>\n"; // show some information
    }
  }

  s += "</body>\n</html>"; // wrap the webpage up
  server.send(200, "text/html", s); //Send web page
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.