お片付け応援システムを作る

#はじめに
ものの配置が決まっている場合、使ったら戻すというルールになっていることが多いです。
しかし、ついつい戻すのを忘れたり、面倒だったりするわけです。
そこで、誰かに背中を押してもらえたら、片付けへの意欲がわくんじゃないかという発想で考えてみました。
基本的には子供のお片付けを想定しています。

#作りたいもの
特定の時間に定位置にものがないと判断された場合、「片付けよう」と応援してくれるシステム
#用意するもの
-距離センサGP2Y0E03
-ESP-WROOM32(ESP32-DevKitC)
-RaspberryPi
-スピーカー
#やり方
⒈ESP-WROOM-32に距離センサを接続し、特定の時間に片付け場所から物体までの距離を測定する。
⒉距離が閾値を超えている場合、片付けられていないと判断し、RaspberryPiにHTTP POSTする。
⒊RaspberryPiはHTTP POSTを受けて、「片付けよう」としゃべる。

#完成イメージ
![86CC6E27-C772-470F-966E-CE2FA51CD35F.png](https://qiita-image-store.s3.amazonaws.com/0/140055/b340d83d-f40f-eef8-553c-166240570fd0.png)


#作る
##ESP-WROOM-32の処理
ソースコード全体は[github](https://github.com/mitazet/esp/tree/master/decluttering_system/main)に置いてあります。
ビルドには[ESP-IDF](https://qiita.com/mitazet/items/8a4c36bd9e1e45f463fd)が必要になります。
###センサから距離を取得する
ソースコードは[sensor.c](https://github.com/mitazet/esp/blob/master/decluttering_system/main/sensor.c)です。
距離センサをESP-WROOM-32に接続して、距離の測定を行います。
以前に書いた[距離センサで防犯システムをDIY](https://qiita.com/mitazet/items/abeff4e2671bda763c2f)と同じ接続です。
###RaspberryPiに通知する
ソースコードは[http_request.c](https://github.com/mitazet/esp/blob/master/decluttering_system/main/http_request.c)です。
距離センサで片付けられていない状態を検出した場合、ESP-WROOM-32からRaspebrryPiに対してHTTP POSTを行います。
###片付け状態を確認する
片付け状態は毎日20時に確認を開始します。距離センサから30cm以内に物体がない場合を片付けられていない状態とします。
片付けが行われない場合、1分毎に片付け応援メッセージが流れます。

```c
#include "http_request.h"
#include "sensor.h"
#include "nvs_flash.h"
#include <time.h>
#include <sys/time.h>
#include "esp_log.h"
#include "esp_sleep.h"
#include "esp_wifi.h"
#include "ntptime.h"

#define EMPTY_DISTANCE  30
#define CHECK_HOUR      20

static const char *TAG = "main";

esp_err_t monitor(bool *need_check)
{
    esp_err_t ret;

    ret = reset_sensor();
    if(ret != ESP_OK){
        ESP_LOGI(TAG, "Sensor reset failed!");
        return ret;
    }

    int distance;
    ret = read_distance(&distance);
    if (ret != ESP_OK) {
        ESP_LOGI(TAG, "data_l:No ack, sensor not connected...skip...");
        return ret;
    }

    if(distance >  EMPTY_DISTANCE){
        ESP_LOGI(TAG, "not declutter...");
        http_post_task();
    }else{
        ESP_LOGI(TAG, "decluttered");
        *need_check = false;
    }

    return ESP_OK;
}

void app_main()
{
    ESP_ERROR_CHECK( nvs_flash_init() );
    init_sensor();
    initialise_wifi();

    time_t now;
    struct tm timeinfo;
    char strftime_buf[64];

    ESP_LOGI(TAG, "Time is not set yet. Connecting to WiFi and getting time over NTP.");
    obtain_time();
    // update 'now' variable with current time
    time(&now);
    
    time_t t = now + (3600 * 9);
    localtime_r(&t, &timeinfo);
    strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
    ESP_LOGI(TAG, "The current date/time in JST-2 is: %s", strftime_buf);

    bool need_check = false;
    int check_start_hour = CHECK_HOUR;
    int check_start_day = timeinfo.tm_mday;
    
    while(true){
         //get time
        time(&now);
        t = now + (3600 * 9);
        localtime_r(&t, &timeinfo);
        strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
        ESP_LOGI(TAG, "The current date/time in JST-2 is: %s", strftime_buf);

        if(timeinfo.tm_mday == check_start_day && timeinfo.tm_hour >= check_start_hour){ 
            need_check = true;
            check_start_day++;
        }

        if(need_check==true){
            esp_err_t ret = monitor(&need_check);
            if(ret != ESP_OK){
                ESP_LOGI(TAG, "Device error occured!!");
                return;
            }
        }

        //sleep 1 minute
        esp_sleep_enable_timer_wakeup(1000000LL * 60);
        esp_light_sleep_start();
    }
}
```
ソースコードは[main.c](https://github.com/mitazet/esp/blob/master/decluttering_system/main/main.c)にもあります。
`monitor`関数で片付け状態を検出し、片付けられていない場合、RaspberryPiに通知を行います。
`main`関数ではPowerOn直後にNTPから時刻を取得し、日本時間で毎日20時に片付け状態確認を行います。
省電力のため、デバイスをsleepさせながら、1分ごとに時刻を更新しています。
省電力を考えるなら、ここは毎日20時に起きるようにタイマーを仕掛けたほうがいいと後から思いました。
NTP時刻の取得処理は[ntptime.c](https://github.com/mitazet/esp/blob/master/decluttering_system/main/ntptime.c)です。

##RaspberryPiの処理
###通知を受け取り、喋る処理を実行する
ソースコードは[index.php](https://github.com/mitazet/php_code/blob/master/index.php)です。
PHPでHTTP POSTメッセージを処理し、喋る処理を実行します。
上記は関係ない処理が入っているため、ここでの処理を抜き出すと以下のようになります。

```php:index.php
<?php
function func_cheer(){
    exec('/home/pi/work/jtalk.sh "そろそろ片付けの時間だよ。頑張って片付けよう。"');
    return;
}

if($_POST['text'] == "decluttering"):
    func_cheer();
endif;
?>
```
###RaspberryPiに喋らせる
音声出力はOpen JTalkを使用します。
[JERRY WARE](http://jellyware.jp/kurage/raspi/raspi_speech_synthesis.html)を参考に、インストールと設定を行いました。
スクリプトを実行することでRaspberryPiが喋るようにしておきます。

```bash:jtalk.sh
#!/bin/bash                                                                                                                         
tempfile=`tempfile`                                                                                                                 
option="-m /usr/share/hts-voice/mei/mei_normal.htsvoice -x /var/lib/mecab/dic/open-jtalk/naist-jdic -ow $tempfile"                  
                                                                                                                                     
echo "$1" | open_jtalk $option                                                                                                      
aplay -q $tempfile                                                                                                                  
rm $tempfile  
```
上記の参考サイトと同じです。
デフォルトの男性の声だと体育会系になってしまったので、女性の声に変えました。
####注意点
実験でPHPから実行してみましたが、音が鳴りませんでした。
調べたところ、/dev/snd/のパーミッションが許可されていないようなので、
Apache2をAudioグループに入れておきます。(セキュリティ面はあまり考えていません)

```
sudo vi /etc/group
```

```:group
...
audio:x:29:pi,pulse,www-data
...
```
のようにします。私の環境ではApache2のユーザー名はwww-dataでした。
これでPHPで音声が出力されるようになりました。

#テスト
ESP-WROOM-32 + 距離センサです。
![572EA474-F44A-4737-B22A-77AFA8E6ECFE.jpeg](https://qiita-image-store.s3.amazonaws.com/0/140055/fa075698-fcf9-f5a9-39da-c8952d2787ce.jpeg)


RaspberryPi + スピーカーです。
![BB48B4B4-B683-4D85-BC9B-AAF0E2EB6260.jpeg](https://qiita-image-store.s3.amazonaws.com/0/140055/b6b28419-0475-3ef7-fe02-49e3dcac98a3.jpeg)

まず、片付けられていない状態で確認開始時間を迎えます。(時間はテスト用に変えています)
![15756033-1777-4CEB-8F4E-B2E06FCCC5D9.png](https://qiita-image-store.s3.amazonaws.com/0/140055/95aae898-e7d8-23b7-4cae-36db436051a9.png)

するとRaspberryPiが応援してくれました。
![E21A6BB6-7CB8-4F24-8425-B6D32B327007.png](https://qiita-image-store.s3.amazonaws.com/0/140055/2d40922f-b629-b589-ebea-bec0aaec94a7.png)

よし、片付けだ。人形を元の場所に片付そう。
![DB15D495-A7F4-416F-888A-41F40264DD64.png](https://qiita-image-store.s3.amazonaws.com/0/140055/c5cf9e58-a0d6-4c1a-f30f-9f1d9d733116.png)

何も言わなくなった。
![8F8357AD-84B8-46F9-BF65-47A298395734.png](https://qiita-image-store.s3.amazonaws.com/0/140055/01837bcf-bc74-f923-57d3-6e4721a11f73.png)
ちょっと寂しいので、褒めメッセージを仕込んだ方がいいと思いました。
時間があれば追加します。

#おわりに
何かものを置いておけば、黙ってしまうのが欠点です。