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

1- مقدمة

26b7f4f6b3ea0700.png

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

تحدد مواصفات سلسلة المحادثات بروتوكول اتصال جهاز لاسلكي موثوق به وآمن ومنخفض الطاقة يستند إلى IPv6 للتطبيقات المنزلية. وتنفّذ Openthread جميع طبقات الاتصال بسلاسل المحادثات، بما في ذلك IPv6 و6LoWPAN وIEEE 802.15.4 مع أمان MAC و"إنشاء شبكة متداخلة" و"توجيه الشبكة المتداخلة".

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

2a6db2e258c32237.png

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

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

المتطلبات

الأجهزة:

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

برامج:

  • مجموعة أدوات GNU
  • أدوات سطر أوامر nRF5x في الشمال
  • برنامج Segar J-Link
  • OpenThread
  • Git

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

2- البدء

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

قبل بدء هذا الدرس التطبيقي، عليك إكمال الدرس التطبيقي حول الترميز إنشاء شبكة سلسلة محادثات باستخدام nRF52840 Boards وOpenthread.

  • تفاصيل حول جميع البرامج التي تحتاجها لبناء وامض
  • تعلّم طريقة إنشاء OpenThread ووميضه على لوحات Nordic nRF52840
  • يوضِّح أساسيات شبكة سلاسل المحادثات

لا تتطلب أي من البيئة التي تم إعدادها تصميم OpenThread وفلاش الألواح بالتفصيل في هذا الدرس التطبيقي حول الترميز، وتُستخدَم هذه التعليمات الأساسية فقط للّوحات. يُفترض أنك أكملت الدرس التطبيقي حول إنشاء شبكة سلاسل محادثات.

جهاز Linux

تم تصميم هذا الدرس التطبيقي لاستخدام آلة Linux i386 أو x86 لتثبيت جميع لوحات تطوير سلاسل المحادثات. تم اختبار جميع الخطوات على Ubuntu 14.04.5 LTS (Trusty Tahr).

لوحات شبه عمودية nRF52840 لأمريكا الشمالية

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

a6693da3ce213856.png

تثبيت البرامج

لإصدار برنامج Openthread ووميضه، يجب تثبيت SEGGER J-Link، وأدوات nRF5x Command Line، وأداة ARM GNU Toolchain، وحزم Linux المتعددة. إذا أكملت الدرس التطبيقي حول ترميز "سلسلة محادثات الشبكة" حسب الحاجة، سيكون لديك كل الميزات اللازمة. إذا لم يكن الأمر كذلك، أكمِل ذلك الدرس التطبيقي قبل مواصلة عملية التأكّد من أنه يمكنك إنشاء Openسلسلة وفلاش للوحات nRF52840 dev.

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

يتضمّن Openthread نموذجًا لرمز التطبيق الذي يمكنك استخدامه كنقطة بداية لهذا الدرس التطبيقي.

استنساخ OpenRod Nordic 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() في مثال تطبيق سطر الأوامر:

./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.

سيتم استخدام هذه الدالة لتبديل LED4 عندما يتلقى الجهاز رسالة 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 على النحو التالي:

  • LED1 = قائد
  • LED2 = جهاز توجيه
  • LED3 = الجهاز النهائي

لتفعيل هذه الوظيفة، يحتاج التطبيق إلى معرفة وقت تغيّر دور الجهاز وكيفية تفعيل إضاءة LED الصحيحة استجابةً. سنستخدم مثيل OpenOpen للجزء الأول، وراجِع نظام 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. يطلب تسجيل الاتصال هذا من OpenOpen استدعاء الدالة 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 الواردة
  • تبديل LED4 استجابةً إلى رسائل 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، بعد تضمين القسم "include" وقبل أي عبارات #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 المقبس بواجهة شبكة سلسلة المحادثات عن طريق تمرير 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 التي أضفتها للتو. وتعمل هذه الدالة ببساطة على تبديل LED4.

/**
 * 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. واجهة برمجة التطبيقات: إعداد شبكة سلسلة المحادثات

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

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

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

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

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

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

#include <openthread/dataset_ftd.h>

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

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

static void setNetworkConfiguration(otInstance *aInstance);

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

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

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

الإجراء: إضافة الاستدعاءات لتفعيل واجهة شبكة Stack وتجميعها.

في 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);

الإجراء: تنفيذ ضبط شبكة سلاسل المحادثات.

في 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                      = 1;
    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);
}

بالتفصيل في الدالة، معلمات شبكة سلسلة المحادثات التي نستخدمها لهذا التطبيق هي:

  • القناة = 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. ويستخدمها نظام الإصدار لتجميع تطبيقك وربطه.

./third_party/NordicSemiconductor/CMakeLists.txt

الآن، أضِف بعض العلامات إلى الرمز الشمالي 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
  ...
)

...

./third_party/NordicSemiconductor/CMakeLists.txt

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

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

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

...

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

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

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

إصدار OpenThread

يمكنك إنشاء البرامج الثنائية على Openسلسلة FTD في النظام الأساسي nRF52840.

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

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

$ 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:

.c00d519eبيو7e5f0.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 أمر وامض.

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

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

يجب الآن تشغيل جميع لوحات nRF52840 الثلاث وتشغيلها في تطبيق Openthread. كما ذكرنا سابقًا بالتفصيل، يحتوي هذا التطبيق على ميزتين أساسيتين.

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

يعكس ضوء LED المضاء في كل لوحة الدور الحالي لعقدة سلسلة المحادثات:

  • LED1 = قائد
  • LED2 = جهاز توجيه
  • LED3 = الجهاز النهائي

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

الإرسال المتعدد لبروتوكول UDP

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

203dd094acca1f97.png

9bbd96d9b1c63504.png

13- إصدار تجريبي: يمكنك ملاحظة التغييرات في أدوار الجهاز.

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

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

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

4e1885861{0}66570.png

إزالة القائد

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

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

4c57c87adb40e0e3.png

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

5f40fma2dcc4b5b.png

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

أوقِف جميع اللوحات الثلاثة، ثم أعِد تشغيلها مرة أخرى مع ملاحظة أضواء LED. من المفترض أن تبدأ أول لوحة تم تشغيلها في دور القائد (LED1 مضاءة)، وسيصبح أول جهاز توجيه في شبكة سلاسل المحادثات القائد تلقائيًا.

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

أقسام الشبكة

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

طريقة معالجة الشفاء ذاتيًا

14- عرض توضيحي: إرسال بث متعدد عبر UDP

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

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

f186a2616fdbe3fd.png

اضغط على الزر 1 للوحة نفسها مرة أخرى. يجب تبديل مؤشر LED4 على جميع اللوحات الأخرى مرة أخرى.

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

f5865ccb8ab7aa34.png

أقسام الشبكة

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

15- تهانينا.

لقد أنشأت تطبيقًا يستخدم واجهات برمجة تطبيقات OpenThread.

أصبحت تعرف الآن:

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

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

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

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

قراءة إضافية

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

المرجع: