OpenThread API로 개발

1. 소개

26b7f4f6b3ea0700.png

OpenThread 둥지 발표는의 오픈 소스 구현입니다 Thread® 네트워킹 프로토콜입니다. Nest는 Connected Home용 제품 개발을 가속화하기 위해 개발자가 Nest 제품에 사용되는 기술을 광범위하게 사용할 수 있도록 OpenThread를 출시했습니다.

나사 규격은 홈 애플리케이션을위한 신뢰성, 보안 IPv6 기반 저전력 무선 장치 간 통신 프로토콜을 정의합니다. OpenThread는 IPv6, 6LoWPAN, MAC 보안이 포함된 IEEE 802.15.4, 메시 링크 설정 및 메시 라우팅을 포함한 모든 스레드 네트워킹 계층을 구현합니다.

이 Codelab에서는 OpenThread API를 사용하여 스레드 네트워크를 시작하고, 장치 역할의 변경 사항을 모니터링 및 대응하고, UDP 메시지를 보내고, 이러한 작업을 실제 하드웨어의 버튼 및 LED에 연결합니다.

2a6db2e258c32237.png

배울 내용

  • Nordic nRF52840 개발 보드의 버튼과 LED를 프로그래밍하는 방법
  • 일반적인 OpenThread API 및 사용 방법 otInstance 클래스
  • OpenThread 상태 변경을 모니터링하고 대응하는 방법
  • 스레드 네트워크의 모든 장치에 UDP 메시지를 보내는 방법
  • Makefile을 수정하는 방법

필요한 것

하드웨어:

  • 3 Nordic Semiconductor nRF52840 개발 기판
  • 보드를 연결하기 위한 3개의 USB-Micro-USB 케이블
  • 최소 3개의 USB 포트가 있는 Linux 시스템

소프트웨어:

  • GNU 툴체인
  • Nordic nRF5x 명령줄 도구
  • Segger J-링크 소프트웨어
  • 오픈스레드
  • 힘내

별도로 언급 된 경우를 제외하고,이 코드 랩의 내용은 아래에 라이센스 크리 에이 티브 커먼즈 저작자 3.0 라이센스 및 코드 샘플은에 의거하여 라이센스가 부여됩니다 아파치 2.0 라이센스 .

2. 시작하기

하드웨어 Codelab 완료

이 코드 랩을 시작하기 전에 빌드 nRF52840 보드 및 OpenThread 코드 랩과 스레드 네트워크를 완료해야한다 :

  • 빌드 및 플래싱에 필요한 모든 소프트웨어를 자세히 설명합니다.
  • OpenThread를 구축하고 Nordic nRF52840 보드에서 플래시하는 방법을 알려줍니다.
  • 스레드 네트워크의 기본을 보여줍니다.

OpenThread를 빌드하고 보드를 플래시하는 데 필요한 환경 설정은 이 Codelab에 자세히 설명되어 있지 않으며 보드 플래시에 대한 기본 지침만 있습니다. 스레드 네트워크 구축 Codelab을 이미 완료했다고 가정합니다.

스레드 네트워크 구축 Codelab 완료

리눅스 머신

이 Codelab은 i386 또는 x86 기반 Linux 시스템을 사용하여 모든 스레드 개발 보드를 플래시하도록 설계되었습니다. 모든 단계는 Ubuntu 14.04.5 LTS(Trusty Tahr)에서 테스트되었습니다.

Nordic Semiconductor nRF52840 보드

이 코드 랩은 세 가지 사용 nRF52840 PDK 보드 .

a6693da3ce213856.png

소프트웨어 설치

OpenThread를 빌드하고 플래시하려면 SEGGER J-Link, nRF5x 명령줄 도구, ARM GNU 도구 체인 및 다양한 Linux 패키지를 설치해야 합니다. 필요에 따라 Build a Thread Network Codelab을 완료했다면 필요한 모든 것이 이미 설치된 것입니다. 그렇지 않은 경우 계속해서 OpenThread를 빌드하고 nRF52840 개발 보드에 플래시할 수 있는지 확인하기 전에 해당 Codelab을 완료하십시오.

스레드 네트워크 구축 Codelab 완료

3. 저장소 복제

OpenThread는 이 Codelab의 시작점으로 사용할 수 있는 예제 애플리케이션 코드와 함께 제공됩니다.

OpenThread 복제 및 설치:

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

4. OpenThread API 기초

OpenThread의 공개 API는에 있습니다 ./openthread/include/openthread OpenThread 저장소한다. 이러한 API는 애플리케이션에서 사용하기 위해 스레드 및 플랫폼 수준 모두에서 다양한 OpenThread 기능에 대한 액세스를 제공합니다.

  • OpenThread 인스턴스 정보 및 제어
  • IPv6, UDP, CoAP 등의 애플리케이션 서비스
  • 커미셔너 및 조이너 역할과 함께 네트워크 자격 증명 관리
  • 경계 라우터 관리
  • 아동 감독 및 잼 감지와 같은 향상된 기능

모든 OpenThread API에 대한 참조 정보에서 확인할 수 있습니다 openthread.io/reference .

API 사용

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 인스턴스

otInstance 구조는 OpenThread API를 사용하여 작업 할 때 자주 사용하는 것입니다 무언가이다. 일단 초기화되면 이 구조는 OpenThread 라이브러리의 정적 인스턴스를 나타내며 사용자가 OpenThread API 호출을 할 수 있도록 합니다.

예를 들어, 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 은 Using, 헤더 otSys 모든 기능에 대한 네임 스페이스를. 그런 다음 플랫폼별 소스 파일에서 구현합니다. 이런 식으로 추상화하면 다른 예제 플랫폼에 동일한 함수 헤더를 사용할 수 있습니다.

예를 들어, 우리는 nRF52840 버튼 및 LED로 후크를 사용하려고하고있는 GPIO 기능에 선언해야 openthread-system.h .

오픈 ./openthread/examples/platforms/openthread-system.h 원하는 텍스트 편집기에서 파일을.

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

조치: 플랫폼별 GPIO 함수 선언 추가

애프터 이러한 함수 선언을 추가 #include 에 대한 openthread/instance.h 헤더 :

/**
 * Init LED module.
 *
 */
void otSysLedInit(void);
void otSysLedSet(uint8_t aLed, bool aOn);
void otSysLedToggle(uint8_t aLed);

/**
* A callback will be called when GPIO interrupts occur.
*
*/
typedef void (*otSysButtonCallback)(otInstance *aInstance);
void otSysButtonInit(otSysButtonCallback aCallback);
void otSysButtonProcess(otInstance *aInstance);

다음 단계에서 이를 구현할 것입니다.

있습니다 otSysButtonProcess 함수 선언이 사용 otInstance . 그렇게 하면 필요한 경우 버튼을 눌렀을 때 애플리케이션이 OpenThread 인스턴스에 대한 정보에 액세스할 수 있습니다. 그것은 모두 응용 프로그램의 요구 사항에 따라 다릅니다. 이 기능의 구현에 필요하지 않은 경우에는 사용할 수 OT_UNUSED_VARIABLE 일부 툴체인 사용하지 변수 주위 억제 빌드 오류에 OpenThread API의 매크로. 나중에 이에 대한 예를 살펴보겠습니다.

5. GPIO 플랫폼 추상화 구현

이전 단계에서, 우리의 플랫폼 별 함수 선언 위로 넘어가 ./openthread/examples/platforms/openthread-system.h GPIO 사용할 수 있습니다. nRF52840 개발 보드의 버튼과 LED에 액세스하려면 nRF52840 플랫폼에 대해 해당 기능을 구현해야 합니다. 이 코드에서는 다음과 같은 기능을 추가합니다.

  • GPIO 핀 및 모드 초기화
  • 핀의 전압 제어
  • GPIO 인터럽트 활성화 및 콜백 등록

에서 ./src/src 디렉토리라는 파일 생성 gpio.c . 이 새 파일에 다음 내용을 추가합니다.

./src/src/gpio.c (새 파일)

조치: 정의 추가

이러한 정의는 nRF52840 관련 값과 OpenThread 애플리케이션 수준에서 사용되는 변수 간의 추상화 역할을 합니다.

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

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

#define GPIO_LOGIC_HI 0
#define GPIO_LOGIC_LOW 1

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

nRF52840 버튼 및 LED에 대한 자세한 내용은 참조 노르딕 세미 컨덕터 정보 센터를 .

조치: 헤더 추가 포함

다음으로 GPIO 기능에 필요한 헤더를 추가합니다.

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

#include <string.h>

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

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

조치: 버튼 1에 대한 콜백 및 인터럽트 기능 추가

다음에 이 코드를 추가하십시오. in_pin1_handler 기능은 버튼 누름 기능이 (이후에이 파일)를 초기화 할 때 등록 된 콜백이다.

이 콜백을 사용하는 방법을 참고 OT_UNUSED_VARIABLE 에 전달 된 변수로, 매크로를 in_pin1_handler 실제로 함수에서 사용되지 않습니다.

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

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

조치: LED를 구성하는 기능 추가

