1- {
2- "targets" : {
3- "esp32" : false ,
4- "esp32c2" : false ,
5- "esp32c3" : false ,
6- "esp32s2" : false ,
7- "esp32s3" : false
8- }
9- }
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+ #define OT_MCAST_ADDR "ff05::abcd"
8+ #define OT_COAP_RESOURCE_NAME "Lamp"
9+
10+ const char *otSetupChild[] = {
11+ // clear/disable all
12+ " coap" , " stop" , " thread" , " stop" , " ifconfig" , " down" , " dataset" , " clear" ,
13+ // set dataset
14+ " dataset channel" , OT_CHANNEL, "dataset networkkey", OT_NETWORK_KEY, "dataset", "commit active",
15+ // network start
16+ " ifconfig" , " up" , " thread" , " start"
17+ };
18+
19+ const char *otCoapSwitch[] = {
20+ // start and create a CoAP resource
21+ " coap" ,
22+ " start" ,
23+ };
24+
25+ bool otDeviceSetup(
26+ const char **otSetupCmds, uint8_t nCmds1, const char **otCoapCmds, uint8_t nCmds2, ot_device_role_t expectedRole1, ot_device_role_t expectedRole2
27+ ) {
28+ Serial.println("Starting OpenThread.");
29+ Serial.println("Running as Switch - use the BOOT button to toggle the other C6/H2 as a Lamp");
30+ uint8_t i;
31+ for (i = 0; i < nCmds1; i++) {
32+ if (!otExecCommand(otSetupCmds[i * 2], otSetupCmds[i * 2 + 1])) {
33+ break;
34+ }
35+ }
36+ if (i != nCmds1) {
37+ log_e("Sorry, OpenThread Network setup failed!");
38+ neopixelWrite(RGB_BUILTIN, 255, 0, 0); // RED ... failed!
39+ return false;
40+ }
41+ Serial.println("OpenThread started.\r\nWaiting for activating correct Device Role.");
42+ // wait for the expected Device Role to start
43+ uint8_t tries = 24; // 24 x 2.5 sec = 1 min
44+ while (tries && otGetDeviceRole() != expectedRole1 && otGetDeviceRole() != expectedRole2) {
45+ Serial.print(".");
46+ delay(2500);
47+ tries--;
48+ }
49+ Serial.println();
50+ if (!tries) {
51+ log_e("Sorry, Device Role failed by timeout! Current Role: %s.", otGetStringDeviceRole());
52+ neopixelWrite(RGB_BUILTIN, 255, 0, 0); // RED ... failed!
53+ return false;
54+ }
55+ Serial.printf("Device is %s.\r\n", otGetStringDeviceRole());
56+ for (i = 0; i < nCmds2; i++) {
57+ if (!otExecCommand(otCoapCmds[i * 2], otCoapCmds[i * 2 + 1])) {
58+ break;
59+ }
60+ }
61+ if (i != nCmds2) {
62+ log_e("Sorry, OpenThread CoAP setup failed!");
63+ neopixelWrite(RGB_BUILTIN, 255, 0, 0); // RED ... failed!
64+ return false;
65+ }
66+ Serial.println("OpenThread setup done. Node is ready.");
67+ // all fine! LED goes and stays Blue
68+ neopixelWrite(RGB_BUILTIN, 0, 0, 64); // BLUE ... Swtich is ready!
69+ return true;
70+ }
71+
72+ void setupNode() {
73+ // tries to set the Thread Network node and only returns when succeded
74+ bool startedCorrectly = false;
75+ while (!startedCorrectly) {
76+ startedCorrectly |= otDeviceSetup(
77+ otSetupChild, sizeof(otSetupChild) / sizeof(char *) / 2, otCoapSwitch, sizeof(otCoapSwitch) / sizeof(char *) / 2, OT_ROLE_CHILD, OT_ROLE_ROUTER
78+ );
79+ if (!startedCorrectly) {
80+ Serial.println("Setup Failed...\r\nTrying again...");
81+ }
82+ }
83+ }
84+
85+ // Sends the CoAP frame to the Lamp node
86+ bool otCoapPUT(bool lampState) {
87+ bool gotDone = false, gotConfirmation = false;
88+ String coapMsg = "coap put ";
89+ coapMsg += OT_MCAST_ADDR;
90+ coapMsg += " ";
91+ coapMsg += OT_COAP_RESOURCE_NAME;
92+ coapMsg += " con 0";
93+
94+ // final command is "coap put ff05::abcd Lamp con 1" or "coap put ff05::abcd Lamp con 0"
95+ if (lampState) {
96+ coapMsg[coapMsg.length() - 1] = '1';
97+ }
98+ OThreadCLI.println(coapMsg.c_str());
99+ log_d("Send CLI CMD:[%s]", coapMsg.c_str());
100+
101+ char cliResp[256];
102+ // waits for the CoAP confirmation and Done message for about 1.25 seconds
103+ // timeout is based on Stream::setTimeout()
104+ // Example of the expected confirmation response: "coap response from fdae:3289:1783:5c3f:fd84:c714:7e83:6122"
105+ uint8_t tries = 5;
106+ *cliResp = '\0';
107+ while (tries && !(gotDone && gotConfirmation)) {
108+ size_t len = OThreadCLI.readBytesUntil('\n', cliResp, sizeof(cliResp));
109+ cliResp[len - 1] = '\0';
110+ log_d("Try[%d]::MSG[%s]", tries, cliResp);
111+ if (strlen(cliResp)) {
112+ if (!strncmp(cliResp, "coap response from", 18)) {
113+ gotConfirmation = true;
114+ }
115+ if (!strncmp(cliResp, "Done", 4)) {
116+ gotDone = true;
117+ }
118+ }
119+ tries--;
120+ }
121+ if (gotDone && gotConfirmation) {
122+ return true;
123+ }
124+ return false;
125+ }
126+
127+ // this fucntion is used by the Switch mode to check the BOOT Button and send the user action to the Lamp node
128+ void checkUserButton() {
129+ static long unsigned int lastPress = 0;
130+ const long unsigned int debounceTime = 500;
131+ static bool lastLampState = true; // first button press will turn the Lamp OFF from inital Green
132+
133+ pinMode(USER_BUTTON, INPUT_PULLUP); // C6/H2 User Button
134+ if (millis() > lastPress + debounceTime && digitalRead(USER_BUTTON) == LOW) {
135+ lastLampState = !lastLampState;
136+ if (!otCoapPUT(lastLampState)) { // failed: Lamp Node is not responding due to be off or unreachable
137+ // timeout from the CoAP PUT message... restart the node.
138+ neopixelWrite(RGB_BUILTIN, 255, 0, 0); // RED ... something failed!
139+ Serial.println("Reseting the Node as Switch... wait.");
140+ // start over...
141+ setupNode();
142+ }
143+ lastPress = millis();
144+ }
145+ }
146+
147+ void setup() {
148+ Serial.begin(115200);
149+ // LED starts RED, indicating not connected to Thread network.
150+ neopixelWrite(RGB_BUILTIN, 64, 0, 0);
151+ OThreadCLI.begin(false); // No AutoStart is necessary
152+ OThreadCLI.setTimeout(250); // waits 250ms for the OpenThread CLI response
153+ setupNode();
154+ // LED goes and keeps Blue when all is ready and Red when failed.
155+ }
156+
157+ void loop() {
158+ checkUserButton();
159+ delay(10);
160+ }
0 commit comments