1. บทนำ

OpenThread ที่ Nest เปิดตัวเป็นการใช้งานโปรโตคอลเครือข่าย Thread® แบบโอเพนซอร์ส Nest ได้เปิดตัว OpenThread เพื่อให้เทคโนโลยีที่ใช้ในผลิตภัณฑ์ Nest พร้อมให้บริการแก่นักพัฒนาซอฟต์แวร์ในวงกว้างเพื่อเร่งการพัฒนาผลิตภัณฑ์สำหรับบ้านอัจฉริยะ
ข้อกำหนดของ Thread กำหนดโปรโตคอลการสื่อสารแบบไร้สายที่เชื่อถือได้ ปลอดภัย และใช้พลังงานต่ำจากอุปกรณ์หนึ่งไปยังอีกอุปกรณ์หนึ่งที่ใช้ IPv6 สำหรับแอปพลิเคชันในบ้าน OpenThread ใช้เลเยอร์เครือข่าย Thread ทั้งหมด ซึ่งรวมถึง IPv6, 6LoWPAN, IEEE 802.15.4 ที่มีการรักษาความปลอดภัย MAC, การสร้างลิงก์ Mesh และการกำหนดเส้นทาง Mesh
ใน Codelab นี้ คุณจะได้ใช้ OpenThread API เพื่อเริ่มเครือข่าย Thread ตรวจสอบและตอบสนองต่อการเปลี่ยนแปลงบทบาทของอุปกรณ์ รวมถึงส่งข้อความ UDP ตลอดจนเชื่อมโยงการดำเนินการเหล่านี้กับปุ่มและไฟ LED ในฮาร์ดแวร์จริง

สิ่งที่คุณจะได้เรียนรู้
- วิธีตั้งโปรแกรมปุ่มและไฟ LED ในบอร์ดพัฒนา Nordic nRF52840
- วิธีใช้ API ของ OpenThread ทั่วไปและคลาส
otInstance - วิธีตรวจสอบและตอบสนองต่อการเปลี่ยนแปลงสถานะ OpenThread
- วิธีส่งข้อความ UDP ไปยังอุปกรณ์ทั้งหมดในเครือข่าย Thread
- วิธีแก้ไข Makefile
สิ่งที่คุณต้องมี
ฮาร์ดแวร์
- บอร์ดพัฒนา nRF52840 ของ Nordic Semiconductor 3 บอร์ด
- สาย USB เป็นไมโคร USB 3 เส้นสำหรับเชื่อมต่อบอร์ด
- เครื่อง Linux ที่มีพอร์ต USB อย่างน้อย 3 พอร์ต
ซอฟต์แวร์:
- GNU Toolchain
- เครื่องมือบรรทัดคำสั่ง Nordic nRF5x
- ซอฟต์แวร์ Segger J-Link
- OpenThread
- Git
เว้นแต่จะระบุไว้เป็นอย่างอื่น เนื้อหาของ Codelab นี้ได้รับอนุญาตภายใต้สัญญาอนุญาตครีเอทีฟคอมมอนส์สำหรับยอมรับสิทธิของผู้สร้าง (Creative Commons Attribution License) 3.0 และตัวอย่างโค้ดได้รับอนุญาตภายใต้สัญญาอนุญาต Apache 2.0
2. เริ่มต้นใช้งาน
ทำ Codelab ฮาร์ดแวร์ให้เสร็จสมบูรณ์
ก่อนเริ่ม Codelab นี้ คุณควรทำ Codelab สร้างเครือข่าย Thread ด้วยบอร์ด nRF52840 และ OpenThread ให้เสร็จสมบูรณ์ ซึ่งมีเนื้อหาดังนี้
- รายละเอียดซอฟต์แวร์ทั้งหมดที่คุณต้องใช้ในการสร้างและแฟลช
- สอนวิธีสร้าง OpenThread และแฟลชลงในบอร์ด Nordic nRF52840
- แสดงพื้นฐานของเครือข่าย Thread
Codelab นี้ไม่ได้อธิบายรายละเอียดเกี่ยวกับการตั้งค่าสภาพแวดล้อมที่จำเป็นในการสร้าง OpenThread และการเขียนบอร์ด มีเพียงวิธีการพื้นฐานสำหรับการเขียนบอร์ดเท่านั้น เราถือว่าคุณได้ทำ Codelab สร้างเครือข่าย Thread เสร็จแล้ว
เครื่อง Linux
Codelab นี้ออกแบบมาเพื่อใช้เครื่อง Linux ที่ใช้ i386 หรือ x86 เพื่อแฟลชบอร์ดพัฒนา Thread ทั้งหมด เราได้ทดสอบทุกขั้นตอนใน Ubuntu 14.04.5 LTS (Trusty Tahr)
บอร์ด Nordic Semiconductor nRF52840
Codelab นี้ใช้บอร์ด nRF52840 PDK 3 บอร์ด

ติดตั้งซอฟต์แวร์
หากต้องการสร้างและแฟลช OpenThread คุณต้องติดตั้ง SEGGER J-Link, เครื่องมือบรรทัดคำสั่ง nRF5x, ARM GNU Toolchain และแพ็กเกจ Linux ต่างๆ หากทำ Codelab สร้างเครือข่าย Thread ตามที่กำหนด คุณจะติดตั้งทุกอย่างที่จำเป็นไว้แล้ว หากยังไม่ได้ทำ ให้ทำ Codelab นั้นให้เสร็จก่อนดำเนินการต่อ เพื่อให้แน่ใจว่าคุณสามารถสร้างและแฟลช OpenThread ไปยังบอร์ดพัฒนา nRF52840 ได้
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 ทั้งในระดับ Thread และแพลตฟอร์มเพื่อใช้ในแอปพลิเคชันของคุณ
- ข้อมูลและการควบคุมอินสแตนซ์ OpenThread
- บริการแอปพลิเคชัน เช่น IPv6, UDP และ CoAP
- การจัดการข้อมูลเข้าสู่ระบบเครือข่าย รวมถึงบทบาทผู้ดูแลระบบและผู้เข้าร่วม
- การจัดการ Border Router
- ฟีเจอร์ที่ได้รับการปรับปรุง เช่น การควบคุมดูแลบุตรหลานและการตรวจจับการติดขัด
ดูข้อมูลอ้างอิงเกี่ยวกับ OpenThread API ทั้งหมดได้ที่ openthread.io/reference
การใช้ API
หากต้องการใช้ 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 และอนุญาตให้ผู้ใช้ทำการเรียก API ของ OpenThread ได้
ตัวอย่างเช่น อินสแตนซ์ 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 ที่เราจะใช้เพื่อเชื่อมต่อกับปุ่มและ LED ของ nRF52840 ต้องประกาศใน 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 คุณต้องใช้ฟังก์ชันเหล่านั้นสำหรับแพลตฟอร์ม nRF52840 ในโค้ดนี้ คุณจะเพิ่มฟังก์ชันที่ทำสิ่งต่อไปนี้
- เริ่มต้นพินและโหมด GPIO
- ควบคุมแรงดันไฟฟ้าในพิน
- เปิดใช้การขัดจังหวะ GPIO และลงทะเบียนการเรียกกลับ
ในไดเรกทอรี ./src/src ให้สร้างไฟล์ใหม่ชื่อ gpio.c เพิ่มเนื้อหาต่อไปนี้ในไฟล์ใหม่นี้
./src/src/gpio.c (ไฟล์ใหม่)
การดำเนินการ: เพิ่มคำจำกัดความ
คำจำกัดความเหล่านี้ทำหน้าที่เป็นนามธรรมระหว่างค่าเฉพาะของ nRF52840 กับตัวแปรที่ใช้ในระดับแอปพลิเคชัน OpenThread
/** * @file * This file implements the system abstraction for GPIO and GPIOTE. * */ #define BUTTON_GPIO_PORT 0x50000300UL #define BUTTON_PIN 11 // button #1 #define GPIO_LOGIC_HI 0 #define GPIO_LOGIC_LOW 1 #define LED_GPIO_PORT 0x50000300UL #define LED_1_PIN 13 // turn on to indicate leader role #define LED_2_PIN 14 // turn on to indicate router role #define LED_3_PIN 15 // turn on to indicate child role #define LED_4_PIN 16 // turn on to indicate UDP receive
ดูข้อมูลเพิ่มเติมเกี่ยวกับปุ่มและไฟ LED ของ nRF52840 ได้ที่ Infocenter ของ 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"
การดำเนินการ: เพิ่มฟังก์ชัน Callback และฟังก์ชัน Interrupt สำหรับปุ่ม 1
จากนั้นเพิ่มโค้ดนี้ in_pin1_handler คือฟังก์ชัน Callback ที่ลงทะเบียนเมื่อมีการเริ่มต้นฟังก์ชันการทำงานของการกดปุ่ม (ในภายหลังในไฟล์นี้)
โปรดสังเกตว่าการเรียกกลับนี้ใช้มาโคร 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
การดำเนินการ: เพิ่มส่วนหัว
ในส่วน includes ของไฟล์ 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 การลงทะเบียน Callback นี้จะบอกให้ OpenThread เรียกใช้ฟังก์ชัน handleNetifStateChange เมื่อใดก็ตามที่สถานะอินสแตนซ์ OpenThread เปลี่ยนแปลง
/* Register Thread state change handler */ otSetStateChangedCallback(instance, handleNetifStateChanged, instance);
การดำเนินการ: เพิ่มการติดตั้งใช้งานการเปลี่ยนแปลงสถานะ
ใน main.c ให้ใช้ฟังก์ชัน handleNetifStateChanged หลังจากฟังก์ชัน main() ฟังก์ชันนี้จะตรวจสอบ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 ได้
- จัดการข้อความ UDP ขาเข้า
- สลับ LED4 เพื่อตอบสนองต่อข้อความ UDP ขาเข้า
เปิดไฟล์ ./openthread/examples/apps/cli/main.c ในโปรแกรมแก้ไขข้อความที่ต้องการ
./openthread/examples/apps/cli/main.c
การดำเนินการ: เพิ่มไฟล์ส่วนหัว
ในส่วน includes ที่ด้านบนของไฟล์ 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 คือที่อยู่มัลติคาสต์เฉพาะใน Mesh ระบบจะส่งข้อความที่ส่งไปยังที่อยู่นี้ไปยังอุปกรณ์ Full Thread ทั้งหมดในเครือข่าย ดูข้อมูลเพิ่มเติมเกี่ยวกับการรองรับการส่งแบบหลายผู้รับใน 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 ฟังก์ชันจะเปิดซ็อกเก็ตและลงทะเบียนฟังก์ชัน Callback (handleUdpReceive) เมื่อได้รับข้อความ UDP otUdpBind จะเชื่อมซ็อกเก็ตกับอินเทอร์เฟซเครือข่าย Thread โดยส่ง 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 ซึ่งจะล้างบัฟเฟอร์
ดูข้อมูลเพิ่มเติมเกี่ยวกับฟังก์ชันที่ใช้ในการเริ่มต้น 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: กำหนดค่าเครือข่าย Thread
เพื่อความสะดวกในการสาธิต เราต้องการให้อุปกรณ์เริ่ม Thread ทันทีและเข้าร่วมเครือข่ายเมื่อเปิดเครื่อง โดยเราจะใช้otOperationalDatasetโครงสร้าง โครงสร้างนี้มีพารามิเตอร์ทั้งหมดที่จำเป็นในการส่งข้อมูลเข้าสู่ระบบเครือข่าย Thread ไปยังอุปกรณ์
การใช้โครงสร้างนี้จะลบล้างค่าเริ่มต้นของเครือข่ายที่สร้างขึ้นใน OpenThread เพื่อให้แอปพลิเคชันมีความปลอดภัยมากขึ้น และจำกัดโหนด Thread ในเครือข่ายให้เฉพาะโหนดที่เรียกใช้แอปพลิเคชันเท่านั้น
เปิดไฟล์ ./openthread/examples/apps/cli/main.c ในโปรแกรมแก้ไขข้อความที่ต้องการอีกครั้ง
./openthread/examples/apps/cli/main.c
การดำเนินการ: เพิ่มการรวมส่วนหัว
ในส่วน includes ที่ด้านบนของไฟล์ main.c ให้เพิ่มไฟล์ส่วนหัวของ API ที่คุณจะต้องใช้เพื่อกำหนดค่าเครือข่าย Thread ดังนี้
#include <openthread/dataset_ftd.h>
การดำเนินการ: เพิ่มการประกาศฟังก์ชันสำหรับการตั้งค่าการกำหนดค่าเครือข่าย
เพิ่มการประกาศนี้ลงใน main.c หลังจากส่วนหัวรวมและก่อนคำสั่ง #if ระบบจะกำหนดฟังก์ชันนี้หลังจากฟังก์ชันแอปพลิเคชันหลัก
static void setNetworkConfiguration(otInstance *aInstance);
การดำเนินการ: เพิ่มการเรียกการกำหนดค่าเครือข่าย
ใน main.c ให้เพิ่มการเรียกฟังก์ชันนี้ไปยังฟังก์ชัน main() หลังจากการเรียก otSetStateChangedCallback ฟังก์ชันนี้จะกำหนดค่าชุดข้อมูลเครือข่าย Thread
/* Override default network credentials */ setNetworkConfiguration(instance);
การดำเนินการ: เพิ่มการเรียกเพื่อเปิดใช้อินเทอร์เฟซเครือข่ายและสแต็ก Thread
ใน 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);
การดำเนินการ: ใช้การกำหนดค่าเครือข่าย Thread
ใน 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);
}
ดังที่ระบุไว้ในฟังก์ชัน พารามิเตอร์เครือข่าย Thread ที่เราใช้สำหรับแอปพลิเคชันนี้คือ
- Channel = 15
- PAN ID = 0x2222
- รหัส PAN แบบขยาย = C0DE1AB5C0DE1AB5
- คีย์เครือข่าย = 1234C0DE1AB51234C0DE1AB51234C0DE
- ชื่อเครือข่าย = OTCodelab
นอกจากนี้ เรายังลดความผันผวนในการเลือกเราเตอร์ที่นี่ด้วย เพื่อให้อุปกรณ์เปลี่ยนบทบาทได้เร็วขึ้นเพื่อวัตถุประสงค์ในการสาธิต โปรดทราบว่าการดำเนินการนี้จะทำก็ต่อเมื่อโหนดเป็น FTD (อุปกรณ์ Thread แบบเต็ม) เท่านั้น ดูข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ได้ในขั้นตอนถัดไป
9. API: ฟังก์ชันที่ถูกจำกัด
API บางรายการของ OpenThread จะแก้ไขการตั้งค่าที่ควรแก้ไขเพื่อวัตถุประสงค์ในการทดสอบหรือการสาธิตเท่านั้น ไม่ควรใช้ API เหล่านี้ในการติดตั้งใช้งานแอปพลิเคชันที่ใช้ OpenThread ในเวอร์ชันที่ใช้งานจริง
ตัวอย่างเช่น ฟังก์ชัน otThreadSetRouterSelectionJitter จะปรับเวลา (เป็นวินาที) ที่อุปกรณ์ปลายทางใช้ในการเลื่อนตัวเองเป็นเราเตอร์ ค่าเริ่มต้นสำหรับค่านี้คือ 120 ตามข้อกำหนดของ Thread เพื่อความสะดวกในการใช้งานใน Codelab นี้ เราจะเปลี่ยนเป็น 20 เพื่อให้คุณไม่ต้องรอนานมากนักเพื่อให้โหนด Thread เปลี่ยนบทบาท
หมายเหตุ: อุปกรณ์ MTD จะไม่กลายเป็นเราเตอร์ และการรองรับฟังก์ชันอย่าง otThreadSetRouterSelectionJitter จะไม่รวมอยู่ในการสร้าง MTD ต่อมาเราต้องระบุตัวเลือก CMake -DOT_MTD=OFF มิฉะนั้นเราจะพบว่าการสร้างล้มเหลว
คุณยืนยันได้โดยดูที่otThreadSetRouterSelectionJitterคำจำกัดความของฟังก์ชันOPENTHREAD_FTD ซึ่งอยู่ในคำสั่งก่อนประมวลผลของ 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 ใหม่ ดังนี้
การดำเนินการ: เพิ่มแหล่งที่มาของ GPIO ลงใน./src/CMakeLists.txt file
เปิด ./src/CMakeLists.txt ในโปรแกรมแก้ไขข้อความที่ต้องการ แล้วเพิ่มไฟล์ลงในส่วน NRF_COMM_SOURCES
... set(NRF_COMM_SOURCES ... src/gpio.c ... ) ...
./third_party/NordicSemiconductor/CMakeLists.txt
สุดท้าย ให้เพิ่มไฟล์ไดรเวอร์ nrfx_gpiote.c ลงในไฟล์ CMakeLists.txt ของ NordicSemiconductor เพื่อให้รวมอยู่ในการสร้างไลบรารีของไดรเวอร์ Nordic
การดำเนินการ: เพิ่มไดรเวอร์ GPIO ลงในไฟล์ CMakeLists.txt NordicSemiconductor
เปิด ./third_party/NordicSemiconductor/CMakeLists.txt ในโปรแกรมแก้ไขข้อความที่ต้องการ แล้วเพิ่มไฟล์ลงในส่วน COMMON_SOURCES
... set(COMMON_SOURCES ... nrfx/drivers/src/nrfx_gpiote.c ... ) ...
11. ตั้งค่าอุปกรณ์
เมื่ออัปเดตโค้ดทั้งหมดเสร็จแล้ว คุณก็พร้อมที่จะสร้างและแฟลชแอปพลิเคชันไปยังบอร์ดพัฒนา Nordic nRF52840 ทั้ง 3 บอร์ด อุปกรณ์แต่ละเครื่องจะทําหน้าที่เป็นอุปกรณ์ Thread แบบเต็ม (FTD)
สร้าง OpenThread
สร้างไบนารี FTD ของ OpenThread สำหรับแพลตฟอร์ม nRF52840
$ cd ~/ot-nrf528xx $ ./script/build nrf52840 UART_trans -DOT_MTD=OFF -DOT_APP_RCP=OFF -DOT_RCP=OFF
ไปที่ไดเรกทอรีที่มีไบนารี CLI ของ FTD ของ OpenThread แล้วแปลงเป็นรูปแบบเลขฐานสิบหกด้วย 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 เข้ากับพอร์ตแก้ไขข้อบกพร่องแบบไมโคร USB ข้างหมุดจ่ายไฟภายนอกบนบอร์ด nRF52840 แล้วเสียบเข้ากับเครื่อง Linux หากตั้งค่าถูกต้อง LED5 จะติด

