Skip to content

Commit 7a1fcdf

Browse files
Merge pull request #6469 from kb2ma/gcoap/observe
gcoap: Observe extension server
2 parents 18bad15 + 3abff6d commit 7a1fcdf

File tree

5 files changed

+473
-23
lines changed

5 files changed

+473
-23
lines changed

examples/gcoap/README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ Build with `Makefile.slip`. Follow the setup instructions in README-slip.md, whi
1414
## Current Status
1515
gcoap includes server and client capability. Available features include:
1616

17+
* Message Type: Supports non-confirmable (NON) messaging. Additionally provides a callback on timeout.
18+
* Observe extension: Provides server-side registration and notifications.
1719
* Server and Client provide helper functions for writing the response/request. See the CoAP topic in the source documentation for details. See the gcoap example for sample implementations.
1820
* Server allows an application to register a 'listener', which includes an array of endpoint paths and function callbacks used to write a response.
1921
* Server listens on a port at startup; defaults to 5683.
20-
* Client operates asynchronously; sends request and then handles response in a user provided callback. Also executes callback on timeout.
22+
* Client operates asynchronously; sends request and then handles response in a user provided callback.
2123
* Client generates token; length defined at compile time.
22-
* Message Type: Supports non-confirmable (NON) messaging.
23-
* Options: Supports Content-Format for response payload.
24+
* Options: Supports Content-Format for payload.
2425

2526

2627
## Example Use

examples/gcoap/gcoap_cli.c

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
#include "od.h"
2727
#include "fmt.h"
2828

29+
#define ENABLE_DEBUG (0)
30+
#include "debug.h"
31+
2932
static void _resp_handler(unsigned req_state, coap_pkt_t* pdu);
3033
static ssize_t _stats_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len);
3134

@@ -149,15 +152,33 @@ int gcoap_cli_cmd(int argc, char **argv)
149152
argv[4]);
150153
}
151154
printf("gcoap_cli: sending msg ID %u, %u bytes\n", coap_get_id(&pdu),
152-
(unsigned) len);
155+
(unsigned) len);
153156
if (!_send(&buf[0], len, argv[2], argv[3])) {
154157
puts("gcoap_cli: msg send failed");
155158
}
159+
else {
160+
/* send Observe notification for /cli/stats */
161+
switch (gcoap_obs_init(&pdu, &buf[0], GCOAP_PDU_BUF_SIZE,
162+
&_resources[0])) {
163+
case GCOAP_OBS_INIT_OK:
164+
DEBUG("gcoap_cli: creating /cli/stats notification\n");
165+
size_t payload_len = fmt_u16_dec((char *)pdu.payload, req_count);
166+
len = gcoap_finish(&pdu, payload_len, COAP_FORMAT_TEXT);
167+
gcoap_obs_send(&buf[0], len, &_resources[0]);
168+
break;
169+
case GCOAP_OBS_INIT_UNUSED:
170+
DEBUG("gcoap_cli: no observer for /cli/stats\n");
171+
break;
172+
case GCOAP_OBS_INIT_ERR:
173+
DEBUG("gcoap_cli: error initializing /cli/stats notification\n");
174+
break;
175+
}
176+
}
156177
return 0;
157178
}
158179
else {
159180
printf("usage: %s <get|post|put> <addr> <port> <path> [data]\n",
160-
argv[0]);
181+
argv[0]);
161182
return 1;
162183
}
163184
}

pkg/nanocoap/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
PKG_NAME=nanocoap
22
PKG_URL=https://github.com/kaspar030/sock
3-
PKG_VERSION=4035239dce751216038bb2dc8632aa4a734d68a7
3+
PKG_VERSION=1bba8bb16463914266dd5a04fe2a3eac4ee129ae
44
PKG_LICENSE=LGPL-2.1
55

66
.PHONY: all

sys/include/net/gcoap.h

Lines changed: 163 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525
* port, which supports RFC 6282 compression. Internally, gcoap depends on the
2626
* nanocoap package for base level structs and functionality.
2727
*
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+
*
2832
* ## Server Operation ##
2933
*
3034
* gcoap listens for requests on GCOAP_PORT, 5683 by default. You can redefine
@@ -75,7 +79,7 @@
7579
*
7680
* ### Creating a request ###
7781
*
78-
* Here is the expected sequence for preparing and sending a request:
82+
* Here is the expected sequence to prepare and send a request:
7983
*
8084
* Allocate a buffer and a coap_pkt_t for the request.
8185
*
@@ -109,6 +113,43 @@
109113
* _content_type_ attributes.
110114
* -# Read the payload, if any.
111115
*
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+
*
112153
* ## Implementation Notes ##
113154
*
114155
* ### Building a packet ###
@@ -175,6 +216,13 @@ extern "C" {
175216
*/
176217
#define GCOAP_RESP_OPTIONS_BUF (8)
177218

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+
178226
/** @brief Maximum number of requests awaiting a response */
179227
#define GCOAP_REQ_WAITING_MAX (2)
180228

@@ -225,6 +273,69 @@ extern "C" {
225273
*/
226274
#define GCOAP_MSG_TYPE_INTR (0x1502)
227275

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+
228339
/**
229340
* @brief A modular collection of resources for a server
230341
*/
@@ -255,16 +366,29 @@ typedef struct {
255366
msg_t timeout_msg; /**< For response timer */
256367
} gcoap_request_memo_t;
257368

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+
258377
/**
259378
* @brief Container for the state of gcoap itself
260379
*/
261380
typedef struct {
262-
gcoap_listener_t *listeners; /**< List of registered listeners */
381+
gcoap_listener_t *listeners; /**< List of registered listeners */
263382
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 */
268392
} gcoap_state_t;
269393

270394
/**
@@ -402,6 +526,39 @@ static inline ssize_t gcoap_response(coap_pkt_t *pdu, uint8_t *buf, size_t len,
402526
: -1;
403527
}
404528

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+
405562
/**
406563
* @brief Provides important operational statistics.
407564
*

0 commit comments

Comments
 (0)