The Internet of Things on AWS – Official Blog

How to get started with the new AWS IoT Core Device Location service

Introduction

The new AWS IoT Core Device Location feature allows Internet of Things (IoT) devices to retrieve and report their current location without relying on Global Positioning System (GPS) hardware. Devices and clients connected to AWS IoT Core can now use cloud-assisted Global Navigation Satellite System(GNSS), WiFi scan, cellular triangulation, and reverse IP lookup techniques with the AWS IoT Core Device Location feature to determine their GPS coordinates and overall location.

Geo-location information and location tracking are requirements in many Internet of Thing (IoT) applications. Segments such as logistics and automotive cannot deliver significant results without this data. Historically, Geolocation monitoring relies on specific hardware, like Global Positioning System (GPS) modules. If devices don’t have a GPS hardware, adding one or upgrading existing devices can be costly at scale or might not be feasible to implement. However, even with a built-in GPS hardware, there isn’t a guarantee that these devices will have constant connectivity to GPS satellites to retrieve and report their coordinates.

In this blog post, we’ll show how to get started with the AWS IoT Core Device Location. We’ll detail what steps you can take before using the feature and demonstrate how to use it to resolve your device’s location based on only its IP address.

Prerequisites

To follow through this blog post, you will need an AWS account, an AWS IoT Core supported region,  permissions to create AWS IoT Rules, AWS Lambda Functions,  AWS Identity and Access Management (IAM) roles and policies, and access to AWS CloudShell. We also assume you have familiar with the basics of Linux bash commands.

Walkthrough

For the demonstration in this post, you will resolve a device’s location by first publishing its IP address via an MQTT message to AWS IoT Core. Second, an AWS IoT Rule will forward the message to a Lambda function. Third, this Lambda function will call the AWS IoT Core Device Location feature, via the GetPositionEstimate API. Finally, the Lambda function will publish the device’s location data as an MQTT message back to the device or any MQTT client subscribed to the location response topic. The illustration below details what this solution will look like once fully implemented.

Step 1: Create AWS Lambda execution role and policy

From your AWS CloudShell environment implement the following commands:

  1. Create the environmental variables for the upcoming command
    export ACCOUNT_ID=<Replace with your account ID>
    export REGION=<Replace with your region>
    
  2. Create the IAM execution role for the Lambda function using the create-role command
    aws iam create-role --role-name "lambda-ex-ipAddressToDeviceLocationService" \
    --assume-role-policy-document '{
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Principal": {
                    "Service": "lambda.amazonaws.com"
                },
                "Action": "sts:AssumeRole"
            }
        ]
    }'
    
  3. Now we will create the policy document for the role’s permissions, run the command below to create the policy document
    ( jq -n \
                --arg region "$REGION" \
                --arg account_id "$ACCOUNT_ID" \
                '{
                "Version": "2012-10-17",
                "Statement": [
                    {
                        "Effect": "Allow",
                        "Action": "iotwireless:GetPositionEstimate",
                        "Resource": "*"
                    },
                    {
                        "Effect": "Allow",
                        "Action": [
                            "logs:CreateLogGroup",
                            "iot:Publish"
                        ],
                        "Resource": [
                            "arn:aws:logs:\($region):\($account_id):*",
                            "arn:aws:iot:\($region):\($account_id):topic/device/test-1/location/ipbased/response"
                        ]
                    },
                    {
                        "Effect": "Allow",
                        "Action": [
                            "logs:CreateLogStream",
                            "logs:PutLogEvents"
                        ],
                        "Resource": "arn:aws:logs:\($region):\($account_id):log-group:/aws/lambda/ipAddressToDeviceLocationService:*"
                    }
                ]
            }' ) > lambda_policy.json
    
  4. Use the put-role-policy command to attach the policy to the role
    aws iam put-role-policy \
    --role-name "lambda-ex-ipAddressToDeviceLocationService" \
    --policy-name lambda-ex-ipAddressToDeviceLocationService-policy \
    --policy-document file://lambda_policy.json
    

    Use the get-role-policy command to check if the policy has been successfully attached

    aws iam get-role-policy \
    --role-name "lambda-ex-ipAddressToDeviceLocationService" \
    --policy-name lambda-ex-ipAddressToDeviceLocationService-policy

Step 2: Create a  Lambda Function

From your AWS CloudShell environment implement the following commands:

Our Lambda function has a dependency on the AWS SDK for Python (Boto3) SDK, and the new GetPositionEstimate API is supported on version 1.26.45 or later.  In order to create a Lambda function with the latest version of Boto3, we will create and publish a Lambda layer.

  1. To Implement the lambda layer use the following commands
    LIB_DIR=boto3-mylayer/python
     
    mkdir -p $LIB_DIR
    
    #(You may need to run this command 2 or 3 times so it can be resolved automatically)
    pip3 install boto3 -t $LIB_DIR
    
    cd boto3-mylayer 
    
    zip -r /tmp/boto3-mylayer.zip .
    
    LAYER=$(aws lambda publish-layer-version --layer-name boto3-mylayer --zip-file fileb:///tmp/boto3-mylayer.zip)
     
    LAYER_ARN=$(jq -r '.LayerVersionArn' <<< "$LAYER")
    
  2. Implement the following commands to create your Lambda function deployment package
    cd /home/cloudshell-user
    touch lambda_function.py
    
    cat > lambda_function.py <<EOF
    import json, boto3
    from datetime import datetime
    iot_wireless_client = boto3.client('iotwireless')
    iot_data_client = boto3.client('iot-data')
    
    def lambda_handler(event, context):
    
        iot_wireless_response = iot_wireless_client.get_position_estimate(Ip={ 'IpAddress': event['ipAddress']}, Timestamp=datetime.now())
    
        print(f"IoT Wireless Response: {iot_wireless_response}")
        
        iot_data_response = iot_data_client.publish(
            topic='device/test-1/location/ipbased/response',
            qos=0,
            payload=json.dumps({'location':json.loads(iot_wireless_response['GeoJsonPayload'].read())})
            )
       
        print(f"IoT Data Response: {iot_data_response}")
    EOF
    
    zip -r lambda_function.zip lambda_function.py
    
  3. Implement the commands below to create your Lambda function and add the Boto3 Lambda layer to it
    LAMBDA_FUNCTION=$(aws lambda create-function --function-name ipAddressToDeviceLocationServiceFunction \
    --zip-file fileb://lambda_function.zip --handler lambda_function.lambda_handler --runtime python3.9 \
    --role arn:aws:iam::$ACCOUNT_ID:role/lambda-ex-ipAddressToDeviceLocationService)
    
    LAMBDA_ARN=$(jq -r '.FunctionArn' <<< "$LAMBDA_FUNCTION")
    
    
    aws lambda update-function-configuration \
    --function-name ipAddressToDeviceLocationServiceFunction \
    --layers $LAYER_ARN
    

    Use the add-permission command to allow AWS IoT Core to invoke the Lambda function

    aws lambda add-permission --function-name ipAddressToDeviceLocationServiceFunction \
    --statement-id iot-events --action "lambda:InvokeFunction" --principal iot.amazonaws.com
    

Step 3: Create an AWS IoT Rule

You will create an AWS IoT Rule use the commands below from the AWS CloudShell session:

  1. Create the rule using the create-topic-rule command
    ( jq -n \
        --arg lambda_arn "$LAMBDA_ARN" \
        '{
            "sql": "SELECT * FROM \"device/+/location/ipbased\"",
            "ruleDisabled": false,
            "awsIotSqlVersion": "2016-03-23",
            "actions": [{
                "lambda": {
                    "functionArn": "\($lambda_arn)"
                }
            }]
        }' ) > iot_rule_document.json
        
    aws iot create-topic-rule \
    --rule-name "ip_address_to_device_location_service" \
    --topic-rule-payload file://iot_rule_document.json
    

Step 4: Viewing device location using the AWS IoT MQTT client

To view MQTT messages in the MQTT client do the following:

  1. In the AWS IoT console, in the left menu, under Test, choose MQTT test client.
  2. In the Subscribe to a topic tab, enter the topic device/test-1/location/ipbased/response and then choose Subscribe

To publish a message to an MQTT topic do the following:

  1. In the Publish to a topic tab, enter the topic device/test-1/location/ipbased and then enter the below JSON as the Message Payload
    • { "ipAddress": "<replace-with-public-ip-address>" }
  2. Hit Publish to publish your message

    Figure 2 – Publish to a topic screen in MQTT test client

    Figure 2 – Publish to a topic screen in MQTT test client

The output from the publish request should look similar to the following:

{
  "location": {
    "coordinates": [
      **Longitude**,
      **Latitude**
    ],
    "type": "Point",
    "properties": {
      "country": "United States",
      "city": "New York",
      "postalCode": "*****",
      "horizontalAccuracy": 20,
      "horizontalConfidenceLevel": 0.67,
      "state": "New York",
      "timestamp": "2023-01-04T20:59:13.024Z"
    }
  }
}


Cleaning Up

Be sure to remove the resources created in this blog to avoid charges. Run the following commands to delete these resources:

  1. aws iot delete-topic-rule --rule-name "ip_address_to_device_location_service"
  2. aws lambda delete-function --function-name "ipAddressToDeviceLocationServiceFunction"
  3. aws iam delete-role-policy --role-name "lambda-ex-ipAddressToDeviceLocationService"  --policy-name "lambda-ex-ipAddressToDeviceLocationService-policy"
  4. aws iam delete-role --role-name "lambda-ex-ipAddressToDeviceLocationService"

Conclusion

In this post you learned how to get started with the new AWS IoT Core Device Location feature, key  steps to take before using the feature, and information to help you resolve your device’s location based on only its IP address. In addition to resolving your device’s location using this feature, you can also take advantage of the Amazon Location Service to track the device’s location on a map. For a more in depth look at developing with the AWS IoT Core Device Location feature, please take a look at the developer guide and watch Send Geo Coordinates from IoT Devices to Amazon Location Service with the AWS IoT Rules Engine.  If your application uses AWS IoT Core for LoRaWan please refer to this blog post for more information, Introducing the new AWS IoT Core Device Location feature to support Asset Tracking solutions.

About the authors

Yuri Chamarelli  is an Amazon Web Services Solution Architect (AWS) based out of the United States. As an IoT specialist, he focuses on helping customers build with AWS IoT and accomplish their business outcomes. Yuri is a Controls engineer with over 10 years of experience in IT/OT systems and has helped several customers with Industrial transformation and Industrial automation projects throughout many industries.

 

 

 

Nicholas Switzer is an IoT Specialist Solutions Architect at Amazon Web Services. He joined AWS in 2022 and specializes in IoT and Edge Computing and works with customers in the connected product space. He is based in the US and enjoys building smart products that improve everyday life.