초기화 중에 모든 LED의 모드와 상태를 구성하려면 이 코드를 추가하십시오.

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

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

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

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

조치: LED의 모드를 설정하는 기능을 추가하십시오.

이 기능은 장치의 역할이 변경될 때 사용됩니다.

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

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

조치: LED 모드를 토글하는 기능을 추가하십시오.

이 기능은 장치가 멀티캐스트 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;
    }
}

조치: 버튼 누름을 초기화하고 처리하는 기능을 추가하십시오.

첫 번째 기능은 버튼 누름에 대해 보드를 초기화하고 두 번째 기능은 버튼 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 파일.

6. API: 기기 역할 변경에 대응

우리 애플리케이션에서는 장치 역할에 따라 다른 LED가 켜지길 원합니다. 리더, 라우터, 최종 장치 역할을 추적해 보겠습니다. 다음과 같이 LED에 할당할 수 있습니다.

  • LED1 = 리더
  • LED2 = 라우터
  • LED3 = 최종 장치

이 기능을 활성화하려면 애플리케이션이 장치 역할이 변경된 시점과 응답으로 올바른 LED를 켜는 방법을 알아야 합니다. 첫 번째 부분에서는 OpenThread 인스턴스를 사용하고 두 번째 부분에서는 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;
        }
    }
}

7. API: 멀티캐스트를 사용하여 LED 켜기

우리 응용 프로그램에서는 한 보드에서 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;

조치: GPIO LED 및 버튼 초기화를 위한 호출 추가

에서 main.c 는 이러한 함수 호출을 추가 main() 애프터 기능 otSetStateChangedCallback 호출. 이 함수는 GPIO 및 GPIOTE 핀을 초기화하고 버튼 푸시 이벤트를 처리하도록 버튼 핸들러를 설정합니다.

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

조치: UDP 초기화 호출 추가

에서 main.c 는이 기능을 추가 main() 애프터 기능 otSysButtonInit 방금 추가 전화 :

initUdp(instance);

이 호출은 애플리케이션 시작 시 UDP 소켓이 초기화되도록 합니다. 이것이 없으면 장치는 UDP 메시지를 보내거나 받을 수 없습니다.

조치: GPIO 버튼 이벤트를 처리하기 위한 호출 추가

에서 main.c 는이 함수 호출을 추가 main() 애프터 함수 otSysProcessDrivers 호출에, while 루프. 선언이 기능 gpio.c , 체크 버튼이 눌러 진 경우와 만약 그렇다면, 핸들러 (호출 handleButtonInterrupt 상기 단계에서 설정 하였다).

otSysButtonProcess(instance);

조치: 버튼 인터럽트 핸들러 구현

에서 main.c ,의 구현 추가 handleButtonInterrupt 애프터 기능 handleNetifStateChanged 이전 단계에서 추가 기능을 :

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

조치: UDP 초기화 구현

에서 main.c ,의 구현 추가 initUdp 애프터 기능 handleButtonInterrupt 방금 추가 기능 :

/**
 * Initialize UDP socket
 */
void initUdp(otInstance *aInstance)
{
    otSockAddr  listenSockAddr;

    memset(&sUdpSocket, 0, sizeof(sUdpSocket));
    memset(&listenSockAddr, 0, sizeof(listenSockAddr));

    listenSockAddr.mPort    = UDP_PORT;

    otUdpOpen(aInstance, &sUdpSocket, handleUdpReceive, aInstance);
    otUdpBind(aInstance, &sUdpSocket, &listenSockAddr, OT_NETIF_THREAD);
}

UDP_PORT 이전에 정의 된 포트 (1212)입니다. otUdpOpen 함수는 소켓 레지스터 콜백 함수 (열리고 handleUdpReceive 은 UDP 메시지가 수신되는 경우를위한). otUdpBind 전달하여 스레드의 네트워크 인터페이스에 소켓을 결합 OT_NETIF_THREAD . 다른 네트워크 인터페이스 옵션의 경우, 참조 otNetifIdentifier 에서 열거 UDP API 참조 .

조치: 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 가 버퍼를 해제 블록.

참고 항목 의 IPv6UDP 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);
}

8. API: 스레드 네트워크 구성

쉽게 시연할 수 있도록 장치가 Thread를 즉시 시작하고 전원이 켜질 때 네트워크에 함께 연결되기를 원합니다. 이를 위해, 우리는 사용합니다 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
  • 팬 ID = 0x2222
  • 확장 PAN ID = C0DE1AB5C0DE1AB5
  • 네트워크 키 = 1234C0DE1AB51234C0DE1AB51234C0DE
  • 네트워크 이름 = OTCodelab

