|
25 | 25 | * port, which supports RFC 6282 compression. Internally, gcoap depends on the |
26 | 26 | * nanocoap package for base level structs and functionality. |
27 | 27 | * |
| 28 | + * gcoap also supports the Observe extension (RFC 7641) for a server. gcoap |
| 29 | + * provides functions to generate and send an observe notification that are |
| 30 | + * similar to the functions to send a client request. |
| 31 | + * |
28 | 32 | * ## Server Operation ## |
29 | 33 | * |
30 | 34 | * gcoap listens for requests on GCOAP_PORT, 5683 by default. You can redefine |
|
75 | 79 | * |
76 | 80 | * ### Creating a request ### |
77 | 81 | * |
78 | | - * Here is the expected sequence for preparing and sending a request: |
| 82 | + * Here is the expected sequence to prepare and send a request: |
79 | 83 | * |
80 | 84 | * Allocate a buffer and a coap_pkt_t for the request. |
81 | 85 | * |
|
109 | 113 | * _content_type_ attributes. |
110 | 114 | * -# Read the payload, if any. |
111 | 115 | * |
| 116 | + * ## Observe Server Operation |
| 117 | + * |
| 118 | + * A CoAP client may register for Observe notifications for any resource that |
| 119 | + * an application has registered with gcoap. An application does not need to |
| 120 | + * take any action to support Observe client registration. However, gcoap |
| 121 | + * limits registration for a given resource to a _single_ observer. |
| 122 | + * |
| 123 | + * An Observe notification is considered a response to the original client |
| 124 | + * registration request. So, the Observe server only needs to create and send |
| 125 | + * the notification -- no further communication or callbacks are required. |
| 126 | + * |
| 127 | + * ### Creating a notification ### |
| 128 | + * |
| 129 | + * Here is the expected sequence to prepare and send a notification: |
| 130 | + * |
| 131 | + * Allocate a buffer and a coap_pkt_t for the notification, then follow the |
| 132 | + * steps below. |
| 133 | + * |
| 134 | + * -# Call gcoap_obs_init() to initialize the notification for a resource. |
| 135 | + * Test the return value, which may indicate there is not an observer for |
| 136 | + * the resource. If so, you are done. |
| 137 | + * -# Write the notification payload, starting at the updated _payload_ pointer |
| 138 | + * in the coap_pkt_t. |
| 139 | + * -# Call gcoap_finish(), which updates the packet for the payload. |
| 140 | + * |
| 141 | + * Finally, call gcoap_obs_send() for the resource. |
| 142 | + * |
| 143 | + * ### Other considerations ### |
| 144 | + * |
| 145 | + * By default, the value for the Observe option in a notification is three |
| 146 | + * bytes long. For resources that change slowly, this length can be reduced via |
| 147 | + * GCOAP_OBS_VALUE_WIDTH. |
| 148 | + * |
| 149 | + * To cancel a notification, the server expects to receive a GET request with |
| 150 | + * the Observe option value set to 1. The server does not support cancellation |
| 151 | + * via a reset (RST) response to a non-confirmable notification. |
| 152 | + * |
112 | 153 | * ## Implementation Notes ## |
113 | 154 | * |
114 | 155 | * ### Building a packet ### |
@@ -175,6 +216,13 @@ extern "C" { |
175 | 216 | */ |
176 | 217 | #define GCOAP_RESP_OPTIONS_BUF (8) |
177 | 218 |
|
| 219 | +/** |
| 220 | + * @brief Size of the buffer used to write options in an Observe notification. |
| 221 | + * |
| 222 | + * Accommodates Content-Format and Observe. |
| 223 | + */ |
| 224 | +#define GCOAP_OBS_OPTIONS_BUF (8) |
| 225 | + |
178 | 226 | /** @brief Maximum number of requests awaiting a response */ |
179 | 227 | #define GCOAP_REQ_WAITING_MAX (2) |
180 | 228 |
|
@@ -225,6 +273,69 @@ extern "C" { |
225 | 273 | */ |
226 | 274 | #define GCOAP_MSG_TYPE_INTR (0x1502) |
227 | 275 |
|
| 276 | +/** @brief Maximum number of Observe clients; use 2 if not defined */ |
| 277 | +#ifndef GCOAP_OBS_CLIENTS_MAX |
| 278 | +#define GCOAP_OBS_CLIENTS_MAX (2) |
| 279 | +#endif |
| 280 | + |
| 281 | +/** |
| 282 | + * @brief Maximum number of registrations for Observable resources; use 2 if |
| 283 | + * not defined |
| 284 | + */ |
| 285 | +#ifndef GCOAP_OBS_REGISTRATIONS_MAX |
| 286 | +#define GCOAP_OBS_REGISTRATIONS_MAX (2) |
| 287 | +#endif |
| 288 | + |
| 289 | +/** |
| 290 | + * @name States for the memo used to track Observe registrations |
| 291 | + * @{ |
| 292 | + */ |
| 293 | +#define GCOAP_OBS_MEMO_UNUSED (0) /**< This memo is unused */ |
| 294 | +#define GCOAP_OBS_MEMO_IDLE (1) /**< Registration OK; no current activity */ |
| 295 | +#define GCOAP_OBS_MEMO_PENDING (2) /**< Resource changed; notification pending */ |
| 296 | +/** @} */ |
| 297 | + |
| 298 | +/** |
| 299 | + * @brief Width in bytes of the Observe option value for a notification. |
| 300 | + * |
| 301 | + * This width is used to determine the length of the 'tick' used to measure |
| 302 | + * the time between observable changes to a resource. A tick is expressed |
| 303 | + * internally as GCOAP_OBS_TICK_EXPONENT, which is the base-2 log value of the |
| 304 | + * tick length in microseconds. |
| 305 | + * |
| 306 | + * The canonical setting for the value width is 3 (exponent 5), which results |
| 307 | + * in a tick length of 32 usec, per sec. 3.4, 4.4 of the RFC. Width 2 |
| 308 | + * (exponent 16) results in a tick length of ~65 msec, and width 1 (exponent |
| 309 | + * 24) results in a tick length of ~17 sec. |
| 310 | + * |
| 311 | + * The tick length must be short enough so that the Observe value strictly |
| 312 | + * increases for each new notification. The purpose of the value is to allow a |
| 313 | + * client to detect message reordering within the network latency period (128 |
| 314 | + * sec). For resources that change only slowly, the reduced message length is |
| 315 | + * useful when packet size is limited. |
| 316 | + */ |
| 317 | +#ifndef GCOAP_OBS_VALUE_WIDTH |
| 318 | +#define GCOAP_OBS_VALUE_WIDTH (3) |
| 319 | +#endif |
| 320 | + |
| 321 | +/** @brief See GCOAP_OBS_VALUE_WIDTH. */ |
| 322 | +#if (GCOAP_OBS_VALUE_WIDTH == 3) |
| 323 | +#define GCOAP_OBS_TICK_EXPONENT (5) |
| 324 | +#elif (GCOAP_OBS_VALUE_WIDTH == 2) |
| 325 | +#define GCOAP_OBS_TICK_EXPONENT (16) |
| 326 | +#elif (GCOAP_OBS_VALUE_WIDTH == 1) |
| 327 | +#define GCOAP_OBS_TICK_EXPONENT (24) |
| 328 | +#endif |
| 329 | + |
| 330 | +/** |
| 331 | + * @name Return values for gcoap_obs_init() |
| 332 | + * @{ |
| 333 | + */ |
| 334 | +#define GCOAP_OBS_INIT_OK (0) |
| 335 | +#define GCOAP_OBS_INIT_ERR (-1) |
| 336 | +#define GCOAP_OBS_INIT_UNUSED (-2) |
| 337 | +/** @} */ |
| 338 | + |
228 | 339 | /** |
229 | 340 | * @brief A modular collection of resources for a server |
230 | 341 | */ |
@@ -255,16 +366,29 @@ typedef struct { |
255 | 366 | msg_t timeout_msg; /**< For response timer */ |
256 | 367 | } gcoap_request_memo_t; |
257 | 368 |
|
| 369 | +/** @brief Memo for Observe registration and notifications */ |
| 370 | +typedef struct { |
| 371 | + sock_udp_ep_t *observer; /**< Client endpoint; unused if null */ |
| 372 | + coap_resource_t *resource; /**< Entity being observed */ |
| 373 | + uint8_t token[GCOAP_TOKENLEN_MAX]; /**< Client token for notifications */ |
| 374 | + unsigned token_len; /**< Actual length of token attribute */ |
| 375 | +} gcoap_observe_memo_t; |
| 376 | + |
258 | 377 | /** |
259 | 378 | * @brief Container for the state of gcoap itself |
260 | 379 | */ |
261 | 380 | typedef struct { |
262 | | - gcoap_listener_t *listeners; /**< List of registered listeners */ |
| 381 | + gcoap_listener_t *listeners; /**< List of registered listeners */ |
263 | 382 | gcoap_request_memo_t open_reqs[GCOAP_REQ_WAITING_MAX]; |
264 | | - /**< Storage for open requests; if first |
265 | | - byte of an entry is zero, the entry |
266 | | - is available */ |
267 | | - uint16_t last_message_id; /**< Last message ID used */ |
| 383 | + /**< Storage for open requests; if first |
| 384 | + byte of an entry is zero, the entry |
| 385 | + is available */ |
| 386 | + uint16_t last_message_id; /**< Last message ID used */ |
| 387 | + sock_udp_ep_t observers[GCOAP_OBS_CLIENTS_MAX]; |
| 388 | + /**< Observe clients; allows reuse for |
| 389 | + observe memos */ |
| 390 | + gcoap_observe_memo_t observe_memos[GCOAP_OBS_REGISTRATIONS_MAX]; |
| 391 | + /**< Observed resource registrations */ |
268 | 392 | } gcoap_state_t; |
269 | 393 |
|
270 | 394 | /** |
@@ -402,6 +526,39 @@ static inline ssize_t gcoap_response(coap_pkt_t *pdu, uint8_t *buf, size_t len, |
402 | 526 | : -1; |
403 | 527 | } |
404 | 528 |
|
| 529 | +/** |
| 530 | + * @brief Initializes a CoAP Observe notification packet on a buffer, for the |
| 531 | + * observer registered for a resource. |
| 532 | + * |
| 533 | + * First verifies that an observer has been registered for the resource. |
| 534 | + * |
| 535 | + * @param[in] pdu Notification metadata |
| 536 | + * @param[in] buf Buffer containing the PDU |
| 537 | + * @param[in] len Length of the buffer |
| 538 | + * @param[in] resource Resource for the notification |
| 539 | + * |
| 540 | + * @return GCOAP_OBS_INIT_OK on success |
| 541 | + * @return GCOAP_OBS_INIT_ERR on error |
| 542 | + * @return GCOAP_OBS_INIT_UNUSED if no observer for resource |
| 543 | + */ |
| 544 | +int gcoap_obs_init(coap_pkt_t *pdu, uint8_t *buf, size_t len, |
| 545 | + const coap_resource_t *resource); |
| 546 | + |
| 547 | +/** |
| 548 | + * @brief Sends a buffer containing a CoAP Observe notification to the |
| 549 | + * observer registered for a resource. |
| 550 | + * |
| 551 | + * Assumes a single observer for a resource. |
| 552 | + * |
| 553 | + * @param[in] buf Buffer containing the PDU |
| 554 | + * @param[in] len Length of the buffer |
| 555 | + * @param[in] resource Resource to send |
| 556 | + * |
| 557 | + * @return length of the packet |
| 558 | + * @return 0 if cannot send |
| 559 | + */ |
| 560 | +size_t gcoap_obs_send(uint8_t *buf, size_t len, const coap_resource_t *resource); |
| 561 | + |
405 | 562 | /** |
406 | 563 | * @brief Provides important operational statistics. |
407 | 564 | * |
|
0 commit comments