blog cover

برمجة النود ام سي يوم كويب سيرفر في برنامج الاردوينو

في السنوات الأخيرة، برزت شريحة ESP8266 وتصدّرت مشهد مشاريع إنترنت الأشياء (IoT) والمشاريع المرتبطة بالواي فاي. فهي وحدة واي فاي منخفضة التكلفة يمكن برمجتها — بقليلٍ من الجهد الإضافي — لتشغيل خادم ويب مستقلّ. يا له من أمرٍ رائع!

ما هو خادم الويب بالضبط، وكيف يعمل؟

خادم الويب هو المكان الذي تُخزَّن فيه صفحات الويب وتُعالَج وتُقدَّم إلى عملاء الويب (web clients). وعميل الويب ليس سوى متصفّح الويب الذي نستخدمه على حواسيبنا وهواتفنا. ويتواصل عميل الويب وخادم الويب باستخدام بروتوكولٍ خاصّ يُعرَف ببروتوكول نقل النصّ الفائق (Hypertext Transfer Protocol - HTTP).

HTTP Web Server Client Illustration

في هذا البروتوكول، يبدأ العميل المحادثة بإرسال طلب HTTP لصفحة ويب محدّدة. ثم يردّ الخادم بإرسال محتوى تلك الصفحة، أو برسالة خطأ إذا لم يتمكّن من العثور عليها (مثل خطأ 404 الشهير).

أوضاع تشغيل ESP8266

من أكثر ميزات ESP8266 فائدةً قدرتها ليس فقط على الاتصال بشبكة واي فاي قائمة والعمل كخادم ويب، بل أيضاً على إنشاء شبكتها الخاصة، ما يتيح للأجهزة الأخرى الاتصال بها مباشرةً والوصول إلى صفحات الويب.

وهذا ممكنٌ لأن شريحة ESP8266 يمكنها العمل في ثلاثة أوضاع: وضع المحطة (Station - STA)، ووضع نقطة الوصول البرمجية (Soft Access Point - AP)، وكلا الوضعَين في آنٍ واحد.

وضع المحطة (STA)

في وضع المحطة (STA)، تتّصل شريحة ESP8266 بشبكة واي فاي قائمة (وهي الشبكة التي يُنشئها موجّه الشبكة اللاسلكي - الراوتر).

ESP8266 NodeMCU Web Server Station STA Mode Demonstration

في وضع المحطة (STA)، تحصل شريحة ESP8266 على عنوان IP من موجّه الشبكة اللاسلكي (الراوتر) المتّصلة به. وبهذا العنوان، يمكنها إعداد خادم ويب وتقديم صفحات الويب إلى جميع الأجهزة المتّصلة على شبكة الواي فاي القائمة.

وضع نقطة الوصول البرمجية (AP)

في وضع نقطة الوصول (AP)، تُنشئ شريحة ESP8266 شبكة الواي فاي الخاصة بها وتعمل كمحور (تماماً مثل موجّه الواي فاي) لمحطةٍ واحدة أو أكثر.

لكنها، بخلاف موجّه الواي فاي، لا تملك واجهةً للاتصال بشبكةٍ سلكية. لذلك يُسمّى وضع التشغيل هذا نقطة الوصول البرمجية (soft-AP). كما لا يمكن أن يتّصل بها أكثر من خمس محطاتٍ في الوقت نفسه.

ESP8266 NodeMCU Web Server Soft Access Point AP Mode Demonstration

في وضع نقطة الوصول (AP)، تُنشئ شريحة ESP8266 شبكة واي فاي جديدة وتُسنِد إليها معرّف SSID (اسم الشبكة) وعنوان IP. وبهذا العنوان، يمكنها تقديم صفحات الويب إلى جميع الأجهزة المتّصلة.

توصيل المصابيح (LEDs) بشريحة ESP8266

بعد أن فهمنا أساسيات عمل خادم الويب والأوضاع التي يمكن لِـ ESP8266 أن يُنشئ فيها خادماً، حان وقت توصيل بعض المصابيح (LEDs) بشريحة ESP8266 التي نريد التحكّم فيها عبر الواي فاي.

