Automatically Applying Configuration to IoT Devices with AWS IoT and AWS Step Functions - Part 1
Tags: awsiot, awsstepfunctions, mqttjs, orchestration
Published: 2023-04-06 15:59:23

## Contents
1. [Introduction](#Introduction)
2. [Assumptions](#Assumptions)
3. [Tools and Services](#Tools and Services)
4. [What workflow are we building?](#Workflow)
5. [How it works](#How)
6. [Why this Developer Technique](#Why)
7. [Conclusion](#Conclusion)
8. [Author] (#Author)

## Introduction<a name="Introduction"></a>
Explicitly defining processes involving IoT devices and externalizing their management and orchestration is a useful mechanism to break down complexity, centralize state management, ensure bounded context and encapsulation in your system design. It is also a useful technique for modelling workflows that must survive external factors which impact IoT device operation in the field, such as intermittent connectivity, reboots or battery loss, causing temporary offline behavior.

In this blog post series, we will look at a simple example of modeling an IoT device process as a workflow, using primarily [AWS IoT] (https://aws.amazon.com/iot/) and [AWS Step Functions](https://aws.amazon.com/step-functions/). Our example is a system where, when a device comes online, you need to get external settings based on the profile of the user the device belongs to and push that configuration to the device. The system that holds the external settings is often a third party system you need to integrate with at API level (HTTPS), based on the API specification and system SLAs. 

A real world example is home automation. A user sets up a default desired temperature for his apartment, based on a User Settings Profile they created when they purchased the gateway responsible for managing the different sensors, including temperature. 

##Assumptions<a name="Assumptions"></a>
To narrow down this implementation example, here is a list of assumptions: 

* We refer to the gateway that controls the different sensors as the device. 
* The workflow starts every time a device comes online. 
* Nothing happens when devices disconnect. The profile is checked only upon reconnects. 
* AWS IoT Core interacts only with the gateway, and not with individual sensors. The gateway reports back to confirm the settings have been correctly configured by the respective sensors. 
* We will simulate the device behavior using an MQTT client application built with the [MQTT.js open-source library](https://github.com/mqttjs/MQTT.js). 

**<u>Note that: </u>**
* This is a sample implementation to get you started. It is not meant to be lifted and shifted for a production environment. It can definitely be used as a starting point. 
* The focus point in this solution is on development techniques and not on the type of device or type of configuration that needs to be applied. Therefore, this solution simulates the device device behavior, the config, and the external API. 

## Tools and Services<a name="Tools and Services"></a>

To build the above described scenario, we use the following technologies:

* AWS Step Functions for workflow management and execution. The state machine is available in [GitHub] (https://github.com/aws-iot-builder-tools/iot-config-management-sample/blob/main/statemachine/config_management.asl.json). The following AWS Step Function features are used in the sample project:
    * [Tasks](https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-task-state.html) - to break down each unit of work.
    * [Choice state] (https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-choice-state.html)
    * [AWS SDK service integrations](https://docs.aws.amazon.com/step-functions/latest/dg/supported-services-awssdk.html#use-awssdk-integ).
    * [Wait for a Callback with the Task Token](https://docs.aws.amazon.com/step-functions/latest/dg/connect-to-resource.html#connect-wait-token).
* AWS IoT Core and AWS IoT Rules Engine for the communication with the IoT Device over MQTT and routing of messages from the device and automatically triggering actions based on message content. 
* Amazon DynamoDB for storing of Task Tokens needed for the callback service integration pattern of AWS Step Functions. 
* AWS Lambda for publishing messages to the device. AWS Lambda is also used for processing responses to temperature set requests from the IoT Device, as well as simulating the Third Party system call to retrieve the user default desired temperature. 
* The device simulator is build in JavaScript, using the open-source Node.js MQTT.js client library. 
* All the AWS resources are created and deployed using AWS SAM. The AWS SAM template is available in [GitHub](https://github.com/aws-iot-builder-tools/iot-config-management-sample/blob/main/template.yaml)

You can have a look at the GitHub repository for the entire solution, here: https://github.com/aws-iot-builder-tools/iot-config-management-sample.


## What workflow are we building?<a name="Workflow"></a>

The flow is as follows: 

* Every time the device comes online, the workflow for retrieving the user profile, to understand their desired room temperature kicks off. The desired temperature is stored in the user profile, available via an API call to an external system. We simulate this system call using an AWS Lambda function. 
* The workflow retrieves the external ID connecting the device to the user it belongs to from IoT thing attributes. It then makes the call to the third party system to retrieve the user profile with the desired temperature value, publishes a message with the configuration setting to the MQTT topic the device is subscribed to, and enters a callback Task, which pauses waiting for the event from the device to come back on the configured response MQTT topic. 
* Upon receiving and parsing the response, success or failure is determined, the paused Task is terminated on callback with the respective result, and the AWS Step Functions workflow terminates. 

Building an automatically triggered workflow contributes to a nice user experience. There is no need for manual intervention to synchronize the IoT device with user specific configuration settings. This is a simple example to introduce the concept. The workflow can get more complex, with more settings and more involved systems. 

## How it works<a name="How"></a>

Fig. 1 below shows the interactions between the different components. 

![How it Works](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ev0sf82h2ga6r1450odz.png) Fig. 1 - _How it works diagram_

The interactions can be described as follows:

* The device comes online and an MQTT presence event is published on the AWS topic `$aws/events/presence/connected/+`. (**1** in Fig. 1 ) 
* An AWS IoT Rule is configured on the above topic, with the Rule Action being an AWS Step Function State Machine execution trigger. The Rule SQL looks as follows: `SELECT *, topic(5) AS thingName FROM $aws/events/presence/connected/+`
(**2** in Fig. 1 )
* On MQTT connect presence event, the Rule will pick up the event and pass it as input to the state machine, which will be started on Rule execution (**3** in Fig. 1 ). The workflow steps will execute as follows: 
    * Firstly, the **IoT DescribeThing SDK call** will be made to retrieve the thing from the Device Registry. The external ID thing attribute will be retrieved and passed on to the next step. Note that the [AWS Step Functions SDK integration](https://docs.aws.amazon.com/step-functions/latest/dg/supported-services-awssdk.html#use-awssdk-integ) is used at this step.
    * **3Party_GetUserProfileForDevice** is executed as a next step. This is an AWS Lambda function which simulates a call to a third party system. 3Party_GetUserProfileForDevice returns a randomly generated desired temperature value for an external ID and thing name. (**4** in the Fig. 1 ).
    * The **SetConfigurationOnDevice** AWS Lambda function uses the IoTDataPlaneClient to publish a message to the IoT Device on the MQTT request topic with the desired temperature value. (**5** in Fig. 1 ), and go next into a “Wait for Callback” Task, waiting for confirmation of the configuration setting from the device.
    * The **“Wait for Callback” Task** integrates an AWS DynamoDB PutItem SDK call. A task token is generated by AWS Step Functions and sent into the Task as input. We store the token so that we can identify and correlate which token corresponds to the request the device sends the response for. The token is stored in an Amazon DynamoDB table, together with the name of the operation (in this case `SET_TEMP`), a unique operation ID (passed as input from the Rules Engine MQTT event session ID ), the device ID (thing name) and a status value of `ACTIVE`. The DynamoDB table is configuration is shown below:

```
  CMTaskTokens:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName:  CMTaskTokens
      AttributeDefinitions:
        - AttributeName: operationId
          AttributeType: S
        - AttributeName: token
          AttributeType: S

      KeySchema:
        - AttributeName: operationId
          KeyType: HASH
        - AttributeName: token
          KeyType: RANGE
      BillingMode: PAY_PER_REQUEST
```
* The device receives the request (**6** in Fig. 1 ), attempts the operation, and sends the event confirming the result on the MQTT response topic (**7** in Fig. 1 ). An IoT Rule picks up the incoming event, and invokes an AWS Lambda Function (**8** in Fig. 1 ) which : 
    * Parses the response and establishes success or failure.
    * Retrieves the token entry based on the operation ID from the database table and uses it to terminate the AWS Step Function **WaitForConfirmationEventFromDevice** state with either success or failure.(**9** in Fig. 1 ).
* The result from the evaluation of the MQTT response event from the IoT device is passed further into a Choice Task, which determines state machine termination with with either success or failure.

## How to Deploy and Run this in your AWS Account

### Pre-requisites: 
* Clone the GitHub repo: [iot-config-management-sample](https://github.com/aws-iot-builder-tools/iot-config-management-sample )
    
* In the root directory, `iot-config-management-sample`, run the following commands:

```
      sam build 
      sam deploy --guided 
```

* Start the device simulator: 
    * Add and validate your configuration in `iot-config-management-sample/device/config.js`: 

```
export const config =
{
    iotEndpoint: '<YOUR IoT Endpoint>',
    clientId: '<YOUR Thing Name>',
    policyName: '<YOUR IoT Policy>',
    verbosity: 'warn',
    region: '<YOUR AWS Region>',
    shouldCreateThingAndIdentity: <true or false> // if true, the simulator will create the AWS IoT Thing and unique identity. Certificate and Key will be stored in certs/
}
```
If your `shouldCreateThingAndIdentity` flag is set to `false`, you need to make sure the IoT thing, certificate and key have already been created, and store the certificate and key in the certs folder prior to running the MQTT client. 

* Start the simulator: `node simulator.js`
* If the simulator is successfully started, you should already see that an AWS Step Functions workflow was triggered automatically, once your device successfully connected to AWS IoT. <u>_Note_</u> that the simulator implements a delay of `10 seconds` between receiving the request and sending a successful response.
* Verify that the workflow was successfully triggered: 
    * Log into the AWS Console, navigate to AWS Step Functions, find the `ConfigManagement` State Machine, and explore the execution, as shown in the 2 images below.


![Waiting for MQTT response from the device](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s9582tr5hotatp3wmbks.png) Fig. 2 - _Waiting for MQTT response from the device_


![Successful workflow execution](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mwz5c08ifb0idru7bgcc.png)Fig 3- _Successful workflow execution_

## Why this Developer Technique? <a name="Why"></a>
The advantages of modeling the workflow using AWS Step Functions are as follows:

* The workflow is explicitly defined and exists outside of various system interactions or the IoT device. 
* Flexibility in configuring retries, error handling and timeouts, at each step:
    * **DescribeThing** can run into TPS limits caused by too many concurrent calls to AWS IoT. You can configure your state machine to retry this step, with an exponential back-off strategy. 
    * **Calls to the Third Party systems** can fail with various errors, some of which are worth retrying on. Exception Handling and Retries can be configured at Task level.
    * If the **device goes offline temporarily** during the workflow execution for whatever reason, you can configure a reasonable [Heartbeat](https://docs.aws.amazon.com/step-functions/latest/apireference/API_SendTaskHeartbeat.html) on the Callback Task. The workflow then pauses until either a message from the device arrives or the timeout is reached. In this way, workflows can exist in the cloud outside of whatever is going on with the device, for as long as needed.
* AWS Step Functions **centralizes state management** for each step in the workflow, as well as the workflow as a whole. Tracing, logging and metrics  are available as execution level, and at task level. These observability features, as well as the integration with AWS X-Ray and Amazon CloudWatch will be covered in Part 2 of this blog series. 

## Conclusion<a name="Conclusion"></a>

In the first part of this blog series, we looked at modelling and building the process of automatically applying configuration to an IoT device, based on default settings in a user profile, using AWS Steps Functions, AWS IoT, Amazon Dynamo DB, AWS Lambda and the open-source MQTT.js client library. This is a developer technique used to reduce complexity in workflows involving IoT devices. To understand more about the implementation, have a look at the code in GitHub. 

For a more complex example of an IoT Workflow, you can have a look at the firmware upgrade (OTA) flow built using AWS IoT and AWS Step Functions in the [iot-workflow-management-and-execution GitHub repo](https://github.com/aws-iot-builder-tools/iot-workflow-management-and-execution).

If you are curious to learn more about IoT workflow orchestration, watch our IoT Builders YouTube episodes: 
{% embed https://www.youtube.com/watch?v=JcTd-hx90PY %}
{% embed https://www.youtube.com/watch?v=3H4I09ZsEws %} 

Stay tuned for the next blog of the series, where we explore in detail observability aspects such as logging, tracing and metrics for this workflow. 

If you have any feedback about this post, or you would like to see more related content, please reach out to me here, or on [Twitter](https://twitter.com/fay_ette) or [LinkedIn](https://www.linkedin.com/in/alinadima/). 

## Author<a name="Author"></a>
{% user fay_ette %}