WebSocket on Arduino, esp8266 and esp32: temperature and humidity realtime update – 3

Spread the love

WebSocket Arduino esp8266 esp32 complete web application with token auth
WebSocket Arduino esp8266 esp32 complete web application with token auth

Do you remember the project created in the “How to create a web server with esp8266 and esp32” guide? The time has come to place it in the modern era, then we will remove polling and add push for live data modification.

This mode is the best way, normal applications retrieve initial data from REST calls and then they are updated via WebSocket.

First we are going to add the WebSocket server.

#include <WebSocketsServer.h>
[...]
const uint8_t wsPort = 81;
WebSocketsServer webSocket = WebSocketsServer(wsPort);
[...]
// In the setup
Serial.print("Starting WebSocket..");
webSocket.begin();
webSocket.onEvent(webSocketEvent);
Serial.println("OK");

[...]
// In the loop
webSocket.loop();

So in the cycle we will add a broadcast message (for all clients) every 2 seconds which updates the temperature and humidity

if (connectionNumber>0 && lastUpdate+messageInterval<millis()){
    Serial.println("[WSc] SENT: Broadcast message!!");
    const size_t capacity = 1024;
    DynamicJsonDocument doc(capacity);

    doc["humidity"] = dht12.readHumidity();
    doc["temp"] = dht12.readTemperature();

// If you don't have a DHT12 put only the library
// comment upper line and decomment this line
    doc["humidity"] = random(10,80);
    doc["temp"] = random(1000,3500)/100.;

    String buf;
    serializeJson(doc, buf);

    webSocket.broadcastTXT(buf);
    lastUpdate = millis();
}

The code is quite simple, but pay attention to the connectionNumber> 0 condition which prevents WebSocket messages from being sent if no clients are connected.

It’s a simple variable that I update every connection/disconnection with the number of connected clients.

So all the management can be said to be completed

esp8266 WebServer humidity gauge
esp8266 WebServer humidity gauge

Here in the webSocketEvent you can see the management of the number of connected clients.

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);
		webSocket.sendTXT(num, "{\"connected\":false}");
		connectionNumber = webSocket.connectedClients();

		Serial.print("Connected devices ");
		Serial.println(connectionNumber);
            break;
        case WStype_CONNECTED:
            {
                IPAddress ip = webSocket.remoteIP(num);
                Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);

		// send message to client
		webSocket.sendTXT(num, "{\"connected\":true}");
		connectionNumber = webSocket.connectedClients();

		Serial.print("Connected devices ");
		Serial.println(connectionNumber);
            }
            break;
        case WStype_TEXT:
        	Serial.printf("[%u] RECEIVE TXT: %s\n", num, payload);

            // send message to client
             webSocket.sendTXT(num, "(ECHO MESSAGE) "+String((char *)payload));

            // send data to all connected clients
            // webSocket.broadcastTXT("message here");
            break;
        case WStype_BIN:
        	Serial.printf("[%u] get binary length: %u\n", num, length);
            hexdump(payload, length);

            // send message to client
            // webSocket.sendBIN(num, payload, length);
            break;
    }
}

So we must add the event listener to the front-end to manage the message

        function onOpen(evt) {
            console.log("CONNECTED");
        }

        function onClose(evt) {
            console.log("DISCONNECTED");
        }

        function onMessage(evt) {
            var res = JSON.parse(evt.data);
            hw.setHumidity(res.humidity);
            tw.setTemperature(res.temp);
        }

        function onError(evt) {
            alert(evt.data);
        }

        function initWebSocket() {
            var uri = 'ws://'+location.hostname+':81/';

            websocket = new WebSocket(uri);

            websocket.onopen = function (evt) {
                onOpen(evt)
            };
            websocket.onclose = function (evt) {
                onClose(evt)
            };
            websocket.onmessage = function (evt) {
                onMessage(evt)
            };
            websocket.onerror = function (evt) {
                onError(evt)
            };
        }
        initWebSocket();

The code is very simple and work correctly.

Authentication

WebServer esp8266 login page
WebServer esp8266 login page

Simple work workaround

This is not so beautiful solution, but It’s quite simple to implement without the use of extra knowledge (like header, cookie, etc.), and It’s quite powerful.

You can add a token like authorization, in this way token is passed like the login.

//  Simple way to invalidate session every restart
//	ws_auth_code = sha1(String(www_username) + ":" + String(www_password) + ":" + String(random(1000)))+":";
	ws_auth_code = sha1(String(www_username) + ":" + String(www_password))+":";

	Serial.println(base64::encode(ws_auth_code).c_str());

	webSocket.setAuthorization(base64::encode(ws_auth_code).c_str());

Than I add new token in the authentication phase (without clientIP)

httpServer.sendHeader("Set-Cookie", "ESPWSSESSIONID=" + String(ws_auth_code));

than I insert the token in the uri of ws, like described in the previous article.

Here retrieve cookie

        var getCookie = function (name) {
            var value = "; " + document.cookie;
            var parts = value.split("; " + name + "=");
            if (parts.length == 2) return parts.pop().split(";").shift();
        };

        if (ENABLE_WS_AUTHORIZATION) {
            // Example
            var cookieVal = getCookie('ESPWSSESSIONID');

            initWebSocket(cookieVal);
        }else{
            initWebSocket();
        }

and it will be passed into the uri in this way

ws://937c7478aaa380b87242dd7aba8ff9304e5cf089@192.168.1.136:81/
            if (authToken){
                var newUri = uri.substring(0,uri.indexOf("//")+2)+authToken+'@'+uri.substring(uri.indexOf("//")+2);
                websocket = new WebSocket(newUri,'arduino');
            }else {
                websocket = new WebSocket(uri);
            }

Check the complete code on GitHub

Token Based Authentication

Last time we did a token-based authentication, we can now retrieve that implementation and integrate the WebSoket.

Has you can see in the article “Web Server with esp8266 and esp32: manage security and authentication” I add the token in ESPSESSIONID cookie,

void handleLogin() {
	Serial.println("Handle login");
	String msg;
	if (httpServer.hasHeader("Cookie")) {
		// Print cookies
		Serial.print("Found cookie: ");
		String cookie = httpServer.header("Cookie");
		Serial.println(cookie);
	}

	if (httpServer.hasArg("username") && httpServer.hasArg("password")) {
		Serial.print("Found parameter: ");

		if (httpServer.arg("username") == String(www_username) && httpServer.arg("password") == String(www_password)) {
			httpServer.sendHeader("Location", "/");
			httpServer.sendHeader("Cache-Control", "no-cache");

			String token = sha1(String(www_username) + ":" + String(www_password) + ":" + httpServer.client().remoteIP().toString());
			httpServer.sendHeader("Set-Cookie", "ESPSESSIONID=" + token);
			httpServer.sendHeader("Set-Cookie", "CLIENTIP=" + httpServer.client().remoteIP().toString());

			httpServer.send(301);
			Serial.println("Log in Successful");
			return;
		}
		msg = "Wrong username/password! try again.";
		Serial.println("Log in Failed");
		httpServer.sendHeader("Location", "/login.html?msg=" + msg);
		httpServer.sendHeader("Cache-Control", "no-cache");
		httpServer.send(301);
		return;
	}
}


then every REST call I check if It’s present.

//Check if header is present and correct
bool is_authenticated() {
	Serial.println("Enter is_authenticated");
	if (httpServer.hasHeader("Cookie")) {
		Serial.print("Found cookie: ");
		String cookie = httpServer.header("Cookie");
		Serial.println(cookie);

		String token = sha1(String(www_username) + ":" +
				String(www_password) + ":" +
				httpServer.client().remoteIP().toString());
//	token = sha1(token);

		if (cookie.indexOf("ESPSESSIONID=" + token) != -1) {
			Serial.println("Authentication Successful");
			return true;
		}
	}
	Serial.println("Authentication Failed");
	return false;
}

As you can see I use IP client also to generate a specified token for device, so to resolve this problem I add the client IP to the cookie.

			String token = sha1(String(www_username) + ":" + String(www_password) + ":" + httpServer.client().remoteIP().toString());
			httpServer.sendHeader("Set-Cookie", "ESPSESSIONID=" + token);
			httpServer.sendHeader("Set-Cookie", "CLIENTIP=" + httpServer.client().remoteIP().toString());

