nRF5 IoT SDK
v0.9.0
|
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.
The illustration in Figure 1 shows the flow of sending a request message from a CoAP client.
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.
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.
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.
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.
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).
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.
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.
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.
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.
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.
In order to initialize coap the coap_init API call is used. The function takes two parameters:
On initialization, the transport layer for CoAP creates and listens on all the Client and Server ports. The define COAP_PORT_COUNT in the sdk_config.h of the application determines the number of ports that are reserved for CoAP communication.
The example below demonstrates how to configure smartCoAP to use port 5683 as local port for CoAP.
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.
The example below demonstrates how to create a Non-Confirmable (NON) request.
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.
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:
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:
The code below sends a CoAP NON request to coap://[2004::b1]:5683/light/led0, doing a PUT with a one-byte payload.
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.
Nordic's smartCoAP library implements secure transport for CoAP transactions using DTLS. The library submodule that implements DTLS as an optional transport for CoAP is coap_transport_dtls.c.
When using DTLS as a transport for CoAP, additional configuration of COAP_MAX_REMOTE_SESSION in the sdk_config.h is needed. This parameter determines the number of simultaneous DTLS sessions that can be managed by the transport layer for both server and client roles. There is no direct correlation between the COAP_PORT_COUNT and the COAP_MAX_REMOTE_SESSION.
For example, an application playing the client role, one port may be sufficient to communicate with serveral servers, therefore COAP_PORT_COUNT is 1. However, the application must ensure that the COAP_MAX_REMOTE_SESSION is equal to the number of remote CoAP servers that it needs to communicate with securely.
The code below demonstrates how the port configuration could be set to use one common local port for two clients. Each client targetting a different peer server.
In order that CoAP messages are secured using DTLS transport, setting up necessary security parameters are required using the coap_security_setup API. This API configures the key settings and role for the DTLS session for a local endpoint. For the client role, the remote server needs to be identified with the DTLS session. On the other hand, for the server role, since it is not possible to identify the clients that will connect to it, no remote client identification is expected.
For the client role, coap_security_setup will trigger DTLS handshake with the remote server. For the server role, no DTLS procedures are triggered until a remote client transmits a message to the local server endpoint.
The code below demonstrates how to configure a secure session and trigger initiating the DTLS handshake as a client using DTLS Pre-Shared Key mode.
When the application is acting as a DTLS server, the remote is not required as it not known in advance what the remote peer address and port number it will use. This is handled by just passing a NULL pointer as parameter for the remote.
The code below demonstrates how to configure a secure session as a server waiting for remote peers to initiate connection using DTLS Pre-Shared Key mode.
It is also possible to use a local port configured to use DTLS-Secured CoAP for non-secure CoAP messages. Even if the secure session is set up and in use, it is possible to send CoAP messages using the same port to a different peer address. If the peer address is matching the one already registered to be secure, it will be sent as secure CoAP message using DTLS. The same applies if the DTLS session is not yet set up or used. If the remote peer address is not indicated to be using secure CoAP messages, it will default to use the port sending it as non-secure.
In order to tear down a secure session established between the local and remote endpoints set up, the coap_security_destroy API is used. It is also possible to tear down all sessions associated with a local endpoint, irrespective of the role, by setting the remote endpoint to NULL.
Behavipr of this API is independent of the roles played on the local port. And it is possible to tear down either a specific DTLS session or all DTLS sessions associated with the local endpoint.
The code below shows how to clean up a specific session.
The code below shows how to clean up for all sessions assocated with the local port.
The following configuration parameters should be defined in sdk_config.h
.
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 |
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 |
Default version number used in a CoAP packet.
Restriction | Value |
---|---|
Minimum value | 0 |
Maximum value | 3 |
Recommended value | 1 |
Dependencies | None |
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 |
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 |
Maximum size of a smartCoAP message excluding the mandatory CoAP header.
Restriction | Value |
---|---|
Minimum value | 1 |
Maximum value | 65535 |
Dependencies | None |
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 |
Maximum length of the resource name that can be supplied from the application.
Restriction | Value |
---|---|
Minimum value | 1 |
Maximum value | 65535 |
Dependencies | None |
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 |
Maximum number of transmit attempts for a Confirmable message.
Restriction | Value |
---|---|
Minimum value | 0 |
Maximum value | 255 |
Recommended value | 4 |
Dependencies | None |
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 |
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 |
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 |
The maximum number of simultanous DTLS-Secure CoAP connections the application can have.
Restriction | Value |
---|---|
Minimum value | 0 |
Maximum value | NRF_TLS_MAX_INSTANCE_COUNT |
Dependencies | NRF_TLS_MAX_INSTANCE_COUNT |
The following sections describe the specifics and limitations to the current implementation.