Google is committed to advancing racial equity for Black communities. See how.
این صفحه به‌وسیله ‏Cloud Translation API‏ ترجمه شده است.
Switch to English

در حال توسعه با API های OpenThread

26b7f4f6b3ea0700.png

OpenThread منتشر شده توسط Nest پیاده سازی منبع باز پروتکل شبکه Thread® است. Nest OpenThread را منتشر کرده است تا فناوری مورد استفاده در محصولات Nest را به طور گسترده در دسترس توسعه دهندگان قرار دهد تا توسعه محصولات برای خانه متصل را تسریع کند.

مشخصات Thread یک پروتکل ارتباطی بی سیم و بی سیم مبتنی بر IPv6 را برای برنامه های خانگی تعریف می کند. OpenThread تمام لایه های شبکه Thread از جمله IPv6 ، 6LoWPAN ، IEEE 802.15.4 را با امنیت MAC ، ایجاد مش پیوند و مسیریابی پیاده سازی می کند.

در این Codelab ، از API OpenThread برای راه اندازی شبکه Thread ، نظارت و واکنش به تغییرات نقش دستگاه و ارسال پیام های UDP و همچنین گره زدن این اقدامات به دکمه ها و LED های سخت افزار واقعی استفاده خواهید کرد.

2a6db2e258c32237.png

آنچه خواهید آموخت

  • نحوه برنامه ریزی دکمه ها و LED ها در صفحه های توسعه Nordic nRF52840
  • نحوه استفاده از API های OpenThread متداول و کلاس otInstance
  • نحوه نظارت و واکنش به تغییرات حالت OpenThread
  • نحوه ارسال پیام های UDP به همه دستگاه های شبکه Thread
  • چگونه Makefiles را اصلاح کنیم

آنچه شما نیاز دارید

سخت افزار:

  • 3 صفحه Nordic Semiconductor nRF52840 dev
  • 3 کابل USB به Micro-USB برای اتصال بردها
  • دستگاه لینوکس با حداقل 3 پورت USB

نرم افزار:

  • ابزار GNU
  • Nordic nRF5x ابزارهای خط فرمان
  • نرم افزار Segger J-Link
  • OpenThread
  • گیت

به استثنای موارد دیگری که ذکر شده باشد ، محتوای این Codelab تحت Creative Commons Attribution 3.0 License و نمونه های کد تحت مجوز Apache 2.0 مجوز دارند .

سخت افزار Codelab را تکمیل کنید

قبل از شروع این Codelab ، شما باید ساخت یک شبکه با نخ های nRF52840 و OpenThread Codelab را ایجاد کنید:

  • جزئیات تمام نرم افزارهای مورد نیاز شما برای ساخت و چشمک زدن
  • نحوه ساخت OpenThread و فلش کردن آن روی تخته های Nordic nRF52840 را به شما آموزش می دهد
  • اصول شبکه Thread را نشان می دهد

هیچ یک از محیط هایی که برای ساخت OpenThread و فلش برد ها لازم نیست در این Codelab - فقط دستورالعمل های اساسی برای چشمک زدن به صفحه ها - لازم نیست. فرض بر این است که شما قبلاً ساخت یک Codelab شبکه ساخت یک موضوع را تکمیل کرده اید.

ساخت Codelab Thread Network Codelab را کامل کنید

دستگاه لینوکس

این Codelab برای استفاده از دستگاه لینوکس مبتنی بر i386 یا x86 برای فلش کردن تمام تابلوهای توسعه Thread طراحی شده است. تمام مراحل در Ubuntu 14.04.5 LTS (Trusty Tahr) آزمایش شد.

تخته های Nordic Semiconductor nRF52840

این Codelab از سه صفحه nRF52840 PDK استفاده می کند.

a6693da3ce213856.png

نصب نرم افزار

برای ساخت و فلش OpenThread ، باید SEGGER J-Link ، ابزارهای خط فرمان nRF5x ، ARM GNU Toolchain و بسته های مختلف لینوکس را نصب کنید. اگر ساخت Code Thread Network Codelab را در صورت لزوم تکمیل کرده باشید ، از قبل همه آنچه را که نیاز دارید نصب خواهید کرد. در غیر اینصورت ، قبل از ادامه ، آن Codelab را تکمیل کنید تا اطمینان حاصل کنید که می توانید OpenThread را به صفحه توسعه nRF52840 تولید و فلش کنید.

ساخت Codelab Thread Network Codelab را کامل کنید

OpenThread همراه با کد کد برنامه ای است که می توانید به عنوان نقطه شروع این Codelab استفاده کنید.

OpenThread را شبیه سازی و نصب کنید:

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

API های عمومی OpenThread در include/openthread در مخزن OpenThread قرار دارد. این API ها به انواع ویژگی ها و قابلیت های OpenThread در سطح Thread و platform برای استفاده در برنامه های شما دسترسی دارند:

  • اطلاعات و کنترل نمونه OpenThread
  • سرویس های کاربردی مانند IPv6 ، UDP و CoAP
  • مدیریت اعتبار نامه شبکه ، همراه با نقش کمیسر و وصال
  • مدیریت روتر مرزی
  • ویژگی های پیشرفته مانند نظارت بر کودک و آشکارسازی Jam

اطلاعات مرجع در تمام API های 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 چیزی است که شما هنگام کار با API های OpenThread مرتباً از آن استفاده خواهید کرد. این ساختار پس از مقداردهی اولیه ، نمونه ای استاتیک از کتابخانه 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 اضافه کنید ، ابتدا آنها را با استفاده از فضای نام otSys برای همه توابع ، در سرصفحه examples/platforms/openthread-system.h اعلام کنید. سپس آنها را در یک فایل منبع مخصوص پلت فرم پیاده سازی کنید. با این روش خلاصه شده ، می توانید از سرآیند عملکرد مشابه برای سایر سیستم عامل ها استفاده کنید.

به عنوان مثال ، عملکردهای GPIO که می خواهیم از آن برای اتصال دکمه های nRF52840 استفاده کنیم و LED ها باید در openthread-system.h اعلام openthread-system.h .

پرونده examples/platforms/openthread-system.h را در ویرایشگر متن examples/platforms/openthread-system.h خود باز کنید.

مثالها / سیستم عاملها / openthread-system.h

ACTION: اظهارات عملکرد 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 اعلان تابع با استفاده از یک otInstance . به این ترتیب برنامه در صورت لزوم می تواند هنگام فشار یک دکمه به اطلاعات مربوط به نمونه OpenThread دسترسی پیدا کند. همه اینها به نیاز برنامه شما بستگی دارد. اگر در اجرای عملکرد به آن نیازی ندارید ، می توانید از ماکرو OT_UNUSED_VARIABLE از OpenThread API برای سرکوب خطاهای ساخت اطراف متغیرهای استفاده نشده برای برخی از ابزارهای ابزار استفاده کنید. بعداً نمونه هایی از این مورد را خواهیم دید.

در مرحله قبل ، ما بیانیه های عملکرد ویژه پلت فرم را در 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

برای کسب اطلاعات بیشتر در مورد دکمه ها و LED های nRF52840 ، به 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;
}

ACTION: برای پیکربندی 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);
}

ACTION: برای تنظیم حالت 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;
    }
}

ACTION: برای تغییر حالت یک LED تابعی اضافه کنید.

هنگامی که دستگاه پیام UDP چندپخشی را دریافت می کند ، از این عملکرد برای تغییر وضعیت LED4 استفاده خواهد شد.

