/* * Copyright 2019-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * You may not use this file except in compliance with the terms and conditions * set forth in the accompanying LICENSE file. * * THESE MATERIALS ARE PROVIDED ON AN "AS IS" BASIS. AMAZON SPECIFICALLY * DISCLAIMS, WITH RESPECT TO THESE MATERIALS, ALL WARRANTIES, EXPRESS, * IMPLIED, OR STATUTORY, INCLUDING THE IMPLIED WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. */ /* The config header is always included first. */ #include "iot_config.h" /* Standard includes. */ #include #include #include #include /* Set up logging for this demo. */ #include "iot_demo_logging.h" /* Platform layer includes. */ #include "platform/iot_clock.h" #include "platform/iot_threads.h" /* AIA include. */ #include "aia_sample_app.h" /** * @cond DOXYGEN_IGNORE * Doxygen should ignore this section. * * Provide default values for undefined configuration settings. */ #ifndef IOT_DEMO_MQTT_TOPIC_PREFIX #define IOT_DEMO_MQTT_TOPIC_PREFIX "iotdemo" #endif #ifndef IOT_DEMO_MQTT_PUBLISH_BURST_SIZE #define IOT_DEMO_MQTT_PUBLISH_BURST_SIZE ( 10 ) #endif #ifndef IOT_DEMO_MQTT_PUBLISH_BURST_COUNT #define IOT_DEMO_MQTT_PUBLISH_BURST_COUNT ( 10 ) #endif /** @endcond */ /** * @brief The first characters in the client identifier. A timestamp is appended * to this prefix to create a unique client identifer. * * This prefix is also used to generate topic names and topic filters used in this * demo. */ #define CLIENT_IDENTIFIER_PREFIX "iotdemo" /** * @brief The longest client identifier that an MQTT server must accept (as defined * by the MQTT 3.1.1 spec) is 23 characters. Add 1 to include the length of the NULL * terminator. */ #define CLIENT_IDENTIFIER_MAX_LENGTH ( 24 ) /** * @brief The keep-alive interval used for this demo. * * An MQTT ping request will be sent periodically at this interval. */ #define KEEP_ALIVE_SECONDS ( 60 ) /** * @brief The Last Will and Testament topic name in this demo. * * The MQTT server will publish a message to this topic name if this client is * unexpectedly disconnected. */ #define WILL_TOPIC_NAME IOT_DEMO_MQTT_TOPIC_PREFIX "/will" /** * @brief The length of #WILL_TOPIC_NAME. */ #define WILL_TOPIC_NAME_LENGTH ( ( uint16_t ) ( sizeof( WILL_TOPIC_NAME ) - 1 ) ) /** * @brief The message to publish to #WILL_TOPIC_NAME. */ #define WILL_MESSAGE "MQTT demo unexpectedly disconnected." /** * @brief The length of #WILL_MESSAGE. */ #define WILL_MESSAGE_LENGTH ( ( size_t ) ( sizeof( WILL_MESSAGE ) - 1 ) ) /*-----------------------------------------------------------*/ /* Declaration of demo function. */ int RunAiaDemo( bool awsIotMqttMode, const char * pIdentifier, void * pNetworkServerInfo, void * pNetworkCredentialInfo, const IotNetworkInterface_t * pNetworkInterface ); /*-----------------------------------------------------------*/ /** * @brief Initialize the MQTT library. * * @return `EXIT_SUCCESS` if all libraries were successfully initialized; * `EXIT_FAILURE` otherwise. */ static int _initializeDemo( void ) { int status = EXIT_SUCCESS; IotMqttError_t mqttInitStatus = IOT_MQTT_SUCCESS; mqttInitStatus = IotMqtt_Init(); if( mqttInitStatus != IOT_MQTT_SUCCESS ) { /* Failed to initialize MQTT library. */ status = EXIT_FAILURE; } return status; } /*-----------------------------------------------------------*/ /** * @brief Clean up the MQTT library. */ static void _cleanupDemo( void ) { IotMqtt_Cleanup(); } /*-----------------------------------------------------------*/ /** * @brief Establish a new connection to the MQTT server. * * @param[in] awsIotMqttMode Specify if this demo is running with the AWS IoT * MQTT server. Set this to `false` if using another MQTT server. * @param[in] pIdentifier NULL-terminated MQTT client identifier. * @param[in] pNetworkServerInfo Passed to the MQTT connect function when * establishing the MQTT connection. * @param[in] pNetworkCredentialInfo Passed to the MQTT connect function when * establishing the MQTT connection. * @param[in] pNetworkInterface The network interface to use for this demo. * @param[out] pMqttConnection Set to the handle to the new MQTT connection. * * @return `EXIT_SUCCESS` if the connection is successfully established; `EXIT_FAILURE` * otherwise. */ static int _establishMqttConnection( bool awsIotMqttMode, const char * pIdentifier, void * pNetworkServerInfo, void * pNetworkCredentialInfo, const IotNetworkInterface_t * pNetworkInterface, IotMqttConnection_t * pMqttConnection ) { int status = EXIT_SUCCESS; IotMqttError_t connectStatus = IOT_MQTT_STATUS_PENDING; IotMqttNetworkInfo_t networkInfo = IOT_MQTT_NETWORK_INFO_INITIALIZER; IotMqttConnectInfo_t connectInfo = IOT_MQTT_CONNECT_INFO_INITIALIZER; IotMqttPublishInfo_t willInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER; char pClientIdentifierBuffer[ CLIENT_IDENTIFIER_MAX_LENGTH ] = { 0 }; /* Set the members of the network info not set by the initializer. This * struct provided information on the transport layer to the MQTT connection. */ networkInfo.createNetworkConnection = true; networkInfo.u.setup.pNetworkServerInfo = pNetworkServerInfo; networkInfo.u.setup.pNetworkCredentialInfo = pNetworkCredentialInfo; networkInfo.pNetworkInterface = pNetworkInterface; #if ( IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 ) && defined( IOT_DEMO_MQTT_SERIALIZER ) networkInfo.pMqttSerializer = IOT_DEMO_MQTT_SERIALIZER; #endif /* Set the members of the connection info not set by the initializer. */ connectInfo.awsIotMqttMode = awsIotMqttMode; connectInfo.cleanSession = true; connectInfo.keepAliveSeconds = KEEP_ALIVE_SECONDS; connectInfo.pWillInfo = &willInfo; /* Set the members of the Last Will and Testament (LWT) message info. The * MQTT server will publish the LWT message if this client disconnects * unexpectedly. */ willInfo.pTopicName = WILL_TOPIC_NAME; willInfo.topicNameLength = WILL_TOPIC_NAME_LENGTH; willInfo.pPayload = WILL_MESSAGE; willInfo.payloadLength = WILL_MESSAGE_LENGTH; /* Use the parameter client identifier if provided. Otherwise, generate a * unique client identifier. */ if( ( pIdentifier != NULL ) && ( pIdentifier[ 0 ] != '\0' ) ) { connectInfo.pClientIdentifier = pIdentifier; connectInfo.clientIdentifierLength = ( uint16_t ) strlen( pIdentifier ); } else { /* Every active MQTT connection must have a unique client identifier. The demos * generate this unique client identifier by appending a timestamp to a common * prefix. */ status = snprintf( pClientIdentifierBuffer, CLIENT_IDENTIFIER_MAX_LENGTH, CLIENT_IDENTIFIER_PREFIX "%lu", ( long unsigned int ) IotClock_GetTimeMs() ); /* Check for errors from snprintf. */ if( status < 0 ) { IotLogError( "Failed to generate unique client identifier for demo." ); status = EXIT_FAILURE; } else { /* Set the client identifier buffer and length. */ connectInfo.pClientIdentifier = pClientIdentifierBuffer; connectInfo.clientIdentifierLength = ( uint16_t ) status; status = EXIT_SUCCESS; } } /* Establish the MQTT connection. */ if( status == EXIT_SUCCESS ) { IotLogInfo( "MQTT demo client identifier is %.*s (length %hu).", connectInfo.clientIdentifierLength, connectInfo.pClientIdentifier, connectInfo.clientIdentifierLength ); connectStatus = IotMqtt_Connect( &networkInfo, &connectInfo, MQTT_TIMEOUT_MS, pMqttConnection ); if( connectStatus != IOT_MQTT_SUCCESS ) { IotLogError( "MQTT CONNECT returned error %s.", IotMqtt_strerror( connectStatus ) ); status = EXIT_FAILURE; } } return status; } /*-----------------------------------------------------------*/ /** * @brief The function that runs the MQTT demo, called by the demo runner. * * @param[in] awsIotMqttMode Specify if this demo is running with the AWS IoT * MQTT server. Set this to `false` if using another MQTT server. * @param[in] pIdentifier NULL-terminated MQTT client identifier. * @param[in] pNetworkServerInfo Passed to the MQTT connect function when * establishing the MQTT connection. * @param[in] pNetworkCredentialInfo Passed to the MQTT connect function when * establishing the MQTT connection. * @param[in] pNetworkInterface The network interface to use for this demo. * * @return `EXIT_SUCCESS` if the demo completes successfully; `EXIT_FAILURE` otherwise. */ int RunAiaDemo( bool awsIotMqttMode, const char * pIdentifier, void * pNetworkServerInfo, void * pNetworkCredentialInfo, const IotNetworkInterface_t * pNetworkInterface ) { /* Return value of this function and the exit status of this program. */ int status = EXIT_SUCCESS; /* Handle of the MQTT connection used in this demo. */ IotMqttConnection_t mqttConnection = IOT_MQTT_CONNECTION_INITIALIZER; /* Flags for tracking which cleanup functions must be called. */ bool librariesInitialized = false, connectionEstablished = false; /* Initialize the libraries required for this demo. */ status = _initializeDemo(); if( status == EXIT_SUCCESS ) { /* Mark the libraries as initialized. */ librariesInitialized = true; /* Establish a new MQTT connection. */ while (EXIT_SUCCESS != (status = _establishMqttConnection( awsIotMqttMode, pIdentifier, pNetworkServerInfo, pNetworkCredentialInfo, pNetworkInterface, &mqttConnection )) ) {} } if( status == EXIT_SUCCESS ) { /* Mark the MQTT connection as established. */ connectionEstablished = true; /* Run the AIA Sample Application. */ AiaSampleApp_t* sampleApp = AiaSampleApp_Create( mqttConnection, clientcredentialIOT_THING_NAME ); if( !sampleApp ) { AiaLogError( "AiaSampleApp_Create failed" ); return EXIT_FAILURE; } AiaHttpStoreNetworkInfo( pNetworkInterface, pNetworkCredentialInfo ); AiaSampleApp_Run( sampleApp ); AiaSampleApp_Destroy( sampleApp ); } /* Disconnect the MQTT connection if it was established. */ if( connectionEstablished == true ) { IotMqtt_Disconnect( mqttConnection, 0 ); } /* Clean up libraries if they were initialized. */ if( librariesInitialized == true ) { _cleanupDemo(); } return status; } /*-----------------------------------------------------------*/