โปรดจดหมายเลขซีเรียลของบอร์ด nRF52840 ไว้เช่นเดิม

ไปที่ตำแหน่งของเครื่องมือบรรทัดคำสั่ง nRFx แล้วแฟลชไฟล์ hex ของ 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 ในลักษณะเดียวกัน และคำสั่งในการแฟลชจะเหมือนกัน ยกเว้นหมายเลขซีเรียลของบอร์ด โปรดตรวจสอบว่าคุณใช้หมายเลขซีเรียลที่ไม่ซ้ำกันของแต่ละบอร์ดใน
nrfjprog คำสั่งการแฟลช
หากสำเร็จ ไฟ LED1, LED2 หรือ LED3 จะสว่างขึ้นในแต่ละบอร์ด คุณอาจเห็นไฟ LED เปลี่ยนจาก 3 เป็น 2 (หรือ 2 เป็น 1) หลังจากแฟลชไม่นาน (ฟีเจอร์การเปลี่ยนบทบาทของอุปกรณ์)
12. ฟังก์ชันการทำงานของแอปพลิเคชัน
ตอนนี้บอร์ด nRF52840 ทั้ง 3 ควรเปิดเครื่องและเรียกใช้แอปพลิเคชัน OpenThread ของเราได้แล้ว ดังที่ได้กล่าวไว้ก่อนหน้านี้ แอปพลิเคชันนี้มีฟีเจอร์หลัก 2 อย่าง
ตัวบ่งชี้บทบาทของอุปกรณ์
ไฟ LED ที่สว่างบนบอร์ดแต่ละบอร์ดจะแสดงบทบาทปัจจุบันของโหนด Thread ดังนี้
- LED1 = ผู้นำ
- LED2 = เราเตอร์
- LED3 = อุปกรณ์ปลายทาง
เมื่อบทบาทเปลี่ยนไป ไฟ LED ที่สว่างก็จะเปลี่ยนไปด้วย คุณควรเห็นการเปลี่ยนแปลงเหล่านี้ในบอร์ดอย่างน้อย 1-2 บอร์ดภายใน 20 วินาทีหลังจากเปิดเครื่องแต่ละอุปกรณ์
UDP Multicast
เมื่อกดปุ่ม 1 บนบอร์ด ระบบจะส่งข้อความ UDP ไปยังที่อยู่มัลติแคสต์เฉพาะเครือข่ายแบบเมช ซึ่งรวมถึงโหนดอื่นๆ ทั้งหมดในเครือข่าย Thread เมื่อได้รับข้อความนี้ LED4 ในบอร์ดอื่นๆ ทั้งหมดจะสลับเปิดหรือปิด LED4 จะยังคงเปิดหรือปิดสำหรับแต่ละบอร์ดจนกว่าจะได้รับข้อความ UDP อื่น


13. การสาธิต: สังเกตการเปลี่ยนแปลงบทบาทของอุปกรณ์
อุปกรณ์ที่คุณแฟลชเป็นอุปกรณ์ Thread แบบเต็ม (FTD) ประเภทหนึ่งที่เรียกว่าอุปกรณ์ปลายทางที่มีสิทธิ์เป็นเราเตอร์ (REED) ซึ่งหมายความว่าอุปกรณ์เหล่านี้สามารถทำหน้าที่เป็นทั้งเราเตอร์หรืออุปกรณ์ปลายทาง และสามารถเลื่อนระดับจากอุปกรณ์ปลายทางเป็นเราเตอร์ได้
Thread รองรับเราเตอร์ได้สูงสุด 32 ตัว แต่พยายามให้มีเราเตอร์อยู่ระหว่าง 16 ถึง 23 ตัว หาก REED แนบเป็นอุปกรณ์ปลายทางและจำนวนเราเตอร์ต่ำกว่า 16 เราเตอร์จะเลื่อนระดับตัวเองเป็นเราเตอร์โดยอัตโนมัติ การเปลี่ยนแปลงนี้ควรเกิดขึ้นในเวลาแบบสุ่มภายในจำนวนวินาทีที่คุณตั้งค่า otThreadSetRouterSelectionJitter ในแอปพลิเคชัน (20 วินาที)
เครือข่าย Thread ทุกเครือข่ายจะมีผู้นำ ซึ่งเป็นเราเตอร์ที่มีหน้าที่จัดการชุดเราเตอร์ในเครือข่าย Thread เมื่อเปิดอุปกรณ์ทั้งหมดแล้ว หลังจากผ่านไป 20 วินาที อุปกรณ์เครื่องใดเครื่องหนึ่งควรเป็นผู้นำ (LED1 ติด) และอีก 2 เครื่องควรเป็นเราเตอร์ (LED2 ติด)

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

เปิดลีดเดอร์บอร์ดเดิมอีกครั้ง โดยควรเข้าร่วมเครือข่าย Thread อีกครั้งโดยอัตโนมัติในฐานะอุปกรณ์ปลายทาง (LED3 สว่าง) ภายใน 20 วินาที (ความผันผวนในการเลือกเราเตอร์) อุปกรณ์จะเลื่อนระดับตัวเองเป็นเราเตอร์ (LED2 สว่างขึ้น)

รีเซ็ตบอร์ด
ปิดบอร์ดทั้ง 3 ตัว จากนั้นเปิดอีกครั้งและสังเกตไฟ LED บอร์ดแรกที่เปิดเครื่องควรเริ่มต้นในบทบาทผู้นำ (LED1 สว่าง) ซึ่งเราเตอร์ตัวแรกในเครือข่าย Thread จะกลายเป็นผู้นำโดยอัตโนมัติ
บอร์ดอีก 2 บอร์ดจะเชื่อมต่อกับเครือข่ายในตอนแรกเป็นอุปกรณ์ปลายทาง (LED3 สว่าง) แต่ควรเลื่อนระดับตัวเองเป็นเราเตอร์ (LED2 สว่าง) ภายใน 20 วินาที
การแบ่งพาร์ติชันเครือข่าย
หากบอร์ดได้รับพลังงานไม่เพียงพอ หรือการเชื่อมต่อวิทยุระหว่างบอร์ดอ่อน เครือข่าย Thread อาจแยกออกเป็นพาร์ติชัน และคุณอาจมีอุปกรณ์มากกว่า 1 เครื่องที่แสดงเป็นลีดเดอร์
Thread จะซ่อมแซมตัวเอง ดังนั้นพาร์ติชันควรจะผสานกลับเป็นพาร์ติชันเดียวที่มีลีดเดอร์ 1 รายในที่สุด
14. การสาธิต: ส่งมัลติแคสต์ UDP
หากทำต่อจากแบบฝึกหัดก่อนหน้า ไฟ LED4 ไม่ควรติดในอุปกรณ์ใดๆ
เลือกบอร์ดใดก็ได้แล้วกดปุ่ม 1 LED4 ในบอร์ดอื่นๆ ทั้งหมดในเครือข่าย Thread ที่เรียกใช้แอปพลิเคชันควรสลับสถานะ หากดำเนินการต่อจากแบบฝึกหัดก่อนหน้า ตอนนี้ควรเปิดอยู่

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

การแบ่งพาร์ติชันเครือข่าย
หากบอร์ดมีการแบ่งพาร์ติชันและมีลีดเดอร์มากกว่า 1 คน ผลลัพธ์ของข้อความแบบหลายผู้รับจะแตกต่างกันระหว่างบอร์ด หากคุณกดปุ่ม 1 บนบอร์ดที่มีการแบ่งพาร์ติชัน (และเป็นสมาชิกเดียวของเครือข่าย Thread ที่แบ่งพาร์ติชัน) ไฟ LED4 บนบอร์ดอื่นๆ จะไม่สว่างขึ้นเพื่อตอบสนอง หากเกิดกรณีนี้ ให้รีเซ็ตบอร์ด ซึ่งควรจะสร้างเครือข่าย Thread เดียวขึ้นมาใหม่ และการรับส่งข้อความ UDP ควรทำงานได้อย่างถูกต้อง
15. ยินดีด้วย
คุณได้สร้างแอปพลิเคชันที่ใช้ OpenThread API แล้ว
ตอนนี้คุณก็รู้แล้วว่า
- วิธีตั้งโปรแกรมปุ่มและไฟ LED ในบอร์ดพัฒนา Nordic nRF52840
- วิธีใช้ API ของ OpenThread ทั่วไปและคลาส
otInstance - วิธีตรวจสอบและตอบสนองต่อการเปลี่ยนแปลงสถานะ OpenThread
- วิธีส่งข้อความ UDP ไปยังอุปกรณ์ทั้งหมดในเครือข่าย Thread
- วิธีแก้ไข Makefile
ขั้นตอนถัดไป
ลองทำแบบฝึกหัดต่อไปนี้โดยอิงจาก Codelab นี้
- แก้ไขโมดูล GPIO เพื่อใช้พิน GPIO แทน LED ในตัว และเชื่อมต่อ LED RGB ภายนอกที่เปลี่ยนสีตามบทบาทของเราเตอร์
- เพิ่มการรองรับ GPIO สำหรับแพลตฟอร์มตัวอย่างอื่น
- แทนที่จะใช้การส่งแบบหลายผู้รับเพื่อ Ping อุปกรณ์ทั้งหมดจากการกดปุ่ม ให้ใช้ Router/Leader API เพื่อค้นหาและ Ping อุปกรณ์แต่ละเครื่อง
- เชื่อมต่อเครือข่ายที่ทำงานร่วมกันกับอินเทอร์เน็ตโดยใช้ OpenThread Border Router และส่งแบบหลายผู้รับจากภายนอกเครือข่าย Thread เพื่อเปิดไฟ LED
อ่านเพิ่มเติม
ดูข้อมูลเกี่ยวกับ OpenThread ได้ที่ openthread.io และ GitHub ซึ่งมีแหล่งข้อมูล OpenThread มากมาย เช่น
- แพลตฟอร์มที่รองรับ - ดูแพลตฟอร์มทั้งหมดที่รองรับ OpenThread
- สร้าง OpenThread - รายละเอียดเพิ่มเติมเกี่ยวกับการสร้างและกำหนดค่า OpenThread
- ข้อมูลเบื้องต้นเกี่ยวกับ Thread - แหล่งข้อมูลที่ยอดเยี่ยมเกี่ยวกับแนวคิดของ Thread
ข้อมูลอ้างอิง: