Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 156 additions & 0 deletions examples/Advanced/OpenThread_coap_lamp.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
#include <M5NanoC6.h>
#include "OThreadCLI.h"
#include "OThreadCLI_Util.h"

#define OT_CHANNEL "24"
#define OT_NETWORK_KEY "00112233445566778899aabbccddeeff"
#define OT_MCAST_ADDR "ff05::abcd"
#define OT_COAP_RESOURCE_NAME "Lamp"

const char *otSetupLeader[] = {
// -- clear/disable all
// stop CoAP
"coap", "stop",
// stop Thread
"thread", "stop",
// stop the interface
"ifconfig", "down",
// clear the dataset
"dataset", "clear",
// -- set dataset
// create a new complete dataset with random data
"dataset", "init new",
// set the channel
"dataset channel", OT_CHANNEL,
// set the network key
"dataset networkkey", OT_NETWORK_KEY,
// commit the dataset
"dataset", "commit active",
// -- network start
// start the interface
"ifconfig", "up",
// start the Thread network
"thread", "start"
};

const char *otCoapLamp[] = {
// -- create a multicast IPv6 Address for this device
"ipmaddr add", OT_MCAST_ADDR,
// -- start and create a CoAP resource
// start CoAP as server
"coap", "start",
// create a CoAP resource
"coap resource", OT_COAP_RESOURCE_NAME,
// set the CoAP resource initial value
"coap set", "0"
};

bool otDeviceSetup(const char **otSetupCmds, uint8_t nCmds1, const char **otCoapCmds, uint8_t nCmds2, ot_device_role_t expectedRole) {
Serial.println("Starting OpenThread.");
Serial.println("Running as Lamp (RGB LED) - use the other C6/H2 as a Switch");
uint8_t i;
for (i = 0; i < nCmds1; i++) {
if (!otExecCommand(otSetupCmds[i * 2], otSetupCmds[i * 2 + 1])) {
break;
}
}
if (i != nCmds1) {
log_e("Sorry, OpenThread Network setup failed!");
//neopixelWrite(M5NANO_C6_RGB_LED_DATA_PIN, 255, 0, 0); // RED ... failed!
neopixelWrite(M5NANO_C6_RGB_LED_DATA_PIN, 255, 0, 0); // RED ... failed!
return false;
}
Serial.println("OpenThread started.\r\nWaiting for activating correct Device Role.");
// wait for the expected Device Role to start
uint8_t tries = 24; // 24 x 2.5 sec = 1 min
while (tries && otGetDeviceRole() != expectedRole) {
Serial.print(".");
delay(2500);
tries--;
}
Serial.println();
if (!tries) {
log_e("Sorry, Device Role failed by timeout! Current Role: %s.", otGetStringDeviceRole());
neopixelWrite(M5NANO_C6_RGB_LED_DATA_PIN, 255, 0, 0); // RED ... failed!
return false;
}
Serial.printf("Device is %s.\r\n", otGetStringDeviceRole());
for (i = 0; i < nCmds2; i++) {
if (!otExecCommand(otCoapCmds[i * 2], otCoapCmds[i * 2 + 1])) {
break;
}
}
if (i != nCmds2) {
log_e("Sorry, OpenThread CoAP setup failed!");
neopixelWrite(M5NANO_C6_RGB_LED_DATA_PIN, 255, 0, 0); // RED ... failed!
return false;
}
Serial.println("OpenThread setup done. Node is ready.");
// all fine! LED goes Green
neopixelWrite(M5NANO_C6_RGB_LED_DATA_PIN, 0, 64, 8); // GREEN ... Lamp is ready!
return true;
}

void setupNode() {
// tries to set the Thread Network node and only returns when succeded
bool startedCorrectly = false;
while (!startedCorrectly) {
startedCorrectly |=
otDeviceSetup(otSetupLeader, sizeof(otSetupLeader) / sizeof(char *) / 2, otCoapLamp, sizeof(otCoapLamp) / sizeof(char *) / 2, OT_ROLE_LEADER);
if (!startedCorrectly) {
Serial.println("Setup Failed...\r\nTrying again...");
}
}
}

// this function is used by the Lamp mode to listen for CoAP frames from the Switch Node
void otCOAPListen() {
// waits for the client to send a CoAP request
char cliResp[256] = {0};
size_t len = OThreadCLI.readBytesUntil('\n', cliResp, sizeof(cliResp));
cliResp[len - 1] = '\0';
if (strlen(cliResp)) {
String sResp(cliResp);
// cliResp shall be something like:
// "coap request from fd0c:94df:f1ae:b39a:ec47:ec6d:15e8:804a PUT with payload: 30"
// payload may be 30 or 31 (HEX) '0' or '1' (ASCII)
log_d("Msg[%s]", cliResp);
if (sResp.startsWith("coap request from") && sResp.indexOf("PUT") > 0) {
char payload = sResp.charAt(sResp.length() - 1); // last character in the payload
log_i("CoAP PUT [%s]\r\n", payload == '0' ? "OFF" : "ON");
if (payload == '0') {
for (int16_t c = 248; c > 16; c -= 8) {
neopixelWrite(M5NANO_C6_RGB_LED_DATA_PIN, c, c, c); // ramp down
delay(5);
}
neopixelWrite(M5NANO_C6_RGB_LED_DATA_PIN, 0, 0, 0); // Lamp Off
} else {
for (int16_t c = 16; c < 248; c += 8) {
neopixelWrite(M5NANO_C6_RGB_LED_DATA_PIN, c, c, c); // ramp up
delay(5);
}
neopixelWrite(M5NANO_C6_RGB_LED_DATA_PIN, 255, 255, 255); // Lamp On
}
}
}
}

void setup() {
Serial.begin(115200);

// Enable RGB LED Power
pinMode(M5NANO_C6_RGB_LED_PWR_PIN, OUTPUT);
digitalWrite(M5NANO_C6_RGB_LED_PWR_PIN, HIGH);

// LED starts RED, indicating not connected to Thread network.
neopixelWrite(M5NANO_C6_RGB_LED_DATA_PIN, 64, 0, 0);
OThreadCLI.begin(false); // No AutoStart is necessary
OThreadCLI.setTimeout(250); // waits 250ms for the OpenThread CLI response
setupNode();
// LED goes Green when all is ready and Red when failed.
}

void loop() {
otCOAPListen();
delay(10);
}
180 changes: 180 additions & 0 deletions examples/Advanced/OpenThread_coap_switch.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
#include <M5NanoC6.h>
#include "OThreadCLI.h"
#include "OThreadCLI_Util.h"

#define USER_BUTTON 9 // C6/H2 Boot button
#define OT_CHANNEL "24"
#define OT_NETWORK_KEY "00112233445566778899aabbccddeeff"
#define OT_MCAST_ADDR "ff05::abcd"
#define OT_COAP_RESOURCE_NAME "Lamp"

const char *otSetupChild[] = {
// -- clear/disable all
// stop CoAP
"coap", "stop",
// stop Thread
"thread", "stop",
// stop the interface
"ifconfig", "down",
// clear the dataset
"dataset", "clear",
// -- set dataset
// set the channel
"dataset channel", OT_CHANNEL,
// set the network key
"dataset networkkey", OT_NETWORK_KEY,
// commit the dataset
"dataset", "commit active",
// -- network start
// start the interface
"ifconfig", "up",
// start the Thread network
"thread", "start"
};

const char *otCoapSwitch[] = {
// -- start CoAP as client
"coap", "start"
};

bool otDeviceSetup(
const char **otSetupCmds, uint8_t nCmds1, const char **otCoapCmds, uint8_t nCmds2, ot_device_role_t expectedRole1, ot_device_role_t expectedRole2
) {
Serial.println("Starting OpenThread.");
Serial.println("Running as Switch - use the BOOT button to toggle the other C6/H2 as a Lamp");
uint8_t i;
for (i = 0; i < nCmds1; i++) {
if (!otExecCommand(otSetupCmds[i * 2], otSetupCmds[i * 2 + 1])) {
break;
}
}
if (i != nCmds1) {
log_e("Sorry, OpenThread Network setup failed!");
neopixelWrite(M5NANO_C6_RGB_LED_DATA_PIN, 255, 0, 0); // RED ... failed!
return false;
}
Serial.println("OpenThread started.\r\nWaiting for activating correct Device Role.");
// wait for the expected Device Role to start
uint8_t tries = 24; // 24 x 2.5 sec = 1 min
while (tries && otGetDeviceRole() != expectedRole1 && otGetDeviceRole() != expectedRole2) {
Serial.print(".");
delay(2500);
tries--;
}
Serial.println();
if (!tries) {
log_e("Sorry, Device Role failed by timeout! Current Role: %s.", otGetStringDeviceRole());
neopixelWrite(M5NANO_C6_RGB_LED_DATA_PIN, 255, 0, 0); // RED ... failed!
return false;
}
Serial.printf("Device is %s.\r\n", otGetStringDeviceRole());
for (i = 0; i < nCmds2; i++) {
if (!otExecCommand(otCoapCmds[i * 2], otCoapCmds[i * 2 + 1])) {
break;
}
}
if (i != nCmds2) {
log_e("Sorry, OpenThread CoAP setup failed!");
neopixelWrite(M5NANO_C6_RGB_LED_DATA_PIN, 255, 0, 0); // RED ... failed!
return false;
}
Serial.println("OpenThread setup done. Node is ready.");
// all fine! LED goes and stays Blue
neopixelWrite(M5NANO_C6_RGB_LED_DATA_PIN, 0, 0, 64); // BLUE ... Swtich is ready!
return true;
}

void setupNode() {
// tries to set the Thread Network node and only returns when succeded
bool startedCorrectly = false;
while (!startedCorrectly) {
startedCorrectly |= otDeviceSetup(
otSetupChild, sizeof(otSetupChild) / sizeof(char *) / 2, otCoapSwitch, sizeof(otCoapSwitch) / sizeof(char *) / 2, OT_ROLE_CHILD, OT_ROLE_ROUTER
);
if (!startedCorrectly) {
Serial.println("Setup Failed...\r\nTrying again...");
}
}
}

// Sends the CoAP frame to the Lamp node
bool otCoapPUT(bool lampState) {
bool gotDone = false, gotConfirmation = false;
String coapMsg = "coap put ";
coapMsg += OT_MCAST_ADDR;
coapMsg += " ";
coapMsg += OT_COAP_RESOURCE_NAME;
coapMsg += " con 0";

// final command is "coap put ff05::abcd Lamp con 1" or "coap put ff05::abcd Lamp con 0"
if (lampState) {
coapMsg[coapMsg.length() - 1] = '1';
}
OThreadCLI.println(coapMsg.c_str());
log_d("Send CLI CMD:[%s]", coapMsg.c_str());

char cliResp[256];
// waits for the CoAP confirmation and Done message for about 1.25 seconds
// timeout is based on Stream::setTimeout()
// Example of the expected confirmation response: "coap response from fdae:3289:1783:5c3f:fd84:c714:7e83:6122"
uint8_t tries = 5;
*cliResp = '\0';
while (tries && !(gotDone && gotConfirmation)) {
size_t len = OThreadCLI.readBytesUntil('\n', cliResp, sizeof(cliResp));
cliResp[len - 1] = '\0';
log_d("Try[%d]::MSG[%s]", tries, cliResp);
if (strlen(cliResp)) {
if (!strncmp(cliResp, "coap response from", 18)) {
gotConfirmation = true;
}
if (!strncmp(cliResp, "Done", 4)) {
gotDone = true;
}
}
tries--;
}
if (gotDone && gotConfirmation) {
return true;
}
return false;
}

// this fucntion is used by the Switch mode to check the BOOT Button and send the user action to the Lamp node
void checkUserButton() {
static long unsigned int lastPress = 0;
const long unsigned int debounceTime = 500;
static bool lastLampState = true; // first button press will turn the Lamp OFF from inital Green

pinMode(USER_BUTTON, INPUT_PULLUP); // C6/H2 User Button
if (millis() > lastPress + debounceTime && digitalRead(USER_BUTTON) == LOW) {
lastLampState = !lastLampState;
if (!otCoapPUT(lastLampState)) { // failed: Lamp Node is not responding due to be off or unreachable
// timeout from the CoAP PUT message... restart the node.
neopixelWrite(M5NANO_C6_RGB_LED_DATA_PIN, 255, 0, 0); // RED ... something failed!
Serial.println("Reseting the Node as Switch... wait.");
// start over...
setupNode();
}
lastPress = millis();
}
}

void setup() {
Serial.begin(115200);

// Enable RGB LED Power
pinMode(M5NANO_C6_RGB_LED_PWR_PIN, OUTPUT);
digitalWrite(M5NANO_C6_RGB_LED_PWR_PIN, HIGH);

// LED starts RED, indicating not connected to Thread network.
neopixelWrite(M5NANO_C6_RGB_LED_DATA_PIN, 64, 0, 0);
OThreadCLI.begin(false); // No AutoStart is necessary
OThreadCLI.setTimeout(250); // waits 250ms for the OpenThread CLI response
setupNode();
// LED goes and keeps Blue when all is ready and Red when failed.
}

void loop() {
checkUserButton();
delay(10);
}