مقدمة
هل رغبت يوماً في معرفة درجة حرارة غرف منزلك أو حديقتك باستخدام مجموعة حسّاسات ترسل المعلومات بشكل دوري إلى خادم مركزيّ؟ لحسن الحظ فإن إنترنت الأشياء يسمح بتحقيق ذلك بسهولة، فقط تابع معنا هذا المقال.
سنستخدم المتحكّم ESP8266 NodeMCU نظراً لقدرتهِ على الاتّصال بشبكة WI-FI وإنشاء خادم ويب بسهولة، كذلك قدرته على قراءة درجات الحرارة من عدّة حساسات DS18B20، ومن ثمّ إرسالها إلى خادم الويب، حيث يمكننا الوصول إلى صفحة الويب من أيِّ جهاز متّصل بالشبكة ورؤية درجات الحرارة. الشكل أدناه يوضح الشكل النهائي للمشروع.
توصيلُ عدّةِ حساساتِ DS18B20 باستخدامِ سلكٍ واحد
إنَّ أهمّ ما يميّزُ حسّاسات DS18B20 هو إمكانية توصيل العديد منها باستخدام سلكٍ واحدٍ فقط، فلكلّ حساسٍ منها عنوان بطولِ 64 بتاً خاص به يميّزهُ عن غيره من الحساسات، وسنقوم الآن بتوضيح كيفية استغلال هذه الخاصيّة في تصميم المشروع.
توصيل عدة حسّاسات DS18B20 بالمتحكّم ESP8266 NodeMCU
الشكل أدناه يوضح لنا شكل حساس الحرارة DS18B20 المستخدم.
يمكننا توصيل الحسّاساتِ بالمتحكّمِ بسهولة، نبدأ أولاً بتوصيل جميع الحسّاسات على التفرّع، أي أنّنا نقوم بتوصيل أرجل جهد الدخل للحساسات معاً، وكذلك نفعل مع أرجل الأرضي والإشارة. بعد ذلك نُوصِل أرجلَ جهدِ الدّخلِ بقطب الجهد 3.3V على المتحكّم، وأرجل الأرضيّ بالأرضيّ، وأخيراً نوصل رِجل الإشارة بالقطب D2 على المتحكِّم.
وحتى نضمن استقرار نقل البيانات نوصل مقاومة رافعة 4.7 كيلو أوم لجميع الحساسات بين أرجل كلّ من الإشارة والجهد كما في الشكل أدناه.
تجهيز بيئة تطوير الأردوينو
لبرمجة المتحكّم ESP8266 باستخدام بيئة تطوير الأردوينو علينا أن نثبّت الإضافة الخاصّة ببرمجة المتحكّم أولاً، ثم نقوم بتثبيت بعض المكتبات التي ستمكّننا من التحكم بحساسات الحرارة.
1- تثبيت مكتبات حسّاس الحرارة DS18B20
نظراً لصعوبة التعامل مع بروتوكول دالاس للاتّصال الأحادي (Dallas 1-Wire) فإننا سنستخدم المكتبة الملحقة ببرنامج الأردوينو (DallasTemperature.h) التي تمكّننا من التعامل مع الحساس من خلال عدّةِ أوامرَ بسيطة.
لتثبيت المكتبة من واجهة بيئة تطوير الأردوينو نذهب إلى Sketch > Include Library > Manage libraries ثم ننتظر حتى يتمّ تحميل فهرس المكتبات.
نبحث عن ds18b20 في مربع البحث، سيظهر لدينا خياران نختار منهما المكتبة المسماة DallasTemperature لصاحبها Miles Burton ثم نقوم بتثبيتها كما في الشكل أدناه.
هذه المكتبة خاصّة بالتعاملِ مع أوامر الحسّاس ds18b20، ينقصنا الآن مكتبة تمكّننا من التعامل مع البروتوكول أحاديّ المسار، نقوم بالبحث عن مكتبة OneWire ثم نقوم بتثبيتها.
2- معرفة عناوين حسّاسات ds18b20 المستخدمة في المشروع
ذكرنا سابقاً أن لكلِّ حساس ds18b20 عنواناً بطول 64 بتاً يميّزه عن غيره من الحساسات، لذلك سنقوم الآن بتحديد هذه العناوين حتى نستطيع َ أن نتحكّم بكلِّ حساس على حدة.
سنستخدم الكود الآتي حتّى نتعرّف على جميع الحساسات التي قد وصِلَت بالمتحكّم، ثم نُظهِر عناوينها على الشاشة التسلسلية.
وبدلامن ذلك يمكننا توصيل حسّاسٍ واحدٍ فقط في كلّ مرّةٍ، أو نضيف الحساسات واحداً تلو الآخر لمعرفة العنوان الخاصّ بكلّ حسّاس.
لتحميل الكود اضغط هنا.
بعد تحميل الكود وتشغيله نفتح الشاشة التسلسلية وتظهر لنا بيانات عناوين الحسّاسات على الشاشة التسلسليّة كما في الشكل أدناه.
ملاحظة:
نحتفظ بهذه البيانات لاستخدامها لاحقاً.
3- إنشاء خادم ويب للمتحكّم ESP8266 وهو في وضع الاتصال بشبكة Wi-Fi
سنقوم الآن بضبط المتحكِّم ESP8266 ليعملَ في وضع الاتصال بشبكة Wi-Fi، ثم نقوم بإنشاء خادم ويب حتى يُتاح لنا إنشاء صفحة ويب يمكن الوصول إليها من أيّ جهاز متصل بالشبكة.
علينا أن نعدّل بعض الخصائص في الكود الآتي ليلائم تطبيقنا كمايلي:
نقوم بتعديل المتغيّرات الخاصّة باسم الشبكة والرّقم السرِّي لكي يتمكّن المتحكّم من الاتّصال بالشبكة:
const char* ssid = "YourNetworkName"; // Enter SSID here
const char* password = "YourPassword"; //Enter Password here
بعد ذلك نُدخل عناوين حسّاسات الحرارة التي حصلنا عليها سابقاً:
uint8_t sensor1[8] = { 0x28, 0xEE, 0xD5, 0x64, 0x1A, 0x16, 0x02, 0xEC };
uint8_t sensor2[8] = { 0x28, 0x61, 0x64, 0x12, 0x3C, 0x7C, 0x2F, 0x27 };
uint8_t sensor3[8] = { 0x28, 0x61, 0x64, 0x12, 0x3F, 0xFD, 0x80, 0xC6 };
وبعد الانتهاء من ذلك نقوم بتجربة الكود التالي. لتحميل الكود المستخدم اضغط هنا.
4- الاتصال بخادم الويب
بعد أن قمنا برفع الكود السّابق إلى المتحكّم، نفتح الشاشة التسلسليّة على معدّل نقل بيانات يساوي 115200 باود. ثم نضغط على زرّ RST على اللوحة، وإذا نفّذنا الخطوات السابقة بطريقةٍ صحيحة سنرى على الشاشة عنوان IP الديناميكيّ الذي حصلنا عليه من الرّاوتر بالإضافة إلى الرسالة الترحيبيّة “HTTP server started”. حيث يوضح الشكل أدناه رسالة بدء تشغيل الخادم.
نفتح الآن أي متصفّح للإنترنت ونُدخِل ْ عنوان IP نفسه وسنرى الآن درجة الحرارة التي حصلنا عليها من الحسّاسات كما فس الشكل أدناه.
شرح الكود
يبدأ الكود بإدراج المكتبات الآتية:
ESP8266WebServer.h: تمكّننا هذه المكتبة من الاتّصال اللاسلكيّ مع المتحكّم ESP8266، كما تُوفِّر لنا بعض التوابع التي تمكنّنا من إنشاء خادمِ الويب والتعامل مع طلبات صفحات الويب الواردة دون الحاجة لمعرفة التفاصيل المعقّدة لبروتوكول الاتصال.
DallasTemperature.h: وهي مكتبة مخصّصة للتواصل مع العتاد، وإذا استخدمناها مع مكتبة OneWire.h فإننا سنتمكّن من التعامل مع حساسات الحرارة بسهولة.
OneWire.h: وهي مسؤولة عن التعامل مع أيّ جهاز يعمل ببروتوكول المسار الأحاديّ OneWire.
#include <ESP8266WebServer.h>
#include <OneWire.h>
#include <DallasTemperature.h>
سنقوم الآن بإنشاء الكائنات التي سنحتاجها لاحقاً للتعامل مع حساسات الحرارة، والمتغيّرات التي ستحمل قِيمَ الحرارة. تذكّر أننا وصلنا حسّاسات الحرارة بالطرف D2 للوحة ESP8266. لتحميل الكود المستخدم اضغط هنا.
بعد ذلك ندخل عناوين حسّاسات الحرارة التي حصلنا عليها سابقاً:
uint8_t sensor1[8] = { 0x28, 0xEE, 0xD5, 0x64, 0x1A, 0x16, 0x02, 0xEC };
uint8_t sensor2[8] = { 0x28, 0x61, 0x64, 0x12, 0x3C, 0x7C, 0x2F, 0x27 };
uint8_t sensor3[8] = { 0x28, 0x61, 0x64, 0x12, 0x3F, 0xFD, 0x80, 0xC6 };
نضبِط اللوحة ESP8266 لتعمل في وضع الاتّصال بشبكة Wi-Fi موجودة، ثم ننشِئ خادم الويب عبر المنفذ 80، ويجب أولاً أن نسمح للوحة بالاتّصال بالشبكة Wi-Fi عن طريق إدخال اسم الشّبكة وكلمة المرور كما في الكود الآتي:
/*Put your SSID & Password*/
const char* ssid = "YourNetworkName"; // Enter SSID here
const char* password = "YourPassword"; //Enter Password here
ESP8266WebServer server(80);
1- التعليمات الموجودة ضمن التابع setup
علينا الآن أن نهيِّئ خادمَ صفحةِ الويب قبل تشغيله، أولاً ننشِئ اتصالاً تسلسليّاً مع الحاسوب ونبدأ كائن DallasTemperature باستخدام التابع ()begin التي تقوم بدورها بالتعرّف على جميع الحساسات المتّصلة بلوحة التحكّم، ومن ثم تقوم بفهرسة عناوينها وتحديدها بطول 12 بتاً.
Serial.begin(115200);
delay(100);
sensors.begin();
بعد ذلك نتّصل بشبكة Wi-Fi باستخدام التابع ()WiFi.begin:
Serial.println("Connecting to ");
Serial.println(ssid);
//connect to your local wi-fi network
WiFi.begin(ssid, password);
ننتظر حتى يتمّ الاتصال بنجاح ، ويمكننا التحقق من ذلك باستخدام التابعWiFi.status():
//check wi-fi is connected to wi-fi network
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
بعد نجاح الاتّصال، يمكننا معرفة عنوان IP المستخدَم مع لوحة ESP8266 باستخدام الأمر ()WiFi.localIP:
Serial.println("");
Serial.println("WiFi connected..!");
Serial.print("Got IP: "); Serial.println(WiFi.localIP());
نأتي الآن إلى كيفيّة التعامل مع طلبات بروتوكول http الواردة، عندها نحتاج أن نحدّد أي كود سيتمّ تنفيذه عند الضغط على عنوان url معين، فسنستخدم الأمر (on) الذي يعتمد على متغيّرَين الأوّل هو عنوان url والثاني هو الوظيفة المُراد تنفيذها عند الضّغط على هذا العنوان.
ففي الكود الآتي عندما يستقبل الخادم طلب http على العنوان الرئيسيّ (/) فسينفذ الأمر handle_OnConnect:
server.on("/", handle_OnConnect);
ولكن ماذا سيحدُث إذا طلب المستخدم عنوان url لم نحدّده مسبقاً باستخدام الوظيفة server.on()؟ إذا حدث ذلك فينبغي أن يرسِل الخادم حالة http 404 (صفحة غير موجودة)، بالتالى تظهر الرسالة Not found للمستخدم. وللتعامل مع هذه المشكلة بمرونة نستخدم الوظيفة server.onNotFound لتنفّذَ حين يطلب المستخدم عنواناً غير محدّد:
server.onNotFound(handle_NotFound);
بعد أن أنهينا إعداد الخادم نصبح جاهزين لتشغيلهِ:
server.begin();
Serial.println("HTTP server started");
2- التعليمات ضمن دالّة loop
علينا الآن التعامل مع طلبات المستخدمين الواردة لذا ننفذ الأمر handleClient الموجود في الكائن server.
server.handleClient();
نحتاج الآن أن نستخدم الدّالّة التي أضفناها سابقاً إلى العنوانِ الرئيسي باستخدام server.on
نحصل عند استخدام هذا الأمر على قراءاتِ الحرارة من جميع الحساسات، ثم نستخدم الأمر send للاستجابة إلى طلب المستخدم، يمكننا أن ننفّذ هذا الأمر بعدّة طرق، وإن أبسطَ صيغةٍ له تتألّف من رمز الاستجابة لبروتوكول Http، ونوع المحتوى المطلوب والمحتوى المطلوب ذاته.
نُرسل أولاً الرّمز (200) والذي يوافق الاستجابة OK ثم نحدّد نوع المحتوى “text/html“ وأخيراً نستخدم التابع ()SendHTML والتي تنشِئ صفحة ويب تفاعليّة تحتوي على درجات الحرارة. لتحميل الكود المستخدم اضغط هنا.
وبالمثل ننشئ الوظيفة handle_NotFound للتعامل مع طلبات العناوين غير الموجودة.
void handle_NotFound(){
server.send(404, "text/plain", "Not found");
}
عرض صفحة الويب
تتمثّل مهمّة الوظيفة SendHTML في إنشاءِ صفحةِ الويب كلّما تلقّى خادمُ اللوحة ESP8266 طلباً من المستخدم، فهي تقوم بتحويل رموز بروتوكول Html إلى نصٍّ طويل، وبعد ذلك تُعيدُ نتائجَ الوظيفة server.send() التي وضّحناها سابقاً، وتعملُ هذه الوظيفة على استخدام قراءات درجة الحرارة كمعايير تستخدمها في إنشاء صفحة الويب.
يجب علينا أوّلاً أن نعلن للخادم أننا سنُنشِئ صفحة Html فلذلك نبدأ بالوُسْم <!DOCTYPE>:
String SendHTML(float tempSensor1,float tempSensor2,float tempSensor3){
String ptr = "<!DOCTYPE html> <html>\n";
بعد ذلك نستخدم الوسم <meta> لتغييرِ خصائص صفحة الويب حتى نجعلها ملائمة للعرض في مختلف المتصفحات، ويعد الوسم <title> مسؤولاً عن تغيير عنوان الصفحة:
ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
ptr +="<title>ESP8266 Temperature Monitor</title>\n";
1- تنسيق صفحة الويب
نحتاج الآن تنسيق مظهر صفحة الويب باستخدام CSS، استخدمنا هنا نوع الخط Helvetica، وحددنا نوع المحتوى ليظهر بين السطور مع محاذاة المنتصف:
ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
ثمّ نغيّر بعد ذلك اللّون والحوافّ المحيطة بكلٍّ من عنوانِ المحتوى وجسمه وفقراته، وبالطبع يُمكننا تنسيق النصّ حسب الرغبة. عن طريق الكود التالي.
لتحميل الكود اضغط هنا.
2- تغيير ترويسة الصفحة
يمكنك تغيير ترويسة الصفحة باستخدام النصّ الذي تريده من خلال تعديل الوسم <h1>.
ptr +="<div id=\"webpage\">\n";
ptr +="<h1>ESP8266 Temperature Monitor</h1>\n";
3- عرض درجات الحرارة على الصفحة
نستخدم وسم الفقرة <p> لنضع ضمنه درجات الحرارة التي حصلنا عليها سابقاً، ويوفر لنا الترميز ° عرض رمز درجة الحرارة وذلك باستخدام الكود التالي. لتحميل الكود اضغط هنا.
4- تنسيق صفحة الويب بشكل احترافيّ
عادةً ما يُركّزُ المُبرمِجون على الشقِّ البرمجيِّ ويُهمِلُون الجانبَ الجماليّ للصّفحة، يُمكننا الاكتفاءُ بالتّصميمِ السّابقِ طبعاً، ولكن بالقليل من الجهد يُمكن أن نُكسِبَ الصّفحةَ جاذبيّةً وحِرَفيّةً أكبر، لنلقِ نظرةً على الصّورةِ أدناه التي توضّحُ ما سنقومُ به في الخطوات التالية.
أليست رائعةً بهذا الشكل؟ لا يحتاج الأمرُ أن تكونَ مُصمّماً مُحترفاً حتى تُنتِج مثلَ هذهِ الصّفحة، كلُّ ما عليكَ فعلهُ هو نسخُ الكودِ الآتي ووَضعه داخل الوظيفة SendHTML() بدلامن الكود السابق. لتنزيل الكود اضغط هنا.
لن تجدَ فرقاً كبيراً بين هذا الكود والكود السابق إلّا في بعض النقاط:
لقد استخدمنا هنا خط جوجل open sans، وعليك أن تتّصل بالانترنت حتى تتمكّن من رؤيته:
ptr +="<link href='https://fonts.googleapis.com/css?family=Open+Sans:300,400,600' rel='stylesheet'>";
وقد استخدمنا أيقونات من النوع SVG ووضعناها في الوسم المناسب لها، ويمكنك إنشاء أيقوناتكَ باستخدام الخدمة التي يقدمها Google بكلّ سهولة. حيث يوضح ذلك الشكل أدناه.
التحسينات التي يمكن القيام بها، والتحديث التلقائي لقيم الحرارة
كلّ شيءٍ جيّدٌ حتى الآن، ولكن لعلّكَ لاحظتَ أنّ درجة الحرارة الظّاهرة لا تُمثّلُ درجةَ الحرارةِ الحاليّة، بل درجة الحرارة لحظةَ تحميلِ الصفحة.
لا داعي للقلق فبإضافة وسم Html واحد يُمكننا التغلّب على هذهِ المشكلة، أضف وسم <meta> الآتي داخل وسم head حتى يقومَ المُتَصفّح بالتحميل التلقائي للصفحة على فترات محددة:
<meta http-equiv="refresh" content="2" >
تحديث درجة الحرارة باستخدام AJAX
الطريقة السابقة مثاليّة في حالِ كانت صفحةُ الويب صغيرة، ولكن في حالة الصفحات ذات المحتوى الكبير يُفضَّل استخدام تقنيّة AJAX، التي تُمكّنُنا من طلب البيانات من الخادم دونَ الحاجةِ إلى إعادةِ تحميلِ الصفحةِ بأكملها.
سنعتمد هنا على الكائن XMLHttpRequest في لغة جافا سكريبت، فهو يُنفّذُ طلب GET حتى يحدّث محتوى العنصر. يجب أن نضع في اعتبارنا أن AJAX ليست تقنية جديدة في حدّ ذاتها ولا حتّى لغة برمجة مختلفة عن جافا سكريبت، بل هي تطبيقٌ حديثٌ ومرِن لتقنيات موجودة سلفاً، وهي تمكننا أيضاً من:
طلب بيانات من الخادم بعد تحميل الصفحة
استقبال هذه البيانات والتعامل معها
إرسال بيانات إلى الخادم في الخلفية
وكلّ ما علينا فعله هو أن نضع كود AJAX الآتي قبل إغلاق وسم <head>:
لتحميل الكود اضغط هنا.
وكما قلنا سابقاً أنّ AJAX ما هي إلّا جافا سكريبت، لذلك نضعها داخل وسم <script> بعد ذلك نستخدم الوظيفة ()setIntervalحتى نحدّث المحتوى على فترات محدّدة، فهي تعتمد على متغيّرَيّ الدالّة المُراد تنفيذها – وهي في حالتنا طلب قيِم درجات الحرارة الجديدة – والفترة الزمنيّة التي ستكرّر عندها هذه الدالة بالميلي ثانية:
ptr +="<script>\n";
ptr +="setInterval(loadDoc,1000);\n";
التابع loadDoc هي أساس هذا الكود وتعتمد في عمليّة طلبِ البيانات من الخادم على الكائن ()XMLHttpRequest:
ptr +="function loadDoc() {\n";
ptr +="var xhttp = new XMLHttpRequest();\n";
تُنفَّذ التابع ()xhttp.onreadystatechange في كلِّ مرة تتغيّر فيها قيمة المتغيّر readyState والذي يحملُ قيمةً تدلُّ على حالةِ طلب XMLHttpRequest، ويمكن أن تكون إحدى القيم الآتية:
0: وتعني أنّ الطلب لم يُنشَأ بعد.
1: وتعني نجاح الاتصال بالخادم.
2: تُشير إلى نجاح استقبال الطلب.
3: في حالة معالجة الطلب.
4: وتعني انتهاء الطلب وجاهزية الرد.
كما يمكننا تحديد حالة الطلب XMLHttpRequest نفسه من الخاصيّة status فقد تعطي إحدى الرسائل الآتية:
200: OK
403: Forbidden
404: Page not Found
فعندما تكون حالة الاستعداد 4 وحالة الطلب 200 فهذا يعني انتهاء استلام البيانات ومعالجتها فنحصل على قيم درجة الحرارة الجديدة:
ptr +="xhttp.onreadystatechange = function() {\n";
ptr +="if (this.readyState == 4 && this.status == 200) {\n";
ptr +="document.body.innerHTML =this.responseText}\n";
ptr +="};\n";
وأخيراً علينا ألّا ننسى استخدام الدالّتين open و send لبدء طلب صفحة الويب.
ptr +="xhttp.open(\"GET\", \"/\", true);\n";
ptr +="xhttp.send();\n";
ptr +="}\n";
المصدر: هنا.
ترجمة: عبد الرحمن صابر, مراجعة: لؤي ديب, تدقيق لغوي: حنين غاليه, تصميم: علي العلي, تحرير: فادي الشعار.