nRF51 IoT SDK
 All Data Structures Functions Variables Typedefs Enumerations Enumerator Groups Pages
CoAP client/server

Nordic's smartCoAP library supports both the client role, the server role, and a mixed role of the Constrained Application Protocol (CoAP).

Applications can use this library to generate CoAP request messages and to set up servers by setting up CoAP endpoints. The memory management for the requests and responses is handled by the library, as is the token matching on responses due to a request message. This token matching will issue a call to an application-provided function that can handle the request.

Endpoints are composed by linking resources in a linked-list manner, each resource pointing to its children. This way it is possible to dynamically locate a given resource based on the CoAP URI-path options and issue a call to a specific endpoint callback function. The linked list also allows to generate a CoRE Link Format Resource Discovery string according to RFC6690.

Client role

The illustration in Figure 1 shows the flow of sending a request message from a CoAP client.

msc_coap_request_flow
Figure 1. smartCoAP request message flow.

First, the message to be sent is created and configured using smartCoAP library calls. Next, the message is scheduled for transmission by calling the send function in the library. Scheduling the message for transmission creates a copy of the serialized CoAP message and puts this copy in an internal message queue, along with some metadata that is associated with the message. Information like transport layer socket, token number, and other values is stored with the raw CoAP message. This information will be used to match the received response message with the messages in the internal queue. If the message is a confirmable request (CON request), the message will be retransmitted if no response was received within the receive time-out.

When receiving the response, the smartCoAP library will match the token of the response message, compare it against the messages in the transmission queue, issue a callback to the application function that was registered along with the request creation, and return.

The original request message in its serialized format is backed up before it is added to the transmission queue. Therefore, the request can safely be deleted at any time. Deleting a CoAP request will free only the memory allocated during creation of the request, not the serialized copy in the smartCoAP message transmission queue. This way, the application can keep the application-created request and reuse the message without having to create the message again. If token and message ID are the only values that change between requests, the request with its options and payload can be reused several times, and new serialized versions of the requests will be generated when calling the send function in the library.

Server role

The illustration in Figure 2 shows the flow of receiving a request message from a remote CoAP client. The CoAP server responds to the request after calling the method callback function of the endpoint resource.

msc_coap_response_flow
Figure 2. smartCoAP remote request message flow.

First, the endpoint resources are registered, forming a linked-list hierarchy. The first node that is registered acts as the root for subsequent operations on the endpoint resources. Next, a method callback function is set to the endpoint. (Figure 2 uses an endpoint named "light" for illustration.) This way, the client can make method calls on this resource.

When receiving the remote request, the smartCoAP library will resolve the resource that was registered earlier. It will traverse recursively through the hierarchy of resource entities and match the names against the URI-paths in the request message. When the correct resource is found, the library calls the resource method handler function that has been set by the application when registering the resource. Right before the callback is issued, a response message is created by the library. Message type and token ID are copied over to the generated response message and passed to the callback along with the original request. The method handler function can override the response message by setting a new response code, add options, or by adding a payload. When the function returns, the constructed response will be transmitted over UDP as a CoAP response message.

Endpoints

All CoAP endpoints in smartCoAP are called resources. An endpoint can be anything from a resource that is listed in the well-known URI ".well-known/core" on the CoAP server to an endpoint that controls an LED. The resources must be defined in such a way that they stay in memory during the lifetime of the application.

After coap_init has been called, any number of resources can be added. There is no extra memory needed for a resource besides the memory that is used during its declaration. All declared resources are linked together in a hierarchy using the smartCoAP library functions.

Resource Hierarchy

The first resource that is created always forms the root of the hierarchy. The coap_init function clears this information and unassigns the root again. Creating a resource does not allocate any additional memory, but rather copies the provided string into the resource and initializes the resource structure.

After the resource is created, it can be linked to another. If a child is linked to a parent, the parent will form a linked list with children, using a front and a back pointer. Each new child that is added to the same parent will be added in the back. The back pointer of the last sibling that was added is then redirected to the new child.

There is no API for removing a child resource. Therefore, the resource hierarchy must be determined during the design of the application. This resources hierarchy can also serve as basis for generating the .well-known/core string.

The example below demonstrates how to link four resources together, forming a hierarchy of resources: one root with two children, where one of the children has a child (grandchild0).

uint32_t err_code = coap_init();
APP_ERROR_CHECK(err_code);
static coap_resource_t root;
static coap_resource_t child0;
static coap_resource_t grandchild0;
static coap_resource_t child1;
// Create root.
err_code = coap_resource_create(&root, "/");
APP_ERROR_CHECK(err_code);
// Create children.
err_code = coap_resource_create(&child0, "light");
APP_ERROR_CHECK(err_code);
err_code = coap_resource_create(&grandchild0, "led0");
APP_ERROR_CHECK(err_code);
err_code = coap_resource_create(&child1, "temp");
APP_ERROR_CHECK(err_code);
// Add root of all endpoints. The first resource to be
// added will be treated as the root of the resources.
err_code = coap_resource_child_add(&root, &child0);
APP_ERROR_CHECK(err_code);
err_code = coap_resource_child_add(&root, &child1);
APP_ERROR_CHECK(err_code);
err_code = coap_resource_child_add(&child0, &grandchild0);
APP_ERROR_CHECK(err_code);

The image below shows how the resources are linked together to form a resource hierarchy. You can use this hierarchy to locate endpoints when a remote request is sent to an smartCoAP server. The image also shows how the resources in the example code above are conceptually linked together in the linked list.

coap_resource_layout.png
Figure 3. smartCoAP resource hierarchy with four registered resources.

Callback

The smartCoAP endpoints can have a callback function, which is called whenever a CoAP request that has a URI path matching with the registered resources in smartCoAP is detected. If no callback is registered for the URI path, a reply with a "404 (Not Found)" code is returned.

void resource_callback_func(coap_message_t * p_request, coap_message_t * p_response)
{
p_response->header.code = COAP_CODE_205_CONTENT;
uint8_t payload[] = {1};
coap_message_payload_set(p_response, payload, sizeof(payload));
}
...
static coap_resource_t resource;
err_code = coap_resource_create(&resource, "resource");
APP_ERROR_CHECK(err_code);
resource.callback = resource_callback_func;

Permissions

Each endpoint can be configured with different access permissions. Each method can be allowed or not allowed; if no permissions are explicitly defined, the default is to not allow any methods on the endpoint.

The example below demonstrates how to set the permissions of a fictive "resource" endpoint. The permission settings allow GET and PUT requests, but deny all other methods. Requests to other methods will be rejected and replied to with a "405 (Method Not Allowed)" code. The endpoint callback function will not be called but handled internally in the smartCoAP library.

static coap_resource_t resource;
err_code = coap_resource_create(&resource, "resource");
APP_ERROR_CHECK(err_code);

.well-known/core

The smartCoAP library provides an easy-to-use function for collecting the resource names to be used in a .well-known/core resource endpoint. The memory is provided from the application and must be large enough to hold all name strings of all resources plus the link format syntax. If the provided buffer does not satisfy the needed size of the generated well-known string, an error code is returned.

The function coap_resource_well_known_generate should be run once during the start-up of the application, to populate the buffer. Traversing the resource hierarchy is a costly operation if the number of resources is great. Therefore, it is assumed that the resources do not change names during the lifetime of the application.

Note
The smartCoAP does not currently support creation and deletion of resources at runtime.

The example below demonstrates how you can use the coap_resource_well_known_generate function. In this example, the well_known_core buffer is used as a global, because it can easily be accessed by both the main function that creates the resource hierarchy and the callback function for the ".well-known/core" endpoint.

static uint8_t well_known_core[100];
...
uint16_t size = sizeof(well_known_core);
err_code = coap_resource_well_known_generate(well_known_core, &size);
APP_ERROR_CHECK(err_code);

Client requests

Creating a request message

You can generate a request by using the coap_message_new API call. Use the supplied configuration as a base for creating the new message. Parts of the message can also be overridden by setting the structure members of coap_message_t after creating the request. The transport layer socket association will automatically be assigned during the creation of the request.

Note
If the id member of coap_message_conf_t is set to 0, an auto-generated message ID will be assigned to the new request message on creation.

The example below demonstrates how to create a Non-Confirmable (NON) request.

coap_message_t * request;
coap_message_conf_t message_conf;
// Set the CoAP message type to Non-Confirmable message.
message_conf.type = COAP_TYPE_NON;
// Set the CoAP method.
message_conf.code = COAP_CODE_PUT;
// Specify a token for the message.
message_conf.token[0] = 0x2;
message_conf.token[1] = 0x4;
message_conf.token_len = 2;
// Register a callback to be run when a response for this message is received.
message_conf.response_callback = response_handle_func;
uint32_t err_code = coap_message_new(&request, &message_conf);
if (err_code == NRF_SUCCESS)
...

Setting the remote address

Before the send function is called, the remote destination must be set in the smartCoAP request message. The remote address must be set after creating the message, however, because creating the message wipes the allocated memory.

The example below demonstrates how to set the remote address. In this example, the remote address is set to [2003::4] on port number 5683.

static coap_remote_t remote;
remote.port_number = 5683;
remote.addr[0] = 0x20;
remote.addr[1] = 0x03;
remote.addr[15] = 0x04;
...
uint32_t err_code = coap_message_new(&request, &message_conf);
// Request is successfully created and memory allocation for the new request is OK.
if (err_code == NRF_SUCCESS)
{
...
// Set the remote address information.
coap_message_remote_addr_set(request, &remote);
...
// Send the message.
}

Sending the request

After a new request has been successfully created and the address and port number to the remote have been set, the message can be populated with options and payload. Then it would be ready for transmission. The function for sending a request also returns a handle by reference. There is no special usage for this handle today. On the other hand it is meant to be used in a future implementation in order to abort an ongoing confirmable message.

The example below demonstrates how to send the message:

uint32_t err_code = coap_message_new(&request, &message_conf);
if (err_code == NRF_SUCCESS)
{
...
// Trigger the message to be sent.
uint32_t msg_handle;
err_code = coap_message_send(&msg_handle, request);
if (err_code != NRF_SUCCESS)
{
// Skip it or try again.
}
}

Deleting the request

After the message has been scheduled for transmission, it can be cleaned up, so that all of its memory allocations are freed. This function must be explicitly called unless the application reuses the created message.

The example below demonstrates how to delete the message:

coap_message_t * request;
coap_message_conf_t message_conf;
...
uint32_t err_code = coap_message_new(&request, &message_conf);
...
// Clean up the memory used by the request message.
err_code = coap_message_delete(request);
APP_ERROR_CHECK(err_code);

Full client example

The code below sends a CoAP NON request to coap://[2004::b1]:5683/light/led0, doing a PUT with a one-byte payload.

void response_handler(uint32_t status, void * arg, coap_message_t * p_response)
{
if (status == NRF_SUCCESS)
{
...
}
}
coap_message_t * request;
coap_message_conf_t message_conf;
// Set remote address to 2004::b1 to the CoAP server.
remote.addr[0] = 0x20;
remote.addr[1] = 0x04;
remote.addr[15] = 0xb1;
// Set port number of the CoAP server.
remote.port_number = 5683;
// Set the CoAP message type to Non-confirmable message.
message_conf.type = COAP_TYPE_NON;
// Set the CoAP method.
message_conf.code = COAP_CODE_PUT;
// Specify a token for the message.
message_conf.token[0] = 0x3;
message_conf.token[1] = 0x4;
message_conf.token_len = 2;
// Register a callback to be run when a response for this message is received.
message_conf.response_callback = response_handler;
uint32_t err_code = coap_message_new(&request, &message_conf);
if (err_code == NRF_SUCCESS)
{
// Set remote address and port number to the CoAP server.
coap_message_remote_addr_set(request, &remote);
// Add URI path to the endpoint.
err_code = coap_message_opt_str_add(request, COAP_OPT_URI_PATH, (uint8_t *)"light", 5);
APP_ERROR_CHECK(err_code);
err_code = coap_message_opt_str_add(request, COAP_OPT_URI_PATH, (uint8_t *)"led0", 4);
APP_ERROR_CHECK(err_code);
// Add the PUT payload.
uint8_t payload[] = {1};
err_code = coap_message_payload_set(request, payload, sizeof(payload));
APP_ERROR_CHECK(err_code);
// Trigger the message to be sent.
uint32_t msg_handle;
err_code = coap_message_send(&msg_handle, request);
if (err_code != NRF_SUCCESS)
{
// Skip it or try again.
}
// Clean up the memory used by the request message.
err_code = coap_message_delete(request);
APP_ERROR_CHECK(err_code);
}

Message retransmission

In order to handle retransmission and time-outs, smartCoAP has to be provided with a continuous tick. The coap_time_tick API function has to be called every second from the application. The function will go through any outstanding messages in the transmission queue and retransmit them if needed. Or, smartCoAP will flag to the application that a time-out has occurred and remove the message from the transmission queue.

Configuration parameters

The following configuration parameters should be defined in sdk_config.h.

COAP_DISABLE_LOGS

Disables debug tracing in the module. To enable tracing, this flag must be set to 0 and ENABLE_DEBUG_LOG_SUPPORT must be set to 1.

Description Value
Enable debug trace 0
Disable debug trace 1
Dependencies ENABLE_DEBUG_LOG_SUPPORT

COAP_DISABLE_API_PARAM_CHECK

Disables API parameter checks in the module. Set this define to 1 to disable checks on API parameters in the module.

API parameter checks are added to ensure that the correct parameters are passed to the module. These checks are useful during development phase, but they might be redundant when the application is finalized. Disabling these checks might improve performance.

Description Value
Enable API parameters check 0
Disable API parameters check 1
Dependencies None

COAP_VERSION

Default version number used in a CoAP packet.

Restriction Value
Minimum value 0
Maximum value 3
Recommended value 1
Dependencies None

COAP_PORT_COUNT

Maximum number of client/server ports used by the application. One socket will be created for each port.

Restriction Value
Minimum value 0
Maximum value UDP6_MAX_SOCKET_COUNT
Dependencies UDP6_MAX_SOCKET_COUNT

COAP_SERVER_PORT

Port number used for a CoAP server.

Restriction Value
Minimum value 1
Maximum value 65535
Dependencies None

COAP_CLIENT_PORT

Port number used for a CoAP client.

Restriction Value
Minimum value 1
Maximum value 65535
Dependencies None

COAP_MAX_NUMBER_OF_OPTIONS

Maximum number of options that the smartCoAP library can process. If the maximum limit is reached, the library will treat the package as unprocessable.

Restriction Value
Minimum value 1
Maximum value 255
Dependencies None

COAP_MESSAGE_DATA_MAX_SIZE

Maximum size of a smartCoAP message excluding the mandatory CoAP header.

Restriction Value
Minimum value 1
Maximum value 65535
Dependencies None

COAP_MESSAGE_QUEUE_SIZE

Maximum number of smartCoAP messages that can be in transmission at a time.

smartCoAP uses the Memory Manager that is also used by the underlying transport protocol. Therefore, if you increase this value, you should also increase the number of buffers. Depending on the COAP_MESSAGE_DATA_MAX_SIZE + 4 byte CoAP header, you must increase either MEMORY_MANAGER_SMALL_BLOCK_COUNT or MEMORY_MANAGER_MEDIUM_BLOCK_COUNT to ensure that there are additional buffers for the CoAP message queue. Which macro must be increased, depends on the size of the buffer that is sufficient for the CoAP message.

Restriction Value
Minimum value 1
Maximum value 65535
Recommended value 4
Dependencies MEMORY_MANAGER_SMALL_BLOCK_COUNT
MEMORY_MANAGER_MEDIUM_BLOCK_COUNT
MEMORY_MANAGER_SMALL_BLOCK_SIZE
MEMORY_MANAGER_MEDIUM_BLOCK_SIZE

COAP_RESOURCE_MAX_NAME_LEN

Maximum length of the resource name that can be supplied from the application.

Note
One extra byte will be added to the resource name to make sure that the provided string is zero terminated.
Restriction Value
Minimum value 1
Maximum value 65535
Dependencies None

COAP_RESOURCE_MAX_DEPTH

Maximum number of resource depth levels that smartCoAP will use. The number is used when adding resources to the resource structure or when traversing the resources for a matching resource name given in a request. Each added level increases the stack usage runtime with 4 bytes.

Restriction Value
Minimum value 1
Maximum value 255
Recommended value 1 - 10
Dependencies None

COAP_MAX_RETRANSMIT_COUNT

Maximum number of transmit attempts for a Confirmable message.

Restriction Value
Minimum value 0
Maximum value 255
Recommended value 4
Dependencies None

COAP_MAX_TRANSMISSION_SPAN

Maximum time from the first transmission of a Confirmable message to its last retransmission.

Restriction Value
Minimum value 0
Maximum value 65535
Recommended value 45
Dependencies None

COAP_ACK_TIMEOUT

Minimum spacing before another retransmission.

Restriction Value
Minimum value 0
Maximum value COAP_MAX_TRANSMISSION_SPAN / COAP_MAX_RETRANSMIT_COUNT
Recommended value 2
Dependencies None

COAP_ACK_RANDOM_FACTOR

Random factor to calculate the initial time-out value for a Confirmable message.

Restriction Value
Minimum value 0
Maximum value COAP_MAX_TRANSMISSION_SPAN / COAP_MAX_RETRANSMIT_COUNT / COAP_ACK_TIMEOUT
Recommended value 1.5
Dependencies None

Specifics and limitations

The following sections describe the specifics and limitations to the current implementation.

Implemented features

  • CoAP message types CON, NON, ACK, and RESET.
  • Automatic generation of message ID.
  • Token matching on responses to a request generated by a local client.
  • Callback on response to a request generated by a local client.
  • Endpoint creation as resources.
  • Automatic lookup of requested endpoint when receiving a remote request.
  • Endpoint resource function callback to perform an action or to create response message.
  • Auto generation of 404 (Not Found) response message if the resource is not located among the registered resources.
  • Auto generation of 405 (Method Not Allowed) response message if the located endpoint has a permission scheme not allowing the method to be executed on the endpoint resource.
  • Permission setting on endpoints to select methods to be handled by the endpoint resource callback function.
  • CoAP ping (empty message).
  • NON request/reply.
  • CON request/reply (piggybacked).
  • ACK message generated automatically on CON requests.
  • Automatic generation of .well-known/core link format discovery string.
  • Content format support. Resource handlers have access to bitmask indicating which content format the requesting client accepts. Resources also have a bitmask indicating the supported content formats.
  • Simple CON retransmission implemented in order to generate TRANSMISSION_TIMEOUT events.
  • ACK and Reset messages are clearing the original request from the message queue on reception.
  • Messages in the transmission queue are trimmed off the queue if no matching token has been received. The value of COAP_MAX_TRANSMISSION_SPAN is used as max time-out for both NON and CON (in addition to retransmission count).

Limitations

  • Delete message type is not supported.
  • No resource creation on POST and PUT methods.
  • Duplicate message detection is not implemented in the current library.
  • Delayed responses are not supported, only piggybacked responses for CON. If a request receives a delayed response ACK, it will delete the original request from the message queue.
  • No multicast support.
  • Not supporting the proxy role.
  • Generating unique tokens is left to the application in the current implementation of the library.

References