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

  • 0
    いいね
  • 0
    コメント

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

    crystalsignalpi.gif

    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 を使って音声と連携した使い方も試してみたいと思います。

    この投稿は Serverless(2) Advent Calendar 201621日目の記事です。