In the configuration view, scroll down to the select COAP API and optionnaly COAP Observe if you wanted to get notified on resource change.
Now we can start using Coap inside the cli like described here, or by adding code.
Server resource in coap is the structure through which communication happens.
in our case the resource will be named "gpio" and controlled using the PUT method with ON, OFF and TOGGLE commands.
ON and OFF are also states you could read back with a GET method
create a new c file in your project. I named it coap_example.c in my project.
add the necessary headers to the file:
#include <stdio.h>
#include <string.h>
#include <openthread/coap.h>
#include <openthread/cli.h>
#include <openthread/logging.h>
#include "utils/code_utils.h"
#include "em_gpio.h"
#include "em_cmu.h"
now we need to add a function that will start the server and add the new resource:
#define OFF 1
#define ON 0
static const char *UriPath = "gpio";
uint8_t gpioState[3] = "OFF";
otCoapResource coapResourcegpio;
static otInstance *sInstance = NULL;
extern "C" void coap_server_init(otInstance *aInstance)
{
// saves OT instance
sInstance = aInstance;
// Enable GPIO clock and init Led GPIO
CMU_ClockEnable(cmuClock_GPIO, true);
GPIO_PinModeSet(gpioPortA, 4, gpioModePushPull, OFF);
// Start Coap
otCoapStart(aInstance, OT_DEFAULT_COAP_PORT);
// Populate the resource structure
coapResourcegpio.mContext=aInstance;
coapResourcegpio.mUriPath=UriPath;
coapResourcegpio.mHandler=coap_request_handler;
coapResourcegpio.mNext=NULL;
// Create the resource
otCoapAddResource(aInstance, &coapResourcegpio);
// Some comment on the cli to understand the Coap server is ready
otCliOutputFormat("\nCoAP server initialized\r\n");
}
now we need to create the handler pointed in the resource structure to respond to the coap message:
void coap_request_handler(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
{
otError error = OT_ERROR_NONE;
otMessage *responseMessage;
otCoapCode responseCode = OT_COAP_CODE_CHANGED;
otCoapCode messageCode = otCoapMessageGetCode(aMessage); // read code (get,put,post..)
otCoapType messageType = otCoapMessageGetType(aMessage); // read message type (confirmable or not)
// read any payload
char data[32];
uint16_t offset = otMessageGetOffset(aMessage);
uint16_t read = otMessageRead(aMessage, offset, data, sizeof(data) - 1);
data[read] = '\0';
// prepare any message or at leastacknowledge response if confirmation is required
responseMessage = otCoapNewMessage((otInstance*)aContext, NULL);
otCoapMessageInitResponse(responseMessage, aMessage, OT_COAP_TYPE_ACKNOWLEDGMENT, responseCode);
otCoapMessageSetToken(responseMessage, otCoapMessageGetToken(aMessage), otCoapMessageGetTokenLength(aMessage));
otCoapMessageSetPayloadMarker(responseMessage);
// treat code action
switch (messageCode) {
case OT_COAP_CODE_GET:
error = otMessageAppend(responseMessage, gpioState, strlen((const char*)gpioState));
otEXPECT(OT_ERROR_NONE == error);
error = otCoapSendResponse((otInstance*)aContext, responseMessage, aMessageInfo);
otEXPECT(OT_ERROR_NONE == error);
break;
case OT_COAP_CODE_PUT:
case OT_COAP_CODE_POST:
// process message
if(strcmp("ON", data) == 0)
{
// update the attribute state
memcpy(gpioState, "ON", sizeof(3));
// set LED ON
GPIO_PinOutClear(gpioPortA, 4);
}
else if(strcmp("OFF", data) == 0)
{
// update the attribute state
memcpy(gpioState, "OFF", sizeof(3));
// set LED OFF
GPIO_PinOutSet(gpioPortA, 4);
}
else if(strcmp("TOGGLE", data) == 0)
{
// update the attribute state
if (strcmp("ON", gpioState) == 0)
memcpy(gpioState, "OFF", sizeof(3));
else
memcpy(gpioState, "ON", sizeof(3));
// toggle LED
GPIO_PinOutToggle(gpioPortA, 4);
}
else
{
/* no valid body, fail without response */
otEXPECT_ACTION(false, error = OT_ERROR_NO_BUFS);
}
if (OT_COAP_TYPE_CONFIRMABLE == messageType)
{
error = otMessageAppend(responseMessage, gpioState,strlen((const char*)gpioState));
otEXPECT(OT_ERROR_NONE == error);
error = otCoapSendResponse((otInstance*)aContext,responseMessage, aMessageInfo);
otEXPECT(OT_ERROR_NONE == error);
}
break;
default:
// nothing to do...
break;
}
exit:
if (error != OT_ERROR_NONE && responseMessage != NULL)
{
otMessageFree(responseMessage);
}
}
Finally we need to call the coap_server_init() function from the app.c. We should integrate this call as the stack is initialized and commissioned to a network.
This is the case as the Thread device role is greater than DETACHED. it is then either a LEADER, a ROUTER or a CHILD:
extern void coap_server_init(otInstance *aInstance);
/******************************************************************************
* Application Process Action.
*****************************************************************************/
void app_process_action(void)
{
otTaskletsProcess(sInstance);
otSysProcessDrivers(sInstance);
if (!server_started)
{
if (otThreadGetDeviceRole(sInstance) > OT_DEVICE_ROLE_DETACHED) {
// Thread network is active, we can start the coap server
coap_server_init(sInstance);
} else {
// Thread network is not active
}
}
}
Compile and flash the binary on 2 developper kits to be able to create a minimum network and test the coap resource using the cli.
An alternative is to flash only one developper kit and commission to a network created from a border router.
It requires a bootloader so, if you do not have one, please flash one. A good example to use in the SiSDK is bootloader-storage-internal-single-1536k.
open a terminal on each of the developper board CLI.
Generate, view, and commit a new Active Operational Dataset:
> dataset init new
Done
> dataset
Active Timestamp: 1
Channel: 13
Channel Mask: 0x07fff800
Ext PAN ID: d63e8e3e495ebbc3
Mesh Local Prefix: fd3d:b50b:f96d:722d::/64
Network Key: dfd34f0f05cad978ec4e32b0413038ff
Network Name: OpenThread-8f28
PAN ID: 0x8f28
PSKc: c23a76e98f1a6483639b1ac1271e2e27
Security Policy: 0, onrc
Done
> dataset commit active
Done
Bring up the IPv6 interface:
> ifconfig up
Done
Start Thread protocol operation:
> thread start
Done
Wait a few seconds and verify that the device has become a Thread Leader:
> state
leader
Done
View IPv6 addresses assigned to Node 1's Thread interface:
> ipaddr
fd3d:b50b:f96d:722d:0:ff:fe00:fc00
fd3d:b50b:f96d:722d:0:ff:fe00:c00
fd3d:b50b:f96d:722d:7a73:bff6:9093:9117
fe80:0:0:0:6c41:9001:f3d6:4148
Done
Configure the Thread Network Key from Node 1's Active Operational Dataset:
> dataset networkkey dfd34f0f05cad978ec4e32b0413038ff
Done
> dataset commit active
Done
Bring up the IPv6 interface:
> ifconfig up
Done
Start Thread protocol operation:
> thread start
Done
Wait a few seconds and verify that the device has become a Thread Child or Router:
> state
child
Done
Wait to get the following message on the CLI to get sure COAP server is running on both nodes:
CoAP server initialized
from node 2 CLI, check the "gpio" resource state on node 1 IP Address
coap get fd3d:b50b:f96d:722d:7a73:bff6:9093:9117 gpio
Response should be:
Done
> coap response from fd3d:b50b:f96d:722d:7a73:bff6:9093:9117 with payload: 4f4646
4f4646 is OFF in ASCII which is the LED state at init on node 1.
from node 2 CLI, change state the "gpio" resource on node 1 IP Address
coap put fd3d:b50b:f96d:722d:7a73:bff6:9093:9117 gpio con TOGGLE
LED should change state on node 1 and response should be show ON in hexadecimal:
Done
> coap response from fd3d:b50b:f96d:722d:7a73:bff6:9093:9117 with payload: 4f4e