HTTP連携できるサービスからのデータをAPI Gatewayで受けてDynamoDBに書き込む

はじめに

本記事の内容を利用して、LoRaWAN™ 無料ハンズオンセミナー「活用編」でAWS環境の構築からデータ可視化までのハンズオンを実施しました。
https://iot.kyoto/2018/12/17/5265/

本記事では、LoRaWANのネットワークサーバであるThingParkからの連携を例に、HTTP連携できるサービスからのデータをAWS環境に蓄積し可視化する手順を記載しております。

また、対象のセンサデータは、OMRON様の環境センサ「2JCIE-BL」を対象にしております。
https://www.omron.co.jp/ecb/product-detail?partId=73062

前提条件

  • 有効なAWSのアカウントをお持ちであること
  • AWSのコンソールでの操作が難なく出来ること
  • 受信可能なメールアドレスをお持ちであること

目次

STEP0. AWSマネジメントコンソールにログインしよう
STEP1. Amazon DynamoDBのテーブルを作成しよう
STEP2. AWS Lambdaの関数を作成しよう
STEP3. Amazon API Gatewayを作成しよう
STEP4. IoT.kyoto VISでセンサーデータを可視化しよう
STEP5. 後片付け

STEP0. AWSマネジメントコンソールにログインしよう

  • リージョンを「アジアパシフィック(東京)」に変更する

  • 言語が英語の場合は、画面左下の言語変更から日本語に変更する

STEP1. Amazon DynamoDBのテーブルを作成しよう

DynamoDBテーブルの作成

では、実際にDynamoDBテーブルを作成していきましょう。

  • マネジメントコンソールの「サービスの検索」欄に、「dynamo」と入力し、「DynamoDB」をクリックする

  • 「テーブルの作成」をクリック

  • 「テーブル名」「パーティションキー」「ソートキー」に下記値を入力
    ※「ソートキーの追加」をチェックすることで、ソートキーの入力項目が表示されます

テーブル名: lorawan-handson-environmental-sensor
パーティションキー: deviceId
ソートキー: timeStamp

  • 「テーブル設定」の「デフォルト設定の使用」のチェックを外す

  • 「Auto Scaling」の「読み込みキャパシティー」、「書き込みキャパシティー」のチェックを両方外す
  • 右下の「作成」をクリックする

  • しばらくすると、「lorawan-handson-environmental-sensor」という名前のテーブルが作成される

STEP2. AWS Lambdaの関数を作成しよう

Lambda関数の作成

では、実際にLambdaの関数を作成していきましょう。
今回作成するLambda関数は、受け取ったセンサーデータ(16進数)をアスキー文字列に変換し、DynamoDBに保存する処理を行います。

  • マネジメントコンソールの「サービスの検索」欄に、「lambda」と入力し、「Lambda」をクリックする

  • 左の項目の一覧から「ダッシュボード」をクリックする

  • 「関数の作成」をクリックする

  • 「一から作成」が選択されていることを確認し、名前を入力、ランタイムを選択する

名前: lorawan-handson-post-to-dynamodb
ランタイム: Node.js 8.10

  • 「ロール」の「カスタムロールの作成」をクリックする

  • 新しい画面が開くので、IAMロール、ロール名を設定し、「ポリシードキュメントを表示」をクリックする

IAMロール: 新しいIAMロールの作成
ロール名: lorawan_handson_lambda_execution

  • 新しく「編集」リンクが表示されるので、クリックする

  • 「ポリシーの編集」というポップアップが表示されるので、「OK」をクリックする

  • ポリシーの編集が可能になるので、以下の内容をコピー&ペーストする(上書きする)
    以下のポリシーでは、Lmabdaのログの書き込み権限とDynamoDBの2つのテーブルへの書き込み権限を付与しています。
    Lambdaのログは、CloudWatchと呼ばれるAWSのモニタリングサービスに格納されます。

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Action": [
"dynamodb:PutItem"
],
"Effect": "Allow",
"Resource": [
"arn:aws:dynamodb:ap-northeast-1:*:table/lorawan-handson-environmental-sensor"
]
}
]
}
  • 右下の「許可」をクリックする

  • 自動的にLambdaの作成画面に戻るので、「既存のロール」に今回作成したロールが選択されていることを確認して、右下の「関数の作成」をクリックする

  • 下記の画面に自動的に遷移する

  • 下にスクロールし、「関数コード」という項目の「index.js」内に、下記コードをコピー&ペーストして、右上の「保存」をクリックする
    ソースコードの内容はコメントを参照下さい

"use strict";

const AWS = require("aws-sdk");
const docClient = new AWS.DynamoDB.DocumentClient();

exports.handler = async (event, context, callback) => {
const ENVIRONMENTAL_SENSOR_TABLE_NAME =
"lorawan-handson-environmental-sensor";

try {
console.log(event["body"]);
if (event["body"] == null) {
throw new Error("Bodyが設定されていません");
}

const postBody = JSON.parse(event["body"]);
const time = postBody["DevEUI_uplink"]["Time"];
const fPort = postBody["DevEUI_uplink"]["FPort"];
const payloadHex = postBody["DevEUI_uplink"]["payload_hex"];
let dynamoPutItems = {};
let tableName = "";

// UTC時間を日本時間(JST)に修正===========================
const timeUtcDate = new Date(time);
const timeJst = timeUtcDate.getTime() + 1000 * 60 * 60 * 9;
const timeJstDate = new Date(timeJst);

dynamoPutItems["timeStamp"] =
timeJstDate.getUTCFullYear() +
"-" +
shapingNumber(timeJstDate.getUTCMonth() + 1) +
"-" +
shapingNumber(timeJstDate.getUTCDate()) +
" " +
shapingNumber(timeJstDate.getUTCHours()) +
":" +
shapingNumber(timeJstDate.getUTCMinutes()) +
":" +
shapingNumber(timeJstDate.getUTCSeconds());
// ここまで====================================

    // 16進数データをアスキー文字列に変換する======================
    let decimalArray = [];
    for (let i = 0; i < payloadHex.length / 2; i++) {
      const hex = payloadHex.slice(i * 2, i * 2 + 2);
      const decimal = parseInt(hex, 16);
      decimalArray.push(decimal);
    }
    const asciiString = String.fromCharCode(...decimalArray);
    const asciiStringArray = asciiString.split(",");

    if (fPort === 10) {
      // if: オムロン環境センサーの場合

      tableName = ENVIRONMENTAL_SENSOR_TABLE_NAME;
      dynamoPutItems["deviceId"] = asciiStringArray[0];
      dynamoPutItems["temperature"] = asciiStringArray[1];
      dynamoPutItems["humidity"] = asciiStringArray[2];
      dynamoPutItems["illuminance"] = asciiStringArray[3];
      dynamoPutItems["ultraviolet"] = asciiStringArray[4];
      dynamoPutItems["atmosphericPressure"] = asciiStringArray[5];
      dynamoPutItems["noise"] = asciiStringArray[6];
      dynamoPutItems["discomfortIndex"] = asciiStringArray[7];
      dynamoPutItems["heatStrokeIndex"] = asciiStringArray[8];
      dynamoPutItems["battery"] = asciiStringArray[9];
    } else {
      throw new Error("FPortの値が想定の値とは異なります");
    }
    // ここまで====================================

    // DynamoDBにデータを追加===========================
    const dynamoQueryParams = {
      TableName: tableName,
      Item: dynamoPutItems
    };
    await docClient.put(dynamoQueryParams).promise();
    // ここまで====================================

    return callback(null, {
      statusCode: 200,
      body: JSON.stringify({ message: "SUCCEEDED!", res: "" })
    });
  } catch (err) {
    console.error("[ERROR] " + err);

    throw callback(null, {
      statusCode: 400,
      body: JSON.stringify({ message: "ERROR!", error: err })
    });
  }
};

/**
 * 入力された数値が一桁の場合は、先頭に0をつけた文字列の数値を返す関数
 *
 * @param {number} dateNumber
 * @return {string|null}
 */
function shapingNumber(dateNumber) {
  const dateStr = String(dateNumber);
  if (dateStr.length === 1) {
    return "0" + dateStr;
  } else if (dateStr.length === 2) {
    return dateStr;
  } else {
    return null;
  }
}

チェックポイント1: LambdaからDynamoDBのテーブルにデータを追加する

作成したLambdaからテストデータを使用して、DynamoDBのテーブルにデータを追加してみましょう。

  • 右上の「テストイベントの選択」をクリックし、「テストイベントの設定」をクリックする

  • 「テストイベントの設定」というポップアップが表示されるので、下記を設定する

イベント名: EnvironmentalSensor

{
  "body": "{\"DevEUI_uplink\": {\"Time\": \"2018-12-17T06:49:24.174+01:00\",\"FPort\": 10,\"payload_hex\": \"6631363364646566633437332c32392e30392c33312e37322c3137392c302e30322c313030362e342c33342e36342c37362e39362c32332e39332c32393734\"}}"
}

  • 右下の「作成」をクリックする

  • 「EnvironmentalSensor」が選択されていることを確認し、右上の「テスト」をクリックする

  • 関数の実行結果が表示されるので、ログの中の表示が「”statusCode”: 200」になっていることを確認する

  • DynamoDBの画面に遷移し、「テーブル」>「lorawan-handson-environmental-sensor」をチェック>「項目」を選択すると、データが1件書き込まれていることを確認する

テーブルに保存予定のデータ

今回は環境センサーのデータを保存します。
保存時の属性名について説明します。

lorawan-handson-environmental-sensorテーブル

属性名 説明
deviceId デバイスID
timeStamp データ取得時刻
atmosphericPressure 気圧
battery バッテリー
discomfortIndex 不快指数
heatStrokeIndex 熱中症指数
temperature 温度
humidity 湿度
illuminance 照度
noise 騒音
ultraviolet 紫外線

STEP3. Amazon API Gatewayを作成しよう

APIの作成

では、実際にAPIを作成していきましょう。

  • マネジメントコンソールの「サービスの検索」欄に、「api」と入力し、「API Gateway」をクリックする

  • 「今すぐ始める」をクリックする

  • 「サンプルAPIの作成」というポップアップが出た場合は、「OK」をクリックする

  • 項目に必要事項を記入して、「APIの作成」をクリックする

Choose the protocol: REST
新しいAPIの作成: 新しいAPI
名前と説明====
API名: lorawan-handson
説明: LoRaWANハンズオン用API
エンドポイントタイプ: リージョン

  • 自動的に以下のような画面に切り変わるので、「アクション」をクリックする

  • 「リソースの作成」をクリックする

  • 「リソース名」、「リソースパス」を入力し、「リソースの作成」をクリックする

リソース名: Post Sensor Data
リソースパス: post-sensor-data

  • post-sensor-dataリソースが選択されていることを確認し、「アクション」>「メソッドの作成」をクリック

  • 「POST」を選択し、チェックマークをクリックする

  • 必要項目を設定し、「保存」をクリックする

統合タイプ: Lambda関数
Lambdaプロキシ統合の使用: チェックする
Lambdaリージョン: ap-northeast-1
Lambda関数: lorawan-handson-post-to-dynamodb
デフォルトタイムアウトの使用: チェックする

  • 「Lambda関数に権限を追加する」というポップアップが表示されるので、「OK」をクリックする

  • 自動的に以下のような画面が表示される

  • 「アクション」>「APIのデプロイ」をクリックする

  • 「APIのデプロイ」という画面がポップアップするので、必要な項目を入力する

デプロイされるステージ: [新しいステージ]
ステージ名: dev
ステージの説明: LoRaWANハンズオン用

  • 自動的に下記のような画面が表示される
    「https://…」から始まるURLがインターネットからデータを送信する送信先エンドポイントとなる

  • 「ステージ」の「dev」の左の三角をクリックする

  • 「/post-sensor-data」の下の「POST」をクリックし、右側に表示される「URLの呼び出し」のURLをメモする

チェックポイント2: 作成したAPIに対してテストデータをPOSTする

  • 左側の項目一覧から「リソース」>「POST」>「テスト」をクリックする

  • 「/post-sensor-data – POST – メソッドテスト」という画面が開くので、「リクエスト本文」に以下の内容をコピー&ペーストする
{"DevEUI_uplink": {"Time": "2018-12-18T06:49:24.174+01:00","FPort": 10,"payload_hex": "6631363364646566633437332c32392e30392c33312e37322c3137392c302e30322c313030362e342c33342e36342c37362e39362c32332e39332c32393734"}}

  • 右下の「テスト」をクリックする

  • 右側に「ステータス200」と表示されていることを確認する

  • DynamoDBのテーブルのレコードが2件になっていることを確認する

データ連携側のサービス(ThingPark)のデータ送信先の設定

  • データ連携側サービスの送信先に、上記でメモしたURLを設定する

DynamoDBテーブルを確認する

送信したセンサーデータがDynamoDBのテーブルに格納されていることを確認します。

  • マネジメントコンソールの「サービスの検索」欄に、「dynamo」と入力し、「DynamoDB」をクリックする

  • 左の項目一覧から、「テーブル」>「lorawan-handson-environmental-sensor」>「項目」をクリックする

  • DynamoDBのテーブルにセンサーデータが蓄積されていることを確認する

STEP4. IoT.kyoto VISでセンサーデータを可視化しよう

今回のハンズオンでは、センサーデータ可視化のために、IoT.kyoto VISを利用します。
可視化部分の実装についてはハンズオンに含まれておりませんのでご了承ください

IAMユーザの作成

IoT.kyoto VISでセンサーデータを表示するために、DynamoDBの読み取り権限が付与されたIAMユーザの認証情報(アクセスキー+シークレットアクセスキー)が必要です。

  • マネジメントコンソールの「サービスの検索」欄に、「iam」と入力し、「IAM」をクリックする

  • 左側の項目一覧から「ユーザー」をクリックする

  • 「ユーザーの追加」をクリックする

  • 下記項目を設定し、「次のステップ:アクセス権限」をクリックする

ユーザー名: dynamo-readonly-lorawan-handson
アクセスの種類: プログラムによるアクセス

  • 「既存のポリシーを直接アタッチ」>「AmazonDynamoDBReadOnlyAccess」をチェックし、「次のステップ」をクリックする
    ※文字が省略されて全文が見えない場合は、三角をクリックすると詳細が表示される

  • タグは追加せず、「次のステップ:確認」をクリックする

  • 内容に間違いがなければ、「ユーザーの作成」をクリックする

  • 「.csvのダウンロード」をクリックし、「閉じる」をクリックする
    認証情報は厳重な取り扱いをお願いします。

IoT.kyoto VISでのセンサーデータの可視化

  • 下記リンクをクリックし、アカウントを新規作成する。登録後メールでの認証を行なってください。
    IoT.kyoto VISサインアップ

  • 下記リンクから登録したメールアドレス、パスワードを入力し、「サインイン」をクリックする
    IoT.kyoto VISサインイン

  • ログイン後、右上にあるグラフの追加ボタンをクリックする

IoT.kyoto VISのグラフの設定情報については、以下の手順書をご参考に進めて下さい
初めての⽅のための新 IoT.kyoto VIS ⼿順書

  • 「認証情報設定」に「認証情報ストア名」、「アクセスキー」、「シークレットアクセスキー」をそれぞれ入力し、保存ボタンをクリックする
    認証情報ストア名には「lorawan-handson」を入力する
    アクセスキーとシークレットアクセスキーは、ダウンロードしたCSVファイルの内容をコピー&ペーストする

  • 「テーブル設定」のタブを開き、下記内容を入力する

テーブル名: lorawan-handson-environmental-sensor
パーティションキー: deviceId
ソートキー: timeStamp
日時フォーマット: YYY-MM-DD hh:mm:ss(JST)

  • 「グラフ描画設定」のタブを開き、下記項目を入力し、「保存」をクリックする

グラフタイトル: 環境センサー
グラフ更新間隔(秒): 5
グラフ描画幅: 30分
グラフ日時フォーマット: MM-DD HH:mm:ss
パーティションキー: 各自で異なるデバイスID
表示対象キー: illuminance

  • 「表示対象キー」の「illuminance」をチェックする
  • 以下の画像のように、センサーデータを可視化出来ればOK

IoT.kyoto VISのその他の使い方は、以下の手順書をご参考下さい
初めての⽅のための新 IoT.kyoto VIS ⼿順書

後片付け

作成したAWSのサービスを削除します。
今回はハンズオンのため、セキュリティに関する設定は行っておりません。
そのため、下記手順を行い利用したサービスを削除頂くことを推奨します

DynamoDB

「lorawan-handson-environmental-sensor」テーブルを削除します。

  • マネジメントコンソールの「サービスの検索」欄に、「dynamo」と入力し、「DynamoDB」をクリックする

  • 左側の項目一覧から、「テーブル」>lorawan-handson-environmental-sensorテーブルを選択し、「テーブルの削除」をクリック

  • 「テーブルの削除」というポップアップが開くので、内容を確認し、「削除」をクリックする

Lambda

「lorawan-handson-post-to-dynamodb」という名前のLambda関数を削除します。

  • マネジメントコンソールの「サービスの検索」欄に、「lambda」と入力し、「DynamoDB」をクリックする

  • 左側の項目一覧から「関数」を選択し、「lorawan-handson-post-to-dynamodb」をチェックし、右上の「アクション」から「削除」をクリックする

  • 「lorawan-handson-post-to-dynamodb 関数を削除」というポップアップが表示されるので、内容を確認し、「削除」をクリックする

CloudWatch

lorawan-handson-post-to-dynamodb関数が保存したログを削除します。Lambda関数がエラーで終了した場合のエラーログが保存されています。ロググループが存在しない場合は、この手順は必要ありません。

  • マネジメントコンソールの「サービスの検索」欄に、「cloudw」と入力し、「CloudWatch」をクリックする

  • 左の項目一覧から「ログ」をクリックし、フィルタに「/aws/lambda/lora」と入力し、エンターを押す
  • 「/aws/lambda/lorawan-handson-post-to-dynamodb」の左側のチェックをつける

  • 「アクション」>「ロググループの削除」をクリックする

  • 内容を確認し、問題がなければ「はい、削除します」をクリックする

API Gateway

「lorawan-handson」という名前のAPIを削除します。

  • マネジメントコンソールの「サービスの検索」欄に、「api」と入力し、「API Gateway」をクリックする

  • 「lorawan-handson」クリックする

  • 「アクション」>「APIの削除」をクリックする

  • 「RestApiの削除」というポップアップが表示されるので、「lorawan-handson」を入力し、「APIの削除」をクリックする

IAM

IAMユーザとIAMロールを削除します。

  • マネジメントコンソールの「サービスの検索」欄に、「iam」と入力し、「IAM」をクリックする

  • 左側の項目一覧から、「ユーザー」をクリックし、検索窓に「lora」を入力する
  • 「dynamo-readonly-lorawan-handson」にチェックを付け、「ユーザーの削除」をクリックする

  • 「ユーザーの削除」という表示がポップアップするので内容を確認し、チェックをつけ、「はい、削除します」をクリックする

  • 左側の項目一覧から、「ロール」をクリックし、検索窓に「lora」を入力する
  • 「lorawan_handson_lambda_execution」にチェックを付け、「ロールの削除」をクリックする

  • 「ロールの削除」という表示がポップアップするので内容を確認し、「はい、削除します」をクリックする

以上ではハンズオンは終了です。
ありがとうございました。