text: /*
   * FreeRTOS V202012.00
   * 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.
   *
   * https://www.FreeRTOS.org
   * https://github.com/FreeRTOS
   *
   */
  
  /*
   * Demo for showing use of the MQTT API using a mutually authenticated
   * network connection.
   *
   * The Example shown below uses MQTT APIs to create MQTT messages and send them
   * over the mutually authenticated network connection established with the
   * MQTT broker. This example is single threaded and uses statically allocated
   * memory. It uses QoS1 for sending to and receiving messages from the broker.
   *
   * A mutually authenticated TLS connection is used to connect to the
   * MQTT message broker in this example. Define democonfigMQTT_BROKER_ENDPOINT,
   * democonfigROOT_CA_PEM, democonfigCLIENT_CERTIFICATE_PEM,
   * and democonfigCLIENT_PRIVATE_KEY_PEM in demo_config.h to establish a
   * mutually authenticated connection.
   */
  
  /* Standard includes. */
  #include <string.h>
  #include <stdio.h>
  
  /* Kernel includes. */
  #include "FreeRTOS.h"
  #include "task.h"
  
  /* Demo Specific configs. */
  #include "demo_config.h"
  
  /* MQTT library includes. */
  #include "core_mqtt.h"
  
  /* Transport interface implementation include header for TLS. */
  #include "using_wolfSSL.h"
  
  /*-----------------------------------------------------------*/
  
  /* Compile time error for undefined configs. */
  #ifndef democonfigMQTT_BROKER_ENDPOINT
      #error "Define the config democonfigMQTT_BROKER_ENDPOINT by following the instructions in file demo_config.h."
  #endif
  #ifndef democonfigROOT_CA_PEM
      #error "Please define Root CA certificate of the MQTT broker(democonfigROOT_CA_PEM) in demo_config.h."
  #endif
  #ifndef democonfigCLIENT_CERTIFICATE_PEM
      #error "Please define client certificate(democonfigCLIENT_CERTIFICATE_PEM) in demo_config.h."
  #endif
  #ifndef democonfigCLIENT_PRIVATE_KEY_PEM
      #error "Please define client private key(democonfigCLIENT_PRIVATE_KEY_PEM) in demo_config.h."
  #endif
  
  /*-----------------------------------------------------------*/
  
  /* Default values for configs. */
  #ifndef democonfigCLIENT_IDENTIFIER
  
  /**
   * @brief The MQTT client identifier used in this example.  Each client identifier
   * must be unique so edit as required to ensure no two clients connecting to the
   * same broker use the same client identifier.
   *
   * @note Appending __TIME__ to the client id string will help to create a unique
   * client id every time an application binary is built. Only a single instance of
   * this application's compiled binary may be used at a time, since the client ID
   * will always be the same.
   */
      #define democonfigCLIENT_IDENTIFIER    "testClient"__TIME__
  #endif
  
  #ifndef democonfigMQTT_BROKER_PORT
  
  /**
   * @brief The port to use for the demo.
   */
      #define democonfigMQTT_BROKER_PORT    ( 8883 )
  #endif
  
  /*-----------------------------------------------------------*/
  
  /**
   * @brief Timeout for receiving CONNACK packet in milliseconds.
   */
  #define mqttexampleCONNACK_RECV_TIMEOUT_MS                ( 1000U )
  
  /**
   * @brief The topic to subscribe and publish to in the example.
   *
   * The topic name starts with the client identifier to ensure that each demo
   * interacts with a unique topic name.
   */
  #define mqttexampleTOPIC                                  democonfigCLIENT_IDENTIFIER "/example/topic"
  
  /**
   * @brief The MQTT message published in this example.
   */
  #define mqttexampleMESSAGE                                "Hello World!"
  
  /**
   * @brief Time in ticks to wait between each cycle of the demo implemented
   * by prvMQTTDemoTask().
   */
  #define mqttexampleDELAY_BETWEEN_DEMO_ITERATIONS_TICKS    ( pdMS_TO_TICKS( 5000U ) )
  
  /**
   * @brief Timeout for MQTT_ProcessLoop in milliseconds.
   */
  #define mqttexamplePROCESS_LOOP_TIMEOUT_MS                ( 500U )
  
  /**
   * @brief Keep alive time reported to the broker while establishing
   * an MQTT connection.
   *
   * It is the responsibility of the Client to ensure that the interval between
   * Control Packets being sent does not exceed the this Keep Alive value. In the
   * absence of sending any other Control Packets, the Client MUST send a
   * PINGREQ Packet.
   */
  #define mqttexampleKEEP_ALIVE_TIMEOUT_SECONDS             ( 60U )
  
  /**
   * @brief Delay (in ticks) between consecutive cycles of MQTT publish operations in a
   * demo iteration.
   *
   * Note that the process loop also has a timeout, so the total time between
   * publishes is the sum of the two delays.
   */
  #define mqttexampleDELAY_BETWEEN_PUBLISHES_TICKS          ( pdMS_TO_TICKS( 2000U ) )
  
  /**
   * @brief Transport timeout in milliseconds for transport send and receive.
   */
  #define mqttexampleTRANSPORT_SEND_RECV_TIMEOUT_MS         ( 1000U )
  
  /**
   * @brief Milliseconds per second.
   */
  #define _MILLISECONDS_PER_SECOND                          ( 1000U )
  
  /**
   * @brief Milliseconds per FreeRTOS tick.
   */
  #define _MILLISECONDS_PER_TICK                            ( _MILLISECONDS_PER_SECOND / configTICK_RATE_HZ )
  
  /*-----------------------------------------------------------*/
  
  /**
   * @brief The task used to demonstrate the MQTT API.
   *
   * @param[in] pvParameters Parameters as passed at the time of task creation. Not
   * used in this example.
   */
  static void prvMQTTDemoTask( void * pvParameters );
  
  /**
   * @brief Sends an MQTT Connect packet over the already connected TLS over TCP connection.
   *
   * @param[in, out] pxMQTTContext MQTT context pointer.
   * @param[in] xNetworkContext network context.
   */
  static void prvCreateMQTTConnectionWithBroker( MQTTContext_t * pxMQTTContext,
                                                 NetworkContext_t * pxNetworkContext );
  
  /**
   * @brief Subscribes to the topic as specified in mqttexampleTOPIC at the top of
   * this file.
   *
   * @param[in] pxMQTTContext MQTT context pointer.
   */
  static void prvMQTTSubscribeToTopic( MQTTContext_t * pxMQTTContext );
  
  /**
   * @brief Publishes a message mqttexampleMESSAGE on mqttexampleTOPIC topic.
   *
   * @param[in] pxMQTTContext MQTT context pointer.
   */
  static void prvMQTTPublishToTopic( MQTTContext_t * pxMQTTContext );
  
  /**
   * @brief Unsubscribes from the previously subscribed topic as specified
   * in mqttexampleTOPIC.
   *
   * @param[in] pxMQTTContext MQTT context pointer.
   */
  static void prvMQTTUnsubscribeFromTopic( MQTTContext_t * pxMQTTContext );
  
  /**
   * @brief The timer query function provided to the MQTT context.
   *
   * @return Time in milliseconds.
   */
  static uint32_t prvGetTimeMs( void );
  
  /**
   * @brief Process a response or ack to an MQTT request (PING, PUBLISH,
   * SUBSCRIBE or UNSUBSCRIBE). This function processes PINGRESP, PUBACK,
   * SUBACK, and UNSUBACK.
   *
   * @param[in] pxIncomingPacket is a pointer to structure containing deserialized
   * MQTT response.
   * @param[in] usPacketId is the packet identifier from the ack received.
   */
  static void prvMQTTProcessResponse( MQTTPacketInfo_t * pxIncomingPacket,
                                      uint16_t usPacketId );
  
  /**
   * @brief Process incoming Publish message.
   *
   * @param[in] pxPublishInfo is a pointer to structure containing deserialized
   * Publish message.
   */
  static void prvMQTTProcessIncomingPublish( MQTTPublishInfo_t * pxPublishInfo );
  
  /**
   * @brief The application callback function for getting the incoming publishes,
   * incoming acks, and ping responses reported from the MQTT library.
   *
   * @param[in] pxMQTTContext MQTT context pointer.
   * @param[in] pxPacketInfo Packet Info pointer for the incoming packet.
   * @param[in] pxDeserializedInfo Deserialized information from the incoming packet.
   */
  static void prvEventCallback( MQTTContext_t * pxMQTTContext,
                                MQTTPacketInfo_t * pxPacketInfo,
                                MQTTDeserializedInfo_t * pxDeserializedInfo );
  
  /**
   * @brief TLS connect to endpoint democonfigMQTT_BROKER_ENDPOINT.
   *
   * @param[in] pxNetworkCredentials Network credentials to establish a TLS connection
   * with democonfigMQTT_BROKER_ENDPOINT.
   * @param[in] pxNetworkCredentials Network context.
   */
  static void prvTLSConnect( NetworkCredentials_t * pxNetworkCredentials,
                             NetworkContext_t * pxNetworkContext );
  
  /*-----------------------------------------------------------*/
  
  /* @brief Static buffer used to hold MQTT messages being sent and received. */
  static uint8_t ucSharedBuffer[ democonfigNETWORK_BUFFER_SIZE ];
  
  /**
   * @brief Global entry time into the application to use as a reference timestamp
   * in the #prvGetTimeMs function. #prvGetTimeMs will always return the difference
   * between the current time and the global entry time. This will reduce the chances
   * of overflow for the 32 bit unsigned integer used for holding the timestamp.
   */
  static uint32_t ulGlobalEntryTimeMs;
  
  /**
   * @brief Packet Identifier generated when Publish request was sent to the broker;
   * it is used to match received Publish ACK to the transmitted Publish packet.
   */
  static uint16_t usPublishPacketIdentifier;
  
  /**
   * @brief Packet Identifier generated when Subscribe request was sent to the broker;
   * it is used to match received Subscribe ACK to the transmitted Subscribe packet.
   */
  static uint16_t usSubscribePacketIdentifier;
  
  /**
   * @brief Packet Identifier generated when Unsubscribe request was sent to the broker;
   * it is used to match received Unsubscribe response to the transmitted Unsubscribe
   * request.
   */
  static uint16_t usUnsubscribePacketIdentifier;
  
  /** @brief Static buffer used to hold MQTT messages being sent and received. */
  static MQTTFixedBuffer_t xBuffer =
  {
      ucSharedBuffer,
      democonfigNETWORK_BUFFER_SIZE
  };
  
  /*-----------------------------------------------------------*/
  
  /*
   * @brief Create the task that demonstrates the MQTT API Demo over a
   * mutually authenticated network connection with MQTT broker.
   */
  void vStartSimpleMQTTDemo( void )
  {
      /* This example uses a single application task, which in turn is used to
       * connect, subscribe, publish, unsubscribe and disconnect from the MQTT
       * broker. */
      xTaskCreate( prvMQTTDemoTask,          /* Function that implements the task. */
                   "MQTTDemo",               /* Text name for the task - only used for debugging. */
                   democonfigDEMO_STACKSIZE, /* Size of stack (in words, not bytes) to allocate for the task. */
                   NULL,                     /* Task parameter - not used in this case. */
                   tskIDLE_PRIORITY,         /* Task priority, must be between 0 and configMAX_PRIORITIES - 1. */
                   NULL );                   /* Used to pass out a handle to the created task - not used in this case. */
  }
  /*-----------------------------------------------------------*/
  
  /*
   * @brief The Example shown below uses MQTT APIs to create MQTT messages and
   * send them over the mutually authenticated network connection established with the
   * MQTT broker. This example is single threaded and uses statically allocated
   * memory. It uses QoS1 for sending to and receiving messages from the broker.
   *
   * This MQTT client subscribes to the topic as specified in mqttexampleTOPIC at the
   * top of this file by sending a subscribe packet and then waiting for a subscribe
   * acknowledgment (SUBACK).This client will then publish to the same topic it
   * subscribed to, so it will expect all the messages it sends to the broker to be
   * sent back to it from the broker.
   */
  static void prvMQTTDemoTask( void * pvParameters )
  {
      uint32_t ulPublishCount = 0U;
      const uint32_t ulMaxPublishCount = 5UL;
      NetworkContext_t xNetworkContext = { 0 };
      NetworkCredentials_t xNetworkCredentials = { 0 };
      MQTTContext_t xMQTTContext = { 0 };
      MQTTStatus_t xMQTTStatus;
  
      /* Remove compiler warnings about unused parameters. */
      ( void ) pvParameters;
  
      /* Set the entry time of the demo application. This entry time will be used
       * to calculate relative time elapsed in the execution of the demo application,
       * by the timer utility function that is provided to the MQTT library.
       */
      ulGlobalEntryTimeMs = prvGetTimeMs();
  
      for( ; ; )
      {
          /****************************** Connect. ******************************/
  
          /* Establish a TLS connection with the MQTT broker. This example connects to
           * the MQTT broker as specified by democonfigMQTT_BROKER_ENDPOINT and
           * democonfigMQTT_BROKER_PORT in the demo_config.h file. */
          LogInfo( ( "Creating a TLS connection to %s:%u.\r\n",
                     democonfigMQTT_BROKER_ENDPOINT,
                     democonfigMQTT_BROKER_PORT ) );
          prvTLSConnect( &xNetworkCredentials, &xNetworkContext );
  
          /* Sends an MQTT Connect packet over the already established TLS connection,
           * and waits for connection acknowledgment (CONNACK) packet. */
          LogInfo( ( "Creating an MQTT connection to %s.\r\n", democonfigMQTT_BROKER_ENDPOINT ) );
          prvCreateMQTTConnectionWithBroker( &xMQTTContext, &xNetworkContext );
  
          /**************************** Subscribe. ******************************/
  
          /* The client is now connected to the broker. Subscribe to the topic
           * as specified in mqttexampleTOPIC at the top of this file by sending a
           * subscribe packet then waiting for a subscribe acknowledgment (SUBACK).
           * The function #prvMQTTSubscribeToTopic will not wait to receive a SUBACK,
           * but the function #MQTT_ProcessLoop will attempt to receive the SUBACK
           * from network and if a SUBACK is received, application will be notified
           * through the callback registered (#prvEventCallback for this application).
           * This demo uses QoS1 in Subscribe, therefore, the Publish messages
           * received from the broker will have QoS1. */
          LogInfo( ( "Attempt to subscribe to the MQTT topic %s.\r\n", mqttexampleTOPIC ) );
          prvMQTTSubscribeToTopic( &xMQTTContext );
  
          /* Process incoming packet from the broker. After sending the subscribe, the
           * client may receive a publish before it receives a subscribe ack. Therefore,
           * call generic incoming packet processing function. Since this demo is
           * subscribing to the topic to which no one is publishing, probability of
           * receiving Publish message before subscribe ack is zero; but application
           * must be ready to receive any packet.  This demo uses the generic packet
           * processing function everywhere to highlight this fact. */
          xMQTTStatus = MQTT_ProcessLoop( &xMQTTContext, mqttexamplePROCESS_LOOP_TIMEOUT_MS );
  
  
          configASSERT( xMQTTStatus == MQTTSuccess );
  
          /**************************** Publish and Keep Alive Loop. ******************************/
          /* Publish messages with QoS1, send and process Keep alive messages. */
          for( ulPublishCount = 0; ulPublishCount < ulMaxPublishCount; ulPublishCount++ )
          {
              LogInfo( ( "Publish to the MQTT topic %s.\r\n", mqttexampleTOPIC ) );
              prvMQTTPublishToTopic( &xMQTTContext );
  
              /* Process incoming publish echo, since application subscribed to the same
               * topic, the broker will send publish message back to the application. */
              LogInfo( ( "Attempt to receive publish message from broker.\r\n" ) );
              xMQTTStatus = MQTT_ProcessLoop( &xMQTTContext, mqttexamplePROCESS_LOOP_TIMEOUT_MS );
              configASSERT( xMQTTStatus == MQTTSuccess );
  
              /* Leave Connection Idle for some time. */
              LogInfo( ( "Keeping Connection Idle...\r\n\r\n" ) );
              vTaskDelay( mqttexampleDELAY_BETWEEN_PUBLISHES_TICKS );
          }
  
          /************************ Unsubscribe from the topic. **************************/
          LogInfo( ( "Unsubscribe from the MQTT topic %s.\r\n", mqttexampleTOPIC ) );
          prvMQTTUnsubscribeFromTopic( &xMQTTContext );
  
          /* Process incoming UNSUBACK packet from the broker. */
          xMQTTStatus = MQTT_ProcessLoop( &xMQTTContext, mqttexamplePROCESS_LOOP_TIMEOUT_MS );
          configASSERT( xMQTTStatus == MQTTSuccess );
  
          /**************************** Disconnect. ******************************/
  
          /* Send an MQTT Disconnect packet over the already connected TLS over TCP connection.
           * There is no corresponding response for the disconnect packet. After sending
           * disconnect, client must close the network connection. */
          LogInfo( ( "Disconnecting the MQTT connection with %s.\r\n", democonfigMQTT_BROKER_ENDPOINT ) );
          MQTT_Disconnect( &xMQTTContext );
  
          /* Close the network connection.  */
          TLS_FreeRTOS_Disconnect( &xNetworkContext );
  
          /* Wait for some time between two iterations to ensure that we do not
           * the broker. */
          LogInfo( ( "prvMQTTDemoTask() completed an iteration successfully. Total free heap is %u.\r\n", xPortGetFreeHeapSize() ) );
          LogInfo( ( "Demo completed successfully.\r\n" ) );
          LogInfo( ( "Short delay before starting the next iteration.... \r\n\r\n" ) );
          vTaskDelay( mqttexampleDELAY_BETWEEN_DEMO_ITERATIONS_TICKS );
      }
  }
  /*-----------------------------------------------------------*/
  
  static void prvTLSConnect( NetworkCredentials_t * pxNetworkCredentials,
                             NetworkContext_t * pxNetworkContext )
  {
      BaseType_t xNetworkStatus;
  
      /* Set the credentials for establishing a TLS connection. */
      pxNetworkCredentials->pRootCa = ( const unsigned char * ) democonfigROOT_CA_PEM;
      pxNetworkCredentials->rootCaSize = sizeof( democonfigROOT_CA_PEM );
      pxNetworkCredentials->pClientCert = ( const unsigned char * ) democonfigCLIENT_CERTIFICATE_PEM;
      pxNetworkCredentials->clientCertSize = sizeof( democonfigCLIENT_CERTIFICATE_PEM );
      pxNetworkCredentials->pPrivateKey = ( const unsigned char * ) democonfigCLIENT_PRIVATE_KEY_PEM;
      pxNetworkCredentials->privateKeySize = sizeof( democonfigCLIENT_PRIVATE_KEY_PEM );
  
      /* Attempt to create a mutually authenticated TLS connection. */
      xNetworkStatus = TLS_FreeRTOS_Connect( pxNetworkContext,
                                             democonfigMQTT_BROKER_ENDPOINT,
                                             democonfigMQTT_BROKER_PORT,
                                             pxNetworkCredentials,
                                             mqttexampleTRANSPORT_SEND_RECV_TIMEOUT_MS,
                                             mqttexampleTRANSPORT_SEND_RECV_TIMEOUT_MS );
      configASSERT( xNetworkStatus == TLS_TRANSPORT_SUCCESS );
      LogInfo( ( "A mutually authenticated TLS connection established with %s:%u.\r\n",
                 democonfigMQTT_BROKER_ENDPOINT,
                 democonfigMQTT_BROKER_PORT ) );
  }
  /*-----------------------------------------------------------*/
  static void prvCreateMQTTConnectionWithBroker( MQTTContext_t * pxMQTTContext,
                                                 NetworkContext_t * pxNetworkContext )
  {
      MQTTStatus_t xResult;
      MQTTConnectInfo_t xConnectInfo;
      bool xSessionPresent;
      TransportInterface_t xTransport;
  
      /***
       * For readability, error handling in this function is restricted to the use of
       * asserts().
       ***/
  
      /* Fill in Transport Interface send and receive function pointers. */
      xTransport.pNetworkContext = pxNetworkContext;
      xTransport.send = TLS_FreeRTOS_send;
      xTransport.recv = TLS_FreeRTOS_recv;
  
      /* Initialize MQTT library. */
      xResult = MQTT_Init( pxMQTTContext, &xTransport, prvGetTimeMs, prvEventCallback, &xBuffer );
      configASSERT( xResult == MQTTSuccess );
  
      /* Some fields are not used in this demo so start with everything at 0. */
      memset( ( void * ) &xConnectInfo, 0x00, sizeof( xConnectInfo ) );
  
      /* Start with a clean session i.e. direct the MQTT broker to discard any
       * previous session data. Also, establishing a connection with clean session
       * will ensure that the broker does not store any data when this client
       * gets disconnected. */
      xConnectInfo.cleanSession = true;
  
      /* The client identifier is used to uniquely identify this MQTT client to
       * the MQTT broker. In a production device the identifier can be something
       * unique, such as a device serial number. */
      xConnectInfo.pClientIdentifier = democonfigCLIENT_IDENTIFIER;
      xConnectInfo.clientIdentifierLength = ( uint16_t ) strlen( democonfigCLIENT_IDENTIFIER );
  
      /* Set MQTT keep-alive period. If the application does not send packets at an interval less than
       * the keep-alive period, the MQTT library will send PINGREQ packets. */
      xConnectInfo.keepAliveSeconds = mqttexampleKEEP_ALIVE_TIMEOUT_SECONDS;
  
      /* Send MQTT CONNECT packet to broker. LWT is not used in this demo, so it
       * is passed as NULL. */
      xResult = MQTT_Connect( pxMQTTContext,
                              &xConnectInfo,
                              NULL,
                              mqttexampleCONNACK_RECV_TIMEOUT_MS,
                              &xSessionPresent );
      configASSERT( xResult == MQTTSuccess );
  
      /* Successfully established and MQTT connection with the broker. */
      LogInfo( ( "An MQTT connection is established with %s.", democonfigMQTT_BROKER_ENDPOINT ) );
  }
  /*-----------------------------------------------------------*/
  
  static void prvMQTTSubscribeToTopic( MQTTContext_t * pxMQTTContext )
  {
      MQTTStatus_t xResult;
      MQTTSubscribeInfo_t xMQTTSubscription[ 1 ];
  
      /***
       * For readability, error handling in this function is restricted to the use of
       * asserts().
       ***/
  
      /* Some fields are not used by this demo so start with everything at 0. */
      ( void ) memset( ( void * ) &xMQTTSubscription, 0x00, sizeof( xMQTTSubscription ) );
  
      /* Subscribe to the mqttexampleTOPIC topic filter. This example subscribes to
       * only one topic and uses QoS1. */
      xMQTTSubscription[ 0 ].qos = MQTTQoS1;
      xMQTTSubscription[ 0 ].pTopicFilter = mqttexampleTOPIC;
      xMQTTSubscription[ 0 ].topicFilterLength = ( uint16_t ) strlen( mqttexampleTOPIC );
  
      /* Get a unique packet id. */
      usSubscribePacketIdentifier = MQTT_GetPacketId( pxMQTTContext );
  
      /* Send SUBSCRIBE packet. */
      xResult = MQTT_Subscribe( pxMQTTContext,
                                xMQTTSubscription,
                                sizeof( xMQTTSubscription ) / sizeof( MQTTSubscribeInfo_t ),
                                usSubscribePacketIdentifier );
  
      configASSERT( xResult == MQTTSuccess );
  }
  /*-----------------------------------------------------------*/
  
  static void prvMQTTPublishToTopic( MQTTContext_t * pxMQTTContext )
  {
      MQTTStatus_t xResult;
      MQTTPublishInfo_t xMQTTPublishInfo;
  
  
      /***
       * For readability, error handling in this function is restricted to the use of
       * asserts().
       ***/
  
      /* Some fields are not used by this demo so start with everything at 0. */
      ( void ) memset( ( void * ) &xMQTTPublishInfo, 0x00, sizeof( xMQTTPublishInfo ) );
  
      /* This demo uses QoS1 */
      xMQTTPublishInfo.qos = MQTTQoS1;
      xMQTTPublishInfo.retain = false;
      xMQTTPublishInfo.pTopicName = mqttexampleTOPIC;
      xMQTTPublishInfo.topicNameLength = ( uint16_t ) strlen( mqttexampleTOPIC );
      xMQTTPublishInfo.pPayload = mqttexampleMESSAGE;
      xMQTTPublishInfo.payloadLength = strlen( mqttexampleMESSAGE );
  
      /* Get a unique packet id. */
      usPublishPacketIdentifier = MQTT_GetPacketId( pxMQTTContext );
  
      /* Send PUBLISH packet. Packet ID is not used for a QoS1 publish. */
      xResult = MQTT_Publish( pxMQTTContext, &xMQTTPublishInfo, usPublishPacketIdentifier );
  
      configASSERT( xResult == MQTTSuccess );
  }
  /*-----------------------------------------------------------*/
  
  static void prvMQTTUnsubscribeFromTopic( MQTTContext_t * pxMQTTContext )
  {
      MQTTStatus_t xResult;
      MQTTSubscribeInfo_t xMQTTSubscription[ 1 ];
  
      /* Some fields are not used by this demo so start with everything at 0. */
      memset( ( void * ) &xMQTTSubscription, 0x00, sizeof( xMQTTSubscription ) );
  
      /* Unsubscribe to the mqttexampleTOPIC topic filter. */
      xMQTTSubscription[ 0 ].pTopicFilter = mqttexampleTOPIC;
      xMQTTSubscription[ 0 ].topicFilterLength = ( uint16_t ) strlen( mqttexampleTOPIC );
  
      /* Get next unique packet identifier */
      usUnsubscribePacketIdentifier = MQTT_GetPacketId( pxMQTTContext );
      /* Make sure the packet id obtained is valid. */
      configASSERT( usUnsubscribePacketIdentifier != 0 );
  
      /* Send UNSUBSCRIBE packet. */
      xResult = MQTT_Unsubscribe( pxMQTTContext,
                                  xMQTTSubscription,
                                  sizeof( xMQTTSubscription ) / sizeof( MQTTSubscribeInfo_t ),
                                  usUnsubscribePacketIdentifier );
  
      configASSERT( xResult == MQTTSuccess );
  }
  /*-----------------------------------------------------------*/
  
  static void prvMQTTProcessResponse( MQTTPacketInfo_t * pxIncomingPacket,
                                      uint16_t usPacketId )
  {
      switch( pxIncomingPacket->type )
      {
          case MQTT_PACKET_TYPE_PUBACK:
              LogInfo( ( "PUBACK received for packet Id %u.\r\n", usPacketId ) );
              /* Make sure ACK packet identifier matches with Request packet identifier. */
              configASSERT( usPublishPacketIdentifier == usPacketId );
              break;
  
          case MQTT_PACKET_TYPE_SUBACK:
              LogInfo( ( "Subscribed to the topic %s.\r\n", mqttexampleTOPIC ) );
              /* Make sure ACK packet identifier matches with Request packet identifier. */
              configASSERT( usSubscribePacketIdentifier == usPacketId );
              break;
  
          case MQTT_PACKET_TYPE_UNSUBACK:
              LogInfo( ( "Unsubscribed from the topic %s.\r\n", mqttexampleTOPIC ) );
              /* Make sure ACK packet identifier matches with Request packet identifier. */
              configASSERT( usUnsubscribePacketIdentifier == usPacketId );
              break;
  
          case MQTT_PACKET_TYPE_PINGRESP:
              LogInfo( ( "Ping Response successfully received.\r\n" ) );
              break;
  
          /* Any other packet type is invalid. */
          default:
              LogWarn( ( "prvMQTTProcessResponse() called with unknown packet type:(%02X).\r\n",
                         pxIncomingPacket->type ) );
      }
  }
  
  /*-----------------------------------------------------------*/
  
  static void prvMQTTProcessIncomingPublish( MQTTPublishInfo_t * pxPublishInfo )
  {
      configASSERT( pxPublishInfo != NULL );
  
      /* Process incoming Publish. */
      LogInfo( ( "Incoming QoS : %d\n", pxPublishInfo->qos ) );
  
      /* Verify the received publish is for the we have subscribed to. */
      if( ( pxPublishInfo->topicNameLength == strlen( mqttexampleTOPIC ) ) &&
          ( 0 == strncmp( mqttexampleTOPIC, pxPublishInfo->pTopicName, pxPublishInfo->topicNameLength ) ) )
      {
          LogInfo( ( "\r\nIncoming Publish Topic Name: %.*s matches subscribed topic.\r\n"
                     "Incoming Publish Message : %.*s\r\n",
                     pxPublishInfo->topicNameLength,
                     pxPublishInfo->pTopicName,
                     pxPublishInfo->payloadLength,
                     pxPublishInfo->pPayload ) );
      }
      else
      {
          LogInfo( ( "Incoming Publish Topic Name: %.*s does not match subscribed topic.\r\n",
                     pxPublishInfo->topicNameLength,
                     pxPublishInfo->pTopicName ) );
      }
  }
  
  /*-----------------------------------------------------------*/
  
  static void prvEventCallback( MQTTContext_t * pxMQTTContext,
                                MQTTPacketInfo_t * pxPacketInfo,
                                MQTTDeserializedInfo_t * pxDeserializedInfo )
  {
      /* The MQTT context is not used for this demo. */
      ( void ) pxMQTTContext;
  
      if( ( pxPacketInfo->type & 0xF0U ) == MQTT_PACKET_TYPE_PUBLISH )
      {
          prvMQTTProcessIncomingPublish( pxDeserializedInfo->pPublishInfo );
      }
      else
      {
          prvMQTTProcessResponse( pxPacketInfo, pxDeserializedInfo->packetIdentifier );
      }
  }
  
  /*-----------------------------------------------------------*/
  
  static uint32_t prvGetTimeMs( void )
  {
      TickType_t xTickCount = 0;
      uint32_t ulTimeMs = 0UL;
  
      /* Get the current tick count. */
      xTickCount = xTaskGetTickCount();
  
      /* Convert the ticks to milliseconds. */
      ulTimeMs = ( uint32_t ) xTickCount * _MILLISECONDS_PER_TICK;
  
      /* Reduce ulGlobalEntryTimeMs from obtained time so as to always return the
       * elapsed time in the application. */
      ulTimeMs = ( uint32_t ) ( ulTimeMs - ulGlobalEntryTimeMs );
  
      return ulTimeMs;
  }
  
  /*-----------------------------------------------------------*/
  