ESP8266 and the Arduino IDE Part 8: Auto Updating Webpage

So far I have gone through controlling LEDs from a simple web app where all control is done via buttons in the app. This works well but you need to click a button in the app to make things happen, some kind of user action is required to update the webpage. Here we start to look at getting a webpage to update itself.

Traditionally, websites were static, once the page was displayed it didn’t change and the user had to click a link or button to move forward. For example, if you had a simple temperature display you would need to click an update button to get the latest value. To (kind of) address this the HTML refresh meta statement was added. This allowed the page to refresh itself at regular intervals.

The below causes the webpage to reload every 30 seconds.

 <head>
  <meta http-equiv="refresh" content="30">
</head>

The HTML refresh meta statement works but is very clunky and the smallest interval is 1 second which for many applications may not be quick enough. We also have the issue that the whole page reloads so, depending on the page content, the refresh may actually take longer than 1 second.

Then Javascript came along and added its own version of the page refresh/reload

document.location.reload(true/false)

this reloads the page but only when called. To get the page to reload every 5 seconds you need to add a timer that calls the reload function.

<head>
 <title>Example</title>
 <script>
  function refresh(refreshPeriod) 
  {
      setTimeout('location.reload(true)', refreshPeriod);
  } 
  window.onload = refresh(5000);
 </script>
</head>

When the page loads window.onload triggers which in turn calls the function refresh(5000). Function refresh(5000) then calls setTimeout(“location.reload(true);”, refreshPeriod); The value set in refresh(5000) IE 5000 is the timer used for the timer.

setTimeout sets a count down timer and when finished counting down calls location.reload(true). In the above example the countdown or delay period is 5000 ms or 5 seconds.

You may notice that this does exactly the same as the HTML but takes far more code so why use this method? You normally wouldn’t but it serves as a good getting started with Javascript timers example. However, when using Javascript we do not need to load the whole page, we can load specific parts of the page only. If you have followed the previous guides you will have already seen seen how Javascript can be used behind the scenes to make changes.

 

Webpage auto update example using HTML

Here we have an example of a basic webpage that auto updates every 5 seconds. The webpage has a count showing how many times it has updated.
ESP8266AutoUpdateEample_01_HTML

The important bit is the meta statement

  <meta http-equiv="refresh" content="30">
 
/*
 * Sketch: ESP8266_Part8_01_AutoUpdate_HTML
 * Intended to be run on an ESP8266
 */
 
String header = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";
 
String html_1 = R"=====(
<!DOCTYPE html>
<html>
 <head>
 <meta name='viewport' content='width=device-width, initial-scale=1.0'/>
 <meta charset='utf-8'>
 <meta http-equiv='refresh' content='5'>
 <style>
   body {font-size:100%;} 
   #main {display: table; margin: auto;  padding: 0 10px 0 10px; } 
   h2 {text-align:center; } 
   p { text-align:center; }
 </style>
   <title>Auto Update Example Using HTML</title>
 </head>
 
 <body>
   <div id='main'>
     <h2>Auto Update Example Using HTML</h2>
     <div id='count'> 
       <p>Count = %count%</p>
     </div>
   </div> 
 </body>
</html>
)====="; 
 
#include <ESP8266WiFi.h>
 
// change these values to match your network
char ssid[] = "NetworkName";       //  your network SSID (name)
char pass[] = "password";          //  your network password
 
WiFiServer server(80);
 
String tmpString = "";
unsigned int count = 0; 
 
void setup() 
{
    Serial.begin(115200);
    Serial.println();
    Serial.println("Serial started at 115200");
    Serial.println();
 
    // Connect to a WiFi network
    Serial.print(F("Connecting to "));  Serial.println(ssid);
    WiFi.begin(ssid, pass);
 
    while (WiFi.status() != WL_CONNECTED) 
    {
        Serial.print(".");
        delay(500);
    }
 
    Serial.println("");
    Serial.println(F("[CONNECTED]"));
    Serial.print("[IP ");              
    Serial.print(WiFi.localIP()); 
    Serial.println("]");
 
    // start a server
    server.begin();
    Serial.println("Server started");
 
} // void setup()
 
void loop() 
{
    // Check if a client has connected
    WiFiClient client = server.available();
    if (!client)  {  return;  }
 
    count ++;
 
    tmpString = html_1;
    tmpString.replace("%count%", String(count) );
 
    client.flush();
    client.print( header );
    client.print( tmpString );  
 
    Serial.print("count = "); Serial.println(count); 
 
 
    delay(5);
 
 
  // The client will actually be disconnected when the function returns and 'client' object is destroyed
} // void loop()

The sketch is a little different to the previous ones and all non essential stuff has been removed. To update the count I am using a place holder “%count%” and overwriting it with the actual value. To allow me to do this I am first copying the original HTML to a temporary variable. This keeps the code simple but is very wasteful on memory and for a final project I would use a different method.

 

Webpage Auto Update example using Javascript

And here is the same effect using Javascript.
ESP8266AutoUpdateEample_02_JS
The bulk of the sketch is the same. The only difference is the Javascript script. The important bit here is the Javascript.

<head>
 <title>Example</title>
 <script>
  function refresh(refreshPeriod) 
  {
      setTimeout("location.reload(true);", refreshPeriod);
  } 
  window.onload = refresh(5000);
 </script>
</head>

When the page has loaded the window.onload = refresh(5000); statement calls refresh(refreshPeriod). This then starts a countdown timer that refreshes the page after the refreshPeriod which in this case is set to 5000ms.

/*
 * Sketch: ESP8266_Part8_02_AutoUpdate_Javascript         
 * Intended to be run on an ESP8266
 */
 
String header = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";
 
String html_1 = R"=====(
<!DOCTYPE html>
<html>
 <head>
  <meta name='viewport' content='width=device-width, initial-scale=1.0'/>
  <meta charset='utf-8'>
  <style>
    body {font-size:100%;} 
    #main {display: table; margin: auto;  padding: 0 10px 0 10px; } 
    h2 {text-align:center; } 
    p { text-align:center; }
  </style>
  <script>
   function refresh(refreshPeriod) 
   {
      setTimeout("location.reload(true);", refreshPeriod);
   } 
   window.onload = refresh(5000);
  </script>
  <title>Auto Update Example Using Javascript</title>
 </head>
 
 <body>
   <div id='main'>
     <h2>Auto Update Example Using Javascript</h2>
     <div id='count'> 
       <p>Count = %count%</p>
     </div>
   </div> 
 </body>
</html>
)====="; 
 
#include <ESP8266WiFi.h>
 
// change these values to match your network
char ssid[] = "NetworkName";       //  your network SSID (name)
char pass[] = "password";          //  your network password
 
WiFiServer server(80);
 
String tmpString = "";
unsigned int count = 0; 
 
void setup() 
{
    Serial.begin(115200);
    Serial.println();
    Serial.println("Serial started at 115200");
    Serial.println();
 
    // Connect to a WiFi network
    Serial.print(F("Connecting to "));  Serial.println(ssid);
    WiFi.begin(ssid, pass);
 
    while (WiFi.status() != WL_CONNECTED) 
    {
        Serial.print(".");
        delay(500);
    }
 
    Serial.println("");
    Serial.println(F("[CONNECTED]"));
    Serial.print("[IP ");              
    Serial.print(WiFi.localIP()); 
    Serial.println("]");
 
    // start a server
    server.begin();
    Serial.println("Server started");
 
} // void setup()
 
 
void loop() 
{
    // Check if a client has connected
    WiFiClient client = server.available();
    if (!client)  {  return;  }
 
    count ++;
 
    tmpString = html_1;
    tmpString.replace("%count%", String(count) );
 
    client.flush();
    client.print( header );
    client.print( tmpString );   
 
    Serial.print("count = "); Serial.println(count);
 
    delay(5);
 
  // The client will actually be disconnected when the function returns and 'client' object is destroyed
} // void loop()

In both examples above we are reloading the whole page. If you have followed along with the earlier guides you will know we do not need to do this. Using javascript we can update just specific parts such as just the count value.

 

Webpage Auto Update example using Javascript: Using AJAX

In the example above we used setTimeout(function, milliseconds);. This is basically a one time use count down timer. After it fires it stops. Javascript also offers the window.setInterval(function, milliseconds); command. setInterval is a regular timer that fires at a regular frequency as set by the milliseconds value.

window.setInterval(updateCount, 5000);

This sets a timer to fire every 5000 ms (or every 5 seconds). The timer will continue indefinitely or until it is stopped. When the timer fires the function updateCount() is called. updateCount() then calls ajaxLoad(‘getCount’) which sends the request for the new count value.

ESP8266AutoUpdateEample_04_JS

Here the sketch. After the sketch I try to explain what it does.

/*
 * Sketch: ESP8266_Part8_03_AutoUpdate_Javascript_AJAX
 * Intended to be run on an ESP8266
 */
 
String header = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";
 
String html_1 = R"=====(
<!DOCTYPE html>
<html>
 <head>
  <meta name='viewport' content='width=device-width, initial-scale=1.0'/>
  <meta charset='utf-8'>
  <style>
    body {font-size:100%;} 
    #main {display: table; margin: auto;  padding: 0 10px 0 10px; } 
    h2 {text-align:center; } 
    p { text-align:center; }
  </style>
 
  <script> 
    function updateCount() 
    {  
       ajaxLoad('getCount'); 
    }
 
 
    var ajaxRequest = null;
    if (window.XMLHttpRequest)  { ajaxRequest =new XMLHttpRequest(); }
    else                        { ajaxRequest =new ActiveXObject("Microsoft.XMLHTTP"); }
 
    function ajaxLoad(ajaxURL)
    {
      if(!ajaxRequest){ alert('AJAX is not supported.'); return; }
 
      ajaxRequest.open('GET',ajaxURL,true);
      ajaxRequest.onreadystatechange = function()
      {
        if(ajaxRequest.readyState == 4 && ajaxRequest.status==200)
        {
          var ajaxResult = ajaxRequest.responseText;
          document.getElementById('count_P').innerHTML = ajaxResult;
        }
      }
      ajaxRequest.send();
    }
 
    setInterval(updateCount, 5000);
 
  </script>
  <title>Auto Update Example Using Javascript 2</title>
 </head>
 
 <body>
   <div id='main'>
     <h2>Auto Update Example Using Javascript 2</h2>
     <div id='count_DIV'> 
       <p id='count_P'>Count = 0</p>
     </div>
   </div> 
 </body>
</html>
)====="; 
 
#include <ESP8266WiFi.h>
 
// change these values to match your network
char ssid[] = "NetworkName";       //  your network SSID (name)
char pass[] = "password";          //  your network password
 
WiFiServer server(80);
 
String request = "";
unsigned int count = 0; 
 
void setup() 
{
    Serial.begin(115200);
    Serial.println();
    Serial.println("Serial started at 115200");
    Serial.println();
 
    // Connect to a WiFi network
    Serial.print(F("Connecting to "));  Serial.println(ssid);
    WiFi.begin(ssid, pass);
 
    while (WiFi.status() != WL_CONNECTED) 
    {
        Serial.print(".");
        delay(500);
    }
 
    Serial.println("");
    Serial.println(F("[CONNECTED]"));
    Serial.print("[IP ");              
    Serial.print(WiFi.localIP()); 
    Serial.println("]");
 
    // start a server
    server.begin();
    Serial.println("Server started");
 
} // void setup()
 
 
void loop() 
{
    // Check if a client has connected
    WiFiClient client = server.available();
    if (!client)  {  return;  }
 
    // Read the first line of the request
    request = client.readStringUntil('\r');
 
    if ( request.indexOf("getCount") > 0 )
     { 
        count ++;
        client.print( header );
        client.print( "Count = " ); 
        client.print( count ); 
 
        Serial.print("Count=");
        Serial.println(count);
     }
 
     else
     {
        client.flush();
        client.print( header );
        client.print( html_1 ); 
        count = 0;
     }
 
    delay(5);
 
 
  // The client will actually be disconnected when the function returns and 'client' object is destroyed
} // void loop()

You may notice that I have added the id “count_P” to the html paragraph element.

<p id='count_P'>Count = 0</p>

This makes updating it a little easier and getElementById(‘count_P’) can be used when updating the count value.

When the page loads the timer is started with

The server is constantly checking for a request and when it gets one it chacks to see if the request include the string “getCount”

// Read the first line of the request
request = client.readStringUntil('\r');
 
if ( request.indexOf("getCount") > 0 )

If the request contains the string “getCount”

if ( request.indexOf("getCount") > 0 )
{ 
   count ++;
   client.print( header );
   client.print( "Count = " ); 
   client.print( count ); 
 
   Serial.print("Count=");
   Serial.println(count);
}

The reply header and the count data is sent.

If the request does not contain “getCount” we send the main webpage and reset count to zero.

else
{
   client.flush();
   client.print( header );
   client.print( html_1 ); 
   count = 0;
}

When you make the first request, there is no getCount so the whole webpage is returned. Once the webpage is loaded the javascript timer and AJAX take over to update the count value.

Ther timer is started with

setInterval(updateCount, 5000);

When the timer fires updateCount() is called. UpdateCount then calls ajaxLoad(). ajaxLoad is where the real work takes place.

    function updateCount() 
    {  
       ajaxLoad('getCount'); 
 
 
    var ajaxRequest = null;
    if (window.XMLHttpRequest)  { ajaxRequest =new XMLHttpRequest(); }
    else                        { ajaxRequest =new ActiveXObject("Microsoft.XMLHTTP"); }
 
    function ajaxLoad(ajaxURL)
    {
      if(!ajaxRequest){ alert('AJAX is not supported.'); return; }
 
      ajaxRequest.open('GET',ajaxURL,true);
      ajaxRequest.onreadystatechange = function()
      {
        if(ajaxRequest.readyState == 4 && ajaxRequest.status==200)
        {
          var ajaxResult = ajaxRequest.responseText;
          document.getElementById('count_P').innerHTML = ajaxResult;
        }
      }
      ajaxRequest.send();
    }

Give them a go, you should see all three examples do the same thing. Since the last example is using AJAX and only updating part of the page it should appear a lot smoother.

 

Temperature & Humidly Monitor

Updating a count value is all well and good for showing the update method but it doesn’t have any real world use. So, as a final example I am using a DHT11 to get the temperature and humidity and I show these on a self updating webpage. I don’t go in to a lot of detail but the circuit is fairly simple and if you have followed along with the other guides you should be able to see how the sketch and the webpage work.

Circuit

ESP8266_Part8_04_AutoUpdate_DHT11_Circuit
ESP8266_Part8_04_AutoUpdate_DHT11_BreadBoard01
ESP8266_Part8_04_AutoUpdate_DHT11_BreadBoard02

The webpage
ESP8266_Part8_04_AutoUpdate_DHT11
As an extra bonus I added the current time

The Sketch

 
/*
 * Sketch: ESP8266_Part8_04_AutoUpdate_DHT11
 * Intended to be run on an ESP8266
 */
 
String header = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";
 
String html_1 = R"=====(
<!DOCTYPE html>
<html>
 <head>
  <meta name='viewport' content='width=device-width, initial-scale=1.0'/>
  <meta charset='utf-8'>
  <style>
    body {font-size:100%;} 
    #main {display: table; margin: auto;  padding: 10px 10px 10px 10px; } 
    #content { border: 5px solid blue; border-radius: 15px; padding: 10px 0px 10px 0px;}
    h2 {text-align:center; margin: 10px 0px 10px 0px;} 
    p { text-align:center; margin: 5px 0px 10px 0px; font-size: 120%;}
    #time_P { margin: 10px 0px 15px 0px;}
  </style>
 
  <script> 
    function updateTime() 
    {  
       var d = new Date();
       var t = "";
       t = d.toLocaleTimeString();
       document.getElementById('P_time').innerHTML = t;
    }
 
    function updateTemp() 
    {  
       ajaxLoad('getTemp'); 
    }
 
    var ajaxRequest = null;
    if (window.XMLHttpRequest)  { ajaxRequest =new XMLHttpRequest(); }
    else                        { ajaxRequest =new ActiveXObject("Microsoft.XMLHTTP"); }
 
    function ajaxLoad(ajaxURL)
    {
      if(!ajaxRequest){ alert('AJAX is not supported.'); return; }
 
      ajaxRequest.open('GET',ajaxURL,true);
      ajaxRequest.onreadystatechange = function()
      {
        if(ajaxRequest.readyState == 4 && ajaxRequest.status==200)
        {
          var ajaxResult = ajaxRequest.responseText;
          var tmpArray = ajaxResult.split("|");
          document.getElementById('temp_C').innerHTML = tmpArray[0];
          document.getElementById('temp_F').innerHTML = tmpArray[1];
          document.getElementById('hmd').innerHTML = tmpArray[2];
        }
      }
      ajaxRequest.send();
    }
 
    var myVar1 = setInterval(updateTemp, 5000);  
    var myVar2 = setInterval(updateTime, 1000);  
 
  </script>
 
 
  <title>Temperature & Humidy Monitor</title>
 </head>
 
 <body>
   <div id='main'>
     <h2>Temperature & Humidity Monitor</h2>
     <div id='content'> 
       <p id='P_time'>Time</p>
       <h2>Temperature</h2>
       <p> <span id='temp_C'>--.-</span> &deg;C &nbsp;-&nbsp; <span id='temp_F'>--.-</span> &deg;F </p>
       <h2>Humidity</h2>
       <p> <span id='hmd'>--</span> % </p>
     </div>
   </div> 
 </body>
</html>
)====="; 
 
#include <ESP8266WiFi.h>
 
