การพัฒนาด้วย OpenThread API

1. บทนำ

26b7f4f6b3ea0700.png

OpenThread ที่เผยแพร่โดย Nest เป็นการใช้งานแบบโอเพนซอร์สของโปรโตคอลเครือข่าย Thread® Nest ได้เปิดตัว OpenThread เพื่อทําให้เทคโนโลยีที่ใช้ในผลิตภัณฑ์ Nest พร้อมใช้งานในวงกว้างแก่นักพัฒนาซอฟต์แวร์เพื่อเร่งการพัฒนาผลิตภัณฑ์สําหรับบ้านที่เชื่อมต่อ

ข้อกําหนดชุดข้อความกําหนดโปรโตคอลการสื่อสารของอุปกรณ์ไร้สายที่เชื่อถือได้ต่ออุปกรณ์แบบพกพาที่ใช้ IPv6 ได้สําหรับแอปพลิเคชันที่บ้าน OpenThread ใช้งานเลเยอร์เครือข่ายชุดข้อความทั้งหมด รวมถึง IPv6, 6LoWPAN, IEEE 802.15.4 ที่มีความปลอดภัยของ MAC, Mesh Link Establishment และ Mesh Routing

ใน Codelab นี้ คุณจะใช้ OpenThread API เพื่อเริ่มเครือข่ายชุดข้อความ ตรวจสอบและตอบสนองต่อการเปลี่ยนแปลงบทบาทในอุปกรณ์ รวมถึงส่งข้อความ UDP และเชื่อมโยงการทํางานเหล่านี้กับปุ่มและ LED ในฮาร์ดแวร์จริง

2a6db2e258c32237.png

สิ่งที่คุณจะได้เรียนรู้

  • วิธีตั้งโปรแกรมให้ปุ่มและ LED บนกระดานนักพัฒนาซอฟต์แวร์ Nดิก NRF52840
  • วิธีใช้ API ของ OpenThread API ทั่วไปและคลาส otInstance
  • วิธีตรวจสอบและตอบสนองต่อการเปลี่ยนแปลงสถานะ OpenThread
  • วิธีส่งข้อความ UDP ไปยังอุปกรณ์ทั้งหมดในเครือข่ายชุดข้อความ
  • วิธีแก้ไขไฟล์

สิ่งที่ต้องมี

ฮาร์ดแวร์:

  • บอร์ด Nordic Interconductor nRF52840 จํานวน 3 รายการ
  • สายเคเบิล USB กับไมโคร USB 3 เส้นเพื่อเชื่อมต่อบอร์ด
  • เครื่อง Linux ที่มีพอร์ต USB อย่างน้อย 3 พอร์ต

ซอฟต์แวร์:

  • เชนเครื่องมือ GNU
  • เครื่องมือบรรทัดคําสั่ง nRF5x แบบนอร์ดิก
  • ซอฟต์แวร์ Segger J-Link
  • OpenThread
  • Git

เนื้อหาของ Codelab นี้ได้รับอนุญาตภายใต้สัญญาอนุญาตครีเอทีฟคอมมอนส์แบบระบุแหล่งที่มา 3.0และตัวอย่างโค้ดได้รับอนุญาตภายใต้ใบอนุญาต Apache 2.0ของ Google Play

2. เริ่มต้นใช้งาน

ทํา Codelab ฮาร์ดแวร์ให้เสร็จสมบูรณ์

ก่อนเริ่มต้น Codelab นี้ คุณควรดําเนินการสร้างเครือข่ายชุดข้อความด้วย nRF52840 Boards และ OpenThread Codelab ซึ่งดําเนินการดังนี้

  • ดูรายละเอียดซอฟต์แวร์ทั้งหมดที่ต้องใช้ในการสร้างและกะพริบ
  • สอนวิธีสร้าง OpenThread และแฟลชบนบอร์ด nRF52840 แบบนอร์ดิก
  • แสดงข้อมูลพื้นฐานของเครือข่ายชุดข้อความ

ไม่มีสภาพแวดล้อมใดที่ต้องใช้ในการตั้งค่า OpenThread และแฟลชบอร์ดจะโดยละเอียดใน Codelab นี้ ซึ่งเป็นเพียงคําแนะนําเบื้องต้นสําหรับการกะพริบกระดาน โดยถือว่าคุณสร้าง Codelab การสร้างเครือข่ายชุดข้อความเรียบร้อยแล้ว

เครื่อง Linux

Codelab นี้ออกแบบมาเพื่อใช้เครื่อง Linux ที่ใช้ i386 หรือ x86 เพื่อแฟลชบอร์ดการพัฒนาชุดข้อความทั้งหมด ขั้นตอนทั้งหมดได้รับการทดสอบใน Ubuntu 14.04.5 LTS (Trusty Tahr)

กระดานข่าวเหนือปกติแบบ nRF52840

Codelab นี้ใช้กระดาน nRF52840 PDK จํานวน 3 กระดาน

a6693da3ce213856.png

ติดตั้งซอฟต์แวร์

หากต้องการสร้างและ Flash Openเทรด คุณต้องติดตั้ง SEGger J-Link, เครื่องมือบรรทัดคําสั่ง nRF5x, ARM GNU Toolchain และแพ็กเกจ Linux ต่างๆ ถ้าสร้าง Codelab การสร้างชุดข้อความเป็นชุดข้อความเรียบร้อยแล้ว คุณก็จะมีทุกสิ่งที่ต้องการติดตั้งอยู่แล้ว หากไม่มีให้ทําตามคําแนะนําของ Codelab ก่อน เพื่อให้แน่ใจว่าคุณจะสร้าง Flash OpenFlash และบอร์ด nRF52840 dev ได้

3. โคลนที่เก็บ

OpenThread มาพร้อมกับตัวอย่างรหัสแอปพลิเคชันที่ใช้เป็นจุดเริ่มต้นสําหรับ Codelab นี้ได้

โคลนที่เก็บ OpenThread Nordic nRF528xx แล้วสร้าง OpenThread ดังนี้

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

4. ข้อมูลเบื้องต้นเกี่ยวกับ OpenThread API

API สาธารณะของ OpenThread จะอยู่ที่ ./openthread/include/openthread ในที่เก็บ OpenThread API เหล่านี้มอบการเข้าถึงฟังก์ชันและการทํางานของ OpenThread ทั้งในระดับชุดข้อความและระดับแพลตฟอร์มเพื่อใช้ในแอปพลิเคชันของคุณ

  • ข้อมูลและการควบคุมอินสแตนซ์ OpenThread
  • บริการแอปพลิเคชัน เช่น IPv6, UDP และ CoAP
  • การจัดการข้อมูลเข้าสู่ระบบเครือข่าย รวมถึงบทบาทของกรรมาธิการและผู้เข้าร่วม
  • การจัดการเราเตอร์ Border
  • ฟีเจอร์ขั้นสูง เช่น การควบคุมดูแลเด็กและการตรวจจับ Jam

อ้างอิงข้อมูล OpenOpen API ทั้งหมดได้ที่ openthread.io/reference

การใช้ API

หากต้องการใช้ API ให้รวมไฟล์ส่วนหัวไว้ในไฟล์แอปพลิเคชันไฟล์ใดไฟล์หนึ่งของคุณ จากนั้นเรียกใช้ฟังก์ชันที่ต้องการ

เช่น แอปตัวอย่าง CLI ที่รวมอยู่ใน OpenThread ใช้ส่วนหัว 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 example

./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 สําหรับฟังก์ชันทั้งหมด จากนั้นจึงนําไปใช้ในไฟล์ต้นฉบับเฉพาะแพลตฟอร์ม ซึ่งแสดงในลักษณะนี้ คุณจะใช้ส่วนหัวของฟังก์ชันเดียวกันสําหรับแพลตฟอร์มตัวอย่างอื่นๆ ได้

ตัวอย่างเช่น เราจะใช้ฟังก์ชัน GPIO ที่จะใช้ในการเชื่อมต่อปุ่ม nRF52840 และ LED ใน 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 ในโค้ดนี้ คุณจะเพิ่มฟังก์ชันที่

  • เริ่มต้น PIN และโหมดของ GPIO
  • ควบคุมแรงดันไฟฟ้าบนหมุด
  • เปิดใช้ GPIO เพื่อขัดจังหวะและลงทะเบียนโค้ดเรียกกลับ

สร้างไฟล์ใหม่ชื่อ gpio.c ในไดเรกทอรี ./src/src เพิ่มเนื้อหาใหม่ในไฟล์ใหม่นี้

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

โปรดดูข้อมูลเพิ่มเติมเกี่ยวกับปุ่ม และ LED ของ nRF52840 ที่ศูนย์ข้อมูลของ Nordic Semiconductor

การดําเนินการ: เพิ่มส่วนหัวรวม

ถัดไป ให้เพิ่มส่วนหัวที่คุณจําเป็นต้องใช้สําหรับฟังก์ชัน 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

ฟังก์ชันนี้จะใช้ในการสลับ LED4 เมื่ออุปกรณ์ได้รับข้อความ UDP แบบมัลติแคสต์

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

การดําเนินการ: เพิ่มฟังก์ชันเพื่อเริ่มต้นและประมวลผลการกดปุ่ม

ฟังก์ชันแรกจะเริ่มกระดานสําหรับการกดปุ่ม และปุ่มที่สองจะส่งข้อความ UDP แบบมัลติแคสต์เมื่อกดปุ่ม 1

/**
 * @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 ไฟล์

6. API: ตอบสนองต่อการเปลี่ยนแปลงบทบาทของอุปกรณ์

ในแอปพลิเคชันของเรา เราต้องการทําให้ไฟ LED ต่างๆ สว่างขึ้นตามบทบาทของอุปกรณ์ มาติดตามบทบาทต่อไปนี้: ผู้นํา เราเตอร์ อุปกรณ์ปลายทาง โดยเราจะใช้ไฟ LED ในธีมต่อไปนี้

  • LED1 = ผู้นํา
  • LED2 = เราเตอร์
  • LED3 = อุปกรณ์สิ้นสุด

หากต้องการเปิดใช้ฟังก์ชันการทํางานนี้ แอปพลิเคชันต้องทราบเมื่อเปลี่ยนบทบาทของอุปกรณ์และวิธีเปิดไฟ LED ที่ถูกต้องในการตอบกลับ เราจะใช้อินสแตนซ์ OpenThread สําหรับส่วนแรกและนามธรรม GPIO สําหรับแพลตฟอร์มสําหรับส่วนที่ 2

เปิดไฟล์ ./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

ในแอปพลิเคชันของเรา เราต้องการส่งข้อความ UDP ไปยังอุปกรณ์อื่นๆ ทั้งหมดในเครือข่ายเมื่อมีการกดปุ่ม11 บนกระดานเดียว เราจะสลับ LED4 บนกระดานอื่นๆ เพื่อตอบกลับเพื่อยืนยันข้อความ

หากต้องการเปิดใช้ฟังก์ชันนี้ แอปพลิเคชันต้องมีคุณสมบัติดังนี้

  • เริ่มต้นการเชื่อมต่อ UDP เมื่อเริ่มต้นใช้งาน
  • ส่งข้อความ UDP ไปยังที่อยู่มัลติแคสต์แบบ Mesh-Local ได้
  • จัดการข้อความ UDP ขาเข้า
  • เปิด/ปิด LED4 เพื่อตอบสนองต่อข้อความ UDP ที่เข้ามา

เปิดไฟล์ ./openthread/examples/apps/cli/main.c ในเครื่องมือแก้ไขข้อความที่ต้องการ

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

การดําเนินการ: เพิ่มส่วนหัวรวม

ในส่วน "รวม" ที่ด้านบนของไฟล์ main.c ให้เพิ่มไฟล์ส่วนหัว API ที่ต้องใช้สําหรับฟีเจอร์ UDP แบบมัลติแคสต์

#include <string.h>

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

#include "utils/code_utils.h"

ส่วนหัว code_utils.h จะใช้สําหรับมาโคร otEXPECT และ otEXPECT_ACTION ที่ตรวจสอบเงื่อนไขรันไทม์และจัดการข้อผิดพลาดอย่างค่อยเป็นค่อยไป

การดําเนินการ: เพิ่มคําจํากัดความและค่าคงที่:

ในไฟล์ main.c หลังจากส่วน include และก่อนคําสั่ง #if ให้เพิ่มค่าคงที่เฉพาะ UDP และกําหนด

#define UDP_PORT 1212

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

ff03::1 คือที่อยู่มัลติแคสต์ในท้องถิ่นแบบ Mesh ข้อความที่ส่งไปยังอีเมลนี้จะส่งไปยังอุปกรณ์ชุดข้อความทั้งหมดในเครือข่าย ดูข้อมูลเพิ่มเติมเกี่ยวกับการรองรับมัลติแคสต์ใน OpenThread ได้ที่ Multicast ใน 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;

การดําเนินการ: เพิ่มการโทรเพื่อเริ่มไฟ LED และปุ่ม GPIO

ใน main.c ให้เพิ่มการเรียกใช้ฟังก์ชันเหล่านี้ลงในฟังก์ชัน main() หลังจากการเรียกใช้ otSetStateChangedCallback ฟังก์ชันเหล่านี้จะเป็นการเริ่มต้น PIN ของ 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);
    }
}

จดมาโคร otEXPECT และ otEXPECT_ACTION ไว้ การดําเนินการนี้ช่วยให้มั่นใจได้ว่าข้อความ UDP นั้นถูกต้องและจัดสรรได้อย่างถูกต้องในบัฟเฟอร์ และหากไม่มี ฟังก์ชันดังกล่าวจะจัดการกับข้อผิดพลาดได้อย่างมีประสิทธิภาพโดยข้ามไปยังบล็อก exit ซึ่งจะทําให้พื้นที่ดังกล่าวมีบัฟเฟอร์มากยิ่งขึ้น

โปรดดูข้อมูลอ้างอิงของ IPv6 และ UDP ใน openthread.io สําหรับข้อมูลเพิ่มเติมเกี่ยวกับฟังก์ชันที่ใช้เริ่มต้น UDP

การดําเนินการ: ใช้การจัดการข้อความ 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 ในการดําเนินการนี้ โครงสร้างนี้เก็บพารามิเตอร์ทั้งหมดที่จําเป็นต่อการส่งข้อมูลรับรองเครือข่ายชุดข้อความไปยังอุปกรณ์

การใช้โครงสร้างนี้จะลบล้างค่าเริ่มต้นของเครือข่ายที่ติดตั้งใน OpenThreade เพื่อทําให้แอปพลิเคชันของเรามีความปลอดภัยยิ่งขึ้น และจํากัดโหนด Thread ในเครือข่ายของเราให้แสดงเฉพาะโหนดที่ใช้งานแอปพลิเคชันเท่านั้น

เปิดไฟล์ ./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 = 0x2222
  • รหัส PAN แบบขยาย = C0DE1AB5C0DE1AB5
  • คีย์เครือข่าย = 1234C0DE1AB51234C0DE1AB51234C0DE
  • ชื่อเครือข่าย = OTCodelab

นอกจากนี้ เรายังลดเสียงรบกวนของเราเตอร์ที่เลือกไว้ อุปกรณ์จึงจะเปลี่ยนบทบาทได้เร็วขึ้นเพื่อการสาธิต โปรดทราบว่าการดําเนินการนี้จะดําเนินการได้ก็ต่อเมื่อโหนดเป็น FTD (อุปกรณ์ชุดข้อความแบบเต็ม) ดูข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ในขั้นตอนถัดไป

9. API: ฟังก์ชันที่จํากัด

API ของ OpenThread บางรายการแก้ไขการตั้งค่าที่ควรแก้ไขเพื่อการสาธิตหรือการทดสอบเท่านั้น ไม่ควรนํา API เหล่านี้ไปใช้งานจริงในแอปพลิเคชันที่ใช้ OpenThread

เช่น ฟังก์ชัน otThreadSetRouterSelectionJitter จะปรับเวลา (เป็นวินาที) ที่อุปกรณ์ปลายทางใช้ในการโปรโมตตนเองกับเราเตอร์ ค่าเริ่มต้นของค่านี้คือ 120 ตามข้อกําหนดของชุดข้อความ เพื่อการใช้งานใน Codelab นี้ เราจะเปลี่ยนเป็น 20 เพื่อให้คุณไม่ต้องรอนานมากที่โหนดชุดข้อความจะเปลี่ยนบทบาท

หมายเหตุ: อุปกรณ์ MTD จะไม่กลายเป็นเราเตอร์ และการรองรับฟังก์ชันอย่าง otThreadSetRouterSelectionJitter จะไม่รวมอยู่ในบิลด์ของ MTD และหลังจากนั้นต้องระบุตัวเลือก CMake เป็น -DOT_MTD=OFF มิเช่นนั้นจะเกิดปัญหาบิลด์

คุณสามารถตรวจสอบได้จากคําจํากัดความของฟังก์ชัน otThreadSetRouterSelectionJitter ซึ่งอยู่ภายในคําสั่ง Preprocessor 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. ทําการอัปเดต C

ก่อนสร้างแอปพลิเคชัน คุณต้องทําการอัปเดตเล็กน้อยสําหรับไฟล์ CMake จํานวน 3 ไฟล์ ใช้สําหรับบิลด์เพื่อคอมไพล์และลิงก์แอปพลิเคชันของคุณ

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

แก้ไขไฟล์ ./src/CMakeLists.txt เพื่อเพิ่มไฟล์ต้นฉบับ gpio.c ใหม่:

การดําเนินการ: เพิ่มแหล่งที่มาของ GPS ไปที่ไฟล์./src/CMakeLists.txt

เปิด ./src/CMakeLists.txt ในเครื่องมือแก้ไขข้อความที่ต้องการ แล้วเพิ่มส่วนไปยังส่วน NRF_COMM_SOURCES

...

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

...

./third_party/NordicSemiconductor/CMakeLists.txt

สุดท้ายให้เพิ่มnrfx_gpiote.c ไฟล์ไดรเวอร์ไปยัง NordicSemiconductorCMakeLists.txt ไฟล์ดังกล่าวจะรวมอยู่ในไลบรารีไฟล์ไดรเวอร์ของนอร์ดิก

การดําเนินการ: เพิ่มไดรเวอร์ GPio ลงในไฟล์CMakeLists.txt ของ NordicSemiconductor

เปิด ./third_party/NordicSemiconductor/CMakeLists.txt ในเครื่องมือแก้ไขข้อความที่ต้องการ แล้วเพิ่มส่วนไปยังส่วน COMMON_SOURCES

...

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

11. ตั้งค่าอุปกรณ์

การอัปเดตโค้ดทั้งหมดเสร็จสมบูรณ์แล้ว คุณก็พร้อมที่จะสร้างและแฟลชแอปพลิเคชันไปยังกระดาน NVF 22444 ทั้ง 3 แผงของ Nordic อุปกรณ์แต่ละเครื่องจะทํางานเป็นชุดข้อความที่สมบูรณ์ (FTD)

สร้าง OpenThread

สร้างไบนารีของ OpenThread FTD สําหรับแพลตฟอร์ม nRF52840

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

ไปยังไดเรกทอรีที่มีไบนารี OpenOpen FTD CLI และแปลงเป็นรูปแบบเลขฐานสิบหกด้วย ARM Embedded Toolchain ดังนี้

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

เปิดแฟลชบอร์ด

แฟลชไฟล์ ot-cli-ftd.hex ไปยังกระดาน nRF52840 แต่ละกระดาน

เสียบสาย USB เข้ากับพอร์ตแก้ไขข้อบกพร่องของ Micro-USB ถัดจากหมุดไฟฟ้าภายนอกบนกระดาน nRF52840 แล้วเสียบเข้ากับเครื่อง Linux ตั้ง LED5 ให้ถูกต้อง

20a3b4b480356447.png

จดหมายเลขซีเรียลของกระดาน nRF52840 ไว้เช่นเดิม

c00d519ebec7e5f0.jpeg

ไปที่ตําแหน่งของเครื่องมือบรรทัดคําสั่ง nRFx แล้วแฟลชไฟล์เลขฐานสิบหกของ 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.

ทําซ้ําขั้นตอน "แฟลชบอร์ด" สําหรับกระดานอีก 2 แผ่น กระดานควรเชื่อมต่อกับเครื่อง Linux ในลักษณะเดียวกัน และคําสั่ง Flash จะเหมือนกัน ยกเว้นหมายเลขซีเรียลของกระดาน โปรดใช้หมายเลขซีเรียลที่ไม่ซ้ํากันของกระดานใน

nrfjprog คําสั่งกะพริบ

หากสําเร็จ ไฟ LED1, LED2 หรือ LED3 จะติดสว่างในแต่ละกระดาน คุณอาจเห็นสวิตช์ไฟ LED ติดสว่างตั้งแต่ 3 ถึง 2 (หรือ 2 ถึง 1) ทันทีหลังจากกะพริบ (ฟีเจอร์เปลี่ยนบทบาทของอุปกรณ์)

12. ฟังก์ชันของแอปพลิเคชัน

กระดาน nRF52840 ทั้ง 3 พอร์ตควรทํางานและเรียกใช้แอปพลิเคชัน OpenThread แล้ว ก่อนหน้านี้แอปพลิเคชันนี้มีฟีเจอร์หลัก 2 รายการดังนี้

ตัวบ่งชี้บทบาทอุปกรณ์

ไฟ LED ที่ติดสว่างในแต่ละกระดานจะแสดงบทบาทปัจจุบันของโหนดชุดข้อความ ดังนี้

  • LED1 = ผู้นํา
  • LED2 = เราเตอร์
  • LED3 = อุปกรณ์สิ้นสุด

ไฟ LED จะสว่างเมื่อมีการเปลี่ยนแปลง คุณควรเห็นการเปลี่ยนแปลงเหล่านี้แล้วในกระดาน 1 หรือ 2 เครื่องภายใน 20 วินาทีของอุปกรณ์แต่ละเครื่องเปิดอยู่

มัลติแคสต์แบบ UDP

เมื่อกดปุ่ม 1 บนกระดาน ข้อความ UDP จะถูกส่งไปยังที่อยู่มัลติแคสต์ภายใน Mesh-Local ซึ่งรวมถึงโหนดอื่นๆ ทั้งหมดในเครือข่ายชุดข้อความ เมื่อได้รับข้อความนี้ ให้ปิดหรือเปิดไฟ LED4 บนกระดานอื่นๆ ทั้งหมด ไฟ LED 4 ดวงจะเปิดหรือปิดแต่ละกระดานจนกว่าจะได้รับข้อความ UDP อีก

203dd094acca1f97.png

9bbd96d9b1c63504

13. สาธิต: สังเกตการเปลี่ยนแปลงบทบาทของอุปกรณ์

อุปกรณ์ที่คุณแฟลชเป็นอุปกรณ์ชุดข้อความที่สมบูรณ์ (FTD) ประเภทหนึ่งที่เรียกว่าอุปกรณ์ที่มีสิทธิ์ใช้งานเราเตอร์ (REED) ซึ่งหมายความว่าแพลตฟอร์มเหล่านี้จะทํางานเป็นเราเตอร์หรืออุปกรณ์ปลายทาง และโปรโมตตัวเองจากอุปกรณ์ปลายทางไปยังเราเตอร์ได้

เธรดสามารถรองรับเราเตอร์ได้สูงสุด 32 ตัว และพยายามทําให้จํานวนเราเตอร์อยู่ในระหว่าง 16 ถึง 23 ตัว หากมี REED แนบมากับอุปกรณ์ปลายทางและเราเตอร์มีจํานวนต่ํากว่า 16 เครื่อง ก็จะโปรโมตเราเตอร์ด้วยตัวเองโดยอัตโนมัติ การเปลี่ยนแปลงนี้ควรเป็นเวลาแบบสุ่มภายในไม่กี่วินาทีที่คุณตั้งค่า otThreadSetRouterSelectionJitter ในแอปพลิเคชัน (20 วินาที)

เครือข่ายเทรดทั้งหมดจะมี Leader ซึ่งเป็นเราเตอร์ที่ทําหน้าที่จัดการชุดเราเตอร์ในเครือข่าย Thread เมื่ออุปกรณ์ทั้งหมดเปิดอยู่ หลังจาก 20 วินาที อุปกรณ์เครื่องหนึ่งควรเป็นผู้นํา (เปิด LED1) และอีก 2 เครื่องควรเป็นเราเตอร์ (เปิด LED2)

4e1e885861a66570.png

นําตัวแปรที่ได้คะแนนนํา

หากมีการนําผู้นําออกจากเครือข่ายชุดข้อความ เราเตอร์อื่นจะโปรโมตตัวเองให้ผู้นําเพื่อให้มั่นใจว่าเครือข่ายยังคงมีผู้นําอยู่

ปิดกระดานผู้นํา (อันที่มีไฟ LED1) โดยใช้สวิตช์เปิด/ปิด รอประมาณ 20 วินาที ไฟ LED 2 ดวง (หีบห่อ) จะปิดหนึ่งใน 2 กระดานที่เหลือ และจะปิดด้วย LED1 (ผู้นํา) ตอนนี้อุปกรณ์เครื่องนี้คือผู้นําเครือข่ายชุดข้อความ

4c57c87adb40e0e3.png

เปิดใช้กระดานผู้นําเดิมอีกครั้ง ควรเข้าร่วมเครือข่ายชุดข้อความอีกครั้งโดยอัตโนมัติเป็นอุปกรณ์ปลายทาง (ไฟ LED3 สว่าง) ภายใน 20 วินาที (Jitter Selectเป็นภาษาJitter) โปรโมตตัวเองกับเราเตอร์ (ไฟ LED2 ติดสว่าง)

5f40afca2dcc4b5b.png

รีเซ็ตกระดาน

ปิดกระดานทั้ง 3 บาน แล้วเปิดอีกครั้งและสังเกตไฟ LED กระดานแรกที่เปิดขึ้นควรเริ่มต้นในบทบาทผู้นํา (มีไฟ LED1 อยู่) เราเตอร์แรกในเครือข่ายชุดข้อความจะกลายเป็นผู้นําโดยอัตโนมัติ

กระดานอีก 2 เครื่องเชื่อมต่อกับเครือข่ายตั้งแต่แรกเมื่ออุปกรณ์ปลายทาง (ไฟ LED 3 ดวง) แต่ควรโปรโมตตัวเองกับเราเตอร์ (มีไฟ LED2 จุด) ภายใน 20 วินาที

พาร์ติชันเครือข่าย

หากบอร์ดของคุณไม่ได้รับพลังงานเพียงพอ หรือการเชื่อมต่อวิทยุที่อ่อนเกินไป เครือข่ายชุดข้อความอาจแยกออกเป็นพาร์ติชัน และคุณอาจมีอุปกรณ์มากกว่า 1 เครื่องแสดงเป็นผู้นํา

ชุดข้อความเป็นการเยียวยาตัวเองได้ ดังนั้นพาร์ติชันควรผสานกลับไปเป็นพาร์ติชันเดียวโดยมีผู้นํา 1 คน

14. ตัวอย่าง: ส่งมัลติแคสต์แบบ UDP

หากดําเนินการต่อจากการออกกําลังกายก่อนหน้านี้ ไฟ LED จะไม่สว่างขึ้นบนอุปกรณ์ใดๆ

เลือกกระดานที่ต้องการแล้วกดปุ่ม 1 LED4 บนกระดานอื่นๆ ทั้งหมดในกลุ่มชุดข้อความที่เรียกใช้แอปพลิเคชันควรสลับสถานะ หากดําเนินการต่อจากแบบฝึกหัดก่อนหน้า ผู้เรียนควรเปิดใช้กิจกรรมนี้

f186a2618fdbe3fd.png

กดปุ่ม1 สําหรับกระดานเดิมอีกครั้ง LED4 บนกระดานอื่นๆ ทั้งหมดควรสลับอีกครั้ง

กดปุ่ม 1 บนกระดานอื่น และดูว่า LED4 เปิดใช้งานอย่างไรบนกระดานอื่นๆ กดปุ่ม 1 บนกระดานกระดานที่ LED4 เปิดอยู่ ไฟ LED 4 ดวงยังคงอยู่ในกระดานนั้น แต่สลับที่แผงอื่นๆ

f5865ccb8ab7aa34.png

พาร์ติชันเครือข่าย

หากกระดานมีการแบ่งพาร์ติชันและมีผู้นํามากกว่า 1 คน ผลลัพธ์ของข้อความมัลติแคสต์จะแตกต่างกันไป หากคุณกดปุ่ม 1 บนกระดานที่แบ่งพาร์ติชันแล้ว (จึงเป็นสมาชิกเพียงรายเดียวของเครือข่ายชุดข้อความที่แบ่งพาร์ติชันแล้ว) LED4 บนกระดานอื่นๆ จะไม่สว่างขึ้นในการตอบกลับ ในกรณีนี้ ให้รีเซ็ตกระดาน ซึ่งจะทําให้รูปแบบเครือข่ายชุดข้อความเดียวและการรับส่งข้อความ UDP ทํางานได้อย่างถูกต้อง

15. ยินดีด้วย

คุณได้สร้างแอปพลิเคชันที่ใช้ OpenThread API แล้ว

ตอนนี้คุณทราบแล้วว่า

  • วิธีตั้งโปรแกรมให้ปุ่มและ LED บนกระดานนักพัฒนาซอฟต์แวร์ Nดิก NRF52840
  • วิธีใช้ API ของ OpenThread API ทั่วไปและคลาส otInstance
  • วิธีตรวจสอบและตอบสนองต่อการเปลี่ยนแปลงสถานะ OpenThread
  • วิธีส่งข้อความ UDP ไปยังอุปกรณ์ทั้งหมดในเครือข่ายชุดข้อความ
  • วิธีแก้ไขไฟล์

ขั้นตอนถัดไป

จาก Codelab นี้ ให้ลองใช้แบบฝึกหัดต่อไปนี้

  • แก้ไขโมดูล GPIO ให้ใช้หมุด GPIO แทน LED ออนบอร์ด และเชื่อมต่อไฟ LED RGB ภายนอกที่เปลี่ยนสีตามบทบาทเราเตอร์
  • เพิ่มการรองรับ GPIO สําหรับแพลตฟอร์มตัวอย่างอื่น
  • ใช้ Router/Leader API เพื่อค้นหาและใช้คําสั่ง ping อุปกรณ์แต่ละรายการแทนการใช้มัลติแคสต์ไปยัง ping อุปกรณ์ทั้งหมดจากการกดปุ่ม
  • เชื่อมต่อเครือข่ายที่ทํางานร่วมกันของคุณกับอินเทอร์เน็ตโดยใช้เราเตอร์ OpenOpen Border แล้วนําหลายรายการออกจากเครือข่าย Thread เพื่อเปิดไฟ LED

อ่านเพิ่มเติม

โปรดดูแหล่งข้อมูล Openthread.io และ GitHub สําหรับแหล่งข้อมูลต่างๆ ของ OpenThread ได้แก่

  • แพลตฟอร์มที่รองรับ - ดูแพลตฟอร์มทั้งหมดที่รองรับ OpenThread
  • สร้าง OpenThread - รายละเอียดเพิ่มเติมเกี่ยวกับการสร้างและกําหนดค่า OpenThread
  • Thread Primer — การอ้างอิงที่ยอดเยี่ยมเกี่ยวกับแนวคิดของชุดข้อความ

ข้อมูลอ้างอิง: