AWS IoT - インスタンスの起動状態をRaspberry Piに繋げたLEDで監視しよう

Amazon Echoが国内販売される前、Alexa用として使っていたラズパイにほこりが積もってきたので久々に使ってみようと思います。

今回はAWS IoTを使用し、EC2インスタンスが起動中であればラズパイに繋げた緑色のLED、
停止中の場合は赤色のLEDを光らせる仕組みを一から作りましょう。

目次

概要
モノの準備
AWS IoTの設定
Thing Shadowの設定
Lambda関数の作成
Cloudwatch Eventsの追加
IoT連携用スクリプトの作成

 

概要

大まかな流れは……

・CloudWatch EventsがEC2の状態が変更されたらLambdaをトリガー
・LambdaがIoT Shadowを更新
・Thing(モノ、今回はRaspberry Piの事)がIoT ShadowよりStateを読み込み、LEDを制御
Raspberry PiのStateを更新

を目指してみます。

AWS IoT

 

それではまずラズパイの準備から始めましょう。

 

モノの準備

今回はLEDを使った仕組みとなりますので以下を追加で購入しました。

・ブレッドボード
・ジャンプワイヤー(オスメス)
・LED(赤、緑)
・抵抗(330Ω)

さっそく電子回路を繋いでみます。

 

ラズパイの準備

 

まずはラズパイからLEDを制御出来るかテストしてみましょう。
GPIOを操作する為のRPi.GPIOパッケージをインストールします。

sudo pip install python-rpi.gpio

インストールしましたら適当にスクリプトを組んで動作確認を行います。

xmaslight.py

 

import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD)
GPIO.setup(13, GPIO.OUT)
GPIO.setup(11, GPIO.OUT)
try:
while True:
GPIO.output(13, False)
GPIO.output(11, True)
time.sleep(2)
GPIO.output(13, True)
GPIO.output(11, False)
time.sleep(2)

except KeyboardInterrupt:
GPIO.cleanup()

上の図の通り今回は11番、13番のピンを使用しているのでそちらの制御を行っています。

では実行しましょう。

sudo python xmaslight.py

2秒ごとにLEDが交互に光ったら成功です。

 

AWS IoTの設定

次はAWS IoTにラズパイをThingとして登録しましょう。
AWSコンソールよりIoT Coreのページに移動し、「管理」より「モノの登録」をクリックします。

 

AWS IoTにラズパイをThingとして登録

 

ラズパイ一つだけ登録するので「単一のモノを作成する」をクリックします。

 

AWS IoT

 

名前を入力して次へ。

 

AWS IoT 名前を入力

 

1-Click証明書作成より「証明書の作成」をクリックし、証明書を作成し、「有効化」をクリックしましょう。

証明書はデバイスAWS IoTに認証されているかを確認してくれますが、
ポリシーをアタッチしないとAWS上のリソースにアクセス出来ませんので、
ポリシーを先に作成します。

IoTコンソールに戻り、「安全性」の「ポリシー」より「作成」をクリックします。

 

IoTコンソール

 

任意の名前を入力し、アクションに「 iot:」、
リソースARNに「
」 を入力し、
許可にチェックを入れ「作成」しましょう。

 

IoTコンソール ポリシーの作成

 

では作成したポリシーを証明書にアタッチしましょう。
「安全性」の「証明書」より先程作成した証明書を開き、
「アクション」よりポリシーをアタッチします。

 

IoTコンソール ポリシーをアタッチ

 

基本的な設定は以上です。後は証明書をモノに導入して接続テストを行います。

IoTコンソールより追加したモノのページに移動し、操作タブの「デバイスの接続」をクリックします。

 

IoTコンソール 追加

 

「今すぐ始める」をクリックし「Linux/OSX」→「Python]と選択し、Linux/OSX用の接続キットをダウンロードします。

この接続キットに先程作成した証明書、IoT用のPython SDK、後は接続テスト用のスクリプトが入っています。

 

AWS IoT 接続キット

 

ダウンロードしたキットをモノで展開し、手順通りにstart.shを実行しましょう。

Hello World がIoTコンソール側に表示されましたら接続テスト成功です。

 

IoTコンソール 接続テスト

 

では本番のインスタンス監視の設定を行いましょう。

 

Thing Shadowの設定

Thing Shadowはモノの状態をjsonで管理する機能です。
このシャドウステータスは二つ状態の項目があり、desiredはモノの望まれる状態、
reportedはモノの実際の状態となっています。
この二つの項目に差異がある場合deltaとして差異が表示されます。

IoTコンソールのモノの管理画面へ移動し、シャドウタブのシャドウステータスに以下を記載しましょう

 

{
 "desired": {
 "running": 0
 },
 "reported": {
 "running": 0
 }
}

 

今回管理するステータスはrunningになります。
runningが1の場合、インスタンスのステータスがrunning、
0の場合はstoppedとします。

 

Lambda関数の作成

上記シャドウステータスはLambdaより変更しますのでLambda関数を作りましょう。
Python3.6で関数を作成、ロールにはAWSIoTDataAccessを追加し、以下のコードを追加します。

 

import boto3
import json

def lambda_handler(event, context):
    
    stateNow = event['detail']['state']
    if stateNow == "running":
        state=1
    else:
        state=0
        
    client = boto3.client('iot-data')
    response = client.update_thing_shadow(
        thingName='xp_rasp_pi',
        payload=json.dumps({"state":{"desired": {"running": state}}})
    )

 

thingNameはIoTに登録したモノの名前を入力しますので、環境に応じて変更しましょう。
こちらの関数はCloudwatch Eventsよりトリガーされ、渡されたインスタンス状態に応じて
シャドウステータスを変更いたします。

 

Cloudwatch Eventsの追加

インスタンスの状態の監視はCloudwatch Eventsより行いますので以下の様に新しくルールを作成をしましょう。

 

サービス名:EC2
イベントタイプ:EC2 Instance State-change Notification
特定の状態:running,stopped
Specifi instance id(s):【監視対象のインスタンスID】
ターゲット:Lambda 関数
機能:【先程作成したLambda関数】

 

Cloudwatch Eventsの追加

 

AWS側の設定が完了したので最後にモノ側でスクリプトを作成します。

接続テストを行ったディレクトリに以下のスクリプトを追加します。

runstatus.py

from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTShadowClient

import RPi.GPIO as GPIO
import json
import time
import logging

GPIO.setmode(GPIO.BOARD)
GPIO.setup(11, GPIO.OUT)
GPIO.setup(13, GPIO.OUT)

#Bot = None

def updateState(newState):
    report_json = '{"state":{"reported":'+ newState+ '}}'
    print "updating...\n"
    print report_json
    deviceShadowHandler.shadowUpdate(report_json, None, 5)

def toggle_led(payload, response, token):
    payloadDict = json.loads(payload)
    print("~~~~~~~~~~~~~~~~~~~~~~~")
    print("state changed")
    print("~~~~~~~~~~~~~~~~~~~~~~~\n\n")

    if int(payloadDict["state"]["running"]) == 1:
        print("Running!")
        GPIO.output(11, True)
        GPIO.output(13, False)
    else:
        print("Stopped!")
        GPIO.output(11, False)
        GPIO.output(13, True)

    newState = json.dumps(payloadDict["state"])
    updateState(newState)

host = "************.iot.ap-northeast-1.amazonaws.com"
port = 8883
clientId = "*******"
thingName = "*******"
rootCAPath = "*******"
certificatePath = "*******"
privateKeyPath = "*******"


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(clientId)
myAWSIoTMQTTShadowClient.configureEndpoint(host, 8883)
myAWSIoTMQTTShadowClient.configureCredentials(rootCAPath, privateKeyPath, certificatePath)

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

# Connect to AWS IoT
myAWSIoTMQTTShadowClient.connect()

deviceShadowHandler= myAWSIoTMQTTShadowClient.createShadowHandlerWithName(thingName, True)

deviceShadowHandler.shadowRegisterDeltaCallback(toggle_led)

while True:
    pass

変更点は以下のMQTTクライアントの設定です。

host = モノの操作タブに記載されているエンドポイント
port = 8883
clientId = モノ名前
thingName = モノ名前
rootCAPath, certificatePath, privateKeyPath = 展開されている各種証明書ののファイル名

後は別GPIOピンを使用している場合そちらも変更しましょう。

スクリプトの内容としてはshadowRegisterDeltaCallbackより
シャドウステータスのdesiredとreportedに差異がある場合は、
モノの状態を変更(LEDの切り替え)し、
シャドウステータスをアップデートする処理となっております。

後はスクリプトを実行して正常に動作するか確認しましょう。

 

インスタンスの起動状態をRaspberry Piに繋げたLEDで監視

 

 

 

AWS相談会