/**
 * @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;
    }
}

ACTION: اضافه کردن توابع برای شروع و پردازش فشار دکمه ها.

اولین عملکرد صفحه را فشار داده و دکمه را فشار می دهد و دومی هنگام فشار دادن دکمه 1 پیام UDP را می فرستد.

/**
 * @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);
    }
}

ACTION: پرونده gpio.c را ذخیره و بسته کنید.

در برنامه ما ، ما می خواهیم LED های مختلف بسته به نقش دستگاه روشن شوند. بیایید نقشهای زیر را پیگیری کنیم: رهبر ، روتر ، دستگاه پایان. ما می توانیم آنها را به LED ها مانند این اختصاص دهیم:

  • LED1 = رهبر
  • LED2 = روتر
  • LED3 = دستگاه پایان

برای فعال کردن این قابلیت ، برنامه باید بداند که چه زمانی نقش دستگاه تغییر کرده است و چگونه می تواند در پاسخ LED صحیح را روشن کند. ما برای قسمت اول از نمونه OpenThread و برای قسمت دوم از انتزاع پلت فرم GPIO استفاده خواهیم کرد.

پرونده examples/apps/cli/main.c را در ویرایشگر متن examples/apps/cli/main.c خود باز کنید.

مثالها / برنامه ها / cli / main.c

عملکرد: افزودن هدر شامل

در بخش شامل پرونده main.c ، پرونده های سرآیند API مورد نیاز برای ویژگی تغییر نقش را اضافه کنید.

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

ACTION: برای تغییر حالت نمونه OpenThread اعلامیه عملکرد کنترل کننده را اضافه کنید

این اعلامیه را بعد از اضافه کردن عنوان و قبل از هر عبارت #if ، به main.c اضافه کنید. این عملکرد بعد از برنامه اصلی تعریف خواهد شد.

void handleNetifStateChanged(uint32_t aFlags, void *aContext);

ACTION: برای عملکرد کنترل کننده تغییر وضعیت ، یک ثبت نام پاسخ به تماس اضافه کنید

در main.c ، این عملکرد را بعد از otCliUartInit به تابع main() اضافه کنید. این ثبت تماس مجدد به OpenThread می گوید تا هر زمان که حالت نمونه OpenThread تغییر کرد ، با عملکرد handleNetifStateChange تماس handleNetifStateChange .

/* 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;
        }
    }
}

در برنامه ما ، ما همچنین می خواهیم پیام های UDP را به همه دستگاه های موجود در شبکه با فشار دادن دکمه 1 در یک صفحه بفرستیم. برای تأیید دریافت پیام ، در پاسخ ، صفحه های دیگر LED4 را تغییر می دهیم.

برای فعال کردن این قابلیت ، برنامه باید:

  • هنگام شروع اتصال UDP را آغاز کنید
  • قادر به ارسال پیام UDP به آدرس چند محلی از طریق شبکه محلی باشید
  • پیام های UDP ورودی را مدیریت کنید
  • در پاسخ به پیام های ورودی UDP ، LED4 را تغییر دهید

پرونده examples/apps/cli/main.c را در ویرایشگر متن examples/apps/cli/main.c خود باز کنید.

مثالها / برنامه ها / cli / main.c

عملکرد: افزودن هدر شامل

در بخش شامل در بالای پرونده main.c ، پرونده های سرآیند API مورد نیاز برای ویژگی multicast 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 ، ثابتهای خاص 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 چندپخشی برای اطلاعات بیشتر در multicast در 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;

ACTION: تماسها را برای مقداردهی اولیه LED ها و دکمه GPIO اضافه کنید

در main.c ، اضافه کردن این فراخوانی تابع به main() پس از عملکرد otSetStateChangedCallback پاسخ. این عملکردها پین های GPIO و GPIOTE را مقداردهی اولیه می کنند و یک کنترل کننده دکمه را برای مدیریت رویدادهای فشار دکمه تنظیم می کنند.

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

اقدام: تماس اولیه UDP را اضافه کنید

در main.c ، بعد از تماس otSysButtonInit که اضافه کردید ، این تابع را به تابع main() اضافه کنید:

initUdp(instance);

این تماس تضمین می کند که سوکت UDP با شروع برنامه راه اندازی می شود. بدون این ، دستگاه نمی تواند پیام های UDP را ارسال یا دریافت کند.

ACTION: برای پردازش رویداد دکمه GPIO تماس اضافه کنید

در main.c ، بعد از تماس otSysProcessDrivers ، در حلقه while این فراخوانی تابع را به تابع main() اضافه کنید. این عملکرد که در gpio.c اعلام شده است ، بررسی می کند که آیا دکمه فشرده شده است ، و در این صورت ، کنترل کننده ( handleButtonInterrupt ) را که در مرحله فوق تنظیم شده است فراخوانی می کند.

otSysButtonProcess(instance);

ACTION: کنترل کننده وقفه دکمه را پیاده سازی کنید

در 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 سوکت را باز می کند و برای دریافت پیام UDP عملکرد handleUdpReceive ( handleUdpReceive ) را ثبت می کند.

اقدام: پیام رسانی 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 ، جایی که بافر را آزاد می کند ، با خطا مقابله می کند.

برای اطلاعات بیشتر در مورد کارکردهای استفاده شده برای شروع اولیه UDP ، به منابع IPv6 و UDP در openthread.io مراجعه کنید.

اقدام: مدیریت پیام 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 . این ساختار تمام پارامترهای مورد نیاز برای انتقال اطلاعات کاربری شبکه Thread به دستگاه را در خود جای داده است.

استفاده از این ساختار پیش فرض های شبکه موجود در OpenThread را نادیده می گیرد تا برنامه ما از امنیت بیشتری برخوردار شود و گره های Thread در شبکه ما فقط به افرادی که برنامه را اجرا می کنند محدود شود.

مجدداً ، پرونده examples/apps/cli/main.c را در ویرایشگر متن examples/apps/cli/main.c خود باز کنید.

مثالها / برنامه ها / cli / main.c

اقدام: افزودن هدر شامل

در بخش شامل در بالای پرونده main.c ، پرونده هدر API مورد نیاز برای پیکربندی شبکه Thread را اضافه کنید:

#include <openthread/dataset_ftd.h>

ACTION: برای تنظیم پیکربندی شبکه ، اعلان عملکرد را اضافه کنید

این اعلامیه را پس از اضافه شدن عنوان و قبل از هر عبارت #if ، به main.c اضافه کنید. این عملکرد بعد از عملکرد اصلی برنامه تعریف خواهد شد.

static void setNetworkConfiguration(otInstance *aInstance);

عملکرد: تماس پیکربندی شبکه را اضافه کنید

در main.c ، بعد از تماس otSetStateChangedCallback این otSetStateChangedCallback تابع را به تابع main() اضافه کنید. این عملکرد مجموعه داده شبکه Thread را پیکربندی می کند.

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

ACTION: برای فعال کردن رابط شبکه 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);

ACTION: پیکربندی شبکه 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 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
}

همانطور که در عملکرد ذکر شده است ، پارامترهای شبکه Thread که ما برای این برنامه استفاده می کنیم عبارتند از:

  • کانال = 15
  • شناسه PAN = 0x2222
  • شناسه PAN تمدید = C0DE1AB5C0DE1AB5
  • کلید اصلی شبکه = 1234C0DE1AB51234C0DE1AB51234C0DE
  • نام شبکه = OTCodelab

علاوه بر این ، این جایی است که ما Router Selection Jitter را کاهش می دهیم ، بنابراین دستگاه های ما برای اهداف نمایشی سریعتر نقش را تغییر می دهند. توجه داشته باشید که این تنها در صورتی انجام می شود که گره FTD (دستگاه با نخ کامل) باشد. در مرحله بعدی اطلاعات بیشتری در مورد آن کسب کنید.

برخی از API های OpenThread تنظیماتی را تغییر می دهند که فقط باید برای اهداف آزمایشی یا آزمایشی اصلاح شوند. این API ها نباید در استقرار تولید برنامه با استفاده از OpenThread استفاده شوند.

به عنوان مثال ، عملکرد otThreadSetRouterSelectionJitter زمان (در چند ثانیه) را تنظیم می کند تا یک دستگاه End خود را به یک روتر معرفی کند. پیش فرض این مقدار در مشخصات Thread 120 است. برای سهولت استفاده در این Codelab ، ما قصد داریم آن را به 20 تغییر دهیم ، بنابراین لازم نیست خیلی منتظر بمانید تا یک گره Thread نقش ها را تغییر دهد.

با این حال ، ممکن است متوجه شده باشید که این عملکرد در یک دستورالعمل پیش پردازنده فراخوانی می شود:

#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

قبل از اینکه برنامه خود را بسازید ، چند مورد به روزرسانی جزئی برای سه Makefile مورد نیاز است. این موارد توسط سیستم build برای گردآوری و پیوند دادن برنامه شما استفاده می شوند.

مثالها / Makefile-nrf52840

اکنون برای اطمینان از تعریف توابع GPIO در برنامه ، چند پرچم به nrf52840 Makefile اضافه کنید.

اقدام: پرچم هایی را به nrf52840 Makefile اضافه کنید

examples/Makefile-nrf52840 در ویرایشگر متن examples/Makefile-nrf52840 خود باز کنید و COMMONCFLAGS زیر را پس include ... common-switches.mk خط 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 جدید را به پرونده gpio.c فایل Makefile.am کنید.

اقدام: منبع gpio را به بستر nrf52840 Makefile اضافه کنید

examples/platforms/nrf528xx/nrf52840/Makefile.am در ویرایشگر متن examples/platforms/nrf528xx/nrf52840/Makefile.am خود باز کنید و فایل را در انتهای بخش PLATFORM_COMMON_SOURCES ، درست قبل از عبارت $(NULL) . فراموش نکنید که در انتهای خط یک بک لش قرار دهید!

...

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

...

third_party / NordicSemiconductor / Makefile.am

در آخر ، فایل درایور nrfx_gpiote.c را به Makefile شخص ثالث nrfx_gpiote.c اضافه کنید ، بنابراین با ساخت کتابخانه درایورهای Nordic همراه است.

اقدام: درایور gpio را به nrf52840 بستر Makefile اضافه کنید

third_party/NordicSemiconductor/Makefile.am در ویرایشگر متن third_party/NordicSemiconductor/Makefile.am خود باز کنید و فایل را در انتهای بخش NORDICSEMI_COMMON_SOURCES ، درست قبل از عبارت $(NULL) . فراموش نکنید که در انتهای خط یک بک لش قرار دهید!

...

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

$(NULL)

...

با تمام به روزرسانی های کد ، شما آماده ساخت و فشرده سازی برنامه به هر سه صفحه Nordic nRF52840 dev هستید. هر دستگاه به عنوان یک دستگاه Full Thread (FTD) عمل خواهد کرد.

OpenThread را بسازید

nRF52840 پلت فرم نمونه را بسازید. از آنجا که Makefiles در مرحله قبل تغییر کرده است ، قبل از ساخت باید اسکریپت bootstrap را اجرا کنید:

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

با دودویی OpenThread FTD CLI به فهرست بروید و آن را با ARM Embedded Toolchain به قالب hex تبدیل کنید:

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

تابلوها را فلش بزنید

فایل ot-cli-ftd.hex را به هر صفحه nRF52840 فلش کنید.

کابل USB را به پورت اشکال زدایی Micro-USB کنار پین برق خارجی در صفحه nRF52840 وصل کنید و سپس آن را به دستگاه Linux خود متصل کنید. به درستی تنظیم کنید ، LED5 روشن است.

20a3b4b480356447.png

مانند قبل ، شماره سریال برد nRF52840 را یادداشت کنید:

c00d519ebec7e5f0.jpeg

به محل ابزارهای خط فرمان nRFx بروید و با استفاده از شماره سریال برد ، فایل hex OpenThread CLI FTD را روی صفحه nRF52840 فلش کنید:

$ cd ~/nrfjprog
$ ./nrfjprog -f nrf52 -s 683704924 --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.

این مرحله "Flash the bords" را برای دو صفحه دیگر تکرار کنید. هر برد باید از همان طریق به دستگاه لینوکس متصل شود و دستور فلش همان است ، بجز شماره سریال برد. اطمینان حاصل کنید که از شماره سریال منحصر به فرد هر صفحه در دستور فلش nrfjprog .

در صورت موفقیت ، LED1 یا LED2 یا LED3 روی هر صفحه روشن می شود. حتی ممکن است به زودی پس از چشمک زدن سوئیچ LED روشن شده را از 3 به 2 (یا 2 به 1) مشاهده کنید (ویژگی تغییر نقش دستگاه).

اکنون هر سه صفحه nRF52840 باید مجهز شده و برنامه OpenThread ما را اجرا کنند. همانطور که قبلاً شرح داده شد ، این برنامه دارای دو ویژگی اصلی است.

شاخص های نقش دستگاه

LED روشن در هر صفحه نشان دهنده نقش فعلی گره Thread است:

  • LED1 = رهبر
  • LED2 = روتر
  • LED3 = دستگاه پایان

با تغییر نقش ، LED روشن نیز تغییر می کند. شما قبلاً باید این تغییرات را روی یک یا دو برد در طی 20 ثانیه از فعال شدن هر دستگاه مشاهده کرده باشید.

چند برنامه UDP

هنگامی که دکمه 1 بر روی یک صفحه فشار داده می شود ، یک پیام UDP به آدرس چند محلی پخش محلی ارسال می شود ، که شامل همه گره های دیگر در شبکه Thread است. در پاسخ به دریافت این پیام ، LED4 در سایر تابلوها روشن یا خاموش می شود . LED4 برای هر صفحه روشن یا خاموش می ماند تا زمانی که پیام UDP دیگری دریافت کند.

203dd094acca1f97.png

9bbd96d9b1c63504.png

دستگاه هایی که فلش کرده اید نوع خاصی از دستگاه Full Thread (FTD) به نام Router Eligible End Device (REED) هستند. این بدان معنی است که آنها می توانند به عنوان روتر یا دستگاه پایان عمل کنند و می توانند خود را از یک دستگاه پایان یافته به یک روتر ارتقا دهند.

Thread می تواند تا 32 روتر را پشتیبانی کند ، اما سعی دارد تعداد روترها را بین 16 تا 23 نگه دارد. اگر REED به عنوان یک دستگاه پایان متصل شود و تعداد روترها کمتر از 16 باشد ، به طور خودکار به یک روتر ارتقا می یابد. این تغییر باید در یک زمان تصادفی و در عرض چند ثانیه رخ دهد که مقدار otThreadSetRouterSelectionJitter را در برنامه تنظیم کرده otThreadSetRouterSelectionJitter (20 ثانیه).

هر شبکه Thread همچنین دارای یک Leader است ، که یک مسیریاب است که وظیفه مدیریت مجموعه روترهای شبکه Thread را بر عهده دارد. با روشن بودن همه دستگاه ها ، پس از 20 ثانیه یکی از آنها باید Leader (LED1 روشن) و دو دستگاه دیگر Routers (LED2 روشن) باشد.

4e1e885861a66570.png

رهبر را بردارید

اگر Leader از شبکه Thread حذف شود ، روتر دیگری خود را به یک Leader ارتقا می دهد ، تا اطمینان حاصل کند که شبکه هنوز دارای Leader است.

با استفاده از کلید Power صفحه Leader (صفحه ای با LED1 روشن) را خاموش کنید. حدود 20 ثانیه صبر کنید. در یکی از دو برد باقی مانده ، LED2 (روتر) خاموش و LED1 (رهبر) روشن می شود. این دستگاه اکنون رهبر شبکه شبکه است.

4c57c87adb40e0e3.png

صفحه اصلی Leader را دوباره روشن کنید. باید به طور خودکار به عنوان یک دستگاه پایان (Thread 3 LED روشن شود) به شبکه Thread بپیوندد. در عرض 20 ثانیه (Router Selection Jitter) خود را به یک روتر ارتقا می دهد (LED2 روشن است).

5f40afca2dcc4b5b.png

تابلوها را دوباره تنظیم کنید

هر سه صفحه را خاموش کنید ، سپس آنها را دوباره روشن کنید و LED ها را مشاهده کنید. اولین بوردی که تغذیه می شود باید از نقش Leader شروع شود (LED1 روشن است) - اولین روتر در یک شبکه Thread به طور خودکار رهبر می شود.

دو برد دیگر در ابتدا به عنوان End Devices (LED3 روشن است) به شبکه متصل می شوند اما باید در عرض 20 ثانیه خود را به Routers ارتقا دهند (LED2 روشن است).

پارتیشن های شبکه

اگر بردهای شما توان کافی دریافت نمی کنند یا اتصال رادیویی بین آنها ضعیف است ، شبکه Thread ممکن است به پارتیشن تقسیم شود و شما ممکن است بیش از یک دستگاه به عنوان Leader نشان داده شود.

موضوع خود ترمیم است ، بنابراین پارتیشن ها باید در نهایت به یک پارتیشن واحد با یک Leader تبدیل شوند.

در صورت ادامه تمرین قبلی ، LED4 نباید روی هیچ دستگاهی روشن شود.

هر صفحه ای را انتخاب کنید و دکمه 1 را فشار دهید. LED4 در سایر صفحه های موجود در شبکه Thread که برنامه را اجرا می کند ، باید وضعیت آنها را تغییر دهد. در صورت ادامه تمرین قبلی ، اکنون باید ادامه داشته باشد.

f186a2618fdbe3fd.png

دوباره دکمه 1 را برای همان صفحه فشار دهید. LED4 در سایر تابلوها باید دوباره تغییر حالت دهد.

دکمه 1 را روی صفحه دیگری فشار دهید و مشاهده کنید که چرا LED4 در صفحه های دیگر تغییر حالت می دهد. دکمه 1 را در یکی از صفحه هایی که در حال حاضر LED4 روشن است فشار دهید. LED4 برای آن صفحه روشن است اما بقیه را روشن می کند.

f5865ccb8ab7aa34.png

پارتیشن های شبکه

اگر تابلوهای شما تقسیم شده و بیش از یک رهبر در میان آنها وجود داشته باشد ، پیام پیام چندپخشی بین تابلوها متفاوت خواهد بود. اگر دکمه 1 را روی تخته ای که تقسیم شده است فشار دهید (و بنابراین تنها عضو شبکه رشته تقسیم شده است) ، LED4 در صفحه های دیگر در پاسخ روشن نمی شود. اگر این اتفاق افتاد ، تابلوها را بازنشانی کنید - در حالت ایده آل ، آنها یک شبکه Thread را اصلاح می کنند و پیام رسانی UDP باید به درستی کار کند.

شما برنامه ای ایجاد کرده اید که از API های OpenThread استفاده می کند!

اکنون می دانید:

  • نحوه برنامه ریزی دکمه ها و LED ها در صفحه های توسعه Nordic nRF52840
  • نحوه استفاده از API های OpenThread متداول و کلاس otInstance
  • نحوه نظارت و واکنش به تغییرات حالت OpenThread
  • نحوه ارسال پیام های UDP به همه دستگاه های شبکه Thread
  • چگونه Makefiles را اصلاح کنیم

مراحل بعدی

با استفاده از این Codelab ، تمرینات زیر را امتحان کنید:

  • ماژول GPIO را تغییر دهید تا از پین های GPIO به جای LED های داخلی استفاده شود و LED های خارجی RGB را که براساس نقش روتر تغییر رنگ می دهند متصل کنید
  • پشتیبانی از GPIO را برای یک سیستم عامل متفاوت اضافه کنید
  • به جای استفاده از چندپخشی برای پینگ کردن همه دستگاه ها از طریق فشار دادن دکمه ، از Router / Leader API برای تعیین مکان و پینگ یک دستگاه جداگانه استفاده کنید
  • شبکه مش خود را با استفاده از OpenThread Border Router به اینترنت وصل کنید و آنها را از خارج شبکه Thread چندگانه بسازید تا LED ها روشن شوند

بیشتر خواندن

openthread.io و GitHub را برای انواع منابع OpenThread از جمله:

ارجاع: