RaspberrPi project source code
guowenxue
2024-04-11 69b42a43ca4b2d93be203c34f6b45f5de1e32a15
commit | author | age
d6b4a7 1 /*
G 2  * coreMQTT v2.1.1
3  * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
5  * SPDX-License-Identifier: MIT
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy of
8  * this software and associated documentation files (the "Software"), to deal in
9  * the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11  * the Software, and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24
25 /**
26  * @file core_mqtt.h
27  * @brief User-facing functions of the MQTT 3.1.1 library.
28  */
29 #ifndef CORE_MQTT_H
30 #define CORE_MQTT_H
31
32 /* *INDENT-OFF* */
33 #ifdef __cplusplus
34     extern "C" {
35 #endif
36 /* *INDENT-ON* */
37
38 /* Include MQTT serializer library. */
39 #include "core_mqtt_serializer.h"
40
41 /* Include transport interface. */
42 #include "transport_interface.h"
43
44 /**
45  * @cond DOXYGEN_IGNORE
46  * The current version of this library.
47  */
48 #define MQTT_LIBRARY_VERSION    "v2.1.1"
49 /** @endcond */
50
51 /**
52  * @ingroup mqtt_constants
53  * @brief Invalid packet identifier.
54  *
55  * Zero is an invalid packet identifier as per MQTT v3.1.1 spec.
56  */
57 #define MQTT_PACKET_ID_INVALID    ( ( uint16_t ) 0U )
58
59 /* Structures defined in this file. */
60 struct MQTTPubAckInfo;
61 struct MQTTContext;
62 struct MQTTDeserializedInfo;
63
64 /**
65  * @ingroup mqtt_callback_types
66  * @brief Application provided function to query the time elapsed since a given
67  * epoch in milliseconds.
68  *
69  * @note The timer should be a monotonic timer. It just needs to provide an
70  * incrementing count of milliseconds elapsed since a given epoch.
71  *
72  * @return The time elapsed in milliseconds.
73  */
74 typedef uint32_t (* MQTTGetCurrentTimeFunc_t )( void );
75
76 /**
77  * @ingroup mqtt_callback_types
78  * @brief Application callback for receiving incoming publishes and incoming
79  * acks.
80  *
81  * @note This callback will be called only if packets are deserialized with a
82  * result of #MQTTSuccess or #MQTTServerRefused. The latter can be obtained
83  * when deserializing a SUBACK, indicating a broker's rejection of a subscribe.
84  *
85  * @param[in] pContext Initialized MQTT context.
86  * @param[in] pPacketInfo Information on the type of incoming MQTT packet.
87  * @param[in] pDeserializedInfo Deserialized information from incoming packet.
88  */
89 typedef void (* MQTTEventCallback_t )( struct MQTTContext * pContext,
90                                        struct MQTTPacketInfo * pPacketInfo,
91                                        struct MQTTDeserializedInfo * pDeserializedInfo );
92
93 /**
94  * @ingroup mqtt_enum_types
95  * @brief Values indicating if an MQTT connection exists.
96  */
97 typedef enum MQTTConnectionStatus
98 {
99     MQTTNotConnected, /**< @brief MQTT Connection is inactive. */
100     MQTTConnected     /**< @brief MQTT Connection is active. */
101 } MQTTConnectionStatus_t;
102
103 /**
104  * @ingroup mqtt_enum_types
105  * @brief The state of QoS 1 or QoS 2 MQTT publishes, used in the state engine.
106  */
107 typedef enum MQTTPublishState
108 {
109     MQTTStateNull = 0,  /**< @brief An empty state with no corresponding PUBLISH. */
110     MQTTPublishSend,    /**< @brief The library will send an outgoing PUBLISH packet. */
111     MQTTPubAckSend,     /**< @brief The library will send a PUBACK for a received PUBLISH. */
112     MQTTPubRecSend,     /**< @brief The library will send a PUBREC for a received PUBLISH. */
113     MQTTPubRelSend,     /**< @brief The library will send a PUBREL for a received PUBREC. */
114     MQTTPubCompSend,    /**< @brief The library will send a PUBCOMP for a received PUBREL. */
115     MQTTPubAckPending,  /**< @brief The library is awaiting a PUBACK for an outgoing PUBLISH. */
116     MQTTPubRecPending,  /**< @brief The library is awaiting a PUBREC for an outgoing PUBLISH. */
117     MQTTPubRelPending,  /**< @brief The library is awaiting a PUBREL for an incoming PUBLISH. */
118     MQTTPubCompPending, /**< @brief The library is awaiting a PUBCOMP for an outgoing PUBLISH. */
119     MQTTPublishDone     /**< @brief The PUBLISH has been completed. */
120 } MQTTPublishState_t;
121
122 /**
123  * @ingroup mqtt_enum_types
124  * @brief Packet types used in acknowledging QoS 1 or QoS 2 publishes.
125  */
126 typedef enum MQTTPubAckType
127 {
128     MQTTPuback, /**< @brief PUBACKs are sent in response to a QoS 1 PUBLISH. */
129     MQTTPubrec, /**< @brief PUBRECs are sent in response to a QoS 2 PUBLISH. */
130     MQTTPubrel, /**< @brief PUBRELs are sent in response to a PUBREC. */
131     MQTTPubcomp /**< @brief PUBCOMPs are sent in response to a PUBREL. */
132 } MQTTPubAckType_t;
133
134 /**
135  * @ingroup mqtt_enum_types
136  * @brief The status codes in the SUBACK response to a subscription request.
137  */
138 typedef enum MQTTSubAckStatus
139 {
140     MQTTSubAckSuccessQos0 = 0x00, /**< @brief Success with a maximum delivery at QoS 0. */
141     MQTTSubAckSuccessQos1 = 0x01, /**< @brief Success with a maximum delivery at QoS 1. */
142     MQTTSubAckSuccessQos2 = 0x02, /**< @brief Success with a maximum delivery at QoS 2. */
143     MQTTSubAckFailure = 0x80      /**< @brief Failure. */
144 } MQTTSubAckStatus_t;
145
146 /**
147  * @ingroup mqtt_struct_types
148  * @brief An element of the state engine records for QoS 1 or Qos 2 publishes.
149  */
150 typedef struct MQTTPubAckInfo
151 {
152     uint16_t packetId;               /**< @brief The packet ID of the original PUBLISH. */
153     MQTTQoS_t qos;                   /**< @brief The QoS of the original PUBLISH. */
154     MQTTPublishState_t publishState; /**< @brief The current state of the publish process. */
155 } MQTTPubAckInfo_t;
156
157 /**
158  * @ingroup mqtt_struct_types
159  * @brief A struct representing an MQTT connection.
160  */
161 typedef struct MQTTContext
162 {
163     /**
164      * @brief State engine records for outgoing publishes.
165      */
166     MQTTPubAckInfo_t * outgoingPublishRecords;
167
168     /**
169      * @brief State engine records for incoming publishes.
170      */
171     MQTTPubAckInfo_t * incomingPublishRecords;
172
173     /**
174      * @brief The maximum number of outgoing publish records.
175      */
176     size_t outgoingPublishRecordMaxCount;
177
178     /**
179      * @brief The maximum number of incoming publish records.
180      */
181     size_t incomingPublishRecordMaxCount;
182
183     /**
184      * @brief The transport interface used by the MQTT connection.
185      */
186     TransportInterface_t transportInterface;
187
188     /**
189      * @brief The buffer used in sending and receiving packets from the network.
190      */
191     MQTTFixedBuffer_t networkBuffer;
192
193     /**
194      * @brief The next available ID for outgoing MQTT packets.
195      */
196     uint16_t nextPacketId;
197
198     /**
199      * @brief Whether the context currently has a connection to the broker.
200      */
201     MQTTConnectionStatus_t connectStatus;
202
203     /**
204      * @brief Function used to get millisecond timestamps.
205      */
206     MQTTGetCurrentTimeFunc_t getTime;
207
208     /**
209      * @brief Callback function used to give deserialized MQTT packets to the application.
210      */
211     MQTTEventCallback_t appCallback;
212
213     /**
214      * @brief Timestamp of the last packet sent by the library.
215      */
216     uint32_t lastPacketTxTime;
217
218     /**
219      * @brief Timestamp of the last packet received by the library.
220      */
221     uint32_t lastPacketRxTime;
222
223     /**
224      * @brief Whether the library sent a packet during a call of #MQTT_ProcessLoop or
225      * #MQTT_ReceiveLoop.
226      */
227     bool controlPacketSent;
228
229     /**
230      * @brief Index to keep track of the number of bytes received in network buffer.
231      */
232     size_t index;
233
234     /* Keep alive members. */
235     uint16_t keepAliveIntervalSec; /**< @brief Keep Alive interval. */
236     uint32_t pingReqSendTimeMs;    /**< @brief Timestamp of the last sent PINGREQ. */
237     bool waitingForPingResp;       /**< @brief If the library is currently awaiting a PINGRESP. */
238 } MQTTContext_t;
239
240 /**
241  * @ingroup mqtt_struct_types
242  * @brief Struct to hold deserialized packet information for an #MQTTEventCallback_t
243  * callback.
244  */
245 typedef struct MQTTDeserializedInfo
246 {
247     uint16_t packetIdentifier;          /**< @brief Packet ID of deserialized packet. */
248     MQTTPublishInfo_t * pPublishInfo;   /**< @brief Pointer to deserialized publish info. */
249     MQTTStatus_t deserializationResult; /**< @brief Return code of deserialization. */
250 } MQTTDeserializedInfo_t;
251
252 /**
253  * @brief Initialize an MQTT context.
254  *
255  * This function must be called on an #MQTTContext_t before any other function.
256  *
257  * @note The #MQTTGetCurrentTimeFunc_t function for querying time must be defined. If
258  * there is no time implementation, it is the responsibility of the application
259  * to provide a dummy function to always return 0, provide 0 timeouts for
260  * all calls to #MQTT_Connect, #MQTT_ProcessLoop, and #MQTT_ReceiveLoop and configure
261  * the #MQTT_RECV_POLLING_TIMEOUT_MS and #MQTT_SEND_TIMEOUT_MS configurations
262  * to be 0. This will result in loop functions running for a single iteration, and
263  * #MQTT_Connect relying on #MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT to receive the CONNACK packet.
264  *
265  * @param[in] pContext The context to initialize.
266  * @param[in] pTransportInterface The transport interface to use with the context.
267  * @param[in] getTimeFunction The time utility function which can return the amount of time
268  *    (in milliseconds) elapsed since a given epoch. This function will be used to ensure that
269  *    timeouts in the API calls are met and keep-alive messages are sent on time.
270  * @param[in] userCallback The user callback to use with the context to notify about incoming
271  *     packet events.
272  * @param[in] pNetworkBuffer Network buffer provided for the context. This buffer will be used
273  *     to receive incoming messages from the broker. This buffer must remain valid and in scope
274  *     for the entire lifetime of the @p pContext and must not be used by another context and/or
275  *     application.
276  *
277  * @return #MQTTBadParameter if invalid parameters are passed;
278  * #MQTTSuccess otherwise.
279  *
280  * <b>Example</b>
281  * @code{c}
282  *
283  * // Function for obtaining a timestamp.
284  * uint32_t getTimeStampMs();
285  * // Callback function for receiving packets.
286  * void eventCallback(
287  *      MQTTContext_t * pContext,
288  *      MQTTPacketInfo_t * pPacketInfo,
289  *      MQTTDeserializedInfo_t * pDeserializedInfo
290  * );
291  * // Network send.
292  * int32_t networkSend( NetworkContext_t * pContext, const void * pBuffer, size_t bytes );
293  * // Network receive.
294  * int32_t networkRecv( NetworkContext_t * pContext, void * pBuffer, size_t bytes );
295  *
296  * MQTTContext_t mqttContext;
297  * TransportInterface_t transport;
298  * MQTTFixedBuffer_t fixedBuffer;
299  * // Create a globally accessible buffer which remains in scope for the entire duration
300  * // of the MQTT context.
301  * uint8_t buffer[ 1024 ];
302  *
303  * // Clear context.
304  * memset( ( void * ) &mqttContext, 0x00, sizeof( MQTTContext_t ) );
305  *
306  * // Set transport interface members.
307  * transport.pNetworkContext = &someTransportContext;
308  * transport.send = networkSend;
309  * transport.recv = networkRecv;
310  *
311  * // Set buffer members.
312  * fixedBuffer.pBuffer = buffer;
313  * fixedBuffer.size = 1024;
314  *
315  * status = MQTT_Init( &mqttContext, &transport, getTimeStampMs, eventCallback, &fixedBuffer );
316  *
317  * if( status == MQTTSuccess )
318  * {
319  *      // Do something with mqttContext. The transport and fixedBuffer structs were
320  *      // copied into the context, so the original structs do not need to stay in scope.
321  *      // However, the memory pointed to by the fixedBuffer.pBuffer must remain in scope.
322  * }
323  * @endcode
324  */
325 /* @[declare_mqtt_init] */
326 MQTTStatus_t MQTT_Init( MQTTContext_t * pContext,
327                         const TransportInterface_t * pTransportInterface,
328                         MQTTGetCurrentTimeFunc_t getTimeFunction,
329                         MQTTEventCallback_t userCallback,
330                         const MQTTFixedBuffer_t * pNetworkBuffer );
331 /* @[declare_mqtt_init] */
332
333 /**
334  * @brief Initialize an MQTT context for QoS > 0.
335  *
336  * This function must be called on an #MQTTContext_t after MQTT_Init and before any other function.
337  *
338  * @param[in] pContext The context to initialize.
339  * @param[in] pOutgoingPublishRecords Pointer to memory which will be used to store state of outgoing
340  * publishes.
341  * @param[in] outgoingPublishCount Maximum number of records which can be kept in the memory
342  * pointed to by @p pOutgoingPublishRecords.
343  * @param[in] pIncomingPublishRecords Pointer to memory which will be used to store state of incoming
344  * publishes.
345  * @param[in] incomingPublishCount Maximum number of records which can be kept in the memory
346  * pointed to by @p pIncomingPublishRecords.
347  *
348  * @return #MQTTBadParameter if invalid parameters are passed;
349  * #MQTTSuccess otherwise.
350  *
351  * <b>Example</b>
352  * @code{c}
353  *
354  * // Function for obtaining a timestamp.
355  * uint32_t getTimeStampMs();
356  * // Callback function for receiving packets.
357  * void eventCallback(
358  *      MQTTContext_t * pContext,
359  *      MQTTPacketInfo_t * pPacketInfo,
360  *      MQTTDeserializedInfo_t * pDeserializedInfo
361  * );
362  * // Network send.
363  * int32_t networkSend( NetworkContext_t * pContext, const void * pBuffer, size_t bytes );
364  * // Network receive.
365  * int32_t networkRecv( NetworkContext_t * pContext, void * pBuffer, size_t bytes );
366  *
367  * MQTTContext_t mqttContext;
368  * TransportInterface_t transport;
369  * MQTTFixedBuffer_t fixedBuffer;
370  * uint8_t buffer[ 1024 ];
371  * const size_t outgoingPublishCount = 30;
372  * MQTTPubAckInfo_t outgoingPublishes[ outgoingPublishCount ];
373  *
374  * // Clear context.
375  * memset( ( void * ) &mqttContext, 0x00, sizeof( MQTTContext_t ) );
376  *
377  * // Set transport interface members.
378  * transport.pNetworkContext = &someTransportContext;
379  * transport.send = networkSend;
380  * transport.recv = networkRecv;
381  *
382  * // Set buffer members.
383  * fixedBuffer.pBuffer = buffer;
384  * fixedBuffer.size = 1024;
385  *
386  * status = MQTT_Init( &mqttContext, &transport, getTimeStampMs, eventCallback, &fixedBuffer );
387  *
388  * if( status == MQTTSuccess )
389  * {
390  *      // We do not expect any incoming publishes in this example, therefore the incoming
391  *      // publish pointer is NULL and the count is zero.
392  *      status = MQTT_InitStatefulQoS( &mqttContext, outgoingPublishes, outgoingPublishCount, NULL, 0 );
393  *
394  *      // Now QoS1 and/or QoS2 publishes can be sent with this context.
395  * }
396  * @endcode
397  */
398 /* @[declare_mqtt_initstatefulqos] */
399 MQTTStatus_t MQTT_InitStatefulQoS( MQTTContext_t * pContext,
400                                    MQTTPubAckInfo_t * pOutgoingPublishRecords,
401                                    size_t outgoingPublishCount,
402                                    MQTTPubAckInfo_t * pIncomingPublishRecords,
403                                    size_t incomingPublishCount );
404 /* @[declare_mqtt_initstatefulqos] */
405
406 /**
407  * @brief Establish an MQTT session.
408  *
409  * This function will send MQTT CONNECT packet and receive a CONNACK packet. The
410  * send and receive from the network is done through the transport interface.
411  *
412  * The maximum time this function waits for a CONNACK is decided in one of the
413  * following ways:
414  * 1. If @p timeoutMs is greater than 0:
415  *    #MQTTContext_t.getTime is used to ensure that the function does not wait
416  *    more than @p timeoutMs for CONNACK.
417  * 2. If @p timeoutMs is 0:
418  *    The network receive for CONNACK is retried up to the number of times
419  *    configured by #MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT.
420  *
421  * @note If a dummy #MQTTGetCurrentTimeFunc_t was passed to #MQTT_Init, then a
422  * timeout value of 0 MUST be passed to the API, and the #MQTT_RECV_POLLING_TIMEOUT_MS
423  * and #MQTT_SEND_TIMEOUT_MS timeout configurations MUST be set to 0.
424  *
425  * @param[in] pContext Initialized MQTT context.
426  * @param[in] pConnectInfo MQTT CONNECT packet information.
427  * @param[in] pWillInfo Last Will and Testament. Pass NULL if Last Will and
428  * Testament is not used.
429  * @param[in] timeoutMs Maximum time in milliseconds to wait for a CONNACK packet.
430  * A zero timeout makes use of the retries for receiving CONNACK as configured with
431  * #MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT.
432  * @param[out] pSessionPresent This value will be set to true if a previous session
433  * was present; otherwise it will be set to false. It is only relevant if not
434  * establishing a clean session.
435  *
436  * @return #MQTTNoMemory if the #MQTTContext_t.networkBuffer is too small to
437  * hold the MQTT packet;
438  * #MQTTBadParameter if invalid parameters are passed;
439  * #MQTTSendFailed if transport send failed;
440  * #MQTTRecvFailed if transport receive failed for CONNACK;
441  * #MQTTNoDataAvailable if no data available to receive in transport until
442  * the @p timeoutMs for CONNACK;
443  * #MQTTSuccess otherwise.
444  *
445  * @note This API may spend more time than provided in the timeoutMS parameters in
446  * certain conditions as listed below:
447  *
448  * 1. Timeouts are incorrectly configured - If the timeoutMS is less than the
449  *    transport receive timeout and if a CONNACK packet is not received within
450  *    the transport receive timeout, the API will spend the transport receive
451  *    timeout (which is more time than the timeoutMs). It is the case of incorrect
452  *    timeout configuration as the timeoutMs parameter passed to this API must be
453  *    greater than the transport receive timeout. Please refer to the transport
454  *    interface documentation for more details about timeout configurations.
455  *
456  * 2. Partial CONNACK packet is received right before the expiry of the timeout - It
457  *    is possible that first two bytes of CONNACK packet (packet type and remaining
458  *    length) are received right before the expiry of the timeoutMS. In that case,
459  *    the API makes one more network receive call in an attempt to receive the remaining
460  *    2 bytes. In the worst case, it can happen that the remaining 2 bytes are never
461  *    received and this API will end up spending timeoutMs + transport receive timeout.
462  *
463  * <b>Example</b>
464  * @code{c}
465  *
466  * // Variables used in this example.
467  * MQTTStatus_t status;
468  * MQTTConnectInfo_t connectInfo = { 0 };
469  * MQTTPublishInfo_t willInfo = { 0 };
470  * bool sessionPresent;
471  * // This is assumed to have been initialized before calling this function.
472  * MQTTContext_t * pContext;
473  *
474  * // True for creating a new session with broker, false if we want to resume an old one.
475  * connectInfo.cleanSession = true;
476  * // Client ID must be unique to broker. This field is required.
477  * connectInfo.pClientIdentifier = "someClientID";
478  * connectInfo.clientIdentifierLength = strlen( connectInfo.pClientIdentifier );
479  *
480  * // The following fields are optional.
481  * // Value for keep alive.
482  * connectInfo.keepAliveSeconds = 60;
483  * // Optional username and password.
484  * connectInfo.pUserName = "someUserName";
485  * connectInfo.userNameLength = strlen( connectInfo.pUserName );
486  * connectInfo.pPassword = "somePassword";
487  * connectInfo.passwordLength = strlen( connectInfo.pPassword );
488  *
489  * // The last will and testament is optional, it will be published by the broker
490  * // should this client disconnect without sending a DISCONNECT packet.
491  * willInfo.qos = MQTTQoS0;
492  * willInfo.pTopicName = "/lwt/topic/name";
493  * willInfo.topicNameLength = strlen( willInfo.pTopicName );
494  * willInfo.pPayload = "LWT Message";
495  * willInfo.payloadLength = strlen( "LWT Message" );
496  *
497  * // Send the connect packet. Use 100 ms as the timeout to wait for the CONNACK packet.
498  * status = MQTT_Connect( pContext, &connectInfo, &willInfo, 100, &sessionPresent );
499  *
500  * if( status == MQTTSuccess )
501  * {
502  *      // Since we requested a clean session, this must be false
503  *      assert( sessionPresent == false );
504  *
505  *      // Do something with the connection.
506  * }
507  * @endcode
508  */
509 /* @[declare_mqtt_connect] */
510 MQTTStatus_t MQTT_Connect( MQTTContext_t * pContext,
511                            const MQTTConnectInfo_t * pConnectInfo,
512                            const MQTTPublishInfo_t * pWillInfo,
513                            uint32_t timeoutMs,
514                            bool * pSessionPresent );
515 /* @[declare_mqtt_connect] */
516
517 /**
518  * @brief Sends MQTT SUBSCRIBE for the given list of topic filters to
519  * the broker.
520  *
521  * @param[in] pContext Initialized MQTT context.
522  * @param[in] pSubscriptionList Array of MQTT subscription info.
523  * @param[in] subscriptionCount The number of elements in @ pSubscriptionList
524  * array.
525  * @param[in] packetId Packet ID generated by #MQTT_GetPacketId.
526  *
527  * @return #MQTTNoMemory if the #MQTTContext_t.networkBuffer is too small to
528  * hold the MQTT packet;
529  * #MQTTBadParameter if invalid parameters are passed;
530  * #MQTTSendFailed if transport write failed;
531  * #MQTTSuccess otherwise.
532  *
533  * <b>Example</b>
534  * @code{c}
535  *
536  * // Variables used in this example.
537  * MQTTStatus_t status;
538  * MQTTSubscribeInfo_t subscriptionList[ NUMBER_OF_SUBSCRIPTIONS ] = { 0 };
539  * uint16_t packetId;
540  * // This context is assumed to be initialized and connected.
541  * MQTTContext_t * pContext;
542  * // This is assumed to be a list of filters we want to subscribe to.
543  * const char * filters[ NUMBER_OF_SUBSCRIPTIONS ];
544  *
545  * // Set each subscription.
546  * for( int i = 0; i < NUMBER_OF_SUBSCRIPTIONS; i++ )
547  * {
548  *      subscriptionList[ i ].qos = MQTTQoS0;
549  *      // Each subscription needs a topic filter.
550  *      subscriptionList[ i ].pTopicFilter = filters[ i ];
551  *      subscriptionList[ i ].topicFilterLength = strlen( filters[ i ] );
552  * }
553  *
554  * // Obtain a new packet id for the subscription.
555  * packetId = MQTT_GetPacketId( pContext );
556  *
557  * status = MQTT_Subscribe( pContext, &subscriptionList[ 0 ], NUMBER_OF_SUBSCRIPTIONS, packetId );
558  *
559  * if( status == MQTTSuccess )
560  * {
561  *      // We must now call MQTT_ReceiveLoop() or MQTT_ProcessLoop() to receive the SUBACK.
562  *      // If the broker accepts the subscription we can now receive publishes
563  *      // on the requested topics.
564  * }
565  * @endcode
566  */
567 /* @[declare_mqtt_subscribe] */
568 MQTTStatus_t MQTT_Subscribe( MQTTContext_t * pContext,
569                              const MQTTSubscribeInfo_t * pSubscriptionList,
570                              size_t subscriptionCount,
571                              uint16_t packetId );
572 /* @[declare_mqtt_subscribe] */
573
574 /**
575  * @brief Publishes a message to the given topic name.
576  *
577  * @param[in] pContext Initialized MQTT context.
578  * @param[in] pPublishInfo MQTT PUBLISH packet parameters.
579  * @param[in] packetId packet ID generated by #MQTT_GetPacketId.
580  *
581  * @return #MQTTNoMemory if pBuffer is too small to hold the MQTT packet;
582  * #MQTTBadParameter if invalid parameters are passed;
583  * #MQTTSendFailed if transport write failed;
584  * #MQTTSuccess otherwise.
585  *
586  * <b>Example</b>
587  * @code{c}
588  *
589  * // Variables used in this example.
590  * MQTTStatus_t status;
591  * MQTTPublishInfo_t publishInfo;
592  * uint16_t packetId;
593  * // This context is assumed to be initialized and connected.
594  * MQTTContext_t * pContext;
595  *
596  * // QoS of publish.
597  * publishInfo.qos = MQTTQoS1;
598  * publishInfo.pTopicName = "/some/topic/name";
599  * publishInfo.topicNameLength = strlen( publishInfo.pTopicName );
600  * publishInfo.pPayload = "Hello World!";
601  * publishInfo.payloadLength = strlen( "Hello World!" );
602  *
603  * // Packet ID is needed for QoS > 0.
604  * packetId = MQTT_GetPacketId( pContext );
605  *
606  * status = MQTT_Publish( pContext, &publishInfo, packetId );
607  *
608  * if( status == MQTTSuccess )
609  * {
610  *      // Since the QoS is > 0, we will need to call MQTT_ReceiveLoop()
611  *      // or MQTT_ProcessLoop() to process the publish acknowledgments.
612  * }
613  * @endcode
614  */
615 /* @[declare_mqtt_publish] */
616 MQTTStatus_t MQTT_Publish( MQTTContext_t * pContext,
617                            const MQTTPublishInfo_t * pPublishInfo,
618                            uint16_t packetId );
619 /* @[declare_mqtt_publish] */
620
621 /**
622  * @brief Cancels an outgoing publish callback (only for QoS > QoS0) by
623  * removing it from the pending ACK list.
624  *
625  * @note This cannot cancel the actual publish as that might have already
626  * been sent to the broker. This only removes the details of the given packet
627  * ID from the list of unACKed packet. That allows the caller to free any memory
628  * associated with the publish payload, topic string etc. Also, after this API
629  * call, the user provided callback will not be invoked when the ACK packet is
630  * received.
631  *
632  * @param[in] pContext Initialized MQTT context.
633  * @param[in] packetId packet ID corresponding to the outstanding publish.
634  *
635  * @return #MQTTBadParameter if invalid parameters are passed;
636  * #MQTTSuccess otherwise.
637  */
638 /* @[declare_mqtt_cancelcallback] */
639 MQTTStatus_t MQTT_CancelCallback( const MQTTContext_t * pContext,
640                                   uint16_t packetId );
641 /* @[declare_mqtt_cancelcallback] */
642
643 /**
644  * @brief Sends an MQTT PINGREQ to broker.
645  *
646  * @param[in] pContext Initialized and connected MQTT context.
647  *
648  * @return #MQTTNoMemory if pBuffer is too small to hold the MQTT packet;
649  * #MQTTBadParameter if invalid parameters are passed;
650  * #MQTTSendFailed if transport write failed;
651  * #MQTTSuccess otherwise.
652  */
653 /* @[declare_mqtt_ping] */
654 MQTTStatus_t MQTT_Ping( MQTTContext_t * pContext );
655 /* @[declare_mqtt_ping] */
656
657 /**
658  * @brief Sends MQTT UNSUBSCRIBE for the given list of topic filters to
659  * the broker.
660  *
661  * @param[in] pContext Initialized MQTT context.
662  * @param[in] pSubscriptionList List of MQTT subscription info.
663  * @param[in] subscriptionCount The number of elements in pSubscriptionList.
664  * @param[in] packetId packet ID generated by #MQTT_GetPacketId.
665  *
666  * @return #MQTTNoMemory if the #MQTTContext_t.networkBuffer is too small to
667  * hold the MQTT packet;
668  * #MQTTBadParameter if invalid parameters are passed;
669  * #MQTTSendFailed if transport write failed;
670  * #MQTTSuccess otherwise.
671  *
672  * <b>Example</b>
673  * @code{c}
674  *
675  * // Variables used in this example.
676  * MQTTStatus_t status;
677  * MQTTSubscribeInfo_t unsubscribeList[ NUMBER_OF_SUBSCRIPTIONS ] = { 0 };
678  * uint16_t packetId;
679  * // This context is assumed to be initialized and connected.
680  * MQTTContext_t * pContext;
681  * // This is assumed to be a list of filters we want to unsubscribe from.
682  * const char * filters[ NUMBER_OF_SUBSCRIPTIONS ];
683  *
684  * // Set information for each unsubscribe request.
685  * for( int i = 0; i < NUMBER_OF_SUBSCRIPTIONS; i++ )
686  * {
687  *      unsubscribeList[ i ].pTopicFilter = filters[ i ];
688  *      unsubscribeList[ i ].topicFilterLength = strlen( filters[ i ] );
689  *
690  *      // The QoS field of MQTT_SubscribeInfo_t is unused for unsubscribing.
691  * }
692  *
693  * // Obtain a new packet id for the unsubscribe request.
694  * packetId = MQTT_GetPacketId( pContext );
695  *
696  * status = MQTT_Unsubscribe( pContext, &unsubscribeList[ 0 ], NUMBER_OF_SUBSCRIPTIONS, packetId );
697  *
698  * if( status == MQTTSuccess )
699  * {
700  *      // We must now call MQTT_ReceiveLoop() or MQTT_ProcessLoop() to receive the UNSUBACK.
701  *      // After this the broker should no longer send publishes for these topics.
702  * }
703  * @endcode
704  */
705 /* @[declare_mqtt_unsubscribe] */
706 MQTTStatus_t MQTT_Unsubscribe( MQTTContext_t * pContext,
707                                const MQTTSubscribeInfo_t * pSubscriptionList,
708                                size_t subscriptionCount,
709                                uint16_t packetId );
710 /* @[declare_mqtt_unsubscribe] */
711
712 /**
713  * @brief Disconnect an MQTT session.
714  *
715  * @param[in] pContext Initialized and connected MQTT context.
716  *
717  * @return #MQTTNoMemory if the #MQTTContext_t.networkBuffer is too small to
718  * hold the MQTT packet;
719  * #MQTTBadParameter if invalid parameters are passed;
720  * #MQTTSendFailed if transport send failed;
721  * #MQTTSuccess otherwise.
722  */
723 /* @[declare_mqtt_disconnect] */
724 MQTTStatus_t MQTT_Disconnect( MQTTContext_t * pContext );
725 /* @[declare_mqtt_disconnect] */
726
727 /**
728  * @brief Loop to receive packets from the transport interface. Handles keep
729  * alive.
730  *
731  * @note If a dummy timer function, #MQTTGetCurrentTimeFunc_t, is passed to the library,
732  * then the keep-alive mechanism is not supported by the #MQTT_ProcessLoop API.
733  * In that case, the #MQTT_ReceiveLoop API function should be used instead.
734  *
735  * @param[in] pContext Initialized and connected MQTT context.
736  *
737  * @note Calling this function blocks the calling context for a time period that
738  * depends on the passed the configuration macros, #MQTT_RECV_POLLING_TIMEOUT_MS
739  * and #MQTT_SEND_TIMEOUT_MS, and the underlying transport interface implementation
740  * timeouts, unless an error occurs. The blocking period also depends on the execution time of the
741  * #MQTTEventCallback_t callback supplied to the library. It is recommended that the supplied
742  * #MQTTEventCallback_t callback does not contain blocking operations to prevent potential
743  * non-deterministic blocking period of the #MQTT_ProcessLoop API call.
744  *
745  * @return #MQTTBadParameter if context is NULL;
746  * #MQTTRecvFailed if a network error occurs during reception;
747  * #MQTTSendFailed if a network error occurs while sending an ACK or PINGREQ;
748  * #MQTTBadResponse if an invalid packet is received;
749  * #MQTTKeepAliveTimeout if the server has not sent a PINGRESP before
750  * #MQTT_PINGRESP_TIMEOUT_MS milliseconds;
751  * #MQTTIllegalState if an incoming QoS 1/2 publish or ack causes an
752  * invalid transition for the internal state machine;
753  * #MQTTNeedMoreBytes if MQTT_ProcessLoop has received
754  * incomplete data; it should be called again (probably after a delay);
755  * #MQTTSuccess on success.
756  *
757  * <b>Example</b>
758  * @code{c}
759  *
760  * // Variables used in this example.
761  * MQTTStatus_t status;
762  * // This context is assumed to be initialized and connected.
763  * MQTTContext_t * pContext;
764  *
765  * while( true )
766  * {
767  *      status = MQTT_ProcessLoop( pContext );
768  *
769  *      if( status != MQTTSuccess && status != MQTTNeedMoreBytes )
770  *      {
771  *          // Determine the error. It's possible we might need to disconnect
772  *          // the underlying transport connection.
773  *      }
774  *      else
775  *      {
776  *          // Other application functions.
777  *      }
778  * }
779  * @endcode
780  */
781 /* @[declare_mqtt_processloop] */
782 MQTTStatus_t MQTT_ProcessLoop( MQTTContext_t * pContext );
783 /* @[declare_mqtt_processloop] */
784
785 /**
786  * @brief Loop to receive packets from the transport interface. Does not handle
787  * keep alive.
788  *
789  * @note If a dummy #MQTTGetCurrentTimeFunc_t was passed to #MQTT_Init, then the
790  * #MQTT_RECV_POLLING_TIMEOUT_MS and #MQTT_SEND_TIMEOUT_MS timeout configurations
791  * MUST be set to 0.
792  *
793  * @param[in] pContext Initialized and connected MQTT context.
794  *
795  * @note Calling this function blocks the calling context for a time period that
796  * depends on the the configuration macros, #MQTT_RECV_POLLING_TIMEOUT_MS and
797  * #MQTT_SEND_TIMEOUT_MS, and the underlying transport interface implementation
798  * timeouts, unless an error occurs. The blocking period also depends on the execution time of the
799  * #MQTTEventCallback_t callback supplied to the library. It is recommended that the supplied
800  * #MQTTEventCallback_t callback does not contain blocking operations to prevent potential
801  * non-deterministic blocking period of the #MQTT_ReceiveLoop API call.
802  *
803  * @return #MQTTBadParameter if context is NULL;
804  * #MQTTRecvFailed if a network error occurs during reception;
805  * #MQTTSendFailed if a network error occurs while sending an ACK or PINGREQ;
806  * #MQTTBadResponse if an invalid packet is received;
807  * #MQTTIllegalState if an incoming QoS 1/2 publish or ack causes an
808  * invalid transition for the internal state machine;
809  * #MQTTNeedMoreBytes if MQTT_ReceiveLoop has received
810  * incomplete data; it should be called again (probably after a delay);
811  * #MQTTSuccess on success.
812  *
813  * <b>Example</b>
814  * @code{c}
815  *
816  * // Variables used in this example.
817  * MQTTStatus_t status;
818  * uint32_t keepAliveMs = 60 * 1000;
819  * // This context is assumed to be initialized and connected.
820  * MQTTContext_t * pContext;
821  *
822  * while( true )
823  * {
824  *      status = MQTT_ReceiveLoop( pContext );
825  *
826  *      if( status != MQTTSuccess && status != MQTTNeedMoreBytes )
827  *      {
828  *          // Determine the error. It's possible we might need to disconnect
829  *          // the underlying transport connection.
830  *      }
831  *      else
832  *      {
833  *          // Since this function does not send pings, the application may need
834  *          // to in order to comply with keep alive.
835  *          if( ( pContext->getTime() - pContext->lastPacketTxTime ) > keepAliveMs )
836  *          {
837  *              status = MQTT_Ping( pContext );
838  *          }
839  *
840  *          // Other application functions.
841  *      }
842  * }
843  * @endcode
844  */
845 /* @[declare_mqtt_receiveloop] */
846 MQTTStatus_t MQTT_ReceiveLoop( MQTTContext_t * pContext );
847 /* @[declare_mqtt_receiveloop] */
848
849 /**
850  * @brief Get a packet ID that is valid according to the MQTT 3.1.1 spec.
851  *
852  * @param[in] pContext Initialized MQTT context.
853  *
854  * @return A non-zero number.
855  */
856 /* @[declare_mqtt_getpacketid] */
857 uint16_t MQTT_GetPacketId( MQTTContext_t * pContext );
858 /* @[declare_mqtt_getpacketid] */
859
860 /**
861  * @brief A utility function that determines whether the passed topic filter and
862  * topic name match according to the MQTT 3.1.1 protocol specification.
863  *
864  * @param[in] pTopicName The topic name to check.
865  * @param[in] topicNameLength Length of the topic name.
866  * @param[in] pTopicFilter The topic filter to check.
867  * @param[in] topicFilterLength Length of topic filter.
868  * @param[out] pIsMatch If the match is performed without any error, that is if the
869  * return value is MQTTSuccess, then and only then the value in this parameter is valid
870  * and updated. In such a case, if the topic filter and the topic name match, then this
871  * value is set to true; otherwise if there is no match then it is set to false.
872  *
873  * @note The API assumes that the passed topic name is valid to meet the
874  * requirements of the MQTT 3.1.1 specification. Invalid topic names (for example,
875  * containing wildcard characters) should not be passed to the function.
876  * Also, the API checks validity of topic filter for wildcard characters ONLY if
877  * the passed topic name and topic filter do not have an exact string match.
878  *
879  * @return Returns one of the following:
880  * - #MQTTBadParameter, if any of the input parameters is invalid.
881  * - #MQTTSuccess, if the matching operation was performed.
882  *
883  * <b>Example</b>
884  * @code{c}
885  *
886  * // Variables used in this example.
887  * const char * pTopic = "topic/match/1";
888  * const char * pFilter = "topic/#";
889  * MQTTStatus_t status = MQTTSuccess;
890  * bool match = false;
891  *
892  * status = MQTT_MatchTopic( pTopic, strlen( pTopic ), pFilter, strlen( pFilter ), &match );
893  * // Our parameters were valid, so this will return success.
894  * assert( status == MQTTSuccess );
895  *
896  * // For this specific example, we already know this value is true. This
897  * // check is placed here as an example for use with variable topic names.
898  * if( match )
899  * {
900  *      // Application can decide what to do with the matching topic name.
901  * }
902  * @endcode
903  */
904 MQTTStatus_t MQTT_MatchTopic( const char * pTopicName,
905                               const uint16_t topicNameLength,
906                               const char * pTopicFilter,
907                               const uint16_t topicFilterLength,
908                               bool * pIsMatch );
909
910 /**
911  * @brief Parses the payload of an MQTT SUBACK packet that contains status codes
912  * corresponding to topic filter subscription requests from the original
913  * subscribe packet.
914  *
915  * Each return code in the SUBACK packet corresponds to a topic filter in the
916  * SUBSCRIBE Packet being acknowledged.
917  * The status codes can be one of the following:
918  *  - 0x00 - Success - Maximum QoS 0
919  *  - 0x01 - Success - Maximum QoS 1
920  *  - 0x02 - Success - Maximum QoS 2
921  *  - 0x80 - Failure
922  * Refer to #MQTTSubAckStatus_t for the status codes.
923  *
924  * @param[in] pSubackPacket The SUBACK packet whose payload is to be parsed.
925  * @param[out] pPayloadStart This is populated with the starting address
926  * of the payload (or return codes for topic filters) in the SUBACK packet.
927  * @param[out] pPayloadSize This is populated with the size of the payload
928  * in the SUBACK packet. It represents the number of topic filters whose
929  * SUBACK status is present in the packet.
930  *
931  * @return Returns one of the following:
932  * - #MQTTBadParameter if the input SUBACK packet is invalid.
933  * - #MQTTSuccess if parsing the payload was successful.
934  *
935  * <b>Example</b>
936  * @code{c}
937  *
938  * // Global variable used in this example.
939  * // This is assumed to be the subscription list in the original SUBSCRIBE packet.
940  * MQTTSubscribeInfo_t pSubscribes[ NUMBER_OF_SUBSCRIPTIONS ];
941  *
942  * // MQTT_GetSubAckStatusCodes is intended to be used from the application
943  * // callback that is called by the library in MQTT_ProcessLoop or MQTT_ReceiveLoop.
944  * void eventCallback(
945  *      MQTTContext_t * pContext,
946  *      MQTTPacketInfo_t * pPacketInfo,
947  *      MQTTDeserializedInfo_t * pDeserializedInfo
948  * )
949  * {
950  *      MQTTStatus_t status = MQTTSuccess;
951  *      uint8_t * pCodes;
952  *      size_t numCodes;
953  *
954  *      if( pPacketInfo->type == MQTT_PACKET_TYPE_SUBACK )
955  *      {
956  *          status = MQTT_GetSubAckStatusCodes( pPacketInfo, &pCodes, &numCodes );
957  *
958  *          // Since the pointers to the payload and payload size are not NULL, and
959  *          // we use the packet info struct passed to the app callback (verified
960  *          // to be valid by the library), this function must return success.
961  *          assert( status == MQTTSuccess );
962  *          // The server must send a response code for each topic filter in the
963  *          // original SUBSCRIBE packet.
964  *          assert( numCodes == NUMBER_OF_SUBSCRIPTIONS );
965  *
966  *          for( int i = 0; i < numCodes; i++ )
967  *          {
968  *              // The only failure code is 0x80 = MQTTSubAckFailure.
969  *              if( pCodes[ i ] == MQTTSubAckFailure )
970  *              {
971  *                  // The subscription failed, we may want to retry the
972  *                  // subscription in pSubscribes[ i ] outside of this callback.
973  *              }
974  *              else
975  *              {
976  *                  // The subscription was granted, but the maximum QoS may be
977  *                  // lower than what was requested. We can verify the granted QoS.
978  *                  if( pSubscribes[ i ].qos != pCodes[ i ] )
979  *                  {
980  *                      LogWarn( (
981  *                          "Requested QoS %u, but granted QoS %u for %s",
982  *                          pSubscribes[ i ].qos, pCodes[ i ], pSubscribes[ i ].pTopicFilter
983  *                      ) );
984  *                  }
985  *              }
986  *          }
987  *      }
988  *      // Handle other packet types.
989  * }
990  * @endcode
991  */
992 /* @[declare_mqtt_getsubackstatuscodes] */
993 MQTTStatus_t MQTT_GetSubAckStatusCodes( const MQTTPacketInfo_t * pSubackPacket,
994                                         uint8_t ** pPayloadStart,
995                                         size_t * pPayloadSize );
996 /* @[declare_mqtt_getsubackstatuscodes] */
997
998 /**
999  * @brief Error code to string conversion for MQTT statuses.
1000  *
1001  * @param[in] status The status to convert to a string.
1002  *
1003  * @return The string representation of the status.
1004  */
1005 /* @[declare_mqtt_status_strerror] */
1006 const char * MQTT_Status_strerror( MQTTStatus_t status );
1007 /* @[declare_mqtt_status_strerror] */
1008
1009 /* *INDENT-OFF* */
1010 #ifdef __cplusplus
1011     }
1012 #endif
1013 /* *INDENT-ON* */
1014
1015 #endif /* ifndef CORE_MQTT_H */