WebSocket تکنولوژی است که ارتباط TCP را باز نگه داشته. و باعث می شود که برای هر پیامی لازم نباشد یک ارتباط جدید TCP ایجاد کنیم.تا الان ما برای گرفتن و ارسال داده از، یا به ESP8266 خود، مجبور بودیم یک لینک را در آدرس بار مرورگر وارد کنیم. یا که این از تگ <form> با متد POST استفاده کنیم. ولی هربار که اطلاعاتی جدید بخواهد نمایش داده شود، صفحه refresh می شود.
برای جلوگیری از refresh صفحه راه های زیادی وجود دارد. استفاده از AJAX و XMLHTTP request یکی از این راه ها میباشد. ولی برای ارسال هر دادهای باید یک ارتباط جدید TCP ایجاد شود.
WebSocket
WebSocket یک پروتکل برای انتقال داده بین مرورگر و سرور است. داده بین سرور و مرورگر بصورت بسته هایی جابجا میشود، بدون اینکه ارتباط قطع شود. در JavaScript وقتی یک Socket ایجاد میکنیم، به چهار Event باید گوش بدیم.
- open وقتی ارتباط ایجاد می شود.
- message وقتی پیغامی میرسد.
- error وقتی خطایی رخ بدهد.
- close وقتی ارتباط قطع میشود.
وقتی هم که بخواهیم چیزی ارسال کنیم، آنرا با socket.send(data) ارسال می کنیم.
وقتی یک WebSocket ایجاد میشود. مرورگر از سرور میپرسد که “آیا WebSocket را پشتیبانی میکنی؟” و سرور جواب میدهد “بله” . و بعد از این ارتباط با پروتکلی که، لزومی ندارد حتما HTTP باشه، ادامه پیدا میکند.
دانلود WebSocket برای آردوینو
برای WebSocket کتابخانههای زیادی وجود دارد. من کتابخانهی WebSocket را از این لینک در GitHub دانلود کردم. می توانید از طریق Sketch > Include Library > Add .ZIP Library آن را به نرم افزار آردوینو اضافه کنید.
نمونهی برنامه برای WebSocket
برنامه را بصورت بخش بخش توضیح میدهم. قسمتهایی از برنامه را هم که در پستهای قبلی بررسی کردیم. پس تکرار نمی کنیم.
#include <ESP8266WiFi.h> #include <ESP8266WebServer.h> #include <FS.h> #include <WebSocketsServer.h> ESP8266WebServer server(80); // Create an instance of the ESP8266WiFiMulti class, called 'wifiMulti' WebSocketsServer webSocket(81); // create a websocket server on port 81 const char* ssid = "SSID"; const char* password = "PASSWORRD"; #define PinLED 2 unsigned int rythm[50] = {1000, 2000}; bool Condition = false;
در اولین بخش که کتابخانهها را به برنامه اضافه کردهام. آبجکتها و متغیرهای گلوبالی را هم که قرار بوده داشته باشیم در اینجا ایجاد کردهام. با بیشتر کدهای این برنامه قبلا آشنا شدهایم و فقط قسمت WebSocket جدید میباشد.
////////////////////////////////////////////////////////////////////////SPIFFS Function String getContentType(String filename) { // convert the file extension to the MIME type if (filename.endsWith(".html")) return "text/html"; else if (filename.endsWith(".css")) return "text/css"; else if (filename.endsWith(".js")) return "application/javascript"; else if (filename.endsWith(".ico")) return "image/x-icon"; return "text/plain"; } bool handleFileRead(String path) { // send the right file to the client (if it exists) Serial.println("handleFileRead: " + path); if (path.endsWith("/")) path += "index.html"; // If a folder is requested, send the index file String contentType = getContentType(path); // Get the MIME type if (SPIFFS.exists(path)) { // If the file exists File file = SPIFFS.open(path, "r"); // Open it size_t sent = server.streamFile(file, contentType); // And send it to the client file.close(); // Then close the file again return true; } Serial.println("\tFile Not Found"); return false; // If the file doesn't exist, return false } void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght) { // When a WebSocket message is received switch (type) { case WStype_DISCONNECTED: // if the websocket is disconnected Serial.printf("[%u] Disconnected!\n", num); break; case WStype_CONNECTED: { // if a new websocket connection is established Serial.printf("WebSocket Connected"); } break; case WStype_TEXT: // if new text data is received byte rythmIndex = 0; String stringVal; byte i = 0; Serial.print("I received: "); Serial.println((char*)payload); if ((char)payload[0] == '[') { while ((char)payload[i] != ']' && (char)payload[i] != '\0') { i++; while (((char)payload[i] != ',') && ((char)payload[i] != ']') && (char)payload[i] != '\0' ) { stringVal += (char)payload[i]; i++; delay(1); } rythm[rythmIndex] = stringVal.toInt(); stringVal = ""; Serial.println(rythm[rythmIndex]); rythmIndex++; } } else { rythm[0] = 1000; rythm[1] = 1000; rythmIndex = 2; } for (; rythmIndex < 50; rythmIndex++) { rythm[rythmIndex] = 0; } webSocket.broadcastTXT(payload); break; } }
در این بخش سه فانکشن تعریف کردهایم. دو فانکشن اولی را قبلا کار کردهایم. برای مطالعهی بیشتر به این پست مراجعه کنید.
در قسمت WebSocketEvent پیامی که از طریق WebSocket دریافت می شود، مدیریت میشود.
void startSPIFFS() { // Start the SPIFFS and list all contents SPIFFS.begin(); // Start the SPI Flash File System (SPIFFS) Serial.println("SPIFFS started. Contents:"); } void startWebSocket() { // Start a WebSocket server webSocket.begin(); // start the websocket server webSocket.onEvent(webSocketEvent); // if there's an incomming websocket message, go to function 'webSocketEvent' Serial.println("WebSocket server started."); } void startServer() { // Start a HTTP server with a file read handler and an upload handler server.onNotFound([]() { // If the client requests any URI if (!handleFileRead(server.uri())) // send it if it exists server.send(404, "text/plain", "404: Not Found"); // otherwise, respond with a 404 (Not Found) error }); server.begin(); // start the HTTP server Serial.println("HTTP server started."); } void setupWifi(void) { //Constant IP for Local WiFi IPAddress ipWiFi(192, 168, 1, 22); IPAddress gatewayWiFi(192, 168, 1, 249); IPAddress subnetWiFi(255, 255, 255, 0); WiFi.config(ipWiFi, gatewayWiFi, subnetWiFi); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(F("Local WiFi Connected")); }
در این قسمت برای SetUp کردن قسمتهای مختلف برنامه فانکشنهایی ایجاد کردهایم. برای SetUp وایفای به این پست مراجعه کنید.
void setup() { Serial.begin(115200); // Start the Serial communication to send messages to the computer delay(10); Serial.println('\n'); setupWifi(); startSPIFFS(); startWebSocket(); startServer(); pinMode(PinLED, OUTPUT); digitalWrite(PinLED, LOW); } unsigned long previousTime = millis(); byte k = 0; void loop() { webSocket.loop(); // constantly check for websocket events server.handleClient(); // run the server unsigned long diff; if ((millis() - previousTime) > 0 ) { diff = millis() - previousTime; } else { diff = 0; } if (diff > rythm[k]) { if ((k % 2) != 0) { Condition = true; } else { Condition = false; } digitalWrite(PinLED, Condition); // Change the state of the LED previousTime += diff; k++; // Serial.println(sizeof(rythm) / sizeof(rythm[0])); if (k > (sizeof(rythm) / sizeof(rythm[0]) - 1)) { k = 0; } } }
این برنامه قرار است یک چراغ چشمک زن باشد، که با مرورگر بتوانیم سرعت آنرا کنترل کنیم.
قسمت متفاوت این برنامه با قبلیها این است که، برای استفاده از WebSocket باید بخش مرورگر آن را هم داشته باشیم. بخش کلاینت آن را باید با زبان جاوااسکریپت بنویسیم.
پس اول کد HTML آن را قرار می دهیم.
<!DOCTYPE HTML> <html> <head> <meta name="viewport" content="width=device-width,height=device-height,initial-scale=1.0"/> <title>USP & Flasher</title> <link rel="stylesheet" type="text/css" href="style.css" /> <script src="https://code.jquery.com/jquery-3.4.1.js"></script> </head> <body> <div> <form method="POST"> <h1 class="slide" id="slide">Choose Time For ON/OFF LED</h1> <div id="led"> <h1>Time ON</h1> <div class="container"> <div class="range-slider"> <input id="ledtimeon" class="rs-range" type="range" value="0" min="0" max="5000"> </div> <div class="box-minmax"> <span>0</span><span id="numon">0</span><span>5000</span> </div> </div> <h1>Time OFF</h1> <div class="container"> <div class="range-slider"> <input id="ledtimeoff" class="rs-range" type="range" value="0" min="0" max="5000"> </div> <div class="box-minmax"> <span>0</span><span id="numoff">0</span><span>5000</span> </div> </div> </div> </form> </div> </body> <script type="text/javascript" src="script.js"></script> </html>
بخش CSS آن:
body { text-align: center; background : #bababa; width: 100%; max-width: 550px; margin: 10px auto; } @media(min-width: 900px){ body{ width: 100%; } } .slide{ max-width: 500px; margin: 10px auto; background: #46403c; padding: 10px; border-radius: 5px; cursor: pointer; } #led{ display: none; background: #b7b7b7; max-width: 500px; margin: 10px auto; padding: 10px; } #led h1{ background: #8c8c8d; padding: 10px; } .container { display: flex; flex-direction: column; align-items: center; justify-content: center; } .box-minmax { margin-top: 30px; width: 100%; display: flex; justify-content: space-between; font-size: 20px; color: #7c7c7c; } .box-minmax span:first-child { margin-left: 10px; } .range-slider { width: 100%; } .rs-range { margin-top: 35px; width: 100%; -webkit-appearance: none; } .rs-range:focus { outline: none; } .rs-range::-webkit-slider-runnable-track { width: 100%; height: 1px; cursor: pointer; box-shadow: none; background: #ffffff; border-radius: 0px; border: 0px solid #010101; } .rs-range::-moz-range-track { width: 100%; height: 1px; cursor: pointer; box-shadow: none; background: #ffffff; border-radius: 0px; border: 0px solid #010101; } .rs-range::-webkit-slider-thumb { box-shadow: none; border: 0px solid #ffffff; box-shadow: 0px 10px 10px rgba(0, 0, 0, 0.25); height: 42px; width: 22px; border-radius: 22px; background: white; cursor: pointer; -webkit-appearance: none; margin-top: -20px; } .rs-range::-moz-range-thumb { box-shadow: none; border: 0px solid #ffffff; box-shadow: 0px 10px 10px rgba(0, 0, 0, 0.25); height: 42px; width: 22px; border-radius: 22px; background: white; cursor: pointer; -webkit-appearance: none; margin-top: -20px; } .rs-range::-moz-focus-outer { border: 0; } #num{ color: #434343; }
قسمت جاوااسکریپت:
var ledTimeOn = document.getElementById("ledtimeon"); var ledTimeOnNum = document.getElementById("numon"); ledTimeOn.addEventListener("input", showSliderValueon, false); function showSliderValueon() { ledValue(); ledTimeOnNum.innerHTML = ledTimeOn.value; } var ledTimeOff = document.getElementById("ledtimeoff"); var ledTimeOffNum = document.getElementById("numoff"); ledTimeOff.addEventListener("input", showSliderValueOff, false); function showSliderValueOff() { ledValue(); ledTimeOffNum.innerHTML = ledTimeOff.value; } var connection = new WebSocket('ws://192.168.1.22:81/', ['arduino']); connection.onopen = function () { console.log('Connect ' + new Date()); }; connection.onerror = function (error) { console.log('WebSocket Error ', error); }; connection.onmessage = function (e) { console.log('Server: ', e.data); var res = e.data.replace("[", ""); res = res.replace("]", ""); var Arr = res.split(","); ledTimeOn.value = Arr[0]; ledTimeOff.value = Arr[1]; ledTimeOnNum.innerHTML = Arr[0]; ledTimeOffNum.innerHTML = Arr[1]; }; connection.onclose = function () { console.log('WebSocket connection closed'); }; function ledValue () { var onled = ledTimeOn.value; var offled = ledTimeOff.value; console.log('Time For ON LED: ' + onled + 'Time For ON LED: ' + offled); connection.send("[" + onled + "," +offled + "]"); }
برای ما صفحهای به شکل زیر ایجاد میشود.
برای آپلود فایلهای HTML و CSS و JavaScript بر روی ESP8266 خود به این پست مراجعه کنید.
دیدگاه ها :