Google は、黒人コミュニティのための人種的公平の促進に取り組んでいます。詳細をご覧ください。

OpenThreadAPIを使用した開発

26b7f4f6b3ea0700.png

OpenThread巣によって解放はのオープンソース実装であるThread®ネットワーキングプロトコル。 NestはOpenThreadをリリースし、Nest製品で使用されているテクノロジーを開発者が広く利用できるようにして、コネクテッドホーム向けの製品の開発を加速しています。

スレッド仕様はホームアプリケーションのための信頼性の高い、安全なIPv6ベースと低電力無線デバイス・ツー・デバイス通信プロトコルを定義します。 OpenThreadは、IPv6、6LoWPAN、MACセキュリティを備えたIEEE 802.15.4、メッシュリンク確立、メッシュルーティングを含むすべてのスレッドネットワーク層を実装します。

このCodelabでは、OpenThread APIを使用して、スレッドネットワークを開始し、デバイスロールの変更を監視して対応し、UDPメッセージを送信し、これらのアクションを実際のハードウェアのボタンとLEDに関連付けます。

2a6db2e258c32237.png

あなたが学ぶこと

  • 北欧のnRF52840開発ボードのボタンとLEDをプログラムする方法
  • 共通OpenThread APIと使用方法otInstanceクラスを
  • OpenThreadの状態変化を監視して対応する方法
  • スレッドネットワーク内のすべてのデバイスにUDPメッセージを送信する方法
  • Makefileを変更する方法

必要なもの

ハードウェア:

  • 3つのNordicSemiconductornRF52840開発ボード
  • ボードを接続するための3本のUSB-Micro-USBケーブル
  • 少なくとも3つのUSBポートを備えたLinuxマシン

ソフトウェア:

  • GNUツールチェーン
  • 北欧のnRF5xコマンドラインツール
  • SeggerJ-Linkソフトウェア
  • OpenThread
  • ギット

特記されている場合を除き、本コードラボの内容は下でライセンスされてクリエイティブ・コモンズ表示3.0ライセンス、およびコードサンプルは、下でライセンスされているApache 2.0のライセンス

ハードウェアコードラボを完成させる

このコードラボを開始する前に、あなたは、ビルドにスレッドnRF52840ボードとのネットワークとOpenThreadコードラボを完了する必要があります。

  • ビルドとフラッシュに必要なすべてのソフトウェアの詳細
  • OpenThreadを構築し、北欧のnRF52840ボードでフラッシュする方法を説明します
  • スレッドネットワークの基本を示します

このCodelabでは、OpenThreadの構築とボードのフラッシュに必要な環境設定について詳しく説明していません。ボードをフラッシュするための基本的な手順のみを説明しています。スレッドネットワークコードラボの構築がすでに完了していることを前提としています。

スレッドネットワークコードラボの構築を完了する

Linuxマシン

このCodelabは、i386またはx86ベースのLinuxマシンを使用して、すべてのスレッド開発ボードをフラッシュするように設計されています。すべてのステップは、Ubuntu 14.04.5 LTS(Trusty Tahr)でテストされました。

Nordic SemiconductornRF52840ボード

このコードラボは3枚の使用nRF52840 PDKボードを

a6693da3ce213856.png

ソフトウェアをインストールする

OpenThreadをビルドしてフラッシュするには、SEGGER J-Link、nRF5xコマンドラインツール、ARM GNUツールチェーン、およびさまざまなLinuxパッケージをインストールする必要があります。必要に応じて「スレッドネットワークコードラボの構築」を完了している場合は、必要なものがすべてインストールされています。そうでない場合は、OpenThreadをビルドしてnRF52840開発ボードにフラッシュできることを確認する前に、そのCodelabを完了してください。

スレッドネットワークコードラボの構築を完了する

OpenThreadには、このCodelabの開始点として使用できるサンプルアプリケーションコードが付属しています。

OpenThreadのクローンを作成してインストールします。

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

OpenThreadの公開APIは次の場所にあります。 ./openthread/include/openthread OpenThreadリポジトリに。これらのAPIは、アプリケーションで使用するために、スレッドレベルとプラットフォームレベルの両方でさまざまなOpenThreadの機能へのアクセスを提供します。

  • OpenThreadインスタンスの情報と制御
  • IPv6、UDP、CoAPなどのアプリケーションサービス
  • コミッショナーとジョイナーの役割に加えて、ネットワーククレデンシャル管理
  • ボーダールーター管理
  • 子の監視や紙詰まりの検出などの拡張機能

すべてのOpenThread APIのリファレンス情報は、用意されていopenthread.io/reference

APIの使用

APIを使用するには、そのヘッダーファイルをアプリケーションファイルの1つにインクルードします。次に、目的の関数を呼び出します。

たとえば、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インスタンス

otInstance構造は、OpenThreadのAPIで作業するときに頻繁に使用しますものです。初期化されると、この構造はOpenThreadライブラリの静的インスタンスを表し、ユーザーがOpenThreadAPI呼び出しを行えるようにします。

例えば、OpenThreadインスタンスが初期化されるmain() CLI例アプリの機能:

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

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

...

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

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

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

...

    return 0;
}

プラットフォーム固有の機能

あなたはOpenThreadに含まれているサンプルアプリケーションのいずれかにプラットフォーム固有の機能を追加したい場合は、最初に宣言し./openthread/examples/platforms/openthread-system.h使用して、ヘッダotSysすべての機能の名前空間を。次に、それらをプラットフォーム固有のソースファイルに実装します。このように抽象化すると、他のサンプルプラットフォームに同じ関数ヘッダーを使用できます。

例えば、我々はnRF52840のボタンとLEDにフックするために使用するつもりだGPIO機能がで宣言する必要がありopenthread-system.h

開き./openthread/examples/platforms/openthread-system.hお好みのテキストエディタでファイルを。

./openthread/examples/platforms/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関数宣言が使用するotInstance 。これにより、必要に応じて、ボタンが押されたときにアプリケーションがOpenThreadインスタンスに関する情報にアクセスできるようになります。それはすべて、アプリケーションのニーズによって異なります。あなたは、関数の実装でそれを必要としない場合は、使用することができOT_UNUSED_VARIABLE OpenThread APIからのいくつかのツールチェーンのための未使用の変数の周りの抑制ビルドエラーにマクロを。この例については後で説明します。

前のステップでは、我々は、プラットフォーム固有の関数宣言の上に行ってきました./openthread/examples/platforms/openthread-system.h GPIOのために使用することができます。 nRF52840開発ボードのボタンとLEDにアクセスするには、nRF52840プラットフォームにこれらの機能を実装する必要があります。このコードでは、次のような関数を追加します。

  • GPIOピンとモードを初期化します
  • ピンの電圧を制御する
  • GPIO割り込みを有効にし、コールバックを登録します

./src/srcディレクトリ、というファイルを作成gpio.c 。この新しいファイルに、次のコンテンツを追加します。

./src/src/gpio.c(新しいファイル)

アクション:定義を追加

これらの定義は、OpenThreadアプリケーションレベルで使用されるnRF52840固有の値と変数の間の抽象化として機能します。

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

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

#define GPIO_LOGIC_HI 0
#define GPIO_LOGIC_LOW 1

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

nRF52840のボタンとLEDの詳細については、を参照北欧半導体インフォメーション・センターを

アクション:ヘッダーの追加には次のものが含まれます

次に、GPIO機能に必要なヘッダーインクルードを追加します。

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

#include <string.h>

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

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

アクション:ボタン1のコールバック関数と割り込み関数を追加します

次にこのコードを追加します。 in_pin1_handler機能は、ボタンを押し機能は(後でこのファイルに)初期化されるときに登録されたコールバックです。

このコールバックがどのように使用するか注意OT_UNUSED_VARIABLEに渡された変数として、マクロをin_pin1_handler実際に関数の中で使用されていません。

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

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

アクション:LEDを構成する機能を追加します

このコードを追加して、初期化中にすべてのLEDのモードと状態を構成します。

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

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

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

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

アクション:LEDのモードを設定する機能を追加します。

この機能は、デバイスの役割が変更されたときに使用されます。

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

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

アクション:LEDのモードを切り替える機能を追加します。

この機能は、デバイスがマルチキャスト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;
    }
}

アクション:ボタンの押下を初期化および処理する機能を追加します。

最初の関数はボタンを押すとボードを初期化し、2番目の関数はボタン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インスタンスを使用し、2番目の部分にはGPIOプラットフォームの抽象化を使用します。

開き./openthread/examples/apps/cli/main.cお好みのテキストエディタでファイルを。

./openthread/examples/apps/cli/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インスタンスの状態の変更機能を。

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

このアプリケーションでは、1つのボードでButton1が押されたときに、ネットワーク内の他のすべてのデバイスにUDPメッセージを送信する必要もあります。メッセージの受信を確認するために、それに応じて他のボードのLED4を切り替えます。

この機能を有効にするには、アプリケーションで次のことを行う必要があります。

  • 起動時にUDP接続を初期化します
  • メッシュローカルマルチキャストアドレスにUDPメッセージを送信できる
  • 着信UDPメッセージを処理する
  • 着信UDPメッセージに応答してLED4を切り替えます

開き./openthread/examples/apps/cli/main.cお好みのテキストエディタでファイルを。

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

アクション:ヘッダーの追加には次のものが含まれます

上部のセクションが含までmain.cファイルを、あなたはマルチキャストUDPの機能のために必要がありますAPIのヘッダファイルを追加します。

#include <string.h>

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

#include "utils/code_utils.h"

code_utils.hヘッダが使用されotEXPECTotEXPECT_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上でマルチキャストを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;

アクション:GPIOLEDとボタンを初期化するための呼び出しを追加します

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);
    }
}

注意otEXPECTotEXPECT_ACTIONマクロを。 UDPメッセージが有効で、バッファに正しく割り当てられ、そうでない場合であることを確認してこれらのメイクは、機能が正常にジャンプすることでエラーを処理exit 、それはバッファを解放ブロック、。

参照のIPv6およびUDP 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);
}

デモンストレーションを簡単にするために、デバイスの電源がオンになると、デバイスがすぐにスレッドを開始し、ネットワークに参加するようにします。これを行うために、我々は、使用しますotOperationalDataset構造を。この構造は、スレッドネットワーククレデンシャルをデバイスに送信するために必要なすべてのパラメーターを保持します。

この構造を使用すると、OpenThreadに組み込まれているネットワークのデフォルトが上書きされ、アプリケーションがより安全になり、ネットワーク内のスレッドノードがアプリケーションを実行しているノードのみに制限されます。

ここでも、開い./openthread/examples/apps/cli/main.cお好みのテキストエディタでファイルを。

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

アクション:ヘッダーインクルードを追加

内の上部にある部分を含んでいるmain.c使用すると、スレッドのネットワークを設定する必要がありますAPIのヘッダファイルを追加し、ファイル:

#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 Key, PSKc, Security Policy
     */
    aDataset.mActiveTimestamp                      = 1;
    aDataset.mComponents.mIsActiveTimestampPresent = true;

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

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

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

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

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

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

関数で詳しく説明されているように、このアプリケーションに使用しているスレッドネットワークパラメーターは次のとおりです。

  • チャネル= 15
  • PAN ID = 0x2222
  • 拡張PANID = C0DE1AB5C0DE1AB5
  • ネットワークキー= 1234C0DE1AB51234C0DE1AB51234C0DE
  • ネットワーク名= OTCodelab

さらに、ここでルーター選択ジッターを減らし、デモの目的でデバイスの役割をすばやく変更します。これは、ノードがFTD(フルスレッドデバイス)の場合にのみ行われることに注意してください。これについては、次のステップで詳しく説明します。

OpenThreadのAPIの一部は、デモまたはテストの目的でのみ変更する必要がある設定を変更します。これらのAPIは、OpenThreadを使用するアプリケーションの実稼働デプロイメントでは使用しないでください。

例えば、 otThreadSetRouterSelectionJitter機能は、それがルータに自分自身を促進するためのエンドデバイスのにかかる時間(秒)を調整します。この値のデフォルトは、スレッド仕様に従って120です。このCodelabでの使いやすさのために、20に変更するので、スレッドノードがロールを変更するのをそれほど長く待つ必要はありません。

注:MTDデバイスがルータになっていない、などの機能をサポートotThreadSetRouterSelectionJitter MTDのビルドに含まれていません。その後、私たちはCMakeのオプションを指定する必要があります-DOT_MTD=OFFそれ以外の場合は、我々は、ビルドの失敗が発生します。

あなたはを見て、これを確認することができますotThreadSetRouterSelectionJitterのプリプロセッサディレクティブ内に含まれている関数定義、 OPENTHREAD_FTD

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

#if OPENTHREAD_FTD

#include <openthread/thread_ftd.h>

...

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

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

...

#endif // OPENTHREAD_FTD

アプリケーションをビルドする前に、3つの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ファイル。

アクション:gpioソースをに追加します

./src/CMakeLists.txt

ファイル

オープン./src/CMakeLists.txtお好みのテキストエディタで、とにファイルを追加NRF_COMM_SOURCESセクション。

...

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

...

./third_party/NordicSemiconductor/CMakeLists.txt

最後に、追加nrfx_gpiote.c NordicSemiconductorにドライバファイルをCMakeLists.txtそれは北欧のドライバーのライブラリのビルドに含まれていますので、ファイル。

アクション:gpioドライバーをNordicSemiconductorCMakeLists.txtに追加します

オープン./third_party/NordicSemiconductor/CMakeLists.txtお好みのテキストエディタで、とにファイルを追加COMMON_SOURCESセクション。

...

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

すべてのコード更新が完了したら、アプリケーションをビルドして、3つのNordicnRF52840開発ボードすべてにフラッシュする準備が整いました。各デバイスはフルスレッドデバイス(FTD)として機能します。

OpenThreadを構築する

nRF52840プラットフォーム用のOpenThreadバイナリをビルドします。

$ cd ~/ot-nrf528xx
$ ./script/build nrf52840 UART_trans

OpenThread FTD CLIバイナリを使用してディレクトリに移動し、ARM EmbeddedToolchainを使用して16進形式に変換します。

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

ボードをフラッシュします

フラッシュot-cli-ftd.hex各nRF52840ボードにファイルを。

次のnRF52840ボード上の外部電源端子にマイクロUSBデバッグポートにUSBケーブルを接続して、あなたのLinuxマシンに接続します。正しく設定し、LED5がオンになっています。

20a3b4b480356447.png

前と同じように、nRF52840ボードのシリアル番号に注意してください。

c00d519ebec7e5f0.jpeg

nRFxコマンドラインツールの場所に移動し、ボードのシリアル番号を使用して、OpenThread CLIFTDの16進ファイルを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.

他の2つのボードについて、この「ボードのフラッシュ」手順を繰り返します。各ボードは同じ方法でLinuxマシンに接続する必要があり、フラッシュするコマンドはボードのシリアル番号を除いて同じです。で各ボードの一意のシリアル番号を使用してください

nrfjprogコマンドを点滅。

成功すると、各ボードでLED1、LED2、またはLED3のいずれかが点灯します。点滅した直後に、点灯しているLEDが3から2(または2から1)に切り替わる場合もあります(デバイスの役割変更機能)。

これで、3つのnRF52840ボードすべてに電力が供給され、OpenThreadアプリケーションが実行されます。前に詳しく説明したように、このアプリケーションには2つの主要な機能があります。

デバイスの役割のインジケーター

各ボードの点灯しているLEDは、スレッドノードの現在の役割を反映しています。

  • LED1 =リーダー
  • LED2 =ルーター
  • LED3 =エンドデバイス

役割が変わると、点灯しているLEDも変わります。各デバイスの電源を入れてから20秒以内に、ボード上でこれらの変更をすでに確認しているはずです。

UDPマルチキャスト

ボード上でButton1が押されると、UDPメッセージがメッシュローカルマルチキャストアドレスに送信されます。これには、スレッドネットワーク内の他のすべてのノードが含まれます。オンまたはオフのすべての他のボードのトグルにこのメッセージ、LED4を受信することに応答して。 LED4は、別のUDPメッセージを受信するまで、各ボードでオンまたはオフのままです。

203dd094acca1f97.png

9bbd96d9b1c63504.png

フラッシュしたデバイスは、ルーター適格エンドデバイス(REED)と呼ばれる特定の種類のフルスレッドデバイス(FTD)です。これは、ルーターまたはエンドデバイスとして機能し、エンドデバイスからルーターに昇格できることを意味します。

スレッドは最大32のルーターをサポートできますが、ルーターの数を16〜23に維持しようとします。REEDがエンドデバイスとして接続され、ルーターの数が16未満の場合、スレッドは自動的にルーターに昇格します。この変更は、ユーザーが設定する秒数以内にランダムな時間で発生しますotThreadSetRouterSelectionJitterアプリケーション(20秒)内に値を。

