1.. _coap_server_interface: 2 3CoAP server 4########### 5 6.. contents:: 7 :local: 8 :depth: 2 9 10Overview 11******** 12 13Zephyr comes with a batteries-included CoAP server, which uses services to listen for CoAP 14requests. The CoAP services handle communication over sockets and pass requests to registered 15CoAP resources. 16 17Setup 18***** 19 20Some configuration is required to make sure services can be started using the CoAP server. The 21:kconfig:option:`CONFIG_COAP_SERVER` option should be enabled in your project: 22 23.. code-block:: cfg 24 :caption: ``prj.conf`` 25 26 CONFIG_COAP_SERVER=y 27 28All services are added to a predefined linker section and all resources for each service also get 29their respective linker sections. If you would have a service ``my_service`` it has to be 30prefixed with ``coap_resource_`` and added to a linker file: 31 32.. code-block:: c 33 :caption: ``sections-ram.ld`` 34 35 #include <zephyr/linker/iterable_sections.h> 36 37 ITERABLE_SECTION_RAM(coap_resource_my_service, Z_LINK_ITERABLE_SUBALIGN) 38 39Add this linker file to your application using CMake: 40 41.. code-block:: cmake 42 :caption: ``CMakeLists.txt`` 43 44 # Support LD linker template 45 zephyr_linker_sources(DATA_SECTIONS sections-ram.ld) 46 47 # Support CMake linker generator 48 zephyr_iterable_section(NAME coap_resource_my_service 49 GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT}) 50 51You can now define your service as part of the application: 52 53.. code-block:: c 54 55 #include <zephyr/net/coap_service.h> 56 57 static const uint16_t my_service_port = 5683; 58 59 COAP_SERVICE_DEFINE(my_service, "0.0.0.0", &my_service_port, COAP_SERVICE_AUTOSTART); 60 61.. note:: 62 63 Services defined with the ``COAP_SERVICE_AUTOSTART`` flag will be started together with the CoAP 64 server thread. Services can be manually started and stopped with ``coap_service_start`` and 65 ``coap_service_stop`` respectively. 66 67Sample Usage 68************ 69 70The following is an example of a CoAP resource registered with our service: 71 72.. code-block:: c 73 74 #include <zephyr/net/coap_service.h> 75 76 static int my_get(struct coap_resource *resource, struct coap_packet *request, 77 struct sockaddr *addr, socklen_t addr_len) 78 { 79 static const char *msg = "Hello, world!"; 80 uint8_t data[CONFIG_COAP_SERVER_MESSAGE_SIZE]; 81 struct coap_packet response; 82 uint16_t id; 83 uint8_t token[COAP_TOKEN_MAX_LEN]; 84 uint8_t tkl, type; 85 86 type = coap_header_get_type(request); 87 id = coap_header_get_id(request); 88 tkl = coap_header_get_token(request, token); 89 90 /* Determine response type */ 91 type = (type == COAP_TYPE_CON) ? COAP_TYPE_ACK : COAP_TYPE_NON_CON; 92 93 coap_packet_init(&response, data, sizeof(data), COAP_VERSION_1, type, tkl, token, 94 COAP_RESPONSE_CODE_CONTENT, id); 95 96 /* Set content format */ 97 coap_append_option_int(&response, COAP_OPTION_CONTENT_FORMAT, 98 COAP_CONTENT_FORMAT_TEXT_PLAIN); 99 100 /* Append payload */ 101 coap_packet_append_payload_marker(&response); 102 coap_packet_append_payload(&response, (uint8_t *)msg, strlen(msg)); 103 104 /* Send to response back to the client */ 105 return coap_resource_send(resource, &response, addr, addr_len, NULL); 106 } 107 108 static int my_put(struct coap_resource *resource, struct coap_packet *request, 109 struct sockaddr *addr, socklen_t addr_len) 110 { 111 /* ... Handle the incoming request ... */ 112 113 /* Return a CoAP response code as a shortcut for an empty ACK message */ 114 return COAP_RESPONSE_CODE_CHANGED; 115 } 116 117 static const char * const my_resource_path[] = { "test", NULL }; 118 COAP_RESOURCE_DEFINE(my_resource, my_service, { 119 .path = my_resource_path, 120 .get = my_get, 121 .put = my_put, 122 }); 123 124.. note:: 125 126 As demonstrated in the example above, a CoAP resource handler can return response codes to let 127 the server respond with an empty ACK response. 128 129Observable resources 130******************** 131 132The CoAP server provides logic for parsing observe requests and stores these using the runtime data 133of CoAP services. An example using a temperature sensor can look like: 134 135.. code-block:: c 136 137 #include <zephyr/kernel.h> 138 #include <zephyr/drivers/sensor.h> 139 #include <zephyr/net/coap_service.h> 140 141 static void notify_observers(struct k_work *work); 142 K_WORK_DELAYABLE_DEFINE(temp_work, notify_observers); 143 144 static int send_temperature(struct coap_resource *resource, 145 const struct sockaddr *addr, socklen_t addr_len, 146 uint16_t age, uint16_t id, const uint8_t *token, uint8_t tkl, 147 bool is_response) 148 { 149 const struct device *dev = DEVICE_DT_GET(DT_ALIAS(ambient_temp0)); 150 uint8_t data[CONFIG_COAP_SERVER_MESSAGE_SIZE]; 151 struct coap_packet response; 152 char payload[14]; 153 struct sensor_value value; 154 double temp; 155 uint8_t type; 156 157 /* Determine response type */ 158 type = is_response ? COAP_TYPE_ACK : COAP_TYPE_CON; 159 160 if (!is_response) { 161 id = coap_next_id(); 162 } 163 164 coap_packet_init(&response, data, sizeof(data), COAP_VERSION_1, type, tkl, token, 165 COAP_RESPONSE_CODE_CONTENT, id); 166 167 if (age >= 2U) { 168 coap_append_option_int(&response, COAP_OPTION_OBSERVE, age); 169 } 170 171 /* Set content format */ 172 coap_append_option_int(&response, COAP_OPTION_CONTENT_FORMAT, 173 COAP_CONTENT_FORMAT_TEXT_PLAIN); 174 175 /* Get the sensor data */ 176 sensor_sample_fetch_chan(dev, SENSOR_CHAN_AMBIENT_TEMP); 177 sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &value); 178 temp = sensor_value_to_double(&value); 179 180 snprintk(payload, sizeof(payload), "%0.2f°C", temp); 181 182 /* Append payload */ 183 coap_packet_append_payload_marker(&response); 184 coap_packet_append_payload(&response, (uint8_t *)payload, strlen(payload)); 185 186 return coap_resource_send(resource, &response, addr, addr_len, NULL); 187 } 188 189 static int temp_get(struct coap_resource *resource, struct coap_packet *request, 190 struct sockaddr *addr, socklen_t addr_len) 191 { 192 uint8_t token[COAP_TOKEN_MAX_LEN]; 193 uint16_t id; 194 uint8_t tkl; 195 int r; 196 197 /* Let the CoAP server parse the request and add/remove observers if needed */ 198 r = coap_resource_parse_observe(resource, request, addr); 199 200 id = coap_header_get_id(request); 201 tkl = coap_header_get_token(request, token); 202 203 return send_temperature(resource, addr, addr_len, r == 0 ? resource->age : 0, 204 id, token, tkl, true); 205 } 206 207 static void temp_notify(struct coap_resource *resource, struct coap_observer *observer) 208 { 209 send_temperature(resource, &observer->addr, sizeof(observer->addr), resource->age, 0, 210 observer->token, observer->tkl, false); 211 } 212 213 static const char * const temp_resource_path[] = { "sensors", "temp1", NULL }; 214 COAP_RESOURCE_DEFINE(temp_resource, my_service, { 215 .path = temp_resource_path, 216 .get = temp_get, 217 .notify = temp_notify, 218 }); 219 220 static void notify_observers(struct k_work *work) 221 { 222 if (sys_slist_is_empty(&temp_resource.observers)) { 223 return; 224 } 225 226 coap_resource_notify(&temp_resource); 227 k_work_reschedule(&temp_work, K_SECONDS(1)); 228 } 229 230CoAP Events 231*********** 232 233By enabling :kconfig:option:`CONFIG_NET_MGMT_EVENT` the user can register for CoAP events. The 234following example simply prints when an event occurs. 235 236.. code-block:: c 237 238 #include <zephyr/sys/printk.h> 239 #include <zephyr/net/coap_mgmt.h> 240 #include <zephyr/net/coap_service.h> 241 242 #define COAP_EVENTS_SET (NET_EVENT_COAP_OBSERVER_ADDED | NET_EVENT_COAP_OBSERVER_REMOVED | \ 243 NET_EVENT_COAP_SERVICE_STARTED | NET_EVENT_COAP_SERVICE_STOPPED) 244 245 void coap_event_handler(uint64_t mgmt_event, struct net_if *iface, 246 void *info, size_t info_length, void *user_data) 247 { 248 switch (mgmt_event) { 249 case NET_EVENT_COAP_OBSERVER_ADDED: 250 printk("CoAP observer added"); 251 break; 252 case NET_EVENT_COAP_OBSERVER_REMOVED: 253 printk("CoAP observer removed"); 254 break; 255 case NET_EVENT_COAP_SERVICE_STARTED: 256 if (info != NULL && info_length == sizeof(struct net_event_coap_service)) { 257 struct net_event_coap_service *net_event = info; 258 259 printk("CoAP service %s started", net_event->service->name); 260 } else { 261 printk("CoAP service started"); 262 } 263 break; 264 case NET_EVENT_COAP_SERVICE_STOPPED: 265 if (info != NULL && info_length == sizeof(struct net_event_coap_service)) { 266 struct net_event_coap_service *net_event = info; 267 268 printk("CoAP service %s stopped", net_event->service->name); 269 } else { 270 printk("CoAP service stopped"); 271 } 272 break; 273 } 274 } 275 276 NET_MGMT_REGISTER_EVENT_HANDLER(coap_events, COAP_EVENTS_SET, coap_event_handler, NULL); 277 278CoRE Link Format 279**************** 280 281The :kconfig:option:`CONFIG_COAP_SERVER_WELL_KNOWN_CORE` option enables handling the 282``.well-known/core`` GET requests by the server. This allows clients to get a list of hypermedia 283links to other resources hosted in that server. 284 285API Reference 286************* 287 288.. doxygengroup:: coap_service 289.. doxygengroup:: coap_mgmt 290