/* * coreMQTT v1.2.0 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** * @file core_mqtt.h * @brief User-facing functions of the MQTT 3.1.1 library. */ #ifndef CORE_MQTT_H #define CORE_MQTT_H /* *INDENT-OFF* */ #ifdef __cplusplus extern "C" { #endif /* *INDENT-ON* */ /* MQTT_DO_NOT_USE_CUSTOM_CONFIG allows building the MQTT library * without a custom config. If a custom config is provided, the * MQTT_DO_NOT_USE_CUSTOM_CONFIG macro should not be defined. */ #ifndef MQTT_DO_NOT_USE_CUSTOM_CONFIG /* Include custom config file before other headers. */ #include "core_mqtt_config.h" #endif /* Include config defaults header to get default values of configs not * defined in core_mqtt_config.h file. */ #include "core_mqtt_config_defaults.h" /* Include MQTT serializer library. */ #include "core_mqtt_serializer.h" /* Include transport interface. */ #include "transport_interface.h" /** * @cond DOXYGEN_IGNORE * The current version of this library. */ #define MQTT_LIBRARY_VERSION "v1.2.0" /** @endcond */ /** * @ingroup mqtt_constants * @brief Invalid packet identifier. * * Zero is an invalid packet identifier as per MQTT v3.1.1 spec. */ #define MQTT_PACKET_ID_INVALID ( ( uint16_t ) 0U ) /* Structures defined in this file. */ struct MQTTPubAckInfo; struct MQTTContext; struct MQTTDeserializedInfo; /** * @ingroup mqtt_callback_types * @brief Application provided function to query the current time in * milliseconds. * * @return The current time in milliseconds. */ typedef uint32_t (* MQTTGetCurrentTimeFunc_t )( void ); /** * @ingroup mqtt_callback_types * @brief Application callback for receiving incoming publishes and incoming * acks. * * @note This callback will be called only if packets are deserialized with a * result of #MQTTSuccess or #MQTTServerRefused. The latter can be obtained * when deserializing a SUBACK, indicating a broker's rejection of a subscribe. * * @param[in] pContext Initialized MQTT context. * @param[in] pPacketInfo Information on the type of incoming MQTT packet. * @param[in] pDeserializedInfo Deserialized information from incoming packet. */ typedef void (* MQTTEventCallback_t )( struct MQTTContext * pContext, struct MQTTPacketInfo * pPacketInfo, struct MQTTDeserializedInfo * pDeserializedInfo ); /** * @ingroup mqtt_enum_types * @brief Values indicating if an MQTT connection exists. */ typedef enum MQTTConnectionStatus { MQTTNotConnected, /**< @brief MQTT Connection is inactive. */ MQTTConnected /**< @brief MQTT Connection is active. */ } MQTTConnectionStatus_t; /** * @ingroup mqtt_enum_types * @brief The state of QoS 1 or QoS 2 MQTT publishes, used in the state engine. */ typedef enum MQTTPublishState { MQTTStateNull = 0, /**< @brief An empty state with no corresponding PUBLISH. */ MQTTPublishSend, /**< @brief The library will send an outgoing PUBLISH packet. */ MQTTPubAckSend, /**< @brief The library will send a PUBACK for a received PUBLISH. */ MQTTPubRecSend, /**< @brief The library will send a PUBREC for a received PUBLISH. */ MQTTPubRelSend, /**< @brief The library will send a PUBREL for a received PUBREC. */ MQTTPubCompSend, /**< @brief The library will send a PUBCOMP for a received PUBREL. */ MQTTPubAckPending, /**< @brief The library is awaiting a PUBACK for an outgoing PUBLISH. */ MQTTPubRecPending, /**< @brief The library is awaiting a PUBREC for an outgoing PUBLISH. */ MQTTPubRelPending, /**< @brief The library is awaiting a PUBREL for an incoming PUBLISH. */ MQTTPubCompPending, /**< @brief The library is awaiting a PUBCOMP for an outgoing PUBLISH. */ MQTTPublishDone /**< @brief The PUBLISH has been completed. */ } MQTTPublishState_t; /** * @ingroup mqtt_enum_types * @brief Packet types used in acknowledging QoS 1 or QoS 2 publishes. */ typedef enum MQTTPubAckType { MQTTPuback, /**< @brief PUBACKs are sent in response to a QoS 1 PUBLISH. */ MQTTPubrec, /**< @brief PUBRECs are sent in response to a QoS 2 PUBLISH. */ MQTTPubrel, /**< @brief PUBRELs are sent in response to a PUBREC. */ MQTTPubcomp /**< @brief PUBCOMPs are sent in response to a PUBREL. */ } MQTTPubAckType_t; /** * @ingroup mqtt_enum_types * @brief The status codes in the SUBACK response to a subscription request. */ typedef enum MQTTSubAckStatus { MQTTSubAckSuccessQos0 = 0x00, /**< @brief Success with a maximum delivery at QoS 0. */ MQTTSubAckSuccessQos1 = 0x01, /**< @brief Success with a maximum delivery at QoS 1. */ MQTTSubAckSuccessQos2 = 0x02, /**< @brief Success with a maximum delivery at QoS 2. */ MQTTSubAckFailure = 0x80 /**< @brief Failure. */ } MQTTSubAckStatus_t; /** * @ingroup mqtt_struct_types * @brief An element of the state engine records for QoS 1 or Qos 2 publishes. */ typedef struct MQTTPubAckInfo { uint16_t packetId; /**< @brief The packet ID of the original PUBLISH. */ MQTTQoS_t qos; /**< @brief The QoS of the original PUBLISH. */ MQTTPublishState_t publishState; /**< @brief The current state of the publish process. */ } MQTTPubAckInfo_t; /** * @ingroup mqtt_struct_types * @brief A struct representing an MQTT connection. */ typedef struct MQTTContext { /** * @brief State engine records for outgoing publishes. */ MQTTPubAckInfo_t outgoingPublishRecords[ MQTT_STATE_ARRAY_MAX_COUNT ]; /** * @brief State engine records for incoming publishes. */ MQTTPubAckInfo_t incomingPublishRecords[ MQTT_STATE_ARRAY_MAX_COUNT ]; /** * @brief The transport interface used by the MQTT connection. */ TransportInterface_t transportInterface; /** * @brief The buffer used in sending and receiving packets from the network. */ MQTTFixedBuffer_t networkBuffer; /** * @brief The next available ID for outgoing MQTT packets. */ uint16_t nextPacketId; /** * @brief Whether the context currently has a connection to the broker. */ MQTTConnectionStatus_t connectStatus; /** * @brief Function used to get millisecond timestamps. */ MQTTGetCurrentTimeFunc_t getTime; /** * @brief Callback function used to give deserialized MQTT packets to the application. */ MQTTEventCallback_t appCallback; /** * @brief Timestamp of the last packet sent by the library. */ uint32_t lastPacketTime; /** * @brief Whether the library sent a packet during a call of #MQTT_ProcessLoop or * #MQTT_ReceiveLoop. */ bool controlPacketSent; /* Keep alive members. */ uint16_t keepAliveIntervalSec; /**< @brief Keep Alive interval. */ uint32_t pingReqSendTimeMs; /**< @brief Timestamp of the last sent PINGREQ. */ bool waitingForPingResp; /**< @brief If the library is currently awaiting a PINGRESP. */ } MQTTContext_t; /** * @ingroup mqtt_struct_types * @brief Struct to hold deserialized packet information for an #MQTTEventCallback_t * callback. */ typedef struct MQTTDeserializedInfo { uint16_t packetIdentifier; /**< @brief Packet ID of deserialized packet. */ MQTTPublishInfo_t * pPublishInfo; /**< @brief Pointer to deserialized publish info. */ MQTTStatus_t deserializationResult; /**< @brief Return code of deserialization. */ } MQTTDeserializedInfo_t; /** * @brief Initialize an MQTT context. * * This function must be called on a #MQTTContext_t before any other function. * * @note The #MQTTGetCurrentTimeFunc_t function for querying time must be defined. If * there is no time implementation, it is the responsibility of the application * to provide a dummy function to always return 0, provide 0 timeouts for * all calls to #MQTT_Connect, #MQTT_ProcessLoop, and #MQTT_ReceiveLoop and configure * the #MQTT_RECV_POLLING_TIMEOUT_MS and #MQTT_SEND_RETRY_TIMEOUT_MS configurations * to be 0. This will result in loop functions running for a single iteration, and * #MQTT_Connect relying on #MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT to receive the CONNACK packet. * * @param[in] pContext The context to initialize. * @param[in] pTransportInterface The transport interface to use with the context. * @param[in] getTimeFunction The time utility function to use with the context. * @param[in] userCallback The user callback to use with the context to * notify about incoming packet events. * @param[in] pNetworkBuffer Network buffer provided for the context. * * @return #MQTTBadParameter if invalid parameters are passed; * #MQTTSuccess otherwise. * * Example * @code{c} * * // Function for obtaining a timestamp. * uint32_t getTimeStampMs(); * // Callback function for receiving packets. * void eventCallback( * MQTTContext_t * pContext, * MQTTPacketInfo_t * pPacketInfo, * MQTTDeserializedInfo_t * pDeserializedInfo * ); * // Network send. * int32_t networkSend( NetworkContext_t * pContext, const void * pBuffer, size_t bytes ); * // Network receive. * int32_t networkRecv( NetworkContext_t * pContext, void * pBuffer, size_t bytes ); * * MQTTContext_t mqttContext; * TransportInterface_t transport; * MQTTFixedBuffer_t fixedBuffer; * uint8_t buffer[ 1024 ]; * * // Clear context. * memset( ( void * ) &mqttContext, 0x00, sizeof( MQTTContext_t ) ); * * // Set transport interface members. * transport.pNetworkContext = &someTransportContext; * transport.send = networkSend; * transport.recv = networkRecv; * * // Set buffer members. * fixedBuffer.pBuffer = buffer; * fixedBuffer.size = 1024; * * status = MQTT_Init( &mqttContext, &transport, getTimeStampMs, eventCallback, &fixedBuffer ); * * if( status == MQTTSuccess ) * { * // Do something with mqttContext. The transport and fixedBuffer structs were * // copied into the context, so the original structs do not need to stay in scope. * } * @endcode */ /* @[declare_mqtt_init] */ MQTTStatus_t MQTT_Init( MQTTContext_t * pContext, const TransportInterface_t * pTransportInterface, MQTTGetCurrentTimeFunc_t getTimeFunction, MQTTEventCallback_t userCallback, const MQTTFixedBuffer_t * pNetworkBuffer ); /* @[declare_mqtt_init] */ /** * @brief Establish an MQTT session. * * This function will send MQTT CONNECT packet and receive a CONNACK packet. The * send and receive from the network is done through the transport interface. * * The maximum time this function waits for a CONNACK is decided in one of the * following ways: * 1. If @p timeoutMs is greater than 0: * #MQTTContext_t.getTime is used to ensure that the function does not wait * more than @p timeoutMs for CONNACK. * 2. If @p timeoutMs is 0: * The network receive for CONNACK is retried up to the number of times * configured by #MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT. * * @note If a dummy #MQTTGetCurrentTimeFunc_t was passed to #MQTT_Init, then a * timeout value of 0 MUST be passed to the API, and the #MQTT_RECV_POLLING_TIMEOUT_MS * and #MQTT_SEND_RETRY_TIMEOUT_MS timeout configurations MUST be set to 0. * * @param[in] pContext Initialized MQTT context. * @param[in] pConnectInfo MQTT CONNECT packet information. * @param[in] pWillInfo Last Will and Testament. Pass NULL if Last Will and * Testament is not used. * @param[in] timeoutMs Maximum time in milliseconds to wait for a CONNACK packet. * A zero timeout makes use of the retries for receiving CONNACK as configured with * #MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT. * @param[out] pSessionPresent Whether a previous session was present. * Only relevant if not establishing a clean session. * * @return #MQTTNoMemory if the #MQTTContext_t.networkBuffer is too small to * hold the MQTT packet; * #MQTTBadParameter if invalid parameters are passed; * #MQTTSendFailed if transport send failed; * #MQTTRecvFailed if transport receive failed for CONNACK; * #MQTTNoDataAvailable if no data available to receive in transport until * the @p timeoutMs for CONNACK; * #MQTTSuccess otherwise. * * @note This API may spend more time than provided in the timeoutMS parameters in * certain conditions as listed below: * * 1. Timeouts are incorrectly configured - If the timeoutMS is less than the * transport receive timeout and if a CONNACK packet is not received within * the transport receive timeout, the API will spend the transport receive * timeout (which is more time than the timeoutMs). It is the case of incorrect * timeout configuration as the timeoutMs parameter passed to this API must be * greater than the transport receive timeout. Please refer to the transport * interface documentation for more details about timeout configurations. * * 2. Partial CONNACK packet is received right before the expiry of the timeout - It * is possible that first two bytes of CONNACK packet (packet type and remaining * length) are received right before the expiry of the timeoutMS. In that case, * the API makes one more network receive call in an attempt to receive the remaining * 2 bytes. In the worst case, it can happen that the remaining 2 bytes are never * received and this API will end up spending timeoutMs + transport receive timeout. * * Example * @code{c} * * // Variables used in this example. * MQTTStatus_t status; * MQTTConnectInfo_t connectInfo = { 0 }; * MQTTPublishInfo_t willInfo = { 0 }; * bool sessionPresent; * // This is assumed to have been initialized before calling this function. * MQTTContext_t * pContext; * * // True for creating a new session with broker, false if we want to resume an old one. * connectInfo.cleanSession = true; * // Client ID must be unique to broker. This field is required. * connectInfo.pClientIdentifier = "someClientID"; * connectInfo.clientIdentifierLength = strlen( connectInfo.pClientIdentifier ); * * // The following fields are optional. * // Value for keep alive. * connectInfo.keepAliveSeconds = 60; * // Optional username and password. * connectInfo.pUserName = "someUserName"; * connectInfo.userNameLength = strlen( connectInfo.pUserName ); * connectInfo.pPassword = "somePassword"; * connectInfo.passwordLength = strlen( connectInfo.pPassword ); * * // The last will and testament is optional, it will be published by the broker * // should this client disconnect without sending a DISCONNECT packet. * willInfo.qos = MQTTQoS0; * willInfo.pTopicName = "/lwt/topic/name"; * willInfo.topicNameLength = strlen( willInfo.pTopicName ); * willInfo.pPayload = "LWT Message"; * willInfo.payloadLength = strlen( "LWT Message" ); * * // Send the connect packet. Use 100 ms as the timeout to wait for the CONNACK packet. * status = MQTT_Connect( pContext, &connectInfo, &willInfo, 100, &sessionPresent ); * * if( status == MQTTSuccess ) * { * // Since we requested a clean session, this must be false * assert( sessionPresent == false ); * * // Do something with the connection. * } * @endcode */ /* @[declare_mqtt_connect] */ MQTTStatus_t MQTT_Connect( MQTTContext_t * pContext, const MQTTConnectInfo_t * pConnectInfo, const MQTTPublishInfo_t * pWillInfo, uint32_t timeoutMs, bool * pSessionPresent ); /* @[declare_mqtt_connect] */ /** * @brief Sends MQTT SUBSCRIBE for the given list of topic filters to * the broker. * * @param[in] pContext Initialized MQTT context. * @param[in] pSubscriptionList List of MQTT subscription info. * @param[in] subscriptionCount The number of elements in pSubscriptionList. * @param[in] packetId Packet ID generated by #MQTT_GetPacketId. * * @return #MQTTNoMemory if the #MQTTContext_t.networkBuffer is too small to * hold the MQTT packet; * #MQTTBadParameter if invalid parameters are passed; * #MQTTSendFailed if transport write failed; * #MQTTSuccess otherwise. * * Example * @code{c} * * // Variables used in this example. * MQTTStatus_t status; * MQTTSubscribeInfo_t subscriptionList[ NUMBER_OF_SUBSCRIPTIONS ] = { 0 }; * uint16_t packetId; * // This context is assumed to be initialized and connected. * MQTTContext_t * pContext; * // This is assumed to be a list of filters we want to subscribe to. * const char * filters[ NUMBER_OF_SUBSCRIPTIONS ]; * * // Set each subscription. * for( int i = 0; i < NUMBER_OF_SUBSCRIPTIONS; i++ ) * { * subscriptionList[ i ].qos = MQTTQoS0; * // Each subscription needs a topic filter. * subscriptionList[ i ].pTopicFilter = filters[ i ]; * subscriptionList[ i ].topicFilterLength = strlen( filters[ i ] ); * } * * // Obtain a new packet id for the subscription. * packetId = MQTT_GetPacketId( pContext ); * * status = MQTT_Subscribe( pContext, &subscriptionList[ 0 ], NUMBER_OF_SUBSCRIPTIONS, packetId ); * * if( status == MQTTSuccess ) * { * // We must now call MQTT_ReceiveLoop() or MQTT_ProcessLoop() to receive the SUBACK. * // If the broker accepts the subscription we can now receive publishes * // on the requested topics. * } * @endcode */ /* @[declare_mqtt_subscribe] */ MQTTStatus_t MQTT_Subscribe( MQTTContext_t * pContext, const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, uint16_t packetId ); /* @[declare_mqtt_subscribe] */ /** * @brief Publishes a message to the given topic name. * * @param[in] pContext Initialized MQTT context. * @param[in] pPublishInfo MQTT PUBLISH packet parameters. * @param[in] packetId packet ID generated by #MQTT_GetPacketId. * * @return #MQTTNoMemory if pBuffer is too small to hold the MQTT packet; * #MQTTBadParameter if invalid parameters are passed; * #MQTTSendFailed if transport write failed; * #MQTTSuccess otherwise. * * Example * @code{c} * * // Variables used in this example. * MQTTStatus_t status; * MQTTPublishInfo_t publishInfo; * uint16_t packetId; * // This context is assumed to be initialized and connected. * MQTTContext_t * pContext; * * // QoS of publish. * publishInfo.qos = MQTTQoS1; * publishInfo.pTopicName = "/some/topic/name"; * publishInfo.topicNameLength = strlen( publishInfo.pTopicName ); * publishInfo.pPayload = "Hello World!"; * publishInfo.payloadLength = strlen( "Hello World!" ); * * // Packet ID is needed for QoS > 0. * packetId = MQTT_GetPacketId( pContext ); * * status = MQTT_Publish( pContext, &publishInfo, packetId ); * * if( status == MQTTSuccess ) * { * // Since the QoS is > 0, we will need to call MQTT_ReceiveLoop() * // or MQTT_ProcessLoop() to process the publish acknowledgments. * } * @endcode */ /* @[declare_mqtt_publish] */ MQTTStatus_t MQTT_Publish( MQTTContext_t * pContext, const MQTTPublishInfo_t * pPublishInfo, uint16_t packetId ); /* @[declare_mqtt_publish] */ /** * @brief Sends an MQTT PINGREQ to broker. * * @param[in] pContext Initialized and connected MQTT context. * * @return #MQTTNoMemory if pBuffer is too small to hold the MQTT packet; * #MQTTBadParameter if invalid parameters are passed; * #MQTTSendFailed if transport write failed; * #MQTTSuccess otherwise. */ /* @[declare_mqtt_ping] */ MQTTStatus_t MQTT_Ping( MQTTContext_t * pContext ); /* @[declare_mqtt_ping] */ /** * @brief Sends MQTT UNSUBSCRIBE for the given list of topic filters to * the broker. * * @param[in] pContext Initialized MQTT context. * @param[in] pSubscriptionList List of MQTT subscription info. * @param[in] subscriptionCount The number of elements in pSubscriptionList. * @param[in] packetId packet ID generated by #MQTT_GetPacketId. * * @return #MQTTNoMemory if the #MQTTContext_t.networkBuffer is too small to * hold the MQTT packet; * #MQTTBadParameter if invalid parameters are passed; * #MQTTSendFailed if transport write failed; * #MQTTSuccess otherwise. * * Example * @code{c} * * // Variables used in this example. * MQTTStatus_t status; * MQTTSubscribeInfo_t unsubscribeList[ NUMBER_OF_SUBSCRIPTIONS ] = { 0 }; * uint16_t packetId; * // This context is assumed to be initialized and connected. * MQTTContext_t * pContext; * // This is assumed to be a list of filters we want to unsubscribe from. * const char * filters[ NUMBER_OF_SUBSCRIPTIONS ]; * * // Set information for each unsubscribe request. * for( int i = 0; i < NUMBER_OF_SUBSCRIPTIONS; i++ ) * { * unsubscribeList[ i ].pTopicFilter = filters[ i ]; * unsubscribeList[ i ].topicFilterLength = strlen( filters[ i ] ); * * // The QoS field of MQTT_SubscribeInfo_t is unused for unsubscribing. * } * * // Obtain a new packet id for the unsubscribe request. * packetId = MQTT_GetPacketId( pContext ); * * status = MQTT_Unsubscribe( pContext, &unsubscribeList[ 0 ], NUMBER_OF_SUBSCRIPTIONS, packetId ); * * if( status == MQTTSuccess ) * { * // We must now call MQTT_ReceiveLoop() or MQTT_ProcessLoop() to receive the UNSUBACK. * // After this the broker should no longer send publishes for these topics. * } * @endcode */ /* @[declare_mqtt_unsubscribe] */ MQTTStatus_t MQTT_Unsubscribe( MQTTContext_t * pContext, const MQTTSubscribeInfo_t * pSubscriptionList, size_t subscriptionCount, uint16_t packetId ); /* @[declare_mqtt_unsubscribe] */ /** * @brief Disconnect an MQTT session. * * @param[in] pContext Initialized and connected MQTT context. * * @return #MQTTNoMemory if the #MQTTContext_t.networkBuffer is too small to * hold the MQTT packet; * #MQTTBadParameter if invalid parameters are passed; * #MQTTSendFailed if transport send failed; * #MQTTSuccess otherwise. */ /* @[declare_mqtt_disconnect] */ MQTTStatus_t MQTT_Disconnect( MQTTContext_t * pContext ); /* @[declare_mqtt_disconnect] */ /** * @brief Loop to receive packets from the transport interface. Handles keep * alive. * * @note Passing a timeout value of 0 will run the loop for a single iteration. * * @note If a dummy timer function, #MQTTGetCurrentTimeFunc_t, is passed to the library, * then the keep-alive mechanism is not supported by the #MQTT_ProcessLoop API. * In that case, the #MQTT_ReceiveLoop API function should be used instead. * * @param[in] pContext Initialized and connected MQTT context. * @param[in] timeoutMs Minimum time in milliseconds that the receive loop will * run, unless an error occurs. * * @note Calling this function blocks the calling context for a time period that * depends on the passed @p timeoutMs, the configuration macros, #MQTT_RECV_POLLING_TIMEOUT_MS * and #MQTT_SEND_RETRY_TIMEOUT_MS, and the underlying transport interface implementation * timeouts, unless an error occurs. The blocking period also depends on the execution time of the * #MQTTEventCallback_t callback supplied to the library. It is recommended that the supplied * #MQTTEventCallback_t callback does not contain blocking operations to prevent potential * non-deterministic blocking period of the #MQTT_ProcessLoop API call. * * @return #MQTTBadParameter if context is NULL; * #MQTTRecvFailed if a network error occurs during reception; * #MQTTSendFailed if a network error occurs while sending an ACK or PINGREQ; * #MQTTBadResponse if an invalid packet is received; * #MQTTKeepAliveTimeout if the server has not sent a PINGRESP before * #MQTT_PINGRESP_TIMEOUT_MS milliseconds; * #MQTTIllegalState if an incoming QoS 1/2 publish or ack causes an * invalid transition for the internal state machine; * #MQTTSuccess on success. * * Example * @code{c} * * // Variables used in this example. * MQTTStatus_t status; * uint32_t timeoutMs = 100; * // This context is assumed to be initialized and connected. * MQTTContext_t * pContext; * * while( true ) * { * status = MQTT_ProcessLoop( pContext, timeoutMs ); * * if( status != MQTTSuccess ) * { * // Determine the error. It's possible we might need to disconnect * // the underlying transport connection. * } * else * { * // Other application functions. * } * } * @endcode */ /* @[declare_mqtt_processloop] */ MQTTStatus_t MQTT_ProcessLoop( MQTTContext_t * pContext, uint32_t timeoutMs ); /* @[declare_mqtt_processloop] */ /** * @brief Loop to receive packets from the transport interface. Does not handle * keep alive. * * @note Passing a timeout value of 0 will run the loop for a single iteration. * If a dummy #MQTTGetCurrentTimeFunc_t was passed to #MQTT_Init, then the timeout * value passed to the API MUST be 0, and the #MQTT_RECV_POLLING_TIMEOUT_MS * and #MQTT_SEND_RETRY_TIMEOUT_MS timeout configurations MUST be set to 0. * * @param[in] pContext Initialized and connected MQTT context. * @param[in] timeoutMs Minimum time in milliseconds that the receive loop will * run, unless an error occurs. * * @note Calling this function blocks the calling context for a time period that * depends on the passed @p timeoutMs, the configuration macros, #MQTT_RECV_POLLING_TIMEOUT_MS * and #MQTT_SEND_RETRY_TIMEOUT_MS, and the underlying transport interface implementation * timeouts, unless an error occurs. The blocking period also depends on the execution time of the * #MQTTEventCallback_t callback supplied to the library. It is recommended that the supplied * #MQTTEventCallback_t callback does not contain blocking operations to prevent potential * non-deterministic blocking period of the #MQTT_ReceiveLoop API call. * * @return #MQTTBadParameter if context is NULL; * #MQTTRecvFailed if a network error occurs during reception; * #MQTTSendFailed if a network error occurs while sending an ACK or PINGREQ; * #MQTTBadResponse if an invalid packet is received; * #MQTTIllegalState if an incoming QoS 1/2 publish or ack causes an * invalid transition for the internal state machine; * #MQTTSuccess on success. * * Example * @code{c} * * // Variables used in this example. * MQTTStatus_t status; * uint32_t timeoutMs = 100; * uint32_t keepAliveMs = 60 * 1000; * // This context is assumed to be initialized and connected. * MQTTContext_t * pContext; * * while( true ) * { * status = MQTT_ReceiveLoop( pContext, timeoutMs ); * * if( status != MQTTSuccess ) * { * // Determine the error. It's possible we might need to disconnect * // the underlying transport connection. * } * else * { * // Since this function does not send pings, the application may need * // to in order to comply with keep alive. * if( ( pContext->getTime() - pContext->lastPacketTime ) > keepAliveMs ) * { * status = MQTT_Ping( pContext ); * } * * // Other application functions. * } * } * @endcode */ /* @[declare_mqtt_receiveloop] */ MQTTStatus_t MQTT_ReceiveLoop( MQTTContext_t * pContext, uint32_t timeoutMs ); /* @[declare_mqtt_receiveloop] */ /** * @brief Get a packet ID that is valid according to the MQTT 3.1.1 spec. * * @param[in] pContext Initialized MQTT context. * * @return A non-zero number. */ /* @[declare_mqtt_getpacketid] */ uint16_t MQTT_GetPacketId( MQTTContext_t * pContext ); /* @[declare_mqtt_getpacketid] */ /** * @brief A utility function that determines whether the passed topic filter and * topic name match according to the MQTT 3.1.1 protocol specification. * * @param[in] pTopicName The topic name to check. * @param[in] topicNameLength Length of the topic name. * @param[in] pTopicFilter The topic filter to check. * @param[in] topicFilterLength Length of topic filter. * @param[out] pIsMatch This is filled with the whether there * exists a match or not. * * @note The API assumes that the passed topic name is valid to meet the * requirements of the MQTT 3.1.1 specification. Invalid topic names (for example, * containing wildcard characters) should not be passed to the function. * Also, the API checks validity of topic filter for wildcard characters ONLY if * the passed topic name and topic filter do not have an exact string match. * * @return Returns one of the following: * - #MQTTBadParameter, if any of the input parameters is invalid. * - #MQTTSuccess, if the matching operation was performed. * * Example * @code{c} * * // Variables used in this example. * const char * pTopic = "topic/match/1"; * const char * pFilter = "topic/#"; * MQTTStatus_t status = MQTTSuccess; * bool match = false; * * status = MQTT_MatchTopic( pTopic, strlen( pTopic ), pFilter, strlen( pFilter ), &match ); * // Our parameters were valid, so this will return success. * assert( status == MQTTSuccess ); * * // For this specific example, we already know this value is true. This * // check is placed here as an example for use with variable topic names. * if( match ) * { * // Application can decide what to do with the matching topic name. * } * @endcode */ MQTTStatus_t MQTT_MatchTopic( const char * pTopicName, const uint16_t topicNameLength, const char * pTopicFilter, const uint16_t topicFilterLength, bool * pIsMatch ); /** * @brief Parses the payload of an MQTT SUBACK packet that contains status codes * corresponding to topic filter subscription requests from the original * subscribe packet. * * Each return code in the SUBACK packet corresponds to a topic filter in the * SUBSCRIBE Packet being acknowledged. * The status codes can be one of the following: * - 0x00 - Success - Maximum QoS 0 * - 0x01 - Success - Maximum QoS 1 * - 0x02 - Success - Maximum QoS 2 * - 0x80 - Failure * Refer to #MQTTSubAckStatus_t for the status codes. * * @param[in] pSubackPacket The SUBACK packet whose payload is to be parsed. * @param[out] pPayloadStart This is populated with the starting address * of the payload (or return codes for topic filters) in the SUBACK packet. * @param[out] pPayloadSize This is populated with the size of the payload * in the SUBACK packet. It represents the number of topic filters whose * SUBACK status is present in the packet. * * @return Returns one of the following: * - #MQTTBadParameter if the input SUBACK packet is invalid. * - #MQTTSuccess if parsing the payload was successful. * * Example * @code{c} * * // Global variable used in this example. * // This is assumed to be the subscription list in the original SUBSCRIBE packet. * MQTTSubscribeInfo_t pSubscribes[ NUMBER_OF_SUBSCRIPTIONS ]; * * // MQTT_GetSubAckStatusCodes is intended to be used from the application * // callback that is called by the library in MQTT_ProcessLoop or MQTT_ReceiveLoop. * void eventCallback( * MQTTContext_t * pContext, * MQTTPacketInfo_t * pPacketInfo, * MQTTDeserializedInfo_t * pDeserializedInfo * ) * { * MQTTStatus_t status = MQTTSuccess; * uint8_t * pCodes; * size_t numCodes; * * if( pPacketInfo->type == MQTT_PACKET_TYPE_SUBACK ) * { * status = MQTT_GetSubAckStatusCodes( pPacketInfo, &pCodes, &numCodes ); * * // Since the pointers to the payload and payload size are not NULL, and * // we use the packet info struct passed to the app callback (verified * // to be valid by the library), this function must return success. * assert( status == MQTTSuccess ); * // The server must send a response code for each topic filter in the * // original SUBSCRIBE packet. * assert( numCodes == NUMBER_OF_SUBSCRIPTIONS ); * * for( int i = 0; i < numCodes; i++ ) * { * // The only failure code is 0x80 = MQTTSubAckFailure. * if( pCodes[ i ] == MQTTSubAckFailure ) * { * // The subscription failed, we may want to retry the * // subscription in pSubscribes[ i ] outside of this callback. * } * else * { * // The subscription was granted, but the maximum QoS may be * // lower than what was requested. We can verify the granted QoS. * if( pSubscribes[ i ].qos != pCodes[ i ] ) * { * LogWarn( ( * "Requested QoS %u, but granted QoS %u for %s", * pSubscribes[ i ].qos, pCodes[ i ], pSubscribes[ i ].pTopicFilter * ) ); * } * } * } * } * // Handle other packet types. * } * @endcode */ /* @[declare_mqtt_getsubackstatuscodes] */ MQTTStatus_t MQTT_GetSubAckStatusCodes( const MQTTPacketInfo_t * pSubackPacket, uint8_t ** pPayloadStart, size_t * pPayloadSize ); /* @[declare_mqtt_getsubackstatuscodes] */ /** * @brief Error code to string conversion for MQTT statuses. * * @param[in] status The status to convert to a string. * * @return The string representation of the status. */ /* @[declare_mqtt_status_strerror] */ const char * MQTT_Status_strerror( MQTTStatus_t status ); /* @[declare_mqtt_status_strerror] */ /* *INDENT-OFF* */ #ifdef __cplusplus } #endif /* *INDENT-ON* */ #endif /* ifndef CORE_MQTT_H */