AWS IoT Eventsを使って、LambdaやDynamoDBを使わずにハートビート監視をやってみた

AWS IoT Eventsを使って、LambdaやDynamoDBを使わずにハートビート監視をやってみました。 デバイスからの通信が一定時間無いときにデバイス切断と判断してメール送信する仕組みです。
2020.07.29

IoTの仕事に携わっていると、ハートビート監視が話題にあがります。 具体的には、デバイスが定期的に発信する信号をクラウドで監視し、一定期間内に受信できなければ「デバイスとの接続が切れています」的な内容をメールで送信します。

ハートビートの概要図

この仕組を実現するとき、DynamoDBに状態を保存して、一定時間毎に起動するLambdaで確認していたのですが、IoT Eventsを使えば楽にできそうなので試してみました。

おすすめの方

  • IoT Eventsの雰囲気を知りたい方
  • ハートビート監視の仕組みを検討中の方

IoT Eventsとは

下記をご覧ください。

構築するハートビートの要件

ざっくりと下記です。リセットしないウォッチドッグタイマーですね。

  • デバイス
    • 2分毎にハートビート用の信号をクラウドに発信する
  • クラウド
    • デバイスからハートビート用の信号が2回連続で来なかったとき、デバイス切断と判断してメールを送信する
    • デバイス切断状態でハートビート用の信号が届いたとき、デバイス接続が復旧と判断してメールを送信する

ハートビート用のトピックは下記とします。

  • test/<デバイスID>/heartbeat

デバイスが送信するハートビート用のデータは下記とします。タイムスタンプ(ミリ秒)です。

{
    "timestamp": 1595817156346
}

SNSトピックを作成する

デバイス切断時と接続復旧時にIoT Evetnsが発行するSNSトピックを作成します。

  • トピック名: heartbeat-send-mail-topic

SNSトピックを作成

ここでEメールのサブスクリプション設定を行います。

SNSトピックのサブスクリプションでEメールを追加

件名や本文にこだわる場合は、LambdaとSESを経由して送信すると良いですね。

IoT Eventsで探知機モデルを作成する

最初に仕組みを解説

デバイス毎にあらかじめ5分のタイマーをセットしておき、ハートビート用の信号が来たらタイマーを5分にリセットします。 もしハートビート用の信号が来なければ、タイマーがゼロになる5分後にSNSトピックを発行してメールを送信します。 タイマーの5分は4分(2分毎の信号が2回)と1分(バッファ)を足した値です。

探知機モデルの作成

IoT Eventsにアクセスし、探知機モデルの作成を選択します。

IoT Eventsの検知モデルを作成する

テンプレートでも作成できますが、ここでは新しく作成します。

新しい検知モデルを作成する

なにかできました。これを作り込んでいきます。

検知モデルの初期状態

入力の設定

右上の「入力の作成」を選択して設定します。

  • 入力名: TestHeartbeatInputData

次のJSONファイルを作成してアップロードします。これは、デバイスが送信するデータをIoTルールアクションで整形したJSONデータです。

sample_input.json

{
    "deviceId": "d1234",
    "payload": {
        "timestamp": 1595817156346
    }
}

入力データを登録する

入力属性はすべて選択しておきます(すべて使います)。

入力データを登録する(使用するアトリビュート)

online状態の作成

既存のState_1を選択し、状態名をonlineに変更します。

online状態を設定する

OnEnterイベントの設定

OnEnterのイベント追加を選択し、5分間のタイマーを設定します。

  • イベント名: Init_timer
  • 条件: true
  • アクション: タイマーの設定
  • オペレーション: 作成
  • タイマー名: heartbeat_timer
  • 経過時間: 5分

online状態のOnEnterを設定する

online状態のOnEnterを設定する(アクション)

OnInputイベントの設定

OnInputのイベント追加を選択し、ハートビート用のデータを受け取ったときのタイマーをリセットを設定します。

  • イベント名: Reset_timer
  • 条件: $input.TestHeartbeatInputData.payload.timestamp > 0
  • アクション: タイマーの設定
  • オペレーション: リセット
  • タイマー名: heartbeat_timer

online状態のOnInputを設定する

online状態のOnInputを設定する(アクション)

offline状態の作成

「状態」をドラッグして新しい状態を追加し、状態名をofflineに変更します。

offline状態を設定する

OnEnterイベントの設定

OnEnterのイベント追加を選択し、SNSトピックを発行してメール送信する設定を追加します。

  • イベント名: Send_Email_to_offline
  • 条件: true
  • アクション: SNSメッセージの送信
  • SNSトピック: heartbeat-send-mail-topic
  • ペイロード :カスタムペイロード
  • 種類: 文字列
  • カスタムペイロード: 'デバイスの切断を検知しました。対象デバイスID: ${$input.TestHeartbeatInputData.deviceId}'

offline状態のOnEnterを設定する

offline状態のOnEnterを設定する(アクション)

OnExitイベントの設定

OnExitのイベント追加を選択し、SNSトピックを発行してメール送信する設定を追加します。

  • イベント名: Send_Email_to_online
  • 条件: true
  • アクション: SNSメッセージの送信
  • SNSトピック: heartbeat-send-mail-topic
  • ペイロード :カスタムペイロード
  • 種類: 文字列
  • カスタムペイロード: 'デバイスの接続復旧を検知しました。対象デバイスID: ${$input.TestHeartbeatInputData.deviceId}'

offline状態のOnExitを設定する

offline状態のOnExitを設定する(アクション)

状態遷移を定義する

onlineからofflineの遷移

online状態を選択すると表示される三角マークをドラッグしてofflineにつなぎます。

状態遷移を追加する

イベント名とトリガーを下記にします。これによって、5分タイマーがタイムアウトしたとき、offline状態に遷移します。

  • イベント名: to_offline
  • トリガー: timeout("heartbeat_timer")

offlineへの状態遷移を設定する

offlineからonlineへの遷移

同様にofflineからonlineへの遷移を作成します。これによって、TestHeartbeatInputDataのデータ入力があったとき、デバイス復帰と判断してonline状態に遷移します。

  • イベント名: to_online
  • トリガー: currentInput("TestHeartbeatInputData")

onlineへの状態遷移を設定する

探知機モデルを発行する

右上の「発行」ボタンを選択し、モデル名とIAMロール名を入力します。IAMロールはここで同時に新規作成してもらいます。

  • モデル名: SampleHeartbeatModel
  • IAMロール名: iot-events-sample-device-heartbeat-role (※新規作成)
  • 探知器生成メソッド: 一意のキー値ごとに探知器を作成する
  • 探知器作成キー: deviceId
  • ディテクターの評価方法: バッチ評価

IoT Eventsのモデルを発行する

IoT Eventsのモデルを発行する

最後に「保存して発行する」ボタンを押せば完了です!

IoT Eventsのモデルを発行した

ログの有効化

IoT Eventsのログを有効化して、CloudWatch Logsで見れるようにしておきます。

IoT Eventsのログ設定をする

IoTルールアクションの作成

IoT Coreでルールを作成します。

  • ルールの名前: sample_iot_events_heartbeat_rule
  • SQLルール: 下記
  • アクション: IoT Events入力にメッセージを送信する
    • 入力名: TestHeartbeatInputData
    • ロール: sample-iot-events-heartbeat-rule-role (※新規作成)
SELECT
 topic(2) as deviceId
 * as payload
FROM
 'test/+/heartbeat'

IoT Ruleアクションを設定する

動作確認してみる

ここまでで準備は整いました。実デバイスが存在する前提でモノや証明書の作成をしてもよいのですが、そこは省略してIoT Coreのテスト画面を使って、あたかもデバイスが存在するかのように振る舞うことにします。

1台目のデバイスでハートビートを送信する

下記の送信を行います。

  • トピック:test/d1234/heartbeat
  • データ:下記
{
    "timestamp": 1595910356921
}

IoT Coreのテスト画面でデータを送信する

探知機モデルの様子

ディテクターにデバイス(d1234)が追加されました。現在の状態はonlineですね。この状態で5分待ちます。

IoT Eventsのディテクターの様子

5分後にデバイス切断されたメールが届く

メールが届きました!

Eメールの様子

ディテクターの様子もofflineになっています。

IoT Eventsのディテクターの様子

再びハートビートを送信する

IoT Coreのテスト画面で再びハートビートを送信すると、接続復帰メールが来ました。

Eメールの様子

ディテクターの様子もonlineになりました。

IoT Eventsのディテクターの様子

2台目のデバイスでハートビートを送信する

下記の送信を行います。

  • トピック:test/d7777/heartbeat
  • データ:下記
{
    "timestamp": 1595911179367
}

ディテクターが2つに増えました。

IoT Eventsのディテクターの様子

1台目のデバイスのみハートビート送信し続ける

1台目のデバイス(d1234)のみ、ハートビート送信し続けます。5分程待つと、2台目のデバイスの切断メールが届きました。

Eメールの様子

IoT Eventsのディテクターの様子

CloudWatch Logsの様子

次のようなログが出力されていました。

{
    "timestamp": "2020-07-28T04:27:41.441Z",
    "level": "INFO",
    "logMessage": "Initializing state to online",
    "context": "Transition",
    "status": "Success",
    "messageId": "e9f72b9c-e208-4c64-a66c-870e50a3fd29",
    "keyValue": "d1234",
    "detectorModelName": "SampleHeartbeatModel",
    "eventName": "onInitialize"
}
{
    "timestamp": "2020-07-28T04:32:41.535Z",
    "level": "INFO",
    "logMessage": "Changing state from online to offline",
    "context": "Transition",
    "status": "Success",
    "messageId": "ee10a77c-27a3-4197-b086-8a8ddb33ce52",
    "keyValue": "d1234",
    "detectorModelName": "SampleHeartbeatModel",
    "eventName": "to_offline"
}
{
    "timestamp": "2020-07-28T04:32:42.002Z",
    "level": "INFO",
    "logMessage": "SNS message successfully published. Topic ARN: arn:aws:sns:ap-northeast-1:1234567890:heartbeat-send-mail-topic",
    "context": "Action",
    "status": "Success",
    "keyValue": "d1234",
    "detectorModelName": "SampleHeartbeatModel",
    "actionExecutionId": "016be2b6-fe70-364b-8b20-42e1b3b083ce",
    "actionType": "SNSTopicPublish",
    "eventName": "Send_Email_to_offline"
}
{
    "timestamp": "2020-07-28T04:38:13.331Z",
    "level": "INFO",
    "logMessage": "Changing state from offline to online",
    "context": "Transition",
    "status": "Success",
    "messageId": "50236093-c862-4178-bf21-1195123936a0",
    "keyValue": "d1234",
    "detectorModelName": "SampleHeartbeatModel",
    "eventName": "to_online"
}
{
    "timestamp": "2020-07-28T04:38:13.797Z",
    "level": "INFO",
    "logMessage": "SNS message successfully published. Topic ARN: arn:aws:sns:ap-northeast-1:1234567890:heartbeat-send-mail-topic",
    "context": "Action",
    "status": "Success",
    "messageId": "50236093-c862-4178-bf21-1195123936a0",
    "keyValue": "d1234",
    "detectorModelName": "SampleHeartbeatModel",
    "actionExecutionId": "b2bea181-9834-3484-ade6-f828fe3c838a",
    "actionType": "SNSTopicPublish",
    "eventName": "Send_Email_to_online"
}

さいごに

IoT Eventsを使うことによって、DynamoDBの状態管理テーブルや処理を行うLambgaを使わずにハートビート監視を実現しました。 IoTではハートビート監視は切っても切れない関係だと思うので、IoT Eventsを使って楽に実現できそうです。

参考