مؤخرًا مع بداية شهر 3 أطلقت شركة Arduino لوحة Arduino Giga R1 حيث أنها تأتي بإمكانيات عالية وتتضمن معها وحدة WiFi مما يجعلها مناسبة لتطبيقات إنترنت الأشياء، ولكن واجهتني إشكالية تحول بيني وبين تفعيل ميزة الWiFi، سنتحدث عنها لاحقًا.
بشكل مختصر لوحة Arduino Giga R1 WiFi، عبارة عن لوحة تطوير مبنية على متحكم دقيق يدعى STM32H747XIH6 ويمتاز هذا المتحكم بسرعة عالية بالإضافة لكونه مبني على معمارية Arm Cortex M7 فيجعله مناسبٌا لمختلف تطبيقات الهندسية المعقدة.
مميزات لوحة Arduino Giga R1 WiFi
مبني على المتحكم STM32H747XIH6
يحتوى على تعليمات معالجة الإشارات الرقمية
يتوفر على وحدة MPU
وحدة اتصال الخاصة بالوايفاي والبلوتوث Murata 1DX
الذاكرة
STM32H747XI
2 ميجا بايت من ذاكرة الفلاش.
1 ميجا بايت من ذاكرة الوصول العشوائي (RAM).
AT25SF128A-MHB-Tذاكرة فلاش خارجية
16 ميجا بايت ذاكرة فلاش من نوع NOR.
بواجهة تخاطب QUAD SPI.
AS4C4M16SA
8 ميجابايت من ذاكرة وصول عشوائي من نوع ديناميكي.
وحدات الإدخال و الإخراج
76 مدخل ومخرج رقمي.
12 مدخل تماثلي.
12 منفذ بعرض نبضي متغير.
2 مخارج تماثلية.
دعم لبروتوكول USB.
جهد التخاطب المنطقي 3.3 فولت.
واجهات التخاطب (Communication Protocols)
4x UART
3x I2C
2x SPI
Can bus يتطلب وجود مستقبل ومرسل خارجي.
USB
USB HOST
USB Peripheral Type-C
منافذ جاهزة للاستخدام بشكل مباشر
منفذ الكاميرا.
منفذ الشاشة.
منفذ صوتيات.
التغذية
جهد عمل الدائرة 3.3 فولت
الجهد المسموح لتغذية اللوحة من 6 إلى 24 فولت.
هذه مميزات اللوحة بشكل مختصر، والآن سنتجه للإشكالية التي واجهتني خلال عملي واختبارها.
تتمثل الإشكالية بشكل محدد حول عدم إمكانية قراءة من ذاكرة الفلاش الخارجية بشكل صحيح باستخدام حزمة البرمجية التي تأتي بشكل رسمي من شركة الArduino، وقد بدأت المشكلة مع عدم إمكانية تفعيل وحدة الWiFi، حيث كان يظهر خطأ بعدم وجود الملف التشغيلي للWiFi (في عالم العتاد أو الهاردوير يطلق عليه بالفيرموير Firmware) فقررت استخدام الكود المرفق مع الحزمة وللأسف حال دون ذلك.
فكان البرنامج يقف عند هذه النقطة دون تقدم مع وجود إضاءة تحذيرية من اللوحة تبلغ عن وجود خطأ.
No filesystem containing the WiFi firmware was found.
Usually that means that the WiFi firmware has not been installed yet or was overwritten with another firmware.
Formatting the filsystem to install the firmware and certificates...
Error Format Flash: -3101
Flashing /wlan/4343WA1.BIN file
Flashed 0%
فظننت أنه هناك خطوة احتاج إلى القيام بها مسبقًا، فقمت بعمل تهيئة لذاكرة الفلاش حتى اتضح أنه لا يمكن عمل تهيئة وتظهر رسالة بحدوث خطأ.
Available partition schemes:
Partition scheme 1
Partition 1: WiFi firmware and certificates 1MB
Partition 2: OTA and user data 13MB
Partition scheme 2
Partition 1: WiFi firmware and certificates 1MB
Partition 2: OTA 5MB
Partition 3: User data 8MB
Do you want to use partition scheme 1? Y/[n]
If No, partition scheme 2 will be used.
WARNING! Running the sketch all the content of the QSPI flash will be erased.
Do you want to proceed? Y/[n]
Error formatting WiFi partition
فهنا تيقنت جيدًا ربما يكون هناك خلل على صعيد البرمجي أو العتاد، فقررت أن اعمل على بحث عن أصل الإشكالية و وصلت لنتيجة أنه هناك خلل مع ذاكرة الفلاش الخارجية حيث أنه لا تستطيع قراءة البيانات على وضع (Fast Quad Mode I/O) (مستقبلًا سأكتب مقالة مفصلة عن ماهية الQSPI) بشكلها الصحيح، مما دفعني لتطوير مكتبة تحل الإشكالية لحين اصلاحها من طرف شركة الArduino.
حيث أنه تعمل المكتبة على تحميل الملف التشغيلي لأول مرة ومن ثم استخدامها كبديل عن المكتبة الافتراضية.
نقوم بتحميل المكتبة من الرابط المرفق مسبقًا، واتباع الخطوات الآتية:
ننقل مجلد المكتبة إلى مسار التالي Document/
نقوم برفع البرنامج كما موضح في الصور.
ومن ثم سيقوم البرنامج بتحميل البرنامج التشغيلي لوحدة الWiFi على ذاكرة الفلاش الخارجية.
بعد الانتهاء من البرنامج لنقوم باختبارالبرنامج WiFi المرفق مع المكتبة.
وكما يبدو واضحًا، وحدة الWiFi تعمل بشكل جيد بدون أي مشكلة، يجدر الملاحظة أنه لكي تعمل وحدة الWiFi بشكل ممتاز يجدر إضافة مكتبة GigaWiFiSolver في بداية السطر حيث ستعمل كبديل عن المكتبة الإفتراضية التي تأتي مع الحزمة.
وهكذا قمت بحل الإشكالية التي واجهتني و قريبًا ستكون هناك سلسلة مقالات حول لوحة الArduino Giga R1 والمشاريع التي سأقوم بها باستخدام هذه اللوحة.
قررت البدأ بسلسلة من مقالات حول Java 8/9 وماجلبته من مميزات وتحسينات للغة والتي غيرت أسلوب التطوير إلي شكل أفضل، ولعلى أكبر تغير دراماتيكي للجافا وفي إصدارها الثامن كان بإقتراض بعض المفاهيم البرمجة الدالية Functional Programming، وبالتحديد كانت بإضافة lambda expression، method refernces و streams سأتحدث عن كل واحدة منهم في مقالة منفصلة ومقالتنا اليوم ستكون حول lambda expression وتطبيقاتها. (ملاحظة : سأستعمل كلمة دالة كترجمة لmethod في بعض الأحيان رغم إن الترجمة الحرفية ليست دالة لكن تحمل نفس السياق في المفهوم، وبما إن الشرح سيكون حول Functional Programming أو ما يعني البرمجة الدالية، سأضمن ترجمة الأصلية للكلمة بجانب الكلمة العربية لضمان وصول الفكرة إن Function لا تعني Method).
مالحاجة والدافع لإضافة مفاهيم البرمجة الدالية Functional Programming للغة ؟؟ حسنًا كما تري بدأ يصبح هذا المفهوم شائعًا، وايضًا عالم تطوير البرمجيات تغير وتقنيات تحتاج إلي مواكبة تطورات الذي تحدث في وقتنا الحالي، في الماضي في منتصف التسعينات كان قانون مور ساري ولغة جافا كانت يافعة وقوية وكان ماعلينا فعله هو إنتظار بعض سنوات جهاز الحاسب سيصبح ضعف سرعته السابقة.
الان تغير الأمر بعض الحال، فعتاد اليوم أصبح لا يعتمد علي تقليل من كثافة الشرائح من أجل السرعة، وحتى أغلب هواتفنا المحمول تحتوي علي أكثر من نواة في معالجها، مما يعني البرنامج من مفترض عليه العمل علي بيئة متعددة الأنوية، هنا يأتي دور البرمجة الدالية Functional Programming، التي تركز دوال صرفة “pure”، بمعني إنه تقوم بإرجاع نفس القيمة مقابل القيم المدخلة دون أثار جانبية و مع عدم قابلية للتغيرimmutability التي تسهل العمل علي بيئات متعددة الأنوية.
تعبير لامدا Lambda Expression
جافا أصبحت تدعم lambda expression ويقصد بها معاملة الmethods كما لو إنها كائن من درجة الأولي first-object class بمعني من ممكن تمريرها كوسائط لبعض الmethods وهذا محور نقاشنا اليوم .
قبل كل شئ ، علينا النقاش حول ماهية الواجهة الدالية functional interface، الواجهة الدالية functional interface هي مجرد واجهة interface مع دالة واحدة مجردة single abstract method (لاحظ هنا إستعملت دالة في إطار ترجمة method في الغالب تحمل نفس المفهوم ونفس الوظيفة لكن لا تخلط بين method و function فهم مختلفان جدا هنا، لم أجد الترجمة المناسبة في هذا السياق ) يعني نقصد بيها كلاس class تقوم بتوظيف أي واجهة interface بتضمين تطبيقاتها لكل methods .
علي السبيل المثال، كما نعلم جافا تملك العديد من الواجهات interfaces لنقل على سبيل المثال واجهة Runnable interface، تحتوي علي single abstract method وتدعي run ، لا تأخذ متغيرات كوسائط arguments ولا ترجع قيمة بمعنى إنها void، وهناك كلاس Thread دالته البنائية constructor method تأخذ Runnable كوسيط وكمثال سنستعمل anonymous inner class لتمريرها لهذا الconstructor method الخاص بي Thread.
public class Demo {
public static void main(String[] args){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("inside runnable using an anonymous inner class");
}
}).start();
}
}
أصبح الأمر مختصر وأجمل من نسخة السابقة والتي قد كنت إعتد علي إستعمالها، بناء الجملة أو لنقل syntax يستعمل السهم لفصل الوسائط arguments، وهنا لم نمرر أي argument ولم نستعمل الأقواس braces بحكم إنه لا داعي لها طالما إنه في سطر واحد، lambda expression يجب أن يكون النوع الوسائط arguments type والقيمة المرجعة return type متوافقين مع single abstract method في الواجهة وهذا واضحًا عندما إستعملنا واجهة Runnable وذلك تحتوي علي run حيث لا تستقبل arguments و من نوع void بمعني أدق لا ترجع أي قيمة وهذا واضحًا جللًا علي lambda expression، الlambda expression ماهي إلا مجرد تطبيق للدوال methods الواجهة interfaces، ويمكننا أيضًا إسناد lambda experssion للواجهة كما هو موضح هنا
public class Demo {
public static void main(String[] args){
Runnable r = ()->System.out.println("inside Thread constructor using lambda");
new Thread(r).start();
}
}
يجدر الذكر بإن lambda expression ليست كلاس أو مكتبة في الJava ويمكن فقط إسنادها للfunctional interface references
إسناد lambda expression كما لو إنه توظيف احد الدوال المجردة single abstract method داخل الواجهة، يمكنأن تفكر أن lambda expression تشبه من ناحية البنيةanonymous inner class، لهذا السبب يجب أن تكون lambda متوافقة مع الدالة المجردة abstract method ونوع وسائطها وreturn type. المثال الذي أرفقته للشرح كان بسيط بحكم إنه لا يحتوي علي وسائط arguments ولا يقوم بإرجاع قيمة، سأضيف مثال أخر بإستعمال java.io,FilenameFilter interface، من الوثائق تحتوي هذه الواجهة علي single abstract method تدعي accept
boolean accept(File dir, String name)
الوسيط File يرمز للمسار حيث يتم إيجاد الملف مراد إيجاده و String وهو خاص بإسم الملف، وهنا توضيح للمثال بإستعمال anonymous inner class
import java.io.File;
import java.io.FilenameFilter;
import java.util.Arrays;
public class Demo {
public static void main(String[] args){
File directory = new File("./src");
String[] names = directory.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".java");
}
});
System.out.println(Arrays.asList(names));
}
}
هنا accept method يقوم بإرجاع true في حالة إيجاد ملف ينتهي باللاحقة .java في حالة عدم وجودها يرجع false.
وهنا طريقة أخري بإستعمال lambda expression .
import java.io.File;
import java.util.Arrays;
public class Demo {
public static void main(String[] args){
File directory = new File("./src");
String[] names = directory.list((dir, name) -> name.endsWith(".java"));
System.out.println(Arrays.asList(names));
}
}
أصبح البرنامج يستهلك أقل سطور وأكثر وضوح وسهولة لإستعياب الفكرة، هناك شئ غريب ربما ستلاحظه وهو إن الوسائط لا تحمل نوعا خاص بيها وماذا يقصد بيها، في الحقيقة تتم عملية معرفة أنواع الوسائط أثناء الترجمة compile time فالcompiler سيعرف إن list method تعتمد علي FilenameFilter interface ، من المهم إن تكون lambda expression متوافقة مع single abstract method، كما تلاحظ lambda تقوم بإرجاع boolean type وذلك بعد السهم حي ثلاحظ الدالة endsWitdh تقوم بإرجاع true في حالة وجود ملف من لاحقة .java وهذا جليًا في مثالنا السابق حيث accept دالة method من نوع boolean والوسائط التي تحملها الدالة method مثل عدد الوسائط التي أستعملناها في lambda expression.
يمكنك تضمين نوع المتغير إن أردت ذلك ، فلك الحرية الإختيار بين تضمينها أو عدم تضمينها.
وفي حالة إن lambda expression ستتطلب أكثر من السطر أو إضافة أكثر من جملة عليك إستعمال الأقواس braces و إرجاع القيمة بشكل صريح explicit return statement.
ماذا لو أردت بناء تطبيق إنترنت الأشياء أو أحد التطبيقات التي ترتكز حول المتحكمات الدقيقة ولكن لا تملك دراية كبيرة بلغات الرئيسية لهذا المجال مثل الC و الC++ و الAssembly و غيرها من اللغات، وربما قد تملك دراية بسيطة عن أحد لغات البرمجة مثل الPython، حسنًا مع هذه اللغة أصبح من الممكن القيام بأحد هذه التطبيقات بإستخدام منصة MicroPython وهي نسخة مصغرة عن لغة الPython لكي يتم إستخدامها داخل المتحكمات الدقيقة مثل متحكمات STM32 و ESP8266 و ESP32 وغيرها التي تملك القدرة على إستضافة هذه المنصة نظرًا لإمكانياتها الكبيرة، في هذه المقالة سنتطرق إلى عملية تنصيب الMicroPython على المتحكمات ESP8266 و ESP32 نظرًا لتكلفتهم الرخيصة ووفرتهم (إن كنت من ليبيا يمكننا الحصول عليهم من شركة الأساس لتكنولوجيا في الزاوية أو محل القدرة الرقمية في طرابلس).
حسنًا أيها القارئ ربما أنت على دراية بلغة البرمجة Python أو ربما لا ولكن سأقوم بشرح وجيز وبسيط عن هذه اللغة كتمهيد لموضوعنا حول الMicroPython.
كسائر باقي لغات البرمجة و بإختلافاتها، تعتبر لغة الPython أحد لغات البرمجية عالية المستوى وذو أغراض عامة بمعنى أنها لا تقتصر على نطاق (domain) معين في حل المُعضلات؛ بمعنى أدق يمكن إستخدامه على نطاق واسع سواء لبرمجة تطبيقات الويب أو برمجة تطبيقات السطح المكتب أو كتابة أدوات أتمتة لمدراء النظم و غيرها من التطبيقات، ولا يقف الأمر عند هذا الحد وبل أصبح من الممكن أن يتم برمجة المتحكمات الدقيقة بإستخدام لغة الPython!! (نعم يمكن هذا عن طريق إستخدام بيئة MicroPython وسنتطرق إليها قريبًا في هذه المقالة)؛ بجانب هذا تعتبر لغة الPython لغة مُفسرة (Interpreted) وهذا على عكس لغات التي تنتهج عملية الCompilation والتي يقصد بها عملية تحويل الكود البرمجي إلى لغة الألة بحيث يستطيع جهاز الحاسب فهم هذه التعليمات وأما للغات المُفسرة يتم الإعتماد على عملية تنفيذ العمليات البرمجية سطرًا وراء سطر دون الحاجة إلى عملية الCompilation، هذا مجرد موجز بسيط و سأفترض على القارئ على دراية بسيطة بأحد اللغات البرمجة لكي يفهم المقالة بشكل جيد.
ماهو الMicroPython ؟
يمكننا إعتبارها كنسخة مصغرة عن لغة الPython ومخصصة خصيصًا لعالم النظم المتضمنة أو لنقل لعالم المتحكمات الدقيقة (Micro-controller)، بمعنى أدق هو إعادة توظيف للغة البرمجة الPython ولجعلها متاحة لعالم المتحكمات الدقيقة كالتي توجد داخل أجهزتنا المنزلية مثل الغسالة أو المبرد أو وغيرها، وطبعًا لطبيعة هذه الأجهزة والأنظمة حيث أنها تأتي بموارد قليلة سواء من ناحية ذاكرة الوصول العشوائي (RAM) أو ذاكرة الFlash حيث يرتكز فيها التعليمات الرئيسية ويمكن تشبيها بالقرص الصلب الذي يتواجد داخل جهازك الحاسب؛ فأنها لا تأتي بكامل مميزات اللغة سواء من ناحية المكتبيات القياسية أو كمميزات اللغة، ولكن هذا لا يعنى إفتقاد لكل المكتبيات التي تأتي مع لغة الPython حيث تمت إعادة كتابة بعض المكتبيات لكي تتوافق مع طبيعة هذه البيئة و تعمل بشكل جيد، بجانب هذا تحتوي أيضًا على Virtual Machine حيث ترتكز وظيفتها على تنفيذ تعليمات bytecode ولكن يمكن أيضًا ترجمة بعض التعليمات إلى native عن طريق إستخدام decorator native لكي يكون الكود أسرع ولكن الحجم يزيد نسبيًا عن bytecode، على العموم لن أتطرق في هذا الموضوع حاليًا لكن الملخص يمكن إعتبار الMicropython عن نظام تشغيل مصغر يمكن برمجته بإستخدام لغة الPython.
ربما تعلم عن الArduino أو لا تعلم (مستقبلًا سيكون هناك تدوينات حول هذه البيئة) لكن ببساطة هي عبارة عن منظومة متكاملة (ecosystem – لم أجد ترجمة مناسبة لكن ستفى بالغرض حتى الان) منظومة متكاملة تتكون من لوحات تطوير تعتمد على المتحكمات الدقيقة سواء كان متحكمات Atmega أو Arm و تملك واجهة برمجية خاص بها و فلسفة خاصة بها، والغرض الأساسي من تطوير هذه اللوحات أو من الArduino بصفة عامة كان لتسهيل عملية التعامل مع المتحكمات الدقيقة بصورة بسيطة جدًا و الأمر ينطبق هنا على الMicroPython ولكن بنكهة لغة الPython.
وطالما هي عبارة عن نسخة مصغرة من لغة الPython ولنقل الإصدار 3، (أن كنت لا تزال تستخدم الإصدار 2، الرجاء غادر المدونة وراجع نفسك ) فهي تحتوي على غالب المميزات أن لم تكن كل المميزات مثل :
أسلوب برمجة الكائنات-التوجه الخاص بلغة الPython (لكن بدون metaclasses).
تتوفر على مختلف الأنواع من البيانات ( مثل unicode strings, integers, and floating-point numbers) وأيضًا تراكيب البيانات (data structures) (مثل lists, sets, and dictionaries).
ويجدر الذكر بالطبيعة المتغيرة للObjects التي تتميز بها اللغة.
الدوال ككائنات من الدرجة الأولى (أن لم تفهم الأمر، ببساطة يمكننا إعتبار الدوال كمتغير عادي ويمكننا إسنادها وتمريرها مثل أي متغير عادي )
معالجة الإستثناءات (Exception handling)
ومميزات أخرى مثل يمكن إستخدام الكلمات المفتاحية async و await و الدوال المولدة (gererator functions) التي تعتمد على الكلمة المفتاحية yield و ناهيك عن توفر الميزة التي أفضلها في لغة البايثون list comprehensions، والعديد من الكثير لايسعنا ذكرها.
ولطبيعة إختلاف خصائص الأجهزة واللوحات التي تعمل على MicroPython سيكون هناك إختلاف في الوحدات التي تأتي مع MicroPython، على سبيل المثال في اللوحات التي تعتمد على ESP8266 أو ESP32 ستتوفر على مزايا تقنية الWifi والوحدات التي تعتمد على الإنترنت مثل urequest أو ujson (يجعلها مناسبة بشكل ممتاز لتطبيقات إنترنت الإشياء – IOT)، بينما غيرها مثل PyBoard أو MicroBit لا تأتي بمثل هذه الإمكانيات.
لماذا لغة الPython ؟
ربما يطرح أحد هذا السؤال أو لا يطرح، لكن لنسأل أنفسنا لما لغة الPython، بسبب سهولتها وبساطتها في تصميم وتطوير النماذج الأولية للمشاريع النظم المتضمنة ومشاريع إنترنت الأشياء في وقت قصير وهذا يجعلها بديل أفضل عن لغة الC التقليدية أو واجهة الArduino رغم أنه أيضًا أتت لكي تقوم بتسهيل عملية تطوير النماذج، فلغة البايثون أو لنقل الMicroPython هو بديل جيد لخوض تجربة حول المتحكمات الدقيقة واللوحات التطوير وتطوير نماذج تفاعلية أو مشاريع إنترنت الإشياء – IOT حيث أن أسلفت في ذكر مميزاتها سابقًا و على سياق أخر تمتلك مجتمع كبير ومما يعنى عدد كبير من المكتبيات التي تساعد على تطوير المشاريع في وقت وجيز.
ولغة الPython سهلة التعلم ومن الممكن للفرد أن يبدأ البرمجة عن طريقها أو يتعلم البرمجة و النظم المتضمنة على كل حد سواء وهذا نظرًا لي ماتوفره MicroPython من سهولة التعامل معاها حيث تأتي محملة بREPL إختصار إلى Read Evaluate Print Loop وهي بيئة تفاعلية كالتي تأتي بها لغة Python، فمنها يمكننا إختبار وتجربة الأوامر بسطر من أومر مباشرة دون الحاجة إلى كتابة ورفع الكود كما هو متعارف عليه عند أستخدام لوحات الأردوينو، كل ماعليك فعله ربط لوحة محملة بالMicroPython بجهازك الحاسب و إختبار الكود، ومن هذا السياق نفهم أنه الكود يتم تحويله إلى bytecode و أيضًا تتيح MicroPython على القدرة تحويل الكود إلى الmachine code إلى جانب توفرها على Inline Assembler بمعنى مبسط توفر بيئة MicroPython على قدرة للوصول لتعليمات المتحكم كما لو كانت لغة منخفضة المستوى وهذا يجعلها من الممكن تطوير تطبيقات بكفاءة عالية.
ولبدأ العمل على الMicroPython كل مايتطلبه منك هو لغة الPython لا غير وفي أن ذاته يمكنك تعلم الإلكترونيات عن طريق بناء مشاريع بإستخدام هذه المنصة.
أول مشروع بالMicroPython
لبدء بإستخدام الMicroPython تحتاج إلى أحد اللوحات الموضحة في الصور الأتية ويمكن الحصول عليها من داخل ليبيا أو شراءها من الخارج.
لوحة NodeMCU المبنية على متحكم ESP8266
أو لوحة NodeMCU المبنية على متحكم ESP32
أي لوحة من الأثتنين تفي بالغرض حاليًا لن يهم المواصفات نظرًا لطبيعة المشروعين التي سنقوم ببنائهم، وعلى العموم أي لوحة تدعم الMicroPython تفي بالغرض طالما لديها وصول بالإنترنت.
الخطوة الأخرى والمهمة وهي أن تتأكد من وجود بايثون على جهازك الحاسب وتأكد بأن تملك أحدث إصدار من لغة، ولحد ساعة إصدار 3.8 هو أحدث إصدار ويمكن تحميله من هذا الرابط https://www.python.org/downloads/
ولإستخدام بيئة الMicroPython سنبتعد عن أي تعقيد ممكن فهذه المقالة وسنستخدم برنامج uPyCraft وهو مشابه لوظيفة Arduino IDE من ناحية المفهوم و الوظيفة ومنه يمكننا التعامل ورفع برامج المكتوبة بالPython لأحد اللوحات سواء NodeMCU أو غيرها. يمكنك تحميله من هذا الرابط هنا
قبل البدء، عليك التأكد من أن وجود بعض التعريفات على جهازك الحاسب هناك بعض النسخ التي تستخدم ch340 بدل من cp210x، وعيب ch340 يتطلب تعريفها على جهاز الحاسب ويمكنكم تحميل التعريف من هذا الموقع التابع لشركة الرابط من هنا.
كيف يمكن التفريق النسخ، أغلب NodeMCU المبنية على ESP32 تأتي محملة بشريحة cp210x والتي مبنية على ESP8266 تختلف ويمكن تميزها من الصورتين.
ماوظيفة كل من CH340 و CP210X ببساطة هما عبارة عن USB to Serial يعملون كحلقة أو جسر تواصل بين ESP8266 أو ESP32 وجهازك الحاسب حيث كل من المتحكمين لا يأتين بدعم مضمن للUSB في الوقت الحالي واحد من إحد منتجات Espressif وهو ESP32-S2 يأتي بدعم لUSB OTG، على العموم هذا ليس موضوعنا حاليًا لكن مجرد معلومات للفائدة.
كل من المتحكمين لا يأتين بدعم MicroPython مسبقًا وعلى أثر هذا سنقوم بتحميل الFirmware من موقع الرسمي لكل من نسختين ESP8266 و ESP32، سأقوم بتوفير الروابط مباشرة لتسهيل العملية.
لنقم بتوصيل لوحة الNodeMCU ولنتأكد من وجود أحد المنافذ أو تعريف هذه اللوحات على جهازنا عن طريق الذهاب إلى Computer Management ثم إلى Device Manager.
من الصورة نرى ان اللوحة تم التعرف عليها من قبل جهازنا الحاسب وجاهزة للإستخدام، الان لنبدا بحرق الفيرموير على اللوحة عن طريق الخطوات الأتية
لنقم بإختيار اللوحة و الملف والمنفذ ومن ثم نضغط على OK، وستظهر لنا النافذة التالية
عليك الإنتظار حتى تتم العملية، ولنقم الان بربط الuPyCraft بالمتحكم وقبل هذا عليك التأكد من الخيارات الأتية كما هي موضحة في الصور.
في حالتي سأختار esp8266
والان لنقم بالإتصال باللوحة، عند نجاح الاتصال سيظهر لك ثلاث مؤشرات وهي خاص بالREPL والتي من خلالها يمكننا كتابة التعليمات باللغة الPython.
سأقوم بكتابة أول جملة شهيرة وهي طباعة كلمتين Hello, World
بينقو، الأمر يعمل جيدًا كما هو موضح.
من خلال الREPL يمكننا القيام الكثير بالعديد من الأمور سواء من عمليات حسابية مختلفة أو إستدعاء مكتبيات و غيرها الكثير من الأمور، حسنًا سأقوم بكتابة مشروع بسيط وهو مشروع blink وهو عبارة عن إضاءة LED وإطفاءه كل ثانية ورفعه على اللوحة.
قبل البدء في المشروع علينا نفهم كيف يعمل الMicroPython داخل المتحكم، عند تغذية المتحكم أول مايقوم به الMicroPython تجهيز الruntime ومن ثم يبدأ البحث عن أول ملفين وهما boot.py و main.py، يبدا بتنفيذ boot ثم ينتقل إلى main وفي حال عدم وجوده يتجاوزه ولا يقوم بتنفيذ أي شئ.
كما ترى من مجلد device على يسار البرنامج وهو يعكس المحتويات التي توجد داخل المتحكم وحاليًا يتوفر على boot.py ويحبذ تركه كما هو الان.
الان لنقم بإنشاء ملف جديد ولنقم بتسميته main.py من خلال البرنامج، ومن ثم نقم بكتابة الكود الاتي
from machine import Pin
import time
# D8 = GPIO15
p2 = Pin(15, Pin.OUT)
if __name__ == "__main__":
while True:
# led is high
p2.on()
# delay for 1 second
time.sleep_ms(1000)
# led is low
p2.off()
# delay for 1 second
time.sleep_ms(1000)
كما هو ملاحظ الكود يبدو بسيط وواضحًا ربما للبعض والبعض لا، سأقوم بشرحه على كل حدة لهذا الكود فقط كتوضيح.
قمت بتضمين كلاس Pin من الوحدة machine وهذه مسؤولة عن عمليات المنخفضة المستوى لمنافذ المتحكم أو اللوحة حيث سنقوم بتحكم بإضاءة الLED من ناحية تشغيلها أو إطفاءها، وأما بالنسبة إلى time فهي تحتوي على بعض الدوال المهمة ومن ضمنها الدوال التأخير حيث سنقوم بتأخير أو بعمل block لمشروع لفترة من زمن (يستحسن عدم إستخدامها كثيرًا وإستخدام الtimers التي تأتي متضمنة مع الmicropython).
from machine import Pin
import time
لنقم بإنشاء كائن pin ونسنده إلى منفذ D8 والذي هو أساسًا GPIO15 بالنسبة للمتحكم ESP8266، وتحديد حالته كخرج. (المنصة تستخدم منافذ الESP8266 بغض النظر عن اللوحة التي تملكها ومما يعنى أن الlabel على اللوحة غير مهمات وبلا فائدة ونتيجة هذا عليك معرفة أي من منفذ التي على اللوحة مرتبط بي ESP8266 وفي حالتي أستخدم الصورة التالية كمرجع).
# D8 = GPIO15
p2 = Pin(15, Pin.OUT)
سيدخل الكود إلى حلقة مالانهائية حيث ستقوم بعملية تشغيل وإطفاء الLED بإستمرار دون توقف، هذا مجرد مثال بسيط وتوضيحي لألية عملية برمجة اللوحات التي تستخدم الMicroPython.
if __name__ == "__main__":
while True:
# led is high
p2.on()
# delay for 1 second
time.sleep_ms(1000)
# led is low
p2.off()
# delay for 1 second
time.sleep_ms(1000)
بعد ما قمنا بإنشاء ملف main.py و كتابة الكود، لنقم بتحميل الكود على اللوحة وتنفيذه عن طريق الخطوات الأتية
هذا مقالتي لهذا اليوم حاولت أن أشرح بإسهاب حول الMicroPython ومشروع مصغر بسيط لتوضيح، قادمًا سأقوم بعمل مقالات بكثرة حول الMicroPython، وأرجو أن تعجبكم 😁.
مؤخرًا تحصلت على (YP-01 (USB TO TTL Converter وهو عبارة عن محول من Serial إلى USB، بمعنى دقيق هو عبارة عن حلقة وصل بين بروتوكول UART و جهاز الحاسب بإستعمال واجهة USB، إذن مامشكلة YP-01 ؟؟ المشكلة أنه عند خالف توقعاتي على عكس FTDI USB TO TTL Converter و CP2102 الأثتنين أعتدت عليهم على ألية التعريف بشكل مباشر بدأ من نظام تشغيل ويندوز 7 حتى 10 دون أي مشكلة، ويجدر الذكر أنني قمت بالعمل على FTDI مع نظام تشغيل ويندوز XP في محاولة مني تغيير أحد أنظمة الميزان الخاصة في مكان عملي وعمل بشكل سلس دون حاجة إلى تعريف يدوية حيث أنه عادة يحتوى محمل بملف التعاريف داخله.
أذن مشكلة YP-01 المبنى على شريحة PL2303 يحتاج إلى تعاريف لكي يعمل بشكل مباشر، الحقيقة هو لا يحتاج إلى تعاريف إلا على حسب نوعية الشريحة حيث أنه يوجد انواع حسب مايصنف موقع الشركة
حسنًا حسب التجربة أتضح أنه التعريف يعمل بشكل جيد على نظام التشغيل ويندوز 7، لكن ماذا عن 8 و 8.1 و 10، بالنسبة لي 10 لم تفلح المحاولة مع أحد تعاريف الشركة الرسمية وكان هذا الأمر محبطة من البداية حيث أنه عند محاولة التعريف ظهرت لي هذه الرسالة
وبعد التدقيق في موقع الشركة
ياللحظ السئ، أذن مالحل المناسب ؟؟ أعتقدت في بادي الأمر ربما لو أبحث عن تعاريف من إصدارات قديمة وبالأخص مبنية من نظام ويندوز فيستا و 7 قد يعمل دون مشكلة 🤔، لعلى وعسى الAPI لايزال مثل ماهو في ويندوز NT 10.
بعد البحث المطول على تعريف المناسب توقفت على أحد التعاريف ويندوز فيستا وقد عمل بصورة مناسبة دون أي مشكلة للويندوز 10
لنقوم بالتنصيب التعريف مثل أي برنامج أخر، الان بعد التنصيب لنأتي إلى عملية إختيار التعريف المناسب للPL2303، قبل التنصيب تأكد أنك قمت بإدخال PL2303 داخل جهازك الحاسب لأتمام المهمة بنجاح
قم بإختيار التعريف الأقدم من ناحية الإصدار وهو الأول مثل ماهو موضح في الصورة، بعد الإختيار ونجاح عملية إختيار التعريف المناسب قم بإخراج القطعة وإدخالها و أنظر إلى رقم المنفذ للإختبار
رقم المنفذ حسب ماموضح بالصورة هو COM10، ولنتأكد من نجاح العملية هو عن طريق أستعمال برنامج PL2303 CheckChipVersion
بعد إختيار المنفذ وعمل من تحقق إصدار الشريحة ظهر لي كما في الصورة السابقة، مما يعنى عملية التخاطب ناجحة.
من فترة كنت نشتغل على مشروع وقعدت بشكل مباشر نتعامل مع المؤشرات Pointers، في الغالب لو كنت بتبدا مسيرتك مع لغة الC المؤشرات راح تكون جنبك طالما تتعامل مع لغة الC دائمًا، عيب المؤشرات أنها صعبة الإستيعاب من البداية بحيث أنه المفهوم يعتبر جديد وغريب نسبيًا بحيث أن مع بداية التعلم الشخص لهذه لغة من بداية أو لأول مرة في عالم البرمجة بيكون المفهوم غريب بعض الشئ، على العموم هو نحاول نطرح سلسلة مقالات حول لغة C من فترة لفترة حيث أنه بنبدأ بالمؤاشرت حاليًا وجزء الأول راح يتعلق كفكرة من الأول وطرح المقالات بيكون بلغة العامية بعض الشئ لو في أي تعليق بخصوص هذا الموضوع أترك تعليق في الأسفل
لنبدأ بالمؤشرات، المؤشرات في لغة الC تعتبر جزء لا يتجزأ عنها وبالأخص أنها راح تعطيك مرونة وقوة في كتابة برنامجك، وبجانب هذا تعتبر شق مهم في إدارة الذاكرة بالنسبة للغة C، إتقانك للمؤشرات وفهمك ليهم بشكل جيد، راح يخليك تكتب برامج بلغة C بشكل كويس.
أذن ماهي المؤشرات أو ماهو المؤشر بحد ذاته، الفكرة من مؤشر هي إنك تأخذ عنوان المتغير لاحظ قلت أنه ياخذ عنوان المتغير وليس قيمة المتغير بحد ذاته، يعنى لما تحاول تسند قيمة لمتغير بطريقة هذه
int i = 10;
وكما تلاحظ محتوى المتغير راح يكون رقم 10، توا المتغير بحد ذاته في لغة الC وربما كل لغات البرمجية الأخرى، تتكون من عنوان في الذاكرة وقيمة ليحملها هذا العنوان، وتذكر أنه أسم المتغير الi أو غيره مايعنى شئ للكومبيوتر لأن يفهم بالعناوين والقيم، أسم المتغيرعادة يرمز له symbol ونفس الأمر مع اسم الدوال والstruct، الsymbol ماهي إلا مجرد تسهيل لنا نحن كمبرمجين للعمل على حل المشاكل.
بجانب أن قلت المتغير يتكون من عنوان وقيمة، نفس الأمر ينطبق على متغير من نوع مؤشر pointer، غير أن نوع القيمة ليست مثل نوع المتغير الإعتيادي يعنى مثل integer أو float أو user-defined structure، قيمته تكمن في عنوان المتغير يعنى المتغير من نوع مؤشر هو متغير يخزن قيم عناوين المتغير بالطريقة هذه
int i = 10;
int *pInt = &i;
لاحظ في المثال السابق أن علامة * دلالة على أنه المتغير من نوع مؤشر pointer ولأي نوع يأشر، يأشر لنوع integer، يعنى (pInt is pointer to int ) ، طبعًا الشئ هذا مهم ويأخذ في الحسبان بشكل كبير وراح أتطرق لسبب ليش نوع المتغير مهم في عمليات المؤشرpointer arithmetic.
توا السؤال كيف يأخذ عنوان المتغير ؟؟ تتم العملية عن طريق مايسمى بعملية referencing وبإستعمال عملية &، حيث أن ال& تعطيك عنوان المتغير في الذاكرة ويسندها للمؤشر، (يجدر الذكر أنه لا يجب الخلط بين & كعملية AND و ك& كعملية referencing حيث تستعمل الأخيرة في عملية إسناد عنوان المتغير للمؤشر أو لدالة كتمريرها كعنوان reference).
توا سؤال أخر يطرح، هل عملية التعامل مع المتغيرات من النوع مؤشر pointer مثل المتغير الإعتيادي ؟؟ الإجابة إيه ولا، كيف يعنى ؟؟
واحد من تطبيقات المؤشر pointer وهي أكثرها شيوعًا أنه يتم تمرير المتغيرات كعنوان reference، أو بالأحرى تتم عملية تبادل البيانات بسرعة بدون مايدير copying يعنى نسخ قيم المتغير، ليتضح أنه في كفاءة في التعامل مع المتغيرات والقيم الكبيرة لي تستهلك وقت في النسخ حيث أن يتم إستعمال المؤشرات بأخذ عنوان المتغير بدل مايأخذ القيمة بشكل كامل وينسخها كلها.
التعامل مع المؤشرات
التعامل مع المؤشرات عادة تنقسم لنوعين، تعامل معه كمتغير عادي أو كمؤشر، كيف تتعامل معاه كمتغير عادي؟؟ خلي نجي نشوفوا للمثال هذا
// usual variable
int i = 10;
// i لا حظ أن تم أخذ عنوان المتغير
int *iPtr = &i;
// لإستعماله كمتغير عادي أو بالأحرى تعامل معاه كأنه متغير تحتاج إلى عملية تسمى
// Dereferencing
// حيث تتيح لك أخذ قيمة المتغير أو تغيره
// i هنا تم نسخ قيمة المتغير السابق
// y إلى
int y = *iPtr;
في الكود السابق يوضح أني خزنت قيمة لكان يأشرلها المتغير iPtr للمتغير i لي y، ركز هنا أني قلت يأشرلها مش يأخذ قيمة المتغير الiPtr، المتغير iPtr بحد ذاته عنوان يعنى يأشر لعنوان معين زي عنوان الi، والi قيمتها تساوي 10، بمعنى أن الy نسخت بشكل غير مباشر قيمة الi عن طريق المؤشر iPtr بإستعمال عملية Dereferencing يعنى بإضافة * قبل المؤشر iPtr في عملية الإسناد القيمة، ركز أني قلت الإسناد، يعنى باهي لو درت هكي
int y = iPtr;
ليصير هنى بالضبط أنك تنسخ في عنوان المتغير i، هل تقدر تتلاعب بقيمة المؤشر iPtr ؟؟؟ نعم، تقدر تغير قيمة المؤشر يأما بقيمته بإستعمال Dereferencing
int i = 10;
int iPtr = &i;
*iPtr = 10*10;
// قيمة المتغير راح تتغير إلى 100 لأن لصار مثل هذه
i = 10*10;
كيف ممكن تغيرت قيمة الi ؟؟ تتفكر لما قتلك أن المتغير من نوع مؤشر يأخذ عنوان المتغير يعنى بالضبط يأِشرله ليه يعنى زي عملية ربط بينه وبين المتغير i مثل ماتقول أي تغير تديره للمتغير iPtr ينطبق على i لأن iPtr مخزن فيه عنوان المتغير i في نفس الوقت أيضًا المتغير iPtr عنده عنوان خاص بيه مثل ماموضح في الصورة
أو يمكنك تتلاعب بقيمة العنوان وتغير عنوان المؤشر لمكان أخر عن طريق يأما الإسناد أو increamting.
شن الفائدة المكتسبة من تعامل مع المتغيرة والإضافة لنستفيد منها أو يضيف حاجات جديدة لخبرتي ؟؟
الفائدة أن لما بتتعامل مع الدوال وتمرر القيم بين الدوال ممكن حي تمرر قيم من نوع structure ولهو عبارة نوع من البيانات يعرفها المتسخدم وتكون فيها أكثر من نوع وغالبًا يكون حجم الstructure كبير ولما تمرر structure معين لدالة حي يكون الأمر مستنزف للبرنامج ويأخذ وقت لنسخ على خاطر أنك بتقرأ قيم ؟؟!! لهذا المؤاشرات تعتبر حل مناسب للمشاكل هذه فقط مرر عنوان structure ودير عملية dereferencing وأقرا القيم بشكل أكفا، يعنى مرر المتغيرات كعنوان بدل كقيم ويعتبر من الgood practice في لغة الC، مع إعتبار هل أنك تحتاج تغير قيمة المتغير أو لا ،ولتفادي هذه المشكلة من المبرمج يتم إضافة const لمنع من أي تغير بشكل غير متوقع
int readFoo(const UserStruct *p);
كيفية تمرير المتغيرات للدوال وغيرها من المواضيع التي طرحها ،ستكون في المقالة القادمة أرجو أن تكون المقالة مفيدة ولو عندك تعليق خليه 😁
سابقًا في الماضي عندما كان عمري حوالي لنقل 17 عامًا ربيعًا كنت مهتم بعالم التدوين والبرامج والشروحات وأتفكر بداياتي حول منصة Blogger من قوقل و WordPress ومحاولة ترجمة ونسخ من أغلب المصادر وشرح لكن حتى تغير الأمر ما أن بدأت إنخراطي في البرمجة وبعدها معاش طلعت من العالم هذا، وفكرت أني نرجع لتدوين لكن بصورة شخصية بس أتكاسل بشكل مستمر لكن كتحدي علي إنجازه هو كتابة 10 تدوينة في السنة قد يبدو الرقم مبالغ 😂، مبالغ من منظوري لأن أتذكر أني ما أن أبدا المدونة وأكتب مقالة معاش نكمل ونتكاسل وهكذا الدواليك، لنطمح أن تكون بداية جيدة هذه المرة.
حسنًا سبب إختياري لأسم لامدا أو Lambda بالإنجليزي يرجع لإهتمامي وإنخراطي بالبرمجة الدالية Functional Programming، لست محترف فيها أو متعمق بالدرجة كبيرة بس مع الوقت أطلع على أفكارها وراح تكون في مقالات بخصوص الموضوع هذا، بجانب أنه أني مطور C بشكل مكثف حيث أني نشتغل على الهاردوير بشكل كبير واللغة هذه تعتبر Imperative بشكل كبير ومعنى أنه Imperative أنه لغة هذه تنفذ سلسلة من الأوامر بشكل متسلسل وتعاود تسند القيم للمتغيرات وألخ، قصة طووويللة لكن راح نطرق للأمر هذا لمقالات منفصلة حول لغات Imperative و Functional و OOP و Declarative، على العموم أغلب المقالات راح تكون حول الشق البرمجي والهاردوير والدوائر إلكترونية بصفة عامة وبعض من الفلسفة لا بأس بتفلسف قليلًا.