You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

384 lines
13 KiB

/*
* 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 */
/** @} */