Xivelyでセンサデータなどのリアルタイムなチャートを作る


### 概要
センサデータと天気APIのデータを集積してリアルタイムチャートを作る。

### Xively
[Xively](https://xively.com)はデバイスデータなどを集約管理するためのプラットフォーム。

1. ユーザー登録する。
1. `Develop`から`Add Device`する。
1. `API Key`と`Feed ID`をメモる。

無料だとデバイス数などに制約があった気がするが、今回は1デバイスにまとめてデータ送信しているのでとりあえず足りている。

### センサ
回路はそれぞれ簡単なので割愛(気になる人は聞いてください)。

- [温湿度センサー](http://akizukidenshi.com/catalog/g/gM-07002/): 秋月電子で入手可能。
- [CdS(明度センサ)](http://akizukidenshi.com/catalog/g/gI-00110/): 秋月電子で入手可能。
- [コンデンサマイク](http://akizukidenshi.com/catalog/g/gP-08182/): 秋月電子で入手可能。
- [NaPiOn(焦電型赤外線センサ)](http://www3.panasonic.biz/ac/j/control/sensor/human/napion/): 千石電商で入手可能。（[秋月電子のもの](http://akizukidenshi.com/catalog/g/gI-05712/)でもよいのだがそのままだとなかなか使えないのでフレネルレンズをつけて検出距離や検出角を調整する必要がある。）以下の2種を使った。
 - 標準,アナログ
 - 10m,デジタル


### 無線
[XBee Wi-Fi](http://qiita.com/satzz@github/items/c68abf8fb0743b74e807)を使う場合は以下の設定を行う。

- destination : `nslookup api.xively.com`して`64.94.18.120`
- port : `0x50`(=80)


### センサデータの送信
Arduinoのスケッチを書く。C++苦手…
温湿度センサーは以前使ったがややデータの読み取りが難しい。[DHTのライブラリを使う。](http://qiita.com/satzz@github/items/c68abf8fb0743b74e807)


```c++:sensors.ino

#include <DHT.h>

#define DHTTYPE DHT22
#define DHT22_PIN 7
DHT dht(DHT22_PIN, DHTTYPE);


int napionAnalogPin = A0;
int illuminancePin  = A1;
int microphonePin   = A2;
int napion10mPin    = 6;
int interval        = 3; //sec
int loopCount       = 100;

String moduleId = "0001";

String feedId = "YOUR_XIVELY_FEED_ID";
String apiKey = "YOUR_XIVELY_API_KEY";
String reqHeader;

void setup() {
  Serial.begin(9600);
  pinMode(napionAnalogPin,    INPUT);
  pinMode(illuminancePin, INPUT);
  pinMode(microphonePin,  INPUT);  
  pinMode(DHT22_PIN,      INPUT);      
  pinMode(napion10mPin,      INPUT);      

  reqHeader = "PUT /v2/feeds/" + feedId +" HTTP/1.1\r\n" + "X-ApiKey: " + apiKey + "\r\n"+"host: api.xively.com\r\n";

}

int illuminanceValue = 0;
int microphoneValue = 0;

void loop() {
  int illuminanceValueMax = 0;
  int microphoneValueMax = 0;

  long napionAnalogValue = 0;
  long napion10mValue = 0;

  for (int sec=0; sec<interval; sec++) {
    for (int j=0; j<loopCount ; j++) {
      delay(10);
      illuminanceValue = analogRead(illuminancePin);
      if (illuminanceValueMax < illuminanceValue) {
        illuminanceValueMax = illuminanceValue;
      }
      microphoneValue = analogRead(microphonePin);
      if (microphoneValueMax  < microphoneValue) {
        microphoneValueMax  = microphoneValue;
      }
      
      napion10mValue += digitalRead(napion10mPin);
      napionAnalogValue += analogRead(napionAnalogPin);
    }
  }
  float napion10mValueAvg    = 1.0*napion10mValue   /interval/loopCount;
  float napionAnalogValueAvg = 1.0*napionAnalogValue/interval/loopCount;
  char napion10mValueAvgChar[10];
  char napionAnalogValueAvgChar[10];
  dtostrf(napion10mValueAvg,    6, 2, napion10mValueAvgChar);
  dtostrf(napionAnalogValueAvg, 6, 2, napionAnalogValueAvgChar);


  float h  = dht.readHumidity() ;
  float t  = 5.0/9.0*(dht.readTemperature(true)-32.0); // fahrenheit -> celsius
  char ch[10];
  char ct[10];
  dtostrf(h, 6, 2, ch);
  dtostrf(t, 6, 2, ct);

  // HTTP request
  Serial.print(reqHeader);

  String httpBody =  "{\"version\":\"1.0.0\",\"datastreams\" : [  \r\n";
  httpBody.concat(  tuple("napion10m",       String(napion10mValueAvgChar))        + ","        );
  httpBody.concat(  tuple("napionAnalog",    String(napionAnalogValueAvgChar))     + ","        );
  httpBody.concat(  tuple("microphone",      String(microphoneValueMax))    + ","        );
  httpBody.concat(  tuple("humidity",        String(ch))                    + ","        );
  httpBody.concat(  tuple("temperature",     String(ct))                    + ","        );
  httpBody.concat(  tuple("illuminance",     String(illuminanceValueMax))   + " ] }" );
  
  Serial.print("Content-Length: " + String(httpBody.length()) + "\r\n\r\n");
  Serial.print(httpBody);  
  
}

String tuple(String channel, String value) {
  return "{\"id\":\"" + channel + moduleId + "\",  \"current_value\" : \"" + value + "\"}";
}

```

### APIデータの送信
センサを室内に設置したが、公開されている外気温もpostしてみる。
[World Weather Online](http://www.worldweatheronline.com/)のAPIを使う。API keyを取得したら以下のようなコマンドをサーバーのcronにでも登録する。データの更新間隔は30分程度なので実行間隔もそれくらいでよいだろう。
ちょっとダサいがpost内容を1回ファイルに吐いている。
JSONの整形は[jq](http://stedolan.github.io/jq/)を使っている。

```bash
curl "api.worldweatheronline.com/free/v1/weather.ashx?q=Tokyo&format=json&num_of_days=1&key=YOUR_WEATHER_API_KEY" | ~/bin/jq  ' { "datastreams": [ { "id": "temp_tokyo", "current_value": ( .data.current_condition[0].temp_C ) } ] }' > /tmp/temp_tokyo; curl https://api.xively.com/v2/feeds/YOUR_XIVELY_FEED_ID -X PUT -H "X-ApiKey: YOUR_XIVELY_API_KEY" --data @/tmp/temp_tokyo
```


### 結果
Xivelyで`Feed`をクリックすると送られているJSONを確認することができる。

```json
{
  "version": "1.0.0",
  "datastreams": [
    {
      "id": "napion10m0001",
      "current_value": "  0.37"
    },
    {
      "id": "napionAnalog0001",
      "current_value": "497.49"
    },
    {
      "id": "microphone0001",
      "current_value": "396"
    },
    {
      "id": "humidity0001",
      "current_value": " 52.60"
    },
    {
      "id": "temperature0001",
      "current_value": " 27.60"
    },
    {
      "id": "illuminance0001",
      "current_value": "225"
    }
  ]
}
```

idごとに以下のようなグラフが勝手にできてリアルタイムに更新されていく。とりあえずのトレンド確認としては便利。

#### 外気温
更新間隔が粗いが人のデータなので仕方ない。
![](http://i.gyazo.com/dc4179d916a406869a8a1b473bcf2a06.png)
#### 室温
人の出入りがある間は動きが激しい。あと空調のon/offも現れている。
![](http://i.gyazo.com/6b246fc696c0426733ae083b21bda7cf.png)
#### 室内湿度
冷房を切った時に湿度も上がっている気がするが、換気が切れたからか発汗によるものか。
![](http://i.gyazo.com/060e19ea156d4f0de036a7c59e594260.png)
#### 明るさ
暗いほうが値が高い（後々回路を直したい）。夜に段階的に照明が落ちるのと、朝日の日照が徐々に増えていくのが現れている。
![](http://i.gyazo.com/87b9b79036da9b16f74ec09fd080faba.png)
#### 音
人の出入りや会話に反応。
![](http://i.gyazo.com/ee2807d7c6443de0dc0c2139af2ecc1a.png)
#### 赤外線(アナログ)
人の動きに反応。
![](http://i.gyazo.com/6a90561e32b7218b0fcabaa3c72e4fb0.png)
#### 赤外線(デジタル)
3秒間の平均をとっている。デジタルのデータでもアナログっぽく使える気がする。高価なアナログ型を使う理由は減る。
![](http://i.gyazo.com/2fdd434c46f254cc6d9e6dd7d1c67dd9.png)

### 感想
#### データ送信頻度
Xivelyのrequest間隔をどの程度にしようか迷ったが、1デバイスでかつ3秒程度なら全く問題ない。デバイスが増えた場合は分からない。
#### 時刻
センサ取得したデータをすぐに送っているが、タイムスタンプをつけて後でまとめて送るとなるとNTPからの現在時刻の取得が必要。エラー処理や再送など総合的に考えるとRaspberry Piなどのほうが有利かも？
#### 展望
いったん蓄積したデータのAPIによる取得もできそうなので別のフロントエンドや分析プラットフォームにつなぐなどやってみたら面白そう。

### 参考
- [XBee Wi-FiでセンサーデータをAWSにアップロードする](http://qiita.com/satzz@github/items/c68abf8fb0743b74e807)
- [XBee Wi-FiをWebに接続する](http://qiita.com/satzz@github/items/814eee8dd37347fcb4e6)