いかにしてSlack botが駐輪場のシャッターを開けるようになったか（raspberry pi + サーボ + slack botでシャッターを開ける）

# まとめ

- 駐輪場のシャッターを開けるのに、ビルのエントランスから入ってボタンを押さないといけなくて面倒だったのでSlackでつぶやくとbotが開けてくれるようにした


![kekka.gif](https://qiita-image-store.s3.amazonaws.com/0/54356/5a6e1bcc-3079-de45-0d1a-72bcf7516f9a.gif)


会社の駐輪場は、恐らく車庫として使われてた場所を流用しているのですが、シャッターを開けるためのスイッチが車庫内にしかなくて、自転車を入れるためにはいったんビル内に入ってから中から開ける必要があり面倒でした。

会社の先輩の発案で、「このボタンをサーボで押せればいいんじゃね？」ということでサーボでボタンを押すようにして、ビル内に入らなくても外からシャッターを開けられるようにしました、というお話です。

ちなみに外にテンキーで開けられるスイッチを付けてもらうと数十万円、みたいな見積もりだったそうな。。

## こういうよくあるボタン。これを押すだけ

![image](https://qiita-image-store.s3.amazonaws.com/0/54356/0ff3d5e7-783c-8d8b-5ea5-d78dcca81067.png)

# アーキテクチャ

## ざっくりした設計

ほんとに最初に書いたざっくりした設計だけど、実際に運用に入ってもほぼ構成はこのままでした。
ざっくりと紙に構成を書いて、その筋に詳しいメンバーにもレビューしてもらっています。

そんなに複雑なことはしてなくて、共通のDBに書かれたメッセージを見て動作する、ただそれだけです。

![image](https://qiita-image-store.s3.amazonaws.com/0/54356/4e6662a5-9696-39f3-cbc9-43a2a28ae2b0.png)

## ドキュメント

普段のプロジェクトでやるような、簡易なドキュメントを作りました。
いつも使ってるフォーマットでドキュメントを書くと、やること・やらないことが整理できるのと、同じフォーマットで長い間運用されているのでレビューもしやすい、と言ったメリットがあります。共有もしやすいし、引継時にもこれがあるとだいぶ楽です。

![image](https://qiita-image-store.s3.amazonaws.com/0/54356/32795564-7b3a-1ed5-cb7b-69ee50080e63.png)


## プロトタイプ

いくつか段階を踏んで開発をする必要があって、下記のようなステップに分けて開発し、それぞれを結合していくという進め方をしました。

1. サーボを動かすコード+ハード raspberry pi
2. Slackとraspberry pi間で情報をやり取りするDB
3. Slack bot側のコード

ソフトならなんとでもなるという感覚はあるので、最も難度が高そうな１のハードの構成と開発から着手しました。
プロトタイプのときは、当時いろいろ使ってみたい技術やサービスがあっていろいろナウなイケてるものを盛り込んで作ろうとしていました。

![image](https://qiita-image-store.s3.amazonaws.com/0/54356/1bf953e4-6bef-9929-814e-8f745223ffe3.png)


### サーボを動かすコード+ハード raspberry pi

```py
#!/usr/bin/python

from Adafruit_PWM_Servo_Driver import PWM
import time

import requests

import sys

import datetime

time.sleep(10)

# Initialise the PWM device using the default address
pwm = PWM(0x40)
# Note if you'd like more debug output you can instead run:
#pwm = PWM(0x40, debug=True)
pwm.setPWMFreq(60)

servoMin = 150  # Min pulse length out of 4096
servoMax = 600  # Max pulse length out of 4096

print("start")

def on_push(e):
        print e
        if e["value"]["message"] == "open":
                # handle open shutter here
                push_button()
                datastore.push({"message":"done"})

def push_button():
        # 0
        print("0 deg.")
        pwm.setPWM(0, 0, servoMax)
        time.sleep(1)

        # 180
        print("180 deg.")
        pwm.setPWM(0, 0, servoMin)
        time.sleep(1)

        # 0
        print("0 deg.")
        pwm.setPWM(0, 0, servoMax)
        time.sleep(1)

        print("stop servo")

timeCount = 0
while(True):
        print("waiting..")
        time.sleep(1)
        timeCount = timeCount + 1


        print datetime.datetime.now().time()
        if datetime.datetime.now().hour > 19:
                sys.exit()

        try:
            r = requests.get('http://data.sparkfun.com/output/xxxx.json')
        #print(r.json())
        if r.json()[0]['message'] == 'open':
            push_button()
            r = requests.get('http://data.sparkfun.com/input/xxxx?private_key=&message=done&user=pi')
        except Exception, e:
        print e, 'error'
```

携帯電話の組込みソフトウェア開発を４年ほどやっていたこともあるので、簡単な電子回路なら問題がないのだけど、サーボをいじったことがまったくありませんでした。。まずはサーボがどういうものかに慣れるために、サーボを買ってきていじりながら感覚を掴んだ、と思っていたのですがサーボは他の部品と比べて

- 個体差が大きい
- 電圧などの回路要件が厳しい

というのに全く気づいてなくてドハマリしました。

- 個体差が大きい

個体差が大きい、と言うのは複数同じ型番のサーボを買ってきたにもかかわらず、同じコードで同じ回路構成でサーボが動いたり動かなかったりする、というのに悩まされたり、冬に開発をしていたので、室内だったら問題なく動く個体が駐輪場（ほぼ外気温）に持っていくと動かなかったりと、ハードらしいトラブルが続出しました。

- 電圧などの回路要件が厳しい
 
ソフトはすぐに書けたのですが、安定して動作させるのが難しくて、結局はRasbperry piからIO直結だと駆動させる電流が不足することが原因で、サーボドライバーボードを挟むことで安定して動作するようになりました。試した段階でIO直結で問題なく動いていたので油断していて、サーボの個体によって瞬間的に流れる電流が違っていたりするというのに思い当たらず苦労しました。

問題点の切り分けがなかなかできなくて、オシロスコープを買ってSPIの波形をチェックしたりしてみたのだけど、結局は電圧・電流の不足で詳しい人が見ればすぐわかったのだろうと思いますが、そういうノウハウがありませんでした。サーボが動かなかったりして苦労している人はサーボドライバボードを使うのがおすすめです。
少なくとも電圧・電流絡みの問題は切り分けが簡単にできて、ソフトの制御の問題だけに集中できます。サーボドライバボードが微妙に高くて（３０００円ぐらい）、動いてるしいらないだろう・・・とケチったのが間違いでした。金で解決できるところは金で解決したほうが結局安くあがるというのが教訓です。

## 最終的な構成（現在はこの構成で運用中）

![image](https://qiita-image-store.s3.amazonaws.com/0/54356/c0ad0cc6-a34d-3c8e-b95d-6063df34f1a3.png)

![image](https://qiita-image-store.s3.amazonaws.com/0/54356/a69e9c4e-a3d0-84fb-f2a5-c5bd8908e23f.png)


いろいろと深い挫折や妥協の結果、この構成で半年ほど運用していました。
ネットはDocomoのモバイルルータを使って金で解決して、rasbperry pi 〜 slack間のメッセージのやりとりも愚直にポーリングして監視しています。MQTTもカッコイイんですが、簡単なことを簡単にやるには大仰でした。

phanto.ioはsparkfunが運用してるIoT向けのDBaasで、簡単なkey/valueの読み書きがHTTP経由でできてとても便利です。難点はパフォーマンス的に遅い時があるのと、あまり安定していないことなのですが、GETで読み書きができるデータストアとしてとても使いやすいので採用しました。

## セキュリティ

セキュリティ面で最も避けたかったのは、

- 意図しないタイミングでうっかりシャッターが開いてしまうこと

で、これは設計当初からうっかり開かないような仕組みを入れていました。

会社の駐輪場ですが、自転車が好きな人は1台xx万円みたいな自転車を止めていて、うっかり開いて自転車が持っていかれるとダメージがでかいので安全側の設計をしています。

例えば通信エラーや、現在の開閉ステータスが不明な場合は、必ず開かない方に倒すようにする、という当たり前のフェイルセーフ設計を各所に入れているだけですが、意図しないタイミングで開いてしまうことはこれまで起きていません（追記: 2016/12に意図しないタイミングで開いてしまうことがあり、回避できないので運用をやめました)。

あとは気休めですが、raspberry piなので動体検出してカメラで撮って記録する、というもパワフルで簡単にできるのでやってあります。事が起きたあとにしか役に立ちませんが、、

## パーツなど（と使ったお金）

- [ＲａｓｐｂｅｒｒｙＰｉ３　Ｍｏｄｅｌ　Ｂ　（ラズベリーパイスリーモデルビー）　ＲＳ版　日本製: マイコン関連 秋月電子通商 電子部品 ネット通販](http://akizukidenshi.com/catalog/g/gM-10414/)
- [Amazon.co.jp： BUFFALO 11n対応 11g/b 無線LAN子機 親機-子機デュアルモード対応モデル WLI-UC-GNM2: パソコン・周辺機器](https://www.amazon.co.jp/BUFFALO-11n%E5%AF%BE%E5%BF%9C-%E7%84%A1%E7%B7%9ALAN%E5%AD%90%E6%A9%9F-%E8%A6%AA%E6%A9%9F-%E5%AD%90%E6%A9%9F%E3%83%87%E3%83%A5%E3%82%A2%E3%83%AB%E3%83%A2%E3%83%BC%E3%83%89%E5%AF%BE%E5%BF%9C%E3%83%A2%E3%83%87%E3%83%AB-WLI-UC-GNM2/dp/B005DU4XSM)
- microSD card
- USB給電のためのアダプタ
- USBケーブル
- 電源延長ケーブル
- [マイクロサーボ９ｇ　ＳＧ－９０: サーボ 秋月電子通商 電子部品 ネット通販](http://akizukidenshi.com/catalog/g/gM-08761/)
- [Adafruit 16チャンネル PWM/サーボ HAT for Raspberry Pi - スイッチサイエンス](https://www.switch-science.com/catalog/2181/)
- USBカメラ
- 開閉センサ
- ジャンパケーブル

合計2万円ぐらいでしょうか。
raspberry piほどパワフルじゃなくてもarduinoだって十分にできる内容なのですが、
開発とメンテナンスの簡便を考えると十分にペイすると思います。

# 開発していくなかでボツにした技術・サービスなど

なぜ、使わなかったのか、という理由って意外と大切だと思うので開発段階で使ってみたけど採用しなかったものを挙げておきます。今回の用途にはフィットしなかったというだけなので、プロジェクトの性質によって合う合わないはあるとおもうので試してみて下さい。

## milkcocoa

https://mlkcca.com/

milkcocoa、すごい好きなんですが、ハマった時に情報が少なくて自分だけしかはまってる人がいない状態になるのが辛くて使うのをやめました。

シンプルで、データを入れたい、見たい、グラフにしたい、という用途が明確でわかりやすいので使いやすいです。
プロトタイプとして使う分には良いのですが、長期間それなりに安定して稼働させようとするとちょっと不安があるかな、という感覚でした。今は変わっているかもしれません。

## AWS/IoT MQTT

ちょうどAWS/IoTが出始めだったので早速飛びついて使ってみたのですが、どうにもやりたいことに対して大仰で、簡単なことを簡単にやりたい場合にはリッチすぎてむしろ使いづらいな、という印象でした。

ただデータをkey/valueで読み書きしたいだけで、リアルタイム性もそんなにいらない（秒単位で良い）ので不採用にしました。

証明書ありの通信とかそこまでやらなくても良いということもあり、クライアント側それぞれにもライブラリが必要で、セットアップが大変だったのもボツにした理由です。

## SORACOM

こちらも少量のパケットしか流さないし、１日１０円で使える！と思って飛びついたのですが、１時間は安定して動くのだけど２日ぐらい経つと回線が切れたりして苦労しました。USBのドングルでrasbperry piから直接繋いでいたのですが、結局モバイルルータにSIMを指してWifi経由でつなぐようにして安定しました。

従量制なので、制御にバグがあってリトライ無限ループしてパケ死しそうになりましたｗ
パケ死しそうになってもAlertを適切に設定してればすぐに気がつけるようになっています。

SORACOM、すごく良くできていて、やりたいことわかってるなぁ、と感じます。速度の調整がコンソールからできたり、回線切断ができたりと、遠隔に設置した機器の制御に欲しいものが揃っています。

# 失敗の数々

失敗こそなかなか共有されないけれど、共有するのが価値があることではないか、と思っているので列挙します。

- サーボが動かない→駐輪場寒すぎ？室内だと動く・・・
- サーボが動かない→ソフトは正しいはず。。信号を見るためオシロスコープ購入（自腹）
- サーボが動かない→電圧足りてなかったので専用のボード購入して金で解決
- サーボが動かない→サーボの不良品だったので翌日昼休みに秋葉原行って解決
- サーボのホーン(棒のトコロ)がボタン押すの強すぎて折れる
- サーボが押しすぎて外れる → 角度の微調整と強力な両面テープにした
- そもそも駐輪場が圏外  → 新しいモバイルルータ買った（自腹）
- 雑誌についてた0円SIM使ってたら3hで切断される→soracomにした→パケ死
- 再送処理入れたらsoracomは従量課金なので今時パケ死→古塚さんにドコモ買ってもらった
- ボードをショートさせて壊した → 翌日秋葉原行って解決
- USBの電源も壊れた
- まだ動いてないのに[取材されて紹介されてた](https://japan.zdnet.com/article/35081959/18/)
- slack botのサーバを移行したら動かなくなった → コード書き直した
- milkcocoaとかAWS IoTっていうイケてるナウいサービス使ってたら事例がなくて詰んだ→実績あるサービスへ移行

学び: モノがあると試行錯誤の1イテレーションに時間がかかる。考える時間を長く取るよりも、まずは作ってみるのが大切

# 運用開始その後

2016/3から運用を開始して、大きな問題もなく2016/11月まで運用してきました。
1日に20-30人ぐらいが出社時に使用していて、それなりの頻度で使われていた割に安定していたのではないかと思います。

が、2016/11月頃に、DBaasとして使っていたphanto.ioが瞬断を何回か繰り返したり、タイムアウトになったりすることが増えてきて、想定外のタイミングで一度開いてしまったことがあったので運用をやめました。

完全にダウンしてしまうエラーは想定していたのですが、断続的にデータを書けたり読めたりを繰り返す状態になるのは想定していませんでした。

- slack側から開ける指示”open” を書く
- シャッター側で最新の指示が”open"だったら開ける
- 成功したら”done”をシャッターが通知
- 時間内に応答がなければslack botが”fail”を書く
というようにしていたのですが、slack botが応答してる限りはfailが書き込めるのでシャッター側が開けようとはしませんでした。

通知のやり取りをしてるDBaasがダウンしていて、
slack bot側が”fail”を書けない && シャッター側も”done”を書けない、で最新の指示が”open”のままになってしまって、DBaasが時折復旧してシャッター側が”open”を見て開けちゃう、という不具合です。

phanto.ioが有料でもいいから安定してくれればそのまま使えたのですが、無料のものしかなくてSLAは期待できないのでやむなく運用を止めました。

![image](https://qiita-image-store.s3.amazonaws.com/0/54356/af359a6e-bb2b-b353-669f-ce9f6c46c0cc.png)


## 2022/08/15

その後、Pub/Sub形式のアーキテクチャに変更して、再度運用を開始、5年近くに渡り何度かサーボ故障はしたもののそれなりに安定して運用しています。


# ボタンを押したい、既製品を使うなら

既製品もパラパラと出ているみたいです。普通の電気のスイッチみたいなやつなら既製品を使うほうが楽そう。
楽そうだと思っていたら既製品はそれはそれで苦労があるみたい↓

[物理的にボタンを押すIoT、Prota Pushが来た！が…導入解説 - Weed.nagoya：便利ツールでネット活用](http://www.weed.nagoya/entry/2016/12/17/082142)

# 今後の展望

DBaasの置き換えをすれば運用再開できるのですが、手が回らず同僚 @teitei_tk がまるっと作り直してくれるプロジェクトを進めてくれていますので期待！