ابدأ بوضع لوحة NodeMCU على لوحة التجارب (breadboard)، مع الحرص على أن يكون كلّ جانبٍ من اللوحة على جانبٍ مختلفٍ من لوحة التجارب. بعد ذلك، وصّل مصباحَين (LEDs) بطرفَي GPIO الرقميَّين D6 وD7 باستخدام مقاومةٍ للحدّ من التيار بقيمة 220 أوم.

وعندما تنتهي، ينبغي أن يكون لديك ما يشبه الصورة أدناه.

Simple ESP8266 NodeMCU Web Server Wiring Fritzing Connections with LED

Wiring LEDs to ESP8266 NodeMCU

الفكرة الكامنة وراء استخدام خادم ويب ESP8266 للتحكّم في الأشياء

لعلّك تتساءل: "كيف أتحكّم في الأشياء من خلال خادم ويب لا يفعل سوى معالجة صفحات الويب وتقديمها؟".

الأمر بسيطٌ للغاية. سنتحكّم في الأشياء عبر زيارة عنوان URL محدّد.

عندما تُدخِل عنوان URL في متصفّح ويب، فإنه يرسل طلب HTTP (يُعرَف أيضاً بطلب GET) إلى خادم ويب. ويقع على عاتق خادم الويب مسؤولية التعامل مع هذا الطلب.

لنفترض أنك أدخلت عنواناً مثل http://192.168.1.1/ledon في المتصفّح. عندئذٍ يرسل المتصفّح طلب HTTP إلى شريحة ESP8266. وعندما تستقبل ESP8266 هذا الطلب، فإنها تدرك أن المستخدم يرغب في تشغيل المصباح (LED). ونتيجةً لذلك، تُشغّل المصباح وترسل صفحة ويب ديناميكية إلى المتصفّح تعرض حالة المصباح على أنها "مُشغَّل" (on). بسيطٌ جداً، أليس كذلك؟

إعداد خادم ويب ESP8266 في وضع نقطة الوصول (AP)

لننتقل الآن إلى الأمور المشوّقة!

يوضّح هذا المثال، كما يشير عنوانه، كيفية إعداد خادم ويب ESP8266 في وضع نقطة الوصول (AP) وتقديم صفحات الويب إلى أي عميلٍ متّصل. للبدء، وصّل شريحة ESP8266 بحاسوبك وشغّل الكود. ثم سنتناوله بمزيدٍ من التفصيل.

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

/* Put your SSID & Password */
const char* ssid = "NodeMCU";  // Enter SSID here
const char* password = "12345678";  //Enter Password here

/* Put IP Address details */
IPAddress local_ip(192,168,1,1);
IPAddress gateway(192,168,1,1);
IPAddress subnet(255,255,255,0);

ESP8266WebServer server(80);

uint8_t LED1pin = D7;
bool LED1status = LOW;

uint8_t LED2pin = D6;
bool LED2status = LOW;

void setup() {
  Serial.begin(115200);
  pinMode(LED1pin, OUTPUT);
  pinMode(LED2pin, OUTPUT);

  WiFi.softAP(ssid, password);
  WiFi.softAPConfig(local_ip, gateway, subnet);
  delay(100);
  
  server.on("/", handle_OnConnect);
  server.on("/led1on", handle_led1on);
  server.on("/led1off", handle_led1off);
  server.on("/led2on", handle_led2on);
  server.on("/led2off", handle_led2off);
  server.onNotFound(handle_NotFound);
  
  server.begin();
  Serial.println("HTTP server started");
}
void loop() {
  server.handleClient();
  if(LED1status)
  {digitalWrite(LED1pin, HIGH);}
  else
  {digitalWrite(LED1pin, LOW);}
  
  if(LED2status)
  {digitalWrite(LED2pin, HIGH);}
  else
  {digitalWrite(LED2pin, LOW);}
}

void handle_OnConnect() {
  LED1status = LOW;
  LED2status = LOW;
  Serial.println("GPIO7 Status: OFF | GPIO6 Status: OFF");
  server.send(200, "text/html", SendHTML(LED1status,LED2status)); 
}

void handle_led1on() {
  LED1status = HIGH;
  Serial.println("GPIO7 Status: ON");
  server.send(200, "text/html", SendHTML(true,LED2status)); 
}

void handle_led1off() {
  LED1status = LOW;
  Serial.println("GPIO7 Status: OFF");
  server.send(200, "text/html", SendHTML(false,LED2status)); 
}

void handle_led2on() {
  LED2status = HIGH;
  Serial.println("GPIO6 Status: ON");
  server.send(200, "text/html", SendHTML(LED1status,true)); 
}

void handle_led2off() {
  LED2status = LOW;
  Serial.println("GPIO6 Status: OFF");
  server.send(200, "text/html", SendHTML(LED1status,false)); 
}

void handle_NotFound(){
  server.send(404, "text/plain", "Not found");
}

String SendHTML(uint8_t led1stat,uint8_t led2stat){
  String ptr = "<!DOCTYPE html> <html>\n";
  ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
  ptr +="<title>LED Control</title>\n";
  ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
  ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";
  ptr +=".button {display: block;width: 80px;background-color: #1abc9c;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";
  ptr +=".button-on {background-color: #1abc9c;}\n";
  ptr +=".button-on:active {background-color: #16a085;}\n";
  ptr +=".button-off {background-color: #34495e;}\n";
  ptr +=".button-off:active {background-color: #2c3e50;}\n";
  ptr +="p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";
  ptr +="</style>\n";
  ptr +="</head>\n";
  ptr +="<body>\n";
  ptr +="<h1>ESP8266 Web Server</h1>\n";
  ptr +="<h3>Using Access Point(AP) Mode</h3>\n";
  
   if(led1stat)
  {ptr +="<p>LED1 Status: ON</p><a class=\"button button-off\" href=\"/led1off\">OFF</a>\n";}
  else
  {ptr +="<p>LED1 Status: OFF</p><a class=\"button button-on\" href=\"/led1on\">ON</a>\n";}

  if(led2stat)
  {ptr +="<p>LED2 Status: ON</p><a class=\"button button-off\" href=\"/led2off\">OFF</a>\n";}
  else
  {ptr +="<p>LED2 Status: OFF</p><a class=\"button button-on\" href=\"/led2on\">ON</a>\n";}

  ptr +="</body>\n";
  ptr +="</html>\n";
  return ptr;
}

الوصول إلى خادم الويب في وضع نقطة الوصول (AP)

بعد رفع الكود، افتح الـ Serial Monitor على معدّل نقلٍ (baud) قدره 115200، واضغط على زرّ RESET في شريحة ESP8266. وإذا كان كلّ شيء على ما يُرام، فستظهر رسالة "HTTP server started" (بدأ تشغيل خادم HTTP).

ESP8266 NodeMCU Web Server Access Point Mode Serial Monitor Output - Server Started

الآن، أحضِر هاتفاً أو حاسوباً محمولاً أو أي جهازٍ آخر يمكنه الاتصال بشبكة واي فاي، وابحث عن شبكةٍ تُسمّى "NodeMCU". واتّصل بالشبكة باستخدام كلمة المرور 12345678.

ESP8266 NodeMCU Web Server Access Point Mode - Joining Server

بعد الاتصال بشبكة نقطة الوصول (AP) الخاصة بـ NodeMCU، افتح متصفّحاً وانتقل إلى العنوان 192.168.1.1. وينبغي أن تُعيد شريحة ESP8266 صفحة ويب تعرض الحالة الراهنة للمصابيح (LEDs) والأزرار. وفي الوقت نفسه، يمكنك التحقّق من الـ Serial Monitor لرؤية حالة أطراف GPIO في شريحة ESP8266.

ESP8266 NodeMCU Web Server Access Point Mode - Web Page

ESP8266 NodeMCU Web Server Access Point Mode Serial Monitor Output - Webpage Accessed

الآن، ومع مراقبة عنوان URL، اضغط على الزرّ لتشغيل المصباح LED1. وبمجرّد أن تضغط على الزرّ، تستقبل شريحة ESP8266 طلباً للعنوان /led1on. عندئذٍ تُشغّل المصباح LED1 وتقدّم صفحة ويب مع تحديث حالة المصباح. كما تطبع حالة طرف GPIO على الـ Serial Monitor.

ESP8266 NodeMCU Web Server Access Point Mode Web Page - LED Control

ESP8266 NodeMCU Web Server Access Point Mode Serial Monitor Output - LED Control

يمكنك تجربة زرّ المصباح LED2 لترى ما إذا كان يعمل بطريقةٍ مماثلة.

لنلقِ نظرةً أقرب على الكود لنرى كيف يعمل، كي تتمكّن من تعديله بما يلائم احتياجاتك.

شرح مفصّل للكود

يبدأ الكود بتضمين مكتبة ESP8266WiFi.h. وتحتوي هذه المكتبة على دوالّ خاصة بشريحة ESP8266 نستخدمها للاتصال بالشبكة. وبعد ذلك، نُضمّن مكتبة ESP8266WebServer.h، التي تحتوي على بعض الدوالّ التي ستساعدنا في إعداد خادمٍ والتعامل مع طلبات HTTP الواردة دون الحاجة إلى القلق بشأن تفاصيل التنفيذ منخفضة المستوى.

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

بما أننا نُعِدّ خادم ويب ESP8266 في وضع نقطة الوصول (AP)، فإنه سيُنشئ شبكة الواي فاي الخاصة به. لذا، علينا ضبط معرّف SSID، وكلمة المرور، وعنوان IP، وقناع الشبكة الفرعية (subnet mask) لعنوان IP، وبوابة IP (gateway).

/* Put your SSID & Password */
const char* ssid = "NodeMCU";  // Enter SSID here
const char* password = "12345678";  //Enter Password here

/* Put IP Address details */
IPAddress local_ip(192,168,1,1);
IPAddress gateway(192,168,1,1);
IPAddress subnet(255,255,255,0);

بعد ذلك، نُنشئ كائناً (object) من مكتبة ESP8266WebServer كي نتمكّن من الوصول إلى دوالّها. ويقبل باني هذا الكائن (constructor) كمُعامِلٍ المنفذ (port) الذي سيستمع إليه الخادم. وبما أن بروتوكول HTTP يستخدم المنفذ 80 بشكلٍ افتراضي، فسنستخدم هذه القيمة. وهذا يتيح لنا الاتصال بالخادم دون الحاجة إلى تحديد المنفذ في عنوان URL.

// declare an object of ESP8266WebServer library
ESP8266WebServer server(80);

بعد ذلك، نُعرّف أطراف GPIO في لوحة NodeMCU التي تتّصل بها المصابيح (LEDs)، إضافةً إلى حالتها الابتدائية.

uint8_t LED1pin = D7;
bool LED1status = LOW;

uint8_t LED2pin = D6;
bool LED2status = LOW;

داخل دالة Setup()

في دالة الإعداد (setup)، نُهيّئ خادم HTTP لدينا. أولاً، نُنشئ اتصالاً تسلسلياً لأغراض تصحيح الأخطاء (debugging)، ونضبط أطراف GPIO لتعمل كأطراف خرج (OUTPUT).

Serial.begin(115200);
pinMode(LED1pin, OUTPUT);
pinMode(LED2pin, OUTPUT);

ثم نُهيّئ نقطة وصولٍ برمجية (soft access point) لإنشاء شبكة واي فاي، وذلك بتوفير معرّف SSID، وكلمة المرور، وعنوان IP، وقناع الشبكة الفرعية (subnet mask) لعنوان IP، وبوابة IP (gateway).

WiFi.softAP(ssid, password);
WiFi.softAPConfig(local_ip, gateway, subnet);
delay(100);

للتعامل مع طلبات HTTP الواردة، علينا تحديد الكود الذي ينبغي تنفيذه عند الوصول إلى عنوان URL معيّن. ولهذا نستخدم دالة .on(). وتقبل هذه الدالة مُعامِلَين اثنَين: مسار URL نسبي، واسم الدالة التي ستُنفَّذ عند زيارة ذلك العنوان.

فالسطر الأول من مقتطف الكود أدناه، مثلاً، يشير إلى أنه عندما يستقبل الخادم طلب HTTP على المسار الجذر (/)، فإنه سيستدعي دالة handle_OnConnect(). ومن المهمّ ملاحظة أن العنوان المحدّد هو مسارٌ نسبي.

وعلى نحوٍ مماثل، علينا تحديد أربعة عناوين URL إضافية للتعامل مع الحالتَين لكلٍّ من المصباحَين.

server.on("/", handle_OnConnect);
server.on("/led1on", handle_led1on);
server.on("/led1off", handle_led1off);
server.on("/led2on", handle_led2on);
server.on("/led2off", handle_led2off);

لم نحدّد ما ينبغي للخادم تقديمه إذا طلب العميل عنوان URL غير محدّدٍ بدالة server.on(). ففي هذه الحالة، ينبغي أن يقدّم خطأ 404 (الصفحة غير موجودة) كردّ. ولتحقيق ذلك، نستخدم دالة server.onNotFound().

server.onNotFound(handle_NotFound);

الآن، لبدء تشغيل الخادم، نستدعي دالة begin() الخاصة بكائن الخادم.

server.begin();
Serial.println("HTTP server started");

داخل دالة Loop()

تُعالَج طلبات HTTP الواردة الفعلية في دالة الحلقة (loop). ولهذا نستخدم دالة handleClient() الخاصة بكائن الخادم. كما نغيّر حالة المصابيح (LEDs) بناءً على الطلب.

void loop() {
  server.handleClient();
  if(LED1status)
  {digitalWrite(LED1pin, HIGH);}
  else
  {digitalWrite(LED1pin, LOW);}
  
  if(LED2status)
  {digitalWrite(LED2pin, HIGH);}
  else
  {digitalWrite(LED2pin, LOW);}
}

الآن علينا كتابة دالة handle_OnConnect()، التي ربطناها سابقاً بالعنوان الجذر (/) باستخدام server.on. ونبدأ هذه الدالة بتعيين حالة كلا المصباحَين إلى الحالة المنخفضة (LOW) — وهي الحالة الابتدائية للمصابيح — وطباعتها على الـ Serial Monitor.

ونستخدم دالة send للردّ على طلب HTTP. ومع أنه يمكن استدعاء هذه الدالة بعددٍ من المُعامِلات المختلفة، فإن أبسط صورةٍ لها تتطلّب رمز استجابة HTTP، ونوع المحتوى، والمحتوى نفسه.

والمُعامِل الأول الذي نمرّره إلى دالة send هو الرمز 200 (وهو أحد رموز حالة HTTP)، الذي يقابل استجابة "OK" (تمام). ثم نحدّد نوع المحتوى على أنه "text/html"، وأخيراً نمرّر الدالة المخصّصة SendHTML()، التي تُولّد صفحة HTML ديناميكية تتضمّن حالة المصابيح.

void handle_OnConnect() 
{
  LED1status = LOW;
  LED2status = LOW;
  Serial.println("GPIO7 Status: OFF | GPIO6 Status: OFF");
  server.send(200, "text/html", SendHTML(LED1status,LED2status)); 
}

وعلى نحوٍ مماثل، نكتب خمس دوالّ إضافية للتعامل مع طلبات تشغيل/إطفاء المصابيح (LED ON/OFF) وصفحة الخطأ 404.

void handle_led1on() {
  LED1status = HIGH;
  Serial.println("GPIO7 Status: ON");
  server.send(200, "text/html", SendHTML(true,LED2status)); 
}

void handle_led1off() {
  LED1status = LOW;
  Serial.println("GPIO7 Status: OFF");
  server.send(200, "text/html", SendHTML(false,LED2status)); 
}

void handle_led2on() {
  LED2status = HIGH;
  Serial.println("GPIO6 Status: ON");
  server.send(200, "text/html", SendHTML(LED1status,true)); 
}

void handle_led2off() {
  LED2status = LOW;
  Serial.println("GPIO6 Status: OFF");
  server.send(200, "text/html", SendHTML(LED1status,false)); 
}

void handle_NotFound(){
  server.send(404, "text/plain", "Not found");
}

عرض صفحة ويب HTML

كلّما استقبل خادم ويب ESP8266 طلباً من عميل ويب، تُولّد دالة sendHTML() صفحة ويب. فهي ببساطةٍ تدمج كود HTML في سلسلةٍ نصّية طويلة وتُعيدها إلى دالة server.send() التي ناقشناها سابقاً. وتستخدم الدالة حالة المصابيح (LEDs) كمُعامِلٍ لتوليد محتوى HTML بشكلٍ ديناميكي.

