目次
はじめに
以前、下記記事でIBM Cloudでセンサーデータの収集を紹介しました。
実はクラウド環境としてよく利用されているAWSでもセンサーデータを収集することができます。AWSのサービスを使用したIoT活用の方法と今後の応用について取り上げさせていただきます。
1. 今回のシナリオ
上記記事でもお伝えしたように、IoT観点でのクラウド活用フェーズは以下の通りです。
- センサーデータの収集
- データの分析
- 新しい付加価値(サービス)の創出
上記に加えて、データ活用までの時間をより早く、より簡単に、といったことも大事な要素になってくると思います。今回のAWS上ではどのくらい簡単にデータ取得、格納までができるかを体験してみました。なお、AWSではAWS IoTというサービスでAWS単独でもIoTデータを取り扱うことは可能ですが、AWS IoTでは利用するまでに必要となる各種設定作業(Amazon S3の設定、デバイス(Things)の登録、Ruleエンジンの登録)があるため、今回はそのような設定の必要がないセンサーを用いて、AWS内にセンサーデータを取得、格納までの流れをやっておりますのでご了承ください。
2. 今回構築するシステム構成
IoTデータをAWSにて取得し、保存するまでの流れのイメージは以下の通りです。
Webiot(ウェビオ)のWebAPIからデータをAWSのAPI Gatewayで受け、Lambdaを起動させて内部処理を行いデータを格納するといったシンプルな仕組みとなります。
使用するセンサーについて
今回確認するセンサーは、Webiotのセンサーを使用しました。Webiotセンサーの特徴は大きく以下の4つあります。
- 設定不要で、すぐに使用可能
Webiotのセンサーはインターネット接続済みで、届いたその日からセンサーデータの利用が可能のため、IoTハードウェア開発の知識がなくてもすぐに使用可能です。 - 低コスト、低リスク
Webiotの費用は月額使用料のみ(500円~)で初期費用は不要。気軽に試して、いつでもやめられます。また、月額利用料にはセンサー代、回線使用料、クラウド使用量、電池代がオールインワンで含まれておりコストが明瞭です。 - 設置のしやすさ
Webiotのセンサーは電池駆動のため、電源がない場所にも気軽に設置可能です。通信回線は、場所に合わせてBLE通信(ゲートウェイ要)/LoRaWAN通信(ゲートウェイ不要)/Sigfox通信(ゲートウェイ不要)から選択できます。 - 利用のしやすさ
センサーデータの確認や次システムとの連携は、WebiotのWebコンソール上から簡単に行え、Webhook/WebAPIでクラウドサービスとも簡単に接続可能です。
Webiotセンサーに関する情報は<Webiotセンサー>をご覧いただければと思います。
Webiotコンソールへのログイン
Webiotセンサーを利用するには、Webiotコンソール上でアカウントの登録が必要となります。
アカウントはGoogleアカウントで登録することが可能で、ログインすると以下のような画面となります。
センサーデータの注文などもこちらのコンソールから可能となります。(場所にもよりますが、都内であれば午前中に申し込みすれば当日発送も可能で、最短で翌日には利用開始できるようです)今回は超音波距離センサーと通信用のゲートウェイを利用しました。両方利用しても月1,500円とリーズナブルな価格設定です。
データ連携の概要
Webiotのセンサーデータは、Webiotのデータセンターに蓄積される仕組みになっており、自システムに取り込む際にはWebiotが用意しているWebhook/WebAPIを利用して、センサーからデータが送信されるタイミングでWebhook/WebAPIで自システムに取り込むことが可能です。
データ連携の詳細については、Webiotコンソール上のドキュメント<Webiotデータのデータ連携について>にもやり方が載っておりますので合わせてご覧いただければと思います。
今回使用するセンサーの説明と用途
注文したセンサーは、Webiotコンソール上の「センサー」タブにて確認できます。今回は超音波距離センサーを使用してみました。以下は、実際のセンサーとゲートウェイの画像です。(左がセンサーで右がゲートウェイ)
超音波距離センサーは、センサーから物体までの距離を定期的(20秒間隔)に測定できます。活用例などは駐車場の車両有無検知やごみ箱の満空検知、箱物の在庫量の把握など工夫次第で様々な利用が可能になりそうです。また、Webiotのセンサーには今回の超音波距離センサー以外に、温度・湿度・気圧や加速度、人感、CO2といった様々なセンサーが用意されているようです。
詳しくは、Webiotのサイト<Webiotセンサーの種類>に詳細がありますのでご覧いただければと思います。
3. センサーデータの取得
ロールの作成
AWS上ではAPI Gatewayでデータを受けてLambdaを起動させるため、まずはLambda用のロールを作成する必要があり、今回作成するLambda用のロールには以下のポリシーを割り当てておきます。
- AWSLambdaBasicExecutionRole
AWSコンソールにて「サービス」→「IAM」を指定、IAM画面の左のメニューより「ロール」を選択→「ロールの作成」ボタンを押下。ロール作成の画面で、信頼されたエンティティの種類を「AWS サービス」、このロールを使用するサービスを選択で「Lambda」を選択して、「次のステップ:アクセス権限」を押下。
ポリシーのフィルターに「AWSLambdaBasic」を入力し、一覧に表示された「AWSLambdaBasicExecutionRole」にチェックして、「次のステップ:タグ」を押下。
タグの設定は、何もせず「次のステップ:確認」ボタンを押下。最後にロール名を指定する画面があるので、必要な情報を入力して「ロールの作成」ボタンを押下(今回はmiso_20181207_roleというロールを作成)
Lambdaの作成
次に、API Gatewayから起動されるLambdaの作成を行います。
AWSコンソールにて「サービス」→「Lambda」を指定、Lambda画面の右にある「関数の作成」を選択。
次に、名称や説明の入力し、ロールは「既存のロール」指定し、上記で作成したロール(ここではmiso_20181207_role)を指定して「関数の作成」ボタンを押下。
作成したLambda関数のコード入力画面が表示されるので、以下のコードを貼り付けてください。(今回作成したLambdaのランタイムはnode.js 6.10としました)
データ取得用のソースコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
// モジュール var aws = require('aws-sdk'); // AWS SDKモジュール var momentTz = require('moment-timezone'); // moment-timezone.js用ライブラリ /** * リクエストが送られてきたときのハンドラー */ exports.handler = function(req, context, callback) { console.log("401 start."); console.log("402 request : " + JSON.stringify(req)); console.log("403 context : " + JSON.stringify(context)); console.log("404 request-headers : " + req.headers); console.log("405 request-body : " + req.body); // リクエストのbodyをJSONにする var sensor = JSON.parse(req.body); if (sensor != null) { // センサーデータの内容を表示 console.log("406 sensor - id : " + sensor["id"]); console.log("406 sensor - value : " + sensor["value"]); console.log("406 sensor - unit : " + sensor["unit"]); console.log("406 sensor - value01 : " + sensor["value01"]); console.log("406 sensor - value02 : " + sensor["value02"]); console.log("406 sensor - value03 : " + sensor["value03"]); console.log("406 sensor - datatype : " + sensor["datatype"]); console.log("406 sensor - unixtime : " + sensor["unixtime"]); console.log("406 sensor - dataid : " + sensor["dataid"]); console.log("406 sensor - packetid : " + sensor["packetid"]); console.log("406 sensor - rssi : " + sensor["rssi"]); console.log("406 sensor - battery : " + sensor["battery"]); // データ項目のunixtimeをローカルタイム(JST)に変換 console.log("406 moment-tomezone.js unixtime → JST : " + momentTz.unix(sensor["unixtime"], 'X').tz("Asia/Tokyo").format("YYYY-MM-DD HH:mm:ss")); } else { // リクエストのBodyがない場合はエラー console.log("999 error! request body is null !!! "); console.error("999 request body is null !") } // レスポンス&コールバック const response = { statusCode: 200, headers: { "Access-Control-Allow-Origin" : "https://s3-ap-northeast-1.amazonaws.com", "Access-Control-Allow-Credentials" : true, // Required for cookies, authorization headers with HTTPS "Access-Control-Allow-Headers" : "Origin, X-Requested-With, Content-Type, Accept" }, body: JSON.stringify({ "message": "lamdba function is callback!" }) }; callback(null, response); console.log("499 end!"); }; |
貼り付け後のイメージ。最後に「保存」ボタンを押下して完了です。なお、Webiotのセンサーデータの時刻データがUnixtimeとなり、変換するためmode_moduleのフォルダ内にnode.jsよりモジュールのmoment_timezoneを利用していますが、ここでは詳細は割愛します。(node_moduleの利用方法などはググるといっぱいあるので試してみてください)
API Gatewayの作成
次に、センサーデータの受け口となるAPI Gatewayの作成を行います。
AWSコンソールにて「サービス」→「API Gateway」を指定、API Gatewayの画面にある「+APIの作成」を選択。「新しいAPIの作成」画面にてAPI名、説明を入力し、「APIの作成」ボタンを押下。(ここではAPI Gatewayとして、miso_20181207_apiを作成してます)
次に、APIのリソースの「アクション」より「メソッドの作成」、一覧より「POST」を選択し、確定ボタン(レ)を押下。POSTメソッドの統合ポイントの設定が表示されるので、以下の画像のように入力を行い、項目「Lambda関数」には上記で作成したLambda関数(miso_20181207_lambda)を指定して、「保存」ボタンを押下。(API GatewayとLambdaの紐づけの確認メッセージが表示されますがそのまま「OK」を押下)
APIの作成すると以下のような画面となります。
次に、ステージを作成し、作成したAPIのデプロイを行います。
AWSコンソールのAPI Gatewayの画面の左メニューより「リソース」、「アクション」プルダウンより「APIのデプロイ」を選択。
APIのデプロイダイヤログが表示されるので、必要項目の入力を行い「デプロイ」ボタンを押下。(この例では、ステージdemoを作成し、APIをdemoステージにデプロイ)
デプロイが完了すると画面にAPI用のURL呼び出しが表示されるので、URLを記録しておいてください。
Webiotコンソール上での作業
データ連携の設定は、Webiotコンソール上で行います。最初に取得したいデータをJSON形式で指定します。取得可能な情報は<Webiotコンソールのドキュメント>でも記載されていますが、必要な情報のみに絞ることも可能です。今回はすべての情報を取得してみました。
まずは、Webiotコンソールの左メニューより「データ連携」を選択し、「ルールの追加」を押下。タイプの選択ダイヤログにて、「HTTP POST」を選択。
次に、対象となるチーム(Webiot内の契約名称)を選択し、データ連携する対象のセンサーにチェックを入れて、「次へ」ボタンを押下。(この例では、超音波距離センサーのBUxxxx(xxxxはモザイク処理)を選択)
次に、データの送信先設定となります。送信先URLには、上記で取得したAPI GatewayのURLを設定。BODYにはJSON形式でWebiotセンサーデータの全項目を指定し、「次へ」ボタンを押下。
センサーデータの全項目を送信するBODYの設定の参考は以下の通り。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
{ "id":"{{id}}", "value":"{{value}}", "unit":"{{unit}}", "value01":"{{value01}}", "value02":"{{value02}}", "value03":"{{value03}}", "datatype":"{{datatype}}", "unixtime":"{{unixtime}}", "dataid":"{{dataid}}", "packetid":"{{packetid}}", "rssi":"{{rssi}}", "battery":"{{battery}}" } |
設定内容の確認画面にて、「テスト」を押下して正常に通信できることを確認したら、「設定する」ボタンを押下。
元の一覧上に、データ連携用のルールが追加されたことを確認。
以上でAWS側、Webiot側の設定が完了しました。また、今回は実施していませんがAWSのAPI GatewayでAPIキーを作成しておき、Webiotコンソール上で設定させる事でよりセキュアに連携を行うことも可能です。
4. 取得したデータの確認
CloudWatch上でデータ確認
あとは、注文していたWebiotのデートウェイの電源を入れます。(電源を入れた後、初期化に30秒程度待つとゲートウェイのランプが緑に点滅して通信が開始されていました)データは、Webiotコンソール上でも確認できます。
Webiotコンソールの左メニューにて「センサー」を選択。データ連携をした該当のセンサーを選択し、右にあるデータログのアイコンを押下。
しばらくすると定期的にデータが追加されていることが確認できました。
続いて、AWSのCloud WatchログでLambdaの結果を確認します。
AWSコンソールにて「Cloud Watch」を選択。Cloud Watchの画面の左メニューのログを選択し、フィルタに「/aws/lambda/(作成したLambda関数名)」を入れ、上記で作成したLambda関数を選択。
ログストリームを選択すると、API Gatewayに紐づけられたLambda関数(miso_20181207_lambda)内でconsole.log(“xxxxx”)とした部分が、Cloud Watchのログ上に出力され、AWS側でもデータが取得できていることが確認できました。
5.センサーデータの格納
上記でセンサーデータの取得ができたので、一旦、センサーのゲートウェイの電源をOFFにし、次にAWSのDynamoDBにセンサーデータを格納してみようと思います。
DynamoDBのテーブル作成
AWSコンソールにてDynamoDBを指定し、コンソール上の「テーブル作成」ボタンを押下し、新規テーブルを作成を行います。今回は、miso_20181207_dynamodbというテーブルを作成、プライマリーインデックス、セカンダリーインデックスはそれぞれid(文字列)、unixtime(数値)を指定し、他はデフォルトのまま画面下の「作成」ボタンを押下
しばらくするとテーブルが作成されているので、まだデータが登録されていないことを確認します。
Lambda用ロールの編集(追加)
次に、LambdaからDynamoDBを操作するため、上記で作成したIAMのロールに以下の権限を追加します。
- AWSLambdaDynamoDBExecutionRole
- AmazonDynamoDBFullAccess
AWSコンソール上のIAMの画面にて、作成したロールmiso_20181207_roleを選択し、上記のポリシーを追加。
ロールの詳細画面に戻り、追加したポリシーがアタッチされていることを確認。
Lambdaの編集
上記で作成したLambdaの関数index.jsを以下のように変更します。変更内容は、Cloud Watchのログを出力した後に、LambdaからDynamoDBのputでデータを書き込みに行く処理を追加しています。
AWSコンソールよりLambdaを指定し、上記で作成したLambda関数を指定し、以下のソースを貼り付け。
編集後のソース(index.js)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
// モジュール var aws = require('aws-sdk'); // AWS SDKモジュール var momentTz = require('moment-timezone'); // moment-timezone.js用ライブラリ var docClient = new aws.DynamoDB.DocumentClient({region: 'ap-northeast-1'}); /** * リクエストが送られてきたときのハンドラー */ exports.handler = function(req, context, callback) { console.log("401 start."); console.log("402 request : " + JSON.stringify(req)); console.log("403 context : " + JSON.stringify(context)); console.log("404 request-headers : " + req.headers); console.log("405 request-body : " + req.body); // リクエストのbodyをJSONにする var sensor = JSON.parse(req.body); if (sensor != null) { // センサーデータの内容を表示 console.log("406 sensor - id : " + sensor["id"]); console.log("406 sensor - value : " + sensor["value"]); console.log("406 sensor - unit : " + sensor["unit"]); console.log("406 sensor - value01 : " + sensor["value01"]); console.log("406 sensor - value02 : " + sensor["value02"]); console.log("406 sensor - value03 : " + sensor["value03"]); console.log("406 sensor - datatype : " + sensor["datatype"]); console.log("406 sensor - unixtime : " + sensor["unixtime"]); console.log("406 sensor - dataid : " + sensor["dataid"]); console.log("406 sensor - packetid : " + sensor["packetid"]); console.log("406 sensor - rssi : " + sensor["rssi"]); console.log("406 sensor - battery : " + sensor["battery"]); // データ項目のunixtimeをローカルタイム(JST)に変換 console.log("406 moment-tomezone.js unixtime → JST : " + momentTz.unix(sensor["unixtime"], 'X').tz("Asia/Tokyo").format("YYYY-MM-DD HH:mm:ss")); // DynamoDBに登録 registWebiotSensor(sensor); } else { // リクエストのBodyがない場合はエラー console.log("999 error! request body is null !!! "); console.error("999 request body is null !") } // レスポンス&コールバック const response = { statusCode: 200, headers: { "Access-Control-Allow-Origin" : "https://s3-ap-northeast-1.amazonaws.com", "Access-Control-Allow-Credentials" : true, // Required for cookies, authorization headers with HTTPS "Access-Control-Allow-Headers" : "Origin, X-Requested-With, Content-Type, Accept" }, body: JSON.stringify({ "message": "lamdba function is callback!" }) }; callback(null, response); console.log("499 end!"); }; /** * Webiotセンサー情報の登録 */ var registWebiotSensor = function(sensor) { console.log("501 [regist] start !"); // 登録するセンサー情報 var params = { TableName : "miso_20181207_dynamodb", Item : { "id" : sensor["id"], "value" : Number(sensor["value"]), "unit" : sensor["unit"], "datatype" : sensor["datatype"], "unixtime" : Number(sensor["unixtime"]), "sensortime" : momentTz.unix(sensor['unixtime'], 'X').tz('Asia/Tokyo').format('YYYY-MM-DD HH:mm:ss'), "dataid" : sensor["dataid"], "packetid" : sensor["packetid"], "rssi" : Number(sensor["rssi"]), "battery" : Number(sensor["battery"]) } }; // 値が空文字の補完(DynamoDBが空文字を受け付けないための補完処理) for (var key in params.Item) { for (var items in params.Item[key]) { var value = params.Item[key][items]; if (value === undefined || value === "") { value = "N/A"; } } } // センサー情報を登録 docClient.put(params, function(err, data) { if (err) { console.log("503 [regist] insert webiot_sensor faild. Error : ", JSON.stringify(err)); } else { console.log("504 [regist] insert webiot_sensor success.", data); } }); console.log("599 [regist] end ! "); }; |
画面右上の「保存」ボタンを押下し、正常に保存できることを確認。
以上で、DynamoDBにデータを格納する準備が整いました。
DynamoDB上でデータ確認
再度、ゲートウェイの電源をONにしてみると、問題なくDynamoDBにデータが登録されるようになり、IoTデータの見える化ができました。
(項目sensortimeは、Lambda内でunixtimeをローカル時間(JST)に変換した値を登録)
まとめ
今回、Webiotセンサーを用いてセンサーデータの取得、格納までを試み、AWSでも比較的簡単に、短時間でデータ取得、格納できることが確認できました。今回は超音波距離センサーで物体との距離のセンサーを利用しましたが、上述の通りWebiotのセンサーは他にも種類があるので、アイデア次第でいろいろなことに活用できそうです。また、AWS上のDynamoDBへの格納ができ、時系列データを蓄積させることで、市販のBIツールやAWSのQuickSightなどを用いた可視化、SageMakerなどの機械学習による予測など、様々なデータ活用が可能なことが改めて確認できました。IoTデータの可視化は決して新しいことではないですが、今あるサービスを組み合わせることで、よりスピーディにタイムリーなデータを提供できるような仕組みを理解しておくことは我々には重要です。そんな時に今回の記事が情報の入り口として皆さんの理解の一助となれば幸いです。
執筆者プロフィール
- 社内の開発プロジェクトの技術支援、クラウド開発支援を担当。まずは手を動かす!をモットーになんでも屋さんとして奮闘中。最近はコンテナ技術やマイクロサービスの現場活用に向け日々精進してます。
この執筆者の最新記事
- Pick UP!2020.03.27汎用的な仕組みでセンサーデータを見える化してみた――後編
- Pick UP!2020.03.27汎用的な仕組みでセンサーデータを見える化してみた――前編
- AWS・クラウド2019.04.01AWSでIoTデータを見える化してみた その2
- AWS・クラウド2019.01.22AWSでIoTデータを見える化してみた