AWS API Gateway & Lambda

In this chapter we describe how to integrate IoT Creators' Telekom IoT with AWS Timestream database via AWS API Gateway and Lambda functions and to visualize the data with Grafana

We show how you can implement your own end-to-end IoT solution for sensor data monitoring within a few hours.

Beside the NB-IoT sensor which sends data via UDP or CoAP/Leul to IoT Creators Data Provisioning service you need to have access to Amazon Web Services to implement your data storage and Grafana Cloud (https://grafana.com/products/cloud/) to implement your dashboards.
Instead of using Grafana Cloud you can intall you own Grafan instance on your own Laptop or in your own cloud environment.

The solution architecture is shown in the next diagram. As you can see you will use AWS Timestream DB to store your IoT data and AWS Lambda function and API gateway to decode the sensor specific data format and inject the data into Amazon Timesteam DB. To create fancy dashboards for your IoT data you will use Grafana with Amazon Timestream plugin to read your IoT data out of Timestream DB.
This solution will already implement client authentication of IoT Creators for your API Gateway.

1140

IoT Creators / AWS / Grafana Cloud integration architecture

As you can see in the end-to-end architecture above there is no message queuing integreted. Queuing is required as soon you have to deal with high load of device messages. By this you can decoble the message post requests of the clients from the actual data decoding and injection. From the performance and scalabiltiy perspective this brings you a lot of advantages.
To give you an idea how this can look like with standards AWS capabilities we will integrate AWS SQS (Simple Queue Service) into the solution and cut the Lambda functions which receives the device messages, decodes them and injects them into the Timestream DB into two pieces with an ansynchronous message queue between.

Perequisite

To start with this tutorial you need to fulfill the following prerequisites:

  • IoT Creators account and at least a Starterkit SIM card.
  • NB-IoT IoT Sensor which sends its payload via UDP to IoT Creator's UDP server (e.g. comfortsensor from IMBUILDING (see device catalog or documentation library). Any other NB-IoT devkit such as Quectel BC66 will work as well.
  • Access to AWS with permissions to create users, roles, Timestream DB, Lamdda function, API gateway.
  • Grafana or an account for the Grafana Cloud (https://grafana.com/products/cloud/)

Steps

Based on this prerequisites you will implement the architecture above with the following steps:

Step 1: Register and activate sensor device

❗️

First register than activate !

It is important that you first register your device in a project of IoT Creates and than you take the device into operation.
If the device sends messages to IoT Creators before your registered it will be put on the blacklist. After this you can not register it without support from IoT Creators support.
In case this happened to your send an email to [email protected].

Register device in IoT Creators

To register your device perform the following steps:

762
  • Click the button REGISTER DEVICE
528
  • IMEI: input the IMEI of your device
  • Click button REGISTER DEVICE

Activate IMBUILDING Comfort Sensor

If you received a IMBUILDING comfort sensor which is already Ready for IoT Creators you just need to open the device on the bagside and input the 3.6V batteries. As soon you put it under power the devices attaches to the mobile network and starts to send data.

Step 2: AWS: create user and role with IAM

Create Role for Lambda Function

In this step we create a role for the lambda function to provide permissions to it to executed and to write data into the Timestream DB

  • Search and open IAM service
  • Select Roles (on the left side) and push button Create role
  • Select type of trusted entity: AWS services
  • Choose a use case: Lambda
1222
  • Push button Next: Permissions (right bottom)
  • Select policy AWSLambdaBasicExecutionRole
  • Select policy AmazonTimestreamFullAccess
  • Push button Next: Tags (right bottom)
  • Push button Next: Review (right bottom)
1232
  • Push button Create role (right bottom)

Create User for Grafana Client

In this step we create a user for Grafan client to let it read data from the Timestream DB.

  • Search and open IAM service
  • Select Users (on the left side) and push button Add user
  • User name: myGrafanaClient
  • Access type: Programmatic access
1238
  • Push button Next: Permissions (right bottom)
  • Set permissions Attach existing policies directly
  • Select AmazonTimestreamReadOnlyAccess
1242
  • Push button Next: Tags (right bottom)
  • Push button Next: Review (right bottom)
1232
  • Push button Create user (right bottom)
  • Store Secret access key by donwnload CSV file or copy and post it.

Step 3: AWS: create Timesteam DB

Create Timestream DB

In this step we create the Timestream DB.

  • Search and open Timestream DB service
  • Select Databases (on the left side) and push button Create database (on right side)
  • Choose a configuration: Standard database
  • Name: myIoTDB
830
  • Push button Create database (right bottom)

Create Timestream Table

In this step we Timestream table in which the IoT data will be stored.

  • Select Tables (on the left side) and push button Create table (on right side)
  • Database name: myIoTDB
  • Table name: myIoTTable
  • Memory store retention: 1 Week
  • Magnetic store retention: *3 Months
798
  • Push button Create table (right bottom)

Step 4: AWS: create Lambda function for data decoding and injection

Create Lambda function

In this step we create the Lambda function which receives the sensor data from IoT Creates, decodes it into a simple JSON format and injects it into the table of the Timestream DB.
By default Lambda functions are NOT accessable from the internet. To make it accessable from the internet we need to create an AWS API Gateway, bind the Lambda function to it and implement a static token authorizier in one of the later steps. After we made this Lambda function accessable from the internet it will act as the endpoint which we will configure as application URL in the IoT Creators project.
In the follwoing steps we concentrate to create the Lambda function it-self and to the Python source code which performs the decoding or transformation of the received message into a simple JSON format and to write it to the previous created table of Timestream DB.

  • Search and open Lambda service
  • Select Functions (on the left side) and push button Create function (on right side)
1280
  • Choose Autor from scratch
  • Function name: myIoTInjector
  • Runtime: Python 3.8
  • Open Change default execution role
  • Activate: Use an existing role
  • Select Existing role: IoTCreatorsDataInjectorToTimestream
  • Push button Create function (right bottom)
1286

As next you need to replace the 10 lines of initial code in the Lambda function by the following one.

LAMFUNC_NAME="myIoTInjector"
DATABASE = "myIoTDB"
TABLE = "myIoTTable"
DEFAULT_DEVICE_TYPE = "Data"

import traceback
import time
import json

import logging
log = logging.getLogger(LAMFUNC_NAME)
log.setLevel(logging.DEBUG)

import boto3
from botocore.config import Config

def decodeData(dataStr):
    '''
    Generic decoding function for devices which data shall be stored 
    as string and which doesn't require any decoding.
    '''
    d = {
        "deviceType" : "Generic.Data",
        "Data": dataStr
    }
    return d

def decodeHexData(hexString):
    '''
    Generic decoding function for devices which data is a 
    hex coded string. 
    '''
    s = bytes.fromhex(hexString).decode("utf-8")
    
    log.debug("PAYLOAD: as str: %s" % (s))
     
    d = {
        "deviceType" : "Generic.HexData",
        "Data": s
    }
    return d


def decodePikkertonXBS200(hexString):
    '''
    Decoding function for the hex encoded payload
    of XBS200 device from Pikkerton.
    '''
    s = bytes.fromhex(hexString).decode("utf-8")

    log.debug("PAYLOAD: %s" % (s))
        
    d = {
        "deviceType" : "Pikkerton.XBS200"
    }
    
    for e in json.loads(s):
        # Temperature 
        if "/3303/0/5700" == e["n"]:
            d["Temperature"] = float(e["v"])
        
        # Humidity 
        elif "/3304/0/5700" == e["n"]:
            d["Humidity"] = float(e["v"])

        # CO2 
        elif "/3325/2/5700" == e["n"]:
            d["CO2"] = float(e["v"])

    return d


def decodeSDI(hexString):
    '''
    Decoding function for the hex encoded payload
    of comfort sensor from IMBUILDING.
    '''
    a = bytearray.fromhex(hexString)
    type = a[0]
    version = a[1]
    
    if type == 1 and version == 1:
        d = {
            "deviceType"  : "IMBUILDING.Comfort",
            "status"      : a[8],
            "BatteryV"    : int.from_bytes(a[9:11], "big") / 100.,
            "Temperature" : int.from_bytes(a[12:14], "big") / 100.,
            "Humidity"    : int.from_bytes(a[14:16], "big") / 100.,
            "CO2"         : int.from_bytes(a[16:18], "big"),
            "Presence"    : "true" if a[18] > 0 else "false"
        }
        return d
        
    elif type == 2 and version == 4:
        d = {
            "deviceType"  : "IMBUILDING.PeopleCount",
            "status"      : a[8],
            "BatteryV"    : int.from_bytes(a[9:11], "big") / 100.,
            "CounterA"    : int.from_bytes(a[12:14], "big"),
            "CounterB"    : int.from_bytes(a[14:16], "big")
        }
        return d
       
    else:
        log.warn("Unsupported type %d and version %d" % (type, version))
        return None
        
def writeRecordToDB(client, deviceId, timestamp, data):
    '''
    Builds the records and writes them into the Timestream DB. 
    '''
    records = []

    dimensions = [
        {'Name': 'deviceId', 'Value': deviceId},
        {'Name': 'deviceType', 'Value': data["deviceType"]}
    ] 
     
    for e in [  ["CO2", "DOUBLE"], ["Temperature", "DOUBLE"], ["Humidity", "DOUBLE"], 
                ["Presence", "BOOLEAN"],["BatteryV", "DOUBLE"], ["CounterA", "BIGINT"], 
                ["CounterB", "BIGINT"], ["Data", "VARCHAR"]]:
        n = e[0]
        t = e[1]

        if n in data.keys():
            r = { 
                'Dimensions': dimensions, 
                'MeasureName': n, 
                'MeasureValue': str(data[n]), 
                'MeasureValueType': t, 
                'Time': str(timestamp)
            }
            records.append(r)

    log.debug("writting records for device %s to Timestream DB ..." % (deviceId))
    result = client.write_records(DatabaseName=DATABASE, TableName=TABLE, Records=records, CommonAttributes={}) 

def lambda_handler(event, context):
    '''
    Lambda functions main entry point.
    '''
    log.info("=> %s" % (LAMFUNC_NAME))

    log.debug(json.dumps(event, sort_keys=True, indent=4))

    try:
        ###
        # Get the device type from the header. With the device
        # type the decoding function can be selected.
        
        devType = DEFAULT_DEVICE_TYPE
        
        if "DeviceType" in event["headers"].keys():
            devType = event["headers"]["DeviceType"]
            devType = devType.strip() if devType else DEFAULT_DEVICE_TYPE

        log.debug("DeviceType: %s" % (devType))

        decodeFuncName = "decode%s" % devType
        if decodeFuncName not in globals().keys():
            log.warn("Decoding function %s does not exist!" % (decodeFuncName))
            decodeFuncName = decodeData
            
        decodeFunc = globals()[decodeFuncName]
        
        ###
        # Get the Impact reports from the request body
                
        s = event["body"] if "body" in event.keys() else "{}"
        
        # If not body don't do anything
        if not s or len(s) == 0:
            return {'statusCode': 204, 'body': "OK"}        
        
        log.debug("BODY = %s" % (str(s)))
        
        body = json.loads(s)

        if not "reports" in body.keys():
            return {'statusCode': 200, 'body': "no reports"}


        ###
        # Create Timestream session
                    
        session = boto3.Session()
        cfg = Config(read_timeout=20, max_pool_connections=5000, retries={'max_attempts': 10})
        client = session.client('timestream-write', config=cfg)
            
        ###
        # Process each report record 
        
        for rep in body["reports"]:
            log.debug("PAYLOAD: %s" % (rep["value"]))

            data = decodeFunc(rep["value"])
            
            if data:
                writeRecordToDB(client, rep["serialNumber"], rep["timestamp"], data)

        return {'statusCode': 204, 'body': "OK"}
        
    except Exception as ex:
        log.error("%s" % (str(ex)))
        traceback.print_exc()
        return {'statusCode': 400, 'body': "ERROR: %s" % (str(ex))}

This code contains functions

  • lambda_handler: Implements the HTTP POST to receive one or multiple data message from IoT Creators SCS and process each of them.
  • writeRecordToDB: Creates out of the simple JSON message format a write request to the Timeseries DB.
  • decodeSDI: Decodes the payload of the Comfort sensor and People Couter from IMBUILDING to the simple JSON format.
  • decodePikkertonXBS200: Decodes the payload of the XBS200 sensor from Pikkerton to the simple JSON format.
  • decodeHexData: Decodes the hex encoded payload to a single "Data" element in the simple JSON format.
  • decodeData: Copies the payload to a single "Data" element in the simple JSON format.

🚧

Use DeviceType Header field to select the decoding function in your POST request

To control which decoding function shall be used you can use in your POST request the header field DeviceType. To "tell" the myIoTInjector.py Lambda function to decode the device payload with the function decodePikkertonXBS200 just add

DeviceType: PikkertonXBS200

If you don't define DeviceType header field in the POST request the function decodeData is used and the payload is copied without any decoding to simple JSON message.

Create up-to-date boto3 Library Layer and bind it to myIoTInjector Lambda function

As you can see the previous created myIoTInjector Lambda function imports the AWS Python library boto3 to get access to the Timestream DB. Because AWS Timestream DB service is quite new the boto3 library which is made available to Python Lambda functions by default does not contain support for it. Out of this reason we need to create an updated version of boto3 library locally and import it as an library Layer into the Lambda function.
In the following I will describe how to

Create updated boto3 library with Timestream support

📘

As already mentioned above: you can download the updated boto3 library for Python from https://github.com/iotcreators/doclib-downloads/raw/master/iot-platform-integration/integrate-iotcreators-with-grafana-via-AWS/Python-Boto3-with-Timestream.zip
.

On your Linux box perform the following steps (maybe it works on windows as well. I never tried it 😄)

🚧

We created the previous Python Lambda function for rumtime 3.8. Out of this reason you should create the local boto3 library also with a Python 3.8 version. By this you reduce potential problems.

Create a directory for the project
$ mkdir -p boto3-layer/python

Install the boto3 package into the boto3-layer/python directory
$ pip install boto3 -t boto3-layer/python

Zip the contents of boto3-layer into a an archive
$ cd boto3-layer $ zip -r aws-boto-layer.zip python

This description I copied from https://www.stefreitag.de/wp/2019/11/21/creating-an-aws-lambda-layer-for-boto3/. Many thanks to Stefan Freitag who published it 👍

Create a Lambda Layer for the updated boto3 library.

  • Search and open Lambda service
  • Select Layers (on the left side) and push button Create layer (on right side)
740
  • Name: myBoto3
  • Select Upload a zip file and upload your previous created boto3 zip file
  • Choose Compatible runtimes: Python 3.8
  • Push button Create (on the right side)

Assign the updated boto3 library to myIoTInjector Lambda function

  • Search and open Lambda service
  • Select Functions (on the left side) and push Lambda function myIoTInjector
  • Push on Layers
886
  • Push on Add a layer (at the button on the right side)
745
  • Select Custom layers
  • As Customer layer select myBoto3
  • Version: 1
  • Push on Add (at the button on the right side)

Step 5: AWS: publish Lambda function to internet

To make your Lambda function as HTTP REST service callable from the internet you need to bring an API Gateway into place and publish your Lambda function on it.
In this chapter you will

  • Create an API Gateway.
  • Bind your previous created Lambda function to decode sensor data and inject it to Timestream.
  • Test if you can call the Lambda function from the internet and if data ends up in Timestream.

Create API Gateway

With the following steps you can create your API Gateway.

  • Search and open API Gateway service
  • Push button Create API (on the right side)
  • In HTTP API: Push button Build
1226
  • API name: myIoTAPI
  • Push button Next
  • In Configure routes: Push button Next
  • In Configure stages: Push button Next
1227
  • Push button Create

Bind Your Lambda Function to API Gateway

With the following steps you can bind your Lambda function to the API Gateway.

  • Search and open Lambda service
  • Select Functions (on the left side) and push Lambda function myIoTInjector
  • Push on + Add trigger
743
  • Select API Gateway
  • API: myIoTAPI
  • Deployment stage: $default
  • Security: Open
  • Push button Add (on the right side)
812

Test your Lambda function and verify if data has been written to Timestream

To test your Lambda function and if it is accessable via the internet we use the tool Postman. With Postman you can build your HTTP request interactivly and send it to URLs to inspect the results. To get Postman go to https://www.postman.com/downloads/.
We will perform the following steps to post a request to your Lambda function and to verify if data has been written to your Timestream DB:

  • Get public URL of your Lambda function
  • Perform a POST request to your Lamba function with Postman
  • Check if the data has been written to your Timestream DB

Get public URL of your Lambda function

As first you need to get the public URL of your Lambda function.

  • Search and open Lambda service
  • Select Functions (on the left side) and click Lambda function myIoTInjector
  • Click on API Gateway
  • Open the Details of the API Gateway (at the botton)
  • Copy the API endpoint URL.
654

Perform a POST request to your Lamba function with Postman

  • Start Postman and create a new POST request by selecting action type in the list top left.
  • Past the previous copied URL of your Lambda function into the URL field
  • Select the Body page and click the radio button raw
  • Add the JSON payload (see below the image) to the body input.
  • Click button Send
    If the request has been executed successfully you should see 204 No Content in the response.
1286

❗️

How to create new timestamps

For a device you can use a timestamp only once. Timestamp DB will return with an error if you try to send the body twice.
To create a new timestamp goto https://www.epochconverter.com/ and create with Human date to Timestamp a timestamp in milliseconds.

{
    "reports":[{
        "serialNumber":"IMEI:866425033313638",
        "timestamp":1607688000000,
        "subscriptionId":"fa37d89c-a7e2-4f3d-b12f-6002a3642b4c",
        "resourcePath":"uplinkMsg/0/data",
        "value":"0101d880396f026e00014ca709411417052001"
    }],
    "registrations":[],
    "deregistrations":[],
    "updates":[],
    "expirations":[],
    "responses":[]
}

The value in this JSON nessage sample is the sensor payload of the IMBUILDING comfort sensor which contains CO2, temperaturs, humidity, body sense and battery.

Check if the data has been written to your Timestream DB

  • Search and open Timestream service
  • Select Databases (on the left side) and click database myIoTDB
  • Select Tables tab and click table myIoTTable
  • Click button Query table (on the top left side)
  • Click button Run
    If everything worked find you should see multiple rows returned.
952

Step 6: AWS: add static token authentication to Lambda function

I guess you recognized, that your Lambda function which you just triggered from the internet successfully didn't have any kind of authorization. This means that everyone of the world can trigger your function and post data to it. Of cause you don't want to have an entry to your Timestream DB which is open for the rest of the world. Out of this reason we will add the capability to your Lambda function that it can be only called if the calling clients perform a proper authentication before.

There are different methods to enable authentication for Lambda functions. A nice method is to have a programmatic user account for each calling client system in IAM and use AWS HTTP signatures to authenticate the client againt it. This method requires that the fields in the HTTP header which have to passed along with the POST request contains a more or less current timestamp which needs to be generated for each request. Because IoT Creators SCS doesn't support this kind of dynamics and also doesn't support the automatic generation of AWS signatures we have to use a more static method for authentication.

You will implement an additional Lambda function which will act as an authorizer and be bound to your API Gateway.
Your Lambda authorizer will require that the HTTP request with IoT data contains the header field Authorization with colon separated name of your client and its security token.

Authorization:  iotCreators.Starterkit:d86zGcbpRcDiiWb8UQEBChrI1WYxDXRrJDVwfdsd

To enable such static client specific token authorization you will perform the following steps:

  • Create a new Lambda function myIoTAuthorizer
  • Bind myIoTAuthorizer as Lambda authorizer to your API Gateway and Lambda function for which you like to enable the authorization.
  • Test if the token authorization works.

Create new Lambda function myIoTAuthorizer

  • Search and open Lambda service
  • Select Functions (on the left side) and push button Create function (on right side)
948
  • Choose Autor from scratch
  • Function name: myIoTAuthorizer
  • Runtime: Python 3.8
  • Push button Create function (right bottom)
892

As next you need to replace the 10 lines of initial code in the Lambda function by the following one.

import json

# Client systems which are allowed to access the injection lambda
CLIENTS = {
    "iotCreators.Starterkit": {"token":"d86zGcbpRcDiiWb8UQEBChrI1WYxDXRrJDVwfdsd"}
}

def buildPolicyDocument(effect, resource="arn:aws:execute-api:*:*:*"):
    return {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Action": "execute-api:Invoke",
                "Effect": effect,
                "Resource": resource
            }
        ]
    }
    
def lambda_handler(event, context):
    global CLIENTS
    returnValue = None
    
    try:
        auth = event["headers"]["authorization"]
        auth = auth.strip() if auth else ""
        print("*** AUTH: %s" % (auth))

        if len(auth) == 0:
            raise Exception("No authorization token defined in authorziation header.")

        fields = auth.split(":")
        
        if len(fields) != 2:
            raise Exception("Authorization format of \"%s\" is invalid." % (auth))
            
        client = fields[0].strip()
        token = fields[1].strip()

        if client not in CLIENTS.keys():
            raise Exception("Unknown client %s (auth=%s)" % (client, auth))
            
        if token != CLIENTS[client]["token"]:
            raise Exception("Invalid token %s for client %s (auth=%s)" % (token, client, auth))

        # Get the the addressed route
        routeArn = event["routeArn"] if "routeArn" in event.keys() else "arn:aws:execute-api:*:*:*" 
        print("*** ROUTEARN: %s" % (routeArn))

        returnValue = {"principalId":client, "policyDocument": buildPolicyDocument("Allow", routeArn)}
    
    except ValueError as err:
        print(err)
        returnValue = {"principalId":None, "policyDocument": buildPolicyDocument("Deny")}

    print("*** RETURN: ***\n%s" % (json.dumps(returnValue, indent=4)))

    return returnValue

This code contains the following elements

  • CLIENTS: global dictionary in which you can register all your IoT data provider clients with a name and a token of your choice.
  • lambda_handler: Main entry point of the Lambda function which is called by API Gateway to verify the authorization of the HTTP POST request. This function reads the Authorization header fields and verifies it against the CLIENTS registration dictionary, builds a policy document and returns it calling API Gateway.
  • buildPolicyDocument: Function to generate the policy document to allow or deny the access to the posted Lambda function.

Bind myIoTAuthorizer to your API Gateway

In the next steps you will configure the previously created function as Lambda Authorizer for your API Gateway myIoTAPI.

  • Search and open API Gateway service
  • Select API (on the left side) and click myIoTAPI (on right side)
  • Click on Routes (on the left side below Develop)
  • Click on ANY below /myIoTInjector in the list of Routes
1166
  • Click on Attach authorization
1130
  • Click Create and attach an authorizer
622
  • Authorizer type: Lambda
  • Name: myIoTAuthorizer
  • AWS Region: select your region.
  • Lambda function: myIoTAuthorizer
  • Response mode: IAM Policy
  • Click Create and attach (right bottom)
802

Test if the token authorization works

To test if the token authorization works you only need to add the Authorization header field to your Postman test request from before:

Authorization:  iotCreators.Starterkit:d86zGcbpRcDiiWb8UQEBChrI1WYxDXRrJDVwfdsd

If everything works fine you should see a 204 No Content in the Status and an empty body.

1286

Step 7: Integrate IoT Creators with AWS Timestream DB

Register your AWS Lambda function as application callback in your IoT Creators project

To integrate IoT Creators with AWS Timestream DB via Lambda function and API Gateway you simple need to configure the public URL of your Lambda function with its Authorization header field to the application server of the IoT Creators project.
To do so perform the following steps:

763
  • Click on YOUR APPLICATION SERVER
761

Test the integration

To verify if your integration between IoT Creators and your Lambda function works probably you just need to check the table in your Timestream DB as you did it already before when you checked if your Lambda function is able to write data into your Timestream DB.
If everything work fine you should see new records in the table of your Timestream DB.

Step 8: Integrate Grafana with AWS Timestream

In this step you get explained how to integrate your AWS Timestream DB with Grafana. The descriptions in this step require that you have at least a test account for Grafana Cloud.

Add Amazon Timestream Plugin to your Grafana

  • Open Configuration and click on the tab Plugins
  • Click on button Find more plugins on Grafana.com
  • Input Timestream into the input field Name/Desription
706
  • Click on Amazon Timestream data source
706
  • Click on button Install Plugin (on right side)
  • Click on button Install Now
692
  • Go back to Grafana application in your browser and refesh the Plugins browser window
974

Add Amazon Timestream Data Source

  • Open Configuration and click on the tab Data Sources
  • Click on button Add data source (on right side)
  • Search for Amazon Timestream, point on it with the mouse and click button Select
  • Authentication Provider: **Access & secret key
  • Secret Key ID: use the ID from the user myGrafanaClient
  • Secret Access Key: use the key from the user myGrafanaClient
  • Default Region: select AWS region in which you created your Timestream DB
750
  • Click on button Save & Test
826

Step 9: Create an IoT Dashboard in Grafana

Create DevKit payload as data table

  • Mouse over + in function bar of the main window (on the left sideI
  • Click on Dashboard
  • Click on button + Add new panel
766

in right Panel: Panel title: DevKit Payload - 867997031341190

  • in right Panel: Visualization: Table
  • in Query: Select Amazon Timestream
  • in Query: Paste the following query into the query editor.
select time, "measure_value::varchar" as Message
from myIoTDB.myIoTTable
where $__timeFilter
  and measure_name = 'Data'
  and deviceId = 'IMEI:867997031341190'
order by 
  time desc

🚧

Don't forget to replace In the query code above the deviceId IMEI:867997031341190 with id of your device.

Click button refresh Dashboad 🔄 at the top of the window

780

Click button Apply at the right top of the window

Create CO2 diagram for a sensor

  • Mouse over + in function bar of the main window (on the left sideI
  • Click on Dashboard
  • Click on button + Add new panel
766
  • in right Panel: Panel title: CO2
  • in right Panel: Visualization: Graph
  • In right Panel: Display: Lines, Points
  • in Query: Select Amazon Timestream
  • in Query: Paste the following query into the query editor.
select create_time_series(time, measure_value::double) as CO2
from myIoTDB.myIoTTable
where $__timeFilter
  and measure_name = 'CO2'
  and deviceId = 'IMEI:351938100106687'

🚧

Don't forget to replace In the query code above the deviceId IMEI:351938100106687 with id of your device.

  • Click button refresh Dashboad 🔄 at the top of the window
864
  • Click button Apply at the right top of the window

👍

✨ 🌟 👍 👏 Congratulations !!!

If you can see the graph with your values you reached end of this journey. Of cause this is a very simple integration which leaves much space for optmization. But at least it provides you an central theme through an end-to-end IoT solution. It addresses all the important layers of

  • device
  • mobile connectivity and IoT data provisioning
  • IoT data decoding/transformation
  • IoT data injection
  • IoT data storage
  • IoT data visualization

Step 10: Integrate AWS Simple Queue Service

The description how to integrate the AWs Simple Queue Service to decouple the client's post of iot message from its decoding and injection into the Timestream DB will come soon.