Than we must add the custom validator to the WebSoket

    const char * headerkeys[] = { "Cookie" };
    size_t headerKeyCount = sizeof(headerkeys) / sizeof(char*);
    webSocket.onValidateHttpHeader(validateHttpHeader, headerkeys, headerKeyCount);

The onValidateHttpHeader requires 3 parameters:

  • validateHttpHeader: is the function that do the validation;
  • headerkeys: is the list of headers that are mandatory;
  • headerKeyCount: is the size of the headers.

The bool validateHttpHeader(String headerName, String headerValue) function has 2 parameter

  • headerName: the name of the cookie;
  • headerValue: the value of the cookie.

We now have the specific header to validate the cookie, so the mandatory header is the cookie too,

/*
 * The WebSocketServerHttpHeaderValFunc delegate passed to webSocket.onValidateHttpHeader
 */
bool validateHttpHeader(String headerName, String headerValue) {

	//assume a true response for any headers not handled by this validator
	bool valid = true;

	if(headerName.equalsIgnoreCase("Cookie")) {
		//if the header passed is the Cookie header, validate it according to the rules in 'isCookieValid' function
		valid = isCookieValid(headerValue);
	}

	return valid;
}

and when we find this header we must the check of the value

bool isCookieValid(String rawCookieHeaderValue) {
	Serial.print("Cookie validation ");
	Serial.println(rawCookieHeaderValue);
	String clientIP = "";
	if (rawCookieHeaderValue.indexOf("CLIENTIP") != -1) {
			clientIP = rawCookieHeaderValue.substring(rawCookieHeaderValue.indexOf("CLIENTIP=") + 9, rawCookieHeaderValue.indexOf("|"));
			Serial.print("clientIP ");
			Serial.println(clientIP);
	}

	if (rawCookieHeaderValue.indexOf("ESPSESSIONID") != -1) {
		String tokenStr = rawCookieHeaderValue.substring(rawCookieHeaderValue.indexOf("ESPSESSIONID=") + 13, rawCookieHeaderValue.indexOf(";"));

		Serial.print("ESPSESSIONID ");
		Serial.println(tokenStr);

		String token = sha1(String(www_username) + ":" + String(www_password) + ":" + clientIP);
		Serial.print("token ");
		Serial.println(token);

		return tokenStr == token;
	}
	return false;
}

To the front-end you don’t need other change, te take advantage of the existing cookie.

And here is the complete server code

/*
 *  WeMos D1 mini (esp8266)
 *  Simple web server that read from SPIFFS and
 *  stream on browser various type of file
 *  and manage gzip format also.
 *  Here I add a management of a token authentication
 *  with a custom login form, and relative logout.
 *  Add WebSocket data update with the
 *  integration with token authentication handshake
 *
 * DHT12 library https://mischianti.org/dht12-library-en/
 *
 * DHT12      ----- Esp8266
 * SDL        ----- D1
 * SDA        ----- D2
 *
 *  by Mischianti Renzo <https://mischianti.org>
 *
 *  https://mischianti.org/websocket-on-arduino-esp8266-and-esp32-temperature-and-humidity-realtime-update-3/
 *
 */
#define ENABLE_WS_AUTHORIZATION

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <WebSocketsServer.h>
#include <base64.h>


#include <FS.h>
#include "Hash.h"
#include <DHT12.h>
#include <ArduinoJson.h>

const char* ssid = "<YOUR-SSID>";
const char* password = "<YOUR-PASSWD>";

const char* www_username = "admin";
const char* www_password = "esp8266";
String ws_auth_code;

const uint8_t wsPort = 81;
int8_t connectionNumber = 0;
unsigned long messageInterval = 2000;

// Set dht12 i2c comunication on default Wire pin
DHT12 dht12;

ESP8266WebServer httpServer(80);
WebSocketsServer webSocket = WebSocketsServer(wsPort);

void serverRouting();
void manageSecurity();

#ifdef ENABLE_WS_AUTHORIZATION
bool validateHttpHeader(String headerName, String headerValue);
#endif

void setup(void) {
	Serial.begin(115200);
	WiFi.mode(WIFI_STA);
	WiFi.begin(ssid, password);
	Serial.println("");
	// Start sensor
	dht12.begin();

	// Wait for connection
	while (WiFi.status() != WL_CONNECTED) {
		delay(500);
		Serial.print(".");
	}

	Serial.println("");
	Serial.print("Connected to ");
	Serial.println(ssid);
	Serial.print("IP address: ");
	Serial.println(WiFi.localIP());

	Serial.print(F("Inizializing FS..."));
	if (SPIFFS.begin()) {
		Serial.println(F("done."));
	} else {
		Serial.println(F("fail."));
	}

	Serial.println("Set routing for http server!");
	serverRouting();
	httpServer.begin();
	Serial.println("HTTP server started");

	Serial.print("Starting WebSocket..");

#ifdef ENABLE_WS_AUTHORIZATION
//  Simple way to invalidate session every restart
//	ws_auth_code = sha1(String(www_username) + ":" + String(www_password) + ":" + String(random(1000)))+":";
//	ws_auth_code = sha1(String(www_username) + ":" + String(www_password))+":";
//
//	Serial.println(base64::encode(ws_auth_code).c_str());
//
//	webSocket.setAuthorization(base64::encode(ws_auth_code).c_str());

	const char * headerkeys[] = { "Cookie" };
	size_t headerKeyCount = sizeof(headerkeys) / sizeof(char*);
	webSocket.onValidateHttpHeader(validateHttpHeader, headerkeys, headerKeyCount);
#endif

    webSocket.begin();
    webSocket.onEvent(webSocketEvent);
	Serial.println("OK");
}
unsigned long lastUpdate = millis()+messageInterval;

void loop(void) {
	httpServer.handleClient();
    webSocket.loop();

	if (connectionNumber>0 && lastUpdate+messageInterval<millis()){
		Serial.println("[WSc] SENT: Broadcast message!!");
    	const size_t capacity = 1024;
    	DynamicJsonDocument doc(capacity);

    	doc["humidity"] = dht12.readHumidity();
    	doc["temp"] = dht12.readTemperature();

    // If you don't have a DHT12 put only the library
    // comment upper line and decomment this line
    	doc["humidity"] = random(10,80);
    	doc["temp"] = random(1000,3500)/100.;

    	String buf;
    	serializeJson(doc, buf);

		webSocket.broadcastTXT(buf);
		lastUpdate = millis();
	}

}

String getContentType(String filename) {
	if (filename.endsWith(F(".htm"))) return F("text/html");
	else if (filename.endsWith(F(".html"))) return F("text/html");
	else if (filename.endsWith(F(".css"))) return F("text/css");
	else if (filename.endsWith(F(".js"))) return F("application/javascript");
	else if (filename.endsWith(F(".json"))) return F("application/json");
	else if (filename.endsWith(F(".png"))) return F("image/png");
	else if (filename.endsWith(F(".gif"))) return F("image/gif");
	else if (filename.endsWith(F(".jpg"))) return F("image/jpeg");
	else if (filename.endsWith(F(".jpeg"))) return F("image/jpeg");
	else if (filename.endsWith(F(".ico"))) return F("image/x-icon");
	else if (filename.endsWith(F(".xml"))) return F("text/xml");
	else if (filename.endsWith(F(".pdf"))) return F("application/x-pdf");
	else if (filename.endsWith(F(".zip"))) return F("application/x-zip");
	else if (filename.endsWith(F(".gz"))) return F("application/x-gzip");
	return F("text/plain");
}

bool handleFileRead(String path) {
	Serial.print(F("handleFileRead: "));
	Serial.println(path);

	if (!is_authenticated()) {
		Serial.println(F("Go on not login!"));
		path = F("/login.html");
		handleLogout();
		return true;
	} else {
		if (path.endsWith("/")) path += F("index.html"); // If a folder is requested, send the index file
	}
	String contentType = getContentType(path);             	// Get the MIME type
	String pathWithGz = path + F(".gz");
	if (SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)) { // If the file exists, either as a compressed archive, or normal
		if (SPIFFS.exists(pathWithGz)) { // If there's a compressed version available
			path += F(".gz");                      // Use the compressed version
		}
		fs::File file = SPIFFS.open(path, "r");                 // Open the file
		size_t sent = httpServer.streamFile(file, contentType); // Send it to the client
		file.close();                                    // Close the file again
		Serial.println(
				String(F("\tSent file: ")) + path + String(F(" of size "))
						+ sent);
		return true;
	}
	Serial.println(String(F("\tFile Not Found: ")) + path);
	return false;                     // If the file doesn't exist, return false
}

void handleNotFound() {
	String message = "File Not Found\n\n";
	message += "URI: ";
	message += httpServer.uri();
	message += "\nMethod: ";
	message += (httpServer.method() == HTTP_GET) ? "GET" : "POST";
	message += "\nArguments: ";
	message += httpServer.args();
	message += "\n";

	for (uint8_t i = 0; i < httpServer.args(); i++) {
		message += " " + httpServer.argName(i) + ": " + httpServer.arg(i)
				+ "\n";
	}

	httpServer.send(404, "text/plain", message);
}

void handleLogin() {
	Serial.println("Handle login");
	String msg;
	if (httpServer.hasHeader("Cookie")) {
		// Print cookies
		Serial.print("Found cookie: ");
		String cookie = httpServer.header("Cookie");
		Serial.println(cookie);
	}

	if (httpServer.hasArg("username") && httpServer.hasArg("password")) {
		Serial.print("Found parameter: ");

		if (httpServer.arg("username") == String(www_username) && httpServer.arg("password") == String(www_password)) {
			httpServer.sendHeader("Location", "/");
			httpServer.sendHeader("Cache-Control", "no-cache");

			String token = sha1(String(www_username) + ":" + String(www_password) + ":" + httpServer.client().remoteIP().toString());
			httpServer.sendHeader("Set-Cookie", "ESPSESSIONID=" + token);
			httpServer.sendHeader("Set-Cookie", "CLIENTIP=" + httpServer.client().remoteIP().toString());

			httpServer.send(301);
			Serial.println("Log in Successful");
			return;
		}
		msg = "Wrong username/password! try again.";
		Serial.println("Log in Failed");
		httpServer.sendHeader("Location", "/login.html?msg=" + msg);
		httpServer.sendHeader("Cache-Control", "no-cache");
		httpServer.send(301);
		return;
	}
}

/**
 * Manage logout (simply remove correct token and redirect to login form)
 */
void handleLogout() {
	Serial.println("Disconnection");
	httpServer.sendHeader("Location", "/login.html?msg=User disconnected");
	httpServer.sendHeader("Cache-Control", "no-cache");
	httpServer.sendHeader("Set-Cookie", "ESPSESSIONID=0");
//	httpServer.sendHeader("Set-Cookie", "ESPWSSESSIONID=0");
	httpServer.send(301);
	return;
}

/**
 * Retrieve temperature humidity realtime data
 */
void handleTemperatureHumidity(){
	Serial.println("handleTemperatureHumidity");

	manageSecurity();

	Serial.println("handleTemperatureHumidity security pass!");

	const size_t capacity = 1024;
	DynamicJsonDocument doc(capacity);

	doc["humidity"] = dht12.readHumidity();
	doc["temp"] = dht12.readTemperature();

// If you don't have a DHT12 put only the library
// comment upper line and decomment this line
	doc["humidity"] = random(10,80);
	doc["temp"] = random(1000,3500)/100.;

	String buf;
	serializeJson(doc, buf);
	httpServer.send(200, F("application/json"), buf);
}

//Check if header is present and correct
bool is_authenticated() {
	Serial.println("Enter is_authenticated");
	if (httpServer.hasHeader("Cookie")) {
		Serial.print("Found cookie: ");
		String cookie = httpServer.header("Cookie");
		Serial.println(cookie);

		String token = sha1(String(www_username) + ":" +
				String(www_password) + ":" +
				httpServer.client().remoteIP().toString());

		if (cookie.indexOf("ESPSESSIONID=" + token) != -1) {
			Serial.println("Authentication Successful");
			return true;
		}
	}
	Serial.println("Authentication Failed");
	return false;
}

void manageSecurity(){
	if (!is_authenticated()) {
		httpServer.send(401, F("application/json"), "{\"msg\": \"You must authenticate!\"}");
		return;
	}
}

void restEndPoint(){
	// External rest end point (out of authentication)
	httpServer.on("/login", HTTP_POST, handleLogin);
	httpServer.on("/logout", HTTP_GET, handleLogout);

	httpServer.on("/temperatureHumidity", HTTP_GET, handleTemperatureHumidity);
}

void serverRouting() {
	restEndPoint();

	// Manage Web Server
	Serial.println(F("Go on not found!"));
	httpServer.onNotFound([]() {               // If the client requests any URI
				Serial.println(F("On not found"));
				if (!handleFileRead(httpServer.uri())) { // send it if it exists
					handleNotFound();// otherwise, respond with a 404 (Not Found) error
				}
			});

	Serial.println(F("Set cache!"));
	// Serve a file with no cache so every tile It's downloaded
	httpServer.serveStatic("/configuration.json", SPIFFS, "/configuration.json", "no-cache, no-store, must-revalidate");
	// Server all other page with long cache so browser chaching they
	httpServer.serveStatic("/", SPIFFS, "/", "max-age=31536000");

	//here the list of headers to be recorded
	const char * headerkeys[] = { "User-Agent", "Cookie" };
	size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*);
	//ask server to track these headers
	httpServer.collectHeaders(headerkeys, headerkeyssize);
}

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);
			webSocket.sendTXT(num, "{\"connected\":false}");
			connectionNumber = webSocket.connectedClients();

			Serial.print("Connected devices ");
			Serial.println(connectionNumber);
            break;
        case WStype_CONNECTED:
            {
                IPAddress ip = webSocket.remoteIP(num);
                Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);

				// send message to client
				webSocket.sendTXT(num, "{\"connected\":true}");
				connectionNumber = webSocket.connectedClients();

				Serial.print("Connected devices ");
				Serial.println(connectionNumber);
            }
            break;
        case WStype_TEXT:
        	Serial.printf("[%u] RECEIVE TXT: %s\n", num, payload);

            // send message to client
             webSocket.sendTXT(num, "(ECHO MESSAGE) "+String((char *)payload));

            // send data to all connected clients
            // webSocket.broadcastTXT("message here");
            break;
        case WStype_BIN:
        	Serial.printf("[%u] get binary length: %u\n", num, length);
            hexdump(payload, length);

            // send message to client
            // webSocket.sendBIN(num, payload, length);
            break;
    }
}

#ifdef ENABLE_WS_AUTHORIZATION
	bool isCookieValid(String rawCookieHeaderValue) {
		Serial.print("Cookie validation ");
		Serial.println(rawCookieHeaderValue);
		String clientIP = "";
		if (rawCookieHeaderValue.indexOf("CLIENTIP") != -1) {
				clientIP = rawCookieHeaderValue.substring(rawCookieHeaderValue.indexOf("CLIENTIP=") + 9, rawCookieHeaderValue.indexOf("|"));
				Serial.print("clientIP ");
				Serial.println(clientIP);
		}

		if (rawCookieHeaderValue.indexOf("ESPSESSIONID") != -1) {
			String tokenStr = rawCookieHeaderValue.substring(rawCookieHeaderValue.indexOf("ESPSESSIONID=") + 13, rawCookieHeaderValue.indexOf("ESPSESSIONID=") + 13+40);

			Serial.print("ESPSESSIONID ");
			Serial.println(tokenStr);

			String token = sha1(String(www_username) + ":" + String(www_password) + ":" + clientIP);
			Serial.print("token ");
			Serial.println(token);

			return tokenStr == token;
		}
		return false;
	}

	/*
	 * The WebSocketServerHttpHeaderValFunc delegate passed to webSocket.onValidateHttpHeader
	 */
	bool validateHttpHeader(String headerName, String headerValue) {

		//assume a true response for any headers not handled by this validator
		bool valid = true;

		if(headerName.equalsIgnoreCase("Cookie")) {
			//if the header passed is the Cookie header, validate it according to the rules in 'isCookieValid' function
			valid = isCookieValid(headerValue);
		}

		return valid;
	}
#endif

Thanks

  1. WebSocket on Arduino, esp8266 and esp32: client
  2. WebSocket on Arduino, esp8266 and esp32: server and authentication
  3. WebSocket on Arduino, esp8266 and esp32: temperature and humidity realtime update

Complete code on GitHub


Spread the love

Leave a Reply

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