Skip to content

Commit d891ddf

Browse files
SuGliderlucasssvazpre-commit-ci-lite[bot]
authored
New OpenThread CLI Arduino Library for ESP32-C6 and ESP32-H2 (#9908)
* feat(OThread): Add Library * fix(OpenThread): fixes file list in CMakeLists.txt * fix(openthread): Fixes JSON CI Files * fix(openthread): Fixes JSON CI Files * fix(openthread): Include Openthread guarding * fix(openthread): COAP parametrization * fix(openthread): Include Openthread guarding * fix(openthread): Improves commentaries and code * fix(openthread): Improves code * fix(openthread): Includes StreamString.h * feat(openthread): New Scan Example * feat(openthread): Improved Scan Example * feat(openthread): README.md Initial documentation for ESP3 Arduino OpenThread CLI API. * feat(openthread): helper functions documentation Create helper_functions.md for ESP32 Arduino OpenThread API * fix(openthread): begin end * feat(openthread): onReceice example * fix(openthread): tx queue error * fix(doc): fixing documentation apresentation Fixes the documentation first paragraph in order to make it easier fore reading. It also displays in the very top which SoC are supported by the library. * fix(doc): documentation format * feat(openthread): commentary * fix(openthread): Typo, start/stop console * fix(openthread): library properties * ci(pre-commit): Apply automatic fixes * feat(openthread): formatting text * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: Lucas Saavedra Vaz <[email protected]> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
1 parent 9e55ccd commit d891ddf

25 files changed

+1503
-0
lines changed

CMakeLists.txt

+5
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,6 +159,10 @@ set(ARDUINO_LIBRARY_LittleFS_SRCS libraries/LittleFS/src/LittleFS.cpp)
158159

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

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

libraries/OpenThread/README.md

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
| Supported Targets | ESP32-C6 | ESP32-H2 |
2+
| ----------------- | -------- | -------- |
3+
4+
# ESP32 Arduino OpenThreadCLI
5+
6+
The `OpenThreadCLI` class is an Arduino API for interacting with the OpenThread Command Line Interface (CLI). It allows you to manage and configure the Thread stack using a command-line interface.
7+
8+
There is one main class called `OpenThreadCLI` and a global object used to operate OpenThread CLI, called `OThreadCLI`.\
9+
Some [helper functions](helper_functions.md) were made available for working with the OpenThread CLI environment.
10+
11+
The available OpenThread Commands are documented in the [OpenThread CLI Reference Page](https://openthread.io/reference/cli/commands)
12+
13+
It is important to note that the current implementation can only be used with Espressif SoC that has support to IEEE 802.15.4, such as **ESP32-C6** and **ESP32-H2**.
14+
15+
Below are the details of the class:
16+
17+
## Class Definition
18+
19+
```cpp
20+
class OpenThreadCLI : public Stream {
21+
private:
22+
static size_t setBuffer(xQueueHandle &queue, size_t len);
23+
bool otStarted = false;
24+
25+
public:
26+
OpenThreadCLI();
27+
~OpenThreadCLI();
28+
operator bool() const;
29+
30+
// Starts a task to read/write otStream. Default prompt is "ot> ". Set it to NULL to make it invisible.
31+
void startConsole(Stream& otStream, bool echoback = true, const char* prompt = "ot> ");
32+
void stopConsole();
33+
void setPrompt(char* prompt); // Changes the console prompt. NULL is an empty prompt.
34+
void setEchoBack(bool echoback); // Changes the console echoback option
35+
void setStream(Stream& otStream); // Changes the console Stream object
36+
void onReceive(OnReceiveCb_t func); // Called on a complete line of output from OT CLI, as OT Response
37+
38+
void begin(bool OThreadAutoStart = true);
39+
void end();
40+
41+
// Default size is 256 bytes
42+
size_t setTxBufferSize(size_t tx_queue_len);
43+
// Default size is 1024 bytes
44+
size_t setRxBufferSize(size_t rx_queue_len);
45+
46+
size_t write(uint8_t);
47+
int available();
48+
int read();
49+
int peek();
50+
void flush();
51+
};
52+
53+
extern OpenThreadCLI OThreadCLI;
54+
```
55+
56+
## Class Overview
57+
- The `OpenThreadCLI` class inherits from the `Stream` class, making it compatible with Arduino's standard I/O functions.
58+
- It provides methods for managing the OpenThread CLI, including starting and stopping the console, setting prompts, and handling received data.
59+
- You can customize the console behavior by adjusting parameters such as echoback and buffer sizes.
60+
61+
## Public Methods
62+
- `startConsole(Stream& otStream, bool echoback = true, const char* prompt = "ot> ")`: Starts the OpenThread console with the specified stream, echoback option, and prompt.
63+
- `stopConsole()`: Stops the OpenThread console.
64+
- `setPrompt(char* prompt)`: Changes the console prompt (set to NULL for an empty prompt).
65+
- `setEchoBack(bool echoback)`: Changes the console echoback option.
66+
- `setStream(Stream& otStream)`: Changes the console Stream object.
67+
- `onReceive(OnReceiveCb_t func)`: Sets a callback function to handle complete lines of output from the OT CLI.
68+
- `begin(bool OThreadAutoStart = true)`: Initializes the OpenThread stack (optional auto-start).
69+
- `end()`: Deinitializes the OpenThread stack.
70+
- `setTxBufferSize(size_t tx_queue_len)`: Sets the transmit buffer size (default is 256 bytes).
71+
- `setRxBufferSize(size_t rx_queue_len)`: Sets the receive buffer size (default is 1024 bytes).
72+
- `write(uint8_t)`, `available()`, `read()`, `peek()`, `flush()`: Standard Stream methods implementation for OpenThread CLI object.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"targets": {
3+
"esp32": false,
4+
"esp32c2": false,
5+
"esp32c3": false,
6+
"esp32s2": false,
7+
"esp32s3": false
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
#include "OThreadCLI.h"
2+
#include "OThreadCLI_Util.h"
3+
4+
#define OT_CHANNEL "24"
5+
#define OT_NETWORK_KEY "00112233445566778899aabbccddeeff"
6+
#define OT_MCAST_ADDR "ff05::abcd"
7+
#define OT_COAP_RESOURCE_NAME "Lamp"
8+
9+
const char *otSetupLeader[] = {
10+
// -- clear/disable all
11+
// stop CoAP
12+
"coap", "stop",
13+
// stop Thread
14+
"thread", "stop",
15+
// stop the interface
16+
"ifconfig", "down",
17+
// clear the dataset
18+
"dataset", "clear",
19+
// -- set dataset
20+
// create a new complete dataset with random data
21+
"dataset", "init new",
22+
// set the channel
23+
"dataset channel", OT_CHANNEL,
24+
// set the network key
25+
"dataset networkkey", OT_NETWORK_KEY,
26+
// commit the dataset
27+
"dataset", "commit active",
28+
// -- network start
29+
// start the interface
30+
"ifconfig", "up",
31+
// start the Thread network
32+
"thread", "start"
33+
};
34+
35+
const char *otCoapLamp[] = {
36+
// -- create a multicast IPv6 Address for this device
37+
"ipmaddr add", OT_MCAST_ADDR,
38+
// -- start and create a CoAP resource
39+
// start CoAP as server
40+
"coap", "start",
41+
// create a CoAP resource
42+
"coap resource", OT_COAP_RESOURCE_NAME,
43+
// set the CoAP resource initial value
44+
"coap set", "0"
45+
};
46+
47+
bool otDeviceSetup(const char **otSetupCmds, uint8_t nCmds1, const char **otCoapCmds, uint8_t nCmds2, ot_device_role_t expectedRole) {
48+
Serial.println("Starting OpenThread.");
49+
Serial.println("Running as Lamp (RGB LED) - use the other C6/H2 as a Switch");
50+
uint8_t i;
51+
for (i = 0; i < nCmds1; i++) {
52+
if (!otExecCommand(otSetupCmds[i * 2], otSetupCmds[i * 2 + 1])) {
53+
break;
54+
}
55+
}
56+
if (i != nCmds1) {
57+
log_e("Sorry, OpenThread Network setup failed!");
58+
neopixelWrite(RGB_BUILTIN, 255, 0, 0); // RED ... failed!
59+
return false;
60+
}
61+
Serial.println("OpenThread started.\r\nWaiting for activating correct Device Role.");
62+
// wait for the expected Device Role to start
63+
uint8_t tries = 24; // 24 x 2.5 sec = 1 min
64+
while (tries && otGetDeviceRole() != expectedRole) {
65+
Serial.print(".");
66+
delay(2500);
67+
tries--;
68+
}
69+
Serial.println();
70+
if (!tries) {
71+
log_e("Sorry, Device Role failed by timeout! Current Role: %s.", otGetStringDeviceRole());
72+
neopixelWrite(RGB_BUILTIN, 255, 0, 0); // RED ... failed!
73+
return false;
74+
}
75+
Serial.printf("Device is %s.\r\n", otGetStringDeviceRole());
76+
for (i = 0; i < nCmds2; i++) {
77+
if (!otExecCommand(otCoapCmds[i * 2], otCoapCmds[i * 2 + 1])) {
78+
break;
79+
}
80+
}
81+
if (i != nCmds2) {
82+
log_e("Sorry, OpenThread CoAP setup failed!");
83+
neopixelWrite(RGB_BUILTIN, 255, 0, 0); // RED ... failed!
84+
return false;
85+
}
86+
Serial.println("OpenThread setup done. Node is ready.");
87+
// all fine! LED goes Green
88+
neopixelWrite(RGB_BUILTIN, 0, 64, 8); // GREEN ... Lamp is ready!
89+
return true;
90+
}
91+
92+
void setupNode() {
93+
// tries to set the Thread Network node and only returns when succeded
94+
bool startedCorrectly = false;
95+
while (!startedCorrectly) {
96+
startedCorrectly |=
97+
otDeviceSetup(otSetupLeader, sizeof(otSetupLeader) / sizeof(char *) / 2, otCoapLamp, sizeof(otCoapLamp) / sizeof(char *) / 2, OT_ROLE_LEADER);
98+
if (!startedCorrectly) {
99+
Serial.println("Setup Failed...\r\nTrying again...");
100+
}
101+
}
102+
}
103+
104+
// this function is used by the Lamp mode to listen for CoAP frames from the Switch Node
105+
void otCOAPListen() {
106+
// waits for the client to send a CoAP request
107+
char cliResp[256] = {0};
108+
size_t len = OThreadCLI.readBytesUntil('\n', cliResp, sizeof(cliResp));
109+
cliResp[len - 1] = '\0';
110+
if (strlen(cliResp)) {
111+
String sResp(cliResp);
112+
// cliResp shall be something like:
113+
// "coap request from fd0c:94df:f1ae:b39a:ec47:ec6d:15e8:804a PUT with payload: 30"
114+
// payload may be 30 or 31 (HEX) '0' or '1' (ASCII)
115+
log_d("Msg[%s]", cliResp);
116+
if (sResp.startsWith("coap request from") && sResp.indexOf("PUT") > 0) {
117+
char payload = sResp.charAt(sResp.length() - 1); // last character in the payload
118+
log_i("CoAP PUT [%s]\r\n", payload == '0' ? "OFF" : "ON");
119+
if (payload == '0') {
120+
for (int16_t c = 248; c > 16; c -= 8) {
121+
neopixelWrite(RGB_BUILTIN, c, c, c); // ramp down
122+
delay(5);
123+
}
124+
neopixelWrite(RGB_BUILTIN, 0, 0, 0); // Lamp Off
125+
} else {
126+
for (int16_t c = 16; c < 248; c += 8) {
127+
neopixelWrite(RGB_BUILTIN, c, c, c); // ramp up
128+
delay(5);
129+
}
130+
neopixelWrite(RGB_BUILTIN, 255, 255, 255); // Lamp On
131+
}
132+
}
133+
}
134+
}
135+
136+
void setup() {
137+
Serial.begin(115200);
138+
// LED starts RED, indicating not connected to Thread network.
139+
neopixelWrite(RGB_BUILTIN, 64, 0, 0);
140+
OThreadCLI.begin(false); // No AutoStart is necessary
141+
OThreadCLI.setTimeout(250); // waits 250ms for the OpenThread CLI response
142+
setupNode();
143+
// LED goes Green when all is ready and Red when failed.
144+
}
145+
146+
void loop() {
147+
otCOAPListen();
148+
delay(10);
149+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"targets": {
3+
"esp32": false,
4+
"esp32c2": false,
5+
"esp32c3": false,
6+
"esp32s2": false,
7+
"esp32s3": false
8+
}
9+
}

0 commit comments

Comments
 (0)