3.3.7
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
# OpenThread CoAP Lamp Example
|
||||
|
||||
This example demonstrates how to create a CoAP (Constrained Application Protocol) server on a Thread network that controls an RGB LED lamp.\
|
||||
The application acts as a CoAP resource server that receives PUT requests to turn the lamp on or off, demonstrating Thread-based IoT device communication.
|
||||
|
||||
## Supported Targets
|
||||
|
||||
| SoC | Thread | RGB LED | Status |
|
||||
| --- | ------ | ------- | ------ |
|
||||
| ESP32-H2 | ✅ | Required | Fully supported |
|
||||
| ESP32-C6 | ✅ | Required | Fully supported |
|
||||
| ESP32-C5 | ✅ | Required | Fully supported |
|
||||
|
||||
### Note on Thread Support:
|
||||
|
||||
- Thread support must be enabled in the ESP-IDF configuration (`CONFIG_OPENTHREAD_ENABLED`). This is done automatically when using the ESP32 Arduino OpenThread library.
|
||||
- This example requires a companion CoAP Switch device (coap_switch example) to control the lamp.
|
||||
- The lamp device acts as a Leader node and CoAP server.
|
||||
|
||||
## Features
|
||||
|
||||
- CoAP server implementation on Thread network
|
||||
- RGB LED control with smooth fade in/out transitions
|
||||
- Leader node configuration using CLI Helper Functions API
|
||||
- CoAP resource creation and management
|
||||
- Multicast IPv6 address for CoAP communication
|
||||
- Automatic network setup with retry mechanism
|
||||
- Visual status indication using RGB LED (Red = failed, Green = ready)
|
||||
|
||||
## Hardware Requirements
|
||||
|
||||
- ESP32 compatible development board with Thread support (ESP32-H2, ESP32-C6, or ESP32-C5)
|
||||
- RGB LED (built-in RGB LED or external RGB LED)
|
||||
- USB cable for Serial communication
|
||||
- A CoAP Switch device (coap_switch example) to control the lamp
|
||||
|
||||
## Software Setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. Install the Arduino IDE (2.0 or newer recommended)
|
||||
2. Install ESP32 Arduino Core with OpenThread support
|
||||
3. ESP32 Arduino libraries:
|
||||
- `OpenThread`
|
||||
|
||||
### Configuration
|
||||
|
||||
Before uploading the sketch, you can modify the network and CoAP configuration:
|
||||
|
||||
```cpp
|
||||
#define OT_CHANNEL "24"
|
||||
#define OT_NETWORK_KEY "00112233445566778899aabbccddeeff"
|
||||
#define OT_MCAST_ADDR "ff05::abcd"
|
||||
#define OT_COAP_RESOURCE_NAME "Lamp"
|
||||
```
|
||||
|
||||
**Important:**
|
||||
- The network key and channel must match the Switch device configuration
|
||||
- The multicast address and resource name must match the Switch device
|
||||
- The network key must be a 32-character hexadecimal string (16 bytes)
|
||||
- The channel must be between 11 and 26 (IEEE 802.15.4 channels)
|
||||
|
||||
## Building and Flashing
|
||||
|
||||
1. Open the `coap_lamp.ino` sketch in the Arduino IDE.
|
||||
2. Select your ESP32 board from the **Tools > Board** menu (ESP32-H2, ESP32-C6, or ESP32-C5).
|
||||
3. Connect your ESP32 board to your computer via USB.
|
||||
4. Click the **Upload** button to compile and flash the sketch.
|
||||
|
||||
## Expected Output
|
||||
|
||||
Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. You should see output similar to the following:
|
||||
|
||||
```
|
||||
Starting OpenThread.
|
||||
Running as Lamp (RGB LED) - use the other C6/H2 as a Switch
|
||||
OpenThread started.
|
||||
Waiting for activating correct Device Role.
|
||||
........
|
||||
Device is Leader.
|
||||
OpenThread setup done. Node is ready.
|
||||
```
|
||||
|
||||
The RGB LED will turn **green** when the device is ready to receive CoAP commands.
|
||||
|
||||
## Using the Device
|
||||
|
||||
### Lamp Device Setup
|
||||
|
||||
The lamp device automatically:
|
||||
1. Configures itself as a Thread Leader node
|
||||
2. Creates a CoAP server
|
||||
3. Registers a CoAP resource named "Lamp"
|
||||
4. Sets up a multicast IPv6 address for CoAP communication
|
||||
5. Waits for CoAP PUT requests from the Switch device
|
||||
|
||||
### CoAP Resource
|
||||
|
||||
The lamp exposes a CoAP resource that accepts:
|
||||
- **PUT with payload "0"**: Turns the lamp OFF (fades to black)
|
||||
- **PUT with payload "1"**: Turns the lamp ON (fades to white)
|
||||
|
||||
### Visual Status Indication
|
||||
|
||||
The RGB LED provides visual feedback:
|
||||
- **Red**: Setup failed or error occurred
|
||||
- **Green**: Device is ready and waiting for CoAP commands
|
||||
- **White/Black**: Lamp state (controlled by CoAP commands)
|
||||
|
||||
### Working with Switch Device
|
||||
|
||||
1. Start the Lamp device first (this example)
|
||||
2. Start the Switch device (coap_switch example) with matching network key and channel
|
||||
3. Press the button on the Switch device to toggle the lamp
|
||||
4. The lamp will fade in/out smoothly when toggled
|
||||
|
||||
## Code Structure
|
||||
|
||||
The coap_lamp example consists of the following main components:
|
||||
|
||||
1. **`otDeviceSetup()` function**:
|
||||
- Configures the device as a Leader node using CLI Helper Functions
|
||||
- Sets up CoAP server and resource
|
||||
- Waits for device to become Leader
|
||||
- Returns success/failure status
|
||||
|
||||
2. **`setupNode()` function**:
|
||||
- Retries setup until successful
|
||||
- Calls `otDeviceSetup()` with Leader role configuration
|
||||
|
||||
3. **`otCOAPListen()` function**:
|
||||
- Listens for CoAP requests from the Switch device
|
||||
- Parses CoAP PUT requests
|
||||
- Controls RGB LED based on payload (0 = OFF, 1 = ON)
|
||||
- Implements smooth fade transitions
|
||||
|
||||
4. **`setup()`**:
|
||||
- Initializes Serial communication
|
||||
- Starts OpenThread stack with `OpenThread.begin(false)`
|
||||
- Initializes OpenThread CLI
|
||||
- Sets CLI timeout
|
||||
- Calls `setupNode()` to configure the device
|
||||
|
||||
5. **`loop()`**:
|
||||
- Continuously calls `otCOAPListen()` to process incoming CoAP requests
|
||||
- Small delay for responsiveness
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **LED stays red**: Setup failed. Check Serial Monitor for error messages. Verify network configuration.
|
||||
- **Lamp not responding to switch**: Ensure both devices use the same network key, channel, multicast address, and resource name. Check that Switch device is running.
|
||||
- **Device not becoming Leader**: Clear NVS or ensure this is the first device started. Check network configuration.
|
||||
- **CoAP requests not received**: Verify multicast address matches between Lamp and Switch devices. Check Thread network connectivity.
|
||||
- **No serial output**: Check baudrate (115200) and USB connection
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [OpenThread CLI Helper Functions API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_cli.html)
|
||||
- [OpenThread Core API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_core.html)
|
||||
- [OpenThread Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread.html)
|
||||
- [CoAP Protocol](https://coap.technology/)
|
||||
|
||||
## License
|
||||
|
||||
This example is licensed under the Apache License, Version 2.0.
|
||||
@@ -0,0 +1,3 @@
|
||||
requires:
|
||||
- CONFIG_OPENTHREAD_ENABLED=y
|
||||
- CONFIG_SOC_IEEE802154_SUPPORTED=y
|
||||
@@ -0,0 +1,164 @@
|
||||
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#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!");
|
||||
rgbLedWrite(RGB_BUILTIN, 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 && OThread.otGetDeviceRole() != expectedRole) {
|
||||
Serial.print(".");
|
||||
delay(2500);
|
||||
tries--;
|
||||
}
|
||||
Serial.println();
|
||||
if (!tries) {
|
||||
log_e("Sorry, Device Role failed by timeout! Current Role: %s.", OThread.otGetStringDeviceRole());
|
||||
rgbLedWrite(RGB_BUILTIN, 255, 0, 0); // RED ... failed!
|
||||
return false;
|
||||
}
|
||||
Serial.printf("Device is %s.\r\n", OThread.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!");
|
||||
rgbLedWrite(RGB_BUILTIN, 255, 0, 0); // RED ... failed!
|
||||
return false;
|
||||
}
|
||||
Serial.println("OpenThread setup done. Node is ready.");
|
||||
// all fine! LED goes Green
|
||||
rgbLedWrite(RGB_BUILTIN, 0, 64, 8); // GREEN ... Lamp is ready!
|
||||
return true;
|
||||
}
|
||||
|
||||
void setupNode() {
|
||||
// tries to set the Thread Network node and only returns when succeeded
|
||||
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) {
|
||||
rgbLedWrite(RGB_BUILTIN, c, c, c); // ramp down
|
||||
delay(5);
|
||||
}
|
||||
rgbLedWrite(RGB_BUILTIN, 0, 0, 0); // Lamp Off
|
||||
} else {
|
||||
for (int16_t c = 16; c < 248; c += 8) {
|
||||
rgbLedWrite(RGB_BUILTIN, c, c, c); // ramp up
|
||||
delay(5);
|
||||
}
|
||||
rgbLedWrite(RGB_BUILTIN, 255, 255, 255); // Lamp On
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
// LED starts RED, indicating not connected to Thread network.
|
||||
rgbLedWrite(RGB_BUILTIN, 64, 0, 0);
|
||||
OThread.begin(false); // No AutoStart is necessary
|
||||
OThreadCLI.begin();
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
# OpenThread CoAP Switch Example
|
||||
|
||||
This example demonstrates how to create a CoAP (Constrained Application Protocol) client on a Thread network that controls a remote CoAP server (lamp).\
|
||||
The application acts as a CoAP client that sends PUT requests to a lamp device, demonstrating Thread-based IoT device control.
|
||||
|
||||
## Supported Targets
|
||||
|
||||
| SoC | Thread | Button | Status |
|
||||
| --- | ------ | ------ | ------ |
|
||||
| ESP32-H2 | ✅ | Required | Fully supported |
|
||||
| ESP32-C6 | ✅ | Required | Fully supported |
|
||||
| ESP32-C5 | ✅ | Required | Fully supported |
|
||||
|
||||
### Note on Thread Support:
|
||||
|
||||
- Thread support must be enabled in the ESP-IDF configuration (`CONFIG_OPENTHREAD_ENABLED`). This is done automatically when using the ESP32 Arduino OpenThread library.
|
||||
- This example requires a companion CoAP Lamp device (coap_lamp example) to control.
|
||||
- The switch device joins the network as a Router or Child node and acts as a CoAP client.
|
||||
|
||||
## Features
|
||||
|
||||
- CoAP client implementation on Thread network
|
||||
- Button-based control of remote lamp device
|
||||
- Router/Child node configuration using CLI Helper Functions API
|
||||
- CoAP PUT request sending with confirmation
|
||||
- Automatic network join with retry mechanism
|
||||
- Visual status indication using RGB LED (Red = failed, Blue = ready)
|
||||
- Button debouncing for reliable input
|
||||
|
||||
## Hardware Requirements
|
||||
|
||||
- ESP32 compatible development board with Thread support (ESP32-H2, ESP32-C6, or ESP32-C5)
|
||||
- User button (BOOT button or external button)
|
||||
- RGB LED for status indication (optional, but recommended)
|
||||
- USB cable for Serial communication
|
||||
- A CoAP Lamp device (coap_lamp example) must be running first
|
||||
|
||||
## Software Setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. Install the Arduino IDE (2.0 or newer recommended)
|
||||
2. Install ESP32 Arduino Core with OpenThread support
|
||||
3. ESP32 Arduino libraries:
|
||||
- `OpenThread`
|
||||
|
||||
### Configuration
|
||||
|
||||
Before uploading the sketch, you can modify the network and CoAP configuration:
|
||||
|
||||
```cpp
|
||||
#define USER_BUTTON 9 // C6/H2 Boot button (change if needed)
|
||||
#define OT_CHANNEL "24"
|
||||
#define OT_NETWORK_KEY "00112233445566778899aabbccddeeff"
|
||||
#define OT_MCAST_ADDR "ff05::abcd"
|
||||
#define OT_COAP_RESOURCE_NAME "Lamp"
|
||||
```
|
||||
|
||||
**Important:**
|
||||
- The network key and channel **must match** the Lamp device configuration
|
||||
- The multicast address and resource name **must match** the Lamp device
|
||||
- The network key must be a 32-character hexadecimal string (16 bytes)
|
||||
- The channel must be between 11 and 26 (IEEE 802.15.4 channels)
|
||||
- **Start the Lamp device first** before starting this Switch device
|
||||
|
||||
## Building and Flashing
|
||||
|
||||
1. **First, start the Lamp device** using the coap_lamp example
|
||||
2. Open the `coap_switch.ino` sketch in the Arduino IDE.
|
||||
3. Select your ESP32 board from the **Tools > Board** menu (ESP32-H2, ESP32-C6, or ESP32-C5).
|
||||
4. Connect your ESP32 board to your computer via USB.
|
||||
5. Click the **Upload** button to compile and flash the sketch.
|
||||
|
||||
## Expected Output
|
||||
|
||||
Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. You should see output similar to the following:
|
||||
|
||||
```
|
||||
Starting OpenThread.
|
||||
Running as Switch - use the BOOT button to toggle the other C6/H2 as a Lamp
|
||||
OpenThread started.
|
||||
Waiting for activating correct Device Role.
|
||||
........
|
||||
Device is Router.
|
||||
OpenThread setup done. Node is ready.
|
||||
```
|
||||
|
||||
The RGB LED will turn **blue** when the device is ready to send CoAP commands.
|
||||
|
||||
## Using the Device
|
||||
|
||||
### Switch Device Setup
|
||||
|
||||
The switch device automatically:
|
||||
1. Joins the existing Thread network (created by the Lamp Leader)
|
||||
2. Configures itself as a Router or Child node
|
||||
3. Creates a CoAP client
|
||||
4. Waits for button presses to send CoAP commands
|
||||
|
||||
### Button Control
|
||||
|
||||
- **Press the button**: Toggles the lamp state (ON/OFF)
|
||||
- The switch sends CoAP PUT requests to the lamp:
|
||||
- Payload "1" = Turn lamp ON
|
||||
- Payload "0" = Turn lamp OFF
|
||||
|
||||
### Visual Status Indication
|
||||
|
||||
The RGB LED provides visual feedback:
|
||||
- **Red**: Setup failed or CoAP request failed
|
||||
- **Blue**: Device is ready and can send CoAP commands
|
||||
- **Red (after button press)**: CoAP request failed, device will restart setup
|
||||
|
||||
### Working with Lamp Device
|
||||
|
||||
1. Start the Lamp device first (coap_lamp example)
|
||||
2. Start this Switch device with matching network key and channel
|
||||
3. Wait for Switch device to join the network (LED turns blue)
|
||||
4. Press the button on the Switch device
|
||||
5. The lamp on the other device should toggle ON/OFF
|
||||
|
||||
## Code Structure
|
||||
|
||||
The coap_switch example consists of the following main components:
|
||||
|
||||
1. **`otDeviceSetup()` function**:
|
||||
- Configures the device to join an existing network using CLI Helper Functions
|
||||
- Sets up CoAP client
|
||||
- Waits for device to become Router or Child
|
||||
- Returns success/failure status
|
||||
|
||||
2. **`setupNode()` function**:
|
||||
- Retries setup until successful
|
||||
- Calls `otDeviceSetup()` with Router/Child role configuration
|
||||
|
||||
3. **`otCoapPUT()` function**:
|
||||
- Sends CoAP PUT request to the lamp device
|
||||
- Waits for CoAP confirmation response
|
||||
- Returns success/failure status
|
||||
- Uses CLI Helper Functions to send commands and read responses
|
||||
|
||||
4. **`checkUserButton()` function**:
|
||||
- Monitors button state with debouncing
|
||||
- Toggles lamp state on button press
|
||||
- Calls `otCoapPUT()` to send commands
|
||||
- Restarts setup if CoAP request fails
|
||||
|
||||
5. **`setup()`**:
|
||||
- Initializes Serial communication
|
||||
- Starts OpenThread stack with `OpenThread.begin(false)`
|
||||
- Initializes OpenThread CLI
|
||||
- Sets CLI timeout
|
||||
- Calls `setupNode()` to configure the device
|
||||
|
||||
6. **`loop()`**:
|
||||
- Continuously calls `checkUserButton()` to monitor button input
|
||||
- Small delay for responsiveness
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **LED stays red**: Setup failed. Check Serial Monitor for error messages. Verify network configuration matches Lamp device.
|
||||
- **Button press doesn't toggle lamp**: Ensure Lamp device is running and both devices are on the same Thread network. Check that network key, channel, multicast address, and resource name match.
|
||||
- **Device not joining network**: Ensure Lamp device (Leader) is running first. Verify network key and channel match exactly.
|
||||
- **CoAP request timeout**: Check Thread network connectivity. Verify multicast address and resource name match the Lamp device. Ensure Lamp device is responding.
|
||||
- **No serial output**: Check baudrate (115200) and USB connection
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [OpenThread CLI Helper Functions API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_cli.html)
|
||||
- [OpenThread Core API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_core.html)
|
||||
- [OpenThread Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread.html)
|
||||
- [CoAP Protocol](https://coap.technology/)
|
||||
|
||||
## License
|
||||
|
||||
This example is licensed under the Apache License, Version 2.0.
|
||||
@@ -0,0 +1,3 @@
|
||||
requires:
|
||||
- CONFIG_OPENTHREAD_ENABLED=y
|
||||
- CONFIG_SOC_IEEE802154_SUPPORTED=y
|
||||
@@ -0,0 +1,189 @@
|
||||
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#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!");
|
||||
rgbLedWrite(RGB_BUILTIN, 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 && OThread.otGetDeviceRole() != expectedRole1 && OThread.otGetDeviceRole() != expectedRole2) {
|
||||
Serial.print(".");
|
||||
delay(2500);
|
||||
tries--;
|
||||
}
|
||||
Serial.println();
|
||||
if (!tries) {
|
||||
log_e("Sorry, Device Role failed by timeout! Current Role: %s.", OThread.otGetStringDeviceRole());
|
||||
rgbLedWrite(RGB_BUILTIN, 255, 0, 0); // RED ... failed!
|
||||
return false;
|
||||
}
|
||||
Serial.printf("Device is %s.\r\n", OThread.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!");
|
||||
rgbLedWrite(RGB_BUILTIN, 255, 0, 0); // RED ... failed!
|
||||
return false;
|
||||
}
|
||||
Serial.println("OpenThread setup done. Node is ready.");
|
||||
// all fine! LED goes and stays Blue
|
||||
rgbLedWrite(RGB_BUILTIN, 0, 0, 64); // BLUE ... Switch is ready!
|
||||
return true;
|
||||
}
|
||||
|
||||
void setupNode() {
|
||||
// tries to set the Thread Network node and only returns when succeeded
|
||||
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 function 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 initial 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.
|
||||
rgbLedWrite(RGB_BUILTIN, 255, 0, 0); // RED ... something failed!
|
||||
Serial.println("Resetting the Node as Switch... wait.");
|
||||
// start over...
|
||||
setupNode();
|
||||
}
|
||||
lastPress = millis();
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
// LED starts RED, indicating not connected to Thread network.
|
||||
rgbLedWrite(RGB_BUILTIN, 64, 0, 0);
|
||||
OThread.begin(false); // No AutoStart is necessary
|
||||
OThreadCLI.begin();
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user