Skip to content

Commit aa9c922

Browse files
SpontaneousDucknashif
authored andcommitted
net: mqtt-sn: Add Gateway Advertisement and Discovery process support
Fixes: #78010 This commit implements the "Gateway Advertisement and Discovery" process defined in section 6.1 of the MQTT-SN specification. This includes breaking changes to the transport interface and the default included UDP interface implementation as support for UDP multicast messages is added as implemented by the Paho MQTT-SN Gateway. Signed-off-by: Kenneth Witham <[email protected]>
1 parent d716f54 commit aa9c922

File tree

6 files changed

+638
-99
lines changed

6 files changed

+638
-99
lines changed

doc/connectivity/networking/api/mqtt_sn.rst

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -84,17 +84,33 @@ used. An example configuration for UDP transport is shown below:
8484
8585
mqtt_sn_client_init(&client, &client_id, &tp.tp, evt_cb, tx_buf, sizeof(tx_buf), rx_buf, sizeof(rx_buf));
8686
87-
After the configuration is set up, the MQTT-SN client can connect to the gateway.
88-
While the MQTT-SN protocol offers functionality to discover gateways through an
89-
advertisement mechanism, this is not implemented yet in the library.
90-
91-
Call the ``mqtt_sn_connect`` function, which will send a ``CONNECT`` message.
92-
The application should periodically call the ``mqtt_sn_input`` function to process
93-
the response received. The application does not have to call ``mqtt_sn_input`` if it
94-
knows that no data has been received (e.g. when using Bluetooth). Note that
95-
``mqtt_sn_input`` is a non-blocking function, if the transport struct contains a
96-
``poll`` compatible function pointer.
97-
If the connection was successful, ``MQTT_SN_EVT_CONNECTED`` will be notified to the
87+
After the configuration is set up, the network address for the gateway to
88+
connect to must be defined. The MQTT-SN protocol offers functionality to
89+
discover gateways through an advertisement or a search mechanism. A user
90+
should do at least one of the following steps to define a Gateway for the library:
91+
92+
* Call the :c:func:`mqtt_sn_add_gw` function to manually define a Gateway address.
93+
* Wait for an :c:enumerator:`MQTT_SN_EVT_ADVERTISE`.
94+
* Call the :c:func:`mqtt_sn_search` function and wait for an :c:enumerator:`MQTT_SN_EVT_GWINFO` callback.
95+
Make sure to call the :c:func:`mqtt_sn_input` function periodically to process incoming messages.
96+
97+
Example :c:func:`mqtt_sn_search` function call:
98+
99+
.. code-block:: c
100+
101+
err = mqtt_sn_search(&mqtt_client, 1);
102+
k_sleep(K_SECONDS(10));
103+
err = mqtt_sn_input(&mqtt_client);
104+
__ASSERT(err == 0, "mqtt_sn_search() failed %d", err);
105+
106+
After the Gateway address has been defined or found, the MQTT-SN client can
107+
connect to the gateway. Call the :c:func:`mqtt_sn_connect` function, which will send a
108+
``CONNECT`` MQTT-SN message. The application should periodically call the :c:func:`mqtt_sn_input`
109+
function to process the response received. The application does not have to call
110+
:c:func:`mqtt_sn_input` if it knows that no data has been received (e.g. when using Bluetooth).
111+
Note that :c:func:`mqtt_sn_input` is a non-blocking function, if the transport struct contains a
112+
:c:func:`poll` compatible function pointer.
113+
If the connection was successful, :c:enumerator:`MQTT_SN_EVT_CONNECTED` will be notified to the
98114
application through the callback function.
99115

100116
.. code-block:: c
@@ -110,19 +126,19 @@ application through the callback function.
110126
k_sleep(K_MSEC(500));
111127
}
112128
113-
In the above code snippet, the event handler function should set the ``connected``
114-
flag upon a successful connection. If the connection fails at the MQTT level
115-
or a timeout occurs, the connection will be aborted.
129+
In the above code snippet, the gateway is connected to before publishing messages.
130+
If the connection fails at the MQTT level or a timeout occurs, the connection will be aborted and
131+
an error returned.
116132

117-
After the connection is established, an application needs to call ``mqtt_input``
133+
After the connection is established, an application needs to call :c:func:`mqtt_input`
118134
function periodically to process incoming data. Connection upkeep, on the other hand,
119135
is done automatically using a k_work item.
120136
If a MQTT message is received, an MQTT callback function will be called and an
121137
appropriate event notified.
122138

123-
The connection can be closed by calling the ``mqtt_sn_disconnect`` function. This
139+
The connection can be closed by calling the :c:func:`mqtt_sn_disconnect` function. This
124140
has no effect on the transport, however. If you want to close the transport (e.g.
125-
the socket), call ``mqtt_sn_client_deinit``, which will deinit the transport as well.
141+
the socket), call :c:func:`mqtt_sn_client_deinit`, which will deinit the transport as well.
126142

127143
Zephyr provides sample code utilizing the MQTT-SN client API. See
128144
:zephyr:code-sample:`mqtt-sn-publisher` for more information.
@@ -134,7 +150,6 @@ Certain parts of the protocol are not yet supported in the library.
134150

135151
* Pre-defined topic IDs
136152
* QoS -1 - it's most useful with predefined topics
137-
* Gateway discovery using ADVERTISE, SEARCHGW and GWINFO messages.
138153
* Setting the will topic and message after the initial connect
139154
* Forwarder Encapsulation
140155

include/zephyr/net/mqtt_sn.h

Lines changed: 70 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -75,16 +75,16 @@ enum mqtt_sn_topic_type {
7575
* MQTT-SN return codes.
7676
*/
7777
enum mqtt_sn_return_code {
78-
MQTT_SN_CODE_ACCEPTED = 0x00, /**< Accepted */
78+
MQTT_SN_CODE_ACCEPTED = 0x00, /**< Accepted */
7979
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 */
80+
MQTT_SN_CODE_REJECTED_TOPIC_ID = 0x02, /**< Rejected: Invalid Topic ID */
81+
MQTT_SN_CODE_REJECTED_NOTSUP = 0x03, /**< Rejected: Not Supported */
8282
};
8383

8484
/** @brief Abstracts memory buffers. */
8585
struct mqtt_sn_data {
8686
const uint8_t *data; /**< Pointer to data. */
87-
uint16_t size; /**< Size of data, in bytes. */
87+
size_t size; /**< Size of data, in bytes. */
8888
};
8989

9090
/**
@@ -105,19 +105,22 @@ struct mqtt_sn_data {
105105
*
106106
* struct mqtt_sn_data data = MQTT_SN_DATA_BYTES(0x13, 0x37);
107107
*/
108-
#define MQTT_SN_DATA_BYTES(...) \
109-
((struct mqtt_sn_data) { (uint8_t[]){ __VA_ARGS__ }, sizeof((uint8_t[]){ __VA_ARGS__ })})
108+
#define MQTT_SN_DATA_BYTES(...) \
109+
((struct mqtt_sn_data){(uint8_t[]){__VA_ARGS__}, sizeof((uint8_t[]){__VA_ARGS__})})
110110

111111
/**
112112
* Event types that can be emitted by the library.
113113
*/
114114
enum mqtt_sn_evt_type {
115-
MQTT_SN_EVT_CONNECTED, /**< Connected to a gateway */
115+
MQTT_SN_EVT_CONNECTED, /**< Connected to a gateway */
116116
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 */
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 */
121124
};
122125

123126
/**
@@ -180,16 +183,27 @@ struct mqtt_sn_transport {
180183
void (*deinit)(struct mqtt_sn_transport *transport);
181184

182185
/**
183-
* Will be called by the library when it wants to send a message.
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.
184195
*/
185-
int (*msg_send)(struct mqtt_sn_client *client, void *buf, size_t sz);
196+
int (*sendto)(struct mqtt_sn_client *client, void *buf, size_t sz, const void *dest_addr,
197+
size_t addrlen);
186198

187199
/**
188200
* @brief Will be called by the library when it wants to receive a message.
189201
*
190-
* Implementations should follow recv conventions.
202+
* Implementations should follow recvfrom conventions with the exception
203+
* of a NULL src_addr being a broadcast message.
191204
*/
192-
ssize_t (*recv)(struct mqtt_sn_client *client, void *buffer, size_t length);
205+
ssize_t (*recvfrom)(struct mqtt_sn_client *client, void *rx_buf, size_t rx_len,
206+
void *src_addr, size_t *addrlen);
193207

194208
/**
195209
* @brief Check if incoming data is available.
@@ -215,9 +229,9 @@ struct mqtt_sn_transport_udp {
215229
/** Socket FD */
216230
int sock;
217231

218-
/** Address of the gateway */
219-
struct sockaddr gwaddr;
220-
socklen_t gwaddrlen;
232+
/** Address of broadcasts */
233+
struct sockaddr bcaddr;
234+
socklen_t bcaddrlen;
221235
};
222236

223237
#define UDP_TRANSPORT(transport) CONTAINER_OF(transport, struct mqtt_sn_transport_udp, tp)
@@ -265,6 +279,9 @@ struct mqtt_sn_client {
265279
/** Buffer for incoming data */
266280
struct net_buf_simple rx;
267281

282+
/** Buffer for incoming data sender address */
283+
struct net_buf_simple rx_addr;
284+
268285
/** Event callback */
269286
mqtt_sn_evt_cb_t evt_cb;
270287

@@ -277,6 +294,9 @@ struct mqtt_sn_client {
277294
/** List of registered topics */
278295
sys_slist_t topic;
279296

297+
/** List of found gateways */
298+
sys_slist_t gateway;
299+
280300
/** Current state of the MQTT-SN client */
281301
int state;
282302

@@ -286,6 +306,15 @@ struct mqtt_sn_client {
286306
/** Number of retries for failed ping attempts */
287307
uint8_t ping_retries;
288308

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+
289318
/** Delayable work structure for processing MQTT-SN events */
290319
struct k_work_delayable process_work;
291320
};
@@ -317,6 +346,29 @@ int mqtt_sn_client_init(struct mqtt_sn_client *client, const struct mqtt_sn_data
317346
*/
318347
void mqtt_sn_client_deinit(struct mqtt_sn_client *client);
319348

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+
320372
/**
321373
* @brief Connect the client.
322374
*

subsys/net/lib/mqtt_sn/Kconfig

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,51 @@ if MQTT_SN_LIB
1414
config MQTT_SN_LIB_MAX_PAYLOAD_SIZE
1515
int "Maximum payload size of an MQTT-SN message"
1616
default $(UINT8_MAX)
17+
range $(UINT8_MAX) $(UINT16_MAX)
1718

1819
config MQTT_SN_LIB_MAX_MSGS
1920
int "Number of preallocated messages"
2021
default 10
22+
range 1 $(UINT8_MAX)
2123

2224
config MQTT_SN_LIB_MAX_TOPICS
2325
int "Number of topics that can be managed"
2426
default 20
27+
range 1 $(UINT8_MAX)
2528

2629
config MQTT_SN_LIB_MAX_TOPIC_SIZE
2730
int "Maximum topic length"
2831
default 64
32+
range 1 $(UINT16_MAX)
33+
34+
config MQTT_SN_LIB_MAX_GATEWAYS
35+
int "Maximum number of gateways to store internally"
36+
default 2
37+
range 1 $(UINT8_MAX)
38+
39+
config MQTT_SN_LIB_MAX_ADDR_SIZE
40+
int "Maximum address size for the transport"
41+
default 21
42+
range 1 $(UINT8_MAX)
43+
help
44+
The MQTT_SN library stores addresses internally and thus
45+
needs to know how long your addresses are. Set this to the maximum
46+
length in bytes of the address data structure for your implemented transport.
47+
48+
config MQTT_SN_LIB_BROADCAST_RADIUS
49+
int "Radius for broadcast messages"
50+
default 1
51+
range 1 $(UINT8_MAX)
2952

3053
config MQTT_SN_LIB_MAX_PUBLISH
3154
int "Number of publishes that can be in-flight at the same time"
3255
default 5
56+
range 1 $(UINT8_MAX)
3357

3458
config MQTT_SN_KEEPALIVE
3559
int "Maximum number of clients Keep alive time for MQTT-SN (in seconds)"
3660
default 60
61+
range 1 $(UINT8_MAX)
3762
help
3863
Keep alive time for MQTT-SN (in seconds). Sending of Ping Requests to
3964
keep the connection alive are governed by this value.
@@ -50,6 +75,22 @@ config MQTT_SN_LIB_N_RETRY
5075
config MQTT_SN_LIB_T_RETRY
5176
int "Time (seconds) to wait for responses"
5277
default 10
78+
range 0 $(UINT8_MAX)
79+
80+
config MQTT_SN_LIB_T_SEARCHGW
81+
int "Max time (seconds) to wait before sending SEARCHGW"
82+
default 10
83+
range 0 $(UINT8_MAX)
84+
85+
config MQTT_SN_LIB_T_GWINFO
86+
int "Max time (seconds) to wait before sending GWINFO"
87+
default 10
88+
range 0 $(UINT8_MAX)
89+
90+
config MQTT_SN_LIB_N_ADV
91+
int "Number of missed Advertise messages before considering GW lost"
92+
default 2
93+
range 1 $(UINT8_MAX)
5394

5495
module=MQTT_SN
5596
module-dep=NET_LOG

0 commit comments

Comments
 (0)