Edited at

AWS IoT の Thing Shadow と AWS Lambda の Schedule Event で Crystal Signal Pi に通知する

More than 1 year has passed since last update.

Raspberry Pi で作る LED 警告灯ソリューション、Crystal Signal Pi のプリント基板キットが届いたので早速組み立て、AWS IoT の Thing Shadow と AWS Lambda の Schedule Event で通知するところまでやってみました。

Schedule Event で Lambda 関数が実行される毎に点灯の仕方を変えています。


Crystal Signal Pi とは

詳しくは Crystal Signal Pi 公式サイトを参照ください。


事前準備

本記事では主に AWS CLI を使ってリソースを作成していきます。


AWS CLI をアップデート

$ sudo pip install --upgrade awscli


バージョン確認

$ aws --version

aws-cli/1.11.32 Python/2.7.12 Darwin/15.6.0 botocore/1.4.89


AWS IoT 側の準備


Thing を作成

$ aws iot create-thing --thing-name crystalSignalPi


Shadow を登録

$ aws iot-data update-thing-shadow --thing-name crystalSignalPi  \

--payload '{"state": {"desired" : {"blinkColor" : "green"}}}' \
outfile.json


AWS Lambda 側の準備

今回の AWS Lambda 関数作成に必要なものから作成していきます。


assume ロールの決定

$ cat << EOF > lambdaExecution.json

{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF


IAM ロールの作成

$ aws iam create-role \

--role-name lambdaExecution \
--assume-role-policy-document file://lambdaExecution.json


ポリシーの適用

$ aws iam attach-role-policy \

--role-name lambdaExecution \
--policy-arn arn:aws:iam::aws:policy/AWSIoTDataAccess


Lambda 関数

$ cat << EOF > changeBlinkColor.js

console.log('Loading function');
var aws = require('aws-sdk');

var endpoint = 'XXXXXXXXXXXXX.iot.ap-northeast-1.amazonaws.com';
var thingName = 'crystalSignalPi';

exports.handler = function(event, context) {
var iotdata = new aws.IotData( { endpoint: endpoint } );
var params = { thingName: thingName };
iotdata.getThingShadow(params, function (err, data) {
if (!err) {
var payload = JSON.parse(data.payload);
var currentBlinkColor = payload.state.desired.blinkColor;
console.log("Current Color : " + currentBlinkColor);

var desiredBlinkColor;
if(currentBlinkColor == 'red') {
desiredBlinkColor = 'green';
} else {
desiredBlinkColor = 'red';
}
var desiredState = {
state: {
desired: {
blinkColor: desiredBlinkColor
},
reported: {
blinkColor: currentBlinkColor
}
}
};

var params = {
thingName: thingName,
payload: JSON.stringify(desiredState)
};
iotdata.updateThingShadow(params, function (err, data) {
if (!err) {
context.succeed();
} else {
context.fail(err);
}
});
} else {
context.fail(err);
}
});
};
EOF

$ zip changeBlinkColor.zip changeBlinkColor.js


Lambda 関数の作成

$ aws lambda create-function \

--function-name changeBlinkColor \
--zip-file fileb:///path/to/changeBlinkColor.js.zip \
--runtime nodejs4.3 \
--role arn:aws:iam::XXXXXXXXXXXX:role/lambdaExecution \
--handler changeBlinkColor.handler


Schedule 作成

$ aws lambda add-permission \

--function-name changeBlinkColor \
--statement-id "changeBlinkColor00" \
--action 'lambda:InvokeFunction' \
--principal events.amazonaws.com \
--source-arn arn:aws:events:ap-northeast-1:XXXXXXXXXXXX:rule/changeColorBatch


Event のルールを設定

$ aws events put-rule \

--name "changeColorBatch" \
--schedule-expression "cron(*/5 * * * ? *)" \
--state ENABLED

$ aws events put-targets \

--rule "changeColorBatch" \
--targets Arn=arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:changeBlinkColor,Id=XXXXX


Crystal Signal Pi を組み立てる

公式サイトのマニュアルを見ながら Crystal Signal Pi を組み立て、設定していきます。


Raspberry Pi と AWS IoT の連携


pip のインストール

$ sudo apt-get install python-pip


AWS IoT Device SDK for Pythonのインストール

$ sudo pip install AWSIoTPythonSDK


git のインストール

$ sudo apt-get install git


サンプルコードのダウンロード

$ git clone https://github.com/aws/aws-iot-device-sdk-python.git

$ cd aws-iot-device-sdk-python
$ sudo python setup.py install


IAM ユーザーの作成

デバイス用に IAM ユーザーを作成、Managed policy AWSIoTFullAccess をアタッチ


アクセスキーの設定

クレデンシャル情報を設定


サンプルコードをコピー

$ mkdir -p crystal-signal-pi

$ cd crystal-signal-pi
$ cp ../samples/basicShadow/basicShadowDeltaListener.py ./shadowDeltaListener.py


証明書の準備、配置

コピーしたサンプルコードと同じディレクトリに配置


コードを編集


shadowDeltaListener.py

# coding: utf-8

from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTShadowClient
import sys
import logging
import time
import json
import getopt
import pycurl

# Shadow JSON schema:
#
# Name: Bot
# {
# "state": {
# "desired":{
# "property":<INT VALUE>
# }
# }
#}

# Custom Shadow callback
def customShadowCallback_Delta(payload, responseStatus, token):
# payload is a JSON string ready to be parsed using json.loads(...)
# in both Py2.x and Py3.x
print(responseStatus)
payloadDict = json.loads(payload)
print("++++++++DELTA++++++++++")
property = "blinkColor"
blinkColor = str(payloadDict["state"][property])
print("property: " + blinkColor)
#print("version: " + str(payloadDict["version"]))
print("+++++++++++++++++++++++\n\n")
if blinkColor == "red":
url = "http://localhost/ctrl/?color=100,0,0&mode=1&repeat=0&period=250&json=1"
else:
url = "http://localhost/ctrl/?color=0,80,0&mode=1&repeat=0&period=1000&json=1"

curl = pycurl.Curl()
curl.setopt(pycurl.URL, url)
curl.setopt(pycurl.CUSTOMREQUEST, 'GET')
curl.perform()

# Read in command-line parameters
useWebsocket = True
host = "XXXXXXXXXXXXX.iot.ap-northeast-1.amazonaws.com"
rootCAPath = "rootCA.pem"
certificatePath = "XXXXXXXXXX-certificate.pem.crt"
privateKeyPath = "XXXXXXXXXX-private.pem.key"
thingName = "crystalSignalPi"

# Configure logging
logger = logging.getLogger("AWSIoTPythonSDK.core")
logger.setLevel(logging.DEBUG)
streamHandler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
streamHandler.setFormatter(formatter)
logger.addHandler(streamHandler)

# Init AWSIoTMQTTShadowClient
myAWSIoTMQTTShadowClient = None
myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient("basicShadowDeltaListener", useWebsocket=True)
myAWSIoTMQTTShadowClient.configureEndpoint(host, 443)
myAWSIoTMQTTShadowClient.configureCredentials(rootCAPath)

# AWSIoTMQTTShadowClient configuration
myAWSIoTMQTTShadowClient.configureAutoReconnectBackoffTime(1, 32, 20)
myAWSIoTMQTTShadowClient.configureConnectDisconnectTimeout(10) # 10 sec
myAWSIoTMQTTShadowClient.configureMQTTOperationTimeout(5) # 5 sec

# Connect to AWS IoT
myAWSIoTMQTTShadowClient.connect()

# Create a deviceShadow with persistent subscription
Bot = myAWSIoTMQTTShadowClient.createShadowHandlerWithName(thingName, True)

# Listen on deltas
Bot.shadowRegisterDeltaCallback(customShadowCallback_Delta)

# Loop forever
while True:
pass



python-pycurl のインストール

$ sudo apt-get install python-pycurl


コードを実行

$ python shadowDeltaListener.py


おわりに

今回はまず、AWS IoT との連携までやってみました。

次回は光での通知だけでなく、Polly を使って音声と連携した使い方も試してみたいと思います。