Skip to content

Commit 9e26803

Browse files
committed
feat(OThread): Add Library
1 parent 99750cd commit 9e26803

File tree

19 files changed

+1109
-1
lines changed

19 files changed

+1109
-1
lines changed

CMakeLists.txt

+6-1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ set(ARDUINO_ALL_LIBRARIES
9595
LittleFS
9696
NetBIOS
9797
Network
98+
OpenThread
9899
PPP
99100
Preferences
100101
RainMaker
@@ -158,7 +159,11 @@ set(ARDUINO_LIBRARY_LittleFS_SRCS libraries/LittleFS/src/LittleFS.cpp)
158159

159160
set(ARDUINO_LIBRARY_NetBIOS_SRCS libraries/NetBIOS/src/NetBIOS.cpp)
160161

161-
set(ARDUINO_LIBRARY_PPP_SRCS
162+
set(ARDUINO_LIBRARY_OpenThread_SRCS
163+
libraries/OpenThread/src/OThreadCLI.cpp
164+
libraries/OpenThread/src/OThreadCLI_Util.c)
165+
166+
set(ARDUINO_LIBRARY_PPP_SRCS
162167
libraries/PPP/src/PPP.cpp
163168
libraries/PPP/src/ppp.c)
164169

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"targets": {
3+
"esp32": false
4+
"esp32c2": false
5+
"esp32c3": false
6+
"esp32s2": false
7+
"esp32s3": false
8+
}
9+
}
10+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#include "OThreadCLI.h"
2+
#include "OThreadCLI_Util.h"
3+
4+
#define OT_CHANNEL "24"
5+
#define OT_NETWORK_KEY "00112233445566778899aabbccddeeff"
6+
7+
const char *otSetupLeader[] = {
8+
// clear/disable all
9+
"coap", "stop",
10+
"thread", "stop",
11+
"ifconfig", "down",
12+
"dataset", "clear",
13+
// set dataset
14+
"dataset", "init new",
15+
"dataset channel", OT_CHANNEL,
16+
"dataset networkkey", OT_NETWORK_KEY,
17+
"dataset", "commit active",
18+
// network start
19+
"ifconfig", "up",
20+
"thread", "start"
21+
};
22+
23+
const char *otCoapLamp[] = {
24+
// create a multicast IPv6 Address for this device
25+
"ipmaddr add", "ff05::abcd",
26+
// start and create a CoAP resource
27+
"coap", "start",
28+
"coap resource", "Lamp",
29+
"coap set", "0"
30+
};
31+
32+
bool otDeviceSetup(const char **otSetupCmds, uint8_t nCmds1, const char **otCoapCmds, uint8_t nCmds2, ot_device_role_t expectedRole) {
33+
Serial.println("Starting OpenThread.");
34+
Serial.println("Running as Lamp (RGB LED) - use the other C6/H2 as a Switch");
35+
uint8_t i;
36+
for (i = 0; i < nCmds1; i++) {
37+
if (!otExecCommand(otSetupCmds[i * 2], otSetupCmds[i * 2 + 1])) {
38+
break;
39+
}
40+
}
41+
if (i != nCmds1) {
42+
log_e("Sorry, OpenThread Network setup failed!");
43+
neopixelWrite(RGB_BUILTIN, 255, 0, 0); // RED ... failed!
44+
return false;
45+
}
46+
Serial.println("OpenThread started.\r\nWaiting for activating correct Device Role.");
47+
// wait for the expected Device Role to start
48+
uint8_t tries = 24; // 24 x 2.5 sec = 1 min
49+
while (tries && getOtDeviceRole() != expectedRole) {
50+
Serial.print(".");
51+
delay(2500);
52+
tries--;
53+
}
54+
Serial.println();
55+
if (!tries) {
56+
log_e("Sorry, Device Role failed by timeout! Current Role: %s.", getStringOtDeviceRole());
57+
neopixelWrite(RGB_BUILTIN, 255, 0, 0); // RED ... failed!
58+
return false;
59+
}
60+
Serial.printf("Device is %s.\r\n", getStringOtDeviceRole());
61+
for (i = 0; i < nCmds2; i++) {
62+
if (!otExecCommand(otCoapCmds[i * 2], otCoapCmds[i * 2 + 1])) {
63+
break;
64+
}
65+
}
66+
if (i != nCmds2) {
67+
log_e("Sorry, OpenThread CoAP setup failed!");
68+
neopixelWrite(RGB_BUILTIN, 255, 0, 0); // RED ... failed!
69+
return false;
70+
}
71+
Serial.println("OpenThread setup done. Node is ready.");
72+
// all fine! LED goes Green
73+
neopixelWrite(RGB_BUILTIN, 0, 64, 8); // GREEN ... Lamp is ready!
74+
return true;
75+
}
76+
77+
void setupNode() {
78+
// tries to set the Thread Network node and only returns when succeded
79+
bool startedCorrectly = false;
80+
while (!startedCorrectly) {
81+
startedCorrectly |= otDeviceSetup(otSetupLeader, sizeof(otSetupLeader) / sizeof(char *) / 2,
82+
otCoapLamp, sizeof(otCoapLamp) / sizeof(char *) / 2,
83+
OT_ROLE_LEADER);
84+
if (!startedCorrectly) {
85+
Serial.println("Setup Failed...\r\nTrying again...");
86+
}
87+
}
88+
89+
}
90+
91+
// this function is used by the Lamp mode to listen for CoAP frames from the Switch Node
92+
void otCOAPListen() {
93+
// waits for the client to send a CoAP request
94+
char cliResp[256];
95+
size_t len = OThreadCLI.readBytesUntil('\n', cliResp, sizeof(cliResp));
96+
cliResp[len] = '\0';
97+
if (strlen(cliResp)) {
98+
String sResp(cliResp);
99+
log_d("Msg[%s]", cliResp);
100+
if (sResp.startsWith("coap request from") && sResp.indexOf("PUT") > 0) {
101+
uint16_t payloadIdx = sResp.indexOf("payload: ") + 10; // 0x30 | 0x31
102+
char payload = sResp.charAt(payloadIdx);
103+
log_i("CoAP PUT [%s]\r\n", payload == '0' ? "OFF" : "ON");
104+
if (payload == '0') {
105+
for (int16_t c = 248; c > 16; c -= 8) {
106+
neopixelWrite(RGB_BUILTIN, c, c, c); // ramp down
107+
delay(5);
108+
}
109+
neopixelWrite(RGB_BUILTIN, 0, 0, 0); // Lamp Off
110+
} else {
111+
for (int16_t c = 16; c < 248; c += 8) {
112+
neopixelWrite(RGB_BUILTIN, c, c, c); // ramp up
113+
delay(5);
114+
}
115+
neopixelWrite(RGB_BUILTIN, 255, 255, 255); // Lamp On
116+
}
117+
}
118+
}
119+
}
120+
121+
void setup() {
122+
Serial.begin(115200);
123+
// LED starts RED, indicating not connected to Thread network.
124+
neopixelWrite(RGB_BUILTIN, 64, 0, 0);
125+
OThreadCLI.begin(false); // No AutoStart is necessary
126+
OThreadCLI.setTimeout(250); // waits 250ms for the OpenThread CLI response
127+
setupNode();
128+
}
129+
130+
void loop() {
131+
otCOAPListen();
132+
delay(10);
133+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"targets": {
3+
"esp32": false
4+
"esp32c2": false
5+
"esp32c3": false
6+
"esp32s2": false
7+
"esp32s3": false
8+
}
9+
}
10+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
#include "OThreadCLI.h"
2+
#include "OThreadCLI_Util.h"
3+
4+
#define USER_BUTTON 9 // C6/H2 Boot button
5+
#define OT_CHANNEL "24"
6+
#define OT_NETWORK_KEY "00112233445566778899aabbccddeeff"
7+
8+
const char *otSetupChild[] = {
9+
// clear/disable all
10+
"coap", "stop",
11+
"thread", "stop",
12+
"ifconfig", "down",
13+
"dataset", "clear",
14+
// set dataset
15+
"dataset channel", OT_CHANNEL,
16+
"dataset networkkey", OT_NETWORK_KEY,
17+
"dataset", "commit active",
18+
// network start
19+
"ifconfig", "up",
20+
"thread", "start"
21+
};
22+
23+
const char *otCoapSwitch[] = {
24+
// start and create a CoAP resource
25+
"coap", "start",
26+
};
27+
28+
bool otDeviceSetup(const char **otSetupCmds, uint8_t nCmds1, const char **otCoapCmds, uint8_t nCmds2, ot_device_role_t expectedRole1, ot_device_role_t expectedRole2) {
29+
Serial.println("Starting OpenThread.");
30+
Serial.println("Running as Switch - use the BOOT button to toggle the other C6/H2 as a Lamp");
31+
uint8_t i;
32+
for (i = 0; i < nCmds1; i++) {
33+
if (!otExecCommand(otSetupCmds[i * 2], otSetupCmds[i * 2 + 1])) {
34+
break;
35+
}
36+
}
37+
if (i != nCmds1) {
38+
log_e("Sorry, OpenThread Network setup failed!");
39+
neopixelWrite(RGB_BUILTIN, 255, 0, 0); // RED ... failed!
40+
return false;
41+
}
42+
Serial.println("OpenThread started.\r\nWaiting for activating correct Device Role.");
43+
// wait for the expected Device Role to start
44+
uint8_t tries = 24; // 24 x 2.5 sec = 1 min
45+
while (tries && getOtDeviceRole() != expectedRole1 && getOtDeviceRole() != expectedRole2) {
46+
Serial.print(".");
47+
delay(2500);
48+
tries--;
49+
}
50+
Serial.println();
51+
if (!tries) {
52+
log_e("Sorry, Device Role failed by timeout! Current Role: %s.", getStringOtDeviceRole());
53+
neopixelWrite(RGB_BUILTIN, 255, 0, 0); // RED ... failed!
54+
return false;
55+
}
56+
Serial.printf("Device is %s.\r\n", getStringOtDeviceRole());
57+
for (i = 0; i < nCmds2; i++) {
58+
if (!otExecCommand(otCoapCmds[i * 2], otCoapCmds[i * 2 + 1])) {
59+
break;
60+
}
61+
}
62+
if (i != nCmds2) {
63+
log_e("Sorry, OpenThread CoAP setup failed!");
64+
neopixelWrite(RGB_BUILTIN, 255, 0, 0); // RED ... failed!
65+
return false;
66+
}
67+
Serial.println("OpenThread setup done. Node is ready.");
68+
// all fine! LED goes Blue
69+
neopixelWrite(RGB_BUILTIN, 0, 0, 64); // BLUE ... Swtich is ready!
70+
return true;
71+
}
72+
73+
74+
void setupNode() {
75+
// tries to set the Thread Network node and only returns when succeded
76+
bool startedCorrectly = false;
77+
while (!startedCorrectly) {
78+
startedCorrectly |= otDeviceSetup(otSetupChild, sizeof(otSetupChild) / sizeof(char *) / 2,
79+
otCoapSwitch, sizeof(otCoapSwitch) / sizeof(char *) / 2,
80+
OT_ROLE_CHILD, OT_ROLE_ROUTER);
81+
if (!startedCorrectly) {
82+
Serial.println("Setup Failed...\r\nTrying again...");
83+
}
84+
}
85+
86+
}
87+
88+
// Sends the CoAP frame to the Lamp node
89+
bool otCoapPUT(bool lampState) {
90+
bool gotDone = false, gotConfirmation = false;
91+
char coapMsg[] = "coap put ff05::abcd Lamp con 0";
92+
93+
if (lampState) {
94+
coapMsg[strlen(coapMsg) - 1] = '1';
95+
}
96+
OThreadCLI.println(coapMsg);
97+
log_d("Send CLI CMD:[%s]", coapMsg);
98+
99+
char cliResp[256];
100+
// waits for the CoAP confirmation and Done message for about 5 seconds
101+
// timeout is based on Stream::setTimeout()
102+
uint8_t tries = 5;
103+
*cliResp = '\0';
104+
while (tries && !(gotDone && gotConfirmation)) {
105+
size_t len = OThreadCLI.readBytesUntil('\n', cliResp, sizeof(cliResp));
106+
cliResp[len] = '\0';
107+
log_d("Try[%d]::MSG[%s]", tries, cliResp);
108+
if (strlen(cliResp)) {
109+
log_d("%s", cliResp);
110+
if (!strncmp(cliResp, "coap response from", 18)) {
111+
gotConfirmation = true;
112+
}
113+
if (!strncmp(cliResp, "Done", 4)) {
114+
gotDone = true;
115+
}
116+
}
117+
tries--;
118+
}
119+
if (gotDone && gotConfirmation) {
120+
return true;
121+
}
122+
return false;
123+
}
124+
125+
// this fucntion is used by the Switch mode to check the BOOT Button and send the user action to the Lamp node
126+
void checkUserButton() {
127+
static long unsigned int lastPress = 0;
128+
const long unsigned int debounceTime = 500;
129+
static bool lastLampState = false;
130+
131+
pinMode(USER_BUTTON, INPUT_PULLUP); // C6/H2 User Button
132+
if (millis() > lastPress + debounceTime && digitalRead(USER_BUTTON) == LOW) {
133+
if (otCoapPUT(!lastLampState)) {
134+
lastLampState = !lastLampState;
135+
} else {
136+
// timeout from the CoAP PUT message... restart the node.
137+
neopixelWrite(RGB_BUILTIN, 255, 0, 0); // RED ... something failed!
138+
Serial.println("Reseting the Node as Switch... wait.");
139+
// start over...
140+
setupNode();
141+
}
142+
lastPress = millis();
143+
}
144+
}
145+
146+
void setup() {
147+
Serial.begin(115200);
148+
// LED starts RED, indicating not connected to Thread network.
149+
neopixelWrite(RGB_BUILTIN, 64, 0, 0);
150+
OThreadCLI.begin(false); // No AutoStart is necessary
151+
OThreadCLI.setTimeout(250); // waits 250ms for the OpenThread CLI response
152+
setupNode();
153+
}
154+
155+
void loop() {
156+
checkUserButton();
157+
delay(10);
158+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* OpenThread.begin(false) will not start a node in a Thread Network
3+
* You will need to start it manually using the OpenThread CLI commands
4+
* Use the Serial Monitor to interact with the OpenThread CLI
5+
*
6+
* Type 'help' for a list of commands.
7+
* Documentation: https://openthread.io/reference/cli/commands
8+
*
9+
*/
10+
11+
#include "OThreadCLI.h"
12+
13+
void setup() {
14+
Serial.begin(115200);
15+
OThreadCLI.begin(false); // No AutoStart - fresh start
16+
Serial.println("OpenThread CLI started - type 'help' for a list of commands.");
17+
OThreadCLI.startOpenThreadConsole(Serial);
18+
}
19+
20+
void loop() {
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"targets": {
3+
"esp32": false
4+
"esp32c2": false
5+
"esp32c3": false
6+
"esp32s2": false
7+
"esp32s3": false
8+
}
9+
}
10+

0 commit comments

Comments
 (0)