ESP8266 Server-Sent Event: Update Web page using SSE

ESP8266 Server-Sent Event is a mechanism to push updates to the client asynchronously. This tutorial covers how to implement Server-Sent Event (SSE) using ESP8266 and how to develop a Web page that receives events. Using SSE the Web page can receive updates from the ESP8266 through the HTTP protocol asynchronously. To explain how it works, we will suppose we want to send to a Web page the temperature and the humidity values acquired by the DHT22 connected to the ESP8266.

In the previous post, we have covered how to use WebSocket protocol using ESP8266, now we focus our attention on a different way to notify events to a Web client.

The main difference between the SSE and the Websocket is that the WebSocket is bi-directional, or in other words, the Web page and the ESP8266 can send events. In the Server-Sent Event, the Web client can receive updates from the ESP8266 but it can’t send updates to the ESP8266. The final result is shown below:

How the Server-Sent Event works

Before diving into the details of SSE, it is useful to understand how it works. The image below describes how the Web page client and the ESP8266 exchange data using SSE:

Server-Sent Event (SSE) with ESP8266

The process starts with an HTTP request coming from the Web page to the ESP8266. From now on, the ESP8266 can send events to the Web page as they happen. In this tutorial, we have supposed to send events that holds sensor readings but it is possible to send different kind of messages. As stated before, only the ESP8266 can send events toward the Web page.

ESP8266 SSE code

Now we know how SSE works we can focus our attention on the code to implement the Web server on the ESP8266 that supports Server-Sent Event:

#include <Arduino.h> #include <ESP8266WiFi.h> #include <ESPAsyncTCP.h> #include <ESPAsyncWebServer.h> #include "DHT.h" // WiFi config const char *SSID = "your_wifi"; const char *PWD = "your_pwd"; #define DHTPIN 4 #define DHTTYPE DHT22 DHT dht(DHTPIN, DHTTYPE); const char index_html[] PROGMEM = R"rawliteral( <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>ESP8266 SSE</title> <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script> <script language="javascript"> google.charts.load('current', {'packages':['gauge']}); google.charts.setOnLoadCallback(drawChart); var chartHum; var chartTemp; var optionsTemp; var optionsHum; var dataTemp; var dataHum; function drawChart() { dataTemp = google.visualization.arrayToDataTable([ ['Label', 'Value'], ['Temp', 20] ]); optionsTemp = { min:-10, max:50, width: 400, height: 120, greenColor: '#68A2DE', greenFrom: -10, greenTo: 5, yellowFrom:20, yellowTo: 27, redFrom:27, redTo:50, minorTicks: 5 }; chartTemp = new google.visualization.Gauge(document.getElementById('chart_div_temp')); dataHum = google.visualization.arrayToDataTable([ ['Label', 'Value'], ['Humidity', 20] ]); optionsHum = { min:0, max:100, width: 400, height: 120, greenColor: '#68A2DE', greenFrom: 0, greenTo: 100, minorTicks: 5 }; chartHum = new google.visualization.Gauge(document.getElementById('chart_div_hum')); chartTemp.draw(dataTemp, optionsTemp); chartHum.draw(dataHum, optionsHum); } if (!!window.EventSource) { var source = new EventSource('/events'); source.addEventListener('open', function(e) { console.log("Events Connected"); }, false); source.addEventListener('error', function(e) { if (e.target.readyState != EventSource.OPEN) { console.log("Events Disconnected"); } }, false); source.addEventListener('message', function(e) { console.log("message", e.data); }, false); source.addEventListener('temp', function(e) { dataTemp.setValue(0,1, e.data); chartTemp.draw(dataTemp, optionsTemp); }, false); source.addEventListener('hum', function(e) { dataHum.setValue(0,1, e.data); chartHum.draw(dataHum, optionsHum); }, false); } </script> <style> .card { box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); transition: 0.3s; border-radius: 5px; /* 5px rounded corners */ } /* On mouse-over, add a deeper shadow */ .card:hover { box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2); } /* Add some padding inside the card container */ .container { padding: 2px 16px; } </style> </head> <body> <h2>ESP8266 SSE</h2> <div class="content"> <div class="card"> <div class="container"> <h4><b>Environment</b></h4> <div id="chart_div_temp" style="width: 400px; height: 120px;"></div> <div id="chart_div_hum" style="width: 400px; height: 120px;"></div> </div> </div> </div> </body> </html> )rawliteral"; // Web server running on port 80 AsyncWebServer server(80); // Async Events AsyncEventSource events("/events"); void connectToWiFi() { Serial.print("Connecting to "); Serial.println(SSID); WiFi.begin(SSID, PWD); while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(500); // we can even make the ESP32 to sleep } Serial.print("Connected. IP: "); Serial.println(WiFi.localIP()); } void configureEvents() { events.onConnect([](AsyncEventSourceClient *client){ if(client->lastId()){ Serial.printf("Client connections. Id: %u\n", client->lastId()); } // and set reconnect delay to 1 second client->send("hello from ESP8266",NULL,millis(),1000); }); server.addHandler(&events); } void setup() { Serial.begin(9600); connectToWiFi(); server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/html", index_html, NULL); }); configureEvents(); server.begin(); dht.begin(); } void loop() { float temp = dht.readTemperature(); float hum = dht.readHumidity(); // Send event to the client events.send(String(temp).c_str(), "temp", millis()); events.send(String(hum).c_str(), "hum", millis()); //Serial.print("Temp:"); //Serial.println(temp); delay(1000); }
Code language: C++ (cpp)

As you can notice in the code there is the Web page that receives events. We will cover it later. Let us focus our attention on the ESP8266 source code.

How to send events from ESP8266

The first step is implementing the Web server that receives the HTTP connection as shown in the schema:

// Web server running on port 80 AsyncWebServer server(80); // Async Events AsyncEventSource events("/events");
Code language: C++ (cpp)

Moreover, we define a new event source named /events. You can change this name if you want. Next, we have to handle the incoming HTTP client request:

void configureEvents() { events.onConnect([](AsyncEventSourceClient *client){ if(client->lastId()){ Serial.printf("Client connections. Id: %u\n", client->lastId()); } // and set reconnect delay to 1 second client->send("hello from ESP8266",NULL,millis(),1000); }); server.addHandler(&events); }
Code language: C# (cs)

As you can notice, as soon as the Web page connects to the Web server running on the ESP8266, it responds with a simple hello message. Finally, the code attaches the event source to the Web server so that it can handle it.

Other interesting articles:

Sending the Web page to the browser

Once we have configured everything, we can handle the HTTP request made by the browser when it connects for the first time providing the HTML page:

void setup() { Serial.begin(9600); connectToWiFi(); server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/html", index_html, NULL); }); configureEvents(); server.begin(); ... }
Code language: PHP (php)

How to develop a Web page to receive events from ESP8266

Now we can focus our attention on the HTML Web page that receives Server Sent Events from the ESP8266. This page is provided by the ESP8266 as described before. This the Web page:

<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>ESP8266 SSE</title> <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script> <script language="javascript"> google.charts.load('current', {'packages':['gauge']}); google.charts.setOnLoadCallback(drawChart); var chartHum; var chartTemp; var optionsTemp; var optionsHum; var dataTemp; var dataHum; function drawChart() { dataTemp = google.visualization.arrayToDataTable([ ['Label', 'Value'], ['Temp', 20] ]); optionsTemp = { min:-10, max:50, width: 400, height: 120, greenColor: '#68A2DE', greenFrom: -10, greenTo: 5, yellowFrom:20, yellowTo: 27, redFrom:27, redTo:50, minorTicks: 5 }; chartTemp = new google.visualization.Gauge(document.getElementById('chart_div_temp')); dataHum = google.visualization.arrayToDataTable([ ['Label', 'Value'], ['Humidity', 20] ]); optionsHum = { min:0, max:100, width: 400, height: 120, greenColor: '#68A2DE', greenFrom: 0, greenTo: 100, minorTicks: 5 }; chartHum = new google.visualization.Gauge(document.getElementById('chart_div_hum')); chartTemp.draw(dataTemp, optionsTemp); chartHum.draw(dataHum, optionsHum); } if (!!window.EventSource) { var source = new EventSource('/events'); source.addEventListener('open', function(e) { console.log("Events Connected"); }, false); source.addEventListener('error', function(e) { if (e.target.readyState != EventSource.OPEN) { console.log("Events Disconnected"); } }, false); source.addEventListener('message', function(e) { console.log("message", e.data); }, false); source.addEventListener('temp', function(e) { dataTemp.setValue(0,1, e.data); chartTemp.draw(dataTemp, optionsTemp); }, false); source.addEventListener('hum', function(e) { dataHum.setValue(0,1, e.data); chartHum.draw(dataHum, optionsHum); }, false); } </script> <style> .card { box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); transition: 0.3s; border-radius: 5px; /* 5px rounded corners */ } /* On mouse-over, add a deeper shadow */ .card:hover { box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2); } /* Add some padding inside the card container */ .container { padding: 2px 16px; } </style> </head> <body> <h2>ESP8266 SSE</h2> <div class="content"> <div class="card"> <div class="container"> <h4><b>Environment</b></h4> <div id="chart_div_temp" style="width: 400px; height: 120px;"></div> <div id="chart_div_hum" style="width: 400px; height: 120px;"></div> </div> </div> </div> </body> </html> )
Code language: HTML, XML (xml)

How to receive SSE Events

The Web page handles the SSE using Javascript. It handles different kinds of events:

  • connection
  • disconnection
  • messages sent by ESP8266
  • events

The first step is defining an event source (or path):

var source = new EventSource('/events');
Code language: JavaScript (javascript)

Next, it adds some listener to listen to incoming events. This is the listener for incoming messages:

source.addEventListener('message', function(e) { console.log("message", e.data); }, false);
Code language: JavaScript (javascript)

Finally, the two most important listeners: one for the temperature update and the other one for humidity update:

source.addEventListener('temp', function(e) { dataTemp.setValue(0,1, e.data); chartTemp.draw(dataTemp, optionsTemp); }, false); source.addEventListener('hum', function(e) { dataHum.setValue(0,1, e.data); chartHum.draw(dataHum, optionsHum); }, false);
Code language: JavaScript (javascript)

When the page receives these two kinds of events, it updates the values.

Visualizing Server sent events

The last step is visualizing the events received. To this goal, the page uses Google Charts, a very simple and ligth-weight javascript library:

<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script> google.charts.load('current', {'packages':['gauge']}); google.charts.setOnLoadCallback(drawChart); var chartHum; var chartTemp; var optionsTemp; var optionsHum; var dataTemp; var dataHum; function drawChart() { dataTemp = google.visualization.arrayToDataTable([ ['Label', 'Value'], ['Temp', 20] ]); optionsTemp = { min:-10, max:50, width: 400, height: 120, greenColor: '#68A2DE', greenFrom: -10, greenTo: 5, yellowFrom:20, yellowTo: 27, redFrom:27, redTo:50, minorTicks: 5 }; chartTemp = new google.visualization.Gauge(document.getElementById('chart_div_temp')); dataHum = google.visualization.arrayToDataTable([ ['Label', 'Value'], ['Humidity', 20] ]); optionsHum = { min:0, max:100, width: 400, height: 120, greenColor: '#68A2DE', greenFrom: 0, greenTo: 100, minorTicks: 5 }; chartHum = new google.visualization.Gauge(document.getElementById('chart_div_hum')); chartTemp.draw(dataTemp, optionsTemp); chartHum.draw(dataHum, optionsHum); }
Code language: JavaScript (javascript)

The fnal result is shown below:

ESP8266 Server Sent Event Charts with Temperature and Humidity

Wrapping up

In this tutorial, we have covered how to use Server Sent Event to update a Web page with sensor readings with ESP8266. We have discovered that using ESP8266 and SSE it is possible to update asynchronously a Web page with the data coming from sensors. You can further extend this example by implementing a complete Dashboard using HTML and javascript that visualizes the data read by sensors connected to the ESP8266.

    1. Francesco December 12, 2020
    2. Stefan Björklund January 19, 2021
      • Francesco Azzola January 20, 2021
    3. Stefan Björklund January 21, 2021
    4. Stefan Björklund January 23, 2021
      • Francesco Azzola January 23, 2021
    5. Stefan Björklund January 23, 2021
    6. Stefan Björklund January 23, 2021

    Add Your Comment