حل مشكلة الWiFi للوحة Arduino Giga R1 WiFi

مؤخرًا مع بداية شهر 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.

حيث أنه تعمل المكتبة على تحميل الملف التشغيلي لأول مرة ومن ثم استخدامها كبديل عن المكتبة الافتراضية.

رابط المكتبة: https://github.com/ahmedalkabir/GigaWiFiSolver

نقوم بتحميل المكتبة من الرابط المرفق مسبقًا، واتباع الخطوات الآتية:

ننقل مجلد المكتبة إلى مسار التالي Document/

نقوم برفع البرنامج كما موضح في الصور.

ومن ثم سيقوم البرنامج بتحميل البرنامج التشغيلي لوحدة الWiFi على ذاكرة الفلاش الخارجية.

بعد الانتهاء من البرنامج لنقوم باختبارالبرنامج WiFi المرفق مع المكتبة.

وكما يبدو واضحًا، وحدة الWiFi تعمل بشكل جيد بدون أي مشكلة، يجدر الملاحظة أنه لكي تعمل وحدة الWiFi بشكل ممتاز يجدر إضافة مكتبة GigaWiFiSolver في بداية السطر حيث ستعمل كبديل عن المكتبة الإفتراضية التي تأتي مع الحزمة.

وهكذا قمت بحل الإشكالية التي واجهتني و قريبًا ستكون هناك سلسلة مقالات حول لوحة الArduino Giga R1 والمشاريع التي سأقوم بها باستخدام هذه اللوحة.

جافا 8 و Lambda Expression

قررت البدأ بسلسلة من مقالات حول 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.

 يمكنك تضمين نوع المتغير إن أردت ذلك ، فلك الحرية الإختيار بين تضمينها أو عدم تضمينها.

        File directory = new File("./src");
        String[] names = directory.list((File dir,String name) -> name.endsWith(".java"));

وفي حالة إن lambda expression ستتطلب أكثر من السطر أو إضافة أكثر من جملة عليك إستعمال الأقواس braces و إرجاع القيمة بشكل صريح explicit return statement.