การพัฒนาด้วย 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 บนบอร์ดนักพัฒนาซอฟต์แวร์ Nordic nRF52840
  • วิธีใช้ OpenThread API ทั่วไปและคลาส otInstance
  • วิธีตรวจสอบและตอบสนองต่อการเปลี่ยนแปลงสถานะ OpenThread
  • วิธีส่งข้อความ UDP ไปยังอุปกรณ์ทั้งหมดในเครือข่ายชุดข้อความ
  • วิธีแก้ไขการชดเชย

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

ฮาร์ดแวร์:

  • นักพัฒนาซอฟต์แวร์แนวนอร์ดิกเซมิคอนดักเตอร์ nRF52840 3 รายการ
  • สายเชื่อม USB กับ Micro-USB เพื่อเชื่อมต่อกระดาน
  • เครื่อง Linux ที่มีพอร์ต USB อย่างน้อย 3 พอร์ต

ซอฟต์แวร์:

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

เนื้อหาของ Codelab นี้ได้รับอนุญาตภายใต้ใบอนุญาต Creative Commons Attribution 3.0 และตัวอย่างโค้ดได้รับอนุญาตภายใต้ใบอนุญาต Apache 2.0 ยกเว้นที่ระบุไว้เป็นอย่างอื่น

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

กรอกข้อมูลใน Codelab ของฮาร์ดแวร์ให้ครบถ้วน

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

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

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

เครื่อง Linux

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

บอร์ด Ndic Semiconductor nRF52840

Codelab นี้ใช้กระดาน nRF52840 PDK 3 แผ่น

a6693da3ce213856.png

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

ในการสร้างและ Flash OpenThreads ให้ติดตั้ง SEGGER J-Link, เครื่องมือบรรทัดคําสั่ง nRF5x, ARM GNU Toolchain และแพ็กเกจ Linux ต่างๆ หากสร้าง Codelab สําหรับชุดข้อความเป็นชุดข้อความเสร็จแล้ว คุณจะมีทุกอย่างที่จําเป็นติดตั้งอยู่ หากไม่ถูกต้อง ให้ทําการ Codelab ให้เสร็จสิ้นก่อนดําเนินการต่อเพื่อให้มั่นใจว่าคุณจะสร้างและ Flash OpenThread ไปยังบอร์ด 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&#39 อยู่ที่ ./openthread/include/openthread ในที่เก็บ OpenThread API เหล่านี้ช่วยให้คุณเข้าถึงฟีเจอร์และฟังก์ชันต่างๆ ของ OpenThread ได้ทั้งในระดับเทรดและแพลตฟอร์มเพื่อใช้ในแอปพลิเคชัน

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

ดูข้อมูลอ้างอิงเกี่ยวกับ OpenThread 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 คือสิ่งที่คุณมักจะใช้เมื่อทํางานกับ API ของ OpenThread เมื่อเริ่มต้นแล้ว โครงสร้างนี้จะแสดงอินสแตนซ์แบบคงที่ของไลบรารี 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 ก่อน โดยใช้เนมสเปซ 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 ได้ หากต้องการเข้าถึงปุ่มและไฟ LED บนบอร์ด nRF52840 dev คุณต้องใช้ฟังก์ชันเหล่านั้นสําหรับแพลตฟอร์ม nRF52840 ในโค้ดนี้ คุณจะต้องเพิ่มฟังก์ชันที่

  • เริ่มต้นการปักหมุดและโหมดของ 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 และปุ่ม LED ของ nRF52840 ได้ที่ Nordic Semiconductor Infocenter

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

จากนั้นให้เพิ่มส่วนหัวสําหรับฟังก์ชันการทํางานของ 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;
    }
}

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

ฟังก์ชันแรกจะเป็นการเริ่มต้นกระดานสําหรับการกดปุ่ม และปุ่มที่ 2 จะส่งข้อความ 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 สําหรับส่วนที่สอง

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

#define UDP_PORT 1212

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

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

การดําเนินการ: เพิ่มการประกาศฟังก์ชัน

ในไฟล์ main.c หลังจากคําจํากัดความ otTaskletsSignalPending และก่อนฟังก์ชัน main() ให้เพิ่มฟังก์ชันเฉพาะ UDP รวมถึงตัวแปรคงที่เพื่อแสดงซ็อกเก็ต UDP ดังนี้

static void initUdp(otInstance *aInstance);
static void sendUdp(otInstance *aInstance);

static void handleButtonInterrupt(otInstance *aInstance);

void handleUdpReceive(void *aContext, otMessage *aMessage, 
                      const otMessageInfo *aMessageInfo);

static otUdpSocket sUdpSocket;

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

ใน 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 ในการอ้างอิง API ของ UDP

การดําเนินการ: ติดตั้งใช้งานการรับส่งข้อความ UDP

ใน main.c ให้เพิ่มการใช้งานฟังก์ชัน sendUdp หลังจากฟังก์ชัน initUdp ที่คุณเพิ่งเพิ่ม

/**
 * Send a UDP datagram
 */
void sendUdp(otInstance *aInstance)
{
    otError       error = OT_ERROR_NONE;
    otMessage *   message;
    otMessageInfo messageInfo;
    otIp6Address  destinationAddr;

    memset(&messageInfo, 0, sizeof(messageInfo));

    otIp6AddressFromString(UDP_DEST_ADDR, &destinationAddr);
    messageInfo.mPeerAddr    = destinationAddr;
    messageInfo.mPeerPort    = UDP_PORT;

    message = otUdpNewMessage(aInstance, NULL);
    otEXPECT_ACTION(message != NULL, error = OT_ERROR_NO_BUFS);

    error = otMessageAppend(message, UDP_PAYLOAD, sizeof(UDP_PAYLOAD));
    otEXPECT(error == OT_ERROR_NONE);

    error = otUdpSend(aInstance, &sUdpSocket, message, &messageInfo);

 exit:
    if (error != OT_ERROR_NONE && message != NULL)
    {
        otMessageFree(message);
    }
}

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

ดูข้อมูลเพิ่มเติมเกี่ยวกับฟังก์ชันที่ใช้เริ่มต้น UDP ได้ในข้อมูลอ้างอิง IPv6 และ 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: กําหนดค่าเครือข่ายชุดข้อความ

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

การใช้โครงสร้างนี้จะลบล้างค่าเริ่มต้นของเครือข่ายที่มีอยู่ใน OpenThread เพื่อให้แอปพลิเคชันของเราปลอดภัยมากขึ้นและจํากัดโหนด 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.mSeconds             = 1;
    aDataset.mActiveTimestamp.mTicks               = 0;
    aDataset.mActiveTimestamp.mAuthoritative       = false;
    aDataset.mComponents.mIsActiveTimestampPresent = true;

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

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

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

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

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

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

รายละเอียดในฟังก์ชัน พารามิเตอร์เครือข่ายเทรดที่เราใช้สําหรับแอปพลิเคชันนี้มีดังนี้

  • แชแนล = 15
  • รหัส PAN = 0x2222
  • รหัส PAN แบบขยาย = C0DE1AB5C0DE1AB5
  • คีย์เครือข่าย = 1234C0DE1AB51234C0DE1AB51234C0DE
  • ชื่อเครือข่าย = OTCodelab

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

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

API ของ OpenThread&#39 บางรายการแก้ไขการตั้งค่าที่ควรแก้ไขสําหรับการสาธิตหรือการทดสอบเท่านั้น ไม่ควรใช้ 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. ทําการอัปเดต CMake

ก่อนที่จะสร้างแอปพลิเคชัน จะต้องมีการอัปเดตเล็กน้อยเล็กน้อยในไฟล์ 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 ลงในไฟล์ NordicSemiconductor CMakeLists.txt เพื่อรวมไว้ในการสร้างไลบรารีของไดรเวอร์นอร์ดิก

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

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

...

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

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

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

สร้าง OpenThread

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

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

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

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

แฟลชบอร์ด

แฟลชไฟล์ ot-cli-ftd.hex บนกระดาน nRF52840 แต่ละแผ่น

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

20a3b4b480356447.png

โปรดทราบหมายเลขซีเรียลของกระดาน nRF52840 ดังนี้

c00d519ebec7e5f0.jpeg

ไปยังส่วนต่างๆ ของเครื่องมือบรรทัดคําสั่ง nRFx และกดปุ่ม 16 ของ 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 บนกระดานอื่นๆ ทั้งหมดเปิดหรือปิดอยู่ LED4 เปิดหรือปิดอยู่สําหรับแต่ละกระดานจนกว่าจะได้รับข้อความ UDP อื่น

203dd094acca1f97.png

9bbd96d9b1c63504.png

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

อุปกรณ์ที่คุณแฟลชเป็นอุปกรณ์ Full Thread Device (FTD) ประเภทหนึ่งที่เรียกว่า End Screen Eligible End (REED) ของเราเตอร์ ซึ่งหมายความว่าอุปกรณ์เหล่านี้ทําหน้าที่เป็นเราเตอร์หรืออุปกรณ์ปลายทาง และสามารถโปรโมตตนเองจากอุปกรณ์ปลายทางเป็นเราเตอร์ได้

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

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

ไฟล์ 4e1e885861a66570.png

นําผู้นําออก

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

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

ไฟล์ 4c57c87adb40e0e3.png

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

ไฟล์ 5f40afca2dcc4b5b.png

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

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

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

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

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

ชุดข้อความช่วยเยียวยาตัวเองได้ ดังนั้นพาร์ติชันต่างๆ ควรแบ่งพาร์ติชันต่างๆ กลับเข้าไปในพาร์ติชันเดียวโดยมีตัวแปรที่ดีที่สุด 1 กลุ่ม

14. การสาธิต: ส่งมัลติแคสต์ UDP

หากดําเนินการต่อจากการออกกําลังกายก่อนหน้า LED4 ไม่ควรติดสว่างในอุปกรณ์ใดๆ

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

f186a2618fdbe3fd.png

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

กดปุ่ม 1 บนกระดานอื่นและสังเกตสลับการใช้ LED4 บนกระดานอื่นๆ กดปุ่ม 1 บนกระดานหน้าใดหน้าหนึ่งที่ LED4 เปิดอยู่ ไฟ LED4 ยังเปิดอยู่สําหรับบอร์ดนั้น แต่เปิด/ปิดสําหรับเครื่องอื่นๆ อยู่

f5865ccb8ab7aa34.png

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

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

15. ยินดีด้วย

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

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

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

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

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

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

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

โปรดดู openthread.io และ GitHub สําหรับทรัพยากร OpenThread ที่หลากหลาย ซึ่งรวมถึง

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

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