OpenThread API এর সাথে বিকাশ করা হচ্ছে

১. ভূমিকা

26b7f4f6b3ea0700.png

নেস্ট কর্তৃক প্রকাশিত ওপেনথ্রেড হলো থ্রেড® নেটওয়ার্কিং প্রোটোকলের একটি ওপেন-সোর্স বাস্তবায়ন। কানেক্টেড হোমের জন্য পণ্যের উন্নয়নকে ত্বরান্বিত করতে, নেস্ট তাদের পণ্যগুলিতে ব্যবহৃত প্রযুক্তি ডেভেলপারদের কাছে ব্যাপকভাবে সহজলভ্য করার জন্য ওপেনথ্রেড প্রকাশ করেছে।

থ্রেড স্পেসিফিকেশনটি হোম অ্যাপ্লিকেশনের জন্য একটি IPv6-ভিত্তিক নির্ভরযোগ্য, সুরক্ষিত এবং স্বল্প-শক্তিসম্পন্ন ওয়্যারলেস ডিভাইস-টু-ডিভাইস কমিউনিকেশন প্রোটোকল সংজ্ঞায়িত করে। ওপেনথ্রেড IPv6, 6LoWPAN, MAC সিকিউরিটিসহ IEEE 802.15.4, মেশ লিঙ্ক এস্টাবলিশমেন্ট এবং মেশ রাউটিং সহ সমস্ত থ্রেড নেটওয়ার্কিং লেয়ার বাস্তবায়ন করে।

এই কোডল্যাবে, আপনি OpenThread API ব্যবহার করে একটি থ্রেড নেটওয়ার্ক শুরু করবেন, ডিভাইসের ভূমিকার পরিবর্তন পর্যবেক্ষণ ও সে অনুযায়ী ব্যবস্থা নেবেন, এবং UDP বার্তা পাঠাবেন। এছাড়াও, এই কাজগুলোকে বাস্তব হার্ডওয়্যারের বাটন ও LED-এর সাথে সংযুক্ত করবেন।

2a6db2e258c32237.png

আপনি যা শিখবেন

  • নর্ডিক nRF52840 ডেভ বোর্ডের বাটন এবং এলইডি প্রোগ্রাম করার পদ্ধতি
  • সাধারণ OpenThread API এবং otInstance ক্লাস কীভাবে ব্যবহার করবেন
  • OpenThread অবস্থার পরিবর্তনগুলি কীভাবে পর্যবেক্ষণ ও তার প্রতিক্রিয়া জানাতে হয়
  • থ্রেড নেটওয়ার্কের সমস্ত ডিভাইসে কীভাবে UDP বার্তা পাঠাতে হয়
  • মেকফাইলগুলি কীভাবে পরিবর্তন করবেন

আপনার যা যা লাগবে

হার্ডওয়্যার:

  • ৩টি নর্ডিক সেমিকন্ডাক্টর nRF52840 ডেভ বোর্ড
  • বোর্ডগুলো সংযোগ করার জন্য ৩টি ইউএসবি থেকে মাইক্রো-ইউএসবি কেবল
  • কমপক্ষে ৩টি ইউএসবি পোর্ট সহ একটি লিনাক্স মেশিন

সফটওয়্যার:

  • জিএনইউ টুলচেইন
  • নর্ডিক nRF5x কমান্ড লাইন টুলস
  • সেগার জে-লিঙ্ক সফটওয়্যার
  • ওপেনথ্রেড
  • গিট

অন্যথায় উল্লিখিত না থাকলে, এই কোডল্যাবের বিষয়বস্তু ক্রিয়েটিভ কমন্স অ্যাট্রিবিউশন ৩.০ লাইসেন্সের অধীনে এবং কোড স্যাম্পলগুলো অ্যাপাচি ২.০ লাইসেন্সের অধীনে লাইসেন্সপ্রাপ্ত

২. শুরু করা

হার্ডওয়্যার কোডল্যাব সম্পূর্ণ করুন

এই কোডল্যাবটি শুরু করার আগে, আপনাকে 'Build a Thread Network with nRF52840 Boards and OpenThread' কোডল্যাবটি সম্পন্ন করতে হবে, যা:

  • বিল্ড এবং ফ্ল্যাশ করার জন্য আপনার প্রয়োজনীয় সমস্ত সফ্টওয়্যারের বিবরণ।
  • আপনাকে শেখানো হয় কিভাবে OpenThread বিল্ড করতে হয় এবং Nordic nRF52840 বোর্ডে তা ফ্ল্যাশ করতে হয়।
  • থ্রেড নেটওয়ার্কের মূল বিষয়গুলো তুলে ধরা হয়েছে।

ওপেনথ্রেড বিল্ড করতে এবং বোর্ডগুলো ফ্ল্যাশ করার জন্য প্রয়োজনীয় পরিবেশগত সেটআপের কোনো বিস্তারিত বিবরণ এই কোডল্যাবে দেওয়া হয়নি—শুধুমাত্র বোর্ডগুলো ফ্ল্যাশ করার জন্য প্রাথমিক নির্দেশাবলী রয়েছে। ধরে নেওয়া হচ্ছে যে আপনি ইতিমধ্যেই 'বিল্ড এ থ্রেড নেটওয়ার্ক' কোডল্যাবটি সম্পন্ন করেছেন।

লিনাক্স মেশিন

এই কোডল্যাবটি i386- অথবা x86-ভিত্তিক লিনাক্স মেশিন ব্যবহার করে সমস্ত থ্রেড ডেভেলপমেন্ট বোর্ড ফ্ল্যাশ করার জন্য ডিজাইন করা হয়েছিল। সমস্ত ধাপ উবুন্টু 14.04.5 LTS (ট্রাস্টি টাহর)-এ পরীক্ষা করা হয়েছিল।

নর্ডিক সেমিকন্ডাক্টর nRF52840 বোর্ড

এই কোডল্যাবে তিনটি nRF52840 PDK বোর্ড ব্যবহার করা হয়েছে।

a6693da3ce213856.png

সফটওয়্যার ইনস্টল করুন

OpenThread বিল্ড এবং ফ্ল্যাশ করার জন্য, আপনাকে SEGGER J-Link, nRF5x কমান্ড লাইন টুলস, ARM GNU টুলচেইন এবং বিভিন্ন লিনাক্স প্যাকেজ ইনস্টল করতে হবে। যদি আপনি প্রয়োজন অনুযায়ী 'Build a Thread Network Codelab' সম্পন্ন করে থাকেন, তাহলে আপনার প্রয়োজনীয় সবকিছু ইতোমধ্যেই ইনস্টল করা থাকবে। যদি তা না করে থাকেন, তবে nRF52840 ডেভ বোর্ডে OpenThread বিল্ড এবং ফ্ল্যাশ করতে পারবেন তা নিশ্চিত করার জন্য, সামনে এগোনোর আগে সেই কোডল্যাবটি সম্পন্ন করুন।

৩. রিপোজিটরিটি ক্লোন করুন

ওপেনথ্রেডের সাথে উদাহরণ অ্যাপ্লিকেশন কোড দেওয়া আছে, যা আপনি এই কোডল্যাবের সূচনা বিন্দু হিসেবে ব্যবহার করতে পারেন।

OpenThread Nordic nRF528xx উদাহরণ রিপোটি ক্লোন করুন এবং OpenThread বিল্ড করুন:

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

৪. ওপেনথ্রেড এপিআই-এর প্রাথমিক বিষয়সমূহ

OpenThread-এর পাবলিক এপিআইগুলো OpenThread রিপোজিটরির ./openthread/include/openthread এ অবস্থিত। এই এপিআইগুলো আপনার অ্যাপ্লিকেশনে ব্যবহারের জন্য থ্রেড- এবং প্ল্যাটফর্ম-উভয় স্তরেই OpenThread-এর বিভিন্ন বৈশিষ্ট্য ও কার্যকারিতায় অ্যাক্সেস প্রদান করে:

  • ওপেনথ্রেড ইনস্ট্যান্সের তথ্য এবং নিয়ন্ত্রণ
  • IPv6, UDP, এবং CoAP এর মতো অ্যাপ্লিকেশন পরিষেবাগুলি
  • নেটওয়ার্ক ক্রেডেনশিয়াল ম্যানেজমেন্ট, সেইসাথে কমিশনার এবং জয়নারের ভূমিকা
  • সীমান্ত রাউটার ব্যবস্থাপনা
  • শিশু তত্ত্বাবধান এবং জ্যাম সনাক্তকরণের মতো উন্নত বৈশিষ্ট্য

সমস্ত ওপেনথ্রেড এপিআই-এর রেফারেন্স তথ্য openthread.io/reference -এ পাওয়া যায়।

একটি এপিআই ব্যবহার করে

এপিআই (API) ব্যবহার করতে, আপনার অ্যাপ্লিকেশন ফাইলগুলোর মধ্যে যেকোনো একটিতে এর হেডার ফাইলটি অন্তর্ভুক্ত করুন। এরপর কাঙ্ক্ষিত ফাংশনটি কল করুন।

উদাহরণস্বরূপ, OpenThread-এর সাথে অন্তর্ভুক্ত CLI উদাহরণ অ্যাপটি নিম্নলিখিত API হেডারগুলো ব্যবহার করে:

./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 ইনস্ট্যান্স

OpenThread API নিয়ে কাজ করার সময় otInstance স্ট্রাকচারটি আপনাকে প্রায়শই ব্যবহার করতে হবে। একবার ইনিশিয়ালাইজ করা হলে, এই স্ট্রাকচারটি OpenThread লাইব্রেরির একটি স্ট্যাটিক ইনস্ট্যান্সকে প্রতিনিধিত্ব করে এবং ব্যবহারকারীকে OpenThread API কল করার সুযোগ দেয়।

উদাহরণস্বরূপ, CLI উদাহরণ অ্যাপের main() ফাংশনে OpenThread ইনস্ট্যান্সটি ইনিশিয়ালাইজ করা হয়:

./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-এর সাথে অন্তর্ভুক্ত কোনো উদাহরণ অ্যাপ্লিকেশনে প্ল্যাটফর্ম-নির্দিষ্ট ফাংশন যোগ করতে চান, তাহলে প্রথমে সমস্ত ফাংশনের জন্য otSys নেমস্পেস ব্যবহার করে সেগুলোকে ./openthread/examples/platforms/openthread-system.h হেডার ফাইলে ডিক্লেয়ার করুন। এরপর একটি প্ল্যাটফর্ম-নির্দিষ্ট সোর্স ফাইলে সেগুলোকে ইমপ্লিমেন্ট করুন। এভাবে অ্যাবস্ট্রাক্ট করা থাকলে, আপনি অন্যান্য উদাহরণ প্ল্যাটফর্মের জন্যও একই ফাংশন হেডার ব্যবহার করতে পারবেন।

উদাহরণস্বরূপ, nRF52840-এর বাটন এবং এলইডি-তে সংযোগ স্থাপনের জন্য আমরা যে GPIO ফাংশনগুলো ব্যবহার করব, সেগুলো অবশ্যই openthread-system.h ফাইলে ঘোষণা করতে হবে।

আপনার পছন্দের টেক্সট এডিটরে ./openthread/examples/platforms/openthread-system.h ফাইলটি খুলুন।

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

করণীয়: প্ল্যাটফর্ম-নির্দিষ্ট GPIO ফাংশন ঘোষণা যোগ করুন।

