Quality RTOS & Embedded Software

  Real time embedded FreeRTOS RSS feed  
NOTE: The MQTT library and documentation are in the FreeRTOS Labs.  The libraries in the FreeRTOS Labs download directory are fully functional, but undergoing optimizations or refactoring to improve memory usage, modularity, documentation, demo usability, or test coverage.  They are available as part of the main download.

Basic MQTT Demo (without TLS and in plaintext)

Notice:  It is our recommendation to always use strong mutual authentication in any Internet of Things (IoT) application. The first project (this page) is only provided to validate MQTT communication can be established prior to introducing encryption and authentication, and to allow the MQTT packets to be observed using a network packet sniffer such as Wireshark for those who wish to do so. The first two projects are not intended to be examples suitable for production use.

 

Introduction

The basic MQTT demo project uses the FreeRTOS Windows port, enabling it to be built and evaluated with the free Community version of Visual Studios on Windows, so without the need for any particular MCU hardware.

The example projects documented on these pages introduce the concepts described in the “TLS Introduction” section one at a time. The first example (this page) demonstrates unencrypted MQTT communication, the second example builds on the first to introduce weak server authentication, and the third example builds on the second to introduce strong mutual authentication.

The basic MQTT demo is intended to showcase only the basic MQTT use cases of connecting to a MQTT broker, publishing to a topic, and subscribing to a topic.  It uses the simpler asynchronous MQTT library API and does not create a secure connection. The basic MQTT demo can connect to any non-secure MQTT broker. The instructions provided below will demonstrate how to connect to either Mosquitto’s test broker hosted on the internet or on a server running locally on the host.

This demo is intended to be used as a learning exercise only.  All MQTT messages are sent in plaintext and are not encrypted.  Do NOT send any confidential information from your device to the MQTT broker.  The MQTT broker is publicly hosted by a 3rd party that is not affiliated with FreeRTOS.  This MQTT broker may be unavailable at any time, and it is not maintained by FreeRTOS.  

Note: Mosquitto is an open source MQTT message broker that supports MQTT versions 5.0, 3.1.1, and 3.1.  It is part of the Eclipse foundation and is an project.

Source Code Organization


Click to enlarge

The Visual Studio solution for the basic MQTT demo is called mqtt_plain_text_demo.sln and is located in the \FreeRTOS-Plus\Demo\FreeRTOS_IoT_Libraries\mqtt\mqtt_plain_text directory of the main FreeRTOS download.

Note: This project is part of the main download.  

The IoT MQTT library utilizes the IoT Task Pool library, and the MQTT demo project was created by adding the MQTT library source files shown in the image below into task pool demo project.

 

Configuring the Demo Project

The demo uses the FreeRTOS+TCP TCP/IP stack, so follow the instructions provided for the TCP/IP starter project to ensure you:

  1. Have the pre-requisite components installed (such as WinPCap).
  2. Optionally set a static or dynamic IP address, gateway address and netmask.
  3. Optionally set a MAC address.
  4. Select an Ethernet network interface on your host machine.
  5. …and importantly test your network connection before attempting to run the MQTT demo.

All these setting should be performed in the MQTT demo project, not the TCP/IP starter project referenced from the same page! As delivered the TCP/IP stack is configured to use a dynamic IP address.

Configuring the MQTT Broker Connection

Configure the Client ID

The Client ID is currently hard coded within the source file (FreeRTOS-Plus\Demo\FreeRTOS_IoT_Libraries\include\mqtt_demo_profile.h).  As it is hard coded, every user who downloads the demo will have the same Client ID.  Change it to something unique in the line below or else only one client will be able to connect to the broker at a time.  

#define mqttdemoprofileCLIENT_IDENTIFIER "mqttexampleclient"

Mosquitto Public Message Broker (Web Hosted)

The demo project is pre-configured to communicate with Mosquitto’s publicly hosted message broker at “test.mosqitto.org” – so the network to which the demo is connected must have a DHCP service and internet access.  Note public MQTT brokers can be slow.

If you would like to connect to a different public broker then:

  1. Open FreeRTOS-Plus\Demo\FreeRTOS_IoT_Libraries\include\mqtt_demo_profile.h.
  2. Edit the following lines to be correct for your chosen broker:

    #define mqttdemoprofileBROKER_ENDPOINT "test.mosquitto.org"
    #define mqttdemoprofileBROKER_PORT 1883

Mosquitto Local Message Broker (Host Machine)

It is also possible to run the Mosquitto broker locally, either on your host machine (the machine used to build the demo application), or another machine on your local network.  To do this:

  1. Follow the instructions on https://mosquitto.org/download/ to download and install Mosquitto locally.
  2. Find the IP address of your host machine (run the ipconfig command on Windows, or ifconfig on Linux or MAC OS).
  3. Open FreeRTOS-Plus\Demo\FreeRTOS_IoT_Libraries\include\mqtt_demo_profile.h.
  4. Edit the following lines so mqttdemoprofileBROKER_ENDPOINT is set to the IP address of the machine on which Mosquitto is running.
    #define mqttdemoprofileBROKER_ENDPOINT "test.mosquitto.org"
    #define mqttdemoprofileBROKER_PORT 1883

Note: Port number 1883 is the default port number for unencrypted MQTT.  If you cannot use that port (for example if it is blocked by your IT security policy) then change the port used by Mosquitto to a high port number (for example something in the 50000 to 55000 range), and set mqttexampleMQTT_BROKER_PORT accordingly.

 

Building the Demo Project

The demo project uses the free community edition of Visual Studio

To build the demo:

  1. Open the \FreeRTOS-Plus\Demo\FreeRTOS_IoT_Libraries\mqtt\mqtt_plain_text\mqt_plain_text.sln Visual Studio solution file from within the Visual Studio IDE
  2. Select ‘build solution’ from the IDE’s ‘build’ menu

 

Functionality

The demo creates a single application task that loops through a set of examples that demonstrate how to connect to the broker, subscribe to a topic on the broker, publish to a topic on the broker, and disconnect from the broker again. The demo application both subscribes to and publishes to the same topic, as a result of which each time the demo publishes a message to the MQTT broker the broker sends the same message back to the demo application. The structure of the demo is shown below:

static void prvMQTTDemoTask( void * pvParameters )
{
uint32_t ulNotificationValue = 0, ulPublishCount;
const uint32_t ulMaxPublishCount = 5UL;
const TickType_t xNoDelay = ( TickType_t ) 0;

  /* Remove compiler warnings about unused parameters. */
  ( void ) pvParameters;

  /* One time initialisation of the libraries used by this demo. */
  prvInitialiseLibraries();

  for( ; ; )
  {
    /* Notifications are used to send events from the callback functions to this
     * task.  Don't expect any notifications to be pending at the beginning of the
     * loop. */
    configASSERT( ulTaskNotifyTake( pdTRUE, xNoDelay ) == 0 );


    /****************************** Connect. ******************************/

    /* Establish a connection to the MQTT broker. This example connects to
     * the MQTT broker as specified by the compile time constants
     * mqttexampleBROKER_ENDPOINT and mqttexampleBROKER_PORT.
     * Please change it to the MQTT broker you want to connect to. */
    configPRINTF( ( "Attempt to connect to %s:%d\r\n", mqttexampleBROKER_ENDPOINT, mqttexampleBROKER_PORT ) );
    prvMQTTConnect();
    configPRINTF( ( "Connected to %s:%d\r\n", mqttexampleBROKER_ENDPOINT, mqttexampleBROKER_PORT ) );


    /**************************** Subscribe. ******************************/

    /* The client is now connected to the broker. Subscribe to the topic
     * as specified by the mqttexampleTOPIC compile time constant.  This
     * client will then publish to the same topic it subscribed to, so will
     * expect all the messages it sends to the broker to be sent back to it
     * from the broker. */
    configPRINTF( ( "Attempt to subscribed to the topic %s\r\n", mqttexampleTOPIC ) );
    prvMQTTSubscribe();
    configPRINTF( ( "Subscribed to the topic %s\r\n", mqttexampleTOPIC ) );


    /*********************** Publish ulMaxPublishCount messages. **********/

    /* Publish a few messages while connected. */
    for( ulPublishCount = 0; ulPublishCount < ulMaxPublishCount; ulPublishCount++ )
    {
      /* Publish a message on the topic specified by the mqttexampleTOPIC
       * compile time constant. */
      configPRINTF( ( "Publish %s on the topic %s\r\n", mqttexampleMESSAGE, mqttexampleTOPIC ) );
      prvMQTTPublish();
      configPRINTF( ( "Published %s on the topic %s\r\n", mqttexampleMESSAGE, mqttexampleTOPIC ) );

      /* Since we are subscribed to the same topic as we published on, we
       * will get the same message back from the MQTT broker. Wait for the
       * message to be received which is signaled to us by the publish
       * callback (prvExample_OnMessageReceived) setting the
       * mqttexampleMESSAGE_RECEIVED_BIT bit in this task's notification
       * value. Note the bit is then cleared in the task's notification value
       * to ensure the bit being set can be detected on the next iteration. */
      xTaskNotifyWait( 0UL,                             /* Don't clear any bits on entry. */
                       mqttexampleMESSAGE_RECEIVED_BIT, /* Clear bit on exit. */
                       &( ulNotificationValue ),        /* Obtain the notification value. */
                       pdMS_TO_TICKS( mqttexampleMQTT_TIMEOUT_MS ) );
      configASSERT( ( ulNotificationValue & mqttexampleMESSAGE_RECEIVED_BIT ) == mqttexampleMESSAGE_RECEIVED_BIT );
    }

    /******************* Unsubscribe and Disconnect. **********************/

    /* Unsubscribe from the topic mqttexampleTOPIC and disconnect
     * gracefully. */
    prvMQTTUnsubscribe();
    prvMQTTDisconnect();
    configPRINTF( ( "Disconnected from %s:%d\r\n\r\n", mqttexampleBROKER_ENDPOINT, mqttexampleBROKER_PORT ) );

    /* Wait for the disconnect operation to complete which is signaled to us
     * by the disconnect callback (prvExample_OnDisconnect)by setting
     * the mqttexampleDISCONNECTED_BIT bit in this task's notification value.
     * Note the bit is cleared in the task's notification value again to ensure
     * it being set can be detected again on the next iteration. */
    xTaskNotifyWait( 0UL,                         /* Don't clear any bits on entry. */
                     mqttexampleDISCONNECTED_BIT, /* Clear bit on exit. */
                     &( ulNotificationValue ),    /* Obtain the notification value. */
                     pdMS_TO_TICKS( mqttexampleMQTT_TIMEOUT_MS ) );
    configASSERT( ( ulNotificationValue & mqttexampleDISCONNECTED_BIT ) == mqttexampleDISCONNECTED_BIT );

    /* Delay between iterations to avoid broker throttling. */
    configPRINTF( ( "prvMQTTDemoTask() completed an iteration successfully. Total free heap is %u\r\n", xPortGetFreeHeapSize() ) );
    configPRINTF( ( "Demo completed successfully.\r\n" ) );
    configPRINTF( ( "Short delay before starting the next iteration.... \r\n\r\n" ) );
    vTaskDelay( pdMS_TO_TICKS( mqttexampleMQTT_TIMEOUT_MS ) );
  }
}

Where prvInitialiseLibraries() is defined as:

static void prvInitialiseLibraries( void )
{
IotTaskPoolError_t xTaskPoolResult;
IotMqttError_t xResult;
IotNetworkError_t xNetworkResult;

  /* The MQTT library needs a task pool, so create the system task pool. */
  xTaskPoolResult = IotTaskPool_CreateSystemTaskPool( &( xTaskPoolParameters ) );
  configASSERT( xTaskPoolResult == IOT_TASKPOOL_SUCCESS );

  /* Initialize the network stack abstraction for FreeRTOS. */
  xNetworkResult = IotNetworkFreeRTOS_Init();
  configASSERT( xNetworkResult == IOT_NETWORK_SUCCESS );

  /* MQTT library must be initialized before it can be used. This is just one
   * time initialization. */
  xResult = IotMqtt_Init();
  configASSERT( xResult == IOT_MQTT_SUCCESS );
}

The screenshot below shows the expected output when the demo is executing correctly. 


Click to enlarge

 

Connecting to the MQTT Broker

The function prvMQTTConnect() demonstrates how to establish an unencrypted connection to a MQTT broker with clean session. It uses the FreeRTOS+TCP network interface which is implemented in the file FreeRTOS-Plus\Source\FreeRTOS-IoT-Libraries\abstractions\platform\freertos\iot_network_freertos.c.

The definition of prvMQTTConnect() is shown below:

static void prvMQTTConnect( void )
{
IotMqttError_t xResult;

  /* Set the context to pass into the disconnect callback function. */
  xNetworkInfo.disconnectCallback.pCallbackContext = ( void * ) xTaskGetCurrentTaskHandle();

  /* Establish the connection to the MQTT broker - It is a blocking call and
   * will return only when connection is complete or a timeout occurs.  The
   * network and connection structures are declared and initialised at the top
   * of this file. */
  xResult = IotMqtt_Connect( &( xNetworkInfo ),
                             &( xConnectInfo ),
                             mqttexampleMQTT_TIMEOUT_MS,
                             &( xMQTTConnection ) );
  configASSERT( xResult == IOT_MQTT_SUCCESS );
}

Where xMQTTBrokerInfo, xNetworkInfo, and xConnectInfo is defined as: 

static const struct IotNetworkServerInfo xMQTTBrokerInfo =
{
  .pHostName = mqttexampleBROKER_ENDPOINT,
  .port = mqttexampleBROKER_PORT
};

static IotMqttNetworkInfo_t xNetworkInfo =
{
  /* No connection to the MQTT broker has been established yet and we want to
   * establish a new connection. */
  .createNetworkConnection = true,
  .u.setup.pNetworkServerInfo = &( xMQTTBrokerInfo ),

  /* Set the TLS credentials for the new MQTT connection. This member is NULL
   * for the plain text MQTT demo. */
  .u.setup.pNetworkCredentialInfo = NULL, /* Not using TLS so no credentials. */

  /* Use FreeRTOS+TCP network interface. */
  .pNetworkInterface = IOT_NETWORK_INTERFACE_FREERTOS,

  /* Setup the callback which is called when the MQTT connection is
   * disconnected. The task handle is passed as the callback context which
   * is used by the callback to send a task notification to this task.*/
  .disconnectCallback.function = prvExample_OnDisconnect
};

static const IotMqttConnectInfo_t xConnectInfo =
{
  /* Set this flag to true if connecting to the AWS IoT MQTT broker. */
  .awsIotMqttMode = false,

  /* 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. */
  .cleanSession = true,

  /* Since we are starting with a clean session, there are no previous
   * subscriptions to be restored. */
  .pPreviousSubscriptions = NULL,
  .previousSubscriptionCount = 0,

  /* We do not want to publish Last Will and Testament (LWT) message if the
   * client gets disconnected. */
  .pWillInfo = NULL,

  /* Send an MQTT PING request every minute to keep the connection open if
  there is no other MQTT traffic. */
  .keepAliveSeconds = mqttexampleKEEP_ALIVE_SECONDS,

  /* 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. */
  .pClientIdentifier = mqttexampleCLIENT_IDENTIFIER,
  .clientIdentifierLength = ( uint16_t ) sizeof( mqttexampleCLIENT_IDENTIFIER ) - 1,

  /* This example does not authenticate the client and therefore username and
   * password fields are not used. */
  .pUserName = NULL,
  .userNameLength = 0,
  .pPassword = NULL,
  .passwordLength = 0
};

 

Subscribing to a MQTT Topic

The function prvMQTTSubscribe() demonstrates how to subscribe to a topic filter on the MQTT broker. The example demonstrates how to subscribe to one topic filter but it possible to pass a list of topic filters in the same subscribe API call to subscribe to more than one topic filters. The example code uses the synchronous version of the subscribe API which returns only when the subscribe operation is complete or a timeout occurs. The definition of the function is shown below:

static void prvMQTTSubscribe( void )
{
IotMqttError_t xResult;
IotMqttSubscription_t xMQTTSubscription;

  /* Subscribe to the mqttexampleTOPIC topic filter. The task handle is passed
   * as the callback context, which is then used by the callback to send a task
   * notification to this task.*/
  xMQTTSubscription.qos = IOT_MQTT_QOS_1;
  xMQTTSubscription.pTopicFilter = mqttexampleTOPIC;
  xMQTTSubscription.topicFilterLength = ( uint16_t ) strlen( mqttexampleTOPIC );
  xMQTTSubscription.callback.pCallbackContext = ( void * ) xTaskGetCurrentTaskHandle();
  xMQTTSubscription.callback.function = prvExample_OnMessageReceived;

  /* Use the synchronous API to subscribe - It is a blocking call and only
   * returns when the subscribe operation is complete or a timeout occurs. */
  xResult = IotMqtt_SubscribeSync( xMQTTConnection,
                                   &( xMQTTSubscription ),
                                   1, /* We are subscribing to one topic filter. */
                                   0, /* flags - currently ignored. */
                                   mqttexampleMQTT_TIMEOUT_MS );
  configASSERT( xResult == IOT_MQTT_SUCCESS );
}

 

Publishing to a Topic 

The function prvMQTTPublish() demonstrates how to publish on a topic on the MQTT broker. It uses the synchronous version of the publish API which returns only when the publish operation is complete or a timeout occurs. The definition of the function is shown below:

static void prvMQTTPublish( void )
{
IotMqttError_t xResult;
IotMqttPublishInfo_t xMQTTPublishInfo;

  /* Publish a message with QoS1 on the mqttexampleTOPIC topic. Since we are
   * subscribed to the same topic, the MQTT broker will send the same message
   * back to us. It is verified in the publish callback. */ 
  xMQTTPublishInfo.qos = IOT_MQTT_QOS_1;
  xMQTTPublishInfo.retain = false;
  xMQTTPublishInfo.pTopicName = mqttexampleTOPIC;
  xMQTTPublishInfo.topicNameLength = ( uint16_t ) strlen( mqttexampleTOPIC );
  xMQTTPublishInfo.pPayload = mqttexampleMESSAGE;
  xMQTTPublishInfo.payloadLength = strlen( mqttexampleMESSAGE );
  xMQTTPublishInfo.retryMs = mqttexamplePUBLISH_RETRY_MS;
  xMQTTPublishInfo.retryLimit = mqttexamplePUBLISH_RETRY_LIMIT;

  /* Use the synchronous API to publish - It is a blocking call and only
   * returns when the publish operation is complete. */ 
  xResult = IotMqtt_PublishSync( xMQTTConnection,
                                 &( xMQTTPublishInfo ),
                                 0,  /* flags - currently ignored. */
                                 mqttexampleMQTT_TIMEOUT_MS );  
  configASSERT( xResult == IOT_MQTT_SUCCESS );
}

 

Unsubscribing from a Topic

The function prvMQTTUnsubscribe() demonstrates how to unsubscribe from a topic on the MQTT broker. It uses the synchronous version of the unsubscribe API which returns only when the unsubscribe operation is complete or a timeout occurs. The definition of the function is shown below:

static void prvMQTTUnsubscribe( void )
{
IotMqttError_t xResult;
IotMqttSubscription_t xMQTTSubscription;

  /* Unsubscribe from the mqttexampleTOPIC topic filter. */ 
  xMQTTSubscription.pTopicFilter = mqttexampleTOPIC;
  xMQTTSubscription.topicFilterLength = ( uint16_t ) strlen( mqttexampleTOPIC );
   
  /* The following members of the IotMqttSubscription_t are ignored by the
  unsubscribe operation. Just initialize them to avoid "use of uninitialized
  variable" warnings. */ 
  xMQTTSubscription.qos = IOT_MQTT_QOS_1;
  xMQTTSubscription.callback.pCallbackContext = NULL;
  xMQTTSubscription.callback.function = NULL;

  /* Use the synchronous API to unsubscribe - It is a blocking call and only
  returns when the unsubscribe operation is complete. */
  xResult = IotMqtt_UnsubscribeSync( xMQTTConnection,
                                     &( xMQTTSubscription ),
                                     1, /* Unsubscribing from 1 topic filter. */
                                     0, /* flags - currently ignored. */
                                     mqttexampleMQTT_TIMEOUT_MS );
  configASSERT( xResult == IOT_MQTT_SUCCESS );
}

 

Disconnecting from the MQTT Message Broker

The function prvMQTTDisconnect() demonstrates how to gracefully disconnect from the MQTT broker. The definition of the function is shown below:

 static void prvMQTTDisconnect( void )
{
 /* Send a MQTT DISCONNECT packet to the MQTT broker to do a graceful
 disconnect. */ 
 IotMqtt_Disconnect( xMQTTConnection,
                     0 /* flags - 0 means a graceful disconnect by sending MQTT DISCONNECT. */
                     );
}
Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.