blob: cb1a6d3373123a19cbde67ed258370ecf52b0999 [file] [log] [blame]
/*
* Copyright (c) 2022 René Beckmann
*
* SPDX-License-Identifier: Apache-2.0
*/
/** @file mqtt_sn.h
*
* @defgroup mqtt_sn_socket MQTT-SN Client library
* @ingroup networking
* @{
* @brief MQTT-SN Client Implementation
*
* @details
* MQTT-SN Client's Application interface is defined in this header.
* Targets protocol version 1.2.
*
*/
#ifndef ZEPHYR_INCLUDE_NET_MQTT_SN_H_
#define ZEPHYR_INCLUDE_NET_MQTT_SN_H_
#include <stddef.h>
#include <zephyr/net/buf.h>
#include <zephyr/types.h>
#include <sys/types.h>
#ifdef CONFIG_MQTT_SN_TRANSPORT_UDP
#include <zephyr/net/net_ip.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
/**
* Quality of Service. QoS 0-2 work the same as basic MQTT, QoS -1 is an MQTT-SN addition.
* QOS -1 is not supported yet.
*/
enum mqtt_sn_qos {
MQTT_SN_QOS_0, /**< QOS 0 */
MQTT_SN_QOS_1, /**< QOS 1 */
MQTT_SN_QOS_2, /**< QOS 2 */
MQTT_SN_QOS_M1 /**< QOS -1 */
};
/**
* MQTT-SN topic types.
*/
enum mqtt_sn_topic_type {
/**
* Normal topic.
* It allows usage of any valid UTF-8 string as a topic name.
*/
MQTT_SN_TOPIC_TYPE_NORMAL,
/**
* Pre-defined topic.
* It allows usage of a two-byte identifier representing a topic name for
* which the corresponding topic name is known in advance by both the client
* and the gateway/server.
*/
MQTT_SN_TOPIC_TYPE_PREDEF,
/**
* Short topic.
* It allows usage of a two-byte string as a topic name.
*/
MQTT_SN_TOPIC_TYPE_SHORT
};
/**
* MQTT-SN return codes.
*/
enum mqtt_sn_return_code {
MQTT_SN_CODE_ACCEPTED = 0x00, /**< Accepted */
MQTT_SN_CODE_REJECTED_CONGESTION = 0x01, /**< Rejected: congestion */
MQTT_SN_CODE_REJECTED_TOPIC_ID = 0x02, /**< Rejected: Invalid Topic ID */
MQTT_SN_CODE_REJECTED_NOTSUP = 0x03, /**< Rejected: Not Supported */
};
/** @brief Abstracts memory buffers. */
struct mqtt_sn_data {
const uint8_t *data; /**< Pointer to data. */
uint16_t size; /**< Size of data, in bytes. */
};
/**
* @brief Initialize memory buffer from C literal string.
*
* Use it as follows:
*
* struct mqtt_sn_data topic = MQTT_SN_DATA_STRING_LITERAL("/zephyr");
*
* @param[in] literal Literal string from which to generate mqtt_sn_data object.
*/
#define MQTT_SN_DATA_STRING_LITERAL(literal) ((struct mqtt_sn_data){literal, sizeof(literal) - 1})
/**
* @brief Initialize memory buffer from single bytes.
*
* Use it as follows:
*
* struct mqtt_sn_data data = MQTT_SN_DATA_BYTES(0x13, 0x37);
*/
#define MQTT_SN_DATA_BYTES(...) \
((struct mqtt_sn_data) { (uint8_t[]){ __VA_ARGS__ }, sizeof((uint8_t[]){ __VA_ARGS__ })})
/**
* Event types that can be emitted by the library.
*/
enum mqtt_sn_evt_type {
MQTT_SN_EVT_CONNECTED, /**< Connected to a gateway */
MQTT_SN_EVT_DISCONNECTED, /**< Disconnected */
MQTT_SN_EVT_ASLEEP, /**< Entered ASLEEP state */
MQTT_SN_EVT_AWAKE, /**< Entered AWAKE state */
MQTT_SN_EVT_PUBLISH, /**< Received a PUBLISH message */
MQTT_SN_EVT_PINGRESP /**< Received a PINGRESP */
};
/**
* Event metadata.
*/
union mqtt_sn_evt_param {
/** Structure holding publish event details */
struct {
/** The payload data associated with the event */
struct mqtt_sn_data data;
/** The type of topic for the event */
enum mqtt_sn_topic_type topic_type;
/** The identifier for the topic of the event */
uint16_t topic_id;
} publish;
};
/**
* MQTT-SN event structure to be handled by the event callback.
*/
struct mqtt_sn_evt {
/** Event type */
enum mqtt_sn_evt_type type;
/** Event parameters */
union mqtt_sn_evt_param param;
};
struct mqtt_sn_client;
/**
* @brief Asynchronous event notification callback registered by the
* application.
*
* @param[in] client Identifies the client for which the event is notified.
* @param[in] evt Event description along with result and associated
* parameters (if any).
*/
typedef void (*mqtt_sn_evt_cb_t)(struct mqtt_sn_client *client, const struct mqtt_sn_evt *evt);
/**
* @brief Structure to describe an MQTT-SN transport.
*
* MQTT-SN does not require transports to be reliable or to hold a connection.
* Transports just need to be frame-based, so you can use UDP, ZigBee, or even
* a simple UART, given some kind of framing protocol is used.
*/
struct mqtt_sn_transport {
/**
* @brief Will be called once on client init to initialize the transport.
*
* Use this to open sockets or similar. May be NULL.
*/
int (*init)(struct mqtt_sn_transport *transport);
/**
* @brief Will be called on client deinit
*
* Use this to close sockets or similar. May be NULL.
*/
void (*deinit)(struct mqtt_sn_transport *transport);
/**
* Will be called by the library when it wants to send a message.
*/
int (*msg_send)(struct mqtt_sn_client *client, void *buf, size_t sz);
/**
* @brief Will be called by the library when it wants to receive a message.
*
* Implementations should follow recv conventions.
*/
ssize_t (*recv)(struct mqtt_sn_client *client, void *buffer, size_t length);
/**
* @brief Check if incoming data is available.
*
* If poll() returns a positive number, recv must not block.
*
* May be NULL, but recv should not block then either.
*
* @return Positive number if data is available, or zero if there is none.
* Negative values signal errors.
*/
int (*poll)(struct mqtt_sn_client *client);
};
#ifdef CONFIG_MQTT_SN_TRANSPORT_UDP
/**
* Transport struct for UDP based transport.
*/
struct mqtt_sn_transport_udp {
/** Parent struct */
struct mqtt_sn_transport tp;
/** Socket FD */
int sock;
/** Address of the gateway */
struct sockaddr gwaddr;
socklen_t gwaddrlen;
};
#define UDP_TRANSPORT(transport) CONTAINER_OF(transport, struct mqtt_sn_transport_udp, tp)
/**
* @brief Initialize the UDP transport.
*
* @param[in] udp The transport to be initialized
* @param[in] gwaddr Pre-initialized gateway address
* @param[in] addrlen Size of the gwaddr structure.
*/
int mqtt_sn_transport_udp_init(struct mqtt_sn_transport_udp *udp, struct sockaddr *gwaddr,
socklen_t addrlen);
#endif
/**
* Structure describing an MQTT-SN client.
*/
struct mqtt_sn_client {
/** 1-23 character unique client ID */
struct mqtt_sn_data client_id;
/** Topic for Will message.
* Must be initialized before connecting with will=true
*/
struct mqtt_sn_data will_topic;
/** Will message.
* Must be initialized before connecting with will=true
*/
struct mqtt_sn_data will_msg;
/** Quality of Service for the Will message */
enum mqtt_sn_qos will_qos;
/** Flag indicating if the will message should be retained by the broker */
bool will_retain;
/** Underlying transport to be used by the client */
struct mqtt_sn_transport *transport;
/** Buffer for outgoing data */
struct net_buf_simple tx;
/** Buffer for incoming data */
struct net_buf_simple rx;
/** Event callback */
mqtt_sn_evt_cb_t evt_cb;
/** Message ID for the next message to be sent */
uint16_t next_msg_id;
/** List of pending publish messages */
sys_slist_t publish;
/** List of registered topics */
sys_slist_t topic;
/** Current state of the MQTT-SN client */
int state;
/** Timestamp of the last ping request */
int64_t last_ping;
/** Number of retries for failed ping attempts */
uint8_t ping_retries;
/** Delayable work structure for processing MQTT-SN events */
struct k_work_delayable process_work;
};
/**
* @brief Initialize a client.
*
* @param client The MQTT-SN client to initialize.
* @param client_id The ID to be used by the client.
* @param transport The transport to be used by the client.
* @param evt_cb The event callback function for the client.
* @param tx Pointer to the transmit buffer.
* @param txsz Size of the transmit buffer.
* @param rx Pointer to the receive buffer.
* @param rxsz Size of the receive buffer.
*
* @return 0 or a negative error code (errno.h) indicating reason of failure.
*/
int mqtt_sn_client_init(struct mqtt_sn_client *client, const struct mqtt_sn_data *client_id,
struct mqtt_sn_transport *transport, mqtt_sn_evt_cb_t evt_cb, void *tx,
size_t txsz, void *rx, size_t rxsz);
/**
* @brief Deinitialize the client.
*
* This removes all topics and publishes, and also de-inits the transport.
*
* @param client The MQTT-SN client to deinitialize.
*/
void mqtt_sn_client_deinit(struct mqtt_sn_client *client);
/**
* @brief Connect the client.
*
* @param client The MQTT-SN client to connect.
* @param will Flag indicating if a Will message should be sent.
* @param clean_session Flag indicating if a clean session should be started.
*
* @return 0 or a negative error code (errno.h) indicating reason of failure.
*/
int mqtt_sn_connect(struct mqtt_sn_client *client, bool will, bool clean_session);
/**
* @brief Disconnect the client.
*
* @param client The MQTT-SN client to disconnect.
*
* @return 0 or a negative error code (errno.h) indicating reason of failure.
*/
int mqtt_sn_disconnect(struct mqtt_sn_client *client);
/**
* @brief Set the client into sleep state.
*
* @param client The MQTT-SN client to be put to sleep.
* @param duration Sleep duration (in seconds).
*
* @return 0 on success, negative errno code on failure.
*/
int mqtt_sn_sleep(struct mqtt_sn_client *client, uint16_t duration);
/**
* @brief Subscribe to a given topic.
*
* @param client The MQTT-SN client that should subscribe.
* @param qos The desired quality of service for the subscription.
* @param topic_name The name of the topic to subscribe to.
*
* @return 0 or a negative error code (errno.h) indicating reason of failure.
*/
int mqtt_sn_subscribe(struct mqtt_sn_client *client, enum mqtt_sn_qos qos,
struct mqtt_sn_data *topic_name);
/**
* @brief Unsubscribe from a topic.
*
* @param client The MQTT-SN client that should unsubscribe.
* @param qos The quality of service used when subscribing.
* @param topic_name The name of the topic to unsubscribe from.
*
* @return 0 or a negative error code (errno.h) indicating reason of failure.
*/
int mqtt_sn_unsubscribe(struct mqtt_sn_client *client, enum mqtt_sn_qos qos,
struct mqtt_sn_data *topic_name);
/**
* @brief Publish a value.
*
* If the topic is not yet registered with the gateway, the library takes care of it.
*
* @param client The MQTT-SN client that should publish.
* @param qos The desired quality of service for the publish.
* @param topic_name The name of the topic to publish to.
* @param retain Flag indicating if the message should be retained by the broker.
* @param data The data to be published.
*
* @return 0 or a negative error code (errno.h) indicating reason of failure.
*/
int mqtt_sn_publish(struct mqtt_sn_client *client, enum mqtt_sn_qos qos,
struct mqtt_sn_data *topic_name, bool retain, struct mqtt_sn_data *data);
/**
* @brief Check the transport for new incoming data.
*
* Call this function periodically, or if you have good reason to believe there is any data.
* If the client's transport struct contains a poll-function, this function is non-blocking.
*
* @param client The MQTT-SN client to check for incoming data.
*
* @return 0 or a negative error code (errno.h) indicating reason of failure.
*/
int mqtt_sn_input(struct mqtt_sn_client *client);
/**
* @brief Get topic name by topic ID.
*
* @param[in] client The MQTT-SN client that uses this topic.
* @param[in] id Topic identifier.
* @param[out] topic_name Will be assigned to topic name.
*
* @return 0 on success, -ENOENT if topic ID doesn't exist,
* or -EINVAL on invalid arguments.
*/
int mqtt_sn_get_topic_name(struct mqtt_sn_client *client, uint16_t id,
struct mqtt_sn_data *topic_name);
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_NET_MQTT_SN_H_ */
/**@} */