openthread/instance.h হেডারের #include এর পরে এই ফাংশন ডিক্লারেশনগুলো যোগ করুন:

/**
 * 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 ইনস্ট্যান্স সম্পর্কিত তথ্য অ্যাক্সেস করতে পারে। এটি সম্পূর্ণভাবে আপনার অ্যাপ্লিকেশনের চাহিদার উপর নির্ভর করে। যদি আপনার ফাংশনের ইমপ্লিমেন্টেশনে এটির প্রয়োজন না হয়, তবে কিছু টুলচেইনের জন্য অব্যবহৃত ভেরিয়েবল সংক্রান্ত বিল্ড এরর দমন করতে আপনি OpenThread API-এর OT_UNUSED_VARIABLE ম্যাক্রোটি ব্যবহার করতে পারেন। আমরা পরে এর উদাহরণ দেখব।

৫. GPIO প্ল্যাটফর্ম অ্যাবস্ট্রাকশন বাস্তবায়ন করুন

পূর্ববর্তী ধাপে, আমরা ./openthread/examples/platforms/openthread-system.h এ থাকা প্ল্যাটফর্ম-নির্দিষ্ট ফাংশন ডিক্লারেশনগুলো নিয়ে আলোচনা করেছি যা GPIO-এর জন্য ব্যবহার করা যেতে পারে। 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 বাটন এবং এলইডি সম্পর্কে আরও তথ্যের জন্য, নর্ডিক সেমিকন্ডাক্টর ইনফোসেন্টার দেখুন।

করণীয়: হেডার ইনক্লুড যোগ করুন।

এরপরে, 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"

করণীয়: বাটন ১-এর জন্য কলব্যাক এবং ইন্টারাপ্ট ফাংশন যোগ করুন।

এরপরে এই কোডটি যোগ করুন। 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;
}

করণীয়: এলইডিগুলো কনফিগার করার জন্য একটি ফাংশন যোগ করুন।

প্রারম্ভিকীকরণের সময় সমস্ত এলইডি-র মোড ও অবস্থা নির্ধারণ করতে এই কোডটি যোগ করুন।

/**
 * @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) চালু বা বন্ধ করতে এই ফাংশনটি ব্যবহৃত হবে।

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

করণীয়: বাটন চাপ শুরু ও প্রক্রিয়াকরণের জন্য ফাংশন যোগ করুন।

প্রথম ফাংশনটি বাটন চাপার জন্য বোর্ডকে প্রস্তুত করে, এবং দ্বিতীয়টি বাটন ১ চাপলে মাল্টিকাস্ট ইউডিপি বার্তা পাঠায়।

/**
 * @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 প্ল্যাটফর্ম অ্যাবস্ট্রাকশন ব্যবহার করব।

আপনার পছন্দের টেক্সট এডিটরে ./openthread/examples/apps/cli/main.c ফাইলটি খুলুন।

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

করণীয়: হেডার ইনক্লুড যোগ করুন।

main.c ফাইলের include সেকশনে, রোল পরিবর্তনের ফিচারের জন্য প্রয়োজনীয় API হেডার ফাইলগুলো যোগ করুন।

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

করণীয়: OpenThread ইনস্ট্যান্সের অবস্থা পরিবর্তনের জন্য হ্যান্ডলার ফাংশন ঘোষণা যোগ করুন।

এই ডিক্লারেশনটি main.c ফাইলে, হেডার include-গুলোর পরে এবং যেকোনো #if স্টেটমেন্টের আগে যোগ করুন। এই ফাংশনটি মূল অ্যাপ্লিকেশনের পরে সংজ্ঞায়িত হবে।

void handleNetifStateChanged(uint32_t aFlags, void *aContext);

করণীয়: স্টেট পরিবর্তন হ্যান্ডলার ফাংশনের জন্য একটি কলব্যাক রেজিস্ট্রেশন যোগ করুন।

main.c ফাইলে, otAppCliInit কলটির পরে main() ফাংশনে এই ফাংশনটি যোগ করুন। এই কলব্যাক রেজিস্ট্রেশনটি OpenThread-কে বলে দেয় যে, যখনই OpenThread ইনস্ট্যান্সের স্টেট পরিবর্তিত হবে, তখনই যেন handleNetifStateChange ফাংশনটি কল করা হয়।

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

করণীয়: অবস্থা পরিবর্তনের বাস্তবায়ন যোগ করুন।

main.c ফাইলে, main() ফাংশনের পরে handleNetifStateChanged ফাংশনটি ইমপ্লিমেন্ট করুন। এই ফাংশনটি OpenThread ইনস্ট্যান্সের OT_CHANGED_THREAD_ROLE ফ্ল্যাগটি পরীক্ষা করে এবং যদি এটি পরিবর্তিত হয়ে থাকে, তবে প্রয়োজন অনুযায়ী 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 মেসেজ পাঠানো হবে। মেসেজ প্রাপ্তি নিশ্চিত করতে, এর প্রতিক্রিয়ায় আমরা অন্য বোর্ডগুলোর LED4 টগল করব।

এই কার্যকারিতাটি সক্রিয় করতে, অ্যাপ্লিকেশনটির নিম্নলিখিত কাজগুলো করা প্রয়োজন:

  • চালু করার সময় একটি UDP সংযোগ শুরু করুন
  • মেশ-লোকাল মাল্টিকাস্ট অ্যাড্রেসে একটি UDP মেসেজ পাঠাতে সক্ষম হতে হবে।
  • আগত UDP বার্তাগুলি পরিচালনা করুন
  • আগত UDP বার্তার প্রতিক্রিয়ায় LED4 টগল করুন

আপনার পছন্দের টেক্সট এডিটরে ./openthread/examples/apps/cli/main.c ফাইলটি খুলুন।

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

করণীয়: হেডার ইনক্লুড যোগ করুন।

main.c ফাইলের শীর্ষে থাকা include সেকশনে, মাল্টিকাস্ট UDP ফিচারের জন্য প্রয়োজনীয় API হেডার ফাইলগুলো যোগ করুন।

#include <string.h>

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

#include "utils/code_utils.h"

code_utils.h হেডারটি otEXPECT এবং otEXPECT_ACTION ম্যাক্রোগুলোর জন্য ব্যবহৃত হয়, যেগুলো রান-টাইম শর্তগুলো যাচাই করে এবং ত্রুটিগুলো সুষ্ঠুভাবে সামাল দেয়।

করণীয়: ডিফাইন এবং কনস্ট্যান্ট যোগ করুন:

main.c ফাইলে, includes সেকশনের পরে এবং যেকোনো #if স্টেটমেন্টের আগে, UDP-নির্দিষ্ট কনস্ট্যান্ট ও ডিফাইন যোগ করুন:

#define UDP_PORT 1212

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

ff03::1 হলো মেশ-লোকাল মাল্টিকাস্ট অ্যাড্রেস। এই অ্যাড্রেসে পাঠানো যেকোনো মেসেজ নেটওয়ার্কের সমস্ত ফুল থ্রেড ডিভাইসে পাঠানো হবে। OpenThread-এ মাল্টিকাস্ট সাপোর্ট সম্পর্কে আরও তথ্যের জন্য openthread.io-তে মাল্টিকাস্ট দেখুন।

করণীয়: ফাংশন ঘোষণা যোগ করুন।

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 LED এবং বাটন চালু করার জন্য কল যোগ করুন।

main.c ফাইলে, otSetStateChangedCallback কলটির পরে main() ফাংশনে এই ফাংশন কলগুলো যোগ করুন। এই ফাংশনগুলো GPIO এবং GPIOTE পিনগুলোকে ইনিশিয়ালাইজ করে এবং বাটন পুশ ইভেন্টগুলো হ্যান্ডেল করার জন্য একটি বাটন হ্যান্ডলার সেট করে।

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

করণীয়: UDP প্রারম্ভিককরণ কলটি যোগ করুন।

main.c ফাইলে, আপনার যোগ করা otSysButtonInit কলটির পরে main() ফাংশনের মধ্যে এই ফাংশনটি যোগ করুন:

initUdp(instance);

এই কলটি নিশ্চিত করে যে অ্যাপ্লিকেশন চালু হওয়ার সাথে সাথে একটি UDP সকেট ইনিশিয়ালাইজ করা হয়। এটি ছাড়া ডিভাইসটি UDP মেসেজ পাঠাতে বা গ্রহণ করতে পারে না।

করণীয়: GPIO বাটন ইভেন্টটি প্রসেস করার জন্য কল যোগ করুন।

main.c ফাইলে, while লুপের মধ্যে otSysProcessDrivers কলটির পরে এই ফাংশন কলটি যোগ করুন। gpio.c ফাইলে ঘোষিত এই ফাংশনটি পরীক্ষা করে দেখে যে বাটনটি চাপা হয়েছে কিনা, এবং যদি হয়ে থাকে, main() উপরের ধাপে সেট করা হ্যান্ডলারটিকে ( handleButtonInterrupt ) কল করে।

otSysButtonProcess(instance);

করণীয়: বাটন ইন্টারাপ্ট হ্যান্ডলার বাস্তবায়ন করুন।

main.c ফাইলে, আগের ধাপে যোগ করা handleNetifStateChanged ফাংশনটির পরে handleButtonInterrupt ফাংশনটির ইমপ্লিমেন্টেশন যোগ করুন:

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

করণীয়: ইউডিপি প্রারম্ভিকীকরণ বাস্তবায়ন করুন।

main.c ফাইলে, আপনার এইমাত্র যোগ করা handleButtonInterrupt ফাংশনটির পরে initUdp ফাংশনটির ইমপ্লিমেন্টেশন যোগ করুন:

/**
 * 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 ফাংশনটি সকেট খোলে এবং একটি UDP বার্তা গৃহীত হলে তার জন্য একটি কলব্যাক ফাংশন ( handleUdpReceive ) রেজিস্টার করে। otUdpBind OT_NETIF_THREAD পাস করার মাধ্যমে সকেটটিকে থ্রেড নেটওয়ার্ক ইন্টারফেসের সাথে বাইন্ড করে। অন্যান্য নেটওয়ার্ক ইন্টারফেস বিকল্পগুলির জন্য, UDP API রেফারেন্সে otNetifIdentifier এনুমারেশনটি দেখুন।

করণীয়: ইউডিপি মেসেজিং বাস্তবায়ন করুন।

main.c ফাইলে, এইমাত্র যোগ করা initUdp ফাংশনটির পরে sendUdp ফাংশনটির ইমপ্লিমেন্টেশন যোগ করুন:

/**
 * 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 চালু করতে ব্যবহৃত ফাংশনগুলো সম্পর্কে আরও তথ্যের জন্য openthread.io-তে থাকা IPv6 এবং UDP রেফারেন্সগুলো দেখুন।

করণীয়: ইউডিপি বার্তা পরিচালনা ব্যবস্থা বাস্তবায়ন করা।

main.c ফাইলে, আপনার যোগ করা sendUdp ফাংশনটির পরে handleUdpReceive ফাংশনটির ইমপ্লিমেন্টেশন যোগ করুন। এই ফাংশনটি কেবল 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);
}

৮. এপিআই: থ্রেড নেটওয়ার্ক কনফিগার করুন

প্রদর্শনের সুবিধার জন্য, আমরা চাই আমাদের ডিভাইসগুলো চালু হওয়ার সাথে সাথেই যেন থ্রেড শুরু করে এবং একটি নেটওয়ার্কে যুক্ত হয়। এটি করার জন্য, আমরা otOperationalDataset স্ট্রাকচারটি ব্যবহার করব। এই স্ট্রাকচারটিতে একটি ডিভাইসে থ্রেড নেটওয়ার্কের ক্রেডেনশিয়াল পাঠানোর জন্য প্রয়োজনীয় সমস্ত প্যারামিটার থাকে।

এই কাঠামোর ব্যবহার ওপেনথ্রেডের অন্তর্নির্মিত নেটওয়ার্ক ডিফল্টগুলোকে ওভাররাইড করবে, যা আমাদের অ্যাপ্লিকেশনকে আরও সুরক্ষিত করবে এবং আমাদের নেটওয়ার্কের থ্রেড নোডগুলোকে শুধুমাত্র অ্যাপ্লিকেশনটি চালনাকারী নোডগুলোর মধ্যে সীমাবদ্ধ রাখবে।

আবার, আপনার পছন্দের টেক্সট এডিটরে ./openthread/examples/apps/cli/main.c ফাইলটি খুলুন।

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

করণীয়: হেডার অন্তর্ভুক্ত করুন।

main.c ফাইলের শীর্ষে থাকা include সেকশনের মধ্যে, থ্রেড নেটওয়ার্ক কনফিগার করার জন্য প্রয়োজনীয় API হেডার ফাইলটি যোগ করুন:

#include <openthread/dataset_ftd.h>

করণীয়: নেটওয়ার্ক কনফিগারেশন সেট করার জন্য ফাংশন ডিক্লারেশন যোগ করুন।

এই ডিক্লারেশনটি main.c ফাইলে, হেডার include-গুলোর পরে এবং যেকোনো #if স্টেটমেন্টের আগে যোগ করুন। এই ফাংশনটি main অ্যাপ্লিকেশন ফাংশনের পরে সংজ্ঞায়িত হবে।

static void setNetworkConfiguration(otInstance *aInstance);

করণীয়: নেটওয়ার্ক কনফিগারেশন কলটি যোগ করুন।

main.c ফাইলে, otSetStateChangedCallback কলটির পরে main() ফাংশনে এই ফাংশন কলটি যোগ করুন। এই ফাংশনটি থ্রেড নেটওয়ার্ক ডেটাসেট কনফিগার করে।

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

করণীয়: থ্রেড নেটওয়ার্ক ইন্টারফেস এবং স্ট্যাক সক্রিয় করার জন্য কলগুলো যোগ করুন।

main.c ফাইলে, otSysButtonInit কলটির পরে main() ফাংশনে এই ফাংশন কলগুলো যোগ করুন।

/* 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 ফাইলে, main() ফাংশনের পরে setNetworkConfiguration ফাংশনের ইমপ্লিমেন্টেশনটি যোগ করুন:

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

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

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

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

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

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

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

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

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

ফাংশনে যেমন বিস্তারিতভাবে বলা হয়েছে, এই অ্যাপ্লিকেশনের জন্য আমরা যে থ্রেড নেটওয়ার্ক প্যারামিটারগুলো ব্যবহার করছি তা হলো:

  • চ্যানেল = ১৫
  • প্যান আইডি = 0x2222
  • বর্ধিত প্যান আইডি = C0DE1AB5C0DE1AB5
  • নেটওয়ার্ক কী = 1234C0DE1AB51234C0DE1AB51234C0DE
  • নেটওয়ার্কের নাম = ওটিকোডল্যাব

এছাড়াও, এখানেই আমরা রাউটার সিলেকশন জিটার (Router Selection Jitter) হ্রাস করি, যাতে ডেমো প্রদর্শনের উদ্দেশ্যে আমাদের ডিভাইসগুলো আরও দ্রুত ভূমিকা পরিবর্তন করতে পারে। উল্লেখ্য যে, এটি শুধুমাত্র তখনই করা হয় যখন নোডটি একটি এফটিডি (FTD - Full Thread Device) হয়। এ বিষয়ে পরবর্তী ধাপে আরও আলোচনা করা হবে।

৯. এপিআই: সীমাবদ্ধ ফাংশন

OpenThread-এর কিছু API এমন সেটিংস পরিবর্তন করে যা শুধুমাত্র ডেমো বা পরীক্ষার উদ্দেশ্যে পরিবর্তন করা উচিত। OpenThread ব্যবহার করে এমন কোনো অ্যাপ্লিকেশনের প্রোডাকশন ডেপ্লয়মেন্টে এই API-গুলো ব্যবহার করা উচিত নয়।

উদাহরণস্বরূপ, otThreadSetRouterSelectionJitter ফাংশনটি একটি এন্ড ডিভাইস-এর রাউটারে রূপান্তরিত হতে যে সময় লাগে, তা (সেকেন্ডে) সমন্বয় করে। থ্রেড স্পেসিফিকেশন অনুযায়ী, এই মানের ডিফল্ট হলো ১২০। এই কোডল্যাবে ব্যবহারের সুবিধার জন্য, আমরা এটিকে ২০-তে পরিবর্তন করব, যাতে একটি থ্রেড নোডের ভূমিকা পরিবর্তনের জন্য আপনাকে খুব বেশি সময় অপেক্ষা করতে না হয়।

দ্রষ্টব্য: MTD ডিভাইসগুলো রাউটার হয়ে ওঠে না, এবং otThreadSetRouterSelectionJitter মতো ফাংশনের সাপোর্ট একটি MTD বিল্ডে অন্তর্ভুক্ত থাকে না। পরবর্তীতে আমাদের CMake অপশন -DOT_MTD=OFF নির্দিষ্ট করতে হবে, অন্যথায় বিল্ড ব্যর্থ হবে।

আপনি OPENTHREAD_FTD নামক একটি প্রিপ্রসেসর ডিরেক্টিভের মধ্যে থাকা otThreadSetRouterSelectionJitter ফাংশনের সংজ্ঞাটি দেখে এটি নিশ্চিত করতে পারেন:

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

১০. CMake আপডেট

আপনার অ্যাপ্লিকেশনটি বিল্ড করার আগে, তিনটি CMake ফাইলে কয়েকটি ছোটখাটো আপডেট করার প্রয়োজন আছে। বিল্ড সিস্টেম আপনার অ্যাপ্লিকেশনটি কম্পাইল ও লিঙ্ক করার জন্য এই ফাইলগুলো ব্যবহার করে।

./third_party/NordicSemiconductor/CMakeLists.txt

এখন NordicSemiconductor 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

নতুন gpio.c সোর্স ফাইলটি যোগ করতে ./src/CMakeLists.txt ফাইলটি সম্পাদনা করুন:

করণীয়: ./src/CMakeLists.txt ফাইলে gpio সোর্সটি যোগ করুন

আপনার পছন্দের টেক্সট এডিটরে ./src/CMakeLists.txt ফাইলটি খুলুন এবং NRF_COMM_SOURCES সেকশনে ফাইলটি যুক্ত করুন।

...

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

...

./third_party/NordicSemiconductor/CMakeLists.txt

সবশেষে, nrfx_gpiote.c ড্রাইভার ফাইলটি NordicSemiconductor CMakeLists.txt ফাইলে যোগ করুন, যাতে এটি নর্ডিক ড্রাইভারের লাইব্রেরি বিল্ডের সাথে অন্তর্ভুক্ত থাকে।

করণীয়: NordicSemiconductor-এর CMakeLists.txt ফাইলে gpio ড্রাইভারটি যোগ করুন

আপনার পছন্দের টেক্সট এডিটরে ./third_party/NordicSemiconductor/CMakeLists.txt ফাইলটি খুলুন এবং COMMON_SOURCES সেকশনে ফাইলটি যুক্ত করুন।

...

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

১১. ডিভাইসগুলো সেট আপ করুন।

সমস্ত কোড আপডেট সম্পন্ন হলে, আপনি তিনটি নর্ডিক nRF52840 ডেভ বোর্ডেই অ্যাপ্লিকেশনটি বিল্ড এবং ফ্ল্যাশ করার জন্য প্রস্তুত। প্রতিটি ডিভাইস একটি ফুল থ্রেড ডিভাইস (FTD) হিসেবে কাজ করবে।

ওপেনথ্রেড তৈরি করুন

nRF52840 প্ল্যাটফর্মের জন্য OpenThread FTD বাইনারিগুলো বিল্ড করুন।

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

OpenThread FTD CLI বাইনারিটি যে ডিরেক্টরিতে রয়েছে, সেখানে যান এবং ARM Embedded Toolchain ব্যবহার করে এটিকে হেক্স ফরম্যাটে রূপান্তর করুন:

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

বোর্ডগুলো ফ্ল্যাশ করুন

প্রতিটি nRF52840 বোর্ডে ot-cli-ftd.hex ফাইলটি ফ্ল্যাশ করুন।

nRF52840 বোর্ডের এক্সটার্নাল পাওয়ার পিনের পাশে থাকা মাইক্রো-ইউএসবি ডিবাগ পোর্টে ইউএসবি কেবলটি সংযুক্ত করুন এবং তারপরে এটি আপনার লিনাক্স মেশিনে প্লাগ করুন। সঠিকভাবে সেট করলে, LED5 জ্বলে উঠবে।

20a3b4b480356447.png

পূর্বের ন্যায়, nRF52840 বোর্ডের সিরিয়াল নম্বরটি নোট করুন:

c00d519ebec7e5f0.jpeg

nRFx কমান্ড লাইন টুলস-এর অবস্থানে যান এবং বোর্ডের সিরিয়াল নম্বর ব্যবহার করে OpenThread CLI FTD হেক্স ফাইলটি nRF52840 বোর্ডে ফ্ল্যাশ করুন:

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

ফ্ল্যাশিং চলাকালীন LED5 অল্প সময়ের জন্য বন্ধ হয়ে যাবে। সফল হলে নিম্নলিখিত আউটপুট তৈরি হবে:

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

বাকি দুটি বোর্ডের জন্য এই 'বোর্ডগুলো ফ্ল্যাশ করুন' ধাপটি পুনরাবৃত্তি করুন। প্রতিটি বোর্ডকে লিনাক্স মেশিনের সাথে একই উপায়ে সংযুক্ত করতে হবে এবং ফ্ল্যাশ করার কমান্ডও একই, শুধুমাত্র বোর্ডের সিরিয়াল নম্বরটি ছাড়া। প্রতিটি বোর্ডের স্বতন্ত্র সিরিয়াল নম্বর ব্যবহার করা নিশ্চিত করুন।

nrfjprog ফ্ল্যাশিং কমান্ড।

সফল হলে, প্রতিটি বোর্ডে LED1, LED2 বা LED3 জ্বলে উঠবে। এমনকি ফ্ল্যাশ করার কিছুক্ষণ পরেই আপনি জ্বলে থাকা LED-টি ৩ থেকে ২ (বা ২ থেকে ১)-এ পরিবর্তিত হতেও দেখতে পারেন (এটি ডিভাইসের ভূমিকা পরিবর্তনের বৈশিষ্ট্য)।

১২. অ্যাপ্লিকেশনের কার্যকারিতা

এখন তিনটি nRF52840 বোর্ডেই পাওয়ার দেওয়া উচিত এবং সেগুলোতে আমাদের OpenThread অ্যাপ্লিকেশনটি চালু থাকা উচিত। পূর্বে যেমন বিস্তারিতভাবে বলা হয়েছে, এই অ্যাপ্লিকেশনটির দুটি প্রধান বৈশিষ্ট্য রয়েছে।

ডিভাইসের ভূমিকা নির্দেশক

প্রতিটি বোর্ডে জ্বলে থাকা এলইডি-টি থ্রেড নোডের বর্তমান ভূমিকা নির্দেশ করে:

  • LED1 = নেতা
  • LED2 = রাউটার
  • LED3 = প্রান্তিক ডিভাইস

ভূমিকা পরিবর্তনের সাথে সাথে জ্বলে থাকা এলইডি-টিও পরিবর্তিত হয়। প্রতিটি ডিভাইস চালু হওয়ার ২০ সেকেন্ডের মধ্যেই আপনি এক বা দুটি বোর্ডে এই পরিবর্তনগুলো দেখে ফেলার কথা।

ইউডিপি মাল্টিকাস্ট

যখন কোনো বোর্ডের বাটন১ চাপ দেওয়া হয়, তখন মেশ-লোকাল মাল্টিকাস্ট অ্যাড্রেসে একটি ইউডিপি মেসেজ পাঠানো হয়, যার মধ্যে থ্রেড নেটওয়ার্কের অন্য সব নোড অন্তর্ভুক্ত থাকে। এই মেসেজটি পাওয়ার প্রতিক্রিয়ায়, অন্য সব বোর্ডের এলইডি৪ জ্বলে ওঠে বা নিভে যায় । প্রতিটি বোর্ডের এলইডি৪ ততক্ষণ পর্যন্ত জ্বলে বা নিভে থাকে, যতক্ষণ না সেটি আরেকটি ইউডিপি মেসেজ পায়।

203dd094acca1f97.png9bbd96d9b1c63504.png

১৩. ডেমো: ডিভাইসের ভূমিকার পরিবর্তন পর্যবেক্ষণ করুন

আপনি যে ডিভাইসগুলো ফ্ল্যাশ করেছেন সেগুলো এক বিশেষ ধরনের ফুল থ্রেড ডিভাইস (FTD), যাকে রাউটার এলিজিবল এন্ড ডিভাইস (REED) বলা হয়। এর মানে হলো, এগুলো রাউটার বা এন্ড ডিভাইস উভয় হিসেবেই কাজ করতে পারে এবং এন্ড ডিভাইস থেকে নিজেদেরকে রাউটারে উন্নীত করতে পারে।

একটি থ্রেড সর্বোচ্চ ৩২টি রাউটার সমর্থন করতে পারে, তবে রাউটারের সংখ্যা ১৬ থেকে ২৩-এর মধ্যে রাখার চেষ্টা করে। যদি একটি REED এন্ড ডিভাইস হিসেবে সংযুক্ত হয় এবং রাউটারের সংখ্যা ১৬-এর কম থাকে, তবে এটি স্বয়ংক্রিয়ভাবে নিজেকে একটি রাউটারে উন্নীত করে। এই পরিবর্তনটি অ্যাপ্লিকেশনে আপনার সেট করা otThreadSetRouterSelectionJitter ভ্যালুর (২০ সেকেন্ড) মধ্যে যেকোনো এক সময়ে ঘটবে।

প্রতিটি থ্রেড নেটওয়ার্কে একজন লিডারও থাকে, যেটি এমন একটি রাউটার যা থ্রেড নেটওয়ার্কের রাউটারগুলোকে পরিচালনা করার দায়িত্বে থাকে। সমস্ত ডিভাইস চালু থাকা অবস্থায়, ২০ সেকেন্ড পর সেগুলোর মধ্যে একটি লিডার (LED1 চালু) এবং বাকি দুটি রাউটার (LED2 চালু) হওয়া উচিত।

4e1e885861a66570.png

নেতাকে অপসারণ করুন

যদি লিডারকে থ্রেড নেটওয়ার্ক থেকে সরিয়ে দেওয়া হয়, তবে নেটওয়ার্কে লিডার থাকা নিশ্চিত করার জন্য একটি ভিন্ন রাউটার নিজেকে লিডার হিসেবে উন্নীত করে।

পাওয়ার সুইচ ব্যবহার করে লিডার বোর্ডটি (যেটিতে LED1 জ্বলছে) বন্ধ করুন। প্রায় ২০ সেকেন্ড অপেক্ষা করুন। বাকি দুটি বোর্ডের মধ্যে একটিতে, LED2 (রাউটার) বন্ধ হয়ে যাবে এবং LED1 (লিডার) জ্বলে উঠবে। এই ডিভাইসটি এখন থ্রেড নেটওয়ার্কের লিডার।

4c57c87adb40e0e3.png

মূল লিডারবোর্ডটি আবার চালু করুন। এটি স্বয়ংক্রিয়ভাবে একটি এন্ড ডিভাইস হিসেবে থ্রেড নেটওয়ার্কে পুনরায় যোগদান করবে (LED3 জ্বলে উঠবে)। ২০ সেকেন্ডের মধ্যে (রাউটার সিলেকশন জিটার) এটি নিজেকে একটি রাউটারে উন্নীত করবে (LED2 জ্বলে উঠবে)।

5f40afca2dcc4b5b.png

বোর্ডগুলি রিসেট করুন

তিনটি বোর্ডই বন্ধ করুন, তারপর আবার চালু করুন এবং এলইডিগুলো পর্যবেক্ষণ করুন। যে বোর্ডটি প্রথমে চালু করা হয়েছিল, সেটি লিডার ভূমিকায় শুরু হবে (এলইডি১ জ্বলে থাকবে)—একটি থ্রেড নেটওয়ার্কের প্রথম রাউটারটি স্বয়ংক্রিয়ভাবে লিডার হয়ে যায়।

অন্য দুটি বোর্ড প্রাথমিকভাবে এন্ড ডিভাইস (LED3 জ্বলে থাকে) হিসেবে নেটওয়ার্কে সংযুক্ত হয়, কিন্তু ২০ সেকেন্ডের মধ্যে রাউটারে (LED2 জ্বলে থাকে) রূপান্তরিত হয়ে যাবে।

নেটওয়ার্ক পার্টিশন

যদি আপনার বোর্ডগুলো পর্যাপ্ত বিদ্যুৎ না পায়, অথবা সেগুলোর মধ্যেকার রেডিও সংযোগ দুর্বল হয়, তাহলে থ্রেড নেটওয়ার্কটি কয়েকটি ভাগে বিভক্ত হয়ে যেতে পারে এবং সেক্ষেত্রে একাধিক ডিভাইস লিডার হিসেবে প্রদর্শিত হতে পারে।

থ্রেডটি স্ব-নিরাময়যোগ্য, তাই পার্টিশনগুলো অবশেষে একজন লিডারসহ একটি একক পার্টিশনে একীভূত হয়ে যাবে।

১৪. ডেমো: ইউডিপি মাল্টিকাস্ট প্রেরণ

পূর্ববর্তী অনুশীলন থেকে চালিয়ে গেলে, কোনো ডিভাইসেই LED4 জ্বলবে না।

যেকোনো একটি বোর্ড বেছে নিন এবং বাটন১ চাপুন। অ্যাপ্লিকেশনটি চালিত থ্রেড নেটওয়ার্কের অন্য সব বোর্ডের LED4-এর অবস্থা পরিবর্তিত হবে। আগের অনুশীলন থেকে চালিয়ে গেলে, সেগুলো এখন চালু থাকবে।

f186a2618fdbe3fd.png

একই বোর্ডের জন্য বাটন ১ আবার চাপুন। অন্য সব বোর্ডের এলইডি ৪ আবার জ্বলে-নিভে যাবে।

অন্য একটি বোর্ডে বাটন১ চাপুন এবং লক্ষ্য করুন কীভাবে অন্য বোর্ডগুলোতে LED4 জ্বলে-নেভে। যে বোর্ডটিতে বর্তমানে LED4 জ্বলছে, সেটির বাটন১ চাপুন। সেই বোর্ডটির জন্য LED4 জ্বলেই থাকবে কিন্তু অন্য বোর্ডগুলোতে জ্বলে-নেভে।

f5865ccb8ab7aa34.png

নেটওয়ার্ক পার্টিশন

যদি আপনার বোর্ডগুলো বিভক্ত হয়ে যায় এবং সেগুলোর মধ্যে একাধিক লিডার থাকে, তাহলে মাল্টিকাস্ট মেসেজের ফলাফল বোর্ডভেদে ভিন্ন হবে। যদি আপনি বিভক্ত হয়ে যাওয়া কোনো বোর্ডের (এবং সেই কারণে যেটি বিভক্ত থ্রেড নেটওয়ার্কের একমাত্র সদস্য) বাটন ১ চাপেন, তাহলে এর প্রতিক্রিয়ায় অন্য বোর্ডগুলোর LED4 জ্বলে উঠবে না। এমনটা হলে, বোর্ডগুলো রিসেট করুন—আদর্শগতভাবে, সেগুলো পুনরায় একটি একক থ্রেড নেটওয়ার্ক গঠন করবে এবং UDP মেসেজিং সঠিকভাবে কাজ করবে।

১৫. অভিনন্দন!

আপনি এমন একটি অ্যাপ্লিকেশন তৈরি করেছেন যা OpenThread API ব্যবহার করে!

আপনি এখন জানেন:

  • নর্ডিক nRF52840 ডেভ বোর্ডের বাটন এবং এলইডি প্রোগ্রাম করার পদ্ধতি
  • সাধারণ OpenThread API এবং otInstance ক্লাস কীভাবে ব্যবহার করবেন
  • OpenThread অবস্থার পরিবর্তনগুলি কীভাবে পর্যবেক্ষণ ও তার প্রতিক্রিয়া জানাতে হয়
  • থ্রেড নেটওয়ার্কের সমস্ত ডিভাইসে কীভাবে UDP বার্তা পাঠাতে হয়
  • মেকফাইলগুলি কীভাবে পরিবর্তন করবেন

পরবর্তী পদক্ষেপ

এই কোডল্যাবের উপর ভিত্তি করে, নিম্নলিখিত অনুশীলনগুলি চেষ্টা করুন:

  • অনবোর্ড এলইডি-র পরিবর্তে GPIO পিন ব্যবহার করার জন্য GPIO মডিউলটি পরিবর্তন করুন, এবং রাউটারের ভূমিকার উপর ভিত্তি করে রঙ পরিবর্তনকারী বাহ্যিক RGB এলইডি সংযুক্ত করুন।
  • একটি ভিন্ন উদাহরণ প্ল্যাটফর্মের জন্য GPIO সমর্থন যোগ করুন
  • একটি বাটন চেপে সমস্ত ডিভাইস পিং করার জন্য মাল্টিকাস্ট ব্যবহার করার পরিবর্তে, কোনো একটি নির্দিষ্ট ডিভাইস সনাক্ত ও পিং করতে রাউটার/লিডার এপিআই ব্যবহার করুন।
  • একটি ওপেনথ্রেড বর্ডার রাউটার ব্যবহার করে আপনার মেশ নেটওয়ার্ককে ইন্টারনেটের সাথে সংযুক্ত করুন এবং এলইডিগুলো জ্বালানোর জন্য থ্রেড নেটওয়ার্কের বাইরে থেকে সেগুলোকে মাল্টিকাস্ট করুন।

আরও পড়ুন

বিভিন্ন ধরনের ওপেনথ্রেড রিসোর্সের জন্য openthread.io এবং GitHub দেখুন, যার মধ্যে রয়েছে:

তথ্যসূত্র: