The Internet of Things on AWS – Official Blog

Monitor IoT device geolocation with AWS IoT Events

Organizations with large numbers of IoT devices need efficient solutions to track events occurring across multiple devices, in order to identify operational issues and act upon them. This post covers the use case of a fictitious organization AcmeTracker that offers services to track the geolocation of a wide suite of assets (such as vehicles), by using IoT devices to monitor and notify support teams when an asset is outside of its expected geolocation boundaries. The organization AcmeTracker uses AWS IoT services to manage the devices and its data. Each device gets unique device credentials (certificates and private keys) by using the Fleet Provisioning feature in AWS IoT Core, and each device geolocation is monitored by AWS IoT Events.

In this post, we provide an operational overview of the aforementioned asset monitoring solution, and then describe how to setup the applicable AWS IoT services:

  • Setup AWS IoT Events to monitor GPS coordinates
  • Setup unique device credentials with Fleet Provisioning in AWS IoT Core

Solution overview

Consider a scenario where a fleet of vehicles must follow a specific itinerary. The geolocation input is used to monitor each vehicle and notify the vehicle operators if the vehicle is not following the expected itinerary. The organization AcmeTracker provides IoT devices manufactured with embedded provisioning claim credentials (certificates and keys).

The devices use the provisioning claim credentials to authenticate with AWS IoT using the AWS IoT Device SDK for Python. Programming languages other than Python are supported by the AWS IoT Device SDK, and for the full list of programming languages supported by the AWS IoT Device SDK, visit this page.

This asset monitoring solution involves the following sequence of data and message exchanges:

  1. The device requests the creation of unique device credentials (create certificate and keys) to the AWS IoT Core service via a MQTT topic.
  2. The device requests to register itself (activate unique device credentials) in the AWS IoT Core service via a MQTT topic, based on a provisioning template defined in the AWS IoT Core service.
  3. The device retrieves GPS coordinates from a satellite.
  4. The device publishes its GPS coordinates to the AWS IoT Core service over a MQTT topic.
  5. The AWS IoT Core rules engine retrieves the GPS coordinates from the MQTT topic.
  6. The AWS IoT Core rules engine sends the GPS coordinates to the AWS IoT Events service.
  7. The AWS IoT Events service has a detector model that monitors incoming IoT events (GPS coordinates) to detect if a device is in its expected boundaries.
  8. If a device state changes (either in boundary or out of boundary), the detector model sends a message to an Amazon Simple Notification Service (SNS) topic.
  9. The end-users subscribed to the SNS topic receive a notification message to inform them of the device’s state change.

Note: Step 1 and Step 2 are only applicable for new devices for which unique device credentials are not yet generated and activated. Devices that have unique device credentials start the flow from Step 3.

Setup AWS IoT Events to monitor GPS coordinates

In order to monitor and respond to changing GPS coordinates of the IoT device (covered in steps 6-9 above), AWS IoT Events must be configured accordingly. This is reviewed in the section below named Setup AWS IoT Events to monitor GPS coordinates.

For the IoT devices to connect to AWS IoT Core (covered in steps 1, 2, 4 and 5 above), the Fleet Provisioning feature must be enabled. This is reviewed in the section below named Setup unique device credentials with Fleet Provisioning in AWS IoT Core.

Note: This solution implementation requires the selection of an AWS Region where Amazon Simple Notification Service (SNS), AWS IoT Core, and AWS IoT Events services are available. Visit the AWS Region table here for a full list of AWS Regions where these AWS services are available.

Setup AWS IoT Events to monitor GPS coordinates

In AWS IoT Events, the organization AcmeTracker creates the below components to implement the asset monitoring solution:

  • One input representing the data, sent by the IoT device to the AWS Cloud, which includes the geolocation details (latitude and longitude)
  • A detector model with 2 states (and 2 transitions) to detect if the device is in boundary or out of boundary. The detector model uses the input data to detect the state associated to a device.

The following subsections explains how the above components are implemented and how to configure AWS IoT Core to forward the device geolocation data to AWS IoT Events.

Create Input in AWS IoT Events

You can create an input in AWS IoT Events by following the guide to create an input. In our example, the organization AcmeTracker creates an input with the following details:

  • An Input name set to “gpsInput”
  • An example JSON payload/event with the content below
{
  "gpsDeviceID": "1",
  "gpsLat": 10.1,
  "gpsLng": 10.1,
  "gpsDatTm": "03/02/2020  08:03:20.200"
}

Create and publish Detector Model in AWS IoT Events

In our example, the organization AcmeTracker creates a detector model with the following details:

  • Two states (InBoundary and OutOfBoundary)
  • Two transitions (InBoundaryTransition and OutOfBoundaryTransition) in order to move the device from 1 state to another

The detector model design is illustrated below:

detector model

To create a detector model, you can download this sample detector model file and import it into AWS IoT Events by following the steps below:

  1. Create a SNS topic. For more information, see the Amazon Simple Notification Service Developer Guide and, more specifically, the documentation of the CreateTopic operation in the Amazon Simple Notification Service API Reference.
  2. Subscribe to a SNS topic by following the guide To Subscribe an Endpoint to an Amazon SNS Topic Using the AWS Management Console.
  3. Create an IAM role for the Detector Model. For more information, see the documentation for setting up permissions for AWS IoT Events.
  4. Download the sample detector model file from the repository at this location.
  5. Update the file with the SNS topic’s ARN (replace value “#snsTopicArn#” with a value in the following format: arn:aws:sns:region:account:snsTopicName)
  6. Update the file with the Detector Model IAM role’s ARN (replace value “#detectorModelRoleArn#”with a value in the following format: arn:aws:iam::account:role/service-role/detectorRoleName)
  7. Go to the AWS console and select the AWS IoT Events service
  8. Click on Create detector model and click on Import detector model
  9. Click on Import, select the file from your local system and click on Open
  10. Once done, the Detector Model is created
  11. Once the Detector Model is created, you can publish it by following the guide Create a Detector Model. As the organization AcmeTracker needs to track the states of multiple devices, the detector generation model is set to “Create a detector for each key value” with a key set to the gpsDeviceID. The Detector evaluation model is set to “Batch evaluation”.

Boundary State Configuration in Detector Model

In our example, the organization AcmeTracker sets up the detector model’s states to notify a SNS topic after evaluating the input received. If the input received shows that the device is in bounds or out of bounds, a notification is sent to a SNS topic to inform the end user of the current device state (respectively “InBoundary” or “OutOfBoundary”).

The points below outline the configuration of the 2 states:

  • Both states include an OnEnter event and an OnInput event sending a notification to a SNS topic based on an event condition
  • Example of event condition for the “InBoundary” state:

$input.gpsInput.gpsLat>=10.1 && $input.gpsInput.gpsLat<=10.2 && $input.gpsInput.gpsLng>=10.1 && $input.gpsInput.gpsLng<=10.2

  • Example of event condition for the “OutOfBoundary” state:

$input.gpsInput.gpsLat<10.1 || $input.gpsInput.gpsLat>10.2 || $input.gpsInput.gpsLng<10.1 || $input.gpsInput.gpsLng>10.2

Note: The above coordinates (latitude and longitude) are only illustrative.

Boundary Transition Configuration in Detector Model

In our example, the organization AcmeTracker sets up transitions in the detector model in order to change the state of the device after evaluating the device’s input against a condition (Event trigger logic).

The below points outline the configuration of each transition:

  • Both transitions include an event trigger logic which is used as a condition to move from one state to the other
  • Example of Event trigger logic for “InBoundaryTransition” transition:

$input.gpsInput.gpsLat>=10.1 && $input.gpsInput.gpsLat<=10.2 && $input.gpsInput.gpsLng>=10.1 && $input.gpsInput.gpsLng<=10.2

  • Example of Event trigger logic for “OutOfBoundaryTransition” transition:

$input.gpsInput.gpsLat<10.1 || $input.gpsInput.gpsLat>10.2 || $input.gpsInput.gpsLng<10.1 || $input.gpsInput.gpsLng>10.2

Note: The above coordinates (latitude and longitude) are only illustrative.

Configure AWS IoT Core rules

An AWS IoT Core rule needs to be configured to forward device geolocation data from AWS IoT Core (MQTT topic) to AWS IoT Events.

  • Go to the AWS console and select the AWS IoT Core service
  • Click on Act, Rules and then click Create a Rule
  • Set the Name for the rule and Set the Rule query statement to SELECT * FROM ‘gpsTopic
  • Click on “Add action”, Send a message to an IoT Events Input and Configure action
  • Select the Input previously created
  • Click on Create Role and enter a role name
  • Click on Add action
  • Click on Create rule

Setup unique device credentials with Fleet Provisioning in AWS IoT Core

The organization AcmeTracker uses the Fleet Provisioning feature in AWS IoT Core to enable a fleet of IoT devices sharing the same business function to be managed with the same policies and the same AWS IoT Core rules. In our example and as outlined in the solution overview, the organization AcmeTracker provides IoT devices manufactured with embedded provisioning claim credentials (certificates and keys). The devices use the provisioning claim credentials to authenticate with AWS IoT Core using the AWS IoT Device SDK for Python. By using Fleet Provisioning, the IoT devices can exchange these credentials with unique device credentials for regular operations. This section explains how to create provisioning claim credentials, create a provisioning template and connect to AWS IoT Core.

Create provisioning claim credentials

You can create provisioning claim credentials by following the guides below. Once completed, you will have a certificate, private key, public key and root CA certificate.

  1. Create and Activate a Device Certificate
  2. Create an AWS IoT Core Policy
  3. Attach an AWS IoT Core Policy to a Device Certificate. Attach the following policy to the device certificate:

Note: In the policy below, replace the region and account variables (region:account) with a valid region and account number (Example: us-east-1:123456789).

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:Connect",
      "Resource": "arn:aws:iot:region:account:client/clientid"
    },
    {
      "Effect": "Allow",
      "Action": ["iot:Publish","iot:Receive"],
      "Resource": [
        "arn:aws:iot:region:account:topic/$aws/certificates/create/*",
        "arn:aws:iot:region:account:topic/$aws/provisioning-templates/iotDeviceTemplateName/provision/*"
      ]
    },    
   {
      "Effect": "Allow",
      "Action": ["iot:Subscribe"],
      "Resource": [
        "arn:aws:iot:region:account:topicfilter/$aws/certificates/create/*",
        "arn:aws:iot:region:account:topicfilter/$aws/provisioning-templates/iotDeviceTemplateName/provision/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
         "iot:CreateProvisioningClaim"
      ],
      "Resource": [
         "arn:aws:iot:region:account:provisioningtemplate/iotDeviceTemplateName"
      ]
    }
  ]
}

Create a provisioning template in AWS IoT Core

You can create a provisioning template in AWS IoT Core by following the steps below:

  • Go the AWS console and select AWS IoT Core service
  • Click on Onboard, Fleet Provisioning templates and then Create

AWS IoT Fleet Provisioning templates

  • Click on Get Started
  • Choose a template name (in our example: iotDeviceTemplateName)
  • In the Provisioning role, click on Create a Role
  • Then click on Next, click on Advanced mode and copy paste an AWS IoT policy with the following details:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:Connect",
      "Resource": "arn:aws:iot:region:account:client/clientid"
    },
    {
      "Effect": "Allow",
      "Action": ["iot:Publish","iot:Receive"],
      "Resource": "arn:aws:iot:region:account:topic/gpsTopic"
    },
    {
      "Effect": "Allow",
      "Action": ["iot:Subscribe"],
      "Resource": "arn:aws:iot:region:account:topicfilter/*"
    }
  ]
}
  • Click on Create template
  • Click on Enable template (you do not need to select any certificate in the “Use provisioning claim” section)

Connect to AWS IoT Core

Once the provisioning template is enabled, you can now connect your IoT device to AWS IoT Core and publish the GPS coordinates by following the steps outlined below.

Note that you must install the Python SDK (visit this page) and the AWS IoT Device SDK (visit this page) to run the below snippets.

  • Step 1: Create Keys and Certificate by using the below code snippet:
import boto3
import json
import logging
import time
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient

logging.basicConfig(filename='pythonIotDeviceCreate.log', filemode='w', format='%(name)s - %(levelname)s - %(message)s',level=logging.DEBUG)
logger = logging.getLogger('pythonIotDevice')
logger.info("pythonIotDevice")

global certificateOwnershipToken
global certificatePem
global privateKey
certificateOwnershipToken = ''
certificatePem = ''
privateKey = ''

#Connection to the AWS IoT Core with Root CA certificate and provisioning claim credentials (private key and certificates)

# For certificate based connection
myMQTTClient = AWSIoTMQTTClient("clientid")
# For TLS mutual authentication
myMQTTClient.configureEndpoint("your.iot.endpoint", 8883) #Provide your AWS IoT Core endpoint (Example: "abcdef12345-ats.iot.us-east-1.amazonaws.com")
myMQTTClient.configureCredentials("/root/ca/path", "/private/key/path", "/certificate/path") #Set path for Root CA and provisioning claim credentials
myMQTTClient.configureOfflinePublishQueueing(-1)
myMQTTClient.configureDrainingFrequency(2)
myMQTTClient.configureConnectDisconnectTimeout(10)
myMQTTClient.configureMQTTOperationTimeout(5)
 
logger.info("Connecting...")
myMQTTClient.connect()

#Create keys and certificate by publishing a request in a MQTT topic
 
logger.info("Publishing...")
myMQTTClient.publish("$aws/certificates/create/json", "{}", 0)

#Subscribe to a separate MQTT topic to retrieve the unique device credentials and certificate ownership token

def certCallback(client, userdata, message):
jsonMessage = json.loads(message.payload)
certificateOwnershipToken = jsonMessage['certificateOwnershipToken']
certificatePem = jsonMessage['certificatePem']
privateKey = jsonMessage ['privateKey']
logger.info('certificateOwnershipToken=%s, certificatePem=%s, privateKey=%s', certificateOwnershipToken, certificatePem, privateKey)

logger.info("Subscribing...")
myMQTTClient.subscribe("$aws/certificates/create/json/accepted", 1,  certCallback);

#Wait until reception of subscription confirmation (sleep time set to 60 seconds)
time.sleep(60)

logger.info("Disconnecting...")
myMQTTClient.disconnect()
  • Step 2: Open the log file generated (pythonIotDeviceCreate.log). Then retrieve the certificate ownership token and unique device credentials (private key and certificate) as they will be necessary for the next steps. Finally,  save the private key and certificate in separate files.
  • Step 3: Register device by using the below code snippet:
#Register the device (link the unique device credentials with the provisioning template) by publishing a request in a MQTT topic with the certificate ownership token

import boto3
import json
import logging
import time
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient

logging.basicConfig(filename='pythonIotDeviceRegister.log', filemode='w', format='%(name)s - %(levelname)s - %(message)s',level=logging.DEBUG)
logger = logging.getLogger('pythonIotDevice')
logger.info("pythonIotDevice")

#Connection to the AWS IoT Core with Root CA certificate and provisioning claim credentials (private key and certificates)
 
# For certificate based connection
myMQTTClient = AWSIoTMQTTClient("clientid")
# For TLS mutual authentication
myMQTTClient.configureEndpoint("your.iot.endpoint", 8883) 
#Provide your AWS IoT Core endpoint (Example: "abcdef12345-ats.iot.us-east-1.amazonaws.com") myMQTTClient.configureCredentials("/root/ca/path", "/private/key/path", "/certificate/path") 
#Set path for Root CA and provisioning claim credentials (do not use the private key and certificate retrieved from the logs in Step 1 since those credentials are not yet activated) myMQTTClient.configureOfflinePublishQueueing(-1)
myMQTTClient.configureDrainingFrequency(2)
myMQTTClient.configureConnectDisconnectTimeout(10)
myMQTTClient.configureMQTTOperationTimeout(5)

logger.info("Connecting...")
myMQTTClient.connect()

jsonInput = {
    "certificateOwnershipToken": "#certificateOwnershipToken#", #Provide the Certificate Ownership Token previously retrieved from the logs in Step 1
    "parameters": {
        "SerialNumber": "Provide-A-Device-Serial-Number" #Provide a Serial Number (Example: 012)
    }
}
 
logger.info("Publishing...")
myMQTTClient.publish("$aws/provisioning-templates/iotDeviceTemplateName/provision/json", json.dumps(jsonInput), 0)

#Subscribe to a separate MQTT topic to retrieve the confirmation

def templateCallback(client,  userdata, message):
    logger.info("Confirmation received: ")
    logger.info(message.payload)
    logger.info("from topic: ")
    logger.info(message.topic)

myMQTTClient.subscribe("$aws/provisioning-templates/iotDeviceTemplateName/provision/json/accepted", 1, templateCallback);

#Wait until reception of subscription confirmation (sleep time set to 60 seconds)
time.sleep(60)

logger.info("Disconnecting...")
myMQTTClient.disconnect()
  • Step 4: Publish GPS coordinates by using the below code snippet.
import boto3
import json
import logging
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient

logging.basicConfig(filename='pythonIotDevicePublish.log', filemode='w', format='%(name)s - %(levelname)s - %(message)s',level=logging.DEBUG)
logger = logging.getLogger('pythonIotDevice')
logger.info("pythonIotDevice")

#Connection to the AWS IoT Core with Root CA certificate and unique device credentials (keys and certificate) previously retrieved

# For certificate based connection
myMQTTClient = AWSIoTMQTTClient("clientid")
# For TLS mutual authentication
myMQTTClient.configureEndpoint("your.iot.endpoint", 8883) #Provide your AWS IoT Core endpoint (Example: "abcdef12345-ats.iot.us-east-1.amazonaws.com")
myMQTTClient.configureCredentials("/root/ca/path", "/private/key/path", "/certificate/path") #Set path for Root CA and unique device credentials (use the private key and certificate retrieved from the logs in Step 1)
myMQTTClient.configureOfflinePublishQueueing(-1)
myMQTTClient.configureDrainingFrequency(2)
myMQTTClient.configureConnectDisconnectTimeout(10)
myMQTTClient.configureMQTTOperationTimeout(5)
 
logger.info("Connecting...")
myMQTTClient.connect()

#Publish gps coordinates to AWS IoT Core

myMQTTClient.publish("gpsTopic", "{\"gpsDeviceID\":\"1\",\"gpsLat\":10.1,\"gpsLng\":10.1,\"gpsDatTm\":\"03/02/2020  08:03:20.200\"}", 0)
  • Step 5: Verify that you received a notification, reflecting the device state (InBoundary), from the SNS topic you’ve previously subscribed to. You can also confirm that the device is in boundary in AWS IoT Events: Go to AWS IoT Events, click on your Detector model name and confirm the device’s current state is as illustrated below:

This image shows the AWS IoT Events console where you click on your Detector model name and confirm the device’s current state

  • Step 6: Publish GPS coordinates by replacing the gps coordinates in the publish call of the previous code snippet with the below values:
myMQTTClient.publish("gpsTopic", "{\"gpsDeviceID\":\"1\",\"gpsLat\":10.0,\"gpsLng\":10.0,\"gpsDatTm\":\"03/02/2020  08:03:20.200\"}", 0)
  • Step 7: Verify that you received a notification, reflecting the device state (OutOfBoundary), from the SNS topic you’ve previously subscribed to. You can also confirm that the device is out of boundary in AWS IoT Events: Go to AWS IoT Events, click on your Detector model name and confirm the device’s current state is as illustrated below:

Go to AWS IoT Events, click on your Detector model name and confirm the device’s current state is as illustrated in this image

Note: For further AWS IoT SDK (for Python) samples, please visit this page.

Conclusion

In this post, we walked through a use case explaining how to monitor the geolocation of a IoT device fleet with the AWS IoT Events service, and how to create unique device credentials using the Fleet Provisioning feature to connect to AWS IoT Core.