OpenThread API'leri ile Geliştirme

1. Giriş

26b7f4f6b3ea0700.png

OpenThread Nest tarafından yayımlanan bir açık kaynak uygulamasıdır Thread® ağ protokolü. Nest, bağlantılı eve yönelik ürünlerin geliştirilmesini hızlandırmak amacıyla Nest ürünlerinde kullanılan teknolojiyi geliştiricilerin geneline sunmak için OpenThread'i piyasaya sürdü.

Konu şartname ev uygulamaları için bir güvenilir, güvenli IPv6 tabanlı ve düşük güç kablosuz cihazdan cihaza iletişim protokolünü tanımlar. OpenThread, IPv6, 6LoWPAN, IEEE 802.15.4, MAC güvenliği, Mesh Bağlantısı Kurulumu ve Mesh Yönlendirme dahil olmak üzere tüm Thread ağ katmanlarını uygular.

Bu Codelab'de, bir Thread ağı başlatmak, cihaz rollerindeki değişiklikleri izlemek ve bunlara tepki vermek ve UDP mesajları göndermek ve ayrıca bu eylemleri gerçek donanımdaki düğmelere ve LED'lere bağlamak için OpenThread API'lerini kullanacaksınız.

2a6db2e258c32237.png

ne öğreneceksin

  • Nordic nRF52840 geliştirme kartlarında düğmeler ve LED'ler nasıl programlanır
  • Ortak OpenThread API'leri ve nasıl kullanılır otInstance sınıfını
  • OpenThread durum değişiklikleri nasıl izlenir ve bunlara nasıl tepki verilir?
  • Bir Thread ağındaki tüm cihazlara UDP mesajları nasıl gönderilir?
  • Makefiles nasıl değiştirilir

Neye ihtiyacın olacak

Donanım:

  • 3 Nordic Semiconductor nRF52840 geliştirme kartı
  • Kartları bağlamak için 3 USB - Micro-USB kablosu
  • En az 3 USB bağlantı noktasına sahip bir Linux makinesi

Yazılım:

  • GNU Araç Zinciri
  • Nordic nRF5x komut satırı araçları
  • Segger J-Link yazılımı
  • Açık Konu
  • Git

Aksi belirtilmediği sürece, bu Codelab içeriği altında lisanslanmıştır Creative Commons Attribution 3.0 Lisansı ve kod örnekleri kapsamında lisanslanmıştır Apache 2.0 Lisansı .

2. Başlarken

Donanım Codelab'i tamamlayın

Bu Codelab başlamadan önce, Oluştur nRF52840 Kurulları ve OpenThread Codelab, bir Konu Ağ tamamlamak gereken:

  • Oluşturmak ve yanıp sönmek için ihtiyacınız olan tüm yazılımları detaylandırın
  • OpenThread'i nasıl oluşturacağınızı ve Nordic nRF52840 panolarında nasıl flaş edeceğinizi öğretir
  • Bir Thread ağının temellerini gösterir

OpenThread oluşturmak ve panoları flash etmek için gerekli ortamın hiçbiri bu Codelab'de ayrıntılı olarak açıklanmamıştır - yalnızca panoları flash etmek için temel talimatlar. Bir İş Parçacığı Ağı Oluşturma Codelab'ı zaten tamamladığınız varsayılmaktadır.

Bir İş parçacığı Ağı Oluşturun Codelab'i tamamlayın

Linux makinesi

Bu Codelab, tüm Thread geliştirme kartlarını flaş etmek için i386 veya x86 tabanlı bir Linux makinesi kullanmak üzere tasarlanmıştır. Tüm adımlar Ubuntu 14.04.5 LTS (Trusty Tahr) üzerinde test edildi.

Nordic Semiconductor nRF52840 panoları

Bu Codelab üç kullanan nRF52840 PDK panoları .

a6693da3ce213856.png

Yazılımı kur

OpenThread'i oluşturmak ve flash'lamak için SEGGER J-Link, nRF5x Komut Satırı araçları, ARM GNU Araç Zinciri ve çeşitli Linux paketleri kurmanız gerekir. Build a Thread Network Codelab'i gerektiği gibi tamamladıysanız, ihtiyacınız olan her şeye zaten sahip olacaksınız. Değilse, OpenThread'i nRF52840 dev panolarına kurabildiğinizden ve flash'layabildiğinizden emin olmak için devam etmeden önce Codelab'i tamamlayın.

Bir İş parçacığı Ağı Oluşturun Codelab'i tamamlayın

3. Depoyu klonlayın

OpenThread, bu Codelab için başlangıç ​​noktası olarak kullanabileceğiniz örnek uygulama koduyla birlikte gelir.

OpenThread'i klonlayın ve yükleyin:

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

4. OpenThread API Temelleri

OpenThread kamu API'ler yer almaktadır ./openthread/include/openthread OpenThread depoda. Bu API'ler, uygulamalarınızda kullanım için hem Thread hem de platform düzeyinde çeşitli OpenThread özelliklerine ve işlevlerine erişim sağlar:

  • OpenThread örnek bilgileri ve kontrolü
  • IPv6, UDP ve CoAP gibi uygulama hizmetleri
  • Komiser ve Marangoz rolleriyle birlikte ağ kimlik bilgisi yönetimi
  • Sınır Yönlendirici yönetimi
  • Çocuk Denetimi ve Sıkışma Algılama gibi gelişmiş özellikler

Tüm OpenThread API'ler Referans bilgisi mevcuttur openthread.io/reference .

API kullanma

Bir API kullanmak için başlık dosyasını uygulama dosyalarınızdan birine ekleyin. Ardından istediğiniz işlevi çağırın.

Örneğin, OpenThread'e dahil edilen CLI örnek uygulaması aşağıdaki API başlıklarını kullanır:

./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 örneği

otInstance yapısı OpenThread API'leri ile çalışırken sık sık kullanacağız şeydir. Başlatıldığında, bu yapı OpenThread kitaplığının statik bir örneğini temsil eder ve kullanıcının OpenThread API çağrıları yapmasına izin verir.

Örneğin, OpenThread örneği başlatıldıktan main() CLI örnek uygulamanın fonksiyonu:

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

Platforma özel işlevler

Eğer OpenThread ile birlikte örnek uygulamalar birine platforma özgü fonksiyonları eklemek istiyorsanız, öncelikle bunları beyan ./openthread/examples/platforms/openthread-system.h kullanarak başlığında otSys tüm fonksiyonlar için ad. Ardından bunları platforma özel bir kaynak dosyada uygulayın. Bu şekilde özetlendiğinde, diğer örnek platformlar için aynı işlev başlıklarını kullanabilirsiniz.

Örneğin, nRF52840 düğmeleri ve LED'lerin içine kancaya kullanacağız GPIO fonksiyonları beyan edilmelidir openthread-system.h .

./openthread/examples/platforms/openthread-system.h tercih metin editörü dosyasını.

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

EYLEM: Platforma özel GPIO işlevi bildirimleri ekleyin

Sonra bu işlev açıklamalarını ekleyin #include için openthread/instance.h başlığındaki:

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

Bunları bir sonraki adımda uygulayacağız.

O Not otSysButtonProcess fonksiyon bildirimi bir kullanır otInstance . Bu şekilde uygulama, gerekirse bir düğmeye basıldığında OpenThread örneği hakkındaki bilgilere erişebilir. Her şey uygulamanızın ihtiyaçlarına bağlıdır. Eğer fonksiyonun uygulamanızda buna ihtiyacı yoktur varsa, kullanabilirsiniz OT_UNUSED_VARIABLE bazı toolchain kullanılmayan değişkenler etrafında bastırmak inşa hatalarına OpenThread API gelen makro. Bunun örneklerini ileride göreceğiz.

5. GPIO platform soyutlamasını uygulayın

Bir önceki adımda, içinde platforma özgü işlev bildirimleri gitti ./openthread/examples/platforms/openthread-system.h GPIO için kullanılabilir. nRF52840 geliştirme kartlarındaki düğmelere ve LED'lere erişmek için bu işlevleri nRF52840 platformu için uygulamanız gerekir. Bu kodda, şu işlevleri ekleyeceksiniz:

  • GPIO pinlerini ve modlarını başlat
  • Bir pimdeki voltajı kontrol edin
  • GPIO kesintilerini etkinleştirin ve bir geri arama kaydedin

