[初心者向け] CO2濃度データをRaspberry PiからIoT Coreに送り、閾値を超えた時にEmail通知させる

2022.03.11

どうも、アベシです! 前回のエントリでIoT Coreにセンサーのデータを送ってましたが、それを少し発展させてデータの値が閾値を超えた時にAmazon SNSでEmail通知できるようにしてみました。 今回はMH-Z19BというCO2濃度センサーを使います。 実装する全体の概要は以下の通りです。

  1. MH-Z19Bで測定したCO2濃度のデータをラズパイからAWS IoT Coreに送付
  2. 1.で送ったCO2データをCloudWatchLogsに流すためにルールを作って連携
  3. CloudWatchAlarmにCO2濃度の閾値を設定
  4. CO2濃度が閾値を超え、アラーム状態となった時のアクションとしてAmazon SNSによるE-mail通知を設定

構成のイメージは以下のようになります。

前提

IoT Core側へデータをパブリッシュするための以下の設定が完了している状態を前提としています。もしこれらの設定手順を確認したい方は前回のエントリの内容を参考にしてください。

  1. モノの作成
  2. ポリシーの作成
  3. 証明書にポリシーをアタッチ
  4. 証明書をモノにアタッチ

ハード側の紹介と準備

今回使用したハード関係は以下の通りです。

  1. ラズパイ:Raspberry Pi 3 Model B+
  2. CO2センサー:MH-Z19B[シリアル通信(UART)]


ラズパイとセンサー間の配線接続は以下の通りです。

ラズパイ側GPIO センサー側 備考
4pin(5V) Vin ----
6pin(GND) GND ----
8pin(GPIO14 TXD) RX UARTを使う機器同士の接続はクロス結線により、センサーのRXはラズパイのTXに結線
10pin(GPIO15 RXD) TX UARTを使う機器同士の接続はクロス結線により、センサーのTXはラズパイのRXに結線

結線はこちらの情報を参考に実施しました。

↓センサー裏面にピン名が書いています。

↓ラズパイのGPIOピン配置

<引用:https://pinout.xyz/>

Raspberry Pi側のセットアップ

シリアル通信をするための設定

sudo raspi-configでラズパイの設定画面を開きます。 3番のInterface Optionsを選択

6番のSerial Portを選択
Would you like a login shell to be accessible over serial?
という問い合わせについてはいいえを選択

Would you like the serial port hardware to be enabled? という問い合わせははいを選択します。

これでラズパイでシリアル通信(UART)するための準備は整いました。

CO2センサー用のライブラリのインストール

以下のコマンドでライブラリをインストールします。
pip3 install mh-z19
ターミナルから濃度測定を実行するには以下のようにルート権限をつけてコマンドを実行します。
sudo python3 -m mh_z19
結果↓

$ sudo python -m mh_z19 
{"co2": 996}

IoT CoreにCO2濃度のデータを送ってみる

データ送付用のプログラム入手

ラズパイからIoTCoreにデータを送るためのプログラムとして、AWSが提供しているサンプルプログラムを使います。

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

今回使用するプログラムは~/aws-iot-device-sdk-python/samples/basicPubSub内のbasicPubSub.pyです。

データ送付用のプログラムの修正

mh_z19を実行する場合ルート権限が必要なことから、プログラム内ではsubprocessを使ってルート権限で実行し、計測結果を得る形となります。 コードの修正追加の内容は以下のとおりです。

basicPubSub.py

import subprocess //追加

parser.add_argument("-t", "--topic", action="store", dest="topic", default="co2", help="Targeted topic") //topic名を"co2"に変更


//変更前のコード
while True:
    if args.mode == 'both' or args.mode == 'publish':
        message = {}
        message['message'] = args.message
        message['sequence'] = loopCount
        messageJson = json.dumps(message)
        myAWSIoTMQTTClient.publish(topic, messageJson, 1)
        if args.mode == 'publish':
            print('Published topic %s: %s\n' % (topic, messageJson))
        loopCount += 1
    time.sleep(1)

//変更後のコード
while True:
    if args.mode == 'both' or args.mode == 'publish':
        output = subprocess.check_output(['sudo', 'python3', '-m', 'mh_z19'], text=True) // subprocessで一時的にroot権限でCO2センサの値取得プログラムを実行。text=TrueでJSON文字列で計測結果が得られる
        myAWSIoTMQTTClient.publish(topic, output, 1) //JSON形式で得たデータをパブリッシュする形
        if args.mode == 'publish':
            print('Published topic %s: %s\n' % (topic, output))
        loopCount += 1
    time.sleep(5)

修正後、コードを実行してみます。
ターミナルの実行結果に以下の内容が表示され、データがIoTCoreにパブリッシュされた事が確認できました。

Received a new message: 
b'{"co2": 1022}\n'
from topic: 
co2

IoT Core側でも確認してみます。
以下のように指定したトピックにCO2濃度のデータが送られていることを確認できました。

IoT Coreでルールを作成

IoT Coreに渡ってきたCO2濃度のデータをCloudWatchLogsに渡すルールを作ります。
IoT Coreのルール設定画面を開き、作成ボタンを押してルール作成を開始します。

作成画面に遷移しルールの設定を行います。
まずルールの名前を記入します。

ルールクエリステートメントにCO2データIoTCoreに送る際の送信先トピック名を記載します。
今回の場合co2となります。

次にアクションを設定します。
アクションの追加をクリックし、遷移先でCloudWatchLogsを選択します。


その後、ロググループとロールの設定画面に移り、新しいリソースを作成するをクリックするとCloudWatchのロググループ作成の画面に遷移します。

ロググループの作成

ロググループを作成をクリック

ロググループ名を入力して作成します。

ロールの作成

ルール作成画面に戻り、先程作ったロググループを選択します。
次にロールの作成に入ります。ロールの作成ボタンをクリック

ロール作成用の画面が立ち上がるので、名前を記入してロールの作成ボタンを押します。

すると自動的にロールが作られますので以下の画面で作成したロールを選択し、アクションの追加をクリック


自動で作られたロールは以下の内容になっていて、3つのlog操作に関するアクションが含まれ、リソースには先程作ったロググループが記載されてます。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:DescribeLogStreams",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:ap-northeast-1:***:log-group:CO2_Data_Logs:*"
            ]
        }
    ]
}

作成したアクションが追加されているのを確認したら、ルールの作成を押してルールが完成です。

CloudWatchでのCO2データの受信確認

それでは実際にラズパイからCO2センサーのデータをIoTCoreに送った時にCloudWatchにログとして流れていくか確認してみましょう。
ラズパイでデータ送信コマンドを実行後、先程作ったロググループの画面を開きます。
送られてきたログがログストリームに表示されました。\(^o^)/

送られてきたログの中身は以下のようになっていて、メッセージにラズパイからパブリッシュしたCO2センサーの測定結果が記載されています。

アラーム設定とSNSによるE-mail通知

次にCO2濃度が一定値を超えたらアラームを発火させ、Amazon SNSで通知する設定をしていきます。
これにはCloudWatchAlarmを使います。

メトリクスフィルターの設定

アラームには監視対象のメトリクスを指定する必要がありますので、ログ内のCO2のデータをメトリクスとして使えるように、メトリクスフィルターを設定します。
対象のロググループのメトリクスフィルターのタブを開き、メトリクスフィルターを作成をクリックし、作成していきます。

フィルターパターンの書き方についてはこちらの公式ドキュメントを参考にしました。
今回はco2というキーに対する値は1種類なので{ $.co2 = * }としてワイルドカードで指定します。Nextを押して次に進みます。

メトリクスの割り当ての画面に遷移します。
まずフィルターの名前を設定します。

メトリクスの詳細を以下のように設定します。それぞれの項目の役割は項目毎の説明を見てもらえばわかるかと思います。
メトリクス値は$.に続けてキー名を記載します。

最後に設定のレビュー画面が表示されますので、問題なければメトリクスフィルターを作成をクリックして作成完了。

アラームの設定

すべてのアラームの画面を開き、アラームの作成をクリックします。

メトリクスの選択をクリック

すると先程フィルターを作成したメトリクスの名前空間が出てきますので選択。
注意点:フィルターを作った後にメトリクスを一度も流していない場合、この画面に選択できるメトリクスとして表示されませんのでご注意ください。

設定したメトリクス名が表示されますのでチェックを入れます。
もしこの時点で複数のデータをIoTCoreに送っていればグラフ化されて表示されます。

オプションタブではグラフにラベルをつけたり、グラフの表現を色々いじれます。
設定が完了しましたら、メトリクスの選択をクリックします。

指定したメトリクスの内容が表示されます。

下にスクロールするとアラームの発火条件を指定する部分が表示されますので、指定していきます。
今回CO2濃度が800ppmを超えたらアラームを発火させる条件としますので、以下の通りになります。
設定できましたら次へボタンをクリックし、次のステップに移ります。

Amazon SNSによる通知設定

アラーム発火時のアクションとして、Amazon SNSによるEメール通知を設定をしていきます。
今回新たにSNSのトピックも作っていきます。
アラームのトリガーにアラーム状態を設定。
SNS トピックの選択で新しいトピックの作成にチェックを入れます。
SNSトピック名の記入、通知受け取り用のメアドの記入ができたらトピック作成をクリックして通知方法の設定完了です。

設定した通知用のメアド宛に、設定したメアドをサブスクリプションするかどうかの確認のメールが届きますのでConfirm Subscriptionをクリックして確認完了します。

すると、Subscription confirmed!と表示されて確認完了します。

アラームの設定に戻り次に進みます。
アラームの名前を記載して次に進みます。

最後にプレビューが出てきますので、設定内容に問題がなければ一番下のアラームの作成ボタンを押してアラームの作成を完了します。

アラーム発火トライとSNS通知結果の確認

早速出来上がったアラームを見てみましょう。
ちゃんと800ppmに赤線(閾値)が設定させていますね。
CO2濃度が上がって閾値を超えるとアラーム状態になりました\(^o^)/

通知の方も以下の内容でちゃんと来ました!\(^o^)/\(^o^)/

最後に

今回IoT Coreにルールを作り、CloudWatchとSNSを組み合わせて通知アクションをできる構成を作ってみました。
センサー(ハード)からのデータをAWSで処理してユーザーに対しアクションする一連を体験できました。
AWSを使えばこういったことも割とハードル低く始められるので、改めてAWSの手軽さに凄さを感じました。
他にも色々なリソースと組み合わせて目的に有ったアクションが作れるようなので、色々試したくなりますね。
それでは、また!