تطوير التطبيقات باستخدام واجهات برمجة تطبيقات OpenThread

1- المقدمة

26b7f4f6b3ea0700.png

إنّ OpenThread التي أصدرتها Nest هي بروتوكول مفتوح المصدر لبروتوكول شبكات Thread®. أطلقت Nest برنامج OpenThread لإتاحة التكنولوجيا المستخدمة على نطاق واسع لمنتجات مطوّري البرامج لتسريع وتيرة تطوير المنتجات للمنزل.

تحدِّد مواصفات سلاسل المحادثات بروتوكول الاتصال اللاسلكي الآمن والمستنِد إلى الطاقة والذي يعتمد على بروتوكول IPv6 والمعتمَد من جهاز إلى آخر للتطبيقات المنزلية. ينفّذ OpenThread جميع طبقات شبكات Thread بما في ذلك IPv6 و6LoWPAN وIEEE 802.15.4 مع أمان MAC وإنشاء Mesh Link وتوجيه الشبكة المتداخلة.

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

2a6db2e258c32237.png

ما ستتعرَّف عليه

  • طريقة برمجة الأزرار ومصابيح LED على لوحات Nodic nRF52840 لمطوّري البرامج
  • كيفية استخدام واجهات برمجة تطبيقات OpenThread الشائعة وفئة otInstance
  • كيفية مراقبة تغييرات حالة OpenThread والتفاعل معها
  • كيفية إرسال رسائل UDP إلى جميع الأجهزة في شبكة Thread
  • كيفية تعديل Makefiles

المتطلبات

الأجهزة:

  • 3 لوحات لـ Nodic نصف الموصّلة nRF52840
  • 3 كابلات USB إلى USB صغيرة لتوصيل الألواح
  • جهاز يعمل بنظام التشغيل Linux يتضمّن 3 منافذ USB على الأقل

البرامج:

  • سلسلة أدوات GNU
  • أدوات سطر الأوامر من Nordic nRF5x
  • برنامج Segger J-Link
  • OpenThread
  • Git

ما لم يُذكر خلاف ذلك، يخضع محتوى هذا الدرس التطبيقي حول الترميز بموجب ترخيص Creative Commons Attribution 3.0، وتم ترخيص عيّنات التعليمات البرمجية بموجب ترخيص Apache 2.0.

2. البدء

إكمال الدرس التطبيقي حول ترميز الأجهزة

قبل بدء هذا الدرس التطبيقي حول الترميز، عليك إكمال الدرس التطبيقي Create a Thread Network with nRF52840 Boards وOpenThread، وهو ما يلي:

  • تعرَّف على تفاصيل كل البرامج التي تحتاجها لبناء ووميض الصور.
  • يعلّمك كيفية إنشاء منصّة OpenThread وإمساكها في ألواح Nodic nRF52840.
  • يوضح أساسيات شبكة Thread

لا حاجة إلى إعداد أي من البيئات المطلوبة لإنشاء OpenThread وإضاءة الألواح في هذا الدرس التطبيقي حول الترميز، ولا يمكن تقديم سوى التعليمات الأساسية لتزيين ألواح المعلومات. من المفترض أنّك أكملت الدرس التطبيقي حول إنشاء شبكة Thread.

جهاز يعمل بنظام التشغيل Linux

صُمِّم هذا الدرس التطبيقي حول الترميز بحيث يستخدم جهازًا يعمل بنظام التشغيل i386 أو x86 لإضاءة جميع لوحات تطوير سلسلة المحادثات. تم اختبار جميع الخطوات على نظام التشغيل Ubuntu 14.04.5 LTS (Trusty Tahr).

لوحات 5,52840 شمالية شبه موصلّة

يستخدم هذا الدرس التطبيقي حول الترميز ثلاثة nRF52840 لوحات PDK.

a6693da3ce213856.png

تثبيت برامج

لإنشاء منصّة Openthread ووميضها، يجب تثبيت SEGGER J-Link وأدوات سطر الأوامر nRF5x وسلسلة أدوات ARM GNU ومجموعة من حزم Linux المختلفة. إذا كنت قد أكملت الدرس التطبيقي حول إنشاء شبكة Thread على النحو المطلوب، سيكون لديك كل ما تحتاج إليه لتثبيت هذا التطبيق. إذا لم يكن الأمر كذلك، يُرجى إكمال ذلك الدرس التطبيقي حول الترميز قبل المتابعة للتأكّد من أنّه يمكنك إنشاء Flash Open مستخدَمًا مع ألواح تطوير البرامج (nRF52840).

3. استنساخ المستودع

يتضمن OpenThread مثالاً على رمز التطبيق الذي يمكنك استخدامه كنقطة بداية لهذا الدرس التطبيقي حول الترميز.

استنسِخ نماذج Openسلسلة نوردك nRF528xx وأعد إنشاء OpenThread:

$ git clone --recursive https://github.com/openthread/ot-nrf528xx
$ cd ot-nrf528xx
$ ./script/bootstrap

4- أساسيات OpenThread API

تتوفّر واجهات برمجة التطبيقات العلنية لـ OpenThread ضمن مستودع ./openthread/include/openthread في مستودع OpenThread. توفّر واجهات برمجة التطبيقات هذه إمكانية الوصول إلى مجموعة متنوعة من ميزات ووظائف OpenThread على مستوى سلسلة المحادثات والنظام الأساسي للاستخدام في تطبيقاتك:

  • معلومات مثيلات OpenThread والتحكّم فيها
  • خدمات التطبيق مثل IPv6 وUDP وCoAP
  • إدارة بيانات اعتماد الشبكة، إلى جانب أدوار المفوَّض والانضمام
  • إدارة جهاز توجيه الحدود
  • الميزات المحسّنة، مثل الإشراف على الأطفال ورصد محتوى Jam

تتوفّر معلومات المراجع على جميع واجهات برمجة تطبيقات OpenThread على الرابط openthread.io/reference.

استخدام واجهة برمجة تطبيقات

لاستخدام واجهة برمجة تطبيقات، يمكنك تضمين ملف العنوان في أحد ملفات تطبيقك. وبعد ذلك، يمكنك طلب الدالة المطلوبة.

على سبيل المثال، يستخدم نموذج واجهة سطر الأوامر (CLI) المضمّن في OpenThread عناوين واجهة برمجة التطبيقات التالية:

./openthread/examples/apps/cli/main.c

#include <openthread/config.h>
#include <openthread/cli.h>
#include <openthread/diag.h>
#include <openthread/tasklet.h>
#include <openthread/platform/logging.h>

مثيل OpenThread

البنية otInstance هي السمة التي ستستخدمها بشكل متكرر عند العمل على واجهات برمجة تطبيقات OpenThread. وبعد الانتهاء من الإعداد، تمثل هذه البنية مثيلًا ثابتًا من مكتبة OpenThread وتسمح للمستخدم بإجراء طلبات بيانات من واجهة برمجة تطبيقات OpenThread.

على سبيل المثال، يتم إعداد مثيل OpenThread في دالة main() لتطبيق نموذج CLI:

./openthread/examples/apps/cli/main.c

int main(int argc, char *argv[])
{
    otInstance *instance

...

#if OPENTHREAD_ENABLE_MULTIPLE_INSTANCES
    // Call to query the buffer size
    (void)otInstanceInit(NULL, &otInstanceBufferLength);

    // Call to allocate the buffer
    otInstanceBuffer = (uint8_t *)malloc(otInstanceBufferLength);
    assert(otInstanceBuffer);

    // Initialize OpenThread with the buffer
    instance = otInstanceInit(otInstanceBuffer, &otInstanceBufferLength);
#else
    instance = otInstanceInitSingle();
#endif

...

    return 0;
}

الدوال الخاصة بالنظام الأساسي

إذا أردت إضافة دوال خاصة بالنظام الأساسي إلى أحد أمثلة التطبيقات المضمّنة في OpenThread، يمكنك أولاً الإعلان عنها في عنوان ./openthread/examples/platforms/openthread-system.h باستخدام مساحة الاسم otSys لجميع الدوال. بعد ذلك، نفِّذها في ملف مصدر خاص بالنظام الأساسي. نظرًا لذلك، يمكنك استخدام عناوين الدوال نفسها للأنظمة الأساسية الأخرى.

على سبيل المثال، يجب الإعلان عن وظائف GPIO التي سنستخدمها لربط أزرار nRF52840 ومصابيح LED في openthread-system.h.

افتح الملف ./openthread/examples/platforms/openthread-system.h في محرِّر النصوص المفضّل لديك.

./openthread/examples/platforms/openthread-system.h

الإجراء: إضافة تعريفات وظائف GPIO الخاصة بالنظام الأساسي

إضافة تعريفات الدوال هذه بعد #include في العنوان openthread/instance.h:

/**
 * Init LED module.
 *
 */
void otSysLedInit(void);
void otSysLedSet(uint8_t aLed, bool aOn);
void otSysLedToggle(uint8_t aLed);

/**
* A callback will be called when GPIO interrupts occur.
*
*/
typedef void (*otSysButtonCallback)(otInstance *aInstance);
void otSysButtonInit(otSysButtonCallback aCallback);
void otSysButtonProcess(otInstance *aInstance);

وسنطبِّقها في الخطوة التالية.

تجدر الإشارة إلى أنّ بيان الدالة otSysButtonProcess يستخدم otInstance. وبهذه الطريقة، يمكن للتطبيق الوصول إلى المعلومات حول مثيل OpenThread عند الضغط على أحد الأزرار، إذا لزم الأمر. يعتمد كل هذا على احتياجات تطبيقك. إذا لم تكن بحاجة إليها في تنفيذ الدالة، يمكنك استخدام وحدة ماكرو OT_UNUSED_VARIABLE من واجهة برمجة تطبيقات OpenThread لإزالة أخطاء الإصدار حول المتغيرات غير المستخدَمة لبعض سلاسل الأدوات. وسنعرض أمثلة على ذلك لاحقًا.

5. تنفيذ فكرة "منصة GPIO"

في الخطوة السابقة، عرضنا تعريفات الوظائف الخاصة بالنظام الأساسي في ./openthread/examples/platforms/openthread-system.h والتي يمكن استخدامها في GPIO. للوصول إلى الأزرار ومصابيح LED على لوحات إصدار nRF52840، يجب تنفيذ هذه الوظائف في النظام الأساسي nRF52840. في هذا الرمز، عليك إضافة دوال:

  • إعداد الدبابيس والأوضاع في GPIO
  • التحكم في الجهد الكهربائي على دبوس
  • تفعيل ميزة انقطاع خدمات GPIO وتسجيل معاودة اتصال

في الدليل ./src/src، أنشِئ ملفًا جديدًا باسم gpio.c. في هذا الملف الجديد، أضِف المحتوى التالي.

./src/src/gpio.c (ملف جديد)

الإجراء: إضافة تعريف

وتُعرَّف هذه الأوصاف ك ملخصات بين القيم والمتغيّرات الخاصة بـ nRF52840 المستخدَمة على مستوى تطبيق OpenThread.

/**
 * @file
 *   This file implements the system abstraction for GPIO and GPIOTE.
 *
 */

#define BUTTON_GPIO_PORT 0x50000300UL
#define BUTTON_PIN 11 // button #1

#define GPIO_LOGIC_HI 0
#define GPIO_LOGIC_LOW 1

#define LED_GPIO_PORT 0x50000300UL
#define LED_1_PIN 13 // turn on to indicate leader role
#define LED_2_PIN 14 // turn on to indicate router role
#define LED_3_PIN 15 // turn on to indicate child role
#define LED_4_PIN 16 // turn on to indicate UDP receive

لمزيد من المعلومات حول أزرار nRF52840 ومصابيح LED، يُرجى الاطّلاع على مركز بلدان الشمال الأوروبي حول موصّلات الطاقة.

الإجراء: تشمل إضافة العنوان.

بعد ذلك، أضِف العنوان الذي ستحتاج إليه لوظيفة GPIO.

/* Header for the functions defined here */
#include "openthread-system.h"

#include <string.h>

/* Header to access an OpenThread instance */
#include <openthread/instance.h>

/* Headers for lower-level nRF52840 functions */
#include "platform-nrf5.h"
#include "hal/nrf_gpio.h"
#include "hal/nrf_gpiote.h"
#include "nrfx/drivers/include/nrfx_gpiote.h"

الإجراء: إضافة دوال معاودة الاتصال والمقاطعة للزر 1.

أضِف هذا الرمز بعد ذلك. دالة in_pin1_handler هي معاودة الاتصال المسجّلة عند إعداد وظيفة الضغط على الزر (لاحقًا في هذا الملف).

لاحظ كيفية استخدام رد الاتصال هذا وحدة ماكرو OT_UNUSED_VARIABLE، حيث إن المتغيّرات التي تم تمريرها إلى in_pin1_handler لا يتم استخدامها في الدالة.

/* Declaring callback function for button 1. */
static otSysButtonCallback sButtonHandler;
static bool                sButtonPressed;

/**
 * @brief Function to receive interrupt and call back function
 * set by the application for button 1.
 *
 */
static void in_pin1_handler(uint32_t pin, nrf_gpiote_polarity_t action)
{
    OT_UNUSED_VARIABLE(pin);
    OT_UNUSED_VARIABLE(action);
    sButtonPressed = true;
}

الإجراء: إضافة دالة لإعداد مؤشرات LED.

يمكنك إضافة هذا الرمز لضبط وضع مصابيح LED وحالة كل منها أثناء عملية الإعداد.

/**
 * @brief Function for configuring: PIN_IN pin for input, PIN_OUT pin for output,
 * and configures GPIOTE to give an interrupt on pin change.
 */

void otSysLedInit(void)
{
    /* Configure GPIO mode: output */
    nrf_gpio_cfg_output(LED_1_PIN);
    nrf_gpio_cfg_output(LED_2_PIN);
    nrf_gpio_cfg_output(LED_3_PIN);
    nrf_gpio_cfg_output(LED_4_PIN);

    /* Clear all output first */
    nrf_gpio_pin_write(LED_1_PIN, GPIO_LOGIC_LOW);
    nrf_gpio_pin_write(LED_2_PIN, GPIO_LOGIC_LOW);
    nrf_gpio_pin_write(LED_3_PIN, GPIO_LOGIC_LOW);
    nrf_gpio_pin_write(LED_4_PIN, GPIO_LOGIC_LOW);

    /* Initialize gpiote for button(s) input.
     Button event handlers are set in the application (main.c) */
    ret_code_t err_code;
    err_code = nrfx_gpiote_init();
    APP_ERROR_CHECK(err_code);
}

الإجراء: إضافة دالة لضبط وضع مؤشر LED.

سيتم استخدام هذه الدالة عند تغيير دور الجهاز.

/**
 * @brief Function to set the mode of an LED.
 */

void otSysLedSet(uint8_t aLed, bool aOn)
{
    switch (aLed)
    {
    case 1:
        nrf_gpio_pin_write(LED_1_PIN, (aOn == GPIO_LOGIC_HI));
        break;
    case 2:
        nrf_gpio_pin_write(LED_2_PIN, (aOn == GPIO_LOGIC_HI));
        break;
    case 3:
        nrf_gpio_pin_write(LED_3_PIN, (aOn == GPIO_LOGIC_HI));
        break;
    case 4:
        nrf_gpio_pin_write(LED_4_PIN, (aOn == GPIO_LOGIC_HI));
        break;
    }
}

الإجراء: إضافة دالة لتبديل وضع مؤشر LED.

سيتم استخدام هذه الدالة لتبديل LED 4 عندما يتلقّى الجهاز رسالة UDP متعددة البث.

/**
 * @brief Function to toggle the mode of an LED.
 */
void otSysLedToggle(uint8_t aLed)
{
    switch (aLed)
    {
    case 1:
        nrf_gpio_pin_toggle(LED_1_PIN);
        break;
    case 2:
        nrf_gpio_pin_toggle(LED_2_PIN);
        break;
    case 3:
        nrf_gpio_pin_toggle(LED_3_PIN);
        break;
    case 4:
        nrf_gpio_pin_toggle(LED_4_PIN);
        break;
    }
}

الإجراء: أضِف بعض الدوال لإعداد عمليات الضغط على الأزرار ومعالجتها.

تضبط الدالة الأولى اللوحة للضغط على الزر، وترسل الدالة الثانية رسالة UDP متعددة البث عند الضغط على الزر 1.

/**
 * @brief Function to initialize the button.
 */
void otSysButtonInit(otSysButtonCallback aCallback)
{
    nrfx_gpiote_in_config_t in_config = NRFX_GPIOTE_CONFIG_IN_SENSE_LOTOHI(true);
    in_config.pull                    = NRF_GPIO_PIN_PULLUP;

    ret_code_t err_code;
    err_code = nrfx_gpiote_in_init(BUTTON_PIN, &in_config, in_pin1_handler);
    APP_ERROR_CHECK(err_code);

    sButtonHandler = aCallback;
    sButtonPressed = false;

    nrfx_gpiote_in_event_enable(BUTTON_PIN, true);
}

void otSysButtonProcess(otInstance *aInstance)
{
    if (sButtonPressed)
    {
        sButtonPressed = false;
        sButtonHandler(aInstance);
    }
}

الإجراء: يتم حفظ ملف gpio.c وإغلاقه.

6- واجهة برمجة التطبيقات: التفاعل مع تغييرات أدوار الأجهزة

في تطبيقنا، نريد أن تكون مؤشرات LED مختلفة حسب أداء الجهاز. لنتتبّع الأدوار التالية: القائد أو جهاز التوجيه أو الجهاز النهائي. ويمكننا تخصيصهما لمصابيح LED، كما يلي:

  • مؤشر LED = قائد
  • إضاءة LED = جهاز توجيه
  • مصباح LED 3 = الجهاز النهائي

لتفعيل هذه الوظيفة، يجب أن يعرف التطبيق متى تغيّر دور الجهاز وكيفية تشغيل ضوء LED الصحيح. سنستخدم مثيل OpenThread في الجزء الأول، وبيان النظام الأساسي GPIO في الثانية.

افتح الملف ./openthread/examples/apps/cli/main.c في محرِّر النصوص المفضّل لديك.

./openthread/examples/apps/cli/main.c

الإجراء: تشمل إضافة العنوان.

في القسم ويتضمن ملف main.c، أضف ملفات رأس واجهة برمجة التطبيقات التي ستحتاج إليها لميزة تغيير الدور.

#include <openthread/instance.h>
#include <openthread/thread.h>
#include <openthread/thread_ftd.h>

الإجراء: إضافة بيان دالة المعالج لتغيير حالة مثيل OpenThread.

أضِف هذا البيان إلى main.c، بعد أن يتضمّن العنوان أي عبارات #if وقبلها. سيتم تحديد هذه الدالة بعد التطبيق الرئيسي.