// change these values to match your network
char ssid[] = "myNetworkName";       //  your network SSID (name)
char pass[] = "password";          //  your network password
 
#include "DHT.h"
#define DHTPIN 2     // what digital pin we're connected to
#define DHTTYPE DHT11   // DHT 11
DHT dht(DHTPIN, DHTTYPE);
 
float tempF = 0;
float tempC = 0;
float humid = 0;
 
WiFiServer server(80);
String request = "";
 
 
void setup() 
{
    Serial.begin(115200);
    Serial.println();
    Serial.println("Serial started at 115200");
    Serial.println();
 
    dht.begin();
 
    // Connect to a WiFi network
    Serial.print(F("Connecting to "));  Serial.println(ssid);
    WiFi.begin(ssid, pass);
 
    while (WiFi.status() != WL_CONNECTED) 
    {
        Serial.print(".");
        delay(500);
    }
 
    Serial.println("");
    Serial.println(F("[CONNECTED]"));
    Serial.print("[IP ");              
    Serial.print(WiFi.localIP()); 
    Serial.println("]");
 
    // start a server
    server.begin();
    Serial.println("Server started");
 
} // void setup()
 
 
void loop() 
{
    WiFiClient client = server.available();     // Check if a client has connected
    if (!client)  {  return;  }
 
    request = client.readStringUntil('\r');     // Read the first line of the request
 
    Serial.println(request);
    Serial.println("");
 
    if ( request.indexOf("getTemp") > 0 )
     { 
          Serial.println("getTemp received");
 
          // Reading temperature or humidity takes about 250 milliseconds!
          // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
          humid = dht.readHumidity();
          tempC = dht.readTemperature();        // Read temperature as Celsius (the default)
          tempF = dht.readTemperature(true);    // Read temperature as Fahrenheit (isFahrenheit = true)
 
          if ( !isnan(humid) && !isnan(tempC) && !isnan(tempC) )
          {
              client.print( header );
              client.print( tempC );   client.print( "|" );  client.print( tempF );   client.print( "|" );  client.print( humid ); 
              Serial.println("data sent");
          }
          else
          {
              Serial.println("Error reading the sensor");
          }
     }
 
     else
     {
        client.flush();
        client.print( header );
        client.print( html_1 ); 
        Serial.println("New page served");
     }
 
    delay(5);
 
 
  // The client will actually be disconnected when the function returns and the 'client' object is destroyed
 
} // void loop()

The sketch uses the Adafruit DHT library and you will need to copy the library to your libraries folder or install the library using the Library Manager.

The webpage uses 2 timers, one to update the time and the other to update the data.The time is updated once every 1000ms (1 second) and the temperature and humidity is updated every 5000ms (5 seconds).

var myVar1 = setInterval(updateTemp, 5000);  
var myVar2 = setInterval(updateTime, 1000);

The time is updated inside the browser and an AJAX call is not required.

function updateTime() 
{  
   var d = new Date();
   var t = "";
   t = d.toLocaleTimeString();
   document.getElementById('P_time').innerHTML = t;
}

The temperature and humidity are updated using AJAX exactly the same way the count is updated in the previous example. The difference this time is that there are 3 values; temperature in Centigrade, temperature in Fahrenheit, and the humidity.

When the getTemp command is received, the server sends the 3 values separated by the bar character in the form “25.00|77.00|76.00″.

client.print( tempC );   client.print( "|" );  client.print( tempF );   client.print( "|" );  client.print( humid );

The javascript splits the data using the bar character and the 3 values are copied to the webpage.

var ajaxResult = ajaxRequest.responseText;
var tmpArray = ajaxResult.split("|");
document.getElementById('temp_C').innerHTML = tmpArray[0];
document.getElementById('temp_F').innerHTML = tmpArray[1];
document.getElementById('hmd').innerHTML = tmpArray[2];

That’s it. A simple but reliable temperature and humidity webpage.

   

There we have it, auto updating webpages. There are some short comings though. The frequency of the update cannot be too quick and if you look close enough you will realize the control is still coming from the webpage but now it is behind the scenes. What if you need to update the data much quicker? Then you need to start looking at other methods such as web sockets which will be the topic of the next guide.

    

2 thoughts on “ESP8266 and the Arduino IDE Part 8: Auto Updating Webpage

  1. I want to thank you for posting these guides. Your posts are very clear and very well explained. I am a beginner and understood all your examples. There are so many ESP8266 guides that do not explain things and are very hard to follow and I was finding is difficult to progress. Your posts have helped me a lot.

Leave a Reply to Peter Jacks Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>