2

I am carrying out this project in which the Arduino acts as a web server and hosts a website in which I show the constantly updated temperature of a laboratory.

The problem is running all this code in the Arduino: memory runs out easily.

void loop() {
  int value = analogRead(PIN_LM35);
  float temperature = value / 2.046;
  myFile = SD.open("temperature.txt");
  if (myFile) {
    Serial.println(temperature);
  }
  myFile.close();

  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();

        //read char by char HTTP request
        if (readString.length() < 100) {
          //store characters to string
          readString += c;
          //Serial.print(c);
        }

        //if HTTP request has ended
        if (c == '\n') {
          Serial.println(readString); //print to serial monitor for debuging

          client.println(F("HTTP/1.1 200 OK")); //send new page
          client.println(F("Content-Type: text/html"));
          client.println();
          refreshcounter = refreshcounter + 1;
          client.println(F("<HTML>"));
          client.println(F("<HEAD>"));
          client.print(F("<meta http-equiv=\"refresh\" content=\"2\">"));
          client.println(F("<meta name='apple-mobile-web-app-capable' content='yes' />"));
          client.println(F("<meta name='apple-mobile-web-app-status-bar-style' content='black-translucent' />"));
          client.println(F("<TITLE>TEMPERATURE SENSOR LAB01</TITLE>"));
          client.println(F("</HEAD>"));
          client.println(F("<BODY>"));
          client.println(F("<H1>TEMPERATURE SENSOR LAB01</H1>"));
          client.println(F("<hr />"));
          client.println(F("<br />"));
          client.println(F("<H2>Arduino with Ethernet Shield</H2>"));
          client.println(F("<br />"));
          if (temperature < 24) {
            client.println("<p style=\"font-size:50px; color:#8eff59; font-weight:bold; font-style:italic;\">");
            client.println(temperature);
            client.println("</p>");
          }
          else if (temperature >= 24 && temperature <= 26) {
            client.println("<p style=\"font-size:50px; color:#ffbc03; font-weight:bold; font-style:italic;\">");
            client.println(temperature);
            client.println("</p>");
          }
          else {
            client.println("<p style=\"font-size:50px; color:#ff0303; font-weight:bold; font-style:italic;\">");
            client.println(temperature);
            client.println(F("<H2>It is recommended to activate the air conditioner.</H2>"));
            client.println("</p>");
          }
          client.println(F("<p style=\"font-size:30px; color:#000000; font-weight:bold; ;\">Date/Time: <span id=\"datetime\"></span></p>"));
          client.println(F("<script>"));
          client.println(F("var dt = new Date();"));
          client.println(F("document.getElementById(\"datetime\").innerHTML = "
                "((\"0\"+dt.getDate()).slice(-2)) +\".\"+ ((\"0\"+(dt.getMonth()+1)).slice(-2)) "
                "+\".\"+ (dt.getFullYear()) +\" \"+ ((\"0\"+dt.getHours()).slice(-2)) +\":\"+ ((\"0\"+dt.getMinutes()).slice(-2));"));
          client.println(F("</script>"));

          client.println("<svg width=\"1000\" height=\"250\">");
          client.println("<rect width=\"150\" height=\"5\" fill=\"gray\">");
          client.println("<animate attributeName=\"x\" from = \"0\" to =\"10000\" dur=\"10s\" fill=\"freeze\" />");
          client.println("</rect");
          client.println("</svg>");

          client.println("<br />");
          client.println("</BODY>");
          client.println("</HTML>");

          delay(1);
          //stopping client
          client.stop();
          //clearing string for next read
          readString = "";
        }
      }
    }
  }
}

I put all the code of the web page on an external SD card to save memory. However, I don’t really know how to use it from the SD card.

The problem that stops me is the fact that the site is not static, but is constantly updated with new temperature data.

How can I do that?

4
  • why you don't use F() for every string? With Arduino Uno if you use the Ethernet library and the SD library you run out of flash and SRAM very fast.
    – Juraj
    Apr 27, 2022 at 13:00
  • I tried, but I was running out of flash memory, too
    – Leo
    Apr 27, 2022 at 13:27
  • You may want to detail what library, shield, and board you are using. Is this one of the standard AVR Arduinos? What hardware and library does it have that lets you run it as a server in the first place?
    – user47164
    Apr 27, 2022 at 18:47
  • I'd suggest looking into the wifi/ethernet shield library examples or the SD card library examples. You might find one that already does what you need. At very worst you can write a loop that reads characters or lines from the card and server.prints them. Make two files on the card that abut the print line to insert the temperature value, then print File 1, Temperature, File 2.
    – user47164
    Apr 27, 2022 at 18:50

1 Answer 1

6

You wrote:

the site does not have to be static but constantly updated with new temperature data.

A good option is to use Ajax. The basic idea is to split the site in two parts:

  • a static part, potentially large, which contains all the user interface, the styling, the bells and whistles...
  • a tiny, dynamic part that contains the data that is constantly updated and nothing more.

The static part would be served from the SD card when the client sends a GET / request. The dynamic part would be served from another endpoint, for example as a reply to GET /temperature. Serving the dynamic part should be very simple, something like:

client.println(temperature);

Yes, just send the number as ASCII, no HTML formatting. If you ever need to send more than one number (e.g. multiple temperatures, or temperature and humidity), format them in JSON. No need to use a library for that, plain print() should be cheaper and good enough:

client.print(F("{\"temperature\":"));
client.print(temperature);
client.print(F(",\"humidity\":"));
client.print(humidity);
client.print('}');

On the client side, JSON.parse() gets the data back as an easy-to-use data structure.

Here is a minimal example of what the static part could look like. Notice the JavaScript code sent to the client. This code is responsible for querying the temperature every 1000 milliseconds, and updating the Web page with the new data;

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Ajax test</title>
</head>
<body>

<h1>Ajax test</h1>

<p>Temperature: <span id="temperature">---</span> °C</p>

<script>
var data_field = document.getElementById("temperature");
setInterval(function() {
    var request = new XMLHttpRequest();
    request.onreadystatechange = function() {
        if (request.readyState != 4) return;
        data_field.innerText = JSON.parse(request.responseText);
    };
    request.open("GET", "/temperature");
    request.send();
}, 1000);
</script>
</body>
</html>

A few notes:

  • This example is purposefully minimal. You may want to add some error checking, some decorations, some CSS and the such. Handling a temperature-dependent color also belongs to the client code.

  • The example uses good old XMLHttpRequest. You may want to try the more modern fetch API instead:

    setInterval(function() {
        fetch("/temperature")
            .then(response => response.json())
            .then(data => { data_field.innerText = data; });
    }, 1000);
    
  • If you are sending updates frequently enough to significantly load your Arduino, you may want to look at Server-sent events. This is a technique that allows you to significantly reduce the overhead of sending data updates. It is not as popular as Web-sockets, but has the advantage of being simpler to implement server-side.

  • If you can put a reverse-proxy between your Arduino and the Internet, the proxy can handle the static content. Your Arduino then only handles the dynamic data and wouldn't need an SD card. If you reverse-proxy Server-sent events, make sure the proxy doesn't buffer the server response. See for example these tips on configuring Nginx for this purpose.

  • The static data could also be served from a completely different Web site. For this to work, you have to fulfill two conditions:

    1. The Arduino server has to add the header Access-Control-Allow-Origin: * to its responses.

    2. On the client code, you have to provide the full URL of the Arduino as an argument of request.open(), fetch() or new EventSource().

  • The previous trick also allows you to open the Web page directly from your local file system, which can be very handy for developing.

  • Once you get the basic scheme working, Web technologies allow you to get as fancy as you want. You could create an analogue gauge (with SVG transform rotate). You could even add a graph, updated in real time, that shows the time evolution of the temperature, and is entirely handled by the client.

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.