또한 여기에서 라우터 선택 지터를 줄여 장치가 데모 목적으로 더 빠르게 역할을 변경합니다. 이것은 노드가 FTD(전체 스레드 장치)인 경우에만 수행됩니다. 다음 단계에서 이에 대해 자세히 설명합니다.

9. API: 제한된 기능

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

10. 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 파일입니다.

조치: 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 이 북유럽 드라이버 라이브러리 빌드에 포함, 그래서 파일을.

조치: NordicSemiconductor CMakeLists.txt에 gpio 드라이버 추가

열기 ./third_party/NordicSemiconductor/CMakeLists.txt 원하는 텍스트 편집기에서, 그리고에 파일을 추가 COMMON_SOURCES 섹션을 참조하십시오.

...

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

11. 장치 설정

모든 코드 업데이트가 완료되면 애플리케이션을 빌드하고 3개의 Nordic nRF52840 개발 보드 모두에 플래시할 준비가 된 것입니다. 각 장치는 전체 스레드 장치(FTD)로 작동합니다.

OpenThread 빌드

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을 사용하여 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 케이블을 연결 한 다음 리눅스 머신에 연결합니다. 올바르게 설정 LED5이 켜져 있습니다.

20a3b4b480356447.png

이전과 마찬가지로 nRF52840 보드의 일련 번호를 확인합니다.

c00d519ebec7e5f0.jpeg

nRFx 명령줄 도구의 위치로 이동하고 보드의 일련 번호를 사용하여 OpenThread CLI FTD 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.

다른 두 보드에 대해 이 "보드 플래시" 단계를 반복합니다. 각 보드는 동일한 방식으로 Linux 머신에 연결되어야 하며, 보드의 일련 번호를 제외하고 플래시 명령은 동일합니다. 각 보드의 고유한 일련 번호를 사용하십시오.

nrfjprog 명령을 깜박.

성공하면 LED1, LED2 또는 LED3이 각 보드에서 켜집니다. 깜박이는 직후 LED 스위치가 3에서 2(또는 2에서 1)로 켜진 것을 볼 수도 있습니다(장치 역할 변경 기능).

12. 애플리케이션 기능

이제 3개의 nRF52840 보드 모두에 전원이 공급되고 OpenThread 애플리케이션을 실행해야 합니다. 앞에서 자세히 설명했듯이 이 응용 프로그램에는 두 가지 주요 기능이 있습니다.

장치 역할 표시기

각 보드의 켜진 LED는 스레드 노드의 현재 역할을 반영합니다.

  • LED1 = 리더
  • LED2 = 라우터
  • LED3 = 최종 장치

역할이 바뀌면 켜진 LED도 바뀝니다. 각 장치의 전원이 켜진 후 20초 이내에 보드에서 이러한 변경 사항을 이미 보았을 것입니다.

UDP 멀티캐스트

보드에서 Button1을 누르면 UDP 메시지가 Thread 네트워크의 다른 모든 노드를 포함하는 메시 로컬 멀티캐스트 주소로 전송됩니다. 또는 해제 모두에 다른 보드 토글이 메시지, LED4의 수신에 응답. LED4는 다른 UDP 메시지를 수신할 때까지 각 보드에 대해 켜져 있거나 꺼져 있습니다.

203dd094acca1f97.png

9bbd96d9b1c63504.png

13. 데모: 장치 역할 변경 관찰

플래시한 장치는 REED(Router Eligible End Device)라고 하는 특정 종류의 FTD(Full Thread Device)입니다. 이는 라우터 또는 최종 장치로 기능할 수 있고 최종 장치에서 라우터로 승격될 수 있음을 의미합니다.

스레드는 최대 32개의 라우터를 지원할 수 있지만 라우터 수를 16~23개 사이로 유지하려고 합니다. REED가 최종 장치로 연결되고 라우터 수가 16개 미만이면 자동으로 라우터로 승격됩니다. 이러한 변화는 사용자가 설정 한 시간 (초) 내에서 임의의 시간에 발생한다 otThreadSetRouterSelectionJitter 응용 프로그램 (20초)의 가치를.

모든 스레드 네트워크에는 스레드 네트워크에서 라우터 집합을 관리하는 책임이 있는 라우터인 리더도 있습니다. 모든 장치가 켜진 상태에서 20초 후에 그 중 하나는 리더(LED1 켜짐)이고 다른 두 장치는 라우터(LED2 켜짐)가 되어야 합니다.

4e1e885861a66570.png

지시선 제거

