Site icon Renzo Mischianti

WebSocket su Arduino, esp8266 e esp32: aggiornamento temperatura e umidità realtime – 3

WebSocket Arduino esp8266 esp32 applicazione web completa con autenticazione basata su token

WebSocket Arduino esp8266 esp32 applicazione web completa con autenticazione basata su token

Spread the love

WebSocket Arduino esp8266 esp32 complete web application with token auth

Ricordi il progetto creato nella guida “How to create a web server with esp8266 and esp32”? E’ arrivato il momento di porlo nell’era moderna, quindi rimuoveremo il polling e aggiungeremo il push per la modifica dei dati in tempo reale.

Questa modalità è la strada migliore, le normali applicazione recuperano i dati iniziali dalle chiamate REST e poi vengono aggiornati tramite WebSocket.

Per prima cosa aggiungeremo il server WebSocket.

#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();

Quindi nel ciclo andremo ad aggiungere un messaggio di broadcast (per tutti i client) ogni 2 secondi che aggiorna la temperatura e l’umidità

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();
}

Il codice è abbastanza semplice, ma presta attenzione alla condizione connectionNumber>0 la quale impedisce l’invio messaggi di WebSocket se non ci sono client connessi.

È una semplice variabile, che aggiorno ad ogni connessione/disconnessione, con il numero di client connessi.

Quindi tutta la gestione si può dire completata

esp8266 WebServer humidity gauge

Qui nel webSocketEvent puoi vedere la gestione del numero di client connessi.

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;
    }
}

Quindi dobbiamo aggiungere il listener degli eventi al front-end per gestire il messaggio

        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();

Il codice è molto semplice e funziona correttamente.

Autenticazione

WebServer esp8266 login page

Semplice work workaround per l’autenticazione

Questa non è una soluzione così bella, ma è abbastanza semplice da implementare senza l’uso di conoscenze extra (come header, cookie ecc.), Ed è abbastanza potente.

Puoi aggiungere un token come authorization, in questo modo il token viene passato come per il 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());

Poi aggiungo un nuovo token in fase di autenticazione (senza clientIP)

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

ed inserisco il token nell’URI di ws, come descritto nel precedente articolo.

Qui recupero il 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();
        }

e verrà passato nell’uri in questa maniera

 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);
            }

Controlla il codice completo su GitHub

Autenticazione basata su token

L’ultima volta abbiamo eseguito un’autenticazione basata su token, ora possiamo recuperare quell’implementazione e integrare il WebSoket.

Come vedi nell’articolo “Web Server con esp8266 e esp32: gestione sicurezza ed autenticazione” aggiungo il token nel cookie ESPSESSIONID,

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;
	}
}


di ogni chiamata REST controllo se è presente.

//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;
}

Come puoi vedere utilizzo il client IP anche per generare un token specifico per il dispositivo, quindi per risolvere questo problema aggiungo l’IP del client al 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());

Quindi dobbiamo aggiungere il validatore personalizzato al WebSoket

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

L’onValidateHttpHeader richiede 3 parametri:

La funzione bool validateHttpHeader(String headerName, String headerValue) prevede 2 parametri

Ora abbiamo un’header specifico per convalidare il cookie , perciò anche l’header obbligatorio è il cookie,

/*
 * 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;
}

e quando troviamo questa intestazione dobbiamo controllarne il valore

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;
}

Al front-end non servono altre modifiche, per sfruttare il cookie esistente.

Ed ecco il codice del server completo

/*
 *  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/it/websocket-su-arduino-esp8266-e-esp32-aggiornamento-temperatura-e-umidita-realtime-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

Anche la gestione della pagina html varia di un poco.

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" type="text/css" href="./sldierElement.css">
    <link rel="stylesheet" type="text/css" href="./floatButton.css">
    <script src="iot-widget.min.js"></script>
    <script src="sliderManager.js"></script>

    <meta charset="UTF-8">
    <title>Temperature humidity www.mischianti.org</title>

    <style>
        html {
            height: 100%;
        }
        body {
            height: 100%;

            background: #76b852; /* fallback for old browsers */
            background: -webkit-linear-gradient(right, #76b852, #8DC26F);
            background: -moz-linear-gradient(right, #76b852, #8DC26F);
            background: -o-linear-gradient(right, #76b852, #8DC26F);
            background: linear-gradient(to left, #76b852, #8DC26F);
        }

        .titleContainer {
            font-size:  6vw;;
            text-align: center;
            width: 100%;
            height: 16%;
            /*padding: 10px;*/
        }
        .sliderContainer {
            height: 84%;
            position: relative;
        }

        .humidity-container,
        .termometer-container{
            width: 768px;
            height: 100%;
        }
        .humidity-container{
            height: 70%;
        }
    </style>
</head>
<body>
<div class="titleContainer">Temperature humidity</div>
<div class="sliderContainer">
    <div class="slider-wrap">
        <div class="slider" id="slider">
            <div class="holder">
                <div class="slide-wrapper">

                    <div class="humidity-container" id="humidity-container"></div>
                </div>
                <div class="slide-wrapper">
                    <div class="termometer-container" id="termomter-container" ></div>
                </div>
            </div>
        </div>
    </div>
</div>
<script>
    var ENABLE_WS_AUTHORIZATION = true;

    var sliderElement = document.getElementById('slider');
    var sliderImageElements = document.querySelectorAll(".slide-wrapper");
    var holderElement = document.querySelector(".holder");
    var animateElements = document.querySelectorAll('.animate');

    sliderInitialization(  sliderElement,
        sliderImageElements,
        holderElement,
        animateElements,
        2);

    var container = document.getElementById('humidity-container');
    var hw = new Widget.Humidity(container, 50, 'auto', 80);
    hw.draw();

    var container2 = document.getElementById('termomter-container');
    var tw = new Widget.Termometer(container2, 0);
    tw.draw();

</script>

    <script>


        // debugger
        function debounce(fn, threshold) {
            threshold = threshold || 300;
            var timer;
            return function() {
                if (!timer) {
                    fn.apply(this, arguments);
                }
                clearTimeout(timer);
                timer = setTimeout(function() {
                    timer = null;
                }, threshold);
            };
        };

        var requestTempHum = function(){
            var xhttp = new XMLHttpRequest();
            xhttp.onreadystatechange = function() {
                if (this.readyState == 4 && this.status == 200) {
                    // Typical action to be performed when the document is ready:
                    var res = JSON.parse(xhttp.responseText);
                    hw.setHumidity(res.humidity);
                    tw.setTemperature(res.temp);
                }
            };
            xhttp.onerror = function () {
                alert("Status code is " + this.status + " click F12 and check what is the problem on console");
            };

            xhttp.open("GET", "/temperatureHumidity", true);
            xhttp.send();
        }

        var debouncedRequestTempHum = debounce(requestTempHum, 400);

        var refresh = function(){
            debouncedRequestTempHum();
        }
        debouncedRequestTempHum();

        function onOpen(evt) {
            console.log("CONNECTED");
            // doSend("Hi, I'm simple JS client!!");
        }

        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);
            console.error(evt);
        }

        // function doSend(message) {
        //     writeToScreen("SENT: " + message);
        //     websocket.send(message);
        // }

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

            if (authToken){
                var newUri = uri.substring(0,uri.indexOf("//")+2)+authToken+'@'+uri.substring(uri.indexOf("//")+2);
                console.log(newUri);
                try {
                    websocket = new WebSocket(newUri, 'arduino');
                }catch (e) {
                    console.error(e);
                    alert(e);
                }
            }else {
                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)
            };
        }
        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('ESPSESSIONID'); // returns "turkey"

            if (cookieVal && cookieVal!=="0") {
                initWebSocket(cookieVal);
            }else{
                // alert("No cookie found!");
                window.location.href = '/login.html?msg=User not logged in!';
            }
        }else{
            initWebSocket();
        }
        // function polling() {
        //     setTimeout(function () {
        //         debouncedRequestTempHum();
        //         polling();
        //     }, 2500);
        // }
        // polling();
    </script>
    <div style="position: absolute">
        <a href="#" onclick="refresh();" class="floatRefresh">
            <div style="font-size: xx-large">R</div>
        </a>
    </div>
    <div style="position: absolute">
        <a href="/logout" class="floatLogout">
            <div style="font-size: xx-large">L</div>
        </a>
    </div>

</body>
</html>

Grazie

  1. WebSocket su Arduino, esp8266 ed esp32: client
  2. WebSocket su Arduino, esp8266 ed esp32: server e autenticazione
  3. WebSocket su Arduino, esp8266 ed esp32: aggiornamento temperatura e umidità realtime

Codice completo su GitHub


Spread the love
Exit mobile version