1 /* 2 * Copyright (c) 2022 René Beckmann 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 7 /** @file mqtt_sn.h 8 * 9 * @brief MQTT-SN Client Implementation 10 * 11 * @details 12 * MQTT-SN Client's Application interface is defined in this header. 13 * Targets protocol version 1.2. 14 * 15 * @defgroup mqtt_sn_socket MQTT-SN Client library 16 * @since 3.3 17 * @version 0.1.0 18 * @ingroup networking 19 * @{ 20 */ 21 22 #ifndef ZEPHYR_INCLUDE_NET_MQTT_SN_H_ 23 #define ZEPHYR_INCLUDE_NET_MQTT_SN_H_ 24 25 #include <stddef.h> 26 27 #include <zephyr/net_buf.h> 28 #include <zephyr/types.h> 29 30 #include <sys/types.h> 31 32 #ifdef CONFIG_MQTT_SN_TRANSPORT_UDP 33 #include <zephyr/net/net_ip.h> 34 #endif 35 36 #ifdef __cplusplus 37 extern "C" { 38 #endif 39 40 /** 41 * Quality of Service. QoS 0-2 work the same as basic MQTT, QoS -1 is an MQTT-SN addition. 42 * QOS -1 is not supported yet. 43 */ 44 enum mqtt_sn_qos { 45 MQTT_SN_QOS_0, /**< QOS 0 */ 46 MQTT_SN_QOS_1, /**< QOS 1 */ 47 MQTT_SN_QOS_2, /**< QOS 2 */ 48 MQTT_SN_QOS_M1 /**< QOS -1 */ 49 }; 50 51 /** 52 * MQTT-SN topic types. 53 */ 54 enum mqtt_sn_topic_type { 55 /** 56 * Normal topic. 57 * It allows usage of any valid UTF-8 string as a topic name. 58 */ 59 MQTT_SN_TOPIC_TYPE_NORMAL, 60 /** 61 * Pre-defined topic. 62 * It allows usage of a two-byte identifier representing a topic name for 63 * which the corresponding topic name is known in advance by both the client 64 * and the gateway/server. 65 */ 66 MQTT_SN_TOPIC_TYPE_PREDEF, 67 /** 68 * Short topic. 69 * It allows usage of a two-byte string as a topic name. 70 */ 71 MQTT_SN_TOPIC_TYPE_SHORT 72 }; 73 74 /** 75 * MQTT-SN return codes. 76 */ 77 enum mqtt_sn_return_code { 78 MQTT_SN_CODE_ACCEPTED = 0x00, /**< Accepted */ 79 MQTT_SN_CODE_REJECTED_CONGESTION = 0x01, /**< Rejected: congestion */ 80 MQTT_SN_CODE_REJECTED_TOPIC_ID = 0x02, /**< Rejected: Invalid Topic ID */ 81 MQTT_SN_CODE_REJECTED_NOTSUP = 0x03, /**< Rejected: Not Supported */ 82 }; 83 84 /** @brief Abstracts memory buffers. */ 85 struct mqtt_sn_data { 86 const uint8_t *data; /**< Pointer to data. */ 87 size_t size; /**< Size of data, in bytes. */ 88 }; 89 90 /** 91 * @brief Initialize memory buffer from C literal string. 92 * 93 * Use it as follows: 94 * 95 * struct mqtt_sn_data topic = MQTT_SN_DATA_STRING_LITERAL("/zephyr"); 96 * 97 * @param[in] literal Literal string from which to generate mqtt_sn_data object. 98 */ 99 #define MQTT_SN_DATA_STRING_LITERAL(literal) ((struct mqtt_sn_data){literal, sizeof(literal) - 1}) 100 101 /** 102 * @brief Initialize memory buffer from single bytes. 103 * 104 * Use it as follows: 105 * 106 * struct mqtt_sn_data data = MQTT_SN_DATA_BYTES(0x13, 0x37); 107 */ 108 #define MQTT_SN_DATA_BYTES(...) \ 109 ((struct mqtt_sn_data){(uint8_t[]){__VA_ARGS__}, sizeof((uint8_t[]){__VA_ARGS__})}) 110 111 /** 112 * Event types that can be emitted by the library. 113 */ 114 enum mqtt_sn_evt_type { 115 MQTT_SN_EVT_CONNECTED, /**< Connected to a gateway */ 116 MQTT_SN_EVT_DISCONNECTED, /**< Disconnected */ 117 MQTT_SN_EVT_ASLEEP, /**< Entered ASLEEP state */ 118 MQTT_SN_EVT_AWAKE, /**< Entered AWAKE state */ 119 MQTT_SN_EVT_PUBLISH, /**< Received a PUBLISH message */ 120 MQTT_SN_EVT_PINGRESP, /**< Received a PINGRESP */ 121 MQTT_SN_EVT_ADVERTISE, /**< Received a ADVERTISE */ 122 MQTT_SN_EVT_GWINFO, /**< Received a GWINFO */ 123 MQTT_SN_EVT_SEARCHGW /**< Received a SEARCHGW */ 124 }; 125 126 /** 127 * Event metadata. 128 */ 129 union mqtt_sn_evt_param { 130 /** Structure holding publish event details */ 131 struct { 132 /** The payload data associated with the event */ 133 struct mqtt_sn_data data; 134 /** The type of topic for the event */ 135 enum mqtt_sn_topic_type topic_type; 136 /** The identifier for the topic of the event */ 137 uint16_t topic_id; 138 } publish; 139 }; 140 141 /** 142 * MQTT-SN event structure to be handled by the event callback. 143 */ 144 struct mqtt_sn_evt { 145 /** Event type */ 146 enum mqtt_sn_evt_type type; 147 /** Event parameters */ 148 union mqtt_sn_evt_param param; 149 }; 150 151 struct mqtt_sn_client; 152 153 /** 154 * @brief Asynchronous event notification callback registered by the 155 * application. 156 * 157 * @param[in] client Identifies the client for which the event is notified. 158 * @param[in] evt Event description along with result and associated 159 * parameters (if any). 160 */ 161 typedef void (*mqtt_sn_evt_cb_t)(struct mqtt_sn_client *client, const struct mqtt_sn_evt *evt); 162 163 /** 164 * @brief Structure to describe an MQTT-SN transport. 165 * 166 * MQTT-SN does not require transports to be reliable or to hold a connection. 167 * Transports just need to be frame-based, so you can use UDP, ZigBee, or even 168 * a simple UART, given some kind of framing protocol is used. 169 */ 170 struct mqtt_sn_transport { 171 /** 172 * @brief Will be called once on client init to initialize the transport. 173 * 174 * Use this to open sockets or similar. May be NULL. 175 */ 176 int (*init)(struct mqtt_sn_transport *transport); 177 178 /** 179 * @brief Will be called on client deinit 180 * 181 * Use this to close sockets or similar. May be NULL. 182 */ 183 void (*deinit)(struct mqtt_sn_transport *transport); 184 185 /** 186 * @brief Will be called by the library when it wants to send a message. 187 * 188 * Implementations should follow sendto conventions with exceptions. 189 * When dest_addr == NULL, message should be broadcast with addrlen being 190 * the broadcast radius. This should also handle setting up/destroying 191 * connections as required when the address changes. 192 * 193 * @return ENOERR on connection+transmission success, Negative values 194 * signal errors. 195 */ 196 int (*sendto)(struct mqtt_sn_client *client, void *buf, size_t sz, const void *dest_addr, 197 size_t addrlen); 198 199 /** 200 * @brief Will be called by the library when it wants to receive a message. 201 * 202 * Implementations should follow recvfrom conventions with the exception 203 * of a NULL src_addr being a broadcast message. 204 */ 205 ssize_t (*recvfrom)(struct mqtt_sn_client *client, void *rx_buf, size_t rx_len, 206 void *src_addr, size_t *addrlen); 207 208 /** 209 * @brief Check if incoming data is available. 210 * 211 * If poll() returns a positive number, recv must not block. 212 * 213 * May be NULL, but recv should not block then either. 214 * 215 * @return Positive number if data is available, or zero if there is none. 216 * Negative values signal errors. 217 */ 218 int (*poll)(struct mqtt_sn_client *client); 219 }; 220 221 #ifdef CONFIG_MQTT_SN_TRANSPORT_UDP 222 /** 223 * Transport struct for UDP based transport. 224 */ 225 struct mqtt_sn_transport_udp { 226 /** Parent struct */ 227 struct mqtt_sn_transport tp; 228 229 /** Socket FD */ 230 int sock; 231 232 /** Address of broadcasts */ 233 struct sockaddr bcaddr; 234 socklen_t bcaddrlen; 235 }; 236 237 #define UDP_TRANSPORT(transport) CONTAINER_OF(transport, struct mqtt_sn_transport_udp, tp) 238 239 /** 240 * @brief Initialize the UDP transport. 241 * 242 * @param[in] udp The transport to be initialized 243 * @param[in] gwaddr Pre-initialized gateway address 244 * @param[in] addrlen Size of the gwaddr structure. 245 */ 246 int mqtt_sn_transport_udp_init(struct mqtt_sn_transport_udp *udp, struct sockaddr *gwaddr, 247 socklen_t addrlen); 248 #endif 249 250 /** 251 * Structure describing an MQTT-SN client. 252 */ 253 struct mqtt_sn_client { 254 /** 1-23 character unique client ID */ 255 struct mqtt_sn_data client_id; 256 257 /** Topic for Will message. 258 * Must be initialized before connecting with will=true 259 */ 260 struct mqtt_sn_data will_topic; 261 262 /** Will message. 263 * Must be initialized before connecting with will=true 264 */ 265 struct mqtt_sn_data will_msg; 266 267 /** Quality of Service for the Will message */ 268 enum mqtt_sn_qos will_qos; 269 270 /** Flag indicating if the will message should be retained by the broker */ 271 bool will_retain; 272 273 /** Underlying transport to be used by the client */ 274 struct mqtt_sn_transport *transport; 275 276 /** Buffer for outgoing data */ 277 struct net_buf_simple tx; 278 279 /** Buffer for incoming data */ 280 struct net_buf_simple rx; 281 282 /** Buffer for incoming data sender address */ 283 struct net_buf_simple rx_addr; 284 285 /** Event callback */ 286 mqtt_sn_evt_cb_t evt_cb; 287 288 /** Message ID for the next message to be sent */ 289 uint16_t next_msg_id; 290 291 /** List of pending publish messages */ 292 sys_slist_t publish; 293 294 /** List of registered topics */ 295 sys_slist_t topic; 296 297 /** List of found gateways */ 298 sys_slist_t gateway; 299 300 /** Current state of the MQTT-SN client */ 301 int state; 302 303 /** Timestamp of the last ping request */ 304 int64_t last_ping; 305 306 /** Number of retries for failed ping attempts */ 307 uint8_t ping_retries; 308 309 /** Timestamp of the next SEARCHGW transmission */ 310 int64_t ts_searchgw; 311 312 /** Timestamp of the next GWINFO transmission */ 313 int64_t ts_gwinfo; 314 315 /** Radius of the next GWINFO transmission */ 316 int64_t radius_gwinfo; 317 318 /** Delayable work structure for processing MQTT-SN events */ 319 struct k_work_delayable process_work; 320 }; 321 322 /** 323 * @brief Initialize a client. 324 * 325 * @param client The MQTT-SN client to initialize. 326 * @param client_id The ID to be used by the client. 327 * @param transport The transport to be used by the client. 328 * @param evt_cb The event callback function for the client. 329 * @param tx Pointer to the transmit buffer. 330 * @param txsz Size of the transmit buffer. 331 * @param rx Pointer to the receive buffer. 332 * @param rxsz Size of the receive buffer. 333 * 334 * @return 0 or a negative error code (errno.h) indicating reason of failure. 335 */ 336 int mqtt_sn_client_init(struct mqtt_sn_client *client, const struct mqtt_sn_data *client_id, 337 struct mqtt_sn_transport *transport, mqtt_sn_evt_cb_t evt_cb, void *tx, 338 size_t txsz, void *rx, size_t rxsz); 339 340 /** 341 * @brief Deinitialize the client. 342 * 343 * This removes all topics and publishes, and also de-inits the transport. 344 * 345 * @param client The MQTT-SN client to deinitialize. 346 */ 347 void mqtt_sn_client_deinit(struct mqtt_sn_client *client); 348 349 /** 350 * @brief Manually add a Gateway, bypasing the normal search process. 351 * 352 * This function manually creates a gateway that is stored internal to the library. 353 * 354 * @param client The MQTT-SN client to connect. 355 * @param gw_id Single byte Gateway Identifier 356 * @param gw_addr Address data structure to be used by the transport layer. 357 * 358 * @return 0 or a negative error code (errno.h) indicating reason of failure. 359 */ 360 int mqtt_sn_add_gw(struct mqtt_sn_client *client, uint8_t gw_id, struct mqtt_sn_data gw_addr); 361 362 /** 363 * @brief Initiate the MQTT-SN GW Search process. 364 * 365 * @param client The MQTT-SN client to connect. 366 * @param radius Broadcast radius for the search message. 367 * 368 * @return 0 or a negative error code (errno.h) indicating reason of failure. 369 */ 370 int mqtt_sn_search(struct mqtt_sn_client *client, uint8_t radius); 371 372 /** 373 * @brief Connect the client. 374 * 375 * @param client The MQTT-SN client to connect. 376 * @param will Flag indicating if a Will message should be sent. 377 * @param clean_session Flag indicating if a clean session should be started. 378 * 379 * @return 0 or a negative error code (errno.h) indicating reason of failure. 380 */ 381 int mqtt_sn_connect(struct mqtt_sn_client *client, bool will, bool clean_session); 382 383 /** 384 * @brief Disconnect the client. 385 * 386 * @param client The MQTT-SN client to disconnect. 387 * 388 * @return 0 or a negative error code (errno.h) indicating reason of failure. 389 */ 390 int mqtt_sn_disconnect(struct mqtt_sn_client *client); 391 392 /** 393 * @brief Set the client into sleep state. 394 * 395 * @param client The MQTT-SN client to be put to sleep. 396 * @param duration Sleep duration (in seconds). 397 * 398 * @return 0 on success, negative errno code on failure. 399 */ 400 int mqtt_sn_sleep(struct mqtt_sn_client *client, uint16_t duration); 401 402 /** 403 * @brief Subscribe to a given topic. 404 * 405 * @param client The MQTT-SN client that should subscribe. 406 * @param qos The desired quality of service for the subscription. 407 * @param topic_name The name of the topic to subscribe to. 408 * 409 * @return 0 or a negative error code (errno.h) indicating reason of failure. 410 */ 411 int mqtt_sn_subscribe(struct mqtt_sn_client *client, enum mqtt_sn_qos qos, 412 struct mqtt_sn_data *topic_name); 413 414 /** 415 * @brief Unsubscribe from a topic. 416 * 417 * @param client The MQTT-SN client that should unsubscribe. 418 * @param qos The quality of service used when subscribing. 419 * @param topic_name The name of the topic to unsubscribe from. 420 * 421 * @return 0 or a negative error code (errno.h) indicating reason of failure. 422 */ 423 int mqtt_sn_unsubscribe(struct mqtt_sn_client *client, enum mqtt_sn_qos qos, 424 struct mqtt_sn_data *topic_name); 425 426 /** 427 * @brief Publish a value. 428 * 429 * If the topic is not yet registered with the gateway, the library takes care of it. 430 * 431 * @param client The MQTT-SN client that should publish. 432 * @param qos The desired quality of service for the publish. 433 * @param topic_name The name of the topic to publish to. 434 * @param retain Flag indicating if the message should be retained by the broker. 435 * @param data The data to be published. 436 * 437 * @return 0 or a negative error code (errno.h) indicating reason of failure. 438 */ 439 int mqtt_sn_publish(struct mqtt_sn_client *client, enum mqtt_sn_qos qos, 440 struct mqtt_sn_data *topic_name, bool retain, struct mqtt_sn_data *data); 441 442 /** 443 * @brief Check the transport for new incoming data. 444 * 445 * Call this function periodically, or if you have good reason to believe there is any data. 446 * If the client's transport struct contains a poll-function, this function is non-blocking. 447 * 448 * @param client The MQTT-SN client to check for incoming data. 449 * 450 * @return 0 or a negative error code (errno.h) indicating reason of failure. 451 */ 452 int mqtt_sn_input(struct mqtt_sn_client *client); 453 454 /** 455 * @brief Get topic name by topic ID. 456 * 457 * @param[in] client The MQTT-SN client that uses this topic. 458 * @param[in] id Topic identifier. 459 * @param[out] topic_name Will be assigned to topic name. 460 * 461 * @return 0 on success, -ENOENT if topic ID doesn't exist, 462 * or -EINVAL on invalid arguments. 463 */ 464 int mqtt_sn_get_topic_name(struct mqtt_sn_client *client, uint16_t id, 465 struct mqtt_sn_data *topic_name); 466 467 #ifdef __cplusplus 468 } 469 #endif 470 471 #endif /* ZEPHYR_INCLUDE_NET_MQTT_SN_H_ */ 472 473 /**@} */ 474