The Internet of Things on AWS – Official Blog
Perform Protocol Conversion at the Edge with AWS Lambda and AWS Greengrass
In industrial automation, you will find dozens of protocols used by devices and PLC vendors: OPC-UA, Modbus TCP, serial, and more. In fact, often in just one facility, it is common to find two similar processes performing the same function with different equipment.
AWS Greengrass reduces the operational overhead for businesses to change protocols as needed through remote deployments. AWS Greengrass uses AWS Lambda, a service that makes it possible to program devices in the cloud. For example, a business can deploy AWS Lambda functions to field devices to add additional sensors or change vendor equipment. The ability to roll out changes from the cloud to the field reduces the development and maintenance cycle for IoT applications.
In this blog post, we will show an example of how you can convert data from a common industrial protocol, such as Modbus TCP. You might be able to use the same approach with other protocols.
Example: Resistance temperature detector
AWS Greengrass supports OPC-UA, an information exchange standard for industrial communication. Like Modbus TCP, OPC-UA allows you to ingest and process messages from industrial equipment and deliver them to devices in your AWS Greengrass group or to the cloud based on rules you define.
In the example used in this post, we collect simulated temperature readings from a resistance temperature detector (RTD) attached to the bearing housing of a motor. (Although it’s beyond the scope of this blog post, you can use AWS IoT Analytics to analyze this temperature information. It can help you provide predictive maintenance for bearing lubrication or detect imminent failure especially for motors, generators, and other high-voltage equipment)
The following diagram shows how AWS Greengrass runs a Lambda function that connects natively to devices that use Modbus TCP, such as a programmable logic controller (PLC). The same Lambda function publishes the values read from the Modbus Slave and registers to MQTT topics in AWS IoT Core.
Lambda functions can be triggered by events detected locally or from AWS IoT Core, which can invoke writes back to the Modbus Slave device.
Prerequisites
To follow the steps in this post, you need a Raspberry Pi with python-dev, python-pip, pyModbus, pycrypto, pyasn1 and version 1.3 of Greengrass Core software or higher installed.
Set up AWS Greengrass and driver libraries
Follow these steps to set up AWS Greengrass on your Raspberry Pi. For more information, see the Greengrass Developer Guide.
Create a Greengrass core
Create a Greengrass group. If you already have a group, you can skip this step.
- In the left pane of the AWS IoT console, choose Greengrass, and then choose Groups.
- Choose the Create Group button.
- Choose Use easy creation.
- Type a name for your group.
- Type a name for the Greengrass core associated with the group or use the default.
- Choose Create Group and Core.
- Download the core security resources, which are provided as a tar.gz.
Follow the steps in the Developer Guide to load keys to your device and start Greengrass.
Deploy Greengrass core
- In the AWS IoT console, from the Actions menu, choose Deploy.
- Choose Automatic Detection or manually set up the endpoint information for the core.
- After your core is deployed, you should see a Successfully completed message:
Install PyModbus libraries
Now you need to install pyModbus, a full Modbus protocol implementation.
To install pyModbus, execute the following commands on the AWS Greengrass device:
sudo apt-get update sudo apt-get install python-dev sudo apt-get install python-pip sudo pip install pymodbus sudo pip install pycrypto sudo pip install pyasn1
Create Lambda functions
In this section, you create three Lambda functions. You can skip the first two if you are connecting to a real Modbus device.
- Modbus Slave (Server) – This function simulates the extruder machine PLC device.
- Modbus Simulator – This function periodically reads the CPU temperature of your Raspberry Pi and writes it to the Modbus Slave registers to simulate the motor bearing temperature.
- Modbus To AWS IoT – This function periodically reads the Modbus register for the bearing temperature and publishes the value to an MQTT topic.
Create a Modbus Slave Lambda function
-
- Sign in to the AWS Management Console on your computer and open the AWS Lambda console.
- Choose Author from scratch.
- For Name, type ModbusSlave. For Runtime, choose Python 2.7.
- When you choose an IAM role for the function, you must provide CloudWatch log permissions. The Greengrass-Modbus-Role has the following policies attached. Be sure to apply the appropriate permissions for the role.
- The following script starts a Modbus TCP Server (Slave) on the Raspberry Pi. It allocates in memory some Modbus registers that can be used to read/write data to. Insert the following script and publish a new version to be used in AWS Greengrass.Tip: Create an alias for your AWS Lambda function and assign the version published to that alias. This makes it easier to deploy updates to your function later.
- Sign in to the AWS Management Console on your computer and open the AWS Lambda console.
# # Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # ModbusSlaveServer.py # Runs a modbus slave device in memory for testing purposes. # This AWS Lambda function allocates registers for the modbus tcp slave and starts # the server. If an exception occurs, it will wait 5 seconds and try again. # Since the function is long-lived it will run forever when deployed to a # Greengrass core. The handler will NOT be invoked in our example since # the we are executing an infinite loop. import time import logging import sys #import pymodbus libraries for the server from pymodbus.server.sync import StartTcpServer from pymodbus.datastore import ModbusSequentialDataBlock from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext # instantiate logger which will log any exceptions to Cloudwatch or Greengrass # local logs logger = logging.getLogger(__name__) logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) while True: try: #create registy for modbus server store = ModbusSlaveContext( di = ModbusSequentialDataBlock(0, [17]*100), co = ModbusSequentialDataBlock(0, [17]*100), hr = ModbusSequentialDataBlock(0, [17]*100), ir = ModbusSequentialDataBlock(0, [17]*100)) context = ModbusServerContext(slaves=store, single=True) # change default port of modbus from 502 to 5020 as it requires # root permissions below 1024 StartTcpServer(context, address=("localhost", 5020)) except Exception, e: logging.info("Error: {0}".format(str(e))) time.sleep(5) # this function is never called as it will be configured as long-lived. def lambda_handler(event, context): return 'Hello'
Create a Modbus Slave Simulator Lambda function
- Repeat the steps 1-3 above to create a function named ModbusSlaveSim.
- The following script creates a Modbus Client (Master) that connects to the Modbus TCP Server (Slave) from the previous step and stores the bearing temperature captured from an RTD device every 5 seconds. (The RTD device in this case is just the CPU temperature of the Raspberry Pi.) Insert the following script and publish a new version to be used in AWS Greengrass.
# # Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # ModbusSlaveSim.py # This is a simulator script that connects to a modbus slave device and # writes the CPU temperature for the raspberry pi device to a modbus register. # If an exception occurs, it will wait 5 seconds and try again. # Since the function is long-lived it will run forever when deployed to a # Greengrass core. The handler will NOT be invoked in our example since # we are executing an infinite loop. import time import logging import sys import os # import pymodbus libraries for the simulator client from pymodbus.client.sync import ModbusTcpClient as ModbusClient from pymodbus.payload import BinaryPayloadDecoder from pymodbus.payload import BinaryPayloadBuilder from pymodbus.constants import Endian from pymodbus.compat import iteritems # Default port for modbus slave is typically 502. Using 5020 for simulation to # avoid root permissions. client = ModbusClient('127.0.0.1', port=5020) # instantiate logger which will log any exceptions to Cloudwatch or Greengrass # local logs logger = logging.getLogger(__name__) logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) # returns the cpu temperature from the raspberry pi device def get_temp(): f = open("/sys/class/thermal/thermal_zone0/temp") CPUTemp = f.read() f.close() return float(CPUTemp)/1000.0 # in an infinite loop, this procedure will poll the cpu temperature and write # it to a local modbus slave device. def poll_temp(): while True: try: client.connect() builder = BinaryPayloadBuilder(endian=Endian.Big) builder.add_32bit_float(get_temp()) payload = builder.build() address = 0 rq = client.write_registers(address, payload, skip_encode=True, unit=1) except Exception, e: logging.info("Error: {0}".format(str(3))) time.sleep(5) # executing polling on startup. poll_temp() # this function is never called as it will be configured as long-lived. def lambda_handler(event, context): return 'Hello'
Create a Modbus to AWS IoT function
- To create the final function, in the AWS Lambda console, select Blueprints. Under Blueprints, search for greengrass. Choose the Python blueprint runtime and name it ModbusToAWSIoT.
- The following script creates a Modbus Client (Master) that connects to the Modbus Server (Slave) and retrieves the bearing temperature stored in the Modbus Server register. The value is then published to a topic on AWS IoT. Insert the following script and publish a new version to be used in AWS Greengrass.
# # Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # ModbusToAWSIoT.py # This is an example script that connects to a modbus slave device to read # a temperature value and publish to an MQTT Topic in AWS IoT every 5 seconds. # If an exception occurs, it will wait 5 seconds and try again. # Since the function is long-lived it will run forever when deployed to a # Greengrass core. The handler will NOT be invoked in our example since # we are executing an infinite loop. import greengrasssdk import platform from threading import Timer import time import logging import sys import json # import pymodbus libraries for the modbus client from pymodbus.client.sync import ModbusTcpClient as ModbusClient from pymodbus.payload import BinaryPayloadDecoder from pymodbus.payload import BinaryPayloadBuilder from pymodbus.constants import Endian from pymodbus.compat import iteritems # Instantiate the client for your modbus slave device. In this example we are # using the local IP address where a simulator exists. Change this to your # desired IP. In addition, the typical default port for Modbus TCP is 502. For # this example, 5020 was used. mbclient = ModbusClient('127.0.0.1', port=5020) # Default port for modbus slave is typically 502. Using 5020 for simulation to # avoid root permissions. logger = logging.getLogger(__name__) logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) # Creating a greengrass core sdk client client = greengrasssdk.client('iot-data') # in an infinite loop, this procedure will poll the bearing temperature from a # modbus slave device (simulator) and publish the value to AWS IoT via MQTT. def poll_temp(): while True: try: # connect to modbus slave device mbclient.connect() # set the address and number of bytes that will be read on the modbus device address = 0x00 count = 8 # read the holding register value for the temperature rr = mbclient.read_holding_registers(address, count, unit=1) # decode results as a 32 bit float decoder = BinaryPayloadDecoder.fromRegisters(rr.registers, endian=Endian.Big) decoded = { 'float': decoder.decode_32bit_float() } # publish results to topic in AWS IoT for name, value in iteritems(decoded): client.publish(topic='dt/controller/extruder/plc1/rtd', payload=json.dumps({ 'Temp': value})) except Exception, e: logging.info("Error: {0}".format(str(e))) time.sleep(5) poll_temp() # This is a dummy handler and will not be invoked # Instead the code above will be executed in an infinite loop for our example def function_handler(event, context): return
Deploy the AWS Lambda functions
Now you are ready to deploy our protocol conversion script. Sign in to the AWS Management Console and then open the AWS IoT console.
Add AWS Lambda functions to the Greengrass group
- Go to the group you deployed earlier. In the left pane, choose Lambdas and then choose Add Lambda.
- Choose Use existing Lambda
- Choose one of the Lambda functions you created earlier and then choose Next
- Select the alias that was created when you defined the function. If an alias was not created, select the version that was published.Tip: We recommend that you create an alias so that you will not need to perform this step again if a new version of your Lambda function needs to be published.
- In the list of AWS Lambda functions, choose the Lambda function that was added, and then choose Edit. On this page, you can enable access to resources on the device. This could be serial or USB ports, depending on the protocol driver.
- For Lambda lifecycle, select Make this function long-lived and keep it running indefinitely. For Read access to /sys directory, select Enable
- Repeat these steps for the other Lambda functions that were created
Assign subscription
- Go to the group you deployed earlier. In the left pane, choose Subscriptions
- Choose Add Subscription
- Under Subscriptions, for Source, choose ModbusToAWSIoT. For Target, choose IoT Cloud. Do not provide a filter for the topic
This creates a subscription in AWS IoT to capture any published data coming from the Lambda function on the AWS Greengrass device.
Deploy changes
- From the Actions menu on your group configuration page, choose Deploy. From AWS IoT, you can subscribe to dt/controller/extruder/plc1/rtd to view data that is converted from Modbus TCP to MQTT
Conclusion
With AWS Greengrass, it is possible to acquire data from many field devices even if they are using different local communication protocols. The example in this post has shown how you can communicate with protocols such as Modbus TCP and securely transfer data to the cloud in real time.
We hope you found this blog post helpful. Feel free to leave questions and other feedback in the comments.