ESP8266 - WebSocket

آموزش کار با ESP8266 – WebSocket

WebSocket تکنولوژی است که ارتباط TCP را باز نگه داشته. و باعث می شود که برای هر پیامی لازم نباشد یک ارتباط جدید TCP ایجاد کنیم.تا الان ما برای گرفتن و ارسال داده از، یا به ESP8266 خود، مجبور بودیم یک لینک را در آدرس بار مرورگر وارد کنیم. یا که این از تگ <form> با متد POST استفاده کنیم. ولی هربار که اطلاعاتی جدید بخواهد نمایش داده شود، صفحه refresh می شود.

برای جلوگیری از refresh صفحه راه های زیادی وجود دارد. استفاده از AJAX و XMLHTTP request یکی از این راه ها می‌باشد. ولی برای ارسال هر داده‌ای باید یک ارتباط جدید TCP ایجاد شود.

WebSocket

WebSocket یک پروتکل برای انتقال داده بین مرورگر و سرور است. داده بین سرور و مرورگر بصورت بسته هایی جابجا می‌شود، بدون اینکه ارتباط قطع شود. در JavaScript وقتی یک Socket ایجاد می‌کنیم، به چهار Event باید گوش بدیم.

  1.  open وقتی ارتباط ایجاد می شود.
  2. message وقتی پیغامی می‌رسد.
  3. error وقتی خطایی رخ بدهد.
  4. 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 + "]");
}

برای ما صفحه‌ای به شکل زیر ایجاد می‌شود.

WebSocket Flasher

WebSocket Flasher

برای آپلود فایلهای HTML و CSS و JavaScript بر روی ESP8266 خود به این پست مراجعه کنید.

دیدگاه ها :

من بات نیستم

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *