In this tutorial you’ll learn how to build a web server with the ESP32 using WebSocket communication protocol. As an example, we’ll show you how to build a web page to control the ESP32 outputs remotely. The output state is displayed on the web page and it updates automatically in all clients.
The ESP32 will be programmed using Arduino IDE and the ESPAsyncWebServer. We also have a similar WebSocket guide for the ESP8266.
If you’ve been following some of our previous web server projects like this one, you may have noticed that if you have several tabs (in the same or on different devices) opened at the same time, the state doesn’t update in all tabs automatically unless you refresh the web page. To solve this issue, we can use WebSocket protocol – all clients can be notified when a change occurs and update the web page accordingly.
This tutorial was based on a project created and documented by one of our readers (Stéphane Calderoni). You can read his excellent tutorial here.
Introducing WebSocket
A WebSocket is a persistent connection between a client and a server that allows bidirectional communication between both parties using a TCP connection. This means you can send data from the client to the server and from the server to the client at any given time.
The client establishes a WebSocket connection with the server through a process known as WebSocket handshake. The handshake starts with an HTTP request/response, allowing servers to handle HTTP connections as well as WebSocket connections on the same port. Once the connection is established, the client and the server can send WebSocket data in full duplex mode.
Using the WebSockets protocol, the server (ESP32 board) can send information to the client or to all clients without being requested. This also allows us to send information to the web browser when a change occurs.
This change can be something that happened on the web page (you clicked a button) or something that happened on the ESP32 side like pressing a physical button on a circuit.
Project Overview
Here’s the web page we’ll build for this project.
- The ESP32 web server displays a web page with a button to toggle the state of GPIO 2;
- For simplicity, we’re controlling GPIO 2 – the on-board LED. You can use this example to control any other GPIO;
- The interface shows the current GPIO state. Whenever a change occurs on the GPIO state, the interface is updated instantaneously;
- The GPIO state is updated automatically in all clients. This means that if you have several web browser tabs opened on the same device or on different devices, they are all updated at the same time.
How it Works?
The following image describes what happens when click on the “Toggle” button.
Here’s what happens when you click on the “Toggle” button:
- Click on the “Toggle” button;
- The client (your browser) sends data via WebSocket protocol with the “toggle” message;
- The ESP32 (server) receives this message, so it knows it should toggle the LED state. If the LED was previously off, turn it on;
- Then, it sends data with the new LED state to all clients also through WebSocket protocol;
- The clients receive the message and update the led state on the web page accordingly. This allows us to update all clients almost instantaneously when a change happens.
Preparing Arduino IDE
We’ll program the ESP32 board using Arduino IDE, so make sure you have it installed in your Arduino IDE.
Installing Libraries – Async Web Server
To build the web server we’ll use the ESPAsyncWebServer library. This library needs the AsyncTCP library to work properly. Click the links below to download the libraries.
These libraries aren’t available to install through the Arduino Library Manager, so you need to copy the library files to the Arduino Installation Libraries folder. Alternatively, in your Arduino IDE, you can go to Sketch > Include Library > Add .zip Library and select the libraries you’ve just downloaded.
Code for ESP32 WebSocket Server
Copy the following code to your Arduino IDE.
/*********
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp32-websocket-server-arduino/
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*********/
// Import required libraries
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
bool ledState = 0;
const int ledPin = 2;
// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
<title>ESP Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<style>
html {
font-family: Arial, Helvetica, sans-serif;
text-align: center;
}
h1 {
font-size: 1.8rem;
color: white;
}
h2{
font-size: 1.5rem;
font-weight: bold;
color: #143642;
}
.topnav {
overflow: hidden;
background-color: #143642;
}
body {
margin: 0;
}
.content {
padding: 30px;
max-width: 600px;
margin: 0 auto;
}
.card {
background-color: #F8F7F9;;
box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
padding-top:10px;
padding-bottom:20px;
}
.button {
padding: 15px 50px;
font-size: 24px;
text-align: center;
outline: none;
color: #fff;
background-color: #0f8b8d;
border: none;
border-radius: 5px;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
/*.button:hover {background-color: #0f8b8d}*/
.button:active {
background-color: #0f8b8d;
box-shadow: 2 2px #CDCDCD;
transform: translateY(2px);
}
.state {
font-size: 1.5rem;
color:#8c8c8c;
font-weight: bold;
}
</style>
<title>ESP Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
</head>
<body>
<div class="topnav">
<h1>ESP WebSocket Server</h1>
</div>
<div class="content">
<div class="card">
<h2>Output - GPIO 2</h2>
<p class="state">state: <span id="state">%STATE%</span></p>
<p><button id="button" class="button">Toggle</button></p>
</div>
</div>
<script>
var gateway = `ws://${window.location.hostname}/ws`;
var websocket;
window.addEventListener('load', onLoad);
function initWebSocket() {
console.log('Trying to open a WebSocket connection...');
websocket = new WebSocket(gateway);
websocket.onopen = onOpen;
websocket.onclose = onClose;
websocket.onmessage = onMessage; // <-- add this line
}
function onOpen(event) {
console.log('Connection opened');
}
function onClose(event) {
console.log('Connection closed');
setTimeout(initWebSocket, 2000);
}
function onMessage(event) {
var state;
if (event.data == "1"){
state = "ON";
}
else{
state = "OFF";
}
document.getElementById('state').innerHTML = state;
}
function onLoad(event) {
initWebSocket();
initButton();
}
function initButton() {
document.getElementById('button').addEventListener('click', toggle);
}
function toggle(){
websocket.send('toggle');
}
</script>
</body>
</html>
)rawliteral";
void notifyClients() {
ws.textAll(String(ledState));
}
void handleWebSocketMessage(void *arg, uint8_t *data, size_t len) {
AwsFrameInfo *info = (AwsFrameInfo*)arg;
if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
data[len] = 0;
if (strcmp((char*)data, "toggle") == 0) {
ledState = !ledState;
notifyClients();
}
}
}
void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type,
void *arg, uint8_t *data, size_t len) {
switch (type) {
case WS_EVT_CONNECT:
Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
break;
case WS_EVT_DISCONNECT:
Serial.printf("WebSocket client #%u disconnected\n", client->id());
break;
case WS_EVT_DATA:
handleWebSocketMessage(arg, data, len);
break;
case WS_EVT_PONG:
case WS_EVT_ERROR:
break;
}
}
void initWebSocket() {
ws.onEvent(onEvent);
server.addHandler(&ws);
}
String processor(const String& var){
Serial.println(var);
if(var == "STATE"){
if (ledState){
return "ON";
}
else{
return "OFF";
}
}
return String();
}
void setup(){
// Serial port for debugging purposes
Serial.begin(115200);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
// Connect to Wi-Fi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}
// Print ESP Local IP Address
Serial.println(WiFi.localIP());
initWebSocket();
// Route for root / web page
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/html", index_html, processor);
});
// Start server
server.begin();
}
void loop() {
ws.cleanupClients();
digitalWrite(ledPin, ledState);
}
Insert your network credentials in the following variables and the code will work straight away.
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
How the Code Works
Continue reading to learn how the code works or skip to the Demonstration section.
Importing Libraries
Import the necessary libraries to build the web server.
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
Network Credentials
Insert your network credentials in the following variables:
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
GPIO Output
Create a variable called ledState to hold the GPIO state and a variable called ledPin that refers to the GPIO you want to control. In this case, we’ll control the on-board LED (that is connected to GPIO 2).
bool ledState = 0;
const int ledPin = 2;
AsyncWebServer and AsyncWebSocket
Create an AsyncWebServer object on port 80.
AsyncWebServer server(80);
The ESPAsyncWebServer library includes a WebSocket plugin that makes it easy to handle WebSocket connections. Create an AsyncWebSocket object called ws to handle the connections on the /ws path.
AsyncWebSocket ws("/ws");
Building the Web Page
The index_html variable contains the HTML, CSS and JavaScript needed to build and style the web page and handle client-server interactions using WebSocket protocol.
Note: we’re placing everything needed to build the web page on the index_html variable that we use on the Arduino sketch. Note that it may be more practical to have separated HTML, CSS and JavaScript files that then you upload to the ESP32 filesystem and reference them on the code.
Recommended reading: ESP32 Web Server using SPIFFS (SPI Flash File System)
Here’s the content of the index_html variable:
<!DOCTYPE HTML>
<html>
<head>
<title>ESP Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<style>
html {
font-family: Arial, Helvetica, sans-serif;
text-align: center;
}
h1 {
font-size: 1.8rem;
color: white;
}
h2{
font-size: 1.5rem;
font-weight: bold;
color: #143642;
}
.topnav {
overflow: hidden;
background-color: #143642;
}
body {
margin: 0;
}
.content {
padding: 30px;
max-width: 600px;
margin: 0 auto;
}
.card {
background-color: #F8F7F9;;
box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
padding-top:10px;
padding-bottom:20px;
}
.button {
padding: 15px 50px;
font-size: 24px;
text-align: center;
outline: none;
color: #fff;
background-color: #0f8b8d;
border: none;
border-radius: 5px;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
.button:active {
background-color: #0f8b8d;
box-shadow: 2 2px #CDCDCD;
transform: translateY(2px);
}
.state {
font-size: 1.5rem;
color:#8c8c8c;
font-weight: bold;
}
</style>
<title>ESP Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
</head>
<body>
<div class="topnav">
<h1>ESP WebSocket Server</h1>
</div>
<div class="content">
<div class="card">
<h2>Output - GPIO 2</h2>
<p class="state">state: <span id="state">%STATE%</span></p>
<p><button id="button" class="button">Toggle</button></p>
</div>
</div>
<script>
var gateway = `ws://${window.location.hostname}/ws`;
var websocket;
function initWebSocket() {
console.log('Trying to open a WebSocket connection...');
websocket = new WebSocket(gateway);
websocket.onopen = onOpen;
websocket.onclose = onClose;
websocket.onmessage = onMessage; // <-- add this line
}
function onOpen(event) {
console.log('Connection opened');
}
function onClose(event) {
console.log('Connection closed');
setTimeout(initWebSocket, 2000);
}
function onMessage(event) {
var state;
if (event.data == "1"){
state = "ON";
}
else{
state = "OFF";
}
document.getElementById('state').innerHTML = state;
}
window.addEventListener('load', onLoad);
function onLoad(event) {
initWebSocket();
initButton();
}
function initButton() {
document.getElementById('button').addEventListener('click', toggle);
}
function toggle(){
websocket.send('toggle');
}
</script>
</body>
</html>
CSS
Between the <style> </style> tags we include the styles to style the web page using CSS. Feel free to change it to make the web page look as you wish. We won’t explain how the CSS for this web page works because it is not relevant for this WebSocket tutorial.
<style>
html {
font-family: Arial, Helvetica, sans-serif;
text-align: center;
}
h1 {
font-size: 1.8rem;
color: white;
}
h2 {
font-size: 1.5rem;
font-weight: bold;
color: #143642;
}
.topnav {
overflow: hidden;
background-color: #143642;
}
body {
margin: 0;
}
.content {
padding: 30px;
max-width: 600px;
margin: 0 auto;
}
.card {
background-color: #F8F7F9;;
box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
padding-top:10px;
padding-bottom:20px;
}
.button {
padding: 15px 50px;
font-size: 24px;
text-align: center;
outline: none;
color: #fff;
background-color: #0f8b8d;
border: none;
border-radius: 5px;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
.button:active {
background-color: #0f8b8d;
box-shadow: 2 2px #CDCDCD;
transform: translateY(2px);
}
.state {
font-size: 1.5rem;
color:#8c8c8c;
font-weight: bold;
}
</style>
HTML
Between the <body> </body> tags we add the web page content that is visible to the user.
<div class="topnav">
<h1>ESP WebSocket Server</h1>
</div>
<div class="content">
<div class="card">
<h2>Output - GPIO 2</h2>
<p class="state">state: <span id="state">%STATE%</span></p>
<p><button id="button" class="button">Toggle</button></p>
</div>
</div>
There’s a heading 1 with the text “ESP WebSocket Server”. Feel free to modify that text.
<h1>ESP WebSocket Server</h1>
Then, there’s a heading 2 with the “Output – GPIO 2” text.
<h2>Output - GPIO 2</h2>
After that, we have a paragraph that displays the current GPIO state.
<p class="state">state: <span id="state">%STATE%</span></p>
The %STATE% is a placeholder for the GPIO state. It will be replaced with the current value by the ESP32 at the time of sending the web page. The placeholders on the HTML text should go between % signs. This means that this %STATE% text is like a variable that will then be replaced with the actual value.
After sending the web page to the client, the state needs to change dynamically whenever there’s a change in the GPIO state. We’ll receive that information via WebSocket protocol. Then, JavaScript handles what to do with the information received to update the state accordingly. To be able to handle that text using JavaScript, the text must have an id that we can reference. In this case the id is state ( <span id=”state”>).
Finally, there’s a paragraph with the button to toggle the GPIO state.
<p><button id="button" class="button">Toggle</button></p>
Note that we’ve given an id to the button ( id=”button”).
JavaScript – Handling WebSockets
The JavaScript goes between the <script> </script> tags. It is responsible for initializing a WebSocket connection with the server as soon the web interface is fully loaded in the browser and handling data exchange through WebSockets.
<script>
var gateway = `ws://${window.location.hostname}/ws`;
var websocket;
function initWebSocket() {
console.log('Trying to open a WebSocket connection...');
websocket = new WebSocket(gateway);
websocket.onopen = onOpen;
websocket.onclose = onClose;
websocket.onmessage = onMessage; // <-- add this line
}
function onOpen(event) {
console.log('Connection opened');
}
function onClose(event) {
console.log('Connection closed');
setTimeout(initWebSocket, 2000);
}
function onMessage(event) {
var state;
if (event.data == "1"){
state = "ON";
}
else{
state = "OFF";
}
document.getElementById('state').innerHTML = state;
}
window.addEventListener('load', onLoad);
function onLoad(event) {
initWebSocket();
initButton();
}
function initButton() {
document.getElementById('button').addEventListener('click', toggle);
}
function toggle(){
websocket.send('toggle');
}
</script>
Let’s take a look at how this works.
The gateway is the entry point to the WebSocket interface.
var gateway = `ws://${window.location.hostname}/ws`;
window.location.hostname gets the current page address (the web server IP address).
Create a new global variable called websocket.
var websocket;
Add an event listener that will call the onload function when the web page loads.
window.addEventListener('load', onload);
The onload() function calls the initWebSocket() function to initialize a WebSocket connection with the server and the initButton() function to add event listeners to the buttons.
function onload(event) {
initWebSocket();
initButton();
}
The initWebSocket() function initializes a WebSocket connection on the gateway defined earlier. We also assign several callback functions for when the WebSocket connection is opened, closed or when a message is received.
function initWebSocket() {
console.log('Trying to open a WebSocket connection…');
websocket = new WebSocket(gateway);
websocket.onopen = onOpen;
websocket.onclose = onClose;
websocket.onmessage = onMessage;
}
When the connection is opened, we simply print a message in the console and send a message saying “hi”. The ESP32 receives that message, so we know that the connection was initialized.
function onOpen(event) {
console.log('Connection opened');
websocket.send('hi');
}
If for some reason the web socket connection is closed, we call the initWebSocket() function again after 2000 milliseconds (2 seconds).
function onClose(event) {
console.log('Connection closed');
setTimeout(initWebSocket, 2000);
}
The setTimeout() method calls a function or evaluates an expression after a specified number of milliseconds.
Finally, we need to handle what happens when we receive a new message. The server (your ESP board) will either send a “1” or a “0” message. Accordingly to the received message, we want to display an “ON” or a “OFF” message on the paragraph that displays the state. Remember that <span> tag with id=”state”? We’ll get that element and set its value to ON or OFF.
function onMessage(event) {
var state;
if (event.data == "1"){
state = "ON";
}
else{
state = "OFF";
}
document.getElementById('state').innerHTML = state;
}
The initButton() function gets the button by its id (button) and adds an event listener of type ‘click’.
function initButton() {
document.getElementById('button').addEventListener('click', toggle);
}
This means that when you click the button, the toggle function is called.
The toggle function sends a message using the WebSocket connection with the ‘toggle’ text.
function toggle(){
websocket.send('toggle');
}
Then, the ESP32 should handle what happens when it receives this message – toggle the current GPIO state.
Handling WebSockets – Server
Previously, you’ve seen how to handle the WebSocket connection on the client side (browser). Now, let’s take a look on how to handle it on the server side.
Notify All Clients
The notifyClients() function notifies all clients with a message containing whatever you pass as a argument. In this case, we’ll want to notify all clients of the current LED state whenever there’s a change.
void notifyClients() {
ws.textAll(String(ledState));
}
The AsyncWebSocket class provides a textAll() method for sending the same message to all clients that are connected to the server at the same time.
Handle WebSocket Messages
The handleWebSocketMessage() function is a callback function that will run whenever we receive new data from the clients via WebSocket protocol.
void handleWebSocketMessage(void *arg, uint8_t *data, size_t len) {
AwsFrameInfo *info = (AwsFrameInfo*)arg;
if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
data[len] = 0;
if (strcmp((char*)data, "toggle") == 0) {
ledState = !ledState;
notifyClients();
}
}
}
If we receive the “toggle” message, we toggle the value of the ledState variable. Additionally, we notify all clients by calling the notifyClients() function. This way, all clients are notified of the change and update the interface accordingly.
if (strcmp((char*)data, "toggle") == 0) {
ledState = !ledState;
notifyClients();
}
Configure the WebSocket server
Now we need to configure an event listener to handle the different asynchronous steps of the WebSocket protocol. This event handler can be implemented by defining the onEvent() as follows:
void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type,
void *arg, uint8_t *data, size_t len) {
switch (type) {
case WS_EVT_CONNECT:
Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
break;
case WS_EVT_DISCONNECT:
Serial.printf("WebSocket client #%u disconnected\n", client->id());
break;
case WS_EVT_DATA:
handleWebSocketMessage(arg, data, len);
break;
case WS_EVT_PONG:
case WS_EVT_ERROR:
break;
}
}
The type argument represents the event that occurs. It can take the following values:
- WS_EVT_CONNECT when a client has logged in;
- WS_EVT_DISCONNECT when a client has logged out;
- WS_EVT_DATA when a data packet is received from the client;
- WS_EVT_PONG in response to a ping request;
- WS_EVT_ERROR when an error is received from the client.
Initialize WebSocket
Finally, the initWebSocket() function initializes the WebSocket protocol.
void initWebSocket() {
ws.onEvent(onEvent);
server.addHandler(&ws);
}
processor()
The processor() function is responsible for searching for placeholders on the HTML text and replace them with whatever we want before sending the web page to the browser. In our case, we’ll replace the %STATE% placeholder with ON if the ledState is 1. Otherwise, replace it with OFF.
String processor(const String& var){
Serial.println(var);
if(var == "STATE"){
if (ledState){
return "ON";
}
else{
return "OFF";
}
}
}
setup()
In the setup(), initialize the Serial Monitor for debugging purposes.
Serial.begin(115200);
Set up the ledPin as an OUTPUT and set it to LOW when the program first starts.
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
Initialize Wi-Fi and print the ESP32 IP address on the Serial Monitor.
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}
// Print ESP Local IP Address
Serial.println(WiFi.localIP());
Initialize WebSocket protocol by calling the initWebSocket() function created previously.
initWebSocket();
Handle Requests
Serve the text saved on the index_html variable when you receive a request on the root / URL – you need to pass the processor function as an argument to replace the placeholders with the current GPIO state.
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/html", index_html, processor);
});
Finally, start the server.
server.begin();
loop()
The LED will be physically controlled on the loop().
void loop() {
ws.cleanupClients();
digitalWrite(ledPin, ledState);
}
Note that we all call the cleanupClients() method. Here’s why (explanation from the ESPAsyncWebServer library GitHub page):
Browsers sometimes do not correctly close the WebSocket connection, even when the close() function is called in JavaScript. This will eventually exhaust the web server’s resources and will cause the server to crash. Periodically calling the cleanupClients() function from the main loop()limits the number of clients by closing the oldest client when the maximum number of clients has been exceeded. This can be called every cycle, however, if you wish to use less power, then calling as infrequently as once per second is sufficient.
Demonstration
After inserting your network credentials on the ssid and password variables, you can upload the code to your board. Don’t forget to check if you have the right board and COM port selected.
After uploading the code, open the Serial Monitor at a baud rate of 115200 and press the on-board EN/RST button. The ESP IP address should be printed.
Open a browser on your local network and insert the ESP32 IP address. You should get access to the web page to control the output.
Click on the button to toggle the LED. You can open several web browser tabs at the same time or access the web server from different devices and the LED state will be update automatically in all clients whenever there’s a change.
Wrapping Up
In this tutorial you’ve learned how to set up a WebSocket server with the ESP32. The WebSocket protocol allows a full duplex communication between the client and the server. After initializing, the server and the client can exchange data at any given time.
This is very useful because the server can send data to the client whenever something happens. For example, you can add a physical button to this setup that when pressed notifies all clients to update the web interface.
In this example, we’ve shown you how to control one GPIO of the ESP32. You can use this method to control more GPIOs. You can also use the WebSocket protocol to send sensor readings or notifications at any given time.
We hope you’ve found this tutorial useful. We intend to create more tutorials and examples using the WebSocket protocol. So, stay tuned.
Learn more about the ESP32 with our resources:
Hi RNT team!
Another very interesting project…Thanks for keeping publishing the results of all your continuous electronics developing efforts!
As a follow-up, how about a complementary project, this time one client connecting to and controlling the outputs pins of several (ESP32s) servers, all connected servers updating their similarly assigned pin status simultaneously, using the HTTP + the WebSockets protocols?
Looking forward to hear from you, guys, soon!
Hi
Interesting Article. I am attempting to run this on nodemcu (esp12-e) but get errors when trying to compile
E:\Documents\Arduino\libraries\AsyncTCP-master\src/AsyncTCP.h:26:23: fatal error: sdkconfig.h: No such file or directory
#include “sdkconfig.h”
^
compilation terminated.
Using library WiFi at version 1.2.7 in folder: C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.42.0_x86__mdqgnx93n4wtt\libraries\WiFi
Using library AsyncTCP-master at version 1.1.1 in folder: E:\Documents\Arduino\libraries\AsyncTCP-master
exit status 1
Error compiling for board NodeMCU 1.0 (ESP-12E Module).
Are there compatible libraries for the esp-12e???
regards
Meir
Hello Meir, I recommend following this guide for the ESP8266 NodeMCU board: https://randomnerdtutorials.com/esp8266-nodemcu-websocket-server-arduino/
Make sure you have all the libraries updated: ESPAsyncTCP, AsyncTCP, and ESPAsyncWebServer.
You should also have the latest ESP board add-on installed in your Arduino IDE.
Regards,
Rui
@rui – thanks the esp8266 explanation works perfectly.. again thanks for your great work here.
Regards
Meir
Hi, Could you be more specific about the versions please?
I think I have the latest versions of everything downloaded from links on your pages yet have the same error about sdkconfig.h
I am running Arduino 1.8.13 and esp8266 V 2.7.4
ResolveLibrary(sdkconfig.h)In file included from C:\Users\lou\Documents\Arduino\async_web\async_web.ino:10:0:
-> candidates: []
C:\Users\lou\Documents\Arduino\libraries\AsyncTCP-master\src/AsyncTCP.h:26:23: fatal error: sdkconfig.h: No such file or directory
#include “sdkconfig.h”
^
compilation terminated.
Using library WiFi at version 1.2.7 in folder: C:\Program Files (x86)\Arduino\libraries\WiFi
Using library AsyncTCP-master at version 1.1.1 in folder: C:\Users\lou\Documents\Arduino\libraries\AsyncTCP-master
exit status 1
Error compiling for board NodeMCU 1.0 (ESP-12E Module).
Hi.
This code is for the ESP32.
You are trying to compile it for the ESP8266.
For the ESP8266 board, follow this tutorial instead: https://randomnerdtutorials.com/esp8266-nodemcu-websocket-server-arduino/
Regards,
Sara
Thanks Sara,
Yes I see that now, that works for me.
Hi Rui – thanks for this wonderful article.
Where does console.log(event.data) go? Will it output the data to the browser screen? If so, it does not appear on my browser and I need it for debugging.
Also, how can I make event.data appear on the browser? I will be sending some other data from the websocket server and need to see it on the browser screen.
Thanks a lot,
Bernie
Just what I needed!
Amazing! I have made a lights automation in my house, so I need to know the state of each light via html page.
How could I just switch the color of the button when the state changes, instead of ON and OFF words? For instante, yellow for ON and grey for OFF?
Thank you very much.
In the section
[quote]
.button {
padding: 15px 50px;
font-size: 24px;
text-align: center;
outline: none;
color: #fff;
background-color: #0f8b8d;
border: none;
border-radius: 5px;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
.button:active {
background-color: #0f8b8d;
box-shadow: 2 2px #CDCDCD;
transform: translateY(2px);
}
.state {
font-size: 1.5rem;
color:#8c8c8c;
font-weight: bold;
}
[/quote]
try changing the three settings for color: to what you want.
It won’t work, because I want to change the color for each state.
Hi.
You can do that on the onMessage function on the if statements, like this:
https://gist.github.com/sarasantos/c00e585f557e8e2f25ec058069323b71
I hope this helps.
Regards,
Sara
Sara – Can you also change the button text using onMessage?
Hi.
Sure, just get the button for its ID and change its innerHTML to the text you want.
Here’s the line you need to add: https://pastebin.com/Dk9KEFTx
Regards,
Sara
Hi, fantastic project! Can you please make a similar tutorial on websocket servers in micropython?
Hi Rui and Sara, I don’t get much time to do your projects due to a medical issue, but I follow along with your postings. I just love the way you put them all together, especially with the explanation of how the code works and with additional reading references within the project. You both are a great asset to helping those interested in developing their knowledge of various systems and what is available. Thanks also to the readers who contribute. Many thanks to all concerned.
Hi Phil.
Many thanks for your nice words!
And thank you for following our work.
Regards,
Sara
Hi Rui.
to avoid digitalWrite (ledPin, led State); in every cycle I have made these changes to your code.
do you see any problems in doing it in this way.
bool event;
unsigned long check = 1000;
case WS_EVT_DATA:
event = true;
handleWebSocketMessage(arg, data, len)
break;
void loop() {
if (millis() > check) {
check = millis() + 1000;
ws.cleanupClients();
}
if (event == true)
{
digitalWrite(ledPin, ledState);
event = false;
}
}
maybe this is a better way to handle millis and the roll over issue.
long eventTimeout=(long)millis()+1000;
void loop() {
if((long)millis()-eventTimeout>=0)
{
ws.cleanupClients();
Serial.println(“cleanup”);
eventTimeout=(long)millis()+1000;
}
if (event == true)
{
digitalWrite(ledPin, ledState);
event = false;
//Serial.println(“event”);
}
}
Arduino : 1.8.13 (Windows 10), Carte : “NodeMCU-32S, 80MHz, 921600”
C:\Users\Another\Documents\Arduino\ESP32_WebSocket_Server\ESP32_WebSocket_Server.ino: In function ‘String processor(const String&)’:
ESP32_WebSocket_Server:201:1: error: control reaches end of non-void function [-Werror=return-type]
}
Change The String String processor to this.
String processor(const String& var){
Serial.println(var);
if(var == “STATE”){
if (ledState){
return “ON”;
}
else{
return “OFF”;
}
}
return “”;
}
Hi Rui and Sara.
Thanks for another great tutorial. !!
I’m running your code on an ESP32 devboard.
When I refresh the website lot of times, the ESP32 board reboots and I get the error message, shown below.
It only happens when I use my phone on a WiFi connection.
When running the website on the computer there are no problems.
Do you know why this is happening? or does anyone have a solution to this issue.
CORRUPT HEAP: Bad head at 0x3ffcdc30. Expected 0xabba1234 got 0x3ffce05c
assertion “head != NULL” failed: file “/home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/heap/multi_heap_poisoning.c”, line 214, function: multi_heap_free
abort() was called at PC 0x40105487 on core 0
regards JB.
Hi JB,
I got. the Same Problem. With my iphone sometimes the Webserver Even doesen‘t start. On a PC with Firefox, Chrome the ESP32 works fine.
Dis you found the reason?
Regards Chris
Hi, very nice project, I am trying to create more outputs and individual buttons for each of output, what is the easiest way to do that?
Hi Rui and Sara.
Thanks for another great tutorial…
i need to add more GPIO pins to control, please tell me where can i make modifications in this code.
Hi.
You can get inspiration from this tutorial: https://randomnerdtutorials.com/esp32-async-web-server-espasyncwebserver-library/
Regards,
Sara
This is another great project, I’ve done this project but would like to use websockets for sensor data. Are you planning on a project to do that. Like using a DHT22 sensor. Thanks again for all the great projects, very informative.
Hi.
Yes, you can use websockets for that.
At the moment, we don’t have any tutorial with sensor readings and websockets. But, I’ll add that to my list.
Regards,
Sara
Hi
how can a second button switch a second output?
Regards,
Jan
Hello Sara and Rui,
I am an avid user of your learning pages. When someone asks me on the forums where they can educate themselves, I never hesitate to inform your site.
I have 2 small questions regarding this tutorial:
How to add a second button assigned to a second other ESP32 output.
How to invert the output state of the pin when starting the ESP32 (if there is a power failure, subsequently, when the power returns, the state of the pins will be on). I went to see the tutorial with the relays and the, that’s what I’m looking for but the concern is that the method used for this tutorial does not update the events instantly.
Many thanks to you for your work.
Bonjour Sara et Rui,
Merci pour votre tutorial tres explicite.
J’utilise ce mode pour mon projet actuel ESP8266. C’est parfait…
Pour finir ce dernier, pourriez-vous m’aider?
Je souhaite integrer un servo sg90 dans le sketch “ESP Web Server”.
J’ai: 1- un fichier HTML (un jeu de ma création) qui doit se connecter avec “ESP Web Server” à un servo. Quand le jeu est terminé, et que cela est gagné!!..le jeu declenche un basculement du servo et ouvre un verrou. c’est un jeu en extérieur donc “ESP Web Server” est indispensable.
Merci de votre aide dans la mesure du possible
Hi.
I’m sorry, but I have little knowledge of French.
I used translator to read your comment.
I think that this is what you need:
https://randomnerdtutorials.com/esp32-web-server-slider-pwm/
or this one:
https://randomnerdtutorials.com/esp32-servo-motor-web-server-arduino-ide/
Regards,
Sara
Rui and Sara,
Wow, what a great job in explaining the topic. Your postings have been extraordinarily helpful. What a great reference. I just started to learn about arduino and now ESP32 and your pages and videos have been of great help!
THANK YOU!
Thanks 🙂
Hi there, I separated the files into HTML/JS/CSS files to clean it all up a bit. Didn’t change any of the functionality and only had to change a couple of lines. You can download the files at: github.com/rayjamesdev/ESP32-WebSocket-Server-Control-Outputs-Separate-Files
Good Luck Have Fun! 🙂
Thank you very much! SPIFF came in handy.
But there is one problem. If you refresh the page, instead of the status “ON” or “OFF”, displays the variable %STATE%. When the page is first loaded, the same.
Hi
Any suggestions for getting an esp32 plus websockets plus GSM module like the sim7600 working?
Looks like websockets is easily supported for wifi…but cannot figure out for GSM modules.
Thanks
Phil
Hi, I loaded the code as is to ESP32 Huzzah.
Only changed the Led pin to 13 and my credentials of course.
I can open the page on 3 devices but clicking the button does nothing.
At some several seconds later I can see the console to show “STATUS”.
I have updated the AsyncTCP also the ESPAsyncWebServer libraries but no luck.
What could be the issue…?
By the way, the stae of the GPIO does not change at the web pages either.
Reloading the pasges works ok, just the gpio handling is not.
Thank you for your great tutorials!!
I have loaded the example to a ESP32 Huzzah. Changed the credentials & Gpio from 2 to 13. I can open the page on 3 devices but clicking the button does nothing on either device (laptop, phone).
I mean, the state of the GPIO does not change on the hardware and at the web pages either.
Reloading the pages refresh quickly; it is just the Gpio handling is not.
Several seconds later the console show “STATUS”. it looks like the ESP32 is lagging or not responsive.
What could be the cause…?
Hi.
What browser are you using?
Regards,
Sara
Hello!
It is Chrome in all devices.
Now I have tested with Explorer browser too. It is the same thing.
I see some code in the code: like:
console.log(‘Trying to open a WebSocket connection…’);
But at the Arduino terminal this never shows.
Where is supposed I will see these messages…?
console.log(‘Trying to open a WebSocket connection…’);
console.log(‘Connection opened’);
console.log(‘Connection closed’);
Regards.
Hi Bob, I have exactly the same question! Although I have worked through this tutorial and eveything works, I don’t yet understand what a console is and where one finds it. I would like to do that to be able to fully understand Rui and Sara’s detailed explanations about how the code works.
If you, or anyone else, knows of any beginners’ guides to seeing these console messages (on PlatformIO on Visual Studio Code on Mac) that would be great. So far I’ve found nothing that I can understand on Google.
I have received very helpful advice on this question from the PlatformIO Community. The ‘console’ is the actually the browser console, as explained in this link, which also contains information about using the browser console:
https://community.platformio.org/t/where-is-the-console-and-its-log/21525/3
Mystery solved.
Well, after a full day and with the help of my teammates we find out the reason.
It happen the JavaScript code was not executing. The variable “gateway” crashed the browser side. By some unknown reason it not passed the content after your “`” delimiters. At the end it complained by a JavaScript error (Uncaught SyntaxError: Unexpected end of input).
The fix was to replace your delimiters ( ` ) by apostrophe delimiters ( ‘ ) and to concatenate the “window.location.hostname” :
var gateway = ‘ws://’ + window.location.hostname + ‘/ws’;
After that, it worked great.
Hi Bob.
That’s weird.
I’ve just tested the code as it is, and it worked just fine.
Regards,
Sara
Hello Sara,
Hello all,
here is my solution.
Now there is no more error and it works fine.
Uncaught SyntaxError: Missing } in template expression (at (index):84:25)
var ip = “192.168.178.35”, wsf = “ws:\/\/”, wsl = “\/ws”;
var gateway = wsf + ip + wsl;
I got the same error. Only STATUS was output in the serial monitor.
With this I can finally update my solar page with ESP32 variables on all browsers (crome,edge,opera,.). Such a websocket is great.
Many greetings and many thanks Sara. This is one of the best pages I have found. Always happy to help 🙂
Chris
That’s great!
Thank you for sharing the solution.
Regards,
Sara
Hello, Rui and Sara. I see a lot of questions here – How do I add multiple buttons? What parts of the code should I edit? Could you make changes to this project so that it has two buttons? I think a lot of people will thank you for that. With great respect for your work.
Try this Alex https://github.com/eudojr/WebSocketESP32/blob/f934cd44d7def4bc6797bc3bf58f115312725119/Websocket/Websocket.ino
Thank you very much Enrique!
I hope the authors won’t mind? Added quite a bit to your lesson. I made two projects.
1- https://github.com/humaxoid/WebSocket_button_spiffs_ota
Added 5 buttons, Now based on SPIFFS and added the ability to update via OTA.
2- https://github.com/humaxoid/WebSocket_slider_spiffs_ota
The same thing, only the “Slider” type buttons.
That’s great!
Thanks for sharing!
Maybe we’ll use those examples in future tutorials.
Regards,
Sara
Hello
excellent tutorial.
I have even expanded it to 5 buttons as shared by Alex. Thank you.
I use 3 phones and 2 PCs and they connect perfectly and update as long as it’s on the same local network. However, in one of the phones I have put the public IP of my ESP32 device and in another phone the domain name of my place of installation.
In both phones I see the web page generated by the server but when I press the button “to activate a certain output, it is not reflected on any side of the connected clients.
Is it a problem with my modem? I already removed the antivirus and canceled the shields.
I defined the NAT with port 8080 and the fixed address that I assigned to the WiFi module.
But there is no client / server communication.
Can you give me an advice or solution to the external control?
Thanks
Is possible an ESP32 like web socket client?
Olá, muito obrigado pelos tutoriais. Gostaria de saber como alterar as credenciais do wifi sem ter que alterar o código para compilar no ESP32. Existe uma forma de fazer isso? Com a biblioteca WifiManager acho que é possível.
Obrigado!
Hi.
That library is not compatible with the AsyncWebServer library.
Fortunately, some users created versions compatible with the library. See this discussion: https://github.com/tzapu/WiFiManager/issues/158
I haven’t tried any of those libraries.
We have a project in which we build our own Wi-Fi manager from scratch: https://randomnerdtutorials.com/esp32-status-indicator-sensor-pcb/
I hope this helps.
Regards,
Sara
Hi, Can i add sensor value to this project? if so can you enlighten me
Excellent information Rui and Sara Santos, I have a question about WebSocket. There is a possibility that the DHT22 sensor values instead of going to a web page via html. Can those values be sent to another ESP32 with the same communication protocol and can it be observed on the Arduino Ide’s serial monitor?
Greetings and thanks for sharing your work.
Hi Rui and Sara,
So I was experimenting a project with the ESP8266. I came to know that there are ‘websocket’ and just ‘socket’ and that they are different. I couldn’t find anything that says these ‘sockets’ are supported with ESP8266.
I was building a server in ESP8266 to receive some JSON file to be processed from another server that I build using ‘socket’ in flask. But the ESP8266 was not receiving any data. I believe it was due to ‘websocket’ being used in ESP and ‘socket’ being used in flask, looks like the sent data was not compatible enough to be received.
Before using ESP to receive the data, I was using a serve made in flask. So I know that data was being sent and received. I wanted to try a different approach to my project by using ESP.
Is there any libraries that support the use of ‘socket’?
Please help.
Hi.
Check this: https://www.arduino.cc/reference/en/libraries/socketioclient/
Regards,
Sara
Hello,
is it possible that “String” harms the program over time?
if so, what to replace it with?
thank you so much
Hi.
If you’re worried about Strings, use char variables instead.
Regards,
Sara
Hi Rui and Sara,
Have you guys seen or heard of “Canvas-gauges” ? I’m trying to use those gauges to read continuously from analog pin on ESP32 using Websockets, with Arduino IDE. Ive managed to generate the gauge & customize it, set a value and it animates ok, but the websocket to get a live reading I cant figure out. I’d like to see a tutorial on it. here is an example of one type :
https://rawgit.com/Mikhus/canvas-gauges/master/examples/issue-63.html# you can click on numbers at top to see needle move to that reading.
https://canvas-gauges.com/documentation/examples/ some more they’re free and lots of options, animation options on the needle, Radial, Linear types etc.
The Arduino seems maybe limited but close, I had to put the Javascript in the arduino sketch, it’ll display the gauge on a served web page after loading into ESP32, but I can’t get the analog reading from a potentiometer to give live update.
Would you guys be interested in doing a tutorial on something like that? or do you know of someone that would help me out?
Thanks
Hi Stephen.
Yes. That was something that was on my to-do list.
I’ll try to come up with an example web server with gauges soon.
Thanks for sharing.
Regards,
Sara
Hi Sara,
Thank you so much, I’m glad to hear you’ll have an example soon.
Looking forward to it.
Hi.
We’ve just published an example with gauges.
Check it out on the following link:https://randomnerdtutorials.com/esp32-web-server-gauges/
I hope you like it.
Regards,
Sara
Hi Stephen,
To get a live reading in the first example you gave all you have to do is update the guagePS.value whenever you call a new reading event by the addEventListener.
Tony
Thanks Tony, I have way under-estimated the complexity of websockets. I thought if I modified an existing live updating potentiometer to text & bar display output which works, instantly updating on the web page, to these neat canvas-gauges I’d have it.
code: https://akuzechie.blogspot.com/2021/01/esp32-websocket-server-sending.html
I havent understood everything about that javascript code.
All the support code around websockets and Javascript I’m ignorant to. I thought I could just put the variable assigned to the ESP32 pin to change the value of the gauge and that would be enough, as of today I’m not smart enough to figure that out, but I keep looking and reading. The addEventListener im studying now.
Thanks for this great article Rui Santos.
I want (eventually) to make a sort of own home automation system and this is probably the best ‘base’ for it to control it via a website.
However, my intention is to use two kind of ‘boxes’, each with its own ESP32, one type with sensors, and one with relays in it. And to make it flexible, I like any sensor (or combination) to be able to control any relay.
Do I have to use a single webserver (with websockets as in your article), and let the sensor/relay boxes communicate with the webserver also via HTTP_GET (and maybe HTTP_POST) message? Or do I have to do the sensor/relaybox and webserver communication via something else than a WebServer? (I prefer to use WIFI only for all communication).
Hope this question is not much out of context.
Hi.
If you need to control multiple ESP32 boards via the same web interface, I suggest using MQTT for communication and Node-RED for the dashboard.
Regards,
Sara
FYI, the code works with ESP32 C3, only if you make the following change.
In the file StringArray.h of the ESPAsyncWebServer library comment line 131.
From
delete it;
to
// delete it;
bool remove(const T& t){
auto it = _root;
auto pit = _root;
while(it){
if(it->value() == t){
if(it == _root){
_root = _root->next;
} else {
pit->next = it->next;
}
if (_onRemove) {
_onRemove(it->value());
}
131 line // delete it;
return true;
}
pit = it;
it = it->next;
}
return false;
}
Thanks, I had this issue and your fix worked. I started an issue on the GitHub so maybe it will get fixed at the source.
Anyone getting errors such as this
c:/users/cruis/appdata/local/arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/gcc8_4_0-esp-2021r2/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld.exe: C:\Users\cruis\Documents\Arduino\libraries\ESPAsyncWebServer\src/AsyncWebSocket.cpp:1273: undefined reference to `SHA1Update’
I have tried different boards with the same issue. Have they broken things again with firmware updates
Hello!
Tell me please anybody, how to call gif animation from the loop function.
If the button is “ON”, then an animation located at the address should be displayed on the page
https://address.com/file1.gif
If the button is OFF, then
https://address.com/file1.gif
Hi.
You can do that using Javascript by changing the URL of an img tag.
https://www.w3schools.com/jsref/prop_img_src.asp
Regards,
Sara
Many phanks Sara!!!
It works!
Hello Rui and Sara.
I need your help with some direction to add some functionality using Websockets.
1.- Authentication before establishing a communication by Websockets.
2.- Navigate between several pages without losing the connection when going from one page to another.
First of all, Thanks
Hi.
For authentication, check this projecT: https://randomnerdtutorials.com/esp32-esp8266-web-server-http-authentication/
You can also check the official documentation: https://github.com/me-no-dev/ESPAsyncWebServer
I didn’t understand your second question.
Regards,
Sara
Thank you Sarah for your response.
My apologies for not explaining well.
The second query refers to the fact that when a client connects via Websocket, it remains connected as long as the browser is active on that page (index.html), but if I add a link to a new page stored in SPIFFS (page2.html) and that page is accessed then the Websocket client disconnects, but if it returns to index.html then it reconnects. What I am looking for is that the Websocket connection is maintained regardless of the pages (pages.html within SPIFFS) that I browse.
Greetings
Hi! I need to display the time in the browser, via a websocket.
html
javascript
document.getElementById(“dateTime”).innerHTML = ???;
How i can to send the time parameter to javascript?
Code
const char *ntpServer = “pool.ntp.org”;
const long gmtOffset_sec = 10800; // GMT+3
const int daylightOffset_sec = 3600;
void printLocalTime()
{
struct tm timeinfo;
if (!getLocalTime(&timeinfo))
{
Serial.println(“Couldn’t get the time”);
return;
}
Serial.println(&timeinfo, “%d %B %H:%M:%S”);
have just loaded the Firebased Websocket-server-Arduino and this is what i get
Connecting to WiFi..
Connecting to WiFi..
Connecting to WiFi..
Connecting to WiFi..
Connecting to WiFi..
Connecting to WiFi..
Connecting to WiFi..
ets Jun 8 2016 00:22:57
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
flash read err, 1000
ets_main.c 371
ets Jun 8 2016 00:22:57
rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:10944
load:0x40080400,len:6388
entry 0x400806b4
Connecting to WiFi..
Connecting to WiFi..
Connecting to WiFi..
Connecting to WiFi..
Connecting to WiFi..
Connecting to WiFi..
Connecting to WiFi..
any help appreciated
Hi.
What do you mean?
Can you better explain your issue?
Regards,
Sara
Sorry, it won’t connect to WIFI. Have checked spelling of WIFI credentials and password, just keeps scrolling connecting to wifi
Hi.
Make sure your board is relatively close to your router and double-check your network credentials.
Regards,
Sara
Do this:
int retries = 0;
// Connect to Wi-Fi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(2000);
Serial.printf(“Connecting to WiFi.. %i\n”, retries);
if (retries++ > 4) ESP.restart();
}
GREAT CODE! Thank you!!!
However… You left out a very important piece. I put that in and tested it and all is well. Sometimes, you want to have a switch on the board and turn on the LED (or relay or whatever) with that switch. When doing that, you also want to update the web client(s). It works. There is a little bit of extra code to be put into loop (and a couple of other places) to make this happen. Connect switch from the switchPin to ground to toggle the LED and report to clients.
Would be so good if you would update your code with this. Then, you would have a complete demo of WebSockets operation.
(as a global)
const int switchPin = 23; // Or whatever you like.
(in setup)
pinMode(switchPin, INPUT_PULLUP); // Connect to ground to toggle.
(in handleWebSocketMessage)
if (strcmp((char*)data, “toggle”) == 0) {
ledState = !ledState;
digitalWrite(ledPin, ledState); // This line added for better responsiveness of the LED
notifyClients();
}
(new loop routine)
void loop() {
ws.cleanupClients();
if (digitalRead(switchPin) == LOW){ // Switch pressed?
delay(50); // connect debounce
if (digitalRead(switchPin) == LOW) { // Switch still pressed?
ledState = !ledState;
digitalWrite(ledPin, ledState);
notifyClients();
while (digitalRead(switchPin) == LOW); // Wait for unpress.
delay(50); // disconnect debounce
}
}
}
Hi Rui and Sara!
A question about transferring data from the websocket server to the client.
How to properly transfer two data streams to the client?
I need some kind of identifier to distinguish them!
Sketch:
void loop() {
if (abcde…)
{
ws.textAll(String(temperature1));
}
if (edcba…)
{
ws.textAll(String(temperature2));
}
}
JavaScript:
function onMessage(event) {
document.getElementById(“datetemp1”).innerHTML = event.data;
document.getElementById(“datetemp2”).innerHTML = event.data;
}
Could you give an any example? Many thanks!
hello.
very nice exemples .
I search how to add “esp32-websocket-server-arduino”
to ” esp32-esp-now-wi-fi-web-server”.
I have problem to understant how to do this !
thx
Excellent lesson!
If the authors allow, I added a few buttons, placed files on SPIFFS and added the ability to update over the air.
https://github.com/humaxoid/WebSocket_button_spiffs_ota
That’s great!
Thanks for sharing.
Regards,
Sara
Hi Sara,
I followed the ESPWebSocket Server as above and uploaded using Arduino IDE to my
ESP32-DevKitC NodeMCU WiFi WLAN CP2102 ESP32-WROOM-32D Microcontroller Bluetooth Module Development Board. I then opened my computer browser and inserted the IP displayed for my esp32 in my Arduino serial monitor . The Toggle button was displayed but when toggled did not activate the on board led. I also opened a browser on my iPhone and again the toggle was displayed but did not activate the led when pressed but the toggle on my computer changed immediately. The same happened when I used the computer browser – the button on the iPhone changed as expected.
Thinking that the in-built led might have a different port to “2” on this board I changed the sketch using
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
to eliminate the specified port. This compiled but as before the LED did not activate when the toggle was pressed but both browser toggles duplicated the display of the other browser each time a toggle was pressed.
Any ideas how to solve this one ?
I would try uploading a basic “BLINK” sketch (from the Arduino IDE: File>Examples>01.Basics>Blink) to confirm that the on-board LED is working and/or maybe try an external LED attached to a different pin as described in this tutorial:
https://randomnerdtutorials.com/esp32-blink-an-led/
Thanks Jeff. I did as you suggested and an external led blinked fine. I guess the onboard led is defunct for some reason.
Hi! Thanks for the lesson. I didn’t understand what the variable does, that is, the string ledState = !ledState;
It inverts the state of the LED on the server-side (ESP32) after a Websocket message was received from a client telling to change state (i.e., ‘toggle’).
In the example above, the data passed is “toggle”. Where is this value stored? I want to further parse the value.
For example:
String x;
x = “toggle”;
x[0] = ‘t’
x[1] = ‘o’ etc
So I need to know the variable name and type. I’m thinking this must be a Json type but can’t find the actual item.
thanks much
Barry
Further info: Here is the html. There is a table with 2 buttons. The user may select A, or B, or A*B (2 independent transactions). My program will decide what to do with based on the returned id=”A1″ or “B1”. Actually there are multiple lines in the table so the general form is “An” where n=0..9
html code follows:
2
2
<!–
2
–>
Having failed at posting an code snippet, let me ask different question. How do I do a Serial.print of the data IN TO JS, and OUT OF JS?
I understand now that the HTML –> JS for checkboxes is either a 1/0 for each box. The “id” identifies which checkbox. I see that JS has a FOR loop to look at each box, then translate the 1/0 to “toggle”.
Is it possible to have the JS do NO processing and just send the JS input to JS output? That would be simpler. What would that code be?
What is the difference between a synchronous and asynchronous web server? Can WebSocket be used in both? Also, is it possible to get Wifi Manager for an asynchronous web server?
Hi Rui and Sara,
Thanks for the excellent explanation and code.
I’m trying to access the ESP-32 from outside my home network.
I have set up port forwarding on my router from port 8083 to port 80.
It appears that the websocket still sends from the browser on port 80 so I changed line 107 from:
var gateway =
ws://${window.location.hostname}/ws
;to:
var gateway = ‘ws://’ + window.location.hostname + ‘:8083/ws’;
and it now works but it’s not a very elegant solution.
Can you give a suggestion for a better fix?
Sara is my good teacher.
I am always grateful and wish you good health.
I really like John’s method, but there is a disadvantage that websocket does not work in the internal network.
If you want to use it normally both externally and internally,
you can change one Arduino content in addition to your opinion.
AsyncWebServer server(80);
to
AsyncWebServer server(8083);
This will work for now both internally and externally.
As someone who had the same problem for a long time, I appreciate your hint.
Written with Google Translate
Hi Sara and Rui, Could you please consider publishing an example of this websocket tutorial that has 2 led outputs, 2 webpage inputs, plus 2 physical button inputs. These are great tutorials but when it comes to expanding them beyond 1 input and 1 output it is sometimes hard to know where to expand the HTML, CSS, java, etc code and where not too. Several people have requested this above and in some of the other tutorials coments too and I think this would tie together nicely several of your other examples. Thankyou.
Hello Ford,
Why don’t you buy the book they publish like I did. It explains everything you are looking for and a whole lot more, it is not expensive. Everything can’t be free, what Rui and Sara do is hard work and a lot of time.
I did mate, a year ago, and the book has been great in learning webservers. But the book does not cover expanding beyond more than one input output with both web and physical buttons. The comments from Mike above covers some of it, thanks, but I still think it would be great if the RNT team could do a complete example in their teaching style.
In Rui and Sara’s book
Build Web Servers ESP32 ESP8266 V2
in module 3 section 2.2
maybe it’s what you’re looking for.
Hi.
I’ll add to my to-do list: multiple buttons with websocket that can also be controlled by physical buttons.
Maybe a better idea would be a guide that covers multiple types of buttons: momentary switches, toggle buttons, slider switches, ON/Off buttons, and radio buttons.
I already have projects lined up for the next month. So, it may take a little while.
Thanks for supporting our work.
Don’t forget, that as one of our customers, if you have specific issues, you can ask questions in our private forum: https://rntlab.com/forum/
Regards,
Sara
Sorry wrong email herwith the right one
Hi Rui and Sara
Perhaps this comment is duplicaat..
Thanks a lot for the nice and clear explanation of the script.
uploading the script to a esp32 the compiler says
C:\Users\Beheerder\Desktop\Arduino Schetsen Local\0ARuiSantos\ESPWebsocketServer\ESPWebSocketServer\ESPWebSocketServer.ino: In function ‘void loop()’:
ESPWebSocketServer:227:6: error: ‘class AsyncWebSocket’ has no member named ‘cleanupClients’
ws.cleanupClients();
^
C:\Users\Beheerder\Desktop\Arduino Schetsen Local\0ARuiSantos\ESPWebsocketServer\ESPWebSocketServer\ESPWebSocketServer.ino: In function ‘String processor(const String&)’:
ESPWebSocketServer:196:1: error: control reaches end of non-void function [-Werror=return-type]
}
^
cc1plus.exe: some warnings being treated as errors
Please advice what’s wrong…
Thanks,
wouter
Hi.
Can you double-check that you’ve installed and are using the proper libraries?
Regards,
Sara
How to use this code using arduinoJson 6 and more led?
Hello. Thank you, this is a really good example, it works very fast.
How can I display DHT values on this page without refreshing the page?
How to scale the number of buttons? Thank you in advance.
Hello,
Nice presentation of a project applicable for home automation. How can we add several buttons to it with the display of the state (On/Off) in the button? The idea being to control the lighting and other objects having an all or nothing operation. I still managed to put the code (html; css; js) on the spiff and connect to the Access Point of your project presented, it’s already a good start :).
Thank you for the help that will be given to me.
Drops COM port after flash,
Hello, i’m using a ESP32 DEVKITV1, powered and flashed VIA USB from my PC.
i don’t get any errors when flashing, but it drops COM3 as soon as it’s loaded and i can no longer view the board in the serial monitor.
i’ve tried this code a few times, after it flashes the COM port drops and i can’t contact the board.
Holding EN brings it back and i can flash different software on to it.
not sure what i’m missing here.
SOLVED
Although my USB connection was fine for my smaller projects, the issue was coming from the USB cable itself.
I was using a USB extension, as my PC is my TV. bringing the USB right to the PC resolved the issue and everything works as intended.
Hi,
After a while server stop response. Can you help about this.
I try to swap HTML&Co to SPIFFS (added #include “SPIFFS.h”, copied HTML&Co to SPIFFS) but code doesn´t work:
// Route for root / web page
server.on(“/”, HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(SPIFFS, “text/html”, “/index_html”, processor);
});
Hi.
What happens exactly when you say it doesn’t work?
Do you get any error messages on the Serial Monitor?
What about the web browser?
Regards,
Sara
Hi!
Your code contains errors. Here’s the right one
server.on(“/”, HTTP_GET, [](AsyncWebServerRequest *request)
{ request->send(SPIFFS, “/index.html”, “text/html”, false, processor); });
I get a compiler error:
C:\Users\43677\Documents\Arduino\esp32-websocket-server-arduino_SPIFFS_20230114163048\esp32-websocket-server-arduino_SPIFFS_20230114163048.ino: In lambda function:
C:\Users\43677\Documents\Arduino\esp32-websocket-server-arduino_SPIFFS_20230114163048\esp32-websocket-server-arduino_SPIFFS_20230114163048.ino:228:66: error: no matching function for call to ‘AsyncWebServerRequest::send_P(fs::SPIFFSFS&, const char [10], const char [12], String (&)(const String&))’
request->send_P(SPIFFS, “text/html”, “/index_html”, processor);
^
In file included from C:\Users\43677\Documents\Arduino\esp32-websocket-server-arduino_SPIFFS_20230114163048\esp32-websocket-server-arduino_SPIFFS_20230114163048.ino:11:
c:\Users\43677\Documents\Arduino\libraries\ESPAsyncWebServer-master\src/ESPAsyncWebServer.h:243:10: note: candidate: ‘void AsyncWebServerRequest::send_P(int, const String&, const uint8_t*, size_t, AwsTemplateProcessor)’
void send_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
^~~~~~
c:\Users\43677\Documents\Arduino\libraries\ESPAsyncWebServer-master\src/ESPAsyncWebServer.h:243:10: note: no known conversion for argument 1 from ‘fs::SPIFFSFS’ to ‘int’
c:\Users\43677\Documents\Arduino\libraries\ESPAsyncWebServer-master\src/ESPAsyncWebServer.h:244:10: note: candidate: ‘void AsyncWebServerRequest::send_P(int, const String&, const char*, AwsTemplateProcessor)’
void send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr);
^~~~~~
c:\Users\43677\Documents\Arduino\libraries\ESPAsyncWebServer-master\src/ESPAsyncWebServer.h:244:10: note: no known conversion for argument 1 from ‘fs::SPIFFSFS’ to ‘int’
exit status 1
Compilation error: no matching function for call to ‘AsyncWebServerRequest::send_P(fs::SPIFFSFS&, const char [10], const char [12], String (&)(const String&))’
Hi All,
can someone please tell me what the purpose of the following code is? It is located within the handleWebSocketMessage() function.
data[len] = 0;
does this set the last element to 0 or something? What is the purpose of this line anyway?.
BR
it is to close the char array, with a \0
so it can be used by strcmp function
Thanks for the program, which is working across PC browsers. But, the program is not working in mobile phone android chrome browser.
Hi.
Are those browsers on the same network as the ESP32?
What do you get when you try to access?
Regards,
Sara
Yes, the PC browsers and mobile browsers on the same WiFi network.
All PC browsers are working well for the above program.
But, on the android mobile chrome browser it is showing “The site can’t be reached”. “http://192.168.130/ is unreachable. ERR_ADDRESS_UNREACHABLE”.
Turn off your mobile phone data. Your phone is using outside world networks, therefore, it cant access the local area access point. It happens on mobile phones frequently.
Two PC browsers are working. Third PC browser is not working, For Android phone with mobile data off and connected to the same ssid, it is not connecting in mobile phones.
Hi.
I think it is an issue with the browser. But, I’m not sure why that happens.
Check this discussion and see if you can find something: https://github.com/me-no-dev/ESPAsyncWebServer/issues/259
Regards,
Sara
If I want a second page for some configuration values, should the second .html file have another .js file, or can two pages use on .js and share the websockets ?
Hello!
Are you planning a new Websocket tutorial to show sensor readings?
Hi.
Thanks for the suggestion.
I’ll add it to my to-do list.
Maybe I can publish it next month.
Regards,
Sara
tried the code on the wemos D1 R32 board ,
I get many warning errors and exit status 1 err and stops compiler.
using IDE 2.1.0
I also get many warnings with similar ESP8266 ESP8266WiFi.h and WebSocketServer.h
on wemos D1 R1 but compiler finishes and pgm runs with rather large HTML portion…
WebSocketServer.h is great , but no work on ESP32 board
Hi.
First, you need to install all libraries mentioned in the code.
I don’t recommend using Arduino IDE 2.0. I still recommend using the legacy version.
Which errors do you get?
Regards,
Sara
(contains solution)
A few years down the line, this doesn’t seem to work so well anymore…leading to various errors during compilation.
It seems that both the Websocket library as well as the ESP32 Async linrary are not maintained so well.
Errors I got were for instance:
“mbedtls_md5_starts throws error”/”undefined reference to
mbedtls_md5_starts'"
mbedtls_md5_startsSolution for that is to rename the instance of
to
mbedtls_md5_starts_ret` in the Webauthentication.cpp file in the libraries/ESPASyncWebserver/src directoryAnother error was “class IPAddres Public printable’ -> edit libraries\ESPAsyncWebServer-master\src\AsyncWebSocket.cpp to replace “return IPAddress(0U)” by “return IPAddress(0ul)” or “return IPAddress((uin32_t) 0u)”
Now it could be that maybe I had an old library (I thought I was up to date), but if others have this problem as well, perhaps this will help you
correcting type: make that “return IPAddress ((uint32_t) 0u)”
I used the first solution (0ul) and that worked fine
Hi.
If you’re using VS Code with platformio, change on platformio.ini, the platform to [email protected] instead espressif32.
That should solve the problem.
Regards,
Sara
How can i made esp32 with this type of functoinality as access point
Hi.
Check this tutorial to learn how to put the ESP32 in access point mode: https://randomnerdtutorials.com/esp32-access-point-ap-web-server/
Regards,
Sara
I have build many of your applications and very pleased but hav e broblems
Using IDE 2.0.0 Windows 10 ESP32 Dev Module. Latest libs. Good quality cables.
But got the same problem here as I wrote 27/8 in ESP32 WebSocket Server: Display Sensor Readings.
Generating function prototypes…
C:\Users\Peter\AppData\Local\Arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\esp-2021r2-patch5-8.4.0/bin/xtensa-esp32-elf-g++ -DHAVE_CONFIG_H -DMBEDTLS_CONFIG_FILE=”mbedtls/esp_config.h” -DUNITY_INCLUDE_CONFIG_H -DWITH_POSIX -D_GNU_SOURCE -DIDF_VER=”v4.4.5″ -DESP_PLATFORM -D_POSIX_READER_WRITER_LOCKS -IC:\Users\Peter\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.11/tools/sdk/esp32/include/newlib/platform_include –
D:\Documents\Arduino\Esp32-WebSocketControlOutput-Phone\Esp32-WebSocketControlOutput-Phone.ino: In function ‘void loop()’:
D:\Documents\Arduino\Esp32-WebSocketControlOutput-Phone\Esp32-WebSocketControlOutput-Phone.ino:180:6: error: ‘class AsyncWebSocket’ has no member named ‘cleanupClients’; did you mean ‘hasClient’?
ws.cleanupClients();
^~~~~~~~~~~~~~
hasClient
Using library SPIFFS at version 2.0.0 in folder: C:\Users\Peter\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.11\libraries\SPIFFS
Using library FS at version 2.0.0 in folder: C:\Users\Peter\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.11\libraries\FS
Using library WiFi at version 2.0.0 in folder: C:\Users\Peter\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.11\libraries\WiFi
Using library AsyncTCP at version 1.1.4 in folder: D:\Documents\Arduino\libraries\AsyncTCP
Using library ESP Async WebServer at version 1.2.2 in folder: D:\Documents\Arduino\libraries\ESPAsyncWebServer-master
Using library credentials in folder: D:\Documents\Arduino\libraries\credentials (legacy)
exit status 1
Compilation error: ‘class AsyncWebSocket’ has no member named ‘cleanupClients’; did you mean ‘hasClient’?
I am new to C++ and am having a difficult time understanding 1) the parameters passed to handleWebSocketMessage, 2) the AwsFrameInfo, 3) the conditional used in that function, and 4) the data[len] code.
Could someone break these down?
randomnerdtutorials.com/esp32-websocket-server-arduino/
Bonjour Sara
Merci pour vos publications.
Votre tuto fonctionne parfaitement sur PC, mais impossible de transmettre des données sur IPHONE e tablette Android. Ils se connectent mais ne reçoivent pas de données
Cordialement et merci
Hi,
I just want to know: can I fix the local IP in websocket?
thx
PS: sorry typing error upstair
Greeting!
I thank the authors for the code and excellent descriptions of individual parts of the code. I am a beginner with ESP32. I tried to compile the code for the ESP32 WebSocket Server. I got a series of errors with the following descriptions:
error: ‘mbedtls_md5_starts_ret’ was not declared in this scope; did you mean ‘mbedtls_md5_starts’?
error: ‘mbedtls_md5_update_ret’ was not declared in this scope; did you mean ‘mbedtls_md5_update’?
error: ‘ets_printf’ was not declared in this scope; did you mean ‘vswprintf’?
I tried with old and latest libraries. The result is the same.
Where can I look for an error?
Hi.
In the platformio.ini, change the platform to [email protected] instead espressif32.
Regards,
Sara
if the ledState change is performed while the program is running, notifyClients() must be used to restore clients; ?
Yes.
notifyClients() will notify all clients of the changes.
Regards,
Sara
Thank you for a fantastic series of articles on ESP32/EPS8266 networking. I jumped from MicroPython to C++ due performance issues (MP wasn’t able to keep up with real time events; well, expected). Rewriting from MP to C++ isn’t that straightforward; there are always these “small” details here and there. ESP32 documentation helps a lot, but it is much easier to grab your project, run it, learn how it is done, adapt my code accordingly.
(I am glad that Google led me to this site.)
That’s great.
Thank you.
Regards,
Sara
Thank you for this great tutorial!
I just have one problem: Android doesn’t allow me to connect to HTTPS, only to HTTPS – is there any way to add support for SSL/TSL?
It actually works just fine. I just need to make sure one of them is not on the walled-off guest network… 🙂
I get this error:
c:\Users\Betterparts\Documents\Arduino\libraries\ESPAsyncWebServer\src\WebAuthentication.cpp: In function ‘bool getMD5(uint8_t*, uint16_t, char*)’:
c:\Users\Betterparts\Documents\Arduino\libraries\ESPAsyncWebServer\src\WebAuthentication.cpp:74:3: error: ‘mbedtls_md5_starts_ret’ was not declared in this scope; did you mean ‘mbedtls_md5_starts’?
74 | mbedtls_md5_starts_ret(&_ctx);
| ^~~~~~~~~~~~~~~~~~~~~~
| mbedtls_md5_starts
c:\Users\Betterparts\Documents\Arduino\libraries\ESPAsyncWebServer\src\WebAuthentication.cpp:75:3: error: ‘mbedtls_md5_update_ret’ was not declared in this scope; did you mean ‘mbedtls_md5_update’?
75 | mbedtls_md5_update_ret(&_ctx, data, len);
| ^~~~~~~~~~~~~~~~~~~~~~
| mbedtls_md5_update
c:\Users\Betterparts\Documents\Arduino\libraries\ESPAsyncWebServer\src\WebAuthentication.cpp:76:3: error: ‘mbedtls_md5_finish_ret’ was not declared in this scope; did you mean ‘mbedtls_md5_finish’?
76 | mbedtls_md5_finish_ret(&_ctx, _buf);
| ^~~~~~~~~~~~~~~~~~~~~~
| mbedtls_md5_finish
exit status 1
Compilation error: exit status 1