CoAP server

Overview

Zephyr comes with a batteries-included CoAP server, which uses services to listen for CoAP requests. The CoAP services handle communication over sockets and pass requests to registered CoAP resources.

Setup

Some configuration is required to make sure services can be started using the CoAP server. The CONFIG_COAP_SERVER option should be enabled in your project:

prj.conf
CONFIG_COAP_SERVER=y

All services are added to a predefined linker section and all resources for each service also get their respective linker sections. If you would have a service my_service it has to be prefixed with coap_resource_ and added to a linker file:

sections-ram.ld
#include <zephyr/linker/iterable_sections.h>

ITERABLE_SECTION_RAM(coap_resource_my_service, 4)

Add this linker file to your application using CMake:

CMakeLists.txt
zephyr_linker_sources(DATA_SECTIONS sections-ram.ld)

You can now define your service as part of the application:

#include <zephyr/net/coap_service.h>

static const uint16_t my_service_port = 5683;

COAP_SERVICE_DEFINE(my_service, "0.0.0.0", &my_service_port, COAP_SERVICE_AUTOSTART);

Note

Services defined with the COAP_SERVICE_AUTOSTART flag will be started together with the CoAP server thread. Services can be manually started and stopped with coap_service_start and coap_service_stop respectively.

Sample Usage

The following is an example of a CoAP resource registered with our service:

#include <zephyr/net/coap_service.h>

static int my_get(struct coap_resource *resource, struct coap_packet *request,
                  struct sockaddr *addr, socklen_t addr_len)
{
    static const char *msg = "Hello, world!";
    uint8_t data[CONFIG_COAP_SERVER_MESSAGE_SIZE];
    struct coap_packet response;
    uint16_t id;
    uint8_t token[COAP_TOKEN_MAX_LEN];
    uint8_t tkl, type;

    type = coap_header_get_type(request);
    id = coap_header_get_id(request);
    tkl = coap_header_get_token(request, token);

    /* Determine response type */
    type = (type == COAP_TYPE_CON) ? COAP_TYPE_ACK : COAP_TYPE_NON_CON;

    coap_packet_init(&response, data, sizeof(data), COAP_VERSION_1, type, tkl, token,
                     COAP_RESPONSE_CODE_CONTENT, id);

    /* Set content format */
    coap_append_option_int(&response, COAP_OPTION_CONTENT_FORMAT,
                           COAP_CONTENT_FORMAT_TEXT_PLAIN);

    /* Append payload */
    coap_packet_append_payload_marker(&response);
    coap_packet_append_payload(&response, (uint8_t *)msg, sizeof(msg));

    /* Send to response back to the client */
    return coap_resource_send(resource, &response, addr, addr_len, NULL);
}

static int my_put(struct coap_resource *resource, struct coap_packet *request,
                  struct sockaddr *addr, socklen_t addr_len)
{
    /* ... Handle the incoming request ... */

    /* Return a CoAP response code as a shortcut for an empty ACK message */
    return COAP_RESPONSE_CODE_CHANGED;
}

static const char * const my_resource_path[] = { "test", NULL };
COAP_RESOURCE_DEFINE(my_resource, my_service, {
    .path = my_resource_path,
    .get = my_get,
    .put = my_put,
});

Note

As demonstrated in the example above, a CoAP resource handler can return response codes to let the server respond with an empty ACK response.

Observable resources

The CoAP server provides logic for parsing observe requests and stores these using the runtime data of CoAP services. An example using a temperature sensor can look like:

#include <zephyr/kernel.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/net/coap_service.h>

static void notify_observers(struct k_work *work);
K_WORK_DELAYABLE_DEFINE(temp_work, notify_observers);

static int send_temperature(struct coap_resource *resource,
                            const struct sockaddr *addr, socklen_t addr_len,
                            uint16_t age, uint16_t id, const uint8_t *token, uint8_t tkl,
                            bool is_response)
{
    const struct device *dev = DEVICE_DT_GET(DT_ALIAS(ambient_temp0));
    uint8_t data[CONFIG_COAP_SERVER_MESSAGE_SIZE];
    struct coap_packet response;
    char payload[14];
    struct sensor_value value;
    double temp;
    uint8_t type;

    /* Determine response type */
    type = is_response ? COAP_TYPE_ACK : COAP_TYPE_CON;

    if (!is_response) {
        id = coap_next_id();
    }

    coap_packet_init(&response, data, sizeof(data), COAP_VERSION_1, type, tkl, token,
                     COAP_RESPONSE_CODE_CONTENT, id);

    if (age >= 2U) {
        coap_append_option_int(&response, COAP_OPTION_OBSERVE, age);
    }

    /* Set content format */
    coap_append_option_int(&response, COAP_OPTION_CONTENT_FORMAT,
                           COAP_CONTENT_FORMAT_TEXT_PLAIN);

    /* Get the sensor date */
    sensor_sample_fetch_chan(dev, SENSOR_CHAN_AMBIENT_TEMP);
    sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &value);
    temp = sensor_value_to_double(&value);

    snprintk(payload, sizeof(payload), "%0.2f°C", temp);

    /* Append payload */
    coap_packet_append_payload_marker(&response);
    coap_packet_append_payload(&response, (uint8_t *)payload, strlen(payload));

    return coap_resource_send(resource, &response, addr, addr_len, NULL);
}

static int temp_get(struct coap_resource *resource, struct coap_packet *request,
                    struct sockaddr *addr, socklen_t addr_len)
{
    uint8_t token[COAP_TOKEN_MAX_LEN];
    uint16_t id;
    uint8_t tkl;
    int r;

    /* Let the CoAP server parse the request and add/remove observers if needed */
    r = coap_resource_parse_observe(resource, request, addr);

    id = coap_header_get_id(request);
    tkl = coap_header_get_token(request, token);

    return send_temperature(resource, addr, addr_len, r == 0 ? resource->age : 0,
                            id, token, tkl, true);
}

static void temp_notify(struct coap_resource *resource, struct coap_observer *observer)
{
    send_temperature(resource, &observer->addr, sizeof(observer->addr), resource->age, 0,
                     observer->token, observer->tkl, false);
}

static const char * const temp_resource_path[] = { "sensors", "temp1", NULL };
COAP_RESOURCE_DEFINE(temp_resource, my_service, {
    .path = temp_resource_path,
    .get = temp_get,
    .notify = temp_notify,
});

static void notify_observers(struct k_work *work)
{
    if (sys_slist_is_empty(&temp_resource.observers)) {
        return;
    }

    coap_resource_notify(&temp_resource);
    k_work_reschedule(&temp_work, K_SECONDS(1));
}

CoAP Events

By enabling CONFIG_NET_MGMT_EVENT the user can register for CoAP events. The following example simply prints when an event occurs.

#include <zephyr/sys/printk.h>
#include <zephyr/net/coap_mgmt.h>
#include <zephyr/net/coap_service.h>

#define COAP_EVENTS_SET (NET_EVENT_COAP_OBSERVER_ADDED | NET_EVENT_COAP_OBSERVER_REMOVED | \
                         NET_EVENT_COAP_SERVICE_STARTED | NET_EVENT_COAP_SERVICE_STOPPED)

void coap_event_handler(uint32_t mgmt_event, struct net_if *iface,
                        void *info, size_t info_length, void *user_data)
{
    switch (mgmt_event) {
    case NET_EVENT_COAP_OBSERVER_ADDED:
        printk("CoAP observer added");
        break;
    case NET_EVENT_COAP_OBSERVER_REMOVED:
        printk("CoAP observer removed");
        break;
    case NET_EVENT_COAP_SERVICE_STARTED:
        if (info != NULL && info_length == sizeof(struct net_event_coap_service)) {
            struct net_event_coap_service *net_event = info;

            printk("CoAP service %s started", net_event->service->name);
        } else {
            printk("CoAP service started");
        }
        break;
    case NET_EVENT_COAP_SERVICE_STOPPED:
        if (info != NULL && info_length == sizeof(struct net_event_coap_service)) {
            struct net_event_coap_service *net_event = info;

            printk("CoAP service %s stopped", net_event->service->name);
        } else {
            printk("CoAP service stopped");
        }
        break;
    }
}

NET_MGMT_REGISTER_EVENT_HANDLER(coap_events, COAP_EVENTS_SET, coap_event_handler, NULL);

API Reference

group coap_service

CoAP Service API.

CoAP Service configuration flags

COAP_SERVICE_AUTOSTART

Start the service on boot.

Defines

COAP_RESOURCE_DEFINE(_name, _service, ...)

Define a static CoAP resource owned by the service named _service .

static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);

static int led_put(struct coap_resource *resource, struct coap_packet *request,
                   struct sockaddr *addr, socklen_t addr_len)
{
        const uint8_t *payload;
        uint16_t payload_len;

        payload = coap_packet_get_payload(request, &payload_len);
        if (payload_len != 1) {
                return COAP_RESPONSE_CODE_BAD_REQUEST;
        }

        if (gpio_pin_set_dt(&led, payload[0]) < 0) {
                return COAP_RESPONSE_CODE_INTERNAL_ERROR;
        }

        return COAP_RESPONSE_CODE_CHANGED;
}

COAP_RESOURCE_DEFINE(my_resource, my_service, {
        .put = led_put,
});

Note

The handlers registered with the resource can return a CoAP response code to reply with an acknowledge without any payload, nothing is sent if the return value is 0 or negative. As seen in the example.

Parameters:
  • _name – Name of the resource.

  • _service – Name of the associated service.

COAP_SERVICE_DEFINE(_name, _host, _port, _flags)

Define a CoAP service with static resources.

See also

COAP_SERVICE_FLAGS.

Note

The _host parameter can be NULL. If not, it is used to specify an IP address either in IPv4 or IPv6 format a fully-qualified hostname or a virtual host, otherwise the any address is used.

Note

The _port parameter must be non-NULL. It points to a location that specifies the port number to use for the service. If the specified port number is zero, then an ephemeral port number will be used and the actual port number assigned will be written back to memory. For ephemeral port numbers, the memory pointed to by _port must be writeable.

Parameters:
  • _name – Name of the service.

  • _host – IP address or hostname associated with the service.

  • _port[inout] Pointer to port associated with the service.

  • _flags – Configuration flags

COAP_SERVICE_COUNT(_dst)

Count the number of CoAP services.

Parameters:
  • _dst[out] Pointer to location where result is written.

COAP_SERVICE_RESOURCE_COUNT(_service)

Count CoAP service static resources.

Parameters:
  • _service – Pointer to a service.

COAP_SERVICE_HAS_RESOURCE(_service, _resource)

Check if service has the specified resource.

Parameters:
  • _service – Pointer to a service.

  • _resource – Pointer to a resource.

COAP_SERVICE_FOREACH(_it)

Iterate over all CoAP services.

Parameters:
COAP_RESOURCE_FOREACH(_service, _it)

Iterate over static CoAP resources associated with a given _service.

Note

This macro requires that _service is defined with COAP_SERVICE_DEFINE.

Parameters:
  • _service – Name of CoAP service

  • _it – Name of iterator (of type coap_resource)

COAP_SERVICE_FOREACH_RESOURCE(_service, _it)

Iterate over all static resources associated with _service .

Note

This macro is suitable for a _service defined with COAP_SERVICE_DEFINE.

Parameters:
  • _service – Pointer to COAP service

  • _it – Name of iterator (of type coap_resource)

Functions

int coap_service_start(const struct coap_service *service)

Start the provided service .

Note

This function is suitable for a service defined with COAP_SERVICE_DEFINE.

Parameters:
  • service – Pointer to CoAP service

Return values:
  • 0 – in case of success.

  • -EALREADY – in case of an already running service.

  • -ENOTSUP – in case the server has no valid host and port configuration.

int coap_service_stop(const struct coap_service *service)

Stop the provided service .

Note

This function is suitable for a service defined with COAP_SERVICE_DEFINE.

Parameters:
  • service – Pointer to CoAP service

Return values:
  • 0 – in case of success.

  • -EALREADY – in case the service isn’t running.

int coap_service_is_running(const struct coap_service *service)

Query the provided service running state.

Note

This function is suitable for a service defined with COAP_SERVICE_DEFINE.

Parameters:
  • service – Pointer to CoAP service

Return values:
  • 1 – if the service is running

  • 0 – if the service is stopped

  • negative – in case of an error.

int coap_service_send(const struct coap_service *service, const struct coap_packet *cpkt, const struct sockaddr *addr, socklen_t addr_len, const struct coap_transmission_parameters *params)

Send a CoAP message from the provided service .

Note

This function is suitable for a service defined with COAP_SERVICE_DEFINE.

Parameters:
  • service – Pointer to CoAP service

  • cpkt – CoAP Packet to send

  • addr – Peer address

  • addr_len – Peer address length

  • params – Pointer to transmission parameters structure or NULL to use default values.

Returns:

0 in case of success or negative in case of error.

int coap_resource_send(const struct coap_resource *resource, const struct coap_packet *cpkt, const struct sockaddr *addr, socklen_t addr_len, const struct coap_transmission_parameters *params)

Send a CoAP message from the provided resource .

Note

This function is suitable for a resource defined with COAP_RESOURCE_DEFINE.

Parameters:
  • resource – Pointer to CoAP resource

  • cpkt – CoAP Packet to send

  • addr – Peer address

  • addr_len – Peer address length

  • params – Pointer to transmission parameters structure or NULL to use default values.

Returns:

0 in case of success or negative in case of error.

int coap_resource_parse_observe(struct coap_resource *resource, const struct coap_packet *request, const struct sockaddr *addr)

Parse a CoAP observe request for the provided resource .

If the observe option value is equal to 0, an observer will be added, if the value is equal to 1, an existing observer will be removed.

Note

This function is suitable for a resource defined with COAP_RESOURCE_DEFINE.

Parameters:
  • resource – Pointer to CoAP resource

  • request – CoAP request to parse

  • addr – Peer address

Returns:

the observe option value in case of success or negative in case of error.

int coap_resource_remove_observer_by_addr(struct coap_resource *resource, const struct sockaddr *addr)

Lookup an observer by address and remove it from the resource .

Note

This function is suitable for a resource defined with COAP_RESOURCE_DEFINE.

Parameters:
  • resource – Pointer to CoAP resource

  • addr – Peer address

Returns:

0 in case of success or negative in case of error.

int coap_resource_remove_observer_by_token(struct coap_resource *resource, const uint8_t *token, uint8_t token_len)

Lookup an observer by token and remove it from the resource .

Note

This function is suitable for a resource defined with COAP_RESOURCE_DEFINE.

Parameters:
  • resource – Pointer to CoAP resource

  • token – Pointer to the token

  • token_len – Length of valid bytes in the token

Returns:

0 in case of success or negative in case of error.

group coap_mgmt

CoAP Manager Events.

Defines

NET_EVENT_COAP_SERVICE_STARTED

coap_mgmt event raised when a service has started

NET_EVENT_COAP_SERVICE_STOPPED

coap_mgmt event raised when a service has stopped

NET_EVENT_COAP_OBSERVER_ADDED

coap_mgmt event raised when an observer has been added to a resource

NET_EVENT_COAP_OBSERVER_REMOVED

coap_mgmt event raised when an observer has been removed from a resource

struct net_event_coap_service
#include <coap_mgmt.h>

CoAP Service event structure.

Public Members

const struct coap_service *service

The CoAP service for which the event is emitted.

struct net_event_coap_observer
#include <coap_mgmt.h>

CoAP Observer event structure.

Public Members

struct coap_resource *resource

The CoAP resource for which the event is emitted.

struct coap_observer *observer

The observer that is added/removed.