리더가 스레드 네트워크에서 제거되면 다른 라우터가 자신을 리더로 승격시켜 네트워크에 여전히 리더가 있는지 확인합니다.

전원 스위치를 사용하여 리더 보드 (LED1과 하나가 점등)의 전원을 끕니다. 20초 정도 기다립니다. 나머지 두 보드 중 하나에서 LED2(라우터)가 꺼지고 LED1(리더)이 켜집니다. 이 장치는 이제 스레드 네트워크의 리더입니다.

4c57c87adb40e0e3.png

원래 리더 보드를 다시 켭니다. 스레드 네트워크에 자동으로 최종 장치로 다시 연결되어야 합니다(LED3 켜짐). 20초 이내에(라우터 선택 지터) 라우터로 승격됩니다(LED2 켜짐).

5f40afca2dcc4b5b.png

보드 재설정

세 개의 보드를 모두 끈 다음 다시 켜고 LED를 관찰합니다. 전원이 켜진 첫 번째 보드는 리더 역할(LED1 켜짐)에서 시작해야 합니다. 스레드 네트워크의 첫 번째 라우터는 자동으로 리더가 됩니다.

다른 두 보드는 처음에 최종 장치로 네트워크에 연결되지만(LED3 켜짐) 20초 이내에 라우터로 승격되어야 합니다(LED2 켜짐).

네트워크 파티션

보드에 충분한 전력이 공급되지 않거나 보드 간의 무선 연결이 약한 경우 스레드 네트워크가 파티션으로 분할될 수 있으며 리더로 표시되는 장치가 두 개 이상 있을 수 있습니다.

스레드는 자가 치유되므로 파티션은 결국 하나의 리더가 있는 단일 파티션으로 다시 병합되어야 합니다.

14. 데모: UDP 멀티캐스트 보내기

이전 연습에서 계속하면 LED4가 어떤 장치에서도 켜지지 않아야 합니다.

아무 보드나 선택하고 Button1을 누릅니다. 애플리케이션을 실행하는 스레드 네트워크의 다른 모든 보드에 있는 LED4는 상태를 토글해야 합니다. 이전 연습에서 계속하면 이제 켜져 있어야 합니다.

f186a2618fdbe3fd.png

같은 보드에 대해 Button1을 다시 누릅니다. 다른 모든 보드의 LED4는 다시 토글해야 합니다.

다른 보드에서 Button1을 누르고 다른 보드에서 LED4가 어떻게 토글되는지 관찰하십시오. LED4가 현재 켜져 있는 보드 중 하나에서 Button1을 누릅니다. LED4는 해당 보드에 대해 켜져 있지만 다른 보드는 토글합니다.

f5865ccb8ab7aa34.png

네트워크 파티션

보드가 분할되어 있고 둘 이상의 리더가 있는 경우 멀티캐스트 메시지의 결과는 보드마다 다릅니다. 분할된 보드에서 Button1을 누르면(따라서 분할된 스레드 네트워크의 유일한 구성원임) 다른 보드의 LED4는 응답으로 켜지지 않습니다. 이런 일이 발생하면 보드를 재설정하십시오. 이상적으로는 단일 스레드 네트워크를 재구성하고 UDP 메시징이 올바르게 작동해야 합니다.

15. 축하합니다!

OpenThread API를 사용하는 애플리케이션을 만들었습니다!

이제 알 수 있습니다.

  • Nordic nRF52840 개발 보드에서 버튼과 LED를 프로그래밍하는 방법
  • 일반적인 OpenThread API 및 사용 방법 otInstance 클래스
  • OpenThread 상태 변경을 모니터링하고 대응하는 방법
  • 스레드 네트워크의 모든 장치에 UDP 메시지를 보내는 방법
  • Makefile을 수정하는 방법

다음 단계

이 Codelab을 기반으로 다음 연습을 시도해 보세요.

  • 온보드 LED 대신 GPIO 핀을 사용하도록 GPIO 모듈을 수정하고 라우터 역할에 따라 색상이 변하는 외부 RGB LED를 연결합니다.
  • 다른 예제 플랫폼에 대한 GPIO 지원 추가
  • 대신 버튼을 눌러에서 모든 장치를 핑 (ping) 멀티 캐스트를 사용하는 사용하는 라우터 / 리더 API를 찾아 개별 장치를 핑 (ping)
  • 사용하여 인터넷에 메쉬 네트워크를 연결 OpenThread 국경 라우터 의 LED 빛에 스레드 네트워크 외부에서 그들을 캐스트 및 멀티 캐스트

추가 읽기

확인 openthread.ioGitHub의를 OpenThread 자원을 포함하는 다양한 :

참조: