Closed 97Cweb closed 4 days ago
found it finally, it is coap set to set the response that a get command gets. Could this please be added to documentation/instructions somewhere?
would you show the fixed code?
#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 "json"
#define USER_BUTTON 9
String joinerAddress = "";
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 *otCoapHub[] = {
// -- 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,
// set the CoAP resource initial value
"coap set", "",
};
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 HUB");
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 && otGetDeviceRole() != expectedRole) {
Serial.print(".");
delay(2500);
tries--;
}
Serial.println();
if (!tries) {
log_e("Sorry, Device Role failed by timeout! Current Role: %s.", otGetStringDeviceRole());
rgbLedWrite(RGB_BUILTIN, 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!");
rgbLedWrite(RGB_BUILTIN, 0, 255, 0); // RED ... failed!
return false;
}
Serial.println("OpenThread setup done. Node is ready.");
// all fine! LED goes Green
rgbLedWrite(RGB_BUILTIN, 64, 0, 8); // GREEN ... HUB 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, otCoapHub, sizeof(otCoapHub) / sizeof(char *) / 2, OT_ROLE_LEADER);
if (!startedCorrectly) {
Serial.println("Setup Failed...\r\nTrying again...");
}
}
}
// Function to convert hex to ASCII
void hexToAscii(char* hexStr, char* asciiStr) {
// Convert hex string to ASCII string
int len = strlen(hexStr);
for (int i = 0; i < len; i += 2) {
// Convert each pair of hex digits to an ASCII character
char hexChar[3] = { hexStr[i], hexStr[i + 1], '\0' };
asciiStr[i / 2] = (char)strtol(hexChar, NULL, 16);
}
asciiStr[len / 2] = '\0'; // Null-terminate the result
}
// Function to extract and decode payload from CoAP response
void extractAndDecodePayload(String &sResp, char *asciiPayload) {
int payloadIndex = sResp.indexOf("with payload: ") + 14; // Adjust as needed
String hexPayload = sResp.substring(payloadIndex);
// Convert the hex payload to a char array
char cliResp[1024]; // Temporary buffer
hexPayload.toCharArray(cliResp, sizeof(cliResp));
// Decode the hex payload to ASCII
hexToAscii(cliResp, asciiPayload);
// Log the decoded payload
log_i("Decoded Payload: [%s]", asciiPayload);
}
bool sendCoapGet(String joinerAddress) {
String coapMsg = "coap get ";
coapMsg += joinerAddress;
coapMsg += " ";
coapMsg += OT_COAP_RESOURCE;
coapMsg += " con";
Serial.printf("Sending CoAP GET CMD: [%s]\n", coapMsg.c_str());
OThreadCLI.println(coapMsg.c_str());
char cliResp[1024];
char asciiPayload[1024]; // Buffer for decoded payload
bool gotDone = false, gotConfirmation = false;
uint8_t tries = 5;
while (tries && !(gotDone && gotConfirmation)) {
size_t len = OThreadCLI.readBytesUntil('\n', cliResp, sizeof(cliResp));
cliResp[len - 1] = '\0';
Serial.printf("Try[%d]::MSG[%s]\n", tries, cliResp);
if (strlen(cliResp)) {
if (!strncmp(cliResp, "coap response from", 18)) {
gotConfirmation = true;
Serial.println("CoAP response received.");
}
if (!strncmp(cliResp, "Done", 4)) {
gotDone = true;
Serial.println("Command executed successfully.");
}
// Check for JSON payload in the response
if (strstr(cliResp, "payload")) {
String sResp(cliResp);
extractAndDecodePayload(sResp, asciiPayload);
}
}
tries--;
}
return gotDone && gotConfirmation;
}
// Listen for CoAP frames from Joiner Nodes
void otCOAPListen() {
char cliResp[2048] = {0};
char asciiPayload[1024] = {0}; // Buffer for decoded payload
size_t len = OThreadCLI.readBytesUntil('\0', cliResp, sizeof(cliResp));
cliResp[len - 1] = '\0';
if (strlen(cliResp)) {
String sResp(cliResp);
Serial.println(sResp);
if (sResp.startsWith("coap request from") && sResp.indexOf("PUT") > 0) {
extractAndDecodePayload(sResp, asciiPayload);
}
}
}
// Detect button press on GPIO 9
void checkUserButton() {
static unsigned long lastPress = 0;
const unsigned long debounceTime = 500;
if (millis() - lastPress > debounceTime && digitalRead(USER_BUTTON) == LOW) {
lastPress = millis();
if (joinerAddress.length() > 0) {
sendCoapGet(joinerAddress); // Send GET request to the joiner
} else {
Serial.println("No Joiner address found! Provide address via Serial.");
}
}
}
void setup() {
Serial.begin(115200);
// LED starts RED, indicating not connected to Thread network.
rgbLedWrite(RGB_BUILTIN, 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.
pinMode(USER_BUTTON, INPUT_PULLUP);
}
void loop() {
otCOAPListen();
checkUserButton();
// Read joiner address from Serial input
if (Serial.available()) {
joinerAddress = Serial.readStringUntil('\n');
joinerAddress.trim(); // Remove extra spaces or newlines
Serial.print("Joiner address set to: ");
Serial.println(joinerAddress);
}
delay(10);
}
#include "OThreadCLI.h"
#include "OThreadCLI_Util.h"
#include <ArduinoJson.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 "json"
JsonDocument train;
String serializedJson = "";
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 *otCoapJoiner[] = {
// -- start CoAP as client
"coap", "start",
"coap resource", "json",
"coap set", serializedJson.c_str()
};
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 Joiner");
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 && 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());
rgbLedWrite(RGB_BUILTIN, 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!");
rgbLedWrite(RGB_BUILTIN, 0, 255, 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 ... Joiner 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, otCoapJoiner, sizeof(otCoapJoiner) / sizeof(char *) / 2, OT_ROLE_CHILD, OT_ROLE_ROUTER);
if (!startedCorrectly) {
Serial.println("Setup Failed...\r\nTrying again...");
}
}
}
// Sends the CoAP frame to the json node
bool otCoapPUT() {
bool gotDone = false, gotConfirmation = false;
String coapMsg = "coap put ";
coapMsg += OT_MCAST_ADDR;
coapMsg += " ";
coapMsg += OT_COAP_RESOURCE;
coapMsg += " con ";
coapMsg += serializedJson;
// final command is "coap put ff05::abcd json con {<json>}"
OThreadCLI.println(coapMsg.c_str());
log_i("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;
}
void hexToAscii(char* hexStr, char* asciiStr) {
// Convert hex string to ASCII string
int len = strlen(hexStr);
for (int i = 0; i < len; i += 2) {
// Convert each pair of hex digits to an ASCII character
char hexChar[3] = { hexStr[i], hexStr[i + 1], '\0' };
asciiStr[i / 2] = (char)strtol(hexChar, NULL, 16);
}
asciiStr[len / 2] = '\0'; // Null-terminate the result
}
// this function is used by the joiner mode to check the BOOT Button and send the user action to the joiner node
void checkUserButton() {
static long unsigned int lastPress = 0;
const long unsigned int debounceTime = 500;
if (millis() > lastPress + debounceTime && digitalRead(USER_BUTTON) == LOW) {
lastPress = millis();
serializeJson(train,serializedJson);
if (!otCoapPUT()) { // failed: leader Node is not responding due to be off or unreachable
// timeout from the CoAP PUT message... restart the node.
rgbLedWrite(RGB_BUILTIN, 0, 255, 0); // RED ... something failed!
Serial.println("Resetting the Node as joiner... wait.");
// start over...
setupNode();
}
}
}
void setup() {
train["id"] = 1;
serializeJson(train,serializedJson);
Serial.begin(115200);
// LED starts RED, indicating not connected to Thread network.
rgbLedWrite(RGB_BUILTIN, 0, 255, 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.
pinMode(USER_BUTTON, INPUT_PULLUP); // C6/H2 User Button
}
void loop() {
checkUserButton();
//otCOAPListenForGET(); // Continuously listen for GET requests
delay(10);
}
Great! As for the size of the CoAP message, which is limited to 16 bytes, please read https://github.com/espressif/arduino-esp32/issues/10350#issuecomment-2398522815
Ok. I'll close this issue. Feel free to open it again, if necessary.
Board
ESP32-C6 to ESP32-C6
Device Description
Waveshare ESP32-C6 dev board
Hardware Configuration
no
Version
latest development Release Candidate (RC-X)
IDE Name
Arduino IDE
Operating System
Windows 11
Flash frequency
80MHz
PSRAM enabled
yes
Upload speed
921600
Description
Cannot get a response from the Get command of coap. It returns 30 instead of a string
Sketch
Debug Message
Other Steps to Reproduce
press button on joiner to send via put a string, leader prints address. Copy address and paste back into leader serial terminal to populate the destination. Press button on leader to try and activate the GET for the joiner
I have checked existing issues, online documentation and the Troubleshooting Guide