يعرفُ برنامجُ Flutter بأنَّهُ مجموعةُ أدواتٍ مفتوحةُ المصدرِ لتطويرِ البرامجِ ذاتُ واجهةِ المستخدمِ التي تسهّلُ البدءَ في إنشاءِ تطبيقاتِ الأجهزةِ المحمولةِ العاملةِ بنظامي iOS وأندرويد، والأردوينو منصةً لتطويرِ برمجياتِ المتحكماتِ التي تسمحُ بإنشاءِ تطبيقاتٍ مدمجةٍ لمجموعةٍ متنوعةٍ من المتحكماتِ الصغريةِ، وباستخدامِ هاتين الأداتين معاً يسهلُ إنشاء أجهزةٍ يمكنُ التحكمُ بها باستخدامِ تطبيقاتِ الأجهزةِ المحمولةِ.كتمرين، سنستخدمُ بعض العناصرِ الالكترونيةِ الرخيصةِ والأردوينو وFlutter لصنعِ الجهاز والتطبيق اللازمين للتحكمِ بالأجهزةِ التي يتم التحكمُ فيها عن بُعد بالأشعة تحت الحمراء مثل أجهزةِ التلفزيون ومكيفاتِ الهواءِ والمدافئ وأجهزةِ الستيريو.

المعداتُ اللازمةُ لإكمال المشروعِ هي: لوحُ تطوير NodeMCU esp8266، وليد يصدرُ أشعةً تحت حمراء، وجهازُ استقبالٍ يعملُ بالأشعةِ تحتَ الحمراء بتردد 38 كيلو هرتز، وترانزستور 2n3904 PNP.

 

تكوين بيئة التطوير المتكاملة للأردوينو (Arduino IDE)

نحتاجُ في البداية إلى برمجةِ وحدةِ NodeMCU.

 

تشغيل خادم بروتوكول التحكم في الإرسال (TCP) على اللوحة esp8266

كخطوةٍ أولى، نشغلُ خادمَ TCP ونختبرُ ما إذا كان يعمل.

  • نقوم بإنشاء كائن WiFiServer باسم wifiServer.
  • نعرف مؤشراً محرفياً ثابتاً ليحمل اسم الشبكة اللاسلكية (SSID) وكلمة المرور.
إنشاء كائن WiFiServer
إنشاء كائن WiFiServer (مصدر الصورة: timmcdon4ld.medium)
  • نكتب التعليمات التالية في تابع ()setup:

 

تابع setup ()
تابع setup (مصدر الصورة: timmcdon4ld.medium)
  • إنشاء الاتصالِ التسلسلي بسرعة 115200 بت في الثانية.
  • الانتظار لمدةِ ثانيةٍ للتأكد من اكتمالِ تهيئة الاتصال التسلسلي.
  • البدء بتهيئة اتصال شبكة WiFi.
  • الانتظار حتى يتم الاتصال بالشبكة.
  • تشغيلُ الخادم.

ثم ننتقل إلى التابع ()loop.

تابع loop ()،
تابع loop (مصدر الصورة: timmcdon4ld.medium)

 

وظيفته التعامل مع عميل متصل بالخادم ولديه بيانات متاحة للقراءة.

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

الآن نقوم بتجميع ورفعِ الكود إلى ذاكرةِ لوحةِ NodeMCU.

تحميل الكود إلى NodeMCU
تحميل الكود إلى NodeMCU (مصدر الصورة: timmcdon4ld.medium)

بعد رفعِ البرنامج، نفتحُ شاشةَ المنفذ التسلسلي (بالضغط على Ctrl + Shift + M في الحواسيب العاملة على نظام ويندوز) إذا لم يظهر لدينا الموجود في الشكل التالي على الشاشة، فنحاول فصل كابل USB وتوصيله مرةً أخرى ثمَّ نفتح شاشة المنفذ بسرعة.

شاشة المنفذ التسلسلي
شاشة المنفذ التسلسلي (مصدر الصورة: timmcdon4ld.medium)

الآن، بعد أن أصبحَ لدينا خادم TCP بسيط نَشِط، نختبرهُ باستخدام برنامج PuTTY، حيث نقوم بتكوين اتصالٍ باستخدامِ عنوان IP، في الشاشة التسلسلية والمنفذ 80 ونوع الاتصال (Raw).

تكوين اتصالٍ في برنامج PuTTY
تكوين اتصالٍ في برنامج PuTTY (مصدر الصورة: timmcdon4ld.medium)

كل ما يتم كتابته في موجِّه الأوامر يجب أن يُكرَر تلقائياً على شاشة المنفذ التسلسلي.

موجِّه الأوامر
موجِّه الأوامر (مصدر الصورة: timmcdon4ld.medium)

 

شاشة المنفذ التسلسلي
شاشة المنفذ التسلسلي(مصدر الصورة: timmcdon4ld.medium)

إذاً بتوفر أجهزةٍ تعملُ بشكلٍ جيدٍ في الشّق الأول من المشروع يمكننا الانتقال إلى الشّق الذي سنستخدمُ فيه أدواتُ Flutter.

 

إنشاء واجهة بسيطة في Flutter

بدايةً نُغلِقُ برنامج PuTTY، لأنَّ الخادم الذي يعملُ على NodeMCU يمكنه فقط التعامل مع اتصالٍ واحدٍ فقط. الخطوة التالية هي أن نقوم بفتح برنامج Android Studio ونُنشِئ مشروع Flutter جديداً باستخدام الخيارات الافتراضية.

إنشاء مشروع Flutter جديد
إنشاء مشروع Flutter جديد  (مصدر الصورة: timmcdon4ld.medium)

 

المشروع الذي تم إنشاؤه
المشروع الذي تم إنشاؤه، مصدر الصورة: timmcdon4ld.medium

في الدرجةِ MyHomePageState نحذفُ عناصرَ أدواتِ الواجهةِ النصيَّة من المتن.

حذف عناصر واجهة النص،
حذف عناصر واجهة النص (مصدر الصورة: timmcdon4ld.medium)

ونحذف الزر العائم، ولكن قبل القيام بذلك ننتبه للطريقة التي يتصرف بها عند الضغط عليه (onPressed) ودالة incrementCounter المخصصة له.

حذف floatingActionButton
حذف floatingActionButton (مصدر الصورة: timmcdon4ld.medium)

الآن على متنِ الدرجةِ MyHomePageState نقوم بإضافة زر من النوع RasiedButton، سنلاحظ أن الزر يحتوي على عنصر واجهة نصيّ بنيوي مُضمناً فيه. إنَّ العديد من أدوات واجهة المستخدم في Flutter مصنوعة من أدوات واجهة بدائيّة متعدِّدة.

إضافة RasiedButton
إضافة RasiedButton (مصدر الصورة: timmcdon4ld.medium)

في هذه المرحلة سنتمكن من تشغيل التطبيق.

واجهة التطبيق
واجهة التطبيق (مصدر الصورة: timmcdon4ld.medium)

اللون الرَّمادي للزر يشير إلى أنه غير فعّال لأنّنا لم نقم بتحديد وظيفته عندما ضغطنا عليه حيث سنقوم بذلك فيما بعد.

 

إنّنا جاهزون الآن للانتقال إلى مرحلةِ إرسال الأمر عبر TCP إلى NodeMCU عند الضغط على الزر.

الخطوة التالية هي تضمين مكتبتين من الـ Flutter هما foundation.dart و dart.io.

تضمين مكتبتي foundation.dart و dart.io
تضمين مكتبتي foundation.dart و dart.io (مصدر الصورة: timmcdon4ld.medium)

ونعيد كتابة التابع الرئيسي كما هو موضح في الشكل التالي

كتابة التابع الرئيسي
كتابة التابع الرئيسي (مصدر الصورة: timmcdon4ld.medium)

 

كتابة التابع الرئيسي
كتابة التابع الرئيسي (مصدر الصورة: timmcdon4ld.medium)

في الكود الموجود في الصورة السابقة، يجب أن يكون عنوان الـ IP الموجود في دالة Socket.connect  هو نفسه العنوان المنسوخ سابقاً من شاشة المنفذ التسلسلي عندما كنا نختبر خادم الاتصال.

نضيف كائناً ودالة بناء من النوع Socket إلى الدرجة MyApp كما هو موضح في الصورة التالية.

إضافة كائن Socket
إضافة كائن Socket (مصدر الصورة: timmcdon4ld.medium)

إلى الأسفل قليلاً من عبارة return من أداة Build، نضيف بارامتر channel ونسند إليه القيمة socket في عنصر MyHomePage.

إضافة بارامتر channel
إضافة بارامتر channel (مصدر الصورة: timmcdon4ld.medium)

في الدرجة MyHomePage نضيف حقل channel من نوع Socket.

إضافة حقل channel من نوع Socket
إضافة حقل channel من نوع Socket (مصدر الصورة: timmcdon4ld.medium)

في الدرجة MyHomePageState نضيف التابع togglePower_، ويمكننا حذف المتغير counter_ وتابع ()incrementCounter_ منها.

إضافة دالة togglePower
إضافة دالة togglePower، مصدر الصورة: timmcdon4ld.medium

نتجاوز التابع ()dispose في الدرجة MyHomePageState ونغلق القناة عندما يكون كائن الحالة مغلقاً.

تجاوز دالة dispose ()
تجاوز دالة dispose ()، مصدر الصورة: timmcdon4ld.medium

لنعُدْ الآن ونعين القيمة “OnPressed” لدالة togglePower عند الضغط على الزر ،وسيتم إرسال القيمة “POWER\n” إلى وحدة NodeMCU عند الضغط على زر الطاقة.

تعيين قيمة دالة togglePower
تعيين قيمة دالة togglePower، مصدر الصورة: timmcdon4ld.medium

نقوم بتشغيل التطبيق ، وفي حال عدم وجود أية أخطاء سنكون مستعدين لاختبار التطبيق مع لوح NodeMCU.

 

اختبار اتصال الأندرويد وخادم NodeMCU TCP

أولاً، نعيد توصيل لوح NodeMCU ببرنامج الأندرويد الذي قمنا بإنشاءه مسبقاً والذي تم رفعه إليه.

نفتح شاشة المنفذ التسلسلي، ثم نشغل تطبيق flutter الذي كنا نعمل عليه ونضغط زر التشغيل، سنرى النص “POWER\n” يظهر تلقائياً على شاشة المنفذ التسلسلي كما في الشكل.

اختبار الاتصال
اختبار الاتصال (مصدر الصورة: timmcdon4ld.medium)

 

تدريب جهاز التحكم عن بعد

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

لحسن الحظ، فإن مكتبة IRremoteESP8266 توفر علينا الكثير من الجهد والمشقة، وهي متوفرة على هذا الرابط هنا.

بمجرد تثبيت المكتبة، ننتقل إلى File > Examples>IRremoteESP8266>IRrecvDumpV2 ونفتح هذا الرسم التخطيطي.

قبل البدء بفعل أي شيء آخر، نحتاج إلى توصيل مستقبل الأشعة تحت الحمراء إلى لوح NodeMCU، ثم سنحتاج إلى تحديد الطرف الخاص بمستقبل الأشعة تحت الحمراء؛ تم توضيح طرف التوصيل الخاص في الشكل التالي، وهو شكل نموذجي لهذه المستقبلات.

أرجل مستقبل الأشعة تحت الحمراء
أرجل مستقبل الأشعة تحت الحمراء (مصدر الصورة: timmcdon4ld.medium)

 

نقوم بتوصيل الطرف Serial بالطرف رقم 14 للوح NodeMCU D5، والطرف الموجب إلى الطرف 3.3 فولت والأرضي بالطرف GND.

وصيل مستقبل الأشعة تحت الحمراء مع NodeMCU
توصيل مستقبل الأشعة تحت الحمراء مع NodeMCU (مصدر الصورة: timmcdon4ld.medium)

يُمكِنُنا مخطط IRrecvDumpV2 من التقاط أوامر الأشعة تحت الحمراء المرسلة بواسطة جهاز التحكم عن بُعد أو تلكَ التي تتحكم بأجهزتنا المستهدفة. نبدأ بتحميل مخطط IRrecvDumpV2 على NodeMCU ونفتح شاشة المنفذ التسلسلي، ثمَّ نقوم بعد ذلك بتوجيه جهاز التحكم عن بعد إلى مستقبل الأشعة تحت الحمراء ونضغط على زر التشغيل عدة مرات؛ الخرج الناتج في الشكل التالي بواسطة جهاز تحكم عن بعد من شركة Haier AC.

الخرج على شاشة السيريل
الخرج على شاشة العرض التسلسلي (مصدر الصورة: timmcdon4ld.medium)

نحدّد عدة أسطر من النّص مع التأكد من التقاط البيانات الخام و ننسخها إلى الحافظة بالضغط على Ctrl + C على نظام ويندوز، ثم نفتح أي محرّر نصوص ونلصقها بداخله.  تم تنسيق البيانات بحيث تبقى ضمن إطار الشاشة.

الأسطر التي تم نسخها الى محرّر النصوص
الأسطر التي تم نسخها الى محرّر النصوص (مصدر الصورة: timmcdon4ld.medium)

نفتح مجدداً مخطط الأردوينو و الذي أنشأناه سابقًا لاختبار اتصالات TCP، ومن ثمَّ نضيف ثابتاً لتحديد الطرف الذي سنستخدمه لنقل أوامر التحكم عن بُعد.

const uint16_t kIrLed = 4

و سوف نستخدم هذا الثابت عند تنفيذ الأمر IRsend.

IRsend irsend(kIrLed)

 

نقوم بلصق البيانات الخام التي نسخناها سابقاً، مع ضرورة الانتباه بشكل خاص لعدد عناصر المصفوفة وهو 71 كما في الشكل 29.

نسمي المصفوفة HaierAC_power ونتأكد من أن نوع المصفوفة هو uint16_t  (عدد صحيح بدون إشارة بطول 16 بت).

uint16_t HaierAC_power[71] ={8928, 4556, …

مصفوفة HaierAC_power
مصفوفة HaierAC_power (مصدر الصورة: timmcdon4ld.medium)

نجري تغييراً واحداً فقط إلى السطر الأول في التابع ()setup  و قبل تعليمه Serial.begin(115200) وهو: ()irsend.begin

التغيير الذي أجريناه في تابع setup ()
التغيير الذي أجريناه في تابع ()setup (مصدر الصورة: timmcdon4ld.medium)

قمنا سابقاً في تابع الحلقة بنسخ الدخل وطباعته على شاشة المنفذ التسلسلي.

تابع الحلقة
تابع الحلقة، مصدر الصورة: timmcdon4ld.medium

نحتاج إلى تعديل وظيفة الحلقة بحيث تقوم بتجميع الحروف المستلمة حتى تصل إلى حرف فصل (في حالتنا هو محرف السطر الجديد)، ثم تتحقق من كون الكلمة الناتجة أمراً نحتاج إلى تنفيذه.

تابع الحلقة بعد تعديله
تابع الحلقة بعد تعديله، مصدر الصورة: timmcdon4ld.medium

نقوم بتحميل الكود الأخير إلى لوح NodeMCU ثم نفتح تطبيق flutter، وعندها يجب أن نكون قد حصلنا على تطبيق صالح للاستعمال للتحكم عن بعد.

الكود البرمجي: للتحميل من هنا

فيديو التطبيق العملي للمشروع


المصدر: هنا

ترجمة: إيليا سليمان، مراجعة: لؤي ديب، تدقيق لغوي: بولا ابراهيم، تصميم: علي العلي، تحرير: محمد نور البوشي.