وأول نصٍّ ينبغي أن ترسله دائماً هو إعلان <!DOCTYPE>، الذي يشير إلى أننا نرسل كود HTML.

String SendHTML(uint8_t led1stat,uint8_t led2stat){
String ptr = "<!DOCTYPE html> <html>\n";

يجعل عنصر <meta> الخاص بإطار العرض (viewport) صفحة الويب متجاوبة (responsive)، ما يضمن ظهورها بشكلٍ جيد على جميع الأجهزة. أمّا وسم <title> فيحدّد عنوان الصفحة.

ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
ptr +="<title>LED Control</title>\n";

تنسيق صفحة الويب

بعد ذلك، لدينا بعض كود CSS لتنسيق الأزرار والمظهر العامّ لصفحة الويب. فنختار خطّ Helvetica، ونُعرّف المحتوى ليُعرَض ككتلةٍ سطرية (inline-block) محاذاةً للوسط.

ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";

ثم يضبط الكود التالي اللون والخطّ والهامش المحيط بوسوم body وH1 وH3 وp.

ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";
ptr +="p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";

كما تُنسَّق الأزرار بخصائص مثل اللون والحجم والهامش وما إلى ذلك. ويغيّر محدِّد :active مظهر الأزرار أثناء الضغط عليها.

ptr +=".button {display: block;width: 80px;background-color: #1abc9c;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";
ptr +=".button-on {background-color: #1abc9c;}\n";
ptr +=".button-on:active {background-color: #16a085;}\n";
ptr +=".button-off {background-color: #34495e;}\n";
ptr +=".button-off:active {background-color: #2c3e50;}\n";

تعيين عنوان صفحة الويب

بعد ذلك، يُعيَّن عنوان صفحة الويب. ويمكنك تغيير هذا النصّ إلى أي شيءٍ يناسب تطبيقك.

ptr +="<h1>ESP8266 Web Server</h1>\n";
ptr +="<h3>Using Access Point(AP) Mode</h3>\n";

عرض الأزرار والحالة المقابلة لها

تُستخدَم عبارة if لتحديث حالة الأزرار والمصابيح (LEDs) بشكلٍ ديناميكي.

if(led1stat)
  {ptr +="<p>LED1 Status: ON</p><a class=\"button button-off\" href=\"/led1off\">OFF</a>\n";}
else
  {ptr +="<p>LED1 Status: OFF</p><a class=\"button button-on\" href=\"/led1on\">ON</a>\n";}

if(led2stat)
  {ptr +="<p>LED2 Status: ON</p><a class=\"button button-off\" href=\"/led2off\">OFF</a>\n";}
else
  {ptr +="<p>LED2 Status: OFF</p><a class=\"button button-on\" href=\"/led2on\">ON</a>\n";}

إعداد خادم ويب ESP8266 في وضع محطة الواي فاي (STA)

لننتقل إلى المثال التالي، الذي يوضّح كيفية إعداد خادم ويب ESP8266 في وضع المحطة (STA) وتقديم صفحات الويب إلى أي عميلٍ متّصل على الشبكة القائمة.

قبل أن تُتابع رفع الكود، عليك إجراء بعض التغييرات لضمان أن يعمل الكود لديك. فلتوصيل شريحة ESP8266 بشبكةٍ قائمة، عليك تعديل المتغيّرَين التاليَين ببيانات اعتماد شبكتك.

Change SSID & Password before trying STA mode web server sketch

بمجرّد أن تنتهي، تابع وجرّب الكود.

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

/*Put your SSID & Password*/
const char* ssid = "YourNetworkName";  // Enter SSID here
const char* password = "YourPassword";  //Enter Password here

ESP8266WebServer server(80);

uint8_t LED1pin = D7;
bool LED1status = LOW;

uint8_t LED2pin = D6;
bool LED2status = LOW;

void setup() {
  Serial.begin(115200);
  delay(100);
  pinMode(LED1pin, OUTPUT);
  pinMode(LED2pin, OUTPUT);

  Serial.println("Connecting to ");
  Serial.println(ssid);

  //connect to your local wi-fi network
  WiFi.begin(ssid, password);

  //check wi-fi is connected to wi-fi network
  while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected..!");
  Serial.print("Got IP: ");  Serial.println(WiFi.localIP());

  server.on("/", handle_OnConnect);
  server.on("/led1on", handle_led1on);
  server.on("/led1off", handle_led1off);
  server.on("/led2on", handle_led2on);
  server.on("/led2off", handle_led2off);
  server.onNotFound(handle_NotFound);

  server.begin();
  Serial.println("HTTP server started");
}
void loop() {
  server.handleClient();
  if(LED1status)
  {digitalWrite(LED1pin, HIGH);}
  else
  {digitalWrite(LED1pin, LOW);}
  
  if(LED2status)
  {digitalWrite(LED2pin, HIGH);}
  else
  {digitalWrite(LED2pin, LOW);}
}

void handle_OnConnect() {
  LED1status = LOW;
  LED2status = LOW;
  Serial.println("GPIO7 Status: OFF | GPIO6 Status: OFF");
  server.send(200, "text/html", SendHTML(LED1status,LED2status)); 
}

void handle_led1on() {
  LED1status = HIGH;
  Serial.println("GPIO7 Status: ON");
  server.send(200, "text/html", SendHTML(true,LED2status)); 
}

void handle_led1off() {
  LED1status = LOW;
  Serial.println("GPIO7 Status: OFF");
  server.send(200, "text/html", SendHTML(false,LED2status)); 
}

void handle_led2on() {
  LED2status = HIGH;
  Serial.println("GPIO6 Status: ON");
  server.send(200, "text/html", SendHTML(LED1status,true)); 
}

void handle_led2off() {
  LED2status = LOW;
  Serial.println("GPIO6 Status: OFF");
  server.send(200, "text/html", SendHTML(LED1status,false)); 
}

void handle_NotFound(){
  server.send(404, "text/plain", "Not found");
}

String SendHTML(uint8_t led1stat,uint8_t led2stat){
  String ptr = "<!DOCTYPE html> <html>\n";
  ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
  ptr +="<title>LED Control</title>\n";
  ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
  ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}\n";
  ptr +=".button {display: block;width: 80px;background-color: #1abc9c;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}\n";
  ptr +=".button-on {background-color: #1abc9c;}\n";
  ptr +=".button-on:active {background-color: #16a085;}\n";
  ptr +=".button-off {background-color: #34495e;}\n";
  ptr +=".button-off:active {background-color: #2c3e50;}\n";
  ptr +="p {font-size: 14px;color: #888;margin-bottom: 10px;}\n";
  ptr +="</style>\n";
  ptr +="</head>\n";
  ptr +="<body>\n";
  ptr +="<h1>ESP8266 Web Server</h1>\n";
    ptr +="<h3>Using Station(STA) Mode</h3>\n";
  
   if(led1stat)
  {ptr +="<p>LED1 Status: ON</p><a class=\"button button-off\" href=\"/led1off\">OFF</a>\n";}
  else
  {ptr +="<p>LED1 Status: OFF</p><a class=\"button button-on\" href=\"/led1on\">ON</a>\n";}

  if(led2stat)
  {ptr +="<p>LED2 Status: ON</p><a class=\"button button-off\" href=\"/led2off\">OFF</a>\n";}
  else
  {ptr +="<p>LED2 Status: OFF</p><a class=\"button button-on\" href=\"/led2on\">ON</a>\n";}

  ptr +="</body>\n";
  ptr +="</html>\n";
  return ptr;
}

الوصول إلى خادم الويب في وضع المحطة (STA)

بعد رفع الكود، افتح الـ Serial Monitor على معدّل نقلٍ (baud) قدره 115200، واضغط على زرّ RESET في شريحة ESP8266. وإذا كان كلّ شيء على ما يُرام، فسيعرض عنوان IP الديناميكي الذي حصل عليه من موجّه الشبكة (الراوتر)، إضافةً إلى رسالة "HTTP server started" (بدأ تشغيل خادم HTTP).

ESP8266 NodeMCU Web Server Station Mode Serial Monitor Output - Server Started

بعد ذلك، شغّل متصفّحاً وانتقل إلى عنوان IP المعروض على الـ Serial Monitor. وينبغي أن تقدّم لوحة NodeMCU صفحة ويب تتضمّن الحالة الراهنة للمصابيح (LEDs) وزرَّين للتحكّم فيها. وفي الوقت نفسه، يمكنك التحقّق من الـ Serial Monitor لرؤية حالة أطراف GPIO في لوحة NodeMCU.

ESP8266 NodeMCU Web Server Station Mode - Web Page

ESP8266 NodeMCU Web Server Station Mode Serial Monitor Output - Webpage Accessed

الآن، ومع مراقبة عنوان URL، اضغط على الزرّ لتشغيل المصباح LED1. وبمجرّد أن تضغط على الزرّ، تستقبل شريحة ESP8266 طلباً للعنوان /led1on. عندئذٍ تُشغّل المصباح LED1 وتقدّم صفحة ويب مع تحديث حالة المصباح. كما تطبع حالة طرف GPIO على الـ Serial Monitor.

ESP8266 NodeMCU Web Server Station Mode Web Page - LED Control

ESP8266 NodeMCU Web Server Station Mode Serial Monitor Output - LED Control

يمكنك تجربة زرّ المصباح LED2 لترى ما إذا كان يعمل بطريقةٍ مماثلة.

لنلقِ نظرةً أقرب على الكود لنرى كيف يعمل، كي تتمكّن من تعديله بما يلائم احتياجاتك.

شرح الكود

الفرق الوحيد بين هذا الكود والكود السابق هو أننا لا نُنشئ شبكة الواي فاي الخاصة بنا، بل ننضمّ إلى شبكةٍ قائمة باستخدام دالة WiFi.begin().

//connect to your local wi-fi network
  WiFi.begin(ssid, password);

بينما تحاول شريحة ESP8266 الاتصال بالشبكة، يمكننا استخدام دالة WiFi.status() للتحقّق من حالة الاتصال.

//check wi-fi is connected to wi-fi network
  while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.print(".");
  }

للعلم، تُعيد هذه الدالة الحالات التالية:

  • WL_CONNECTED: عند الاتصال بشبكة واي فاي.

  • WL_NO_SHIELD: عند عدم وجود درع واي فاي (Wi-Fi shield).

  • WL_IDLE_STATUS: حالةٌ مؤقّتة تُسنَد عند استدعاء دالة WiFi.begin()، وتبقى نشطةً حتى تنتهي عدد المحاولات (فينتج عنها WL_CONNECT_FAILED) أو حتى يتمّ إنشاء الاتصال (فينتج عنها WL_CONNECTED).

  • WL_NO_SSID_AVAIL: عند عدم توفّر أي معرّف SSID.

  • WL_SCAN_COMPLETED: عند اكتمال مسح الشبكات.

  • WL_CONNECT_FAILED: عند فشل الاتصال في جميع المحاولات.

  • WL_CONNECTION_LOST: عند فقدان الاتصال.

  • WL_DISCONNECTED: عند قطع الاتصال عن الشبكة.

وبمجرّد الاتصال بالشبكة، تُستخدَم دالة WiFi.localIP() لطباعة عنوان IP الخاص بشريحة ESP8266.

Serial.println("");
Serial.println("WiFi connected..!");
Serial.print("Got IP: ");  Serial.println(WiFi.localIP());

الفرق الوحيد بين وضع نقطة الوصول (AP) ووضع المحطة (STA) هو أن أحدهما يُنشئ شبكته الخاصة بينما ينضمّ الآخر إلى شبكةٍ قائمة. لذا، فإن بقية الكود الخاص بالتعامل مع طلبات HTTP وتقديم صفحات الويب في وضع المحطة (STA) هي نفسها المشروحة أعلاه لوضع نقطة الوصول (AP). ويشمل ذلك ما يلي:

  • تعريف أطراف GPIO في لوحة NodeMCU التي تتّصل بها المصابيح (LEDs).

  • تعريف عدّة دوالّ server.on() للتعامل مع طلبات HTTP الواردة.

  • تعريف دالة server.onNotFound() للتعامل مع خطأ HTTP 404.

  • إنشاء دوالّ مخصّصة تُنفَّذ عند الوصول إلى عنوان URL معيّن.

  • إنشاء صفحة HTML.

  • تنسيق صفحة الويب.

  • إنشاء الأزرار وعرض حالتها.