Edisonを使ってセンサデータをAmazon Kinesisにあげてみる【後編】

最近話題のIntel Edisonを用いて、温度センサ等のデータをWiFi経由でAmazon KinesisにあげてみるIoT的なのをやってみましょう。前回の記事でIntel EdisonをWifiでつなぎセンサデータをJavaScriptで取得するところまでやりました。今回の後編では、センサのデータをIoTにうってつけのクラウドサービスであるAmazon Kinesisにあげていきます。

![IMG_7960-2.png](https://qiita-image-store.s3.amazonaws.com/0/6069/e110560b-1793-f5a9-3b58-e4fd842b0257.png)

# 全体の流れ

前回の記事([Edisonを使ってセンサデータをAmazon Kinesisにあげてみる【前編】](http://qiita.com/KenTamagawa/items/3f2e2bd3bb36a27dc439)では、下記のEdisonの準備までやりました。
★Edisonの準備(前編の記事)
* Intel Edison Kit for Arduinoのセットアップ
* Groveスターターキットプラスのセットアップ
* センサデータをJavaScriptで取得する

今回は後編ということで、以下の順で実施していきます。
★AWSリソースの準備　
* AWS Cognitoでデバイスを認証する
* Amazon Kinesisのストリーム作成
* KinesisへのデータのPut
★仕上げ
* センサデータをEdisonからKinesisにPutする
* Kinesis内のデータの確認

では早速手をつけていきましょう。

# AWS Cognitoでデバイスを認証する

Kinesisにデータを書き込む際に、デバイス側に認証のためのクレデンシャルを置きたくないので、AWS Cognitoを使ってデバイスからの特定のリソースへのアクセスを認証することにします。詳しくは、こちら参照 ([Amazon CognitoをJavaScriptから使ってみる](http://qiita.com/KenTamagawa/items/36238d6264ee14c2b50f))。

上記の記事を参考に、CognitoのIndentity Poolを作成します。Amazon Cognitoが米国東リージョンとEUリージョンのみで使えるため(2011/12/02時点)、今回は米国東リージョンでCognitoを使います。Cognitoのゲストアクセスユーザーに対して、Kinesisのみへの書き込み権限を与えます。こうすることで、Kinesis以外のAWSリソースにはアクセス出来ないようにします。その上で、下記のようなjsファイルを作成します。AccountID, RoleArn, IdentityPoolIdにご自身のものを入れるのに注意です。

```js:cognito.js

var AWS = require('aws-sdk');
var awsRegion = "us-east-1";
var cognitoParams = {
    AccountId: "41073212XXXX",
    RoleArn: "arn:aws:iam::41073212XXXX:role/Cognito_iot_hackUnauth_DefaultRole",
    IdentityPoolId: "us-east-1:8286c169-3ff5-4feb-aad9-9f7d9XXXXXXXXX"
};

AWS.config.region = awsRegion;
AWS.config.credentials = new AWS.CognitoIdentityCredentials(cognitoParams);
AWS.config.credentials.get(function(err) {
        if (!err) {
            console.log("Cognito Identity Id: " + AWS.config.credentials.identityId);
        }
});
```

手元のローカル環境(私の場合、Mac Book Air)にて、Cognitoのゲストアクセスができるか試してみましょう。AWS SDK for JavaScriptとnode.js環境があれば、実行できます。すると下記のように、Cognito Identityがとれていることが分かります。これでAWSのリソースにアクセスできます。

```
$ node cognito.js
Cognito Identity Id: us-east-1:2908a57d-fe97-4b15-a0a9-03dbXXXXXXXX
```

つまり、同様のJSをEdison上で動かせば、EdisonからCognito認証が行え、AWSのリソースにアクセスできるようになります。簡単！
# Amazon Kinesisのストリーム作成

次は、マネージメントコンソールで、Amazon Kinesisのストリーム(ストリーム処理をするときの単位。システム毎にストリームを作ることになります)を作っていきましょう。
(Amazon Cognitoが米国東リージョンとEUリージョンのみで使えるために、今回はKinesisも米国東リージョンで使うことにします。Kinesisのみを使う場合はもちろん東京リージョンをお使い頂けます。)
![2014-11-29_22-09-04.png](https://qiita-image-store.s3.amazonaws.com/0/6069/e92a9116-e324-3188-04e3-3f5fa49a291f.png)

シャードという概念がでてきます。シャードは、Amazon Kinesis ストリームを構成しており、シャードの量でスループットを決めることになります(ちなみに、1シャードあたり、1 MB/秒のデータ入力、2MB/秒のデータ出力能力を提供します。また、1シャードでは秒あたり最大1000 PUT および 5READをサポートできます)。
![2014-11-29_22-09-05.png](https://qiita-image-store.s3.amazonaws.com/0/6069/1a198d0f-e202-5190-1173-355448aeec20.png)

はい、これでKinesisのストリームができました。Kinesis の料金は、時間単位のシャード速度と PUT 取引の量で決まります。1シャードあたり約1500円〜/月となるのがかなり低額でリアルタイムストリーミングが行えますが、詳しくは[こちら](http://aws.amazon.com/jp/kinesis/pricing/)をご参照ください。


# Amazon KinesisへのデータのPut

では作成しておいたKinesisストリームに、疑似のセンサデータをローカル環境からPutしてみて、Cognitoでの認証とKinesisへのデータのPutが動作するかチェックしてみましょう。

```js

var AWS = require('aws-sdk');
var deviceId = "Edison";
var streamName = 'iot_hack_edison_sensordata'; // Kinesisストリーム
var partitionKey = "xyz"; // Kinesisのパーティション用のキー

// Congito setting
var awsRegion = "us-east-1";
var cognitoParams = {
    AccountId: "41073212XXXX",
    RoleArn: "arn:aws:iam::41073212XXXX:role/Cognito_iot_hackUnauth_DefaultRole",
    IdentityPoolId: "us-east-1:8286c169-3ff5-4feb-aad9-9f7d9129b797"
};

// Get identity from Amazon Cognito
AWS.config.region = awsRegion;
AWS.config.credentials = new AWS.CognitoIdentityCredentials(cognitoParams);
AWS.config.credentials.get(function(err) {
        if (!err) {
            console.log("Cognito Identity Id: " + AWS.config.credentials.identityId);
        }
});
// Get sensor data
var sensorData = "100";//疑似のセンサーデータ

// Put data into Kinesis 
var kinesis = new AWS.Kinesis();

setInterval(periodicActivity, 1000);

function periodicActivity() {
    console.log("Sensor " + sensorData);

    var json = {
        device_id: deviceId,
        time: (new Date).getTime(),
        sensors: [{
                sensordata: {
                    temp: sensorData,
                }
            }]
    };
   var kparams = {
        Data: JSON.stringify(json),                                                 
        PartitionKey: partitionKey,
        StreamName: streamName
    };

    kinesis.putRecord(kparams, function(err, data) {
            if (err) {
                console.log(err, err.stack);
            } else {
                console.log(data);
            }
        });
}
```

これで実行してみましょう。

```
$ node kinesis.js
{ SequenceNumber: '49545554908569968986498146905221840262797727776028753922',
  ShardId: 'shardId-000000000000' }
```

実際にKinesisにデータを書き込むことができ、その際にShardIDと、SequenceNumberがかえってきました。

今回作ったKinesisストリームには、Shardは一つしかないので、必ず同じIDがかえってきますが、もしセンサが大量にあってシャードを複数持っている場合は、コードの中のパーティションキーによって、異なるシャードに振り分けられることになります。こういう設計なので、後からでもAPIを用いてシームレスにシャードの数を増やすことで、デバイスやデータ量の数の増加に対応できます。

また、Kinesisは一つ一つのデータにシーケンシャルな番号がつくのが特徴です。データを入れた際に、必ずこのSequenceNumberが返ってくるのでこの位置からデータを取り出すことができます。

# センサデータをEdisonからAmazon KinesisにPutする

さて、いよいよ仕上げです。ここまでEdisonのセットアップを終えた後に、AWSリソースのセットアップをし、ローカル開発環境からAWSリソースへの認証と疑似データのPutを行ってきました。

ここからは、またEdisonに戻って、最後の仕上げということで、EdisonからKinesisにデータをPutしてみましょう。先ほどのコードとほとんど同じですが、センサーからデータを取り込む部分([前回の記事参照](http://qiita.com/KenTamagawa/items/3f2e2bd3bb36a27dc439))のみ書き換えます。Kinesisに送り込むデータは、前回の記事でA0ピンに取り付けた光電センサのみとすると、こんな感じになるでしょう。

```js
var m = require('mraa'); //require mraa
var AWS = require('aws-sdk');
var deviceId = "Edison";
var streamName = 'iot_hack_edison_sensordata'; // Kinesisストリーム
var partitionKey = "xyz"; // Kinesisのパーティション用のキー

// Congito setting
var awsRegion = "us-east-1";
var cognitoParams = {
    AccountId: "41073212XXXX",
    RoleArn: "arn:aws:iam::41073212XXXX:role/Cognito_iot_hackUnauth_DefaultRole",
    IdentityPoolId: "us-east-1:8286c169-3ff5-4feb-aad9-9f7d9129b797"
};

//setup access analog inpuput pin 0
var analogPin0 = new m.Aio(0); 

// Get identity from Amazon Cognito
AWS.config.region = awsRegion;
AWS.config.credentials = new AWS.CognitoIdentityCredentials(cognitoParams);
AWS.config.credentials.get(function(err) {
        if (!err) {
            console.log("Cognito Identity Id: " + AWS.config.credentials.identityId);
        }
});

// Put data into Kinesis 
var kinesis = new AWS.Kinesis();

setInterval(periodicActivity, 1000);

function periodicActivity() {
    var analogValue0 = analogPin0.read(); // Get sensor data
    console.log("Sensor: " + analogValue0); 

    var json = {
        device_id: deviceId,
        time: (new Date).getTime(),
        sensors: [{
                sensordata: {
                    temp: analogValue0,
                }
            }]
    };
   var kparams = {
        Data: JSON.stringify(json), 
        PartitionKey: partitionKey,
        StreamName: streamName
    };

    kinesis.putRecord(kparams, function(err, data) {
            if (err) {
                console.log(err, err.stack);
            } else {
                console.log(data);
            }
        });
}
```
これで実行すると、同様にSequenceNumberが返ってくるはずです！ようやくEdisonからKinesisにデータを入れることができました！Congrats！

```
$ node kinesis.js
{ SequenceNumber: '495455549085699689864981469052218402627977277760287XXXXX',
  ShardId: 'shardId-000000000000' }
```


# Amazon Kinesis内のデータの確認
さて、しばし心を落ち着けてみると、本当にこれでEdisonからセンサデータがKinesisに入っているのか気になってきます。実際にKinesisにきちんとデータが入っているのかどうか、データを取り出して検証してみましょう。

Kinesisからデータを取り込んだり、可視化するには様々な方法があります。Kinesis Client Applicationを作って、DynamoDBやRedShiftにデータをおき、BIツールなのでアプリで可視化しても良いですね。実際にAWS re:InventでのIoT Hack Dayでは、DynamoDB経由でWebアプリが作られてハッカソンに参加している全チームのセンサデータを可視化するアプリが作られていました。

今回はよりシンプルに、ローカル開発環境(私の場合Mac Book Air)から直接Kinesisにアクセスして、AWS CLI(コマンドラインインターフェース)を使ってデータをとってみましょう。

データをKinesisにPutした際のシーケンスナンバーを使えばその時点からデータを取得することができます([参照](http://docs.aws.amazon.com/cli/latest/reference/kinesis/get-shard-iterator.html
))。

まずは、そのシーケンスナンバーを入れて、データ取得用のシャードイテレーターをとります。

```
$ aws kinesis get-shard-iterator --stream-name iot_hack_edison_sensordata --shard-id shardId-000000000000 --shard-iterator-type AT_SEQUENCE_NUMBER --region us-east-1 --starting-sequence-number "シーケンスナンバー"
{
"ShardIterator": "AAAAAAAAAAET7feSut....XXXXX"
}
```

このシャードイテレーターを用いると、無事にデータが取得できます。

```
$ aws kinesis get-records --region us-east-1 --shard-iterator "シャードイテレーター"

{
    "Records": [
        {
            "PartitionKey": "xyz", 
            "Data": "eyJkZXZpY2VfaWQiOiJFZGlzb24iLCJ0aW1lIjoxNDE3MjY3MjQwMDc3LCJzZW5zb3JzIjpbeyJzZW5zb3JkYXRhIjp7InRlbXAiOjEwMH19XX0=", 
            "SequenceNumber": "49545554908569968986498146905139633307063711990311550978"
        }, 
        {
            "PartitionKey": "xyz", 
            "Data": "eyJkZXZpY2VfaWQiOiJFZGlzb24iLCJ0aW1lIjoxNDE3MjY3MjQxMDgwLCJzZW5zb3JzIjpbeyJzZW5zb3JkYXRhIjp7InRlbXAiOjEwMH19XX0=", 
            "SequenceNumber": "49545554908569968986498146905140842232883326688205733890"
        }, 

　　　==略   

    "NextShardIterator": "AAAAAAAAAAF7ICgZLM4DI5Iav9z1lKFfcz3kAN2XPyzUpQrInH0eP3leHr+S9kt5kjkS8xXAvEM1cuaz3YW+cQwDy8fgcH8jSlkElIBMI9/C3vKsabUHc4ZC5ENn9OdGKqIeGfYqm10W+midO2vacRITlbkb8TWztFTcL+KLECNPgDgQOUKJdLO/WuwTEFj31blAKat+BBlBlmzfVOZ97g1DPk/aCfFQ"
}
```
結果はBase64エンコードされていますが(現時点のCLIではBase64デコードするオプションがないようです)、[デコード](https://www.base64decode.org/)すると、こんな感じで確かにPutしたデータが入っています！

```js
{"device_id":"Edison","time":1417267643687,"sensors":[{"sensordata":{"temp":100}}]}
```

# さいごに所感

さて、前編と後編を通じて、Intel Edisonを用いて温度センサ等のデータをWiFi経由でAmazon KinesisにあげてみるIoT的なのをやってみました。最後に例によって所感です。

前編の所感
> * Edisonは起動してすぐにwifiにつなげるし、npmでAWS SDK for JavaScriptを組み込み環境にそのままインストールできるなんてのは2000年頃に組み込み系開発をやっていた頃からすると隔世の感がある。
* GROVEスターターキットは色々なセンサーをハンダ付け無しでつないで遊べるので学習用には非常に良い。
* 組み込みボードのIOをnode.jsのJavaScriptで叩いて、そのままAWSのリソースもJavaScriptで書ける。サーバーサイド、スマホ等のクライアントサイドのみならず、デバイスサイドまでJavaScriptである。JS万歳。

今回の後編の所感
* Amazon Cognito使うとAWSリソースのアクセス制限も簡単に出来てよい。スマホ、デバイスからAWSリソースアクセスの際には便利。
* Amazon Kinesisは後からシャードも増やせていけるので、IoTのインフラをスモールスタートで構築できる。

免責
こちらは個人の意見で会社とは関係ありません。