すべてのスレッドネットワークにはリーダーもあります。リーダーは、スレッドネットワーク内のルーターのセットの管理を担当するルーターです。すべてのデバイスがオンの状態で、20秒後に、そのうちの1つがリーダー(LED1がオン)になり、他の2つがルーター(LED2がオン)になります。

4e1e885861a66570.png

リーダーを削除する

リーダーがスレッドネットワークから削除された場合、ネットワークにリーダーが残っていることを確認するために、別のルーターがリーダーに昇格します。

電源スイッチを使用して(LED1との1が点灯)リーダーボードの電源をオフにします。約20秒待ちます。残りの2つのボードの1つで、LED2(ルーター)がオフになり、LED1(リーダー)がオンになります。このデバイスは現在、スレッドネットワークのリーダーです。

4c57c87adb40e0e3.png

元のリーダーボードをオンに戻します。エンドデバイスとしてスレッドネットワークに自動的に再参加する必要があります(LED3が点灯します)。 20秒以内(ルーター選択ジッター)にルーターに昇格します(LED2が点灯します)。

5f40afca2dcc4b5b.png

ボードをリセットします

3つのボードをすべてオフにしてから、再度オンにしてLEDを確認します。電源が入った最初のボードはリーダーの役割で開始する必要があります(LED1が点灯します)。スレッドネットワークの最初のルーターが自動的にリーダーになります。

他の2つのボードは、最初はエンドデバイス(LED3が点灯)としてネットワークに接続しますが、20秒以内にルーター(LED2が点灯)に昇格する必要があります。

ネットワークパーティション

ボードに十分な電力が供給されていない場合、またはボード間の無線接続が弱い場合、スレッドネットワークがパーティションに分割され、複数のデバイスがリーダーとして表示される可能性があります。

スレッドは自己回復するため、パーティションは最終的に1つのリーダーを持つ単一のパーティションにマージされます。

前の演習から続行する場合は、どのデバイスでもLED4を点灯しないでください。

ボードを選択してButton1を押します。アプリケーションを実行しているスレッドネットワーク内の他のすべてのボードのLED4は、それらの状態を切り替える必要があります。前の演習から続行する場合は、オンになっているはずです。

f186a2618fdbe3fd.png

同じボードのButton1をもう一度押します。他のすべてのボードのLED4は再び切り替わるはずです。

別のボードでButton1を押して、他のボードでLED4がどのように切り替わるかを観察します。 LED4が現在オンになっているボードの1つでButton1を押します。 LED4はそのボードではオンのままですが、他のボードではオンになります。

f5865ccb8ab7aa34.png

ネットワークパーティション

ボードがパーティション化されていて、その中にリーダーが複数ある場合、マルチキャストメッセージの結果はボード間で異なります。パーティション化された(したがって、パーティション化されたスレッドネットワークの唯一のメンバーである)ボードでButton1を押すと、他のボードのLED4はそれに応じて点灯しません。これが発生した場合は、ボードをリセットします。理想的には、ボードはシングルスレッドネットワークを再構築し、UDPメッセージングが正しく機能するはずです。

OpenThreadAPIを使用するアプリケーションを作成しました。

あなたは今知っています:

  • 北欧のnRF52840開発ボードのボタンとLEDをプログラムする方法
  • 共通OpenThread APIと使用方法otInstanceクラスを
  • OpenThreadの状態変化を監視して対応する方法
  • スレッドネットワーク内のすべてのデバイスにUDPメッセージを送信する方法
  • Makefileを変更する方法

次のステップ

このCodelabを基に、次の演習を試してください。

  • オンボードLEDの代わりにGPIOピンを使用するようにGPIOモジュールを変更し、ルーターの役割に基づいて色が変わる外部RGBLEDを接続します
  • 別のサンプルプラットフォームのGPIOサポートを追加する
  • 代わりに、ボタンを押してから、すべてのデバイスにpingを実行するために、マルチキャストを使用しての、使用ルーター/リーダーAPIを見つけて、個々のデバイスにpingを実行します
  • あなたのメッシュネットワークは、使用してインターネットに接続OpenThread境界ルータをLEDの光にスレッドのネットワークの外からそれらをマルチキャスト

参考文献

チェックアウトopenthread.ioGitHubのを含め、OpenThread資源の多様性のために:

リファレンス: