Original URL: https://aws.amazon.com/blogs/iot/perform-protocol-conversion-at-the-edge-with-aws-lambda-and-aws-greengrass/
在工业自动化领域,您会发现设备和 PLC 供应商使用了数十种协议:OPC-UA、Modbus TCP、串行协议等等。事实上,往往仅在一个工厂,就会很容易发现有不同的设备,却执行相同的功能去完成相似的工序。AWS Greengrass 通过远程部署来帮助企业减少更改协议时的运行开销。AWS Greengrass 使用 AWS Lambda,借助这种服务可以在云中对设备进行编程。例如,企业可以将 AWS Lambda 函数部署到本地设备以添加额外的传感器或更改供应商设备。通过云中编程并部署到本地,缩短了物联网应用程序的开发和维护周期。
在本博文中,我们将演示如何从常见的工业协议(例如 Modbus TCP)来转换数据的例子。其他协议的转换也可以用同样的方法。
例如:电阻温度探测器
AWS Greengrass 支持 OPC-UA(一种用于工业通信的信息交换标准)。与 Modbus TCP 类似,OPC-UA 允许您提取并处理来自工业设备的消息,然后根据您定义的规则将它们发布到 AWS Greengrass 组中或云中的设备。
在本博文使用的示例中,我们采集了安装在电机轴承外壳上的电阻温度探测器 (RTD) 的模拟温度读数。(您可以使用 AWS IoT Analytics 来分析此温度信息,虽然这不属于本博文的范畴。它可以帮助您对轴承润滑进行预防性维护,或者检测即将出现的故障,尤其是电机、发电机和其他高压设备)
下图显示了 AWS Greengrass 如何运行 Lambda 函数原生连接到使用 Modbus TCP 的设备,例如可编程逻辑控制器 (PLC)。同样的 Lambda 函数将从 Modbus 到设备读取的值发布到 AWS IoT Core 中的 MQTT 主题。
Lambda 函数可以通过本地检测到的事件触发,也可从 AWS IoT Core 触发,调用写回至 Modbus 从设备。
![](https://d2908q01vomqb2.awsstatic-china.com/f6e1126cedebf23e1463aee73f9df08783640400/2018/07/23/image002-1024x695.png)
先决条件
要执行本博文中的步骤,您需要拥有一台安装了 python-dev、python-pip、pyModbus、pycrypto、pyasn1 和 1.3 版或更高版本 Greengrass Core 软件的 Raspberry Pi。
设置 AWS Greengrass 并安装相应的函数库
遵循以下步骤设置您的 Raspberry Pi 与 AWS Greengrass。有关更多信息,请参阅 Greengrass 开发人员指南。
创建 Greengrass 核心
创建 Greengrass 组。如果您已经拥有一个组,则可以跳过这一步。
- 在 AWS IoT 控制台的左侧窗格中,选择 Greengrass,然后选择组。
- 选择创建组按钮。
- 选择使用快捷创捷。
- 键入您的组的名称。
- 键入与该组关联的 Greengrass 核心的名称,或者使用默认值。
- 然后选择创建组和核心。
- 下载核心的安全性资源,这些资源将以 tar.gz 的形式提供。
遵循开发人员指南中规定的步骤以将密钥加载到您的设备并启动 Greengrass。
部署 Greengrass 核心
- 在 AWS IoT 控制台中,从“操作”菜单中选择“部署”。
- 选择“自动检测”或者手动设置核心的终端节点信息。
- 部署好核心后,您应会看到一条“已成功完成”的消息:
安装 PyModbus 库
现在,您需要安装 pyModbus,这是一个完整的 Modbus 协议实现。
要安装 pyModbus,请在 AWS Greengrass 设备上执行以下命令:
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
创建 Lambda 函数
在这一部分,您将创建三个 Lambda 函数。如果您已经连接到真实的 Modbus 设备,则可以跳过前两个命令。
- Modbus Slave (Server) — 此函数会模拟挤出机 PLC 设备。
- Modbus Simulator — 此函数会定期读取您的 Raspberry Pi 的 CPU 温度,并将它写入 Modbus 从设备的寄存器以模拟电机轴承温度。
- Modbus To AWS IoT — 此函数会定期读取 Modbus 寄存器中的轴承温度,并将该值发布到任何 MQTT 主题。
创建 Modbus Slave Lambda 函数
-
- 在您的计算机上登录 登录 AWS 管理控制台并打开 AWS Lambda 控制台。
- 选择全新编写。
- 对于“名称”,键入 ModbusSlave。对于运行时,选择 Python 2.7。
- 在选择函数的 IAM 角色时,您必须提供 CloudWatch 日志权限。Greengrass-Modbus-Role 已经附加了下列策略。请务必为该角色应用恰当的权限。
- 下面的脚本会在 Raspberry Pi 上启动 Modbus TCP 服务器(从站)。它会在内存中分配一些 Modbus 寄存器,这些寄存器可以用于读取/写入数据。插入下面的脚本并发布一个新版本以在 AWS Greengrass 中使用。提示:为您的 AWS Lambda 函数创建一个别名并将发布的版本分配到该别名。这样可以方便您以后向函数部署更新。
#
# Copyright 2010-2017 Amazon.com, Inc. or its affiliates.保留所有权利。
#
# 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'
创建 Modbus Slave Simulator Lambda 函数
- 重复上文第 1-3 步以创建一个名为 ModbusSlaveSim 的函数。
- 下面的脚本将创建一个 Modbus 客户端(主站),连接到上一步的 Modbus TCP 服务器(从站)并且存储每隔 5 秒从 RTD 设备获取的轴承温度值。(此例中的 RTD 设备仅仅是 Raspberry Pi 的 CPU 温度。) 插入以下脚本并发布一个要在 AWS Greengrass 中使用的新版本。
#
# Copyright 2010-2017 Amazon.com, Inc. or its affiliates.保留所有权利。
#
# 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'
创建 Modbus to AWS IoT 函数
- 要创建最后一个函数,请在 AWS Lambda 控制台中选择蓝图。在蓝图下搜索 greengrass。选择 Python 蓝图运行时并将它命名为 ModbusToAWSIoT。
- 下面的脚本会创建一个 Modbus 客户端(主站),连接到 Modbus 服务器(从站)并检索存储在 Modbus 服务器寄存器中的轴承温度。然后将该值发布到 AWS IoT 上的某个主题。插入以下脚本并发布一个要在 AWS Greengrass 中使用的新版本。
#
# Copyright 2010-2017 Amazon.com, Inc. or its affiliates.保留所有权利。
#
# 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
部署 AWS Lambda 函数
现在您可以部署您的协议转换脚本。登录 AWS 管理控制台并打开 AWS IOT 控制台。
将 AWS Lambda 函数添加到 Greengrass 组
- 打开您之前部署的组。在左侧窗格中,选择 Lambdas,然后选择添加 Lambda。
- 选择使用现有 Lambda
- 从您之前的创建的 Lambda 函数中选择一个,然后选择下一步
- 选择您在定义函数时创建的别名。如果未创建别名,则选择发布的版本。提示:我们建议您创建别名,这样在需要发布 Lambda 函数的新版本时无需再次执行这一步。
- 在 AWS Lambda 函数列表中,选择添加的 Lambda 函数,然后选择编辑。 在这一页,您可以启动对设备上资源的访问权限。这可能是串口或 USB 端口,具体取决于协议驱动程序。
- 对于 Lambda 生命周期,选择使此函数长时间生存,保持其无限期运行。对 /sys 目录的只读访问权限,请选择启用
- 为创建的其他 Lambda 函数重复这些步骤
分配订阅
- 打开您之前部署的组。在左侧窗格中,选择订阅
- 选择添加订阅
- 对于订阅下的源,选择 ModbusToAWSIoT。对于目标,选择 IoT 云。不要为该主题设置筛选条件
这将在 AWS IoT 中创建一个订阅,以捕获从 AWS Greengrass 设备上的 Lamdba 函数发布的任何数据。
![](https://d2908q01vomqb2.awsstatic-china.com/f6e1126cedebf23e1463aee73f9df08783640400/2018/07/23/image017-1024x483.png)
部署更改
- 从组配置页面的操作菜单,选择部署。您可以从 AWS IoT 订阅 dt/controller/extruder/plc1/rtd 以查看从 Modbus TCP 转换为 MQTT 的数据
小结
借助 AWS Greengrass,可以从多种不同工业协议的设备上采集数据,即使这些设备使用不同的本地通信协议。本博文中的示例演示了如何与 Modbus TCP 等协议通信,并安全地将数据实时传输到云中。
希望本博文对您有所帮助。欢迎在评论区中留下问题和其他反馈。