In ./src/src dizininde adlı bir dosya oluşturmak gpio.c . Bu yeni dosyaya aşağıdaki içeriği ekleyin.

./src/src/gpio.c (YENİ DOSYA)

EYLEM: Tanım ekle

Bu tanımlar, nRF52840'a özgü değerler ve OpenThread uygulama düzeyinde kullanılan değişkenler arasında soyutlamalar olarak hizmet eder.

/**
 * @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 düğmeleri ve LED'ler hakkında daha fazla bilgi için bkz Nordic Semiconductor Infocenter .

EYLEM: Başlık ekle içerir

Ardından, GPIO işlevi için ihtiyaç duyacağınız üstbilgiyi ekleyin.

/* 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"

EYLEM: Düğme 1 için geri arama ve kesme işlevleri ekleyin

Daha sonra bu kodu ekleyin. in_pin1_handler fonksiyon düğmesi basın işlevselliği (daha sonra bu dosyada) başlatıldığında kayıtlı geri arama değildir.

Bu geri arama kullanma biçimini Not OT_UNUSED_VARIABLE geçirilen değişkenler olarak, makro in_pin1_handler aslında işlevinde kullanılmaz.

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

EYLEM: LED'leri yapılandırmak için bir işlev ekleyin

Başlatma sırasında tüm LED'lerin modunu ve durumunu yapılandırmak için bu kodu ekleyin.

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

EYLEM: Bir LED modunu ayarlamak için bir işlev ekleyin.

Bu işlev, cihazın rolü değiştiğinde kullanılacaktır.

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

EYLEM: Bir LED modunu değiştirmek için bir işlev ekleyin.

Bu işlev, cihaz bir çok noktaya yayın UDP mesajı aldığında LED4'ü değiştirmek için kullanılacaktır.

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

EYLEM: Düğmeye basışları başlatmak ve işlemek için işlevler ekleyin.

İlk işlev, bir düğmeye basıldığında kartı başlatır ve ikincisi, Düğme 1'e basıldığında çok noktaya yayın UDP mesajını gönderir.

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

EYLEM: Kaydet ve yakın gpio.c dosyası.

6. API: Cihaz rolü değişikliklerine tepki verin

Uygulamamızda cihaz rolüne göre farklı ledlerin yanmasını istiyoruz. Şu rolleri izleyelim: Lider, Yönlendirici, Uç Cihaz. Bunları LED'lere şu şekilde atayabiliriz:

  • LED1 = Lider
  • LED2 = Yönlendirici
  • LED3 = Cihazı Bitir

Bu işlevi etkinleştirmek için uygulamanın, cihaz rolünün ne zaman değiştiğini ve yanıt olarak doğru LED'i nasıl açacağını bilmesi gerekir. İlk kısım için OpenThread örneğini ve ikinci kısım için GPIO platform soyutlamasını kullanacağız.

./openthread/examples/apps/cli/main.c tercih metin editörü dosyasını.

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

EYLEM: Başlık ekle içerir

Bölümünü kapsamaktadır yılında main.c dosyası, Rol değişimi özelliği için gerekir API başlık dosyaları eklemek.

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

EYLEM: OpenThread örneği durum değişikliği için işleyici işlevi bildirimi ekleyin

Bu bildiriyi ekle main.c başlık içerir sonra ve herhangi önce #if ifadeleri. Bu fonksiyon ana uygulamadan sonra tanımlanacaktır.

void handleNetifStateChanged(uint32_t aFlags, void *aContext);

EYLEM: Durum değişikliği işleyici işlevi için bir geri arama kaydı ekleyin

In main.c , bu işlev eklemek main() sonra işlevi otAppCliInit çağrı. Bu geri arama kaydı aramak için OpenThread söyler handleNetifStateChange fonksiyonunu her OpenThread örneği durumu değiştiğinde.

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

EYLEM: Durum değişikliği uygulamasını ekleyin

In main.c , sonra main() fonksiyonu uygulamak handleNetifStateChanged işlevi. Bu fonksiyon kontrolleri OT_CHANGED_THREAD_ROLE OpenThread örneğinin bayrağı ve Değiştiyse gerektiği gibi, açık / kapalı LED döner.

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: Bir LED'i açmak için çok noktaya yayın kullanın

Bizim uygulamamızda da bir board üzerinde Button1'e basıldığında ağdaki diğer tüm cihazlara UDP mesajları göndermek istiyoruz. Mesajın alındığını onaylamak için, yanıt olarak diğer panolarda LED4'ü değiştireceğiz.

Bu işlevi etkinleştirmek için uygulamanın şunları yapması gerekir:

  • Başlatma sırasında bir UDP bağlantısı başlatın
  • Kafes-yerel çok noktaya yayın adresine bir UDP mesajı gönderebilme
  • Gelen UDP mesajlarını işleme
  • Gelen UDP mesajlarına yanıt olarak LED4'ü değiştir

./openthread/examples/apps/cli/main.c tercih metin editörü dosyasını.

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

EYLEM: Başlık ekle içerir

Üstündeki bölümü içerir yılında main.c dosyası, çoklu yayın UDP özelliği için gerekir API başlık dosyaları eklemek.

#include <string.h>

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

#include "utils/code_utils.h"

code_utils.h başlığı için kullanılan otEXPECT ve otEXPECT_ACTION hataları işlemek incelikle validate çalışma zamanı koşulları olduğunu makrolar ve.

EYLEM: Tanımlar ve sabitler ekleyin:

Gelen main.c bölümü ve herhangi önce kapsamaktadır sonra dosyanın, #if ifadeleri, UDP-spesifik sabitleri ve tanımlarınızı:

#define UDP_PORT 1212

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

ff03::1 örgü-içi çoğa adresidir. Bu adrese gönderilen tüm mesajlar ağdaki tüm Tam Konu Aygıtlarına gönderilecektir. Bkz openthread.io üzerinde Multicast'ı OpenThread içinde çok noktaya desteği konusunda daha fazla bilgi için.

EYLEM: İşlev bildirimleri ekleyin

In main.c dosyası, sonra otTaskletsSignalPending tanımı ve önce main() fonksiyonu, UDP özgün fonksiyonları yanı sıra UDP soketi temsil edecek bir statik değişkeni ekleyin:

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;

EYLEM: GPIO LED'lerini ve düğmesini başlatmak için aramalar ekleyin

In main.c , bu işlev çağrıları ekleyin main() sonra işlevi otSetStateChangedCallback çağrı. Bu işlevler, GPIO ve GPIOTE pinlerini başlatır ve düğmeye basma olaylarını işlemek için bir düğme işleyicisi ayarlar.

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

EYLEM: UDP başlatma çağrısını ekleyin

In main.c , bu işlev eklemek main() sonra işlevi otSysButtonInit yeni eklediğiniz çağrı:

initUdp(instance);

Bu çağrı, uygulama başlatıldığında bir UDP soketinin başlatılmasını sağlar. Bu olmadan cihaz UDP mesajları gönderemez veya alamaz.

EYLEM: GPIO düğmesi olayını işlemek için çağrı ekleyin

In main.c , bu işlev çağrısı eklemek main() sonra işlevi otSysProcessDrivers çağrı içinde, while döngü. Bildirilen Bu fonksiyon, gpio.c , kontrol düğmesine basıldığı takdirde, ve eğer öyleyse, işleyici (aramaları handleButtonInterrupt yukarıdaki aşamada ayarlandı).

otSysButtonProcess(instance);

EYLEM: Düğme Kesintisi İşleyicisini Uygulayın

In main.c , uygulanmasını eklemek handleButtonInterrupt sonra fonksiyonu handleNetifStateChanged önceki adımda eklenen fonksiyonu:

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

EYLEM: UDP başlatmayı uygulayın

In main.c , uygulanmasını eklemek initUdp sonra fonksiyonu handleButtonInterrupt eklediğiniz fonksiyonun:

/**
 * 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 daha önce tanımlanmış bağlantı noktası (1212) 'dir. otUdpOpen fonksiyon soketi ve kayıtları, bir geri çağrı işlevi (açar handleUdpReceive UDP mesajı alındığında için). otUdpBind ileterek Konu ağ arayüzüne soket bağlar OT_NETIF_THREAD . Diğer ağ arayüz seçenekleri için, bkz otNetifIdentifier içinde numaralandırma UDP API Başvurusu .

EYLEM: UDP mesajlaşmasını uygulayın

In main.c , uygulanmasını eklemek sendUdp sonra fonksiyonu initUdp eklediğiniz fonksiyonun:

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

Not otEXPECT ve otEXPECT_ACTION makroları. UDP mesajı geçerli ve tampon doğru tahsis ve eğer olmadığından emin Bunlar marka, fonksiyon incelikle için atlama tarafından hatalarını işleme exit o tampon boşaltır blok.

Bkz IPv6 ve UDP UDP başlatmak için kullanılan işlevler hakkında daha fazla bilgi için openthread.io Başvurular.

EYLEM: UDP mesaj işlemeyi uygulayın

In main.c , uygulanmasını eklemek handleUdpReceive sonra fonksiyonu sendUdp eklediğiniz fonksiyonun. Bu işlev sadece LED4'ü değiştirir.

/**
 * 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 ağını yapılandırın

Gösterim kolaylığı için, cihazlarımızın açıldıklarında hemen Thread başlatmasını ve bir ağa katılmasını istiyoruz. Bunu yapmak için, biz kullanacağız otOperationalDataset yapısı. Bu yapı, Thread ağ kimlik bilgilerini bir cihaza iletmek için gereken tüm parametreleri tutar.

Bu yapının kullanılması, uygulamamızı daha güvenli hale getirmek ve ağımızdaki Thread düğümlerini yalnızca uygulamayı çalıştıranlarla sınırlamak için OpenThread'de yerleşik ağ varsayılanlarını geçersiz kılar.

Yine, açmak ./openthread/examples/apps/cli/main.c tercih metin editörü dosyasını.

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

EYLEM: Başlık ekle

Üstündeki bölümü içerir dahilinde main.c dosyası, sen Konu ağı yapılandırmanız gerekir API üstbilgi dosyasına ekleyin:

#include <openthread/dataset_ftd.h>

EYLEM: Ağ yapılandırmasını ayarlamak için işlev bildirimi ekleyin

Bu bildiriyi ekle main.c başlık içerir sonra ve herhangi önce #if ifadeleri. Bu fonksiyon, ana uygulama fonksiyonundan sonra tanımlanacaktır.

static void setNetworkConfiguration(otInstance *aInstance);

EYLEM: Ağ yapılandırma çağrısını ekleyin

In main.c , bu işlev çağrısı eklemek main() sonra işlevi otSetStateChangedCallback çağrı. Bu işlev, Thread ağ veri kümesini yapılandırır.

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

EYLEM: Thread ağ arayüzünü ve yığınını etkinleştirmek için çağrılar ekleyin

In main.c , bu işlev çağrıları ekleyin main() sonra işlevi otSysButtonInit çağrı.

/* Start the Thread network interface (CLI cmd > ifconfig up) */
otIp6SetEnabled(instance, true);

/* Start the Thread stack (CLI cmd > thread start) */
otThreadSetEnabled(instance, true);

EYLEM: Thread ağ yapılandırmasını uygulayın

In main.c , uygulanmasını eklemek setNetworkConfiguration sonra fonksiyonu main() fonksiyonu:

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

Fonksiyonda detaylandırıldığı gibi, bu uygulama için kullandığımız Thread ağ parametreleri şunlardır:

  • Kanal = 15
  • PAN Kimliği = 0x2222
  • Genişletilmiş PAN Kimliği = C0DE1AB5C0DE1AB5
  • Ağ Anahtarı = 1234C0DE1AB51234C0DE1AB51234C0DE
  • Ağ Adı = OTCodelab

Ek olarak, Yönlendirici Seçimi Titreşimini azalttığımız yer burasıdır, böylece cihazlarımız demo amacıyla rolleri daha hızlı değiştirir. Bunun yalnızca düğümün bir FTD (Tam İş Parçacığı Aygıtı) olması durumunda yapıldığını unutmayın. Bir sonraki adımda bununla ilgili daha fazla bilgi.

9. API: Kısıtlı işlevler

OpenThread'in API'lerinden bazıları, yalnızca demo veya test amacıyla değiştirilmesi gereken ayarları değiştirir. Bu API'ler, OpenThread kullanan bir uygulamanın üretim dağıtımında kullanılmamalıdır.

Örneğin, otThreadSetRouterSelectionJitter fonksiyonu Yönlendirici kendini tanıtmak için bir son aygıt için geçen (saniye olarak) ayarlar. Bu değer için varsayılan değer, Diş Spesifikasyonuna göre 120'dir. Bu Codelab'de kullanım kolaylığı için, bunu 20 olarak değiştireceğiz, böylece bir Thread düğümünün rolleri değiştirmesi için çok uzun süre beklemeniz gerekmez.

Not: MTD cihazlar yönlendiriciler olmazlar ve benzeri bir işlev için destek otThreadSetRouterSelectionJitter bir MTD yapı dahil değildir. Daha sonra CKağıt seçeneği belirtmeniz gerekir -DOT_MTD=OFF aksi takdirde biz bir yapı hatası karşılaşacak.

Sen bakarak bunu teyit edebilir otThreadSetRouterSelectionJitter bir önişlemci direktifi içinde yer alan fonksiyon tanımı, 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. Güncellemeleri yapın

Uygulamanızı oluşturmadan önce, üç CMake dosyası için gereken birkaç küçük güncelleme vardır. Bunlar, uygulamanızı derlemek ve bağlamak için yapı sistemi tarafından kullanılır.

./ Third_party/NordicSemiconductor/CMakeLists.txt

Şimdi NordicSemiconductor bazı bayraklar eklemek CMakeLists.txt GPIO fonksiyonları uygulamasında tanımlanır sağlamak için.

EYLEM: CMakeLists.txt dosyasına bayrak ekleyin

Açık ./third_party/NordicSemiconductor/CMakeLists.txt tercih metin editörü ve aşağıdaki satırları ekleyin COMMON_FLAG bölümünde.

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

Şimdi yeni ekle gpio.c dosyayı ./src/CMakeLists.txt dosyası.

EYLEM: gpio kaynağını

./src/CMakeLists.txt

dosya

Açık ./src/CMakeLists.txt tercih metin editörü ve dosya eklemek NRF_COMM_SOURCES bölümünde.

...

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

...

./ Third_party/NordicSemiconductor/CMakeLists.txt

Son olarak, eklemek nrfx_gpiote.c NordicSemiconductor Sürücü dosyasını CMakeLists.txt o İskandinav sürücülerin kütüphane yapı ile birlikte oluyor böylece, dosyanın.

EYLEM: gpio sürücüsünü NordicSemiconductor CMakeLists.txt dosyasına ekleyin

Açık ./third_party/NordicSemiconductor/CMakeLists.txt tercih metin editörü ve dosya eklemek COMMON_SOURCES bölümünde.

...

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

11. Cihazları kurun

Tüm kod güncellemeleri yapıldıktan sonra, uygulamayı üç Nordic nRF52840 geliştirme kartının tümüne oluşturmaya ve flash'lamaya hazırsınız. Her cihaz bir Tam İş Parçacığı Cihazı (FTD) olarak işlev görecektir.

OpenThread'i oluşturun

nRF52840 platformu için OpenThread FTD ikili dosyalarını oluşturun.

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

OpenThread FTD CLI ikili dosyasıyla dizine gidin ve ARM Embedded Toolchain ile onu hex biçimine dönüştürün:

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

panoları flaş

Flaş ot-cli-ftd.hex her nRF52840 kartına dosya.

Bir sonraki nRF52840 gemide harici güç pinine Mikro USB hata ayıklama bağlantı noktasına USB kablosunu takın ve ardından Linux makine takın. Doğru alan LED5 üzerindedir.

20a3b4b480356447.png

Daha önce olduğu gibi, nRF52840 kartının seri numarasını not edin:

c00d519ebec7e5f0.jpeg

nRFx Komut Satırı Araçları konumuna gidin ve kartın seri numarasını kullanarak OpenThread CLI FTD hex dosyasını nRF52840 kartına flashlayın:

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

LED5, yanıp sönme sırasında kısa süreliğine kapanacaktır. Başarı üzerine aşağıdaki çıktı oluşturulur:

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.

Diğer iki kart için bu "Kartları Flashla" adımını tekrarlayın. Her bir kart, Linux makinesine aynı şekilde bağlanmalıdır ve kartın seri numarası dışında yanıp sönme komutu aynıdır. Kartta her kartın benzersiz seri numarasını kullandığınızdan emin olun.

nrfjprog komutunu yanıp sönen.

Başarılı olursa, her bir kartta LED1, LED2 veya LED3 yanacaktır. Yanıp söndükten hemen sonra yanan LED anahtarının 3'ten 2'ye (veya 2'ye 1) geçtiğini bile görebilirsiniz (aygıt rolü değiştirme özelliği).

12. Uygulama işlevselliği

Üç nRF52840 kartının tümüne artık güç verilmeli ve OpenThread uygulamamız çalıştırılmalıdır. Daha önce ayrıntılı olarak açıklandığı gibi, bu uygulamanın iki temel özelliği vardır.

Cihaz rolü göstergeleri

Her panodaki yanan LED, Thread düğümünün mevcut rolünü yansıtır:

  • LED1 = Lider
  • LED2 = Yönlendirici
  • LED3 = Cihazı Bitir

Rol değiştikçe yanan LED de değişir. Bu değişiklikleri, her cihaz açıldıktan sonraki 20 saniye içinde bir veya iki kartta görmüş olmalısınız.

UDP Çok Noktaya Yayın

Bir kartta Düğme1'e basıldığında, Thread ağındaki diğer tüm düğümleri içeren ağ yerel çok noktaya yayın adresine bir UDP mesajı gönderilir. Açık veya kapalı tüm diğer panoları geçiş yapar bu mesajı, LED4 alınmasına karşılık olarak. LED4, başka bir UDP mesajı alana kadar her kart için açık veya kapalı kalır.

203dd094acca1f97.png

9bbd96d9b1c63504.png

13. Demo: Cihaz rolü değişikliklerini gözlemleyin

Flashladığınız aygıtlar, Yönlendiriciye Uygun Son Aygıt (REED) adı verilen belirli bir Tam İş Parçacığı Aygıtı (FTD) türüdür. Bu, bir Yönlendirici veya Uç Aygıt olarak işlev görebilecekleri ve kendilerini bir Uç Aygıttan Yönlendiriciye yükseltebilecekleri anlamına gelir.

Thread, 32 Router'a kadar destekleyebilir, ancak Router sayısını 16 ile 23 arasında tutmaya çalışır. Bir REED, End Device olarak bağlanırsa ve Router sayısı 16'nın altındaysa, kendisini otomatik olarak Router'a yükseltir. Bu değişiklik, ayarladığınız saniye sayısı içinde rastgele bir zamanda gerçekleşmelidir otThreadSetRouterSelectionJitter uygulaması (20 saniye) içinde değer.

Her Thread ağının ayrıca bir Thread ağındaki Yönlendiriciler kümesini yönetmekten sorumlu olan bir Yönlendirici olan bir Lideri vardır. Tüm cihazlar açıkken, 20 saniye sonra bunlardan biri Lider (LED1 açık) ve diğer ikisi Yönlendirici (LED2 açık) olmalıdır.

4e1e885861a66570.png

Lideri Kaldır

Lider Konu ağından çıkarılırsa, ağın hala bir Lideri olduğundan emin olmak için farklı bir Yönlendirici kendisini Liderliğe yükseltir.

Güç anahtarını kullanarak Lider tahtası (LED1 biri yaktı) kapatın. Yaklaşık 20 saniye bekleyin. Kalan iki karttan birinde LED2 (Router) kapanacak ve LED1 (Leader) açılacaktır. Bu cihaz artık Thread ağının Lideri.

4c57c87adb40e0e3.png

Orijinal Lider panosunu tekrar açın. Bir Uç Cihaz olarak Thread ağına otomatik olarak yeniden katılmalıdır (LED3 yanar). 20 saniye içinde (Yönlendirici Seçimi Sarsıntısı) kendisini bir Yönlendiriciye yükseltir (LED2 yanar).

5f40afca2dcc4b5b.png

panoları sıfırla

Üç kartı da kapatın, ardından tekrar açın ve LED'leri gözlemleyin. Güç verilen ilk pano Lider rolünde başlamalıdır (LED1 yanar)— Thread ağındaki ilk Yönlendirici otomatik olarak Lider olur.

Diğer iki kart başlangıçta ağa Uç Cihazlar (LED3 yanar) olarak bağlanır, ancak 20 saniye içinde kendilerini Yönlendiricilere (LED2 yanar) tanıtmalıdır.

Ağ bölümleri

Panolarınız yeterli güç almıyorsa veya aralarındaki radyo bağlantısı zayıfsa, Thread ağı bölümlere ayrılabilir ve Lider olarak gösterilen birden fazla cihazınız olabilir.

İş parçacığı kendi kendini iyileştirir, bu nedenle bölümler sonunda tek bir Lider ile tek bir bölüm halinde birleşmelidir.

14. Demo: UDP çok noktaya yayın gönder

Önceki alıştırmadan devam edilirse, hiçbir cihazda LED4 yanmamalıdır.

Herhangi bir tahta seçin ve Düğme1'e basın. Uygulamayı çalıştıran Thread ağındaki diğer tüm kartlardaki LED4, durumlarını değiştirmelidir. Önceki alıştırmadan devam ediyorsanız, şimdi açık olmalıdırlar.

f186a2618fdbe3fd.png

Aynı pano için Düğme1'e tekrar basın. Diğer tüm kartlardaki LED4 tekrar geçiş yapmalıdır.

Farklı bir panoda Düğme1'e basın ve LED4'ün diğer panolarda nasıl geçiş yaptığını gözlemleyin. LED4'ün açık olduğu panolardan birinde Düğme1'e basın. LED4, bu kart için açık kalır, ancak diğerlerinde geçiş yapar.

f5865ccb8ab7aa34.png

Ağ bölümleri

Panolarınız bölünmüşse ve aralarında birden fazla Lider varsa, çok noktaya yayın mesajının sonucu panolar arasında farklılık gösterecektir. Bölümlenmiş (ve dolayısıyla bölümlenmiş Thread ağının tek üyesi olan) bir kartta Düğme1'e basarsanız, diğer kartlardaki LED4 yanıt olarak yanmayacaktır. Bu olursa, panoları sıfırlayın - ideal olarak tek bir Thread ağını yeniden yapılandırırlar ve UDP mesajlaşması doğru şekilde çalışmalıdır.

15. Tebrikler!

OpenThread API'lerini kullanan bir uygulama oluşturdunuz!

Artık biliyorsunuz:

  • Nordic nRF52840 geliştirme kartlarında düğmeler ve LED'ler nasıl programlanır
  • Ortak OpenThread API'leri ve nasıl kullanılır otInstance sınıfını
  • OpenThread durum değişiklikleri nasıl izlenir ve bunlara nasıl tepki verilir?
  • Bir Thread ağındaki tüm cihazlara UDP mesajları nasıl gönderilir?
  • Makefiles nasıl değiştirilir

Sonraki adımlar

Bu Codelab'den yola çıkarak aşağıdaki alıştırmaları deneyin:

  • GPIO modülünü yerleşik LED'ler yerine GPIO pinlerini kullanacak şekilde değiştirin ve Yönlendirici rolüne göre renk değiştiren harici RGB LED'leri bağlayın
  • Farklı bir örnek platform için GPIO desteği ekleyin
  • Bunun yerine bir düğmeye basarak tüm cihazlara ping çok noktaya kullanmak yerine, kullanmak Yönlendirici / Lider API bulmak ve bireysel cihazı ping
  • Bir kullanarak internete örgü ağını bağlayın OpenThread Sınır Router LED'ler ışık için Konu ağı dışında onları ve çok noktaya

daha fazla okuma

Check out openthread.io ve GitHub OpenThread dahil kaynakları çeşitli:

Referans: