在过去的几个星期里,我一直在写关于如何解决我在花园里遇到的问题—— 我经常忘记给我的植物浇水,所以我创建了一个仪表板和通知系统。
如果我不能将水分读数发送到 AWS,我的仪表板和通知系统没有太多的用处。因此,这个项目的下一步是创建 IoT 服务。
正如我在之前的文章中(Slack Webhooks with the Serverless Framework)写道,AWS IoT 服务在这个系统中做了很大的努力。AWS IoT 提供设备认证、通信以及与其他 AWS 服务的集成。为了建立我的花园监控系统,我已经利用了三个 AWS IoT 服务。
Device Gateway(设备网关)是 AWS IoT 服务的核心。设备风头是设备和 AWS 服务之间的消息代理(Message Broker)。设备网关支持三种协议:
MQTT 专为电源和带宽受限的设备而设计。这使得我想要用它,为我的花园建立土壤水分传感器。
设备网关需要对接收到的所有消息进行身份验证。虽然有几个选项来验证消息。 但是,由于我使用 MQTT,我必须使用 X.509 证书验证消息。
设备网关使用相互的 TLS 身份验证来验证客户端证书。当使用 X.509 证书时,设备风头需要使用 TLS 1.2。正如我发现,在为您的项目选择硬件之前,了解这一点很重要。
如果设备网关是 AWS IoT 的核心,那么规则引擎(Rules Engine)是大脑。Rules Engine 对传入的 IoT 消息进行处理、过滤、转换和处理。
每个 IoT 规则都包含一个 SQL 语句。SQL语句支持以下关键字:
规则引擎可以使用每个消息的转换数据来调用操作。我为我的项目使用的动作是:
更多规则相关的操作,可以查看官方文档:AWS IoT Rule Actions。
这些理论已经差不多了。接下来,让我们来看看:如何使用 Serverless Framework 进行设置。
在 Serverless Framework 中,serverless.yml 是 serverless 服务的主要配置文件。serverless.yml 配置文件定义了服务中的功能和资源。资源是使用 YAML 而不是 JSON 编写的 CloudFormation 资源类型。
在这个例子里,我把我的项目的配置文件分散到三个 gists 文件上。
service: garden-aid-iot-hub
provider:
name: aws
runtime: nodejs4.3
# custom variable syntax is needed to avoid conflits with aws cloudformation functions
variableSyntax: '\${{([\s\S]+?)}}'
# load custom variables from a file
custom: ${{file(./vars-${{opt:stage}}.yml)}}
# define the check moisture level function
functions:
checkMoistureLevel:
handler: index.checkMoistureLevel
resources:
Resources:
# DynamoDB - see https://gist.github.com/johncmckim/9bec3ee3ed07722eb1bd4335bcd80910
# IoT - see https://gist.github.com/johncmckim/5d149fb2416f38957c2d0e30f56c6aba
LambdaInvokePermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: { Fn::GetAtt: [ checkMoistureLevel, Arn ] }
Action: lambda:InvokeFunction
Principal: iot.amazonaws.com
要注意的两个关键部分是 variableSyntax
属性和 LambdaInvokePermission
资源。
variableSyntax 属性允许开发人员更改在 serverless.yml 中引用变量的语法。我改变了变量语法,以允许使用 IoT Sql 函数,例如 ${timestamp()}
。
LambdaInvokePermission 资源授予 AWS IoT 服务访问 checkMoistureLevel 功能。不要忘了添加这个,否则你会遇到一个糟糕的时刻。
下一步是定义一个 DynamoDB 表来存储水分数据。我需要存储发送消息的设备、消息时间戳和湿度水平。
MoistureData:
Type: AWS::DynamoDB::Table
DeletionPolicy: Retain
Properties:
TableName: moisture-data-${{opt:stage}}
AttributeDefinitions:
-
AttributeName: ClientId
AttributeType: S
-
AttributeName: Timestamp
AttributeType: S
KeySchema:
-
AttributeName: ClientId
KeyType: HASH
-
AttributeName: Timestamp
KeyType: RANGE
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
我使用设置 ID(ClientId)和时间戳作为此表的键。它允许我在 DynamoDB 中查询来自特定设备的消息。你可以阅读我之前的文章,《GraphQL with the Serverless Framework》来了解如何实现。
最后一步是定义 AWS IoT 服务。以下代码定义了:
SensorThing:
Type: AWS::IoT::Thing
Properties:
AttributePayload:
Attributes:
SensorType: soil
SensorThingPolicy:
Type: AWS::IoT::Policy
Properties:
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: ["iot:Connect"]
Resource: ["${{custom.sensorThingClientResource}}"]
- Effect: "Allow"
Action: ["iot:Publish"]
Resource: ["${{custom.sensorThingSoilTopicResource}}"]
SensorPolicyPrincipalAttachmentCert:
Type: AWS::IoT::PolicyPrincipalAttachment
Properties:
PolicyName: { Ref: SensorThingPolicy }
Principal: ${{custom.iotCertificateArn}}
SensorThingPrincipalAttachmentCert:
Type: "AWS::IoT::ThingPrincipalAttachment"
Properties:
ThingName: { Ref: SensorThing }
Principal: ${{custom.iotCertificateArn}}
IoTRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: Allow
Principal:
Service:
- iot.amazonaws.com
Action:
- sts:AssumeRole
IoTRolePolicies:
Type: AWS::IAM::Policy
Properties:
PolicyName: IoTRole_Policy
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: Allow
Action:
- dynamodb:PutItem
Resource: "*"
-
Effect: Allow
Action:
- lambda:InvokeFunction
Resource: "*"
Roles: [{ Ref: IoTRole }]
# AWS IoT SQL Reference
# http://docs.aws.amazon.com/iot/latest/developerguide/iot-sql-functions.html
SensorThingRule:
Type: AWS::IoT::TopicRule
Properties:
TopicRulePayload:
RuleDisabled: false
Sql: "SELECT Level FROM '${{opt:stage}}/garden/soil/moisture'"
Actions:
-
DynamoDB:
TableName: { Ref: MoistureData }
HashKeyField: "ClientId"
HashKeyValue: "${clientId()}"
RangeKeyField: "Timestamp"
RangeKeyValue: "${timestamp()}"
PayloadField: "Data"
RoleArn: { Fn::GetAtt: [ IoTRole, Arn ] }
-
Lambda:
FunctionArn: { Fn::GetAtt: [ checkMoistureLevel, Arn ] }
正如我之前写的,规则引擎是这个项目的大脑。SensorThingRule 将所有湿度水平存储在 DynamoDB 中。它提供了水分仪表板的数据。SensorThingRule 还调用每个消息的 Lambda 函数。它将在潮湿水平太低时通知系统提醒我。
那就是云服务的内容。最后一步是查看设备。
这是我必须作出供认的地方。 我没有能够完成这个项目,因为我有硬件问题。
NodeMCU 是一个用于物联网项目的固件和开发板。我为这个项目购买了一个基于 NodeMCU 的开发板。
NodeMCU 固件允许您使用 Lua 编程语言来控制您的设备。固件包括用于联网和传感器的标准库。
开发板是基于 ESP8266 的板,具有大量用于传感器的 GPIO 引脚。该板大约 $8 美元,从中国交付。该开发板是一个很好的选择,但他们有一个致命的问题。
NodeMCU 不支持 TLS 1.2。没有 TLS 1.2 支持,我无法从 NodeMCU 发送消息到 AWS。我可以使用替代固件,esp-open-rtos。但是,我还没有尝试过。
虽然我无法创建硬件设备,但我已经能够测试 IoT 服务。我创建了一个小型的 Node.js 程序,来连接到 AWS IoT 设备网关并发布假的值。
const awsIot = require('aws-iot-device-sdk');
const interval = 1000 * 60 * 1; // one minute
const device = awsIot.device({
keyPath: './path/to/your/private.pem.key',
certPath: './path/to/your/certificate.pem.crt',
caPath: './path/to/your/verisign-ca.pem',
clientId: 'garden-aid-client-test-js',
region: 'ap-southeast-2'
});
device.on('connect', function() {
console.log('connect');
setInterval(function() {
const level = 1.1; // or generate a random level
const options = {};
const message = JSON.stringify({
Level: level
});
device.publish(topic, message, options, function(err, result) {
console.log('Err: ', err);
console.log('Result: ', result);
});
}, interval);
});
这使我能够继续开发系统,而无需使用工作的硬件设备。
我决心完成这个项目。 我最近购买了我希望用来完成这个项目的新硬件。 当我有一个工作的解决方案时,我将详细介绍这些设备。
我希望你喜欢阅读这个项目。 如果您喜欢这篇文章,请关注我在 Medium 或 Twitter 上获得更多精彩内容。
原文链接:https://serverless.zone/iot-with-the-serverless-framework-e228fae87be
观光\评论区