0

I am just wondering, how can we display page from SD card and still show live data from sensors?

I have web server running on Arduino mega using ethernet shield, in my sketch I am doing client.print to build content of a page being displayed, and refreshing it display live data.

But I can't think of a way how can I load page from SD card and display data from sensors.

8
  • Can you show your code please? It very much depends on how you're doing it.
    – Mark Smith
    Commented Dec 16, 2016 at 16:28
  • @MarkSmith codereview.stackexchange.com/q/149563/22943 Commented Dec 16, 2016 at 16:32
  • 1
    Is your problem really that you want your device to serve two different web pages? Or are you trying to embed your live data into the middle of a pre-formed page? Or something else?
    – Mark Smith
    Commented Dec 16, 2016 at 16:33
  • 1
    I want to embed live data onto a pre formed page. Commented Dec 16, 2016 at 17:04
  • 1
    Use a character that you are not using (e.g. @) and place that where you want to put the live data. Then, when reading the file forward all characters other than '@' to client.print, but when you find a '@' print the live data. Very rudimentary templating.
    – Gerben
    Commented Dec 16, 2016 at 19:58

2 Answers 2

1

Based upon this W3C example you can have w3-include-HTML.js file with the following contents:

function w3IncludeHTML() {
  var z, i, a, file, xhttp;
  z = document.getElementsByTagName("*");
  for (i = 0; i < z.length; i++) {
    if (z[i].getAttribute("w3-include-html")) {
      a = z[i].cloneNode(false);
      file = z[i].getAttribute("w3-include-html");
      xhttp = new XMLHttpRequest();
      xhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
          a.removeAttribute("w3-include-html");
          a.innerHTML = this.responseText;
          z[i].parentNode.replaceChild(a, z[i]);
          w3IncludeHTML();
        }
      }      
      xhttp.open("GET", file, true);
      xhttp.send();
      return;
    }
  }

Now you can put this in your .html file:

<div w3-include-html="generated_data.html"></div>
<script src="w3-include-HTML.js"></script>

and then you can use something as:

server.on("/generated_data.html", live_data);

If you want to be able to use server.on() you must include <ESP8266WebServer.h> but you can serve the content the way you like. You can examine the ESP8266WebServer::on() method source code and adapt it according to your needs or you can just use that method as is.

Everything until now will result with calling the live_data() function and inserting it's response into the div which has the attribute w3-include-html="generated_data.html".

Now you only need to generate the live data inside the live_data() and to send the response:

void live_data() {
  String data_from_sensor;

  // read here the data from some sensor and store it in data_from_sensor
  String response = "<h1>Live data</h1><p>The data is: " + data_from_sensor + "some unit</p>";
  server.send(200, "text/html", response);
}

And that's it. The live data generated inside live_data() will show where was the empty div with w3-include-html attribute.

Of course, you can have more divs with different w3-include-html attributes and belonging server.on() statements.

There are many ways, this is just an example so you can get an idea from where to start. This code will not update the live data on and on but now that you know the mechanism, you can modify the code to do whatever you like.

0

This is a variation on Chupo_cro’s answer... His suggestion to use XMLHttpRequest() is definitely the way to go. But unlike his answer, I suggest here to not send HTML-formatted data. Just send the raw data as JSON, which is the simplest data format suitable for this job. Since your client computer is likely to be orders of magnitude more powerful than your server, you can offload all the job of presenting the data to the client JavaScript code. Keep your Arduino server minimalist and have it only serve:

  • static content (HTML, JavaScript and CSS) from the SD card
  • raw live data as JSON.

And then let the client handle the presentation of the data in whatever form you like.

Your static Web page would then contain no data and have instead placeholders of the form <span id="XXX">---</span>. For example:

<p>Rain sensor reading: <span id="rainValue">---</span>,
<span id="rainQuality">---</span> rain<br>
Temperature: <span id="tempCelsius">---</span> °C
/ <span id="tempFahrenheit">---</span> °F<br>
Humidity: <span id="humidity">---</span>%<br>
Dew point: <span id="dewPoint">---</span> °C</p>

Your client JavaScript code starts by collecting references to those placeholders:

// HTML elements with replaceable content.
var elements = null;

window.onload = function() {
    var getElt = document.getElementById.bind(document);
    elements = {
        rainValue: getElt("rainValue"),
        rainQuality: getElt("rainQuality"),
        tempCelsius: getElt("tempCelsius"),
        tempFahrenheit: getElt("tempFahrenheit"),
        humidity: getElt("humidity"),
        dewPoint: getElt("dewPoint")
    };
    setInterval(requestLiveData, 5000);
};

Then the page can be updated from live data simply by filling the innerHTML attributes of the elements:

function dewPoint(temperature, humidity) {
    return ...;
}

// Update page from live data.
function updatePage(data) {
    elements.rainValue.innerHTML = data.rain;
    elements.rainQuality.innerHTML =
        data.rain < 300 ? "heavy" :
        data.rain < 500 ? "moderate" : "no";
    elements.tempCelsius.innerHTML = data.temperature;
    elements.tempFahrenheit.innerHTML = data.temperature * 9 / 5 + 32;
    elements.humidity.innerHTML = data.humidity;
    elements.dewPoint.innerHTML = dewPoint(data.temperature, data.humidity);
}

Notice that the data processing (Celsius → Fahrenheit conversion, dew point calculation, converting the rain reading to words) is now done by the client. The live data is retrieved from the server using Ajax, aka XMLHttpRequest():

function requestLiveData() {
    var request = new XMLHttpRequest();
    request.onreadystatechange = function() {
        if (request.readyState != 4) return;
        if (request.status != 200) {
            alert("Ajax request failed: "
                + "status = " + request.status);
            return;
        }
        var data = JSON.parse(request.responseText);
        updatePage(data);
    };
    request.open("GET", "/live_data");
    request.send(null);
}

You may have noticed the last line of the window.onload function is

setInterval(requestLiveData, 5000);

which means the requestLiveData function above will be automatically called every 5000 ms.

Now, the only thing your server has to do is answer a request for GET /live_data by sending the raw data as "application/json". It should look like this:

{"temperature":15,"humidity":48.3,"rain":500,"soilMoisture":22}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.