こんにちは、さくらインターネットの菊地です。
さくらインターネットにARCADE1UPのギャラクシアン/ギャラガの筐体がやって来てそして改造された経緯については、先日、弊研究所の鷲北が紹介しました(記事はこちら)。ゲームは好きですがシューティングは苦手な菊地はただ暖かく見守っていたのですが、当該記事内で鷲北(以下、所長)が「受付に置きたい」という話をしており、びっくり仰天しながら「であればリモートメンテナンスできないとだめですね」という展開となり、この度、この筐体をコネクティッドにする計画を始めました。ということで、おそらく世界初?のIoT化したギャラクシアンがとりあえず完成致しましたのでその詳細をご紹介いたします。
要件の抽出
弊社の受付に置くということで、リモートでの電源のOn/Off機能は必須です。所長や私のデスクは受付と離れており、毎朝毎晩電源を入れたり切ったりしに受付に行くのはとても面倒だからです。そもそもリモートワークを推奨する弊社方針とも反します。場所依存性は極力排除したいのです。
また、目の届かないところに置き、来社されたお客様に遊んで頂くわけですから、なにかトラブルがあったときにはすぐに対処できるようにしなければなりません。サービスコールボタンを増設し、それが押されたらすぐわかるように(サービスマン(=我々)がすぐに駆けつけられるように)します。
さらに、せっかくコネクティッドにするのですから、プレー状況を知りたいという欲が出てきます。データを取りたいのです。ギャラクシアン/ギャラガの場合、操作は自機を左右に動かすのとショットボタン(FIREボタン)しかありません。白熱したプレー状況を把握できるように、今回はFIREボタンの押下数を把握できるようにします。
というようなことを所長と話しながら、以下のように要件を抽出しました。
- リモートで電源をOn/Offできるようにする。マスター電源のように、筐体についている電源SWの状態を上書きしてOffにできるようにすること。
- サービスコールボタンを設置し、ボタンが押されたら社内Slackに通知されるようにする。また、コールボタンを押したことでサービスマン(=我々)に通知がなされたことが押した人にわかるように、何らかのフィードバック手段を実装すること。
- FIREボタンの押下数を取得可能にすること。また、このデータは(将来的に)公開できるようにすること。
システム構成
早速、要件を満たすシステムを設計していきます。
メインの制御ノードにはRaspberry Piを使うことにします。今回の内容であればESP32あたりでも作れそうな気がしますが、ネットワーク側との親和性と、将来的な拡張性を考慮してRaspberry Piを採用します。(余談ですが、菊地はエッジノードの実装の最右翼はRaspberry Piだろうと考えています。)
ソフトウェアはPythonで書きます。PythonであればSlackへの通知やデータ(FIREボタン押下数)の通知・公開なども難しくありません。(このあたりは菊地がある程度知っているからという経験に基づくところなので、より良い実装が他にあるかもしれません。)
次に、ARCADE1UPのハードを調べます。
そもそも菊地は、ARCADE1UPは実はRaspberry Piで動いているのではないか?とすら考えていました(ただ、起動が早いので違うかな、とも)。そんなわけで、テスターでFIREボタンの端子のところの電圧を測ってみると、3.3V、これは期待できます。3.3Vなら、同じ3.3V系のRaspberry Piで信号を読み取ったりあるいは制御するのに都合が良いのです。
続いて電源SWです。これも3.3Vで同様にいけるかもと期待していたのですが、残念ながらこれは12V。電源自体をこのSWで直接On/Offしていることがわかりました。ここはリレーを使う必要があります。
ハードウェアの最後のサービスコールボタンですが、これはARCADE1UPとは全く独立な系なので、その辺に転がっているSW(ボタン)とフィードバック用に適当なLEDを使うことに決めました。
概ね決まったので、回路図を書きます。(なお、当方、かつてきちんと回路図の書き方を習ったはずなのですがすっかり忘れいているので、図は適当です。)
配線の色分けは、12V系が紫、5V系が赤、3.3V系(制御信号系)は黒、GNDが青です。Raspberry PiおよびArduino Nanoの未接続配線が赤くなっていて5V系の線とパッと見が紛らわしくてすみません。
これは完成したあとにFritzingを使って清書した回路図なのでキレイですが、作成前の設計時に書いた回路図は、実はこんな感じです…。
回路を設置した様子。
以下、詳しく説明していきます。
※ ここからの改造は製品に直接変更を加えます。私は正規の保証が受けられないことを承知で実施しています。なお当記事を参考に同様の改造を行って生じるいかなる事象について、私は責任を負いませんのであしからずご了承ください。
リモート電源操作
Raspberry Piは12Vはもちろん制御できないため、リレーを使います。ところがRaspberry PiのGPIO(3.3V)ではリレー自体すら駆動できません(リレーの駆動に5V必要)。一般的には、トランジスタを使って、3.3Vの制御信号をもとに5Vをスイッチングする回路を作ります。ですが、そんな難しい回路をソフトウェア技術者の菊地はもちろん書けません。ところが便利なものがあって、そのリレー駆動回路をすでにキットにして売っているのです、秋月電子通商で。(大電流大型リレーモジュールキット 5V版)
下記の回路図は上で示した全体回路図の電源操作部の抜粋ですが、その網掛け部分は秋月のリレーキットであらかじめ作り込まれている部分です。ですので実際の回路はすごく簡単です。
そもそものARCADE1UPの本体と筐体の操作パネルの電源SWとの間の接続のXHコネクタに、このリレーの被制御側回路を直列になるように挿入します。リレーの制御側にRaspberry Piからの5V、GPIO8ピン、GNDを接続するだけです。GPIO8ピンは任意の番号で構いません。
なおこのリレーはC接点タイプなので、被制御側はノーマリークローズ側に接続しておけば、万が一制御系のRaspberry Piに不調があって電源が入らなくてもARCADE1UP筐体には電源が入ります。
(余談ですが、当初この部分は、リレーではなくSSR(ソリッドステート・リレー)を使っていました。SSRであれば5Vの電源供給すら不要で、3.3Vの制御信号の入力だけでターゲットを制御できるからです。ところが実際にやってみると電源が入りっぱなしになってしまい意図したとおりに動きません。調べたところ、(普通の)SSRでは直流は制御できない(交流でないとだめ)、ということがわかりました。素人の浅知恵ではだめですね。勉強になりました。なお秋月のSSRのキットはこちら。)
サービスコールボタン
サービスコールボタンは、当初、その辺に適当に転がっているボタン(=前回記事で取り外したボタン)を使うつもりでした。しかしそのボタンは実に単なるボタンのため、ボタンを押したことに対してなにかフィードバックを返さないと押したか押してないかがわからず、それはつまり、もう何度も押されてしまうことになるに決まっています。フィードバック方法をどうしようかと思っていたところ、弊社のsakura.ioのチームが「LEDが内蔵されてて光るボタンが余っているから、使っていいですよ。」と言ってくれました。
というわけで、これを使います。まさにうってつけです。
ですがこのボタンのLEDは5V駆動です(LEDは普通そうです)。実際には3.3Vでも光る事が多いのですが、試してみると、蛍のようにホンワリとしか光らないのでサービスコールボタンのフィードバックとしては用をなしません。
Raspberry PiでLチカ(LEDをチカチカさせるという、Raspberry Piの入門記事などでよくあるもの)では、このあたりにあえて?触れず3.3Vで駆動させ、とりあえず点灯したことで良しとしているようなものも多くありますが、一般のお客様の目に触れる今回のような用途でその様なごまかしは許されません(すみません、本当はただかっこよくしたかっただけです。後述)。
3.3V系のRaspberry Piで5Vを制御する場合、リレーのところでも触れましたがトランジスタを使って5Vを制御することが一般的です。ですがもちろんそんな難しい回路は回避して、ソフトウェア技術者らしくソフトウェアで対応することにします。
Arduino (Nano)は5V系のマイコンです。5Vをソフトウェアで制御することができます。しかも、5V出力をPWM制御でアナログ的に扱う機能もついています。これを使うと単にOn/Offするだけでなく、LEDの明るさを段階的に制御することもできるのです。今回はこれを使ってボタンのLEDをかっこよく制御することにしました。(完全に仕様外のオマケ機能です。)
ということで回路図はこちら。
LEDに対してArduinoのD5ピンから配線しています。Arduinoには、Raspberry Pi側から5VおよびGND、そして制御信号用に2ビット(D2、D3)を配線しています。ボタンの配線はArduinoに入らず、そのままコネクタを介してRaspberry Pi側に通しています。
Raspberry Pi側からのGPIO 2ビットの組み合わせ(High/High、High/Low、 Low/High、Low/Low)で4種類の状態を伝達できます。これをそれぞれ、LEDの全点灯、点滅、ゆっくり点滅、消灯、に割り当てます。ArduinoのD2、D3ピンの状態が変化したらそれに応じてD5の出力を変更する、というコードを書きました。
ちょっと冗長ですが、消灯状態から点灯→消灯→点滅→ゆっくり点滅→消灯、と変化させています。
(余談です。今回ArduinoではD2、D3を割り込みに、D5をPWM出力に使用していますが、Arduinoでは割り込みやPWM出力に使えるピンが限定されている(しかも機種によって異なる)のです(今回使ったのはArduino Nano)。回路作成時にこれを失念しており、はんだ付けを2回もやり直す事になってしまいました(回路図も書き直した)。直交性のないアーキテクチャはこれだから困るのです笑。Arduino使われる方はご注意下さい。)
というような具合で右往左往しましたが、最終的には、トランジスタを使わずにソフトウェアで5VのLEDをかっこよく制御することができました。
(再び余談です。Raspberry Piは3.3V系、Arduinoは5V系と書きました。そのためRaspberry PiのGPIOをそのままArduinoのピンに入力するとHighレベルの電圧が合いません。ここは本当はレベル変換回路 (ICなど)を使うべきところです。ですが、Arduinoは3.3VをHighとして認識してくれるのでそのまま直結しています。動けばいいのです(さっきと言ってることが違う気が…)。なお、これが逆の場合(Arduinoの出力をRaspberry Pi側で読む場合)はRaspberry Pi側に5Vが掛かることになりRaspberry Piが壊れますので対策が必要です。)
FIREボタン監視
FIREボタンはごく単純に、ARCADE1UPの本体と筐体の操作パネルのFIREボタンとの間の接続のXHコネクタに、線が分岐するように配線を割り込ませます。分岐した線はRaspberry Piの GPIO14に入力します。GPIOは何番でも構いません。
FIREボタンはプルアップされており、FIREボタンが押されていないときは3.3Vに、FIREボタンが押されるとGNDとつながって0Vになります。これをGPIO14を見ることでボタン押下を検出します。
Slackへの通知
ハードウェアについては説明しましたので、ソフトウェアのパートに入ります。
サービスコールボタンが押されたらSlackに通知をするのは非常に簡単で、サービスコールボタンが接続されているGPIO18を監視し、HIGHからLOWに変化(FALLING)したら割り込みハンドラでWEB_HOOK_URLに対してHTTPでメッセージを投げます。予めSlack側でWEB_HOOK_URLを構成しておく必要があります。
# GPIOピン PIN_EMERGENCY = 18 PIN_LED_ON = 23 PIN_LED_RESPONSED =24 # -- snip -- # slackへの出力 def send_to_slack(msg): try: requests.post(WEB_HOOK_URL, data = json.dumps({ 'text' : msg, 'username': u'arcade1up_watcherbot', 'icon_emoji': u':smile_cat:', 'link_names': 1, })) except: pass return # エマージェンシーボタン処理 def emergency_callback(self): time.sleep(0.05) #print("エマージェンシーボタン押下") # LEDを点滅させる GPIO.output(PIN_LED_ON, GPIO.HIGH) GPIO.output(PIN_LED_RESPONSED, GPIO.LOW) # Slackへ通知 msg = u'エマージェンシーボタンが押されました。\n' msg += u'<http://' + str(server_addr) + u':' + str(server_port) + u'/breath_LED |応答 >' msg += u' or ' msg += u'<http://' + str(server_addr) + u':' + str(server_port) + u'/clear_LED |クリア > ' send_to_slack(msg) return #- snip - # ボタン入力検出用にイベントハンドラをセット GPIO.add_event_detect(PIN_EMERGENCY, GPIO.FALLING, bouncetime=500) GPIO.add_event_callback(PIN_EMERGENCY, emergency_callback)
今回は、メッセージ内に、下記で説明するサービスコールボタンのLEDの点灯状態を変更するWebAPIを叩くURLを埋め込んであります。サービスコールボタンが押された事に気がついたサービスマン(我々)が「今から向かいます」という状態を示すことを意図したものです。
WebAPIの実装
ソフトウェアの最後はWebAPIです。リモートでの電源のOn/Off、サービスコールボタンのLED状態の変更、そしてFIREボタンの押下回数はWebAPIで操作・取得できるようにしています。WebAPIは、PythonのWebアプリ(API)のフレームワークであるFlaskを使用しています。
# Flaskの初期設定 app = Flask(__name__) auth = HTTPBasicAuth() @auth.get_password def get_pw(username): if username in users: return users.get(username) return None @app.route('/clear_LED') @auth.login_required def clear_LED(): GPIO.output(PIN_LED_RESPONSED, GPIO.HIGH) GPIO.output(PIN_LED_ON, GPIO.HIGH) return response_body @app.route('/turnon_LED') @auth.login_required def turnon_LED(): GPIO.output(PIN_LED_RESPONSED, GPIO.LOW) GPIO.output(PIN_LED_ON, GPIO.LOW) return response_body @app.route('/blink_LED') @auth.login_required def blink_LED(): GPIO.output(PIN_LED_RESPONSED, GPIO.LOW) GPIO.output(PIN_LED_ON, GPIO.HIGH) return response_body @app.route('/breath_LED') @auth.login_required def breath_LED(): GPIO.output(PIN_LED_RESPONSED, GPIO.HIGH) GPIO.output(PIN_LED_ON, GPIO.LOW) return response_body @app.route('/boot') @auth.login_required def boot_arcade1up(): GPIO.output(PIN_MAINSW, GPIO.LOW) return response_body @app.route('/down') @auth.login_required def shutdown_arcade1up(): GPIO.output(PIN_MAINSW, GPIO.HIGH) return response_body @app.route('/fire_count') def get_fire_count(): return str(fire_upcount) + b'\r\n'
実際のAPI操作はこんな具合です。
PC103429:~ s-kikuchi$ curl -u USER:PASS -X GET http://arcade1up.xxxxxx.jp:8001/down OK PC103429:~ s-kikuchi$ curl -u USER:PASS -X GET http://arcade1up.xxxxxx.jp:8001/boot OK PC103429:~ s-kikuchi$ curl -u USER:PASS -X GET http://arcade1up.xxxxxx.jp:8001/fire_count 42 PC103429:~ s-kikuchi$
このようにURLにHTTPリクエストを送信することで、ARCADE1UPの電源をOn/Offしたり、FIREボタンの押下数(1分ごと)を取得することができます。なおWebAPIにはいたずら防止のため認証を付けています。
リプライのメッセージは本来HTMLで記述しなければならないのですが、ごく簡単なメッセージ内容で十分なため、今回は省略しています。
完成動画
WebAPI経由で電源On/Offできているところをご覧ください。
さいごに
というわけで、受付に置くために最低限必要な機能を実装することができました。現在、ランニングテスト中です。やはりというか、不具合が出るので細かく直していっています(操作パネルを筐体にネジ止めすると電源が不安定になるとか。これは自前増設したケーブルの被膜内の断線が原因でした)。
実際にはRaspberry Piにも電源を供給するための配線の手直しや、安定して可動させるための調整なども実施していますが、それは今回の記事を超えるので別項に改めたいと思います。
ブレッドボード試作のレベルを超える、かっちり?したものを作ったので今回は大変勉強になりました。
おまけ
完成したIoTギャラクシアンを前に、所長と盛り上がりました。
所長:「FIREボタンの状態が取れるということは、逆にそれを制御することもできるんだよね?」
菊地:「原理的には可能ですね」
所長:「方向レバーも仕組みは同じなんだよね?」
菊地:「そうですね」
所長:「ということは、AIで学習させて超絶オートプレイとかできるんだ...」
菊地:「...」
ということで、さくらインターネット研究所の技術探求の旅はまだまだ続きます…?
(注:回路に詳しい方は一見しておわかりかと思いますが、今回作成した回路ではFIREボタンは読み取り専用になっていますので、Raspberry Piでソフト的に書き込みに設定するとRaspberry PiおよびARCADE1UP本体がショートで壊れます。これは、この展開を見越して安全対策をしておくべきでした。日々経験ですね…。)