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:

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:

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.
Very interesting. Thank you
Hi
Unfort when I tested this project it was unsuccesful.
I couldn’t get the gauges to be displayed, just a copy paste of the code. I could see the readings och temp and hum when I created a tab like events.
I will test further .
Best regards
Stefan
Let me know if you found the problem and if it depends on the code. I tried it using Chrome and it worked
I tried with amcharts and it succeded but I will have Google gauges work to. I will try further in the weekend.
Thanks for replaying, your site and examples looks very good. 🙂
It’s something with compiling and gstatic loader . js I think, tested some builds with Google gauges and it’s the same problem.
New install Arduino 1.8.14 and the latest versions of ESPA….
Tested in both M$ and Ubuntu environment
Viewed source:
google.charts.load(‘current’, {‘packages’:[‘gauge’]});
#line 34 “/home/steffe/snap/arduino/current/Arduino/sketch_jan22a/sketch_jan22a.ino”
function drawChart();
#line 25 “/home/steffe/snap/arduino/current/Arduino/sketch_jan22a/sketch_jan22a.ino”
google.charts.setOnLoadCallback(drawChart);
Can you try with a different browser? It could be a browser problem
The same with Chrome, Firefox and Edge.
I think the extra lines that are added to the code makes the fault.
#line 34 “/home/steffe/snap/arduino/current/Arduino/sketch_jan22a/sketch_jan22a.ino”
Why is the filename of the code written to html-page?
I think it’s something in loader.js or the compiler that makes the lines #34 and #25, I will investigate more.
When I try with ESP32 it’s the same problem.
Solved 🙂 (or a workaround as always).
Visual Studio and PlatformIO worked better. Seems to be a problem for Arduino to solve, I will notify this to them.
Best regards
Stefan