ArduinoとDatadogでオフィスの温度と湿度を可視化する

こんにちは、プレイドの @ikemonn です。

プレイドではKARTEというサービスを提供しており、私は主にインフラ側を担当しております。

今回は私の自宅で眠っていたArduinoとインフラの監視で利用しているDatadogを使用してオフィスの温度・湿度を可視化しようと思います。

概要

Datadogとは、システムモニタリングツールで、Karteではサーバの監視やAPIのレスポンタイムの可視化、AWSの利用金額のモニタリング等に利用しています。
AWSやSlack、PagerDutyなど様々なサービスとの連携もしやすく重宝しています。

今回は下記のような手順で温度・湿度をArduinoで計測しDatadogにデータを投げてグラフとして表示させます。

Arduino UnoだとEthernet Shield2を使用しても、https通信は厳しいようなので一旦Nodeサーバにhttp通信でデータを送ってNodeサーバからDatadogのAPIを叩きにいきます。

  1. Arduinoで温度・湿度を計測する
  2. ArduinoからMac上で動かしているNode.jsのサーバに温度・湿度を送信する
  3. Node.jsサーバが温度・湿度を受け取ったらDatadogにデータを送る

また毎時&異常値が出た時にSlackに通知するように設定します。

  • Mac上のcronで毎時、温度・湿度をSlackに通知
  • Datadogのモニタリングでしきい値に達したらSlackにアラートを投げる

使用したもの

手順

Ethernet Shield2を取り付ける

Arduino Unoはデフォルトでは通信ができないので、Ethernet Shield2を使ってhttp通信が出来るようにします。
後ほど使うので、Ethernet Shield2に貼られているmacアドレスをメモしておいてください。
下記の画像のようにArduinoのピンに合わせてEthernet Shield2をとりつけます。

配線する

今回は下記の画像のように配線しました。

コードを書く

今回はEthernet Shield 2を使用するので、Arduino IDE v1.7.2以降をDLします。

湿度・温度の取得

温湿度センサAM2302用のライブラリもあるので、adafruit/DHT-sensor-libraryからgit cloneしておきます。

Arduino IDEを起動し、[メニューバー] -> [スケッチ] -> [ライブラリを使用]-> [ライブラリをインストール]から先ほどcloneしたフォルダを選択します。

ArduinoとAM2302を使って温湿度センサを作る - QuZeeBlog@Hatenaを参考にコードを書いていきます。

#include "DHT.h"
#define DHTTYPE DHT22
#define DHT22_PIN 7
DHT dht(DHT22_PIN, DHTTYPE);

void setup() {
  pinMode(DHT22_PIN, INPUT);
  Serial.begin(9600);
}

void loop()
{
  // 湿度と温度(摂氏)を取得する
  float h  = dht.readHumidity() ;
  float t  = dht.readTemperature();
  Serial.print("humid: ");
  Serial.print(h);
  Serial.print("temp: ");
  Serial.println(t);

  delay(2000);

}

これをコンパイルして実行すると下記のように湿度と温度が取れます。
体感は25℃もない気がするのですが、センサによると気温25度らしいです。

EthernetでMacに通信する

続いて、Ethernet経由でMac内に立てたNode.jsサーバにアクセスしてみます。
[メニューバー] -> [ファイル] -> [スケッチの例] -> [Ethernet2] -> [WebClient]をもとにコードを書きます。

下記の部分を書き換えます

  • byte macにEthernet Sieldのmacアドレス
  • IPAddress ipにArduinoに割り振るIPアドレス
  • char serverにMacのローカルIP
  • client.connect(server, [ポート])にNodeサーバのポート番号(3000番で立ち上げます)
#include <SPI.h>
#include <Ethernet2.h>
// 先ほどメモしたEthernet SieldのMacアドレス
byte mac[] = { 0x90,0xA2,0xDA,0x10,0x67,0xBA }; 
// MacのローカルIP(ifconfigコマンドで調べられる)
char server[] = "192.168.1.66"; 
 // Arudinoに割り当てるIPアドレス(使われていなさそうなIPを適当に設定する)
IPAddress ip(192, 168, 1, 2);

EthernetClient client;

void setup() {
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    Ethernet.begin(mac, ip);
  }

  delay(1000);
  Serial.println("connecting...");

  // Nodeサーバでlistenしているポートを第二引数に指定(今回は3000番)
  if (client.connect(server, 3000)) { 
    Serial.println("connected");
    // q=arduinoが送信される
    client.println("GET /?q=arduino HTTP/1.1");
    client.println("Host: 192.168.1.66");
    client.println("Connection: close");
    client.println();
  }
  else {
    Serial.println("connection failed");
  }
}

void loop()
{
  if (client.available()) {
    char c = client.read();
    Serial.print(c);
  }

  if (!client.connected()) {
    Serial.println();
    Serial.println("disconnecting.");
    client.stop();
    while (true);
  }
}

Node.js

var express = require('express');
var app = express();

app.get('/', function (req, res) {
    console.log(req.query);
});

app.listen(3000, function () {
  console.log(‘App listening on port 3000!');
});

Nodeサーバを立ち上げた後に、ArduinoIDEからコードをコンパイルして実行すると下記のようにパラメータが送られてきているのがわかります。

Nodeサーバに湿度と温度をおくる

ここまででArduinoで温度・湿度を取得する & ArduinoからNodeサーバにデータが送信できるようになりました。
次にそれらを組み合わせてNodeサーバに温度と湿度を送ってみます。

#include <DHT.h>
#include <SPI.h>
#include <Ethernet2.h>

#define DHTTYPE DHT22
#define DHT22_PIN 7
DHT dht(DHT22_PIN, DHTTYPE);
// Ethernetシールドのmacアドレス
byte mac[] = { 0x90,0xA2,0xDA,0x10,0x67,0xBA };
// MacのローカルIP
char server[] = "192.168.1.66";
// 自身のネットワークによって変更する
IPAddress ip(192, 168, 1, 2);
EthernetClient client;

unsigned long lastConnectionTime = 0;
// 60秒ごとに送信する
const unsigned long postingInterval = 60L * 1000L;

void setup() {
  pinMode(DHT22_PIN, INPUT);
  Serial.begin(9600);

  delay(1000);
  Ethernet.begin(mac, ip);
  Serial.print("My IP address: ");
  Serial.println(Ethernet.localIP());
}

void loop()
{
  if (client.available()) {
    char c = client.read();
    Serial.write(c);
  }

  if (millis() - lastConnectionTime > postingInterval) {
    float h  = dht.readHumidity() ;
    float t  = dht.readTemperature();
    httpRequest(h, t);
  }
}

void httpRequest(float humid, float temp) {

  client.stop();

  if (client.connect(server, 3000)) {
    Serial.println("connecting...");
    Serial.println("humid is ");
    Serial.println(humid);
    Serial.println("temp is ");
    Serial.println(temp);
    // /?h=25.02&t=38.23という形式で送る
    client.print("GET ");
    client.print("/?h=");
    client.print(humid);
    client.print("&t=");
    client.print(temp);
    client.println(" HTTP/1.1");
    client.println("Host: 192.168.1.66");
    client.println("Connection: close");
    client.println();

    lastConnectionTime = millis();
  }
  else {
    Serial.println("connection failed");
  }
}

このようにデータが送信されます。

Datadogにデータを送ってみる

では最後にDatadogに湿度と温度を送ってグラフ化してみましょう。
Datadogにアカウント登録して、Mac用のDatadog Agentをインストールします。

データの送信の際にnode-datadogを使用するので、Node.jsサーバと同じディレクトリにgit cloneしておきます。
下記のapi_keyとapp_keyはご自身のアカウントの情報を埋めてください。

var express = require('express');
var app = express();

var DataDog = require('./node-datadog');
var api_key = "your api key";
var app_key = "your app key";
var dd = new DataDog(api_key, app_key);

app.get('/', function (req, res) {
    console.log(req.query);
    var temp = req.query['t'];
    var humid = req.query['h'];

    dd.postSeries({
        "series": [
            {
                "metric": "arduino.temperature",
                "points": [
                    [Date.now()/1000, req.query['t']]
                ],
                "type": "gauge",
                "tags": ["arduino", "temperature"]
            },
            {
                "metric": "arduino.humidity",
                "points": [
                    [Date.now()/1000, req.query['h']]
                ],
                "type": "gauge",
                "tags": ["arduino", "humidity"]
            }
        ]
    });

});

app.listen(3000, function () {
  console.log('App listening on port 3000!');
});

Datadogにアクセス後、左列の[Metrics] -> [Explore]から先ほど送信したデータを確認できます。

下記のように温度と湿度が送信されていればOKです。

DatadogにはDashboardという機能があり、特定のグラフや数値をまとめて見やすくすることができます。
KarteではサーバごとのCPU使用率やメモリ使用量、レスポンスタイム等を表示しています。

毎時温度と湿度をSlackにポストする

Datadogに送られているデータを取得して、毎時Slackに温度と湿度をポストします。
下記のshellを保存し、Macのcrontabから毎時実行するように設定します。
事前にSlackの”Incoming Webhook”を設定し、WebhookのURLを取得しておいてください。
また、jqコマンドを使用するので、brew install jq等でjqをインストールしておいてください。

#!/bin/sh

get_temp() {
    local DD_API_KEY=$1
    local DD_APP_KEY=$2
    local FROM_TIME=$3
    local TO_TIME=$4

    temp=$(curl -sG \
        "https://app.datadoghq.com/api/v1/query" \
        -d "api_key=${DD_API_KEY}" \
        -d "application_key=${DD_APP_KEY}" \
        -d "from=${FROM_TIME}" \
        -d "to=${TO_TIME}" \
        -d "query=arduino.temperature{*}by{host}" \
        | /usr/local/bin/jq '.series[].pointlist[-1][1]' \
        | xargs printf "%.2f")

    echo $temp
}

get_humid() {
    local DD_API_KEY=$1
    local DD_APP_KEY=$2
    local FROM_TIME=$3
    local TO_TIME=$4

    humid=$(curl -sG \
        "https://app.datadoghq.com/api/v1/query" \
        -d "api_key=${DD_API_KEY}" \
        -d "application_key=${DD_APP_KEY}" \
        -d "from=${FROM_TIME}" \
        -d "to=${TO_TIME}" \
        -d "query=arduino.humidity{*}by{host}" \
         | /usr/local/bin/jq '.series[].pointlist[-1][1]' \
         | xargs printf "%.2f")
    echo $humid
}


notify_to_slack() {
  local TEMP=${1:-"null"}
  local HUMID=${2:-"null"}
  local COLOR=${3:-"#439FE0"}
  local CHANNEL="#test_ikemonn"
  local USER_NAME="arduino"
  local MSG="現在の温度は${TEMP}度、湿度は${HUMID}%です。"
  local WEBHOOK="your webhook"

  post_data=`cat <<-EOF
  payload={
    "channel": "${CHANNEL}",
    "username": "${USER_NAME}",
    "attachments": [
      {
        "fallback": "Fallback $MSG",
        "color": "${COLOR}",
        "title": "${MSG}"
      }
    ]
  }
EOF`

  curl -X POST $WEBHOOK --data-urlencode "${post_data}"
}

main() {
    local DD_API_KEY="your api key"
    local DD_APP_KEY="your app key"
    local TO_TIME=$(date +%s)
    local FROM_TIME=$(date -v -1H +%s)

    local temp=$(get_temp ${DD_API_KEY} ${DD_APP_KEY} ${FROM_TIME} ${TO_TIME})
    local humid=$(get_humid ${DD_API_KEY} ${DD_APP_KEY} ${FROM_TIME} ${TO_TIME})

    notify_to_slack $temp $humid
}

main

上記ファイルを保存した後、crontabの設定をします。

# Macにcronを設定する
$ crontab -e
# 平日10~19時の毎時0分に温度・湿度をSlackに通知
0 10-19 * * 2-6 /your/path/to/arduino_post_to_slack.sh

アラートを設定する

最後に温度・湿度がしきい値を超えている時にアラートをSlackに通知するように設定します。
Datadogの"Monitor"という機能を使ってアラートを設定します。
例えば湿度が40%以下の状態が1分以上続いた時に通知したいなどといった設定ができます。

Datadogでは、特定の値だけではなく変化量(値が1分で50%以上増加等)でもアラートを設定できるので便利です。
また、Slackに通知される時にグラフも見られるので状況が分かりやすくて助かります。

最後に

Lチカを試しただけでお蔵入りになっていたArduinoを活用できて良かったです。
1日ほどで作成できますので、家で眠っているArduinoがある方は試してみてはいかがでしょうか?
http通信ができると色々試せることが増えるのでEthernetSield2は買って良かったです。
一方、Arduino Unoだとhttps通信ができないので一旦Nodeサーバ等にデータを送ってから、各種APIを叩きにいかないといけないところが少し面倒でした。

他にも様々なセンサが売られているので今後も試してみようと思います!

PlaidではIoT関係ですと、他にも@makinoyが作った打刻システムが使われていたりします。(定期券等の磁気カードをかざすと打刻される)

何でも自由に作れて皆に使ってもらえる風土がありますので、ご興味のある方は是非!

ウェブ接客プラットフォーム「KARTE」を運営するプレイドでは、
こうやって自由に作れる技術に興味を持つエンジニア(インターンも!)を募集しています。

詳しくはこちら(Wantedly)をご覧ください。

参考