void handleNetifStateChanged(uint32_t aFlags, void *aContext);

الإجراء: إضافة تسجيل معاودة الاتصال لدالة معالج تغيير الحالة.

في main.c، أضِف هذه الدالة إلى الدالة main() بعد استدعاء otAppCliInit. يؤدي تسجيل معاودة الاتصال هذا إلى إبلاغ OpenThread باستدعاء الدالة handleNetifStateChange كلما تغيّرت حالة OpenThread.

/* Register Thread state change handler */
otSetStateChangedCallback(instance, handleNetifStateChanged, instance);

الإجراء: إضافة عملية تنفيذ تغيير الحالة

في الدالة main.c بعد الدالة main()، طبِّق الدالة handleNetifStateChanged. تتحقّق هذه الدالة من علامة OT_CHANGED_THREAD_ROLE لمثيل OpenThread، وإذا تم تغييرها، يتم تفعيل مؤشرات LED أو إيقافها حسب الحاجة.

void handleNetifStateChanged(uint32_t aFlags, void *aContext)
{
   if ((aFlags & OT_CHANGED_THREAD_ROLE) != 0)
   {
       otDeviceRole changedRole = otThreadGetDeviceRole(aContext);

       switch (changedRole)
       {
       case OT_DEVICE_ROLE_LEADER:
           otSysLedSet(1, true);
           otSysLedSet(2, false);
           otSysLedSet(3, false);
           break;

       case OT_DEVICE_ROLE_ROUTER:
           otSysLedSet(1, false);
           otSysLedSet(2, true);
           otSysLedSet(3, false);
           break;

       case OT_DEVICE_ROLE_CHILD:
           otSysLedSet(1, false);
           otSysLedSet(2, false);
           otSysLedSet(3, true);
           break;

       case OT_DEVICE_ROLE_DETACHED:
       case OT_DEVICE_ROLE_DISABLED:
           /* Clear LED4 if Thread is not enabled. */
           otSysLedSet(4, false);
           break;
        }
    }
}

7- واجهة برمجة التطبيقات: استخدام البث المتعدد لتفعيل ضوء LED

في تطبيقنا، نريد أيضًا إرسال رسائل UDP إلى جميع الأجهزة الأخرى في الشبكة عند الضغط على الزر 1 في لوح واحد. لتأكيد استلام الرسالة، سيتم تبديل ضوء LED4 للّوحات الأخرى استجابةً لذلك.

لتفعيل هذه الوظيفة، يحتاج التطبيق إلى:

  • إعداد اتصال UDP عند التشغيل
  • التمكّن من إرسال رسالة UDP إلى عنوان البث المتعدد المحلي للشبكة المتداخلة
  • التعامل مع رسائل UDP الواردة
  • تبديل ضوء LED 4 استجابةً لرسائل UDP الواردة

افتح الملف ./openthread/examples/apps/cli/main.c في محرِّر النصوص المفضّل لديك.

./openthread/examples/apps/cli/main.c

الإجراء: تشمل إضافة العنوان.

في القسم "تضمين" في أعلى الملف main.c، أضِف ملفات واجهة برمجة التطبيقات التي ستحتاج إليها لميزة البث المتعدد UDP.

#include <string.h>

#include <openthread/message.h>
#include <openthread/udp.h>

#include "utils/code_utils.h"

يُستخدَم الرأس code_utils.h لوحدات الماكرو otEXPECT وotEXPECT_ACTION التي تتحقّق من شروط وقت التشغيل وتعالج الأخطاء بنجاح.

الإجراء: إضافة التعريفات والثوابت:

في ملف main.c، بعد القسمIncludes وقبل أي عبارة #if، أضِف ثوابت خاصة ببروتوكول UDP وعرِّفها:

#define UDP_PORT 1212

static const char UDP_DEST_ADDR[] = "ff03::1";
static const char UDP_PAYLOAD[]   = "Hello OpenThread World!";

ff03::1 هو عنوان البث المتعدد للشبكات المتداخلة. سيتم إرسال أي رسائل يتم إرسالها إلى هذا العنوان إلى جميع أجهزة سلسلة المحادثات الكاملة في الشبكة. يُرجى الاطِّلاع على البث المتعدد على Openthread.io للحصول على مزيد من المعلومات عن دعم البث المتعدد في OpenThread.

الإجراء: إضافة تعريفات الوظائف

في ملف main.c، بعد التعريف otTaskletsSignalPending وقبل الدالة main()، أضِف دوال خاصة ببروتوكول UDP، بالإضافة إلى متغيّر ثابت لتمثيل مقبس UDP:

static void initUdp(otInstance *aInstance);
static void sendUdp(otInstance *aInstance);

static void handleButtonInterrupt(otInstance *aInstance);

void handleUdpReceive(void *aContext, otMessage *aMessage, 
                      const otMessageInfo *aMessageInfo);

static otUdpSocket sUdpSocket;

الإجراء: إضافة مكالمات لإعداد مصابيح LED وزر GPIO.

في main.c، يمكنك إضافة استدعاءات الدوال هذه إلى الدالة main() بعد استدعاء otSetStateChangedCallback. تعمل هذه الدوال على إعداد دبابيس GPIO وGPIOTE وضبط معالِج زر للتعامل مع أحداث النقر على الزر.

/* init GPIO LEDs and button */
otSysLedInit();
otSysButtonInit(handleButtonInterrupt);

الإجراء: إضافة مكالمة إعداد UDP.

في main.c، أضِف هذه الدالة إلى الدالة main() بعد استدعاء otSysButtonInit الذي أضفته للتو:

initUdp(instance);

تضمن هذه المكالمة إعداد مقبس UDP عند بدء تشغيل التطبيق. وبدون ذلك، لا يمكن للجهاز إرسال رسائل UDP أو استلامها.

الإجراء: إضافة مكالمة لمعالجة حدث زر GPIO.

في main.c، أضِف استدعاء الدالة هذا إلى الدالة main() بعد استدعاء otSysProcessDrivers في حلقة while. تتحقّق هذه الدالة في gpio.c مما إذا كان قد تم الضغط على الزر. وإذا كان الأمر كذلك، يتم طلب المعالج (handleButtonInterrupt) الذي تم ضبطه في الخطوة أعلاه.

otSysButtonProcess(instance);

الإجراء: تنفيذ المعالج "قاطع الأزرار"

في main.c، أضِف تنفيذ الدالة handleButtonInterrupt بعد دالة handleNetifStateChanged التي أضفتها في الخطوة السابقة:

/**
 * Function to handle button push event
 */
void handleButtonInterrupt(otInstance *aInstance)
{
    sendUdp(aInstance);
}

الإجراء: تنفيذ إعداد UDP.

في الدالة main.c، أضِف تنفيذ الدالة initUdp بعد دالة handleButtonInterrupt التي أضفتها للتو:

/**
 * Initialize UDP socket
 */
void initUdp(otInstance *aInstance)
{
    otSockAddr  listenSockAddr;

    memset(&sUdpSocket, 0, sizeof(sUdpSocket));
    memset(&listenSockAddr, 0, sizeof(listenSockAddr));

    listenSockAddr.mPort    = UDP_PORT;

    otUdpOpen(aInstance, &sUdpSocket, handleUdpReceive, aInstance);
    otUdpBind(aInstance, &sUdpSocket, &listenSockAddr, OT_NETIF_THREAD);
}

UDP_PORT هو المنفذ الذي حدّدته في السابق (1212). تفتح دالة otUdpOpen المقبس وتسجّل دالة رد اتصال (handleUdpReceive) عند استلام رسالة UDP. يربط otUdpBind المقبس بواجهة شبكة Thread عبر تمرير OT_NETIF_THREAD. بالنسبة إلى خيارات واجهة الشبكة الأخرى، يمكنك الرجوع إلى التعداد otNetifIdentifier في مرجع واجهة برمجة التطبيقات لبروتوكول UDP.

الإجراء: تنفيذ مراسلة UDP

في الدالة main.c، أضِف تنفيذ الدالة sendUdp بعد دالة initUdp التي أضفتها للتو:

/**
 * Send a UDP datagram
 */
void sendUdp(otInstance *aInstance)
{
    otError       error = OT_ERROR_NONE;
    otMessage *   message;
    otMessageInfo messageInfo;
    otIp6Address  destinationAddr;

    memset(&messageInfo, 0, sizeof(messageInfo));

    otIp6AddressFromString(UDP_DEST_ADDR, &destinationAddr);
    messageInfo.mPeerAddr    = destinationAddr;
    messageInfo.mPeerPort    = UDP_PORT;

    message = otUdpNewMessage(aInstance, NULL);
    otEXPECT_ACTION(message != NULL, error = OT_ERROR_NO_BUFS);

    error = otMessageAppend(message, UDP_PAYLOAD, sizeof(UDP_PAYLOAD));
    otEXPECT(error == OT_ERROR_NONE);

    error = otUdpSend(aInstance, &sUdpSocket, message, &messageInfo);

 exit:
    if (error != OT_ERROR_NONE && message != NULL)
    {
        otMessageFree(message);
    }
}

دوِّن وحدات الماكرو otEXPECT وotEXPECT_ACTION. وتضمن هذه الشهادات أن رسالة UDP صالحة وأن يتم تخصيصها بشكل صحيح في المخزن المؤقت، وإذا لم تكن تعمل بشكل صحيح، ستتعامل الدالة مع الأخطاء بشكل صحيح من خلال الانتقال إلى كتلة exit، حيث يمكنك إخلاء المخزن المؤقت.

اطّلِع على مراجع IPv6 وUDP على Openthread.io لمزيد من المعلومات حول الدوال المستخدَمة لإعداد UDP.

الإجراء: تنفيذ معالجة رسائل UDP

في الدالة main.c، أضِف تنفيذ الدالة handleUdpReceive بعد دالة sendUdp التي أضفتها للتو. وتعمل هذه الدالة على التبديل من LED.

/**
 * Function to handle UDP datagrams received on the listening socket
 */
void handleUdpReceive(void *aContext, otMessage *aMessage,
                      const otMessageInfo *aMessageInfo)
{
    OT_UNUSED_VARIABLE(aContext);
    OT_UNUSED_VARIABLE(aMessage);
    OT_UNUSED_VARIABLE(aMessageInfo);

    otSysLedToggle(4);
}

8- واجهة برمجة التطبيقات: ضبط شبكة Thread

لتسهيل العرض، نريد من أجهزةنا بدء سلسلة المحادثات على الفور والانضمام إلى شبكة عند تشغيلها. ولإجراء ذلك، سنستخدم بنية otOperationalDataset. تحتفظ هذه البنية بجميع المعلّمات اللازمة لنقل بيانات اعتماد شبكة Thread إلى جهاز.

سيؤدي استخدام هذه البنية إلى إلغاء الإعدادات التلقائية للشبكة المدمجة في OpenThread، لجعل تطبيقنا أكثر أمانًا وقصر عُقد Thread في شبكتنا على تلك التي تشغِّل التطبيق فقط.

مرة أخرى، افتح الملف ./openthread/examples/apps/cli/main.c في محرِّر النصوص المفضّل لديك.

./openthread/examples/apps/cli/main.c

الإجراء: أضِف عنوانًا يتضمّن العنوان.

ضمن القسم Include في أعلى ملف main.c، أضِف ملف عنوان واجهة برمجة التطبيقات الذي ستحتاج إليه لضبط شبكة Thread:

#include <openthread/dataset_ftd.h>

الإجراء: إضافة بيان الدالة لضبط إعدادات الشبكة

أضِف هذا البيان إلى main.c، بعد أن يتضمّن العنوان أي عبارات #if وقبلها. سيتم تحديد هذه الدالة بعد وظيفة التطبيق الرئيسية.

static void setNetworkConfiguration(otInstance *aInstance);

الإجراء: إضافة استدعاء إعدادات الشبكة.

في main.c، أضِف استدعاء الدالة هذا إلى الدالة main() بعد استدعاء otSetStateChangedCallback. تضبط هذه الدالة مجموعة بيانات شبكة Thread.

/* Override default network credentials */
setNetworkConfiguration(instance);

الإجراء: إضافة طلبات تفعيل لواجهة شبكة Thread وحزمة الإجراءات

في main.c، يمكنك إضافة استدعاءات الدوال هذه إلى الدالة main() بعد استدعاء otSysButtonInit.

/* Start the Thread network interface (CLI cmd > ifconfig up) */
otIp6SetEnabled(instance, true);

/* Start the Thread stack (CLI cmd > thread start) */
otThreadSetEnabled(instance, true);

الإجراء: تنفيذ إعدادات شبكة Thread

في main.c، أضِف تنفيذ الدالة setNetworkConfiguration بعد الدالة main():

/**
 * Override default network settings, such as panid, so the devices can join a
 network
 */
void setNetworkConfiguration(otInstance *aInstance)
{
    static char          aNetworkName[] = "OTCodelab";
    otOperationalDataset aDataset;

    memset(&aDataset, 0, sizeof(otOperationalDataset));

    /*
     * Fields that can be configured in otOperationDataset to override defaults:
     *     Network Name, Mesh Local Prefix, Extended PAN ID, PAN ID, Delay Timer,
     *     Channel, Channel Mask Page 0, Network Key, PSKc, Security Policy
     */
    aDataset.mActiveTimestamp.mSeconds             = 1;
    aDataset.mActiveTimestamp.mTicks               = 0;
    aDataset.mActiveTimestamp.mAuthoritative       = false;
    aDataset.mComponents.mIsActiveTimestampPresent = true;

    /* Set Channel to 15 */
    aDataset.mChannel                      = 15;
    aDataset.mComponents.mIsChannelPresent = true;

    /* Set Pan ID to 2222 */
    aDataset.mPanId                      = (otPanId)0x2222;
    aDataset.mComponents.mIsPanIdPresent = true;

    /* Set Extended Pan ID to C0DE1AB5C0DE1AB5 */
    uint8_t extPanId[OT_EXT_PAN_ID_SIZE] = {0xC0, 0xDE, 0x1A, 0xB5, 0xC0, 0xDE, 0x1A, 0xB5};
    memcpy(aDataset.mExtendedPanId.m8, extPanId, sizeof(aDataset.mExtendedPanId));
    aDataset.mComponents.mIsExtendedPanIdPresent = true;

    /* Set network key to 1234C0DE1AB51234C0DE1AB51234C0DE */
    uint8_t key[OT_NETWORK_KEY_SIZE] = {0x12, 0x34, 0xC0, 0xDE, 0x1A, 0xB5, 0x12, 0x34, 0xC0, 0xDE, 0x1A, 0xB5, 0x12, 0x34, 0xC0, 0xDE};
    memcpy(aDataset.mNetworkKey.m8, key, sizeof(aDataset.mNetworkKey));
    aDataset.mComponents.mIsNetworkKeyPresent = true;

    /* Set Network Name to OTCodelab */
    size_t length = strlen(aNetworkName);
    assert(length <= OT_NETWORK_NAME_MAX_SIZE);
    memcpy(aDataset.mNetworkName.m8, aNetworkName, length);
    aDataset.mComponents.mIsNetworkNamePresent = true;

    otDatasetSetActive(aInstance, &aDataset);
    /* Set the router selection jitter to override the 2 minute default.
       CLI cmd > routerselectionjitter 20
       Warning: For demo purposes only - not to be used in a real product */
    uint8_t jitterValue = 20;
    otThreadSetRouterSelectionJitter(aInstance, jitterValue);
}

كما هو موضّح في الدالة، معلَمات شبكة Thread التي نستخدمها لهذا التطبيق هي:

  • القناة = 15
  • رقم PAN = 0x2222
  • رقم تعريف PAN الموسَّع = C0DE1AB5C0DE1AB5
  • مفتاح الشبكة = 1234C0DE1AB51234C0DE1AB51234C0DE
  • اسم الشبكة = OTCodelab

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

9. واجهة برمجة التطبيقات: الدوال المحظورة

تعمل بعض واجهات برمجة تطبيقات OpenThread على تعديل الإعدادات التي يجب تعديلها فقط لأغراض العرض التوضيحي أو الاختبار. يجب عدم استخدام واجهات برمجة التطبيقات هذه في نشر إنتاج تطبيق باستخدام OpenThread.

على سبيل المثال، تضبط دالة otThreadSetRouterSelectionJitter الوقت (بالثواني) الذي تستغرقه الجهاز النهائي للترويج لنفسه إلى جهاز توجيه. الإعداد التلقائي لهذه القيمة هو 120، وفقًا لمواصفات سلاسل المحادثات. لتسهيل الاستخدام في هذا الدرس التطبيقي حول الترميز، سنغيّره إلى 20 حرفًا، لذا ليس عليك الانتظار لفترة طويلة حتى تتغيّر عقدة سلسلة المحادثات.

ملاحظة: لا تصبح أجهزة MTD أجهزة توجيه، ولا يتم تضمين الدعم لوظيفة مثل otThreadSetRouterSelectionJitter في إصدار MTD. علينا لاحقًا تحديد خيار CMake -DOT_MTD=OFF، وإلّا سنواجه خطأ في الإصدار.

يمكنك التأكّد من ذلك من خلال الاطّلاع على تعريف دالة otThreadSetRouterSelectionJitter، المضمّن في توجيه المعالج المسبق للنطاق OPENTHREAD_FTD:

./openthread/src/core/api/thread_ftd_api.cpp

#if OPENTHREAD_FTD

#include <openthread/thread_ftd.h>

...

void otThreadSetRouterSelectionJitter(otInstance *aInstance, uint8_t aRouterJitter)
{
    Instance &instance = *static_cast<Instance *>(aInstance);

    instance.GetThreadNetif().GetMle().SetRouterSelectionJitter(aRouterJitter);
}

...

#endif // OPENTHREAD_FTD

10. إجراء تعديلات

قبل إنشاء تطبيقك، هناك بعض التعديلات الطفيفة المطلوبة لثلاثة ملفات Cmake. ويستخدمها نظام الإصدار لتجميع تطبيقك وربطه.

./three_party/NordicSemiconcmdor/CMakeLists.txt

يمكنك الآن إضافة بعض العلامات إلى NordicSemicon Connectoror CMakeLists.txt، لضمان تحديد دوال GPIO في التطبيق.

الإجراء: إضافة علامات إلى ملفCMakeLists.txt.

افتح ./third_party/NordicSemiconductor/CMakeLists.txt في محرِّر النصوص المفضّل لديك، وأضِف الأسطر التالية في القسم COMMON_FLAG.

...
set(COMMON_FLAG
    -DSPIS_ENABLED=1
    -DSPIS0_ENABLED=1
    -DNRFX_SPIS_ENABLED=1
    -DNRFX_SPIS0_ENABLED=1
    ...

    # Defined in ./third_party/NordicSemiconductor/nrfx/templates/nRF52840/nrfx_config.h
    -DGPIOTE_ENABLED=1
    -DGPIOTE_CONFIG_IRQ_PRIORITY=7
    -DGPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS=1
)

...

./src/CMakeLists.txt

عدِّل ملف ./src/CMakeLists.txt لإضافة ملف المصدر الجديد gpio.c:

الإجراء: أضِف مصدر gpio إلى ملف ./src/CMakeLists.txt.

افتح ./src/CMakeLists.txt في محرِّر النصوص المفضّل لديك، وأضِف الملف إلى القسم NRF_COMM_SOURCES.

...

set(NRF_COMM_SOURCES
  ...
  src/gpio.c
  ...
)

...

./three_party/NordicSemiconcmdor/CMakeLists.txt

وأخيرًا، أضِف ملف برنامج التشغيل nrfx_gpiote.c إلى ملف NordicSemiconconnectoror CMakeLists.txt، بحيث يتم تضمينه مع إصدار مكتبة برامج التشغيل Nordic.

الإجراء: أضِف برنامج تشغيل gpio إلى ملف NordicSemiconconnectoror CMakeLists.txt .

افتح ./third_party/NordicSemiconductor/CMakeLists.txt في محرِّر النصوص المفضّل لديك، وأضِف الملف إلى القسم COMMON_SOURCES.

...

set(COMMON_SOURCES
  ...
  nrfx/drivers/src/nrfx_gpiote.c
  ...
)
...

11- إعداد الأجهزة

بعد الانتهاء من إجراء جميع عمليات تحديث الرموز، أنت جاهز لإنشاء التطبيق وعرضه في جميع لوحات Nordic nRF52840 لمطوّري البرامج. سيعمل كل جهاز كجهاز سلسلة محادثات كاملة (FTD).

إنشاء سلسلة محادثات

أنشِئ برامج ثنائية لـ OpenThread FTD لنظام nRF52840.

$ cd ~/ot-nrf528xx
$ ./script/build nrf52840 UART_trans -DOT_MTD=OFF -DOT_APP_RCP=OFF -DOT_RCP=OFF

انتقِل إلى الدليل باستخدام البرنامج الثنائي لسلسلة Openسلسلة FTD CLI، وحوِّله إلى تنسيق ست عشري باستخدام سلسلة ARM المضمّنة:

$ cd build/bin
$ arm-none-eabi-objcopy -O ihex ot-cli-ftd ot-cli-ftd.hex

ألواح فلاش

شغِّل ملف ot-cli-ftd.hex مع كل لوح nRF52840.

وصِّل كابل USB بمنفذ تصحيح أخطاء USB صغير بجانب دبوس الطاقة الخارجي على لوحة nRF52840، ثم وصِّله بجهاز Linux. اضبط LED5 بشكل صحيح.

20a3b4b480356447.png

وكما في السابق، لاحظ الرقم التسلسلي لللوحة nRF52840:

c00d519ebec7e5f0.jpeg

انتقِل إلى موقع أدوات سطر الأوامر nRFx، وأدرِج ملف سداسي عشري لسلسلة OpenThread CLI FTD على اللوح nRF52840، باستخدام الرقم التسلسلي لللوحة:

$ cd ~/nrfjprog
$ ./nrfjprog -f nrf52 -s 683704924 --verify --chiperase --program \
       ~/openthread/output/nrf52840/bin/ot-cli-ftd.hex --reset

سيتم إيقاف مؤشر LED5 لفترة قصيرة أثناء حدوث الوميض. يتم إنشاء الناتج التالي عند النجاح:

Parsing hex file.
Erasing user available code and UICR flash areas.
Applying system reset.
Checking that the area to write is not protected.
Programing device.
Applying system reset.
Run.

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

nrfjprog أمر وامض.

في حال نجاح هذا الإجراء، سيُضاء كل من LED 1 أو LED 2 أو LED3 على كل لوح. قد ترى أيضًا مفتاح إضاءة LED المضاءة من 3 إلى 2 (أو 2 إلى 1) بعد الوميض مباشرةً (ميزة تغيير دور الجهاز).

12- وظائف التطبيق

يجب الآن تشغيل جميع اللوحات الثلاث المزوّدة بتقنية nRF52840 وتشغيلها على تطبيق OpenThread. كما هو موضّح سابقًا، يحتوي هذا التطبيق على ميزتَين أساسيتَين.

مؤشرات أدوار الأجهزة

يعكس مؤشر LED المضاء على كل لوح الدور الحالي للعقدة Thread:

  • مؤشر LED = قائد
  • إضاءة LED = جهاز توجيه
  • مصباح LED 3 = الجهاز النهائي

مع تغيُّر الدور، يتغير أيضًا مؤشر LED المضاء. من المفترض أن تكون قد ظهرت لك هذه التغييرات على لوح أو اثنين في غضون 20 ثانية عندما يتم تشغيل الجهاز.

البث باستخدام بروتوكول UDP

عند الضغط على الزر 1 في لوح، يتم إرسال رسالة UDP إلى عنوان البث المتعدد المحلي للشبكة المتداخلة، والذي يتضمّن جميع العُقد الأخرى في شبكة Thread. استجابةً لتلقّي هذه الرسالة، يتم تفعيل L44 على جميع اللوحات الأخرى أو إيقافه، وتظل لوحة مفاتيح LED 4 مفعّلة أو غير مفعّلة لكل لوح إلى أن تتلقّى رسالة UDP أخرى.

203dd094acca1f97.png

9bbd96d9b1c63504.png

13- عرض توضيحي: ملاحظة تغييرات في أدوار الجهاز

إنّ الأجهزة التي تتضمّن فلاشًا هي نوع معيّن من الأجهزة المزوّدة بسلسلة محادثات كاملة (FTD) والتي تُعرف باسم "جهاز النهاية المؤهَّل لاستخدام جهاز التوجيه" (REED). وهذا يعني أنّها قد تكون جزءًا من جهاز توجيه أو جهاز نهاية، ويمكنها الترويج لنفسها من جهاز نهائي إلى جهاز توجيه.

يمكن أن تتيح سلسلة المحادثات ما يصل إلى 32 جهاز توجيه، ولكنها تحاول إبقاء عدد أجهزة التوجيه بين 16 و23 جهازًا. إذا أرفق ملف REED كجهاز نهائي وكان عدد أجهزة التوجيه أقل من 16 جهازًا، سيتم تلقائيًا ترويج نفسه لجهاز التوجيه. من المفترض أن يتم إجراء هذا التغيير في وقت عشوائي خلال عدد الثواني الذي تضبط فيه قيمة otThreadSetRouterSelectionJitter في التطبيق (20 ثانية).

تتضمن كل شبكة Thread أيضًا قائدًا، وهو جهاز توجيه مسؤول عن إدارة مجموعة أجهزة التوجيه في شبكة Thread. بعد تفعيل جميع الأجهزة بعد مرور 20 ثانية، يجب أن يكون أحدها جهازًا رائدًا (مُفعَّلاً بمعيار LED1) وأن يكون الجهازان الآخران هما جهازَي التوجيه (LED2).

4e1e885861a66570.png

إزالة القائد

في حال إزالة "القائد" من شبكة Thread، يحيل جهاز توجيه مختلف نفسه إلى "قائد"، ما يضمن حصول الشبكة على قائد.

أوقِف "لوحة الصدارة" (لوحة التحكّم المضاءة بإضاءة LED 1) باستخدام مفتاح التحكّم التشغيل. انتظِر لمدة 20 ثانية تقريبًا. على أحد لوحتين متبقّيَين، سيتم إيقاف ضوء LED 2 (جهاز التوجيه) وسيتم تشغيل ضوء LED 1 (لوحة الصدارة). أصبح هذا الجهاز الآن رائدًا في شبكة Thread.

4c57c87adb40e0e3.png

إعادة تفعيل "لوحة الصدارة" الأصلية من المفترض أن تتم تلقائيًا إعادة الانضمام إلى شبكة Thread كجهاز نهائي (LED3 مضاء). في غضون 20 ثانية (أي عدم استقرار اختيار جهاز التوجيه)، يتم الترويج نفسه لجهاز التوجيه (LED2).

5f40afca2dcc4b5b.png

إعادة ضبط اللوحات

يمكنك إيقاف جميع اللوحات الثلاث، ثم إعادة تشغيلها مرة أخرى ومراقبة مؤشرات LED. يجب أن يبدأ أول لوح تم تشغيله، وذلك في دور "القائد" (تُضاء شاشة LED1)، وتحوّل جهاز التوجيه الأول في شبكة Thread تلقائيًا إلى "قائد".

تعمل اللوحتان الأخريان على الاتصال بالشبكة في البداية عندما تكون الأجهزة النهائية مضاءة (LED3)، ولكن يجب أن تروّجان لنفسهما لأجهزة التوجيه (LED2 مضاءة) في غضون 20 ثانية.

أقسام الشبكة

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

إنّ سلسلة المحادثات هي علاج ذاتي، لذا يجب أن يتم دمج الأقسام في نهاية المطاف في قسم واحد مع "قائد واحد".

14- إصدار تجريبي: إرسال بث متعدد UDP

في حال المتابعة من التمرين السابق، يجب عدم إضاءة LED4 على أي جهاز.

اختَر أي لوح واضغط على الزر 1. من المفترض أن يبدّل مؤشر LED4 جميع اللوحات الأخرى في شبكة Thread التي تشغّل التطبيق من حالتها. في حال مواصلة التمرين السابق، من المفترض أن يكون مفعّلاً.

f186a2618fdbe3fd.png

اضغط على الزر 1 لللوحة نفسها مرة أخرى. من المفترض أن يُبدّل ضوء LED 4 على جميع اللوحات الأخرى.

اضغط على الزر 1 في لوح آخر ولاحظ كيفية تبديل مصباح LED4 إلى ألواح أخرى. اضغط على الزر 1 على أحد اللوحات التي تكون فيها تقنية LED 4 مفعّلة حاليًا. يظل مصباح LED 4 مفعَّلاً على تلك اللوحة، ولكنّه يشغِّل الآخرين.

f5865ccb8ab7aa34.png

أقسام الشبكة

في حال تقسيم ألواحك وكان هناك أكثر من قائد واحد من بينها، تختلف نتيجة رسالة البث المتعدد بين اللوحات. إذا نقرت على الزر 1 في لوح تم تقسيمه (وبالتالي هو العضو الوحيد في شبكة Thread المُقسَّمة)، لن يضيء مصباح LED 4 في اللوحات الأخرى استجابةً له. إذا حدث ذلك، فأعِد ضبط اللوحة. ومن المفترض أن يؤدي ذلك إلى تعديل شبكة Thread واحدة ومن المفترض أن تعمل رسائل UDP بشكل صحيح.

15. تهانينا.

لقد أنشأت تطبيقًا يستخدم OpenThread API.

أنت تعلم الآن:

  • طريقة برمجة الأزرار ومصابيح LED على لوحات Nodic nRF52840 لمطوّري البرامج
  • كيفية استخدام واجهات برمجة تطبيقات OpenThread الشائعة وفئة otInstance
  • كيفية مراقبة تغييرات حالة OpenThread والتفاعل معها
  • كيفية إرسال رسائل UDP إلى جميع الأجهزة في شبكة Thread
  • كيفية تعديل Makefiles

الخطوات التالية

وبناءً على هذا الدرس التطبيقي حول الترميز، جرِّب التمارين التالية:

  • تعديل وحدة GPIO لاستخدام الدبابيس في GPIO بدلاً من أضواء LED المضمَّنة، وتوصيل مصابيح LED بإضاءة خلفية بألوان الأحمر والأخضر والأزرق والتي تغيّر لونها استنادًا إلى دور جهاز التوجيه
  • إضافة دعم GPIO لمنصّة مختلفة لعرض الإعلانات
  • بدلاً من استخدام البث المتعدد لفحص اتصال جميع الأجهزة من خلال الضغط على الزر، استخدم واجهة برمجة التطبيقات لجهاز التوجيه/القائد لتحديد موقع جهاز فردي وفحصه.
  • وصِّل شبكتك المتداخلة بالإنترنت باستخدام جهاز توجيه الحدود في OpenThread وبثه من خارج شبكة Thread لإنشاء مصابيح LED

مواصلة القراءة

يمكنك الاطّلاع على openthread.io وGitHub في مجموعة متنوعة من مراجع OpenThread، بما في ذلك:

مرجع: