ESP32 and ESP8266 WebSocket Guide
ESP32 WebSocket connections enable real-time bidirectional communication between IoT devices and web applications. The ESP32 and ESP8266 microcontrollers provide WiFi capabilities that make them suitable for WebSocket implementations in embedded systems. This guide covers WebSocket server and client implementations using ArduinoWebSockets and MicroPython.
Understanding WebSocket in IoT
WebSocket protocol provides persistent connections between ESP32 devices and remote servers or browsers. Unlike HTTP polling, WebSocket maintains an open connection that allows instant data transfer in both directions. For IoT applications, this reduces latency and bandwidth consumption when streaming sensor data or controlling devices remotely.
The ESP32 WebSocket implementation differs from browser-based WebSocket due to memory constraints and the lack of a full TCP/IP stack. ESP32 boards typically run FreeRTOS with limited RAM, requiring careful memory management. Understanding what is WebSocket helps when adapting the protocol for embedded systems.
ArduinoWebSockets Library
The ArduinoWebSockets library by Markus Sattler provides WebSocket functionality for ESP32, ESP8266, and other Arduino-compatible boards. This library handles the WebSocket handshake, frame parsing, and connection management.
Install the library through Arduino IDE Library Manager by searching for “WebSockets” or download from GitHub. The library supports both WebSocket servers and clients, with options for secure connections using SSL/TLS.
Key features of ArduinoWebSockets:
- WebSocket server and client modes
- Text and binary message support
- Ping/pong keepalive frames
- Multiple simultaneous client connections
- SSL/TLS encryption support
- Fragmented message handling
The library requires the ESP32 or ESP8266 Arduino core to be installed. Memory usage varies based on the number of concurrent connections, with each client consuming approximately 4-8KB of RAM.
ESP32 WebSocket Server
Building a WebSocket server on ESP32 allows browsers and applications to connect directly to your device. The server listens on a specified port and accepts incoming WebSocket connections.
#include <WiFi.h>
#include <WebSocketsServer.h>
const char* ssid = "YourWiFiSSID";
const char* password = "YourWiFiPassword";
WebSocketsServer webSocket = WebSocketsServer(81);
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
Serial.printf("[%u] Disconnected\n", num);
break;
case WStype_CONNECTED:
{
IPAddress ip = webSocket.remoteIP(num);
Serial.printf("[%u] Connected from %d.%d.%d.%d\n", num, ip[0], ip[1], ip[2], ip[3]);
webSocket.sendTXT(num, "Connected to ESP32");
}
break;
case WStype_TEXT:
Serial.printf("[%u] Received text: %s\n", num, payload);
// Echo message back to client
webSocket.sendTXT(num, payload, length);
// Broadcast to all clients
// webSocket.broadcastTXT(payload, length);
break;
case WStype_BIN:
Serial.printf("[%u] Received binary length: %u\n", num, length);
webSocket.sendBIN(num, payload, length);
break;
case WStype_ERROR:
case WStype_FRAGMENT_TEXT_START:
case WStype_FRAGMENT_BIN_START:
case WStype_FRAGMENT:
case WStype_FRAGMENT_FIN:
break;
}
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to WiFi. IP address: ");
Serial.println(WiFi.localIP());
webSocket.begin();
webSocket.onEvent(webSocketEvent);
}
void loop() {
webSocket.loop();
}
The server runs on port 81 in this example. Connect from a browser using ws://ESP32_IP_ADDRESS:81. The event handler processes connection, disconnection, and message events. Each connected client receives a unique number for identification.
For async operations that avoid blocking the main loop, use the ESPAsyncWebServer library combined with AsyncWebSocket:
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client,
AwsEventType type, void * arg, uint8_t *data, size_t len) {
if(type == WS_EVT_CONNECT) {
Serial.printf("Client %u connected\n", client->id());
client->text("Welcome to ESP32");
} else if(type == WS_EVT_DISCONNECT) {
Serial.printf("Client %u disconnected\n", client->id());
} else if(type == WS_EVT_DATA) {
AwsFrameInfo * info = (AwsFrameInfo*)arg;
if(info->final && info->index == 0 && info->len == len) {
if(info->opcode == WS_TEXT) {
data[len] = 0;
Serial.printf("Received: %s\n", (char*)data);
client->text((char*)data);
}
}
}
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
while(WiFi.status() != WL_CONNECTED) {
delay(500);
}
ws.onEvent(onWsEvent);
server.addHandler(&ws);
server.begin();
}
void loop() {
ws.cleanupClients();
delay(10);
}
The async approach handles multiple connections more efficiently and integrates with async web servers for serving HTML pages alongside WebSocket endpoints.
ESP32 WebSocket Client
ESP32 WebSocket client mode connects to remote WebSocket servers. This configuration suits applications that send sensor data to cloud services or communicate with JavaScript WebSocket servers.
#include <WiFi.h>
#include <WebSocketsClient.h>
const char* ssid = "YourWiFiSSID";
const char* password = "YourWiFiPassword";
const char* ws_host = "echo.websocket.org";
const uint16_t ws_port = 80;
const char* ws_path = "/";
WebSocketsClient webSocket;
void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
Serial.println("Disconnected from server");
break;
case WStype_CONNECTED:
Serial.printf("Connected to %s\n", payload);
webSocket.sendTXT("Hello from ESP32");
break;
case WStype_TEXT:
Serial.printf("Received: %s\n", payload);
break;
case WStype_BIN:
Serial.printf("Received binary data, length: %u\n", length);
break;
case WStype_ERROR:
Serial.println("WebSocket error");
break;
case WStype_PING:
Serial.println("Received ping");
break;
case WStype_PONG:
Serial.println("Received pong");
break;
}
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println(WiFi.localIP());
webSocket.begin(ws_host, ws_port, ws_path);
webSocket.onEvent(webSocketEvent);
webSocket.setReconnectInterval(5000);
webSocket.enableHeartbeat(15000, 3000, 2);
}
void loop() {
webSocket.loop();
// Send periodic data
static unsigned long lastSend = 0;
if(millis() - lastSend > 10000) {
String message = "Uptime: " + String(millis() / 1000) + "s";
webSocket.sendTXT(message);
lastSend = millis();
}
}
The client automatically reconnects if the connection drops. The setReconnectInterval() method defines the delay between reconnection attempts. Heartbeat functionality sends ping frames to detect broken connections.
For secure WebSocket connections (wss://), use SSL:
webSocket.beginSSL(ws_host, ws_port, ws_path);
SSL connections require additional memory and may need certificate validation depending on the server configuration.
Streaming Sensor Data Over WebSocket
Real-time sensor monitoring demonstrates WebSocket advantages in IoT. This example streams temperature and humidity data from a DHT22 sensor:
#include <WiFi.h>
#include <WebSocketsServer.h>
#include <DHT.h>
#include <ArduinoJson.h>
#define DHTPIN 4
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
WebSocketsServer webSocket = WebSocketsServer(81);
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
if(type == WStype_CONNECTED) {
Serial.printf("Client %u connected\n", num);
} else if(type == WStype_DISCONNECTED) {
Serial.printf("Client %u disconnected\n", num);
}
}
void setup() {
Serial.begin(115200);
dht.begin();
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
webSocket.begin();
webSocket.onEvent(webSocketEvent);
}
void loop() {
webSocket.loop();
static unsigned long lastUpdate = 0;
if(millis() - lastUpdate > 2000) {
float temperature = dht.readTemperature();
float humidity = dht.readHumidity();
if(!isnan(temperature) && !isnan(humidity)) {
StaticJsonDocument<200> doc;
doc["temperature"] = temperature;
doc["humidity"] = humidity;
doc["timestamp"] = millis();
String json;
serializeJson(doc, json);
webSocket.broadcastTXT(json);
Serial.println(json);
}
lastUpdate = millis();
}
}
The server broadcasts JSON-formatted sensor readings every 2 seconds to all connected clients. Browser applications parse the JSON and display the data in real-time charts or dashboards.
For high-frequency data streams, binary WebSocket frames reduce bandwidth:
struct SensorData {
float temperature;
float humidity;
uint32_t timestamp;
};
SensorData data;
data.temperature = temperature;
data.humidity = humidity;
data.timestamp = millis();
webSocket.broadcastBIN((uint8_t*)&data, sizeof(data));
Binary encoding decreases message size from approximately 60 bytes (JSON) to 12 bytes (struct).
MicroPython WebSocket
MicroPython provides WebSocket support through the uwebsockets library for ESP32 and ESP8266. MicroPython simplifies development with Python syntax while maintaining hardware access.
Install MicroPython firmware on ESP32, then upload the WebSocket library. A basic WebSocket client:
import network
import uwebsockets.client
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect('YourSSID', 'YourPassword')
while not wlan.isconnected():
pass
print('Connected:', wlan.ifconfig())
websocket = uwebsockets.client.connect('ws://echo.websocket.org')
websocket.send('Hello from MicroPython')
while True:
message = websocket.recv()
if message:
print('Received:', message)
websocket.send(message)
MicroPython WebSocket server implementation:
import network
import socket
from uwebsockets.server import WebSocketServer, WebSocketClient
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect('YourSSID', 'YourPassword')
while not wlan.isconnected():
pass
class TestClient(WebSocketClient):
def process(self):
message = self.connection.read()
if message:
print('Received:', message)
self.connection.write(message)
server = WebSocketServer(TestClient, '0.0.0.0', 8000)
server.start()
while True:
server.process_all()
MicroPython offers faster development cycles but uses more memory than compiled C++ code. For memory-constrained applications, Arduino C++ provides better resource utilization.
WebSocket ESP32 Tips and Best Practices
Memory management affects ESP32 WebSocket stability. Monitor free heap memory using ESP.getFreeHeap() and avoid memory leaks by properly closing connections.
void loop() {
webSocket.loop();
static unsigned long lastCheck = 0;
if(millis() - lastCheck > 30000) {
Serial.printf("Free heap: %d bytes\n", ESP.getFreeHeap());
lastCheck = millis();
}
}
Limit concurrent connections based on available memory. Each WebSocket client consumes 4-8KB of RAM. ESP32 with 320KB RAM typically handles 10-20 simultaneous connections safely.
Implement reconnection logic for WebSocket clients:
void loop() {
webSocket.loop();
if(!webSocket.isConnected()) {
static unsigned long lastReconnect = 0;
if(millis() - lastReconnect > 5000) {
Serial.println("Attempting reconnection...");
webSocket.begin(ws_host, ws_port, ws_path);
lastReconnect = millis();
}
}
}
Use heartbeat mechanisms to detect dead connections. ArduinoWebSockets library provides built-in heartbeat support:
webSocket.enableHeartbeat(15000, 3000, 2);
This sends ping frames every 15 seconds, waits 3 seconds for pong responses, and disconnects after 2 missed pongs.
For production deployments, implement error handling and logging:
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_ERROR:
Serial.printf("[%u] Error occurred\n", num);
break;
case WStype_DISCONNECTED:
Serial.printf("[%u] Disconnected at %lu\n", num, millis());
break;
}
}
Optimize message frequency based on network conditions. Sending messages faster than 10-20 per second may overwhelm the TCP stack and cause packet loss.
Store WiFi credentials securely rather than hardcoding them. Use SPIFFS or preferences library to store configuration:
#include <Preferences.h>
Preferences preferences;
preferences.begin("wifi", true);
String ssid = preferences.getString("ssid", "");
String password = preferences.getString("password", "");
preferences.end();
FAQ
What is the difference between websocket esp32 and websocket esp8266?
ESP32 and ESP8266 both support WebSocket but differ in performance and memory. ESP32 has dual-core processors, more RAM (320KB vs 80KB), and better WiFi performance. ESP32 handles more concurrent WebSocket connections and processes data faster. ESP8266 works for basic WebSocket applications with fewer clients. Both use the same ArduinoWebSockets library, making code largely portable between platforms.
How much memory does arduino websocket consume on ESP32?
Each WebSocket connection uses 4-8KB of RAM on ESP32, depending on buffer sizes and message length. The base WebSocketsServer or WebSocketsClient library consumes approximately 10-15KB. With 320KB total RAM, ESP32 safely handles 10-20 concurrent connections while leaving memory for application code. Monitor heap usage with ESP.getFreeHeap() to prevent crashes from memory exhaustion.
Can ESP32 WebSocket use SSL/TLS encryption?
Yes, ESP32 supports secure WebSocket connections (wss://) through SSL/TLS. Use webSocket.beginSSL() instead of webSocket.begin() for clients. SSL connections require additional memory (20-40KB) and CPU resources. Certificate validation can be enabled or disabled based on security requirements. For production systems, implement proper certificate verification to prevent man-in-the-middle attacks.
How do I debug websocket esp8266 connection issues?
Enable serial debugging to monitor connection status and errors. Check WiFi signal strength using WiFi.RSSI() as weak signals cause disconnections. Verify firewall rules allow WebSocket ports. Use public echo servers like echo.websocket.org for initial testing. Monitor free heap memory to detect memory leaks. Enable heartbeat functionality to detect and recover from dead connections automatically. Test with both text and binary messages to identify frame parsing issues.