SORACOM版 Raspberry Pi と光センサーで夫の帰宅時間を記録・通知してみた

## はじめに
こちらは[SORACOM Advent Calendar 2021](https://qiita.com/advent-calendar/2021/soracom) 16日目の記事です。
5年ほど前にAWSを使って稼働させていたシステムをSORACOMで構築してみたので紹介します。

当時、下の子を妊娠中でしたが、夫の帰宅が遅く、ワンオペ状態で上の子の世話もしなければいけない状況でした。夫は日が変わってから帰宅することもよくあったので、もっと早く帰宅するように日々要請していました。

ただ、私が娘たちの寝かしつけで寝落ちしてしまうことも多かったので、朝起きてから夫を問い詰めても帰宅時間を胡麻化される状況でした。夢うつつでも夫が深夜に帰宅したのはバレバレでしたが。

そこで夫が帰宅時に部屋の照明を点けるのを利用して、光センサー(CdSセル)で明るさを計測することで夫の帰宅時間を把握するシステムを構築し、運用していました。当時はRaspberry Piで計測した結果をAWS IoT経由でDynamoDBに記録し、後からDynamoDBの記録を見て帰宅時間を推定していました。測定値をグラフで可視化する機能も用意しようと思っていたのですが、途中で力尽きて実現しませんでした。

今回はSORACOM版として、ハードウェアの構成はそのままで、計測結果をSORACOM Harvestに送信するように変更しました。これで当時はできなかった測定値のグラフ化も実現することができました。さらに機能改良して、夜間に部屋の照明が点灯したのを検知すると、Slackに通知をする機能も付けました。これで夫の帰宅時間が簡単に把握できるようになります。

## システム構成
今回のシステム構成はこんな感じでです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81380/53ee6dac-2b8f-9d93-ca97-027d81d13c4f.png)

- Raspberry Pi のGPIOにADコンバーターとCdSセルをつなぎ、定期的に部屋の明るさを計測
- Raspberry Pi は SORACOM Arc でSORACOMプラットフォームに接続
- 明るさの計測結果は SORACOM Harvest に送信（グラフで可視化！）
- 照明の点灯を検知した場合、SORACOM Beam を経由して、Slackに通知

### ハードウェア
- Raspberry Pi 3 (Model B Rev 1.2)
- CdSセル(型番不明)
- 抵抗(10K)
- ADコンバーター(MCP3002)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81380/7464b5ba-00b9-bcc5-464f-c118a0bae0ed.png)

## 構築手順
### SORACOM Arc の準備
ユーザーガイド「[soratun で SORACOM API の認証キーを使用してバーチャル SIM/Subscriber をブートストラップする](https://users.soracom.io/ja-jp/docs/arc/bootstrap-authkey/)」に従い、SORACOM Arc を利用する準備をします。

1. SORACOMユーザーコンソールで、SORACOM Arc用のSAMユーザーの作成
1. soratunの最新版(v1.1.6)をダウンロードし、Raspberry Pi にインストール

    ```bash
wget https://github.com/soracom/soratun/releases/download/v1.1.6/soratun_1.1.6_linux_armv7.tar.gz
tar xvf soratun_1.1.6_linux_armv7.tar.gz
cd soratun_1.1.6_linux_armv7
sudo cp soratun /usr/local/bin
```

1. ブートストラップで設定ファイルを生成

    ```bash
soratun --auth-key-id <keyId-xxx> --auth-key <secret-xxx> --coverage-type jp --config /path/to/arc.json bootstrap authkey
```
1. soratun を実行して、SORACOMプラットフォームに接続

    ```bash
sudo soratun --config /path/to/arc.json up
```

ここで正常に接続できると、SORACOMユーザーコンソールのSIM管理の一覧に新しいSIMが表示されます。他のSIMと区別できるように名前を付けて、新しいSIMグループを作成して割り当てておきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81380/9ee25417-12a1-1e88-4775-b2db8775f4f9.png)

### SIMグループの設定
SIMグループの設定を行います。
別途、通知用にSlackでIncoming Webhookの設定をし、webhook URLの準備をしておいてください。

1. SORACOM Beam 設定で、「HTTP エントリポイント」を選択してエントリポイントを追加
1. 以下のように設定して保存（エントリポイントのパスは任意、転送先にSlackのwebhook URLを指定）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81380/c6136749-34d4-2d15-b341-3ab749b9c087.png)
1. SORACOM Harvest Data 設定で、SORACOM Harvest Dataを有効化

### 光センサーの値を読んで、SORACOMに送信

1. Raspberry PiでSPIを有効化して、再起動

    ```bash
sudo raspi-config
# raspi-configの設定画面が表示されるので、SPIを有効化
# 1. 「Interface Option」を選択
# 2. 「SPI」を選択
# 3. SPI Interface を有効化
sudo reboot
```

1. Python でSPIインタフェースを扱うため、[spidev](https://pypi.org/project/spidev/)をインストール

    ```bash
sudo pip3 install spidev
```
1. プログラムを用意

定期計測した結果をSORACOM Harvest Dataに送信します。
照明点灯判定時は、SORACOM Beamに送信したメッセージがSlackに転送されます。
光センサーの読み取り部分は、[こちらの記事](https://tekitoh-memdhoi.info/views/745) を参考にしました。

```python:recorder.py
import spidev
import time
from datetime import datetime
import requests

HARVEST_URL = 'http://harvest.soracom.io'
BEAM_URL    = 'http://beam.soracom.io:8888/slack'

interval  = 60 # 測定間隔(秒)
threshold = 150 # 照明ON判定の閾値

# slackに通知する時間帯(21時～6時)
start = 21
end   = 6

# SPI初期化
spi = spidev.SpiDev()
spi.open(0, 0) # CE0
spi.max_speed_hz = 1000000 # 1MHz

def send_data(data):
    res = requests.post(HARVEST_URL, json=data)
    if res.status_code != 201:
        print('Failed to send data: ' + res.status_code)
        print(data)

def is_between_notify_time():
    h = datetime.now().hour
    if start <= end:
        return start <= h and h <= end
    else:
        return h <= end or start <= h

def notify_light_on():
    payload = {"text": "Detect light ON"}
    res = requests.post(BEAM_URL, json=payload)
    if res.status_code != 200:
        print('Failed to notify slack: ' + res.status_code)
        print(payload)

last_val = None
try:
    while True:
        # 光センサーの値を読む
        resp = spi.xfer2([0x68, 0x00])
        val = ((resp[0] << 8) + resp[1]) & 0x3ff
        
        # SORACOM Harvest Dataに送信
        send_data({'time': datetime.now().strftime('%Y/%m/%d %H:%M:%S'), 'cds': val})

        # 前回の測定値との差異が閾値以上の場合、照明点灯と判定
        if last_val is not None and val - last_val > threshold:
            # 通知対象の時間帯の場合、照明点灯通知
            if is_between_notify_time():
                notify_light_on()

        last_val = val # 測定値を保持
        time.sleep(interval)

except KeyboardInterrupt:
    spi.close()
```

最後はプログラムを実行！

```bash
sudo python3 recorder.py
```

SORACOMユーザーコンソールの「データ収集・蓄積・可視化」メニューから「SORACOM Harvest Data」を選択し、リソースでRaspberry Piに紐づいている仮想SIMを選択してグラフが表示されたら成功です。

## 動作確認

試しに一日中、明るさを記録してみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81380/8842b1e7-b9c6-e171-fae0-c56c99f1f8fc.png)

SORACOM Harvest Dataでグラフを確認してみると、日中に部屋がだんだんと明るくなる様子や、朝と夜間の照明状態がばっちり記録されています。

定期計測の実験がうまくいったので、今夜はこれを使って夫の帰宅時間を判定してみましょう。Raspberry Piを設置し、照明を落として待ち伏せです。

待つこと数時間、やっと帰ってきました。グラフにもしっかり反映されています。
たまたまですが、夫の帰宅がいつもより飛び抜けて遅かったので、良いデータが取れました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81380/2ddb7bc6-5e71-ea59-8562-5e8fe8217f99.png)

Slack通知も無事成功！
だけど、帰るのが遅過ぎないか！？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81380/d3f24842-276c-9f67-fd72-9679f6718f28.png)

## あとがき
- SORACOMのサービスを利用することで、簡単にセンサーの計測値を記録、可視化することができました。データを送信するだけでグラフまで表示できるのはとっても便利です。
- Raspberry PiならSlackに直接通知を送ることも簡単ですが、SORACOM Beamを使用することで通知先をSORACOMユーザーコンソールから簡単に変更できるのが良いなと思いました。
- ずっと気になっていた SORACOM Arc もついに試してみました。Wi-Fi環境でもSORACOMのサービスを利用できるようになったのは嬉しいです。
- 久しぶりに夫の帰宅時間を記録してみましたが遅い！はよ帰ってこい！