オムロンの非接触温度センサで人を検知して通知してみる

# はじめに
オムロンの非接触温度センサ「D6T」がGroveコネクタで使えるようになったようです。

[オムロンのオープンプラットフォーム対応センサ](https://omron-devhub.github.io/omron-op-sensor/)

Githubでサンプルコードも公開されているので、これを参考にして簡単な人感システム？のようなものを作ってみようと思います。

# 開発環境
- macOS Sierra
- Arduino IDE 1.8.8

# 概要
1. 計測  
D6Tで温度を計測してGroveコネクタ経由でM5Stackへ送る
2. 判定  
受け取った温度を元に人がいるか判定
3. 通知  
IFTTTでWebhookを作成しスマホのLINEへ通知

# 用意するもの
- M5Stack
- Grove 4ピンケーブル
- 非接触温度センサD6T 変換基板
- 非接触温度センサD6T 本体
- IFTTT アカウント
- LINE アカウント

# 実装

## 1. 計測
まずハードウェア側の準備ですが、D6Tの変換基盤とM5StackはどちらもGroveに対応しているので、4ピンケーブルでコネクタを繋ぐだけでOKです。

ソフトウェア側はGithubで[ライブラリ](https://github.com/omron-devhub/d6t-grove-m5stack)が公開されていますので、これを使います。
まず、`Clone of download`ボタンから`Download ZIP`を選択し、ソースコードをダウンロードします。
次に`Arduino IDE`を起動し`スケッチ`→`ライブラリをインクルード`→`.ZIP形式のライブラリをインストール`を選んでダウンロードしたzipを指定します。

ライブラリの取り込みに成功すれば`ファイル`→`スケッチ例`から`Omron D6T-Grove-M5Stack`が選択できるようになっているはずです。
サンプルは2種類用意されていて、`value_sample`は計測値をLCDディスプレイに出力するシンプルなもの、`coolwarm_sample`は計測値をサーモグラフのように可視化したものです。

温度の計測はサンプルコードを動かすだけで実現できました。
次は`coolwarm_sample`をベースにして、計測値の判定と通知の実装をやってみます。

## 2. 判定
ひとまず、`coolwarm_sample`を読んでみます。
表示する色を7段階に分けて定義しているようです。

```coolwarm_sample.ino
int color[7];
int range_min = 25;
int range_max = 35;

uint16_t rgbToColor(uint8_t red, uint8_t green, uint8_t blue){
  return ((red>>3)<<11) | ((green>>2)<<5) | (blue>>3);
}

void initColormap() {
  color[0] = rgbToColor(76,134,228);
  color[1] = rgbToColor(127,178,248);
  color[2] = rgbToColor(177,208,245);
  color[3] = rgbToColor(221,220,219);
  color[4] = rgbToColor(252,194,173);
  color[5] = rgbToColor(255,151,125);
  color[6] = rgbToColor(234,94,78);
}

int getRangeColor(float temperature) {
  float range = range_max - range_min;
  float block = range / 7;
  if (temperature < range_min) {
    return 0;
  } else if (temperature < (range_min + block)) {
    return 1;
  } else if (temperature < (range_min + (block * 2))) {
    return 2;
  } else if (temperature < (range_min + (block * 3))) {
    return 3;
  } else if (temperature < (range_min + (block * 4))) {
    return 4;
  } else if (temperature < (range_min + (block * 5))) {
    return 5;
  } else {
    return 6;
  }
}
```
通知を飛ばすための判定基準として、16個の計測値のいずれか3つ以上が7段階のうち下から2番目以上になった場合に、人を検知したと判断するようにしてみようと思います。
![スクリーンショット 2019-03-12 15.22.22.png](https://qiita-image-store.s3.amazonaws.com/0/295893/d39bf16e-9e41-b282-3382-96d0eb82879e.png)


`coolwarm_sample`のメインループを改修します。

```d6t_human_detector.ino
struct tm detect_time; // 検知時刻を格納するtm構造体
char format_time[20];  // 出力文字格納用

~中略

void loop() {
  int upsell = 0; // セル
  int detect_flg = 0; // 検知フラグ
  
  sensor.scanTemp();

  int16_t rectw = (M5.Lcd.width() / 4);
  int16_t recth = (M5.Lcd.height() / 4);
  
  Serial.println("-------------------------");
  int x, y;
  for (y = 0; y < 4; y++) {
    
    for (x = 0; x < 4; x++) {
      float temperature = sensor.temp[x][y];
      int rangecolor = getRangeColor(temperature);
      Serial.print("temperature:");
      Serial.print(sensor.temp[x][y]);
      Serial.print(" rangecolor:");
      Serial.println(rangecolor);
      
      // 温度を検知して変化したセルをカウント
      if (rangecolor > 1) {
        upsell++;
      }

      M5.Lcd.fillRect((rectw * x), (recth * y), rectw, recth, color[rangecolor]);
    }
    
    Serial.println("");

    if (upsell > 2 && detect_flg != 1) {
      // 検出フラグをたてる
      detect_flg = 1;
    }
    
  }
  
  if (detect_flg == 1) {    
    Serial.println("誰かいる");

    // 検知した日時を取得
    getLocalTime(&detect_time);
    sprintf(format_time, "%04d/%02d/%02d-%02d:%02d:%02d",
    detect_time.tm_year + 1900, detect_time.tm_mon + 1, detect_time.tm_mday,
    detect_time.tm_hour, detect_time.tm_min, detect_time.tm_sec);
    
    // IFTTTのWebhookに投げる
    send_webhook("HumanDetection!!", "OwnSeat", String(format_time));
  } else {
    Serial.println("誰もいない");
  }
  
  delay(3000);
}
```
判定条件を満たした際は`detect_flg`フラグをたてて`send_webhook`関数を呼び出します。
`send_webhook`はIFTTTのWebhookを呼び出す関数で、次の項目で説明します。

## 3. 通知
LINEへ通知するにあたっては、おなじみのIFTTTを使います。
IFTTT連携するためにはネットワークにつなぐ必要がありますが、M5StackはEspressifのESP32を積んでいるのでWiFi通信やBLE通信が可能です。
今回はWiFi通信でWebhookを呼び出すようにします。

```d6t_human_detector.ino
const char* wifi_ssid = "<SSID名>";
const char* wifi_pass = "<SSIDパスワード>";

~中略

void setup() {
  M5.begin();
  dac_output_disable(DAC_CHANNEL_1);
  Serial.begin(115200);
  delay(1000);

  WiFi.begin(wifi_ssid, wifi_pass);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
    M5.Lcd.printf(".");
  }
  
  M5.Lcd.println("\nwifi connect ok");

  initColormap();

  // 時間設定
  configTime( JST, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");
}
```

WiFiのセットアップ自体は`WiFi.begin`をにSSIDとパスワードを渡すだけです。
通信自体は`HTTPClient`を使います。

IFTTTとLINEの連携についてはこちらの記事で非常にわかりやすく説明されていました。

[IFTTT（イフト）で、Webhooksを使用した簡単に自動でLineにメッセージを送信する方法](https://amg-solution.jp/blog/14267)

この記事を参考にWebhookアプレットを作成し、スケッチから呼び出します。
引数を3つ渡すことができるので、イベント名と場所と時間を渡すようにします。
（POSTでやりたかったのですがうまくいかずGETににげました）

```d6t_human_detector.ino
// IFTTT Webhook URL
const char* webhook_url = "https://maker.ifttt.com/trigger/<Webhook イベント>/with/key/<Webhook キー>";

~中略

void send_webhook(String event_type, String event_place, String event_time)
{
  String url = String(webhook_url);
  url.concat("?value1=");
  url.concat(event_type);
  url.concat("&value2=");
  url.concat(event_place);
  url.concat("&value3=");
  url.concat(event_time);
  Serial.print("url:");
  Serial.println(url);
  http.begin(url);
  int httpStatusCode = http.GET();
  Serial.print("httpStatusCode:");
  Serial.println(httpStatusCode);
  String body = http.getString();
  Serial.print("Response Body: ");
  Serial.println(body);
  }
}
```

# 動かしてみる
見た目はこんな感じです。
M5StackにつないだD6TをTinkerboardのアルミケースにセロハンテープでくっつけただけですね...

![image0.jpg](https://qiita-image-store.s3.amazonaws.com/0/295893/8bbf2ec9-2c2b-27f8-b8f2-b3137d0ad153.jpeg)

人が横切ったりすると以下のようにスマホのLINEに通知が飛んできます。

<img width="375" alt="image0.png" src="https://qiita-image-store.s3.amazonaws.com/0/295893/4a4527a1-8c4e-2661-60a4-344a63bfa194.png">

ソースコードは[こちら](https://github.com/kymybk/d6t_human_detector)に置いてます。

# おわり
お気づきだと思いますが、まだまだ課題がたくさんあります。。。

- センサの前にずっと立ってたら延々と通知が飛ぶよ！
- Webhookに日本語のパラメータ投げると文字化けするよ！
- **そもそも人じゃなくても反応するよ！**

ただ、こういった特徴的なセンサなどを活用して何か作ろうと思った時の
**「これどうやって使ったらいいんだ...?」**
というハードルを、Groveコネクタやサンプルコードの公開などである程度下げられているのではないかなと思います。

こちらからは以上です。
最後まで読んでいただきありがとうございました！