راه اندازی وب سوکت سرور با ESP32

به آموزش راه اندازی وب سوکت سرور با ESP32 خوش اومدید.

تو این آموزش روش راه اندازی سرور با پروتکل وب سوکت رو یاد می گیرید. مثال این مورد ساختن یک صفحه وب هست که به صورت اتوماتیک تو خروجی ها (مرورگرها) رفرش میشه. بدون استفاده از وب سوکت وقتی یک صفحه وب رو همزمان تو مرورگرهای مختلفی باز کنید تا زمانی که صفحات رفرش نشدند اطلاعات اون صفحه آپدیت نمیشه.

وب سوکت چیه ؟

 وب سوکت یک ارتباط پایدار دو طرفه بین سرور و کاربر که با پروتکل TCP برقرار میشه هست.

کاربر درخواست یک ارتباط وب سوکت رو تو یک پروسه ای که بهش حرکت دست وب سوکت گفته میشه میفرسته . این درخواست از طریق ارتباط HTTP میده و سرور روی همون پورتی که درخواست ارسال شده ارتباط وب سوکت رو ایجاد می کنه. بعد از برقراری ارتباط دو طرفه وب سوکت بین سرور و کاربر برقرار میشه.

کاربرد پروتکل وب سوکت تو ESP32 تو برقراری ارتباط بین سرور که اینجا برد  ESP32 هست و کاربرهاست به طوری که ESP32 بدون درخواست کابرها براشون اطلاعات میفرسته . و یا مرورگر سرور اتوماتیک بروزرسانی بشه. وب سوکت با زدن یک کلید تو مرورگر یا کلید ریست تو برد ESP32 فعال میشه.

مثال

به عنوان مثال میخوایم این صفحه وب رو درست کنیم.

در صفحه وب پیاده سازی شده از طریق وب سرور ESP32 ، یک کلیدی داره و تغییر وضعیت پین GPIO2 (یا هر پین دیگه) رو که مربوط به یک LED روی ESP32 هست کنترل میشه. بعد از هر تغییر وضعیت اون پین تو این صفحه و تو هر مرورگر دیگه ای هم نمایش داده میشه.

خب بعد از زدن این کلید (کلید Toggle) چه اتفاقی میفته ؟ تو عکس زیر ببینیم.

  • کلید “Toggle” زده میشه.
  • کاربر (مرورگر شما) با پروتکل وب سوکت اطلاعات تغییر وضعیت رو ارسال می کنه .
  • سرور (ESP32) اطلاعات رو دریافت می کنه و میفهمه باید حالت LED رو تغییر بده. روشن/خاموش
  • از طریق پروتکل وب سوکت حالت جدید LED رو به همه کاربرها اطلاع میده.
  • کاربرها هم از تغییر وضعیت جدی LED مطلع میشن و در صفحات وب حالت LED بروزرسانی میشه. (همه تغییرات بعدی هم در صفحات مرورگرها بروزرسانی میشه )

آپلود کردن کد

اول لازمه کتابخونه های ESPAsyncWebServer و AsyncTCP مربوط به این پروتکل رو به نرم افزار ARDUINO IDE اضافه کنید . بعد کد زیر رو آپلود کنید.

/*********
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-websocket-server-arduino/
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*********/

// Import required libraries
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

bool ledState = 0;
const int ledPin = 2;

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <title>ESP Web Server</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,">
  <style>
  html {
    font-family: Arial, Helvetica, sans-serif;
    text-align: center;
  }
  h1 {
    font-size: 1.8rem;
    color: white;
  }
  h2{
    font-size: 1.5rem;
    font-weight: bold;
    color: #143642;
  }
  .topnav {
    overflow: hidden;
    background-color: #143642;
  }
  body {
    margin: 0;
  }
  .content {
    padding: 30px;
    max-width: 600px;
    margin: 0 auto;
  }
  .card {
    background-color: #F8F7F9;;
    box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
    padding-top:10px;
    padding-bottom:20px;
  }
  .button {
    padding: 15px 50px;
    font-size: 24px;
    text-align: center;
    outline: none;
    color: #fff;
    background-color: #0f8b8d;
    border: none;
    border-radius: 5px;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    -webkit-tap-highlight-color: rgba(0,0,0,0);
   }
   /*.button:hover {background-color: #0f8b8d}*/
   .button:active {
     background-color: #0f8b8d;
     box-shadow: 2 2px #CDCDCD;
     transform: translateY(2px);
   }
   .state {
     font-size: 1.5rem;
     color:#8c8c8c;
     font-weight: bold;
   }
  </style>
<title>ESP Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
</head>
<body>
  <div class="topnav">
    <h1>ESP WebSocket Server</h1>
  </div>
  <div class="content">
    <div class="card">
      <h2>Output - GPIO 2</h2>
      <p class="state">state: <span id="state">%STATE%</span></p>
      <p><button id="button" class="button">Toggle</button></p>
    </div>
  </div>
<script>
  var gateway = `ws://${window.location.hostname}/ws`;
  var websocket;
  window.addEventListener('load', onLoad);
  function initWebSocket() {
    console.log('Trying to open a WebSocket connection...');
    websocket = new WebSocket(gateway);
    websocket.onopen    = onOpen;
    websocket.onclose   = onClose;
    websocket.onmessage = onMessage; // <-- add this line
  }
  function onOpen(event) {
    console.log('Connection opened');
  }
  function onClose(event) {
    console.log('Connection closed');
    setTimeout(initWebSocket, 2000);
  }
  function onMessage(event) {
    var state;
    if (event.data == "1"){
      state = "ON";
    }
    else{
      state = "OFF";
    }
    document.getElementById('state').innerHTML = state;
  }
  function onLoad(event) {
    initWebSocket();
    initButton();
  }
  function initButton() {
    document.getElementById('button').addEventListener('click', toggle);
  }
  function toggle(){
    websocket.send('toggle');
  }
</script>
</body>
</html>
)rawliteral";

void notifyClients() {
  ws.textAll(String(ledState));
}

void handleWebSocketMessage(void *arg, uint8_t *data, size_t len) {
  AwsFrameInfo *info = (AwsFrameInfo*)arg;
  if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
    data[len] = 0;
    if (strcmp((char*)data, "toggle") == 0) {
      ledState = !ledState;
      notifyClients();
    }
  }
}

void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type,
             void *arg, uint8_t *data, size_t len) {
  switch (type) {
    case WS_EVT_CONNECT:
      Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
      break;
    case WS_EVT_DISCONNECT:
      Serial.printf("WebSocket client #%u disconnected\n", client->id());
      break;
    case WS_EVT_DATA:
      handleWebSocketMessage(arg, data, len);
      break;
    case WS_EVT_PONG:
    case WS_EVT_ERROR:
      break;
  }
}

void initWebSocket() {
  ws.onEvent(onEvent);
  server.addHandler(&ws);
}

String processor(const String& var){
  Serial.println(var);
  if(var == "STATE"){
    if (ledState){
      return "ON";
    }
    else{
      return "OFF";
    }
  }
}

void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);

  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  
  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  // Print ESP Local IP Address
  Serial.println(WiFi.localIP());

  initWebSocket();

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, processor);
  });

  // Start server
  server.begin();
}

void loop() {
  ws.cleanupClients();
  digitalWrite(ledPin, ledState);
}

توضیحات مربوط به کد

بعد از خطوط اول که معرفی کتابخونه ها و تنظیمات شبکه نوشته شد، در خطوط ۱۷ و ۱۸ ledState برای نگهداری وضعیت LED و ledPin مربوط به پین GPIO تعریف شده.

AsyncWebServer در پورت ۸۰ برای کنترل وب سوکت مقداردهی و /ws برای کنترل وب سوکت در مسیر /ws تعریف میشه.

از خطوط ۲۴ تا ۱۴۷ با متغیر index_htmlاطلاعات HTML ، CSS و JavaScript برای تنظیمات مربوط به شکل صفحه وب و ارتباطات سرور و کاربر با پروتکل وب سوکت نگهداری میشه.

تنظیمات مربوط به CSS در خطوط ۳۰ تا ۹۰ بین <style> و </style> ، تنظیمات HTML در خطوط ۹۵ تا ۱۴۵ بین <body> و</body> و تنظیمات مربوط به JavaScript در خطوط ۱۰۶ تا ۱۴۴بین <script> و </script> قرار داره.

در بخش HTML در آدرس حافظه %STATE% وضعیت GPIO ذخیره میشه. (خط ۱۰۲)

در خط ۱۰۳ کد مربوط به کلید Toggle در صفحه وب قرار گرفته.

در بخش JavaScript در خط ۱۰۷ ، window.location.hostname آدرس آی پی وب سرور رو دریافت می کنه.

دستور ()notifyClients مربوط به اطلاع رسانی همه کاربرها توسط سرور در زمان ایجاد تغییر وضعیت پین هست. (خطوط ۱۴۹ تا ۱۵۱)

دستور ()handleWebSocketMessage زمانی که اطلاعات جدیدی از کاربرها با پروتکل وی سوکت دریافت میشه اجرا میشه. (خطوط ۱۵۳ تا ۱۶۲)

مراحل آسنکرون پروتکل وب سوکت با دستور ()onEvent پیکربندی میشه.(خطوط ۱۶۴ تا ۱۸۰) خب تو این قسمت ورودی عبارت type هست و اتفاقهای پروسه وب سوکت رو بیان میکنه.

  • WS_EVT_CONNECT  : زمانی که کاربری وارد شبکه میشه
  • WS_EVT_DISCONNECT  : زمانی که کاربر از شبکه خارج میشه
  • WS_EVT_DATA  : اطلاعات از کاربر دریافت میشه
  • WS_EVT_PONG  : در جواب یک درخواست دریافت شده
  • WS_EVT_ERROR  : زمانی که از یک کاربر پیام ارور دریافن میشه

با دستور ()initWebSocket پروتکل وب سوکت پیکربندی میشه.(در خطوط ۱۸۲ تا ۱۸۴)

دستور ()processor مربوط به آدرس دهیHTML  تو حافظه است.(خطوط ۱۸۷ تا ۱۹۴)

در دستور ()setup پیکربندی مانیتور و GPIO ها ، WIFI و آدرس آی پی ESP32 انجام میشه. (خطوط ۲۱۴ تا ۱۹۹)

اطلاعات ذخیره شده تو متغیر index_html ، اگر نیاز باشه دستور ()processor اجرا بشه تنظیمات  آدرس GPIO ها تو حافظه تو خطوط ۲۱۹ تا ۲۲۴ نوشته میشه.

در دستور ()loop با وجود تمام کدهای دستوری مربوط به اتمام ارتباط وب سوکت اما گاهی اوقات مرورگرها به صورت درستی این ارتباط رو قطع نمیکنن. این باعث اختلال تو عملکرد سرور و مصرف توان بیشتری میشه. با فراخوانی دستور ()cleanupClients اگر تعداد کاربرهایی که به سرور متصل هستن از یک مقدار مشخصی بیشتر شده باشه، اتصال کابرهای قدیمی تر از سرور قطع میشه.(خطوط ۲۲۷ تا ۲۲۹)

پیاده سازی

بعد از آپلود کردن کد روی برد و زدن کلید EN//RST روی برد آدرس آی پی ESP32 روی صفحه مانیتور نمایش داده میشه. این آی پی رو تو مرورگر وارد کنید. صفحه مرورگر به شکل زیر شده و با زدن کلید Toggle ، LED رو میشه روشن و خاموش کرد.

پایان آموزش راه اندازی وب سوکت سرور با ESP32.

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

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

قبلا حساب کاربری ایجاد کرده اید؟
گذرواژه خود را فراموش کرده اید؟
Loading...