pdf-icon

Arduino Quick Start

2. Devices & Examples

6. Applications

NanoH2 Thread Arduino

NanoH2 OpenThread Arduino–related example programs.

Preparation

Compilation Requirements

  • M5Stack Board Manager version >= 3.2.5
  • Development board option = M5NanoH2

Basic Configuration Steps

  1. Open Arduino IDE
  2. Click Examples: File -> Examples -> OpenThread
  3. Select the correct development board: Tools -> Board: M5NanoH2
  4. Enable USB CDC on boot: Tools -> USB CDC On Boot: Enabled
  5. Select flash size: Tools -> Flash Size: 4MB
  6. Select partition scheme: Tools -> Partition Scheme: Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS)
  7. Select the correct serial port: Tools -> Port
  8. Upload the firmware to the device
  9. The device will automatically start and try to join the network
  10. Check the network status via the Serial Monitor

Example Programs

1. Simple Node

This example demonstrates how to create a basic Thread node.

  • Automatically starts and joins a Thread network. If no existing Thread network is found, it starts as a Leader node.
  • Uses the default network configuration:
    • Network Name: OpenThread-ESP
    • Network Prefix: fd00:db8:a0:0::/64
    • Network Channel: 15
    • PAN ID: 0x1234
    • Extended PAN ID: dead00beef00cafe
    • Network Key: 00112233445566778899aabbccddeeff
  • Displays the node status every 5 seconds
cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
#include "OThreadCLI.h"
#include "OThreadCLI_Util.h"

// The first device to start Thread will be the Leader
// Next devices will be Router or Child

void setup() {
  Serial.begin(115200);
  OThread.begin();  // AutoStart using Thread default settings
  OThreadCLI.begin();
  OThread.otPrintNetworkInformation(Serial);  // Print Current Thread Network Information
}

void loop() {
  Serial.print("Thread Node State: ");
  Serial.println(OThread.otGetStringDeviceRole());
  delay(5000);
}

2. Thread Network (CLI)

This example demonstrates how to build a complete Thread network with two different types of nodes. The Leader Node sends a "Hello, M5Stack!" message to the Router Node.

2.1 Leader Node

  • Creates and manages the Thread network as the first device
  • Provides a complete network dataset
  • Displays detailed network information:
    • Network Name
    • Channel
    • PAN ID
    • Extended PAN ID
    • Network Key
    • IP Addresses
    • Multicast Addresses
    • Transmission prompts
cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
#include "OThreadCLI.h"
#include "OThreadCLI_Util.h"

#define CLI_NETWORK_KEY    "dataset networkkey 00112233445566778899aabbccddeeff"
#define CLI_NETWORK_CHANEL "dataset channel 24"

otInstance *aInstance = NULL;

bool udpInitialized = false;

void setup() {
  Serial.begin(115200);
  OThread.begin(false);
  OThreadCLI.begin();
  Serial.println();
  Serial.println("Setting up OpenThread Node as Leader");
  aInstance = esp_openthread_get_instance();

  OThreadCLI.println("dataset init new");
  OThreadCLI.println(CLI_NETWORK_KEY);
  OThreadCLI.println(CLI_NETWORK_CHANEL);
  OThreadCLI.println("dataset commit active");
  OThreadCLI.println("ifconfig up");
  OThreadCLI.println("thread start");
}

void loop() {
  while (OThreadCLI.available()) {
    Serial.write(OThreadCLI.read());
  }

  Serial.println("=============================================");
  Serial.print("Thread Node State: ");
  Serial.println(OThread.otGetStringDeviceRole());

  if (OThread.otGetDeviceRole() == OT_ROLE_LEADER) {
    const char *networkName = otThreadGetNetworkName(aInstance);
    Serial.printf("Network Name: %s\r\n", networkName);
    uint8_t channel = otLinkGetChannel(aInstance);
    Serial.printf("Channel: %d\r\n", channel);
    uint16_t panId = otLinkGetPanId(aInstance);
    Serial.printf("PanID: 0x%04x\r\n", panId);
    const otExtendedPanId *extPanId = otThreadGetExtendedPanId(aInstance);
    Serial.printf("Extended PAN ID: ");
    for (int i = 0; i < OT_EXT_PAN_ID_SIZE; i++) {
      Serial.printf("%02x", extPanId->m8[i]);
    }
    Serial.println();
    otNetworkKey networkKey;
    otThreadGetNetworkKey(aInstance, &networkKey);
    Serial.printf("Network Key: ");
    for (int i = 0; i < OT_NETWORK_KEY_SIZE; i++) {
      Serial.printf("%02x", networkKey.m8[i]);
    }
    Serial.println();
    char buf[OT_IP6_ADDRESS_STRING_SIZE];
    const otNetifAddress *address = otIp6GetUnicastAddresses(aInstance);
    while (address != NULL) {
      otIp6AddressToString(&address->mAddress, buf, sizeof(buf));
      Serial.printf("IP Address: %s\r\n", buf);
      address = address->mNext;
    }
    const otNetifMulticastAddress *mAddress = otIp6GetMulticastAddresses(aInstance);
    while (mAddress != NULL) {
      otIp6AddressToString(&mAddress->mAddress, buf, sizeof(buf));
      printf("Multicast IP Address: %s\n", buf);
      mAddress = mAddress->mNext;
    }

    if (!udpInitialized) {
      Serial.println("\nInitializing UDP sender...");
      OThreadCLI.println("udp open");
      delay(100);
      udpInitialized = true;
      Serial.println("UDP initialized");
    }

    Serial.println("\nSending UDP message...");
    OThreadCLI.println("udp send ff03::1 12345 \"Hello,M5Stack\"");
  }

  delay(5000);
}

2.2 Router Node

  • Joins an existing Thread network and forwards network data
  • Displays connection status and network information
  • Prints received messages to the serial monitor
cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
#include "OThreadCLI.h"
#include "OThreadCLI_Util.h"

#define CLI_NETWORK_KEY    "dataset networkkey 00112233445566778899aabbccddeeff"
#define CLI_NETWORK_CHANEL "dataset channel 24"

otInstance *aInstance = NULL;

bool udpInitialized = false;

void setup() {
  Serial.begin(115200);
  OThread.begin(false);
  OThreadCLI.begin();
  Serial.println();
  Serial.println("Setting up OpenThread Node as Router/Child");
  Serial.println("Make sure the Leader Node is already running");
  aInstance = esp_openthread_get_instance();

  OThreadCLI.println("dataset clear");
  OThreadCLI.println(CLI_NETWORK_KEY);
  OThreadCLI.println(CLI_NETWORK_CHANEL);
  OThreadCLI.println("dataset commit active");
  OThreadCLI.println("ifconfig up");
  OThreadCLI.println("thread start");
}

void loop() {
  while (OThreadCLI.available()) {
    Serial.write(OThreadCLI.read());
  }

  Serial.println("=============================================");
  Serial.print("Thread Node State: ");
  Serial.println(OThread.otGetStringDeviceRole());

  if (OThread.otGetDeviceRole() == OT_ROLE_CHILD || OThread.otGetDeviceRole() == OT_ROLE_ROUTER) {
    const char *networkName = otThreadGetNetworkName(aInstance);
    Serial.printf("Network Name: %s\r\n", networkName);
    uint8_t channel = otLinkGetChannel(aInstance);
    Serial.printf("Channel: %d\r\n", channel);
    uint16_t panId = otLinkGetPanId(aInstance);
    Serial.printf("PanID: 0x%04x\r\n", panId);
    const otExtendedPanId *extPanId = otThreadGetExtendedPanId(aInstance);
    Serial.printf("Extended PAN ID: ");
    for (int i = 0; i < OT_EXT_PAN_ID_SIZE; i++) {
      Serial.printf("%02x", extPanId->m8[i]);
    }
    Serial.println();
    otNetworkKey networkKey;
    otThreadGetNetworkKey(aInstance, &networkKey);
    Serial.printf("Network Key: ");
    for (int i = 0; i < OT_NETWORK_KEY_SIZE; i++) {
      Serial.printf("%02x", networkKey.m8[i]);
    }
    Serial.println();
    char buf[OT_IP6_ADDRESS_STRING_SIZE];
    const otNetifAddress *address = otIp6GetUnicastAddresses(aInstance);
    while (address != NULL) {
      otIp6AddressToString(&address->mAddress, buf, sizeof(buf));
      Serial.printf("IP Address: %s\r\n", buf);
      address = address->mNext;
    }
    const otNetifMulticastAddress *mAddress = otIp6GetMulticastAddresses(aInstance);
    while (mAddress != NULL) {
      otIp6AddressToString(&mAddress->mAddress, buf, sizeof(buf));
      printf("Multicast IP Address: %s\n", buf);
      mAddress = mAddress->mNext;
    }

    if (!udpInitialized) {
      Serial.println("\nInitializing UDP receiver...");
      OThreadCLI.println("udp open");
      delay(100);
      OThreadCLI.println("udp bind :: 12345");
      delay(100);
      udpInitialized = true;
      Serial.println("UDP listening on port 12345");
      Serial.println("Waiting for messages...\n");
    }
  }

  delay(5000);
}

3. Thread Scan

This example demonstrates how to scan nearby Thread networks.

  • Scan IEEE 802.15.4 devices
    • Display device addresses
    • Display signal strength
    • Display channel information
  • Scan Thread networks (the device must be at least in Child state)
    • Display network name
    • Display extended PAN ID
    • Display RLOC16
    • Display version information
  • Supports continuous scanning mode
cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
#include "OThreadCLI.h"
#include "OThreadCLI_Util.h"

void setup() {
  Serial.begin(115200);
  OThread.begin(true);  // For scanning, AutoStart must be active, any setup
  OThreadCLI.begin();
  OThreadCLI.setTimeout(100);  // Set a timeout for the CLI response
  Serial.println();
  Serial.println("This sketch will continuously scan the Thread Local Network and all devices IEEE 802.15.4 compatible");
}

void loop() {
  Serial.println();
  Serial.println("Scanning for nearby IEEE 802.15.4 devices:");
  if (!otPrintRespCLI("scan", Serial, 3000)) {
    Serial.println("Scan Failed...");
  }
  delay(5000);
  if (OThread.otGetDeviceRole() < OT_ROLE_CHILD) {
    Serial.println();
    Serial.println("This device has not started Thread yet, bypassing Discovery Scan");
    return;
  }
  Serial.println();
  Serial.println("Scanning MLE Discover:");
  if (!otPrintRespCLI("discover", Serial, 3000)) {
    Serial.println("Discover Failed...");
  }
  delay(5000);
}

4. Simple CLI

This example provides a complete OpenThread CLI console.

  • Provides a full CLI command interface
  • Supports all OpenThread CLI commands:
    • Network management commands
    • Device configuration commands
    • Diagnostic commands
    • Security commands
  • Real-time command responses
  • Supports command history
  • Convenient for debugging and configuration
cpp
1 2 3 4 5 6 7 8 9 10 11
#include "OThreadCLI.h"

void setup() {
  Serial.begin(115200);
  OThread.begin(false);  // No AutoStart - fresh start
  OThreadCLI.begin();
  Serial.println("OpenThread CLI started - type 'help' for a list of commands.");
  OThreadCLI.startConsole(Serial);
}

void loop() {}

5. Receive Callback (onReceive)

This example demonstrates how to handle CLI responses using callback functions.

  • Capture CLI responses
  • Custom response processing
  • Status monitoring
  • Supports asynchronous processing
cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
#include "OThreadCLI.h"

// reads all the lines sent by CLI, one by one
// ignores some lines that are just a sequence of \r\n
void otReceivedLine() {
  String line = "";
  while (OThreadCLI.available() > 0) {
    char ch = OThreadCLI.read();
    if (ch != '\r' && ch != '\n') {
      line += ch;
    }
  }
  if (line.length() > 0) {
    Serial.print("OpenThread CLI RESP===> ");
    Serial.println(line.c_str());
  }
}

void setup() {
  Serial.begin(115200);
  OThread.begin();  // AutoStart
  OThreadCLI.begin();
  OThreadCLI.onReceive(otReceivedLine);
}

void loop() {
  OThreadCLI.println("state");
  delay(1000);
}
On This Page