Quality RTOS & Embedded Software

LIBRARIES
NOTE : The MQTT library and documentation are part of the FreeRTOS LTS Roadmap – the library will be released into the main FreeRTOS download with long term support (LTS) when the refactoring described on the LTS roadmap page is complete. This page presents a simple demo for the MQTT LTS rc1 library version, which is now near completion.

Basic Plaintext Demo Using MQTT LTS rc1 Managed API


Notice: For simplicity of understanding this, MQTT LTS rc1 demo does not use an encrypted network connection and is therefore not intended to demonstrate best practice. Production IoT devices should use a network connection that is both authenticated and encrypted, as demonstrated in the previously released MQTT TLS demos.


Introduction

The project presented on this page uses the FreeRTOS Windows port and builds using the free Community version of Visual Studio on Windows, enabling its use without the need for specific MCU hardware. It demonstrates the most basic use case for the MQTT LTS rc1 library’s managed API. There is also a separate project that uses the MQTT LTS rc1 library’s lightweight API. The MQTT LTS rc1 library does not require dynamic memory allocation, nor does it have any dependencies on libraries other than the standard C library and a simple send/receive transport network interface.

The instructions below describe how to connect to either the unsecured public Mosquitto test broker, or an MQTT broker running locally on the host. Connecting to a broker that requires an authenticated TLS connection, such as AWS IoT Core, requires the use of a different network transport interface.

As per the red text above, 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. Production IoT devices should use a network connection that is both authenticated and encrypted, as previously demonstrated in the MQTT Beta1 demos.


Build Instructions


Click to enlarge

The Visual Studio solution for the plaintext MQTT demo is called mqtt_plain_text_demo.sln and is located in the FreeRTOS-Plus/Demo/FreeRTOS-IoT-Libraries-LTS-Beta2/mqtt/mqtt_plain_text directory of the LTS Development Snapshot download.


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 of these settings should be set in the MQTT LTS rc1 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

Option 1: Using the publicly hosted Mosquitto MQTT broker (web hosted):

As delivered, the demo project is pre-configured to communicate with Mosquitto’s publicly hosted message broker at “test.mosqitto.org”. That should work if the network to which the demo is connected has a DHCP service and Internet access. Note the FreeRTOS Windows port only works with a wired Ethernet network adapter, which can be a virtual Ethernet adapter.

Use a separate MQTT client, such as MQTT.fx, to test the MQTT connection from your host machine to the public MQTT broker.

Option 2: Using a locally hosted Mosquitto MQTT message broker (host machine):

The Mosquitto broker can also run 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. Open “mosquitto.conf”, which is located in the Mosquitto install directory, and set the contained “bind_address” to be correct for the network on which Mosquitto will listen for connection.
  3. Find the IP address of your host machine (run the ipconfig command on Windows, or ifconfig on Linux or macOS). Note the FreeRTOS Windows port only works with a wired Ethernet network adaptor, which can be a virtual Ethernet adapter.
  4. Open FreeRTOS-Plus/Demo/FreeRTOS-IoT-Libraries-LTS-Beta2/mqtt/mqtt_plain_text/demo_config.h.
  5. Add the following lines so democonfigMQTT_BROKER_ENDPOINT is set to the IP address of the machine on which Mosquitto is running, which must be a machine on the same subnet as the network to which the demo is connected:
    • #define democonfigMQTT_BROKER_ENDPOINT "w.x.y.z"
    • #define democonfigMQTT_BROKER_PORT ( 1883 )
Use a separate MQTT client, such as MQTT.fx, to test the MQTT connection from your host machine to the local MQTT broker.

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 democonfigMQTT_BROKER_PORT accordingly. The port number used by Mosquitto is set by the “port” parameter in “mosquitto.conf”, which is located in the Mosquitto install directory.

Option 3: Any other unencrypted MQTT broker of your choosing:

Any MQTT broker that supports unencrypted TCP/IP communication can also be used with this demo. To do this:
  1. Open FreeRTOS-Plus/Demo/FreeRTOS-IoT-Libraries-LTS-Beta2/mqtt/mqtt_plain_text/demo_config.h.
  2. Add the following lines with respect to your chosen broker:
    • #define democonfigMQTT_BROKER_ENDPOINT "your-desired-endpoint"
    • #define democonfigMQTT_BROKER_PORT ( 1883 )


Building the Demo Project

The demo project uses the free community edition of Visual Studio. To build the demo:
  1. Open the mqtt_plain_text_demo.sln Visual Studio solution file from within the Visual Studio IDE
  2. Select Build Solution from the IDE’s Build menu

Note: If you are using Microsoft Visual Studio 2017 or earlier, then you must select a Platform Toolset compatible with your version: Project -> RTOSDemos Properties -> Platform Toolset


Functionality

Note this demo uses the managed MQTT API in its most basic use case – from within a single thread. The final version will include more complex multi-threaded demos.

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, then finally, disconnect from the broker. The demo application both subscribes to and publishes to the same topic. 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 ulPublishCount = 0U;
const uint32_t ulMaxPublishCount = 5UL;
NetworkContext_t xNetworkContext = { 0 };
MQTTContext_t xMQTTContext;
MQTTStatus_t xMQTTStatus;

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

ulGlobalEntryTimeMs = prvGetTimeMs();

for( ; ; )
{
/****************************** Connect. ******************************/



/* Establish a TCP connection with the MQTT broker. This example connects to

* the MQTT broker as specified in democonfigMQTT_BROKER_ENDPOINT and

* democonfigMQTT_BROKER_PORT at the top of this file. */


LogInfo( ( “Create a TCP connection to %s.\r\n”, democonfigMQTT_BROKER_ENDPOINT ) );
Transport_FreeRTOS_Connect( &xNetworkContext,
democonfigMQTT_BROKER_ENDPOINT,
democonfigMQTT_BROKER_PORT,
TRANSPORT_SEND_RECV_TIMEOUT_MS );



/* Sends an MQTT Connect packet over the already connected TCP socket,

* 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).

* 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. This demo uses QOS0 in Subscribe, therefore, the Publish

* messages received from the broker will have QOS0. */


LogInfo( ( “Attempt to subscribed 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 QOS0, 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 );
}

/************************ Unsubscribe from the topic. *****************/
LogInfo( ( “Unsubscribe from the MQTT topic %s.\r\n”, mqttexampleTOPIC ) );
prvMQTTUnsubscribeFromTopic( &xMQTTContext );

/* Process Incoming 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 TCP socket.

* 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. */
Transport_FreeRTOS_Disconnect( &xNetworkContext );



/* Wait for some time between two iterations to ensure that we do not

* bombard the public test mosquitto 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 );
}
}

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


Click to enlarge

Connecting to the MQTT Broker

The function prvCreateMQTTConnectionWithBroker() demonstrates how to establish an unencrypted connection to a MQTT broker with clean session. It uses the FreeRTOS+TCP transport interface which is implemented in the file FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries-LTS-Beta2/abstractions/platform/freertos/transport_interface_freertos.c. The definition of prvCreateMQTTConnectionWithBroker() is shown below:


static void prvCreateMQTTConnectionWithBroker( MQTTContext_t * pxMQTTContext, NetworkContext_t * pxNetworkContext )
{
MQTTStatus_t xResult;
MQTTConnectInfo_t xConnectInfo;
uint16_t usPacketId;
bool xSessionPresent;
TransportInterface_t xTransport;
MQTTApplicationCallbacks_t xCallbacks;



/***

* 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 = Transport_FreeRTOS_send;
xTransport.recv = Transport_FreeRTOS_recv;

/* Application callbacks for receiving incoming published and incoming acks

* from MQTT library. */

xCallbacks.appCallback = prvEventCallback;
xCallbacks.getTime = prvGetTimeMs;

/* Initialize MQTT library. */
xResult = MQTT_Init( pxMQTTContext, &xTransport, &xCallbacks, &xBuffer );
configASSERT( xResult == MQTTSuccess );

/* Many fields 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. It is the responsibility of the application

* to ensure that the interval between Control Packets being sent does not

* exceed the Keep Alive value. In the absence of sending any other Control

* Packets, the Client MUST send a PINGREQ Packet. */

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 );

if( xResult != MQTTSuccess )
{
LogError( ( “Connection with MQTT broker failed.\r\n” ) );
}
}
prvCreateMQTTConnectionWithBroker() demonstrates how to
establish an unencrypted connection to a MQTT broker with clean session

Subscribing to a MQTT Topic

The function prvMQTTSubscribeToTopic() demonstrates how to subscribe to a topic filter on the MQTT broker. The example demonstrates how to subscribe to one topic filter but it is possible to pass a list of topic filters in the same subscribe API call to subscribe to more than one topic filters. The definition of the function is shown below:


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 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 QOS0. */

xMQTTSubscription[ 0 ].qos = MQTTQoS0;
xMQTTSubscription[ 0 ].pTopicFilter = mqttexampleTOPIC;
xMQTTSubscription[ 0 ].topicFilterLength = ( uint16_t ) strlen( mqttexampleTOPIC );

/* Get a unique packet id. */
usSubscribePacketIdentifier = MQTT_GetPacketId( pxMQTTContext );
/* Make sure the packet id obtained is valid. */
configASSERT( usSubscribePacketIdentifier != 0 );

/* Send SUBSCRIBE packet. */
xResult = MQTT_Subscribe( pxMQTTContext,
xMQTTSubscription,
sizeof( xMQTTSubscription ) / sizeof( MQTTSubscribeInfo_t ),
usSubscribePacketIdentifier );

configASSERT( xResult == MQTTSuccess );
}

prvMQTTSubscribeToTopic() demonstrates how to
subscribe to a topic filter on the MQTT broker

Publishing to a Topic

The function prvMQTTPublishToTopic() demonstrates how to publish to a topic filter on the MQTT broker. The definition of the function is shown below:


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 not used by this demo so start with everything at 0. */
( void ) memset( ( void * ) &xMQTTPublishInfo, 0x00, sizeof( xMQTTPublishInfo ) );

/* This demo uses QOS0 */
xMQTTPublishInfo.qos = MQTTQoS0;
xMQTTPublishInfo.retain = false;
xMQTTPublishInfo.pTopicName = mqttexampleTOPIC;
xMQTTPublishInfo.topicNameLength = ( uint16_t ) strlen( mqttexampleTOPIC );
xMQTTPublishInfo.pPayload = mqttexampleMESSAGE;
xMQTTPublishInfo.payloadLength = strlen( mqttexampleMESSAGE );

/* Send PUBLISH packet. Packet ID is not used for a QoS0 publish. */
xResult = MQTT_Publish( pxMQTTContext, &xMQTTPublishInfo, 0U );

configASSERT( xResult == MQTTSuccess );
}
The function prvMQTTPublishToTopic() demonstrates how to
publish on a topic on the MQTT broker. The definition of the function is shown
above.

Unsubscribing from a Topic


static void prvMQTTUnsubscribeFromTopic( Socket_t xMQTTSocket )
{
MQTTStatus_t xResult;
MQTTSubscribeInfo_t xMQTTSubscription[ 1 ];
BaseType_t xStatus;

/* Some fields 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 ].qos = MQTTQoS0;
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 );
}

prvMQTTUnsubscribeFromTopic() demonstrates
how to unsubscribe from a topic on the MQTT broker.

Processing Incoming MQTT Publish Packets

The function prvMQTTProcessIncomingPublish() demonstrates how to process a PUBLISH packet from the MQTT broker. The definition of the function is shown below:


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 ) );
}
prvMQTTProcessIncomingPublish demonstrates how
process an incoming MQTT publish packet. The definition of the function is shown above.
Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.