1. บทนำ
OpenThread ที่ Nest เปิดตัวเป็นการใช้งานโปรโตคอลเครือข่าย Thread® แบบโอเพนซอร์ส Nest ได้เปิดตัว OpenThread เพื่อทำให้เทคโนโลยีที่ใช้ในผลิตภัณฑ์ Nest พร้อมใช้งานสำหรับนักพัฒนาซอฟต์แวร์ในวงกว้าง เพื่อเร่งการพัฒนาผลิตภัณฑ์สำหรับบ้านอัจฉริยะ
ข้อกำหนด Thread กำหนดโปรโตคอลการสื่อสารแบบไร้สายจากอุปกรณ์หนึ่งไปยังอีกอุปกรณ์หนึ่งที่ใช้ IPv6 ซึ่งเชื่อถือได้ ปลอดภัย และประหยัดพลังงานสำหรับแอปพลิเคชันในบ้าน OpenThread ใช้เลเยอร์เครือข่าย Thread ทั้งหมด ซึ่งรวมถึง IPv6, 6LoWPAN, IEEE 802.15.4 ที่มีการรักษาความปลอดภัย MAC, การสร้าง Mesh Link และการกําหนดเส้นทาง Mesh
ใน Codelab นี้ คุณจะใช้ OpenThread API เพื่อเริ่มเครือข่ายเทรด ตรวจสอบและตอบสนองต่อการเปลี่ยนแปลงบทบาทของอุปกรณ์ รวมถึงส่งข้อความ UDP และเชื่อมโยงการดำเนินการเหล่านี้กับปุ่มและ LED ในฮาร์ดแวร์จริง
สิ่งที่คุณจะได้เรียนรู้
- วิธีตั้งโปรแกรมปุ่มและ LED ในบอร์ดพัฒนา Nordic nRF52840
- วิธีใช้ OpenThread API ทั่วไปและคลาส
otInstance
- วิธีตรวจสอบและตอบสนองต่อการเปลี่ยนแปลงสถานะ OpenThread
- วิธีส่งข้อความ UDP ไปยังอุปกรณ์ทั้งหมดในเครือข่ายเทรด
- วิธีแก้ไข 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 สร้างเครือข่ายเทรดด้วยบอร์ด nRF52840 และ OpenThread ให้เสร็จสิ้น ซึ่งจะดำเนินการต่อไปนี้
- รายละเอียดซอฟต์แวร์ทั้งหมดที่จำเป็นสำหรับการสร้างและการแฟลช
- สอนวิธีสร้าง OpenThread และแฟลชลงในบอร์ด Nordic nRF52840
- สาธิตข้อมูลเบื้องต้นเกี่ยวกับเครือข่ายเทรด
ไม่มีการตั้งค่าสภาพแวดล้อมใดๆ ที่จำเป็นต่อการสร้าง OpenThread และแฟลชบอร์ดใน Codelab นี้ มีเพียงวิธีการพื้นฐานในการแฟลชบอร์ดเท่านั้น เราจะถือว่าคุณได้ทำ Codelab สร้างเครือข่ายเทรดเสร็จแล้ว
เครื่อง Linux
Codelab นี้ออกแบบมาเพื่อใช้เครื่อง Linux ที่ใช้ i386 หรือ x86 เพื่อแฟลชบอร์ดการพัฒนาเทรดทั้งหมด ขั้นตอนทั้งหมดได้รับการทดสอบใน Ubuntu 14.04.5 LTS (Trusty Tahr)
บอร์ด nRF52840 ของ Nordic Semiconductor
Codelab นี้ใช้บอร์ด PDK nRF52840 3 บอร์ด
ติดตั้งซอฟต์แวร์
หากต้องการสร้างและแฟลช OpenThread คุณต้องติดตั้ง SEGGER J-Link, เครื่องมือบรรทัดคำสั่ง nRF5x, ARM GNU Toolchain และแพ็กเกจ Linux ต่างๆ หากทำ Codelab สร้างเครือข่ายเทรดตามที่กำหนดเสร็จแล้ว คุณจะมีทุกอย่างที่จำเป็นติดตั้งไว้แล้ว หากยังไม่ได้ทำ ให้ทำตาม 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 ทั้งในระดับเทรดและระดับแพลตฟอร์มเพื่อใช้ในแอปพลิเคชัน
- ข้อมูลและการควบคุมอินสแตนซ์ 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 และอนุญาตให้ผู้ใช้เรียก 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 ที่เราจะใช้เพื่อเชื่อมต่อกับปุ่มและ 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 ได้ที่ศูนย์ข้อมูล 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
หลังส่วนหัว "include" และก่อนคำสั่ง #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()
ฟังก์ชันนี้จะตรวจสอบ Flag 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
การดําเนินการ: เพิ่มส่วนหัว "รวม"
ในส่วน "รวม" ที่ด้านบนของไฟล์ 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
ให้เพิ่มค่าคงที่และคำจำกัดความเฉพาะ UDP หลังส่วน includes และก่อนคำสั่ง #if
#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 on openthread.io
การดําเนินการ: เพิ่มการประกาศฟังก์ชัน
ในไฟล์ main.c
ให้เพิ่มฟังก์ชันเฉพาะ UDP รวมถึงตัวแปรแบบคงที่เพื่อแสดงซ็อกเก็ต UDP หลังคําจํากัดความ otTaskletsSignalPending
และก่อนฟังก์ชัน main()
ดังนี้
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);
การดําเนินการ: ใช้ Button Interrupt Handler
ใน 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
จะเชื่อมโยงซ็อกเก็ตกับอินเทอร์เฟซเครือข่ายเทรดโดยส่ง 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: กำหนดค่าเครือข่ายเทรด
เราต้องการให้อุปกรณ์เริ่มใช้เทรดและรวมกันเป็นเครือข่ายทันทีที่เปิดเครื่องเพื่อให้สาธิตได้ง่าย โดยเราจะใช้โครงสร้าง otOperationalDataset
โครงสร้างนี้มีพารามิเตอร์ทั้งหมดที่จำเป็นในการส่งข้อมูลเข้าสู่ระบบเครือข่ายเทรดไปยังอุปกรณ์
การใช้โครงสร้างนี้จะลบล้างค่าเริ่มต้นของเครือข่ายที่สร้างไว้ใน OpenThread เพื่อให้แอปพลิเคชันของเราปลอดภัยยิ่งขึ้นและจำกัดโหนดเทรดในเครือข่ายของเราไว้เฉพาะโหนดที่ใช้งานแอปพลิเคชันเท่านั้น
เปิดไฟล์ ./openthread/examples/apps/cli/main.c
อีกครั้งในเครื่องมือแก้ไขข้อความที่ต้องการ
./openthread/examples/apps/cli/main.c
การดําเนินการ: เพิ่มการรวมส่วนหัว
ในส่วน includes ที่ด้านบนของไฟล์ main.c
ให้เพิ่มไฟล์ส่วนหัว API ที่จําเป็นในการกําหนดค่าเครือข่ายเทรด
#include <openthread/dataset_ftd.h>
การดำเนินการ: เพิ่มการประกาศฟังก์ชันสำหรับการตั้งค่าการกำหนดค่าเครือข่าย
เพิ่มประกาศนี้ลงใน main.c
หลังส่วนหัว "include" และก่อนคำสั่ง #if
ระบบจะกำหนดฟังก์ชันนี้ไว้หลังฟังก์ชันหลักของแอปพลิเคชัน
static void setNetworkConfiguration(otInstance *aInstance);
การดําเนินการ: เพิ่มการเรียกคําสั่งการกําหนดค่าเครือข่าย
ใน main.c
ให้เพิ่มการเรียกฟังก์ชันนี้ลงในฟังก์ชัน main()
หลังการเรียก otSetStateChangedCallback
ฟังก์ชันนี้จะกำหนดค่าชุดข้อมูลเครือข่ายเทรด
/* 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);
การดําเนินการ: ใช้การกําหนดค่าเครือข่ายเทรด
ใน 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); }
พารามิเตอร์เครือข่ายเทรดที่เราใช้สำหรับแอปพลิเคชันนี้มีรายละเอียดดังนี้
- Channel = 15
- รหัส PAN = 0x2222
- รหัส PAN แบบขยาย = C0DE1AB5C0DE1AB5
- รหัสเครือข่าย = 1234C0DE1AB51234C0DE1AB51234C0DE
- ชื่อเครือข่าย = OTCodelab
นอกจากนี้ เรายังลดการกระตุกของการเลือกเราเตอร์ด้วย เพื่อให้อุปกรณ์เปลี่ยนบทบาทได้เร็วขึ้นเพื่อวัตถุประสงค์ในการสาธิต โปรดทราบว่าการดำเนินการนี้จะทำได้ก็ต่อเมื่อโหนดเป็น FTD (อุปกรณ์เทรดแบบเต็ม) เท่านั้น ดูข้อมูลเพิ่มเติมได้ที่ขั้นตอนถัดไป
9. API: ฟังก์ชันที่ถูกจํากัด
API บางรายการของ OpenThread จะแก้ไขการตั้งค่าที่ควรแก้ไขเพื่อวัตถุประสงค์ในการสาธิตหรือการทดสอบเท่านั้น ไม่ควรใช้ API เหล่านี้ในการติดตั้งใช้งานจริงของแอปพลิเคชันที่ใช้ OpenThread
เช่น ฟังก์ชัน otThreadSetRouterSelectionJitter
จะปรับเวลา (เป็นวินาที) ที่อุปกรณ์ปลายทางใช้ในการโปรโมตตัวเองเป็นเราเตอร์ ค่าเริ่มต้นสำหรับค่านี้คือ 120 ตามข้อกำหนดเฉพาะของเทรด เราจะเปลี่ยนเป็น 20 เพื่อให้ใช้งานใน Codelab นี้ได้ง่ายขึ้น คุณจึงไม่ต้องรอนานมากเพื่อให้โหนดชุดข้อความเปลี่ยนบทบาท
หมายเหตุ: อุปกรณ์ MTD จะไม่กลายเป็นเราเตอร์ และการรองรับฟังก์ชันอย่าง otThreadSetRouterSelectionJitter
จะไม่รวมอยู่ในบิลด์ MTD หลังจากนั้นเราต้องระบุตัวเลือก CMake -DOT_MTD=OFF
ไม่เช่นนั้นการบิลด์จะล้มเหลว
คุณสามารถยืนยันได้โดยดูที่คำจำกัดความของฟังก์ชัน otThreadSetRouterSelectionJitter
ซึ่งอยู่ในคำสั่งของโปรแกรมประมวลผลล่วงหน้าของ OPENTHREAD_FTD
./openthread/src/core/api/thread_ftd_api.cpp
#if OPENTHREAD_FTD #include <openthread/thread_ftd.h> ... void otThreadSetRouterSelectionJitter(otInstance *aInstance, uint8_t aRouterJitter) { Instance &instance = *static_cast<Instance *>(aInstance); instance.GetThreadNetif().GetMle().SetRouterSelectionJitter(aRouterJitter); } ... #endif // OPENTHREAD_FTD
10. การอัปเดต CMake
ก่อนที่จะสร้างแอปพลิเคชัน คุณต้องทำการอัปเดตเล็กน้อยกับไฟล์ CMake 3 ไฟล์ ระบบบิลด์จะใช้ไฟล์เหล่านี้เพื่อคอมไพล์และลิงก์แอปพลิเคชัน
./third_party/NordicSemiconductor/CMakeLists.txt
ตอนนี้ให้เพิ่ม Flag บางอย่างลงใน 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
ลงในไฟล์ NordicSemiconductor CMakeLists.txt
เพื่อให้รวมอยู่ในบิลด์ไลบรารีของไดรเวอร์ Nordic
การดำเนินการ: เพิ่มไดรเวอร์ gpio ลงในไฟล์ NordicSemiconductor CMakeLists.txt
เปิด ./third_party/NordicSemiconductor/CMakeLists.txt
ในเครื่องมือแก้ไขข้อความที่ต้องการ แล้วเพิ่มไฟล์ลงในส่วน COMMON_SOURCES
... set(COMMON_SOURCES ... nrfx/drivers/src/nrfx_gpiote.c ... ) ...
11. ตั้งค่าอุปกรณ์
เมื่ออัปเดตโค้ดทั้งหมดแล้ว คุณก็พร้อมที่จะสร้างและแฟลชแอปพลิเคชันไปยังบอร์ดพัฒนา Nordic nRF52840 ทั้ง 3 บอร์ด อุปกรณ์แต่ละเครื่องจะทํางานเป็นอุปกรณ์เทรดแบบสมบูรณ์ (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 แล้วแปลงเป็นรูปแบบฐาน 16 ด้วย 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 แล้วแฟลชไฟล์ OpenThread CLI FTD ฐาน 16 ลงในบอร์ด 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 ที่สว่างบนแต่ละบอร์ดแสดงบทบาทปัจจุบันของโหนดเทรด
- LED1 = ผู้นำ
- LED2 = เราเตอร์
- LED3 = อุปกรณ์ปลายทาง
เมื่อบทบาทเปลี่ยนแปลง LED ที่สว่างก็จะเปลี่ยนแปลงด้วย คุณควรเห็นการเปลี่ยนแปลงเหล่านี้บนบอร์ด 1-2 บอร์ดภายใน 20 วินาทีหลังจากที่อุปกรณ์แต่ละเครื่องเปิดเครื่อง
UDP มัลติแคสต์
เมื่อกดปุ่ม 1 บนแผง ระบบจะส่งข้อความ UDP ไปยังที่อยู่มัลติแคสต์ภายใน Mesh ซึ่งรวมถึงโหนดอื่นๆ ทั้งหมดในเครือข่ายเทรด เมื่อได้รับข้อความนี้ LED4 ในแผงอื่นๆ ทั้งหมดจะเปิดหรือปิด LED4 จะยังคงเปิดหรือปิดสำหรับแต่ละแผงจนกว่าจะได้รับการตอบกลับ UDP อีกครั้ง
13. สาธิต: ดูการเปลี่ยนแปลงบทบาทของอุปกรณ์
อุปกรณ์ที่คุณแฟลชเป็นอุปกรณ์เทรดแบบสมบูรณ์ (FTD) ประเภทหนึ่งที่เรียกว่าอุปกรณ์ปลายทางที่มีสิทธิ์ใช้เราเตอร์ (REED) ซึ่งหมายความว่าอุปกรณ์เหล่านี้สามารถทําหน้าที่เป็นเราเตอร์หรืออุปกรณ์ปลายทาง และสามารถโปรโมตตัวเองจากอุปกรณ์ปลายทางเป็นเราเตอร์ได้
เทรดรองรับเราเตอร์ได้สูงสุด 32 ตัว แต่พยายามรักษาจำนวนเราเตอร์ให้อยู่ระหว่าง 16 ถึง 23 ตัว หาก REED เชื่อมต่อเป็นอุปกรณ์ปลายทางและจำนวนเราเตอร์น้อยกว่า 16 ตัว REED จะเปลี่ยนเป็นเราเตอร์โดยอัตโนมัติ การเปลี่ยนแปลงนี้ควรเกิดขึ้นแบบสุ่มภายในจำนวนวินาทีที่คุณตั้งค่า otThreadSetRouterSelectionJitter
ในแอปพลิเคชัน (20 วินาที)
เครือข่ายเทรดทุกเครือข่ายยังมีผู้นำ ซึ่งเป็นเราเตอร์ที่มีหน้าที่จัดการชุดเราเตอร์ในเครือข่ายเทรด เมื่อเปิดอุปกรณ์ทั้งหมดแล้ว หลังจากผ่านไป 20 วินาที อุปกรณ์ 1 เครื่องควรเป็นผู้นำ (LED1 เปิดอยู่) และอีก 2 เครื่องควรเป็นเราเตอร์ (LED2 เปิดอยู่)
นำผู้นำออก
หากนำหัวหน้าออกจากเครือข่ายเทรด เราเตอร์ตัวอื่นจะเลื่อนตัวเองเป็นหัวหน้า เพื่อให้เครือข่ายยังคงมีหัวหน้า
ปิดตารางอันดับ (ที่ไฟ LED1 ติดสว่าง) โดยใช้สวิตช์เปิด/ปิด รอประมาณ 20 วินาที ในแผงที่เหลือ 1 ใน 2 แผง ไฟ LED2 (เราเตอร์) จะปิดและไฟ LED1 (ผู้นำ) จะเปิด อุปกรณ์นี้จึงกลายเป็นอุปกรณ์ผู้นำของเครือข่ายเทรด
เปิดใช้ตารางอันดับเดิมอีกครั้ง อุปกรณ์ควรเข้าร่วมเครือข่ายเทรดอีกครั้งโดยอัตโนมัติในฐานะอุปกรณ์ปลายทาง (LED3 ติดสว่าง) ภายใน 20 วินาที (ความผันผวนของการเลือกเราเตอร์) อุปกรณ์จะเปลี่ยนเป็นเราเตอร์ (LED2 ติดสว่าง)
รีเซ็ตบอร์ด
ปิดบอร์ดทั้ง 3 บอร์ด แล้วเปิดขึ้นมาใหม่อีกครั้ง แล้วสังเกตดูไฟ LED บอร์ดตัวแรกที่เปิดเครื่องควรเริ่มต้นในบทบาทผู้นำ (LED1 ติดสว่าง) โดยเราเตอร์ตัวแรกในเครือข่ายเทรดจะกลายเป็นผู้นำโดยอัตโนมัติ
แผงอีก 2 แผงจะเชื่อมต่อกับเครือข่ายในฐานะอุปกรณ์ปลายทาง (LED3 ติดสว่าง) ในตอนแรก แต่ควรเปลี่ยนเป็นเราเตอร์ (LED2 ติดสว่าง) ภายใน 20 วินาที
พาร์ติชันเครือข่าย
หากบอร์ดได้รับพลังงานไม่เพียงพอหรือการเชื่อมต่อวิทยุระหว่างบอร์ดอ่อน เครือข่ายเทรดอาจแยกออกเป็นหลายส่วน และคุณอาจมีอุปกรณ์มากกว่า 1 เครื่องที่แสดงเป็นผู้นำ
ด้ายจะซ่อมแซมตัวเองได้ ดังนั้นในที่สุด พาร์ติชันจะผสานกลับเป็นพาร์ติชันเดียวที่มีผู้นำเพียงรายเดียว
14. สาธิต: ส่ง UDP มัลติแคสต์
หากทำต่อจากแบบฝึกหัดก่อนหน้า LED4 ไม่ควรสว่างในอุปกรณ์ใดๆ
เลือกบอร์ดใดก็ได้แล้วกดปุ่ม 1 LED4 บนแผงอื่นๆ ทั้งหมดในเครือข่ายเทรดที่ใช้งานแอปพลิเคชันควรสลับสถานะ หากทําตามการฝึกก่อนหน้านี้ต่อ การตั้งค่าควรเปิดอยู่
กดปุ่ม 1 สำหรับบอร์ดเดียวกันอีกครั้ง LED4 ในแผงอื่นๆ ทั้งหมดควรกะพริบอีกครั้ง
กดปุ่ม 1 ในบอร์ดอื่น แล้วสังเกตว่า LED4 บนบอร์ดอื่นๆ เปิด/ปิดอย่างไร กดปุ่ม 1 บนแผงใดแผงหนึ่งที่มี LED4 เปิดอยู่ LED4 ของบอร์ดนั้นยังคงติดอยู่ แต่สลับเปิดบอร์ดอื่นๆ
พาร์ติชันเครือข่าย
หากมีการแบ่งพาร์ติชันของบอร์ดและมีหัวหน้ามากกว่า 1 คน ผลลัพธ์ของข้อความมัลติแคสต์จะแตกต่างกันไปในแต่ละบอร์ด หากคุณกดปุ่ม 1 บนแผงที่มีการแบ่งพาร์ติชัน (และจึงเป็นสมาชิกเพียงคนเดียวของเครือข่ายเทรดที่แบ่งพาร์ติชัน) LED4 ในแผงอื่นๆ จะไม่สว่างขึ้นเพื่อตอบสนอง หากเกิดกรณีนี้ขึ้น ให้รีเซ็ตแผง โดยปกติแล้วแผงจะสร้างเครือข่ายเทรดแบบเดียวขึ้นมาใหม่และการรับส่งข้อความ UDP ควรทำงานได้อย่างถูกต้อง
15. ยินดีด้วย
คุณสร้างแอปพลิเคชันที่ใช้ OpenThread API แล้ว
ตอนนี้คุณทราบข้อมูลต่อไปนี้แล้ว
- วิธีตั้งโปรแกรมปุ่มและ LED ในบอร์ดพัฒนา Nordic nRF52840
- วิธีใช้ OpenThread API ทั่วไปและคลาส
otInstance
- วิธีตรวจสอบและตอบสนองต่อการเปลี่ยนแปลงสถานะ OpenThread
- วิธีส่งข้อความ UDP ไปยังอุปกรณ์ทั้งหมดในเครือข่ายเทรด
- วิธีแก้ไข Makefile
ขั้นตอนถัดไป
ลองทำแบบฝึกหัดต่อไปนี้ต่อจาก Codelab นี้
- แก้ไขโมดูล GPIO ให้ใช้ขา GPIO แทน LED บนบอร์ดและเชื่อมต่อ LED RGB ภายนอกที่เปลี่ยนสีตามบทบาทของเราเตอร์
- เพิ่มการรองรับ GPIO สำหรับแพลตฟอร์มตัวอย่างอื่น
- ใช้ Router/Leader API เพื่อค้นหาและ ping อุปกรณ์แต่ละเครื่องแทนการใช้มัลติแคสต์เพื่อ ping อุปกรณ์ทั้งหมดจากการกดปุ่ม
- เชื่อมต่อเครือข่าย Mesh กับอินเทอร์เน็ตโดยใช้ OpenThread Border Router และมัลติแคสต์จากภายนอกเครือข่ายเทรดเพื่อเปิดไฟ LED
อ่านเพิ่มเติม
โปรดไปที่ openthread.io และ GitHub เพื่อดูแหล่งข้อมูล OpenThread ที่หลากหลาย ซึ่งรวมถึง
- แพลตฟอร์มที่รองรับ — ดูแพลตฟอร์มทั้งหมดที่รองรับ OpenThread
- สร้าง OpenThread - รายละเอียดเพิ่มเติมเกี่ยวกับการสร้างและการกําหนดค่า OpenThread
- ข้อมูลเบื้องต้นเกี่ยวกับ Thread - ข้อมูลอ้างอิงที่ดีเกี่ยวกับแนวคิดของ Thread
ข้อมูลอ้างอิง: