emcute.h
13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
/*
* Copyright (C) 2017 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup net_emcute MQTT-SN Client (emCute)
* @ingroup net
* @brief emCute, the MQTT-SN implementation for RIOT
*
* # About
* emCute is the implementation of the OASIS MQTT-SN protocol for RIOT. It is
* designed with a focus on small memory footprint and usability.
*
*
* # Design Decisions and Restrictions
* * emCute is designed to run on top of UDP only, making use of
* @ref net_sock_udp. The design is not intended to be used with any other
* transport.
*
* The implementation is based on a 2-thread model: emCute needs one thread of
* its own, in which receiving of packets and sending of ping messages are
* handled. All 'user space functions' have to run from (a) different (i.e.
* user) thread(s). emCute uses thread flags to synchronize between threads.
*
* Further know restrictions are:
* - ASCII topic names only (no support for UTF8 names, yet)
* - topic length is restricted to fit in a single length byte (248 byte max)
* - no support for wildcards in topic names. This feature requires more
* elaborate internal memory management, supposedly at the cost of quite
* increased ROM and RAM usage
* - no retransmit when receiving a REJ_CONG (reject, reason congestion). when
* getting a REJ_CONG (reject, reason congestion), the spec tells us to resend
* the original message after T_WAIT (default: >5min). This is not supported,
* as this would require to block to calling thread (or keep state) for long
* periods of time and is (in Hauke's opinion) not feasible for constrained
* nodes.
*
*
* # Error Handling
* This implementation tries minimize parameter checks to a minimum, checking as
* many parameters as feasible using assertions. For the sake of run-time
* stability and usability, typical overflow checks are always done during run-
* time and explicit error values returned in case of errors.
*
*
* # Implementation state
* In the current state, emCute supports:
* - connecting to a gateway
* - disconnecting from gateway
* - registering a last will topic and message during connection setup
* - registering topic names with the gateway (obtaining topic IDs)
* - subscribing to topics
* - unsubscribing from topics
* - updating will topic
* - updating will message
* - sending out periodic PINGREQ messages
* - handling re-transmits
*
* The following features are however still missing (but planned):
* @todo Gateway discovery (so far there is no support for handling
* ADVERTISE, GWINFO, and SEARCHGW). Open question to answer here:
* how to put / how to encode the IPv(4/6) address AND the port of
* a gateway in the GwAdd field of the GWINFO message
* @todo QOS level 2
* @todo put the node to sleep (send DISCONNECT with duration field set)
* @todo handle DISCONNECT messages initiated by the broker/gateway
* @todo support for pre-defined and short topic IDs
* @todo handle (previously) active subscriptions on reconnect/disconnect
* @todo handle re-connect/disconnect from unresponsive gateway (in case
* a number of ping requests are unanswered)
* @todo react only to incoming ping requests that are actually send by
* the gateway we are connected to
*
* @{
* @file
* @brief emCute MQTT-SN interface definition
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/
#ifndef NET_EMCUTE_H
#define NET_EMCUTE_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "net/sock/udp.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef EMCUTE_DEFAULT_PORT
/**
* @brief Default UDP port to listen on (also used as SRC port)
*/
#define EMCUTE_DEFAULT_PORT (1883U)
#endif
#ifndef EMCUTE_BUFSIZE
/**
* @brief Buffer size used for emCute's transmit and receive buffers
*
* @note The buffer size MUST be less than 32768 on 16-bit and 8-bit
* platforms to prevent buffer overflows.
*
* The overall buffer size used by emCute is this value time two (Rx + Tx).
*/
#define EMCUTE_BUFSIZE (512U)
#endif
#ifndef EMCUTE_ID_MAXLEN
/**
* @brief Maximum client ID length
*
* @note **Must** be less than (256 - 6) AND less than
* (@ref EMCUTE_BUFSIZE - 6).
*/
#define EMCUTE_ID_MAXLEN (196U)
#endif
#ifndef EMCUTE_TOPIC_MAXLEN
/**
* @brief Maximum topic length
*
* @note **Must** be less than (256 - 6) AND less than
* (@ref EMCUTE_BUFSIZE - 6).
*/
#define EMCUTE_TOPIC_MAXLEN (196U)
#endif
#ifndef EMCUTE_KEEPALIVE
/**
* @brief Keep-alive interval [in s]
*
* The node will communicate this interval to the gateway send a ping message
* every time when this amount of time has passed.
*
* For the default value, see spec v1.2, section 7.2 -> T_WAIT: > 5 min
*/
#define EMCUTE_KEEPALIVE (360) /* -> 6 min*/
#endif
#ifndef EMCUTE_T_RETRY
/**
* @brief Re-send interval [in seconds]
*
* For the default value, see spec v1.2, section 7.2 -> T_RETRY: 10 to 15 sec
*/
#define EMCUTE_T_RETRY (15U) /* -> 15 sec */
#endif
#ifndef EMCUTE_N_RETRY
/**
* @brief Number of retries when sending packets
*
* For the default value, see spec v1.2, section 7.2 -> N_RETRY: 3-5
*/
#define EMCUTE_N_RETRY (3U)
#endif
/**
* @brief MQTT-SN flags
*
* All MQTT-SN functions only support a sub-set of the available flags. It is up
* to the user to only supply valid/supported flags to a function. emCute will
* trigger assertion fails on the use of unsupported flags (if compiled with
* DEVELHELP).
*
* Refer to the MQTT-SN spec section 5.3.4 for further information.
*/
enum {
EMCUTE_DUP = 0x80, /**< duplicate flag */
EMCUTE_QOS_MASK = 0x60, /**< QoS level mask */
EMCUTE_QOS_2 = 0x40, /**< QoS level 2 */
EMCUTE_QOS_1 = 0x20, /**< QoS level 1 */
EMCUTE_QOS_0 = 0x00, /**< QoS level 0 */
EMCUTE_RETAIN = 0x10, /**< retain flag */
EMCUTE_WILL = 0x08, /**< will flag, used during CONNECT */
EMCUTE_CS = 0x04, /**< clean session flag */
EMCUTE_TIT_MASK = 0x03, /**< topic ID type mask */
EMCUTE_TIT_SHORT = 0x02, /**< topic ID: short */
EMCUTE_TIT_PREDEF = 0x01, /**< topic ID: pre-defined */
EMCUTE_TIT_NORMAL = 0x00 /**< topic ID: normal */
};
/**
* @brief Possible emCute return values
*/
enum {
EMCUTE_OK = 0, /**< everything went as expect */
EMCUTE_NOGW = -1, /**< error: not connected to a gateway */
EMCUTE_REJECT = -2, /**< error: operation was rejected by broker */
EMCUTE_OVERFLOW = -3, /**< error: ran out of buffer space */
EMCUTE_TIMEOUT = -4, /**< error: timeout */
EMCUTE_NOTSUP = -5 /**< error: feature not supported */
};
/**
* @brief MQTT-SN topic
*/
typedef struct {
const char *name; /**< topic string (currently ACSII only) */
uint16_t id; /**< topic id, as assigned by the gateway */
} emcute_topic_t;
/**
* @brief Signature for callbacks fired when publish messages are received
*
* @param[in] topic topic the received data was published on
* @param[in] data published data, can be NULL
* @param[in] len length of @p data in bytes
*/
typedef void(*emcute_cb_t)(const emcute_topic_t *topic, void *data, size_t len);
/**
* @brief Data-structure for keeping track of topics we register to
*/
typedef struct emcute_sub {
struct emcute_sub *next; /**< next subscription (saved in a list) */
emcute_topic_t topic; /**< topic we subscribe to */
emcute_cb_t cb; /**< function called when receiving messages */
void *arg; /**< optional custom argument */
} emcute_sub_t;
/**
* @brief Connect to a given MQTT-SN gateway (CONNECT)
*
* When called while already connected to a gateway, call emcute_discon() first
* to disconnect from the current gateway.
*
* @param[in] remote address and port of the target MQTT-SN gateway
* @param[in] clean set to true to start a clean session
* @param[in] will_topic last will topic name, no last will will be
* configured if set to NULL
* @param[in] will_msg last will message content, will be ignored if
* @p will_topic is set to NULL
* @param[in] will_msg_len length of @p will_msg in byte
* @param[in] flags flags used for the last will, allowed are retain and
* QoS
*
* @return EMCUTE_OK on success
* @return EMCUTE_NOGW if already connected to a gateway
* @return EMCUTE_REJECT on connection refused by gateway
* @return EMCUTE_TIMEOUT on connection timeout
*/
int emcute_con(sock_udp_ep_t *remote, bool clean, const char *will_topic,
const void *will_msg, size_t will_msg_len, unsigned flags);
/**
* @brief Disconnect from the gateway we are currently connected to
*
* @return EMCUTE_OK on success
* @return EMCUTE_GW if not connected to a gateway
* @return EMCUTE_TIMEOUT on response timeout
*/
int emcute_discon(void);
/**
* @brief Get a topic ID for the given topic name from the gateway
*
* @param[in,out] topic topic to register, topic.name **must not** be NULL
*
* @return EMCUTE_OK on success
* @return EMCUTE_NOGW if not connected to a gateway
* @return EMCUTE_OVERFLOW if length of topic name exceeds
* @ref EMCUTE_TOPIC_MAXLEN
* @return EMCUTE_TIMEOUT on connection timeout
*/
int emcute_reg(emcute_topic_t *topic);
/**
* @brief Publish data on the given topic
*
* @param[in] topic topic to send data to, topic **must** be registered
* (topic.id **must** populated).
* @param[in] buf data to publish
* @param[in] len length of @p data in bytes
* @param[in] flags flags used for publication, allowed are QoS and retain
*
* @return EMCUTE_OK on success
* @return EMCUTE_NOGW if not connected to a gateway
* @return EMCUTE_REJECT if publish message was rejected (QoS > 0 only)
* @return EMCUTE_OVERFLOW if length of data exceeds @ref EMCUTE_BUFSIZE
* @return EMCUTE_TIMEOUT on connection timeout (QoS > 0 only)
* @return EMCUTE_NOTSUP on unsupported flag values
*/
int emcute_pub(emcute_topic_t *topic, const void *buf, size_t len,
unsigned flags);
/**
* @brief Subscribe to the given topic
*
* When calling this function, @p sub->topic.name and @p sub->cb **must** be
* set.
*
* @param[in,out] sub subscription context, @p sub->topic.name and @p sub->cb
* **must** not be NULL.
* @param[in] flags flags used when subscribing, allowed are QoS, DUP, and
* topic ID type
*
* @return EMCUTE_OK on success
* @return EMCUTE_NOGW if not connected to a gateway
* @return EMCUTE_OVERFLOW if length of topic name exceeds
* @ref EMCUTE_TOPIC_MAXLEN
* @return EMCUTE_TIMEOUT on connection timeout
*/
int emcute_sub(emcute_sub_t *sub, unsigned flags);
/**
* @brief Unsubscripbe the given topic
*
* @param[in] sub subscription context
*
* @return EMCUTE_OK on success
* @return EMCUTE_NOGW if not connected to a gateway
* @return EMCUTE_TIMEOUT on connection timeout
*/
int emcute_unsub(emcute_sub_t *sub);
/**
* @brief Update the last will topic
*
* @param[in] topic new last will topic
* @param[in] flags flags used for the topic, allowed are QoS and retain
*
* @return EMCUTE_OK on success
* @return EMCUTE_NOGW if not connected to a gateway
* @return EMCUTE_OVERFLOW if length of topic name exceeds
* @ref EMCUTE_TOPIC_MAXLEN
* @return EMCUTE_REJECT on rejection by the gateway
* @return EMCUTE_TIMEOUT on response timeout
*/
int emcute_willupd_topic(const char *topic, unsigned flags);
/**
* @brief Update the last will message
*
* @param[in] data new message to send on last will
* @param[in] len length of @p data in bytes
*
* @return EMCUTE_OK on success
* @return EMCUTE_NOGW if not connected to a gateway
* @return EMCUTE_OVERFLOW if length of the given message exceeds
* @ref EMCUTE_BUFSIZE
* @return EMCUTE_REJECT on rejection by the gateway
* @return EMCUTE_TIMEOUT on response timeout
*/
int emcute_willupd_msg(const void *data, size_t len);
/**
* @brief Run emCute, will 'occupy' the calling thread
*
* This function will run the emCute message receiver. It will block the thread
* it is running in.
*
* @param[in] port UDP port used for listening (default: 1883)
* @param[in] id client ID (should be unique)
*/
void emcute_run(uint16_t port, const char *id);
/**
* @brief Return the string representation of the given type value
*
* This function is for debugging purposes.
*
* @param[in] type MQTT-SN message type
*
* @return string representation of the given type
* @return 'UNKNOWN' on invalid type value
*/
const char *emcute_type_str(uint8_t type);
#ifdef __cplusplus
}
#endif
#endif /* NET_EMCUTE_H */
/** @} */