Google is committed to advancing racial equity for Black communities. See how.
ترجمت واجهة Cloud Translation API‏ هذه الصفحة.
Switch to English

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

26b7f4f6b3ea0700.png

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

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

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

2a6db2e258c32237.png

ماذا ستتعلم

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

ماذا ستحتاج

المعدات:

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

برمجة:

  • سلسلة أدوات جنو
  • أدوات سطر الأوامر nRF5x الاسكندنافية
  • برنامج Segger J-Link
  • OpenThread
  • شخص سخيف

باستثناء ما هو مذكور خلافًا لذلك ، يتم ترخيص محتوى Codelab هذا بموجب ترخيص Creative Commons Attribution 3.0 ، ويتم ترخيص نماذج التعليمات البرمجية بموجب ترخيص Apache 2.0 .

أكمل برنامج Codelab للجهاز

قبل البدء في Codelab هذا ، يجب عليك إكمال إنشاء شبكة سلاسل رسائل باستخدام لوحات nRF52840 و OpenThread Codelab ، والتي:

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

لم يتم تفصيل أي من البيئة التي تم إعدادها لإنشاء OpenThread ووميض اللوحات في Codelab هذا - فقط الإرشادات الأساسية لوميض اللوحات. من المفترض أنك قد أكملت بالفعل مختبر Codelab الخاص ببناء شبكة مؤشرات الترابط.

أكمل معمل Codelab الخاص ببناء شبكة مؤشرات الترابط

آلة لينكس

تم تصميم Codelab هذا لاستخدام جهاز Linux يستند إلى i386 أو x86 لفلاش جميع لوحات تطوير Thread. تم اختبار جميع الخطوات على Ubuntu 14.04.5 LTS (Trusty Tahr).

لوحات الشمال أشباه الموصلات nRF52840

يستخدم Codelab هذا ثلاث لوحات nRF52840 PDK .

a6693da3ce213856.png

تثبيت نظام التشغيل

لإنشاء OpenThread وفلاشها ، تحتاج إلى تثبيت SEGGER J-Link وأدوات nRF5x Command Line و ARM GNU Toolchain وحزم Linux المختلفة. إذا كنت قد أكملت إنشاء برنامج تشفير شبكة مؤشر ترابط كما هو مطلوب ، فسيكون لديك بالفعل كل ما تحتاجه مثبتًا. إذا لم يكن الأمر كذلك ، فأكمل Codelab قبل المتابعة للتأكد من أنه يمكنك إنشاء لوحات OpenThread ووميضها إلى nRF52840 dev board.

أكمل معمل Codelab الخاص ببناء شبكة مؤشرات الترابط

يأتي OpenThread مع مثال لرمز التطبيق الذي يمكنك استخدامه كنقطة بداية لـ Codelab هذا.

استنساخ OpenThread وتثبيته:

$ cd ~
$ git clone --recursive https://github.com/openthread/openthread.git
$ cd openthread
$ ./bootstrap

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

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

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

باستخدام API

لاستخدام API ، قم بتضمين ملف الرأس الخاص بها في أحد ملفات التطبيق الخاص بك. ثم اتصل بالوظيفة المطلوبة.

على سبيل المثال ، يستخدم تطبيق مثال CLI المضمن في OpenThread رؤوس API التالية:

أمثلة / تطبيقات / 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 APIs. بمجرد التهيئة ، يمثل هذا الهيكل مثيلًا ثابتًا لمكتبة OpenThread ويسمح للمستخدم بإجراء مكالمات OpenThread API.

على سبيل المثال ، تتم تهيئة مثيل OpenThread في الوظيفة main() لتطبيق مثال CLI:

أمثلة / تطبيقات / 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 ، examples/platforms/openthread-system.h رأس examples/platforms/openthread-system.h ، باستخدام مساحة اسم otSys لجميع الوظائف. ثم قم بتنفيذها في ملف مصدر خاص بالنظام الأساسي. مستخلصًا بهذه الطريقة ، يمكنك استخدام رؤوس الوظائف نفسها لمنصات أمثلة أخرى.

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

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

أمثلة / منصات / openthread-system.h

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

أضف إقرارات الوظائف هذه في أي مكان داخل الملف:

/**
 * 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 الدالة otSysButtonProcess يستخدم otInstance . بهذه الطريقة يمكن للتطبيق الوصول إلى معلومات حول مثيل OpenThread عند الضغط على زر ، إذا لزم الأمر. كل هذا يتوقف على احتياجات التطبيق الخاص بك. إذا لم تكن بحاجة إليها في تنفيذ الوظيفة ، فيمكنك استخدام ماكرو OT_UNUSED_VARIABLE من OpenThread API لمنع أخطاء OT_UNUSED_VARIABLE حول المتغيرات غير المستخدمة لبعض سلاسل الأدوات. سنرى أمثلة على ذلك لاحقًا.

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

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

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

أمثلة / منصات / nrf528xx / 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 ، راجع Nordic Semiconductor Infocenter .

الإجراء: إضافة رأس يشمل

بعد ذلك ، قم بإضافة الرأس الذي سوف تحتاجه لوظيفة 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 أثناء التهيئة.

/**
 * @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 الملف.

في تطبيقنا ، نريد أن تضيء مصابيح LED مختلفة اعتمادًا على دور الجهاز. دعنا نتتبع الأدوار التالية: القائد ، جهاز التوجيه ، الجهاز النهائي. يمكننا تخصيصها لمصابيح LED مثل:

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

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

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

أمثلة / تطبيقات / cli / main.c

الإجراء: إضافة رأس يشمل

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

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

0deb990f0

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

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

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

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

  • قم بتهيئة اتصال UDP عند بدء التشغيل
  • تكون قادرًا على إرسال رسالة UDP إلى عنوان الإرسال المتعدد المحلي
  • معالجة رسائل UDP الواردة
  • قم بتبديل LED4 استجابةً لرسائل UDP الواردة

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

أمثلة / تطبيقات / cli / main.c

الإجراء: إضافة رأس يشمل

في قسم main.c في الجزء العلوي من ملف main.c ، أضف ملفات رأس API التي ستحتاجها لميزة 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 ، بعد قسم #if وقبل أي عبارات #if ، أضف الثوابت الخاصة بـ UDP وحدد:

#define UDP_PORT 1212

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

ff03::1 هو عنوان الإرسال المتعدد المحلي. سيتم إرسال أي رسائل يتم إرسالها إلى هذا العنوان إلى جميع أجهزة السلسلة الكاملة في الشبكة. راجع Multicast على 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;

الإجراء: أضف مكالمات لتهيئة مصابيح وزر 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);
}

UDP_PORT هو المنفذ الذي حددته مسبقًا (1212). تفتح وظيفة otUdpOpen المقبس وتقوم بتسجيل وظيفة رد الاتصال ( handleUdpReceive ) عند تلقي رسالة 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);
}

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

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

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

أمثلة / تطبيقات / cli / main.c

الإجراء: إضافة رأس تضمين

ضمن قسم 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);

الإجراء: أضف مكالمات لتمكين واجهة شبكة مؤشر الترابط والمكدس

في 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 Master 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 master key to 1234C0DE1AB51234C0DE1AB51234C0DE */
    uint8_t key[OT_MASTER_KEY_SIZE] = {0x12, 0x34, 0xC0, 0xDE, 0x1A, 0xB5, 0x12, 0x34, 0xC0, 0xDE, 0x1A, 0xB5, 0x12, 0x34, 0xC0, 0xDE};
    memcpy(aDataset.mMasterKey.m8, key, sizeof(aDataset.mMasterKey));
    aDataset.mComponents.mIsMasterKeyPresent = 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;

#if OPENTHREAD_FTD
    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);
#else
    OT_UNUSED_VARIABLE(aInstance);
#endif
}

كما هو مفصل في الوظيفة ، معلمات شبكة مؤشر الترابط التي نستخدمها لهذا التطبيق هي:

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

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

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

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

ومع ذلك ، ربما لاحظت أن هذه الوظيفة تسمى في توجيه ما قبل المعالج:

#if OPENTHREAD_FTD
    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);
#else
    OT_UNUSED_VARIABLE(aInstance);
#endif

هذا لمنع إضافته إلى MTD (جهاز الخيط الأدنى). لا تصبح أجهزة MTD أجهزة توجيه ، ولا يتم تضمين دعم وظيفة مثل otThreadSetRouterSelectionJitter في بنية MTD.

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

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

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

أمثلة / Makefile-nrf52840

أضف الآن بعض العلامات إلى nrf52840 Makefile ، لضمان تحديد وظائف GPIO في التطبيق.

الإجراء: أضف الأعلام إلى nrf52840 Makefile

افتح examples/Makefile-nrf52840 في محرر النصوص المفضل لديك ، وأضف COMMONCFLAGS التالية بعد سطر include ... common-switches.mk :

...

include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/common-switches.mk

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

...

أمثلة / منصات / nrf528xx / nrf52840 / Makefile.am

قم الآن بإضافة ملف gpio.c الجديد إلى ملف النظام الأساسي nrf52840 Makefile.am .

الإجراء: أضف مصدر gpio إلى منصة Makefile nrf52840

افتح examples/platforms/nrf528xx/nrf52840/Makefile.am في محرر النصوص المفضل لديك ، وأضف الملف في نهاية قسم PLATFORM_COMMON_SOURCES ، قبل العبارة $(NULL) . لا تنسَ وضع شرطة مائلة للخلف في نهاية السطر!

...

PLATFORM_COMMON_SOURCES
...
src/gpio.c             \
$(NULL)

...

3rd_party / NordicSemiconductor / Makefile.am

أخيرًا ، أضف ملف برنامج التشغيل nrfx_gpiote.c إلى Makefile التابع لجهة خارجية NordicSemiconductor ، بحيث يتم تضمينه في مكتبة بناء برامج تشغيل Nordic.

الإجراء: أضف برنامج تشغيل gpio إلى منصة Makefile nrf52840

افتح third_party/NordicSemiconductor/Makefile.am في محرر النصوص المفضل لديك ، وأضف الملف في نهاية قسم NORDICSEMI_COMMON_SOURCES ، قبل العبارة $(NULL) . لا تنسَ وضع شرطة مائلة للخلف في نهاية السطر!

...

NORDICSEMI_COMMON_SOURCES
...
nrfx/drivers/src/nrfx_gpiote.c             \

$(NULL)

...

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

بناء OpenThread

قم ببناء مثال النظام الأساسي nRF52840. نظرًا لأنه تم تغيير ملفات Makefiles في الخطوة السابقة ، فأنت بحاجة إلى تشغيل البرنامج النصي bootstrap قبل إنشاء:

$ cd ~/openthread
$ ./bootstrap
$ make -f examples/Makefile-nrf52840

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

$ cd ~/openthread/output/nrf52840/bin
$ arm-none-eabi-objcopy -O ihex ot-cli-ftd ot-cli-ftd.hex

فلاش الألواح

قم ot-cli-ftd.hex ملف ot-cli-ftd.hex لكل لوحة nRF52840.

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

20a3b4b480356447.png

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

c00d519ebec7e5f0.jpeg

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

0 ببفه 1030

سينطفئ مؤشر 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) بعد وقت قصير من الوميض (ميزة تغيير دور الجهاز).

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

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

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

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

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

الإرسال المتعدد UDP

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

203dd094acca1f97.png

9bbd96d9b1c63504.png

الأجهزة التي قمت بوميضها هي نوع معين من أجهزة الخيط الكامل (FTD) تسمى الجهاز النهائي المؤهل لجهاز التوجيه (REED). هذا يعني أنه يمكنهم العمل إما كموجه أو جهاز نهائي ، ويمكنهم الترويج لأنفسهم من جهاز طرفي إلى جهاز توجيه.

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

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

4e1e885861a66570.png

أزل القائد

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

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

4c57c87adb40e0e3.png

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

5f40afca2dcc4b5b.png

أعد ضبط المجالس

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

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

أقسام الشبكة

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

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

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

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

f186a2618fdbe3fd.png

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

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

f5865ccb8ab7aa34.png

أقسام الشبكة

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

لقد قمت بإنشاء تطبيق يستخدم OpenThread APIs!

أنت تعرف الآن:

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

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

بناء على Codelab هذا ، جرب التمارين التالية:

  • قم بتعديل وحدة GPIO لاستخدام دبابيس GPIO بدلاً من مصابيح LED المدمجة ، وقم بتوصيل مصابيح RGB LED الخارجية التي تغير اللون بناءً على دور جهاز التوجيه
  • أضف دعم GPIO لمنصة نموذجية مختلفة
  • بدلاً من استخدام البث المتعدد لإجراء اختبار ping لجميع الأجهزة من خلال الضغط على الزر ، استخدم Router / Leader API لتحديد موقع جهاز فردي وإجراء اختبار ping
  • قم بتوصيل شبكتك المتداخلة بالإنترنت باستخدام OpenThread Border Router وبثها متعددًا من خارج شبكة Thread لإضاءة مصابيح LED

قراءة متعمقة

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

مرجع: