CloudWatchでアラーム状態になった場合にM5StickでBeep音と画面発光をしてみた

2022.01.25

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、コンサル部@大阪オフィスのTodaです。

ここ最近M5Stickを利用して便利なモノができないか?といろいろ試しています。
今回は、CloudWatchにてアラーム状態が発生した場合にM5StickでBeep音と画面発光をして誰でもわかるようにしてみました。

やりたいこと

API GatewayとLambdaを利用してCloudWatchのアラーム状態のカウントを取得するようにいたします。
M5Stickから定期的にAPIに通信をおこないカウントが増えている場合に、Beep音を発生します。
Beep音は機器のボタンを押すことで解除できるようにします。

やりたいこと

実際できたもの

M5Stick CPlusとは?

M5StickCPlusはM5Stack社が開発、販売をしているIoTデバイス開発ツールキットです。
別機器でM5StickCがありWi-FiとBluetooth機能が追加された物になります。
プログラムはArduino言語を利用して開発します。
Arduino言語はC/C++をベースにして開発されたプログラミング言語です。

・M5Stack 公式サイト
https://m5stack.com/

・M5Stick CPlus Docs
https://docs.m5stack.com/#/en/core/m5stickc_plus

前提条件

M5Stickの開発環境が既にある状態から開始します。
M5Stickの開発環境は別途Blog記事にしたいと思います。

試してみる

下記の順番で対応してきます。

  • Lambdaの作成
  • API Gatewayの作成・設定
  • M5Stackのプログラム

Lambdaの作成

AWSマネージメントコンソールにログインをおこないサービスメニューから[Lambda]を選択します。
[関数の作成]にて下記情報を設定にて新規作成をおこないます。

  • 関数の作成:一から作成
  • 関数名:任意(checkCloudWatch_alarm)
  • ランタイム:Python3.9
  • アーキテクチャ:x86_64
  • アクセス権限:デフォルト

作成をしたら下記ソースコードを設定してCloudWatchでアラーム状態の数をカウントして結果を返すようにします。
AlarmNamesはカウントするアラームを絞る場合に利用します、全てのアラームを対象にする場合は条件を外します。

import json
import boto3

def lambda_handler(event, context):
    # boto3
    client = boto3.client('cloudwatch')
    response = client.describe_alarms(
        AlarmNames = [ 
            '<アラームを絞る場合、アラーム名を指定>',
            '<例>awsec2-i-9999999999999-LessThanOrEqualToThreshold-CPUUtilization'
        ],
        StateValue='ALARM'
    )
    return {
        'statusCode': 200,
        'body': str(len(response['MetricAlarms']))
    }

Boto3にてCloudWatchにアクセスをする場合、権限が必要になります。
タブから[設定] > [アクセス権限] > [実行ロール]にて現状設定されているロールに「CloudWatchReadOnlyAccess」の権限を追加付与します。

上記設定にて、Lambdaのテストをおこないカウントが取得できるかの確認をおこないます。
アラームは条件を一時的に変更して「アラーム状態」にします。
動作が確認できたら[Lambda関数のARN]をメモに残します。

API Gatewayの作成

サービスメニューから[API Gateway]を選択します。
[APIを作成]を選択してREST APIの[構築]をクリックします。
下記情報を設定にて新規作成をおこないます。

  • プロトコル:REST
  • 新しいAPIの作成:新しいAPI
  • API名:任意(checkCloudWatch_alarm)
  • エンドポイントタイプ:リージョン

APIの作成後、[アクション] > [メソッドの作成] > [GET]を選択してメソッドを1点作成します。
セットアップではLambdaを選択して先ほどメモをしたLambda関数のARNを指定します。

  • 統合タイプ:Lambda関数
  • Lambdaプロキシ統合の使用:有効
  • Lambda関数:[Lambda関数のARN]

作成ができたら[アクション]>[APIのデプロイ]を選択して[デプロイされるステージ]を選択してステージ名を入力します。
今回はtestとしています。

作成後、[URLの呼び出し]に表示されるアドレスにアクセスしてLambdaで取得したアラームのカウントが表示される事を確認します。

API Gatewayのアクセス制限(任意)

利用APIを意図しないユーザに見えないようにアクセスできるIPを制限します。 サービスメニューから[API Gateway]を選択します。
作成したAPIの詳細を開き、右メニューから[リソースポリシー]を選択します。
決まったIPからのみアクセスを許可する場合は下記のようなポリシーがあります。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": "arn:aws:execute-api:<リージョン>:<AWSアカウントID>:<API Gateway ID>/<ステージ名>/*",
            "Condition": {
                "NotIpAddress": {
                    "aws:SourceIp": "<許可IPアドレス>/32"
                }
            }
        },
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": "arn:aws:execute-api:<リージョン>:<AWSアカウントID>:<API Gateway ID>/<ステージ名>/*",
        }
    ]
}

API GatewayのIDは画面上部に表示される文字列を設定します。

作成完了後、[APIのURL]をメモに残します。

M5Stackのプログラム

M5StickCPlusでは本体に内蔵されているWifiとボタン、ブザー、画面を利用します。
Arduino IDEのライブラリ管理にて必要な物を取得します。

  • M5StickCPlus

プログラムの内容はシンプルに1秒毎にループするようにして指定回数を超えるとサーバにアラームの数を取得するようにします。
前回取得したアラーム数よりも数が増加している場合は警告状態に移行します。
警告状態はアラーム数が0に戻る または ボタンを押すことで止められるようにします。

※プログラムご利用時の注意事項
下記プログラムは例外処理等を設定していません。
実務でのご利用はプログラムを調整の上、自己責任にてご利用ください。

#include <M5StickCPlus.h>
#include <WiFi.h>
#include <HTTPClient.h>

const char* ssid = "<WIFIのSSID>";
const char* password = "<WIFIのパスワード>";
const String endpoint = "<APIのURL>";

bool alarm_beep = false;
int alarm_count = 0;
int alarm_count_max = 0;
int check_count = 0;
int check_count_max = 10; // APIへの取得タイミング(10=10秒単位)

void setup() {
  M5.begin();
  M5.Lcd.setRotation(1);
  M5.Lcd.setTextSize(2);
  M5.Lcd.setCursor(0, 15);
  
  // Wifi接続
  M5.Lcd.println("Connecting to ");
  M5.Lcd.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      M5.Lcd.print(".");
  }
  M5.Lcd.println("");
  M5.Lcd.println("WiFi connected");
  M5.Lcd.println("IP address: ");
  M5.Lcd.println(WiFi.localIP());
  delay(2000);
}

void loop() {
  M5.update();

  // M5ボタン判定(アラート解除)
  if ( M5.BtnA.wasPressed() ){
    alarm_beep = !alarm_beep;
  }

  // 画面描写
  if(!alarm_beep){
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setTextSize(2);
    M5.Lcd.setCursor(0, 15);
    M5.Lcd.println("WiFi connected");
    M5.Lcd.println("IP address: ");
    M5.Lcd.println(WiFi.localIP());
    for (int tmp_count = 0; tmp_count < check_count; tmp_count++) {
      M5.Lcd.print(".");
    }
  }

  check_count ++;
  if(check_count_max <= check_count ){
    // アラート数取得
    alarm_count=getAlarm();
  
    // アラート判定 
    if(alarm_count_max < alarm_count){
      alarm_beep = true;
    }
    alarm_count_max = alarm_count;
    check_count = 0;
  }
  
  // アラートが0でBeep時解除
  if(alarm_beep && alarm_count == 0){
    alarm_beep = false;
  }

  // Beep処理
  if(alarm_beep){
    actionBeep();
  }
  delay(1000);
}

// アラーム情報取得
int getAlarm(){
  HTTPClient http;
  http.begin(endpoint);
  int httpCode = http.GET();
  int count = 0; 
  if (httpCode > 0) {
    String payload = http.getString();
    count = payload.toInt();
  }
  return count;
}

// Beep画面点滅処理
void actionBeep(){
  M5.Beep.tone(3300);
  M5.Lcd.fillScreen(TFT_RED);
  delay(100);  
  M5.Beep.mute();
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setCursor(0, 15);
  M5.Lcd.setTextSize(3);
  M5.Lcd.println("AWS Alarm");
}

上記プログラムをM5Stickに書き込むことでCloudWatchを監視して通知が必要な場合にBeep音と画面発光で利用者にお知らせします。

さいごに

今回は1AWSアカウントの監視のみおこなうようにしていますが、M5Stickには画面があるため複数AWSアカウントでの通知も対応ができそうです。
また時間があるときに試してみたいと思います。