Advertisement

A peek inside Amazon FreeRTOS: Publishing messages to the cloud

July 29, 2018

Jacob_Beningo-July 29, 2018

In the last several articles, “A Peek Inside Amazon FreeRTOS” and “A peek inside Amazon FreeRTOS: Communication and memory”, we explored how the Amazon FreeRTOS demonstration code behaved on an STM32F475 IoT Discovery Node using Percepio Tracealyzer. This analysis was a great way to understand how the third-party software is behaving, but at some point, we need to dive into the code and start customizing. In this article, we’ll explore how Amazon FreeRTOS publishes messages to Amazon Web Services (AWS) and a few customizations we can create to make those message functions more useful.

The application code that developers should be most interested in is located in aws_hello_world.c. This module is the main demo application which contains:

  • The MQTT client configuration (the subscribe topic, client ID and misc. data)

  • The publish and subscribe task (prvMQTTConnectAndPublishTask)

  • MQTT client and connection (prvCreateClientAndConnectToBroker)

  • Message publishing (prvPublishNextMessage)

  • A MQTT callback for receive message notification (prvMQTTCallback)

  • Echo topic subscribing (prvSubscribe)

The publish and subscribe task drives the entire connecting, message publishing and receiving process. It starts out by creating the client and connecting to the broker. When this is successful, a secondary task is spawned that echo’s back to the server receive messages. At this point, prvMQTTConnectAndPublishTask subscribes to the AWS freertos/demo/echo topic and if this is successful, the application will then post a message every 5 seconds for a minute before closing its connection, deleting resources and entering into an idle state.

What is interesting about the demonstration is that the subscribe and publish functionality is setup to only support a single topic and publish a single message to that topic. This behavior is fine for a demonstration but doesn’t match the dynamic environment that many IoT edge nodes will find themselves where publishing different messages to multiple topics may be required. For example, a team may decide that they want to have a sensor topic that all sensor data is published to while log information about performance and behavior is posted to another topic and a control topic is subscribed to for control data. The limitation is simply how the demonstration code is written and has nothing to do with the RTOS or connectivity stacks. Modifying the application to support multiple topics and messages turns out to be pretty straight forward.

First, a developer will want to copy prvSubscribe and create a new version named prvSubscribeTopic. Unlike prvSubscribe, prvSubscribeTopic should accept a const uint8_t pointer as a parameter rather than void. This will allow a developer to pass in any string as a topic to subscribe to. By default, the demonstration populates the macro echoTOPIC_NAME with the desired topic (freertos/demos/echo). In our customized version, we want to replace all the echoTOPIC_NAME instances with our parameter name such as SubScribeTopic. Once this has been done, a developer should have a function that looks something like Listing 1.

static BaseType_t prvSubscribeTopic( const uint8_t * SubScribeTopic )
{
    MQTTAgentReturnCode_t xReturned;
    BaseType_t xReturn;
    MQTTAgentSubscribeParams_t xSubscribeParams;
 
    /* Setup subscribe parameters to subscribe to echoTOPIC_NAME topic. */
    xSubscribeParams.pucTopic = SubScribeTopic;
    xSubscribeParams.pvPublishCallbackContext = NULL;
    xSubscribeParams.pxPublishCallback = prvMQTTCallback;
    xSubscribeParams.usTopicLength = ( uint16_t ) strlen( ( const char * ) SubScribeTopic );
    xSubscribeParams.xQoS = eMQTTQoS1;
 
    /* Subscribe to the topic. */
    xReturned = MQTT_AGENT_Subscribe( xMQTTHandle,
                                      &xSubscribeParams,
                                      democonfigMQTT_TIMEOUT );
 
    if( xReturned == eMQTTAgentSuccess )
    {
        configPRINTF( ( "MQTT Echo demo subscribed to %s\n", SubScribeTopic ) );
        xReturn = pdPASS;
    }
    else
    {
        configPRINTF( ( "ERROR:  MQTT Echo demo could not subscribe to %s\n", SubScribeTopic ) );
        xReturn = pdFAIL;
    }
 
    return xReturn;
}

Listing 1. An example generic function that developers can use to subscribe to a topic. (Source: Beningo Embedded Group modified Amazon FreeRTOS code)

Next, With prvSubscribeTopic, a developer can create several topic strings that the demonstration will now subscribe to. For example, below are two example topic definitions that can be configured within the MQTT client configuration area:

#define echoTOPIC_NAME         ( ( const uint8_t * ) "freertos/demos/echo" )
#define sensorTOPIC_NAME.      ( ( const uint8_t *) "freertos/demos/sensor")

Before subscribing to these topics, it’s important that within your AWS Console, you update your device policy to include subscribe, publish and receive rights to AWS. If this is not done, subscribing to the topic will fail and the demonstration code will stop executing and provide a fairly nondescript, generic error. Once the policy is updated, a developer can search for prvSubscribe and replace it with the following:

xReturned = prvSubscribeTopic(echoTOPIC_NAME);
xReturned = prvSubscribeTopic(sensorTOPIC_NAME);

We should now be able to successfully connect and subscribe to these topics but if we want to have a generic function to publish data to these topics, we will need to modify the application code and create a new publish function. The existing function prvPublishNextMessage has most of the features that we would be interested in. In order to preserve the original, I recommend copying the function and creating a more generic prvPublishMessage function. This function at a minimum should include a pointer to the topic string a developer wants to publish to and then the data that should be published. Once again, anywhere that the echoTOPIC_NAME is hardcoded it should be replaced with our topic parameter variable. The result will be a function similar to Listing 2. 

static void prvPublishMessage( const uint8_t * Topic, BaseType_t xMessageNumber )
{
    MQTTAgentPublishParams_t xPublishParameters;
    MQTTAgentReturnCode_t xReturned;
    char cDataBuffer[ echoMAX_DATA_LENGTH ];

    /* Check this function is not being called before the MQTT client object has
     * been created. */
    configASSERT( xMQTTHandle != NULL );

    /* Create the message that will be published, which is of the form "Hello World n"
     * where n is a monotonically increasing number. Note that snprintf appends
     * terminating null character to the cDataBuffer. */
    ( void ) snprintf( cDataBuffer, echoMAX_DATA_LENGTH, "Hello World %d", ( int ) xMessageNumber );

    /* Setup the publish parameters. */
    memset( &( xPublishParameters ), 0x00, sizeof( xPublishParameters ) );
    xPublishParameters.pucTopic = Topic;
    xPublishParameters.pvData = cDataBuffer;
    xPublishParameters.usTopicLength = ( uint16_t ) strlen( ( const char * ) Topic );
    xPublishParameters.ulDataLength = ( uint32_t ) strlen( cDataBuffer );
    xPublishParameters.xQoS = eMQTTQoS1;

    /* Publish the message. */
    xReturned = MQTT_AGENT_Publish( xMQTTHandle,
                                    &( xPublishParameters ),
                                    democonfigMQTT_TIMEOUT );

    if( xReturned == eMQTTAgentSuccess )
    {
        configPRINTF( ( "Echo successfully published '%s'
", cDataBuffer ) );
    }
    else
    {
        configPRINTF( ( "ERROR:  Echo failed to publish '%s'
", cDataBuffer ) );
    }

    /* Remove compiler warnings in case configPRINTF() is not defined. */
    ( void ) xReturned;
}

Listing 2. An example generic function that developers can use to publish a message to a topic. (Source: Beningo Embedded Group modified Amazon FreeRTOS code)

This new prvPublishMessage function is a good first step towards a more generic function but depending on the application this function may need further modification in order to handle a large enough dataset. In fact, it probably would make more sense to move the snprintf statement outside this function and have the application code prepare the message and then pass the message and topic into the function. This would ensure the greatest flexibility for our publish function.

Finally, we can update the demonstration code publish loop to no longer use prvPublishNextMessage and instead use prvPublishMessage. The code will look similar to the following:

for ( x = 0; x < xIterationsInAMinute; x++ )
 {
     //prvPublishNextMessage( x );
       prvPublishMessage(echoTOPIC_NAME, x );
       prvPublishMessage(sensorTOPIC_NAME, x );

     /* Five seconds delay&
     vTaskDelay( xFiveSeconds );
 }

For testing, we can publish the same data and message to both topics and verify that we receive the messages in the AWS IoT Console MQTT client. In order to do this, we subscribe to both the freertos/demos/echo and freertos/demos/sensor topics in the IoT Console MQTT client as shown in Figure 1. In this figure, you can see in green that we are receiving messages in the freertos/demos/sensor topic. On the left-hand side of the MQTT client you can also see a green circle next to the freertos/demos/echo topic. This green circle indicates that there are new messages also coming in on this topic as well.

click for larger image

Figure 1. MQTT client receiving messages from the updated Amazon FreeRTOS functions on multiple subscribed topics. (Source: Beningo Embedded Group screen shot of AWS IoT Console MQTT client)

In this article, we’ve examined the main Amazon FreeRTOS demonstration application and made some minor modifications to its behavior in order to make subscribing and publishing messages to AWS more generic. We’ve seen how we can subscribe to different topics and that it is possible to subscribe and publish to multiple topics simultaneously. In the next post, we’ll examine how we can receive data from a topic and use that data to control an LED through the AWS MQTT client.

Download Jacob’s updated aws_hello_world.c module.


Jacob Beningo is an embedded software consultant, advisor and educator who currently works with clients in more than a dozen countries to dramatically transform their software, systems and processes. Feel free to contact him at jacob@beningo.com, at his website www.beningo.com, and sign-up for his monthly Embedded Bytes Newsletter.

 

Loading comments...