今回のサーバーとクライアントを実装する際に大事にしたいことは、拡張性です。その理由は、エアコンの他に照明や扇風機などの家電を追加したり、家電が本当に稼働しているかを確認するために電力メーターを追加するなど、これから先も機能拡張する可能性が高いためです。
クライアント部分の拡張性を担保するため、コンポーネント化を実現しているAngularJSを使います。AngularJSを内包するMEANスタックならば、クライアントとの双方向通信だけではなく、Arduinoやデータベースとの非同期処理を容易に実現できます。また、今回はデータベースに対してトランザクション処理を必要としないので、MongoDBの使いやすさが生きてきます。
MEANスタックを採用するとき、「generator-angular-fullstack」の利用を強くお勧めします。これはyeomanジェネレーターで、高いメンテナンス性を維持しながら、新しい機能を短時間で追加していくためのベストプラクティスが詰め込まれています。筆者が初めて利用したとき、生成されたコードの意図を想像して何度も感動しました。
まずは、サーバー内の処理シーケンスを整理して、全体像を明確にしましょう。全て非同期で連携しますので、イベントハンドラーを利用してシーケンスを形成させます。generator-angular-fullstackとの統一感を重視した構成にしています。
センサーから定期的に温度と湿度を取得します。体感温度は、温度と湿度だけで計算できます。計算はnpmモジュール「heat-index」に任せましょう。
センサーデータを取得するために、サーバーからArduinoへ、取得命令を毎回送信します。普通なら、センサーノードが自発的にデータを送信するよう設計されるため、違和感を覚えることでしょう。
こうする理由は、Arduinoが並列に処理を実行できないためです。Arduinoに自発的にセンサーデータを送信させようとすると、間隔を調整するためにdelay関数を使って処理を休止させます。一方で、赤外線を発信させる命令は人間が行うため、Arduinoに送信するタイミングはランダムです。仮にdelayの時間が60秒だったとき、処理の休止によって赤外線の発生命令を受け取るタイミングが最大60秒遅れることになります。
この遅延を避けるため、サーバー側からデータ取得コマンドを毎回送信してカバーします。Arduinoをもう1台買うのはもったいないですからね。
クライアントやデータベースを含む処理シーケンスは次の通りです。温度湿度データのやりとりは、大きく分けて二つのパターンが存在します。
一つ目の赤色の部分は、setIntervalを使って定期的にキックされます。setIntervalでキックする関数はArduinoへSTRING_DATAを送信するだけです。
サーバー側がセンサーデータを受け取ると、johnny-fiveのBoardインスタンスで「string」イベントがemitされます。
この「string」イベントのハンドラーでMongoDBへの保存だけを行います。
最後に、クライアントへ通知する部分です。MongoDBのnpmモジュール「mongoose」を使うと、保存完了イベント「save」に対してハンドラーを登録できます。このハンドラーの中で、体感温度などをクライアントへ送信します。サーバーからクライアントへのpush送信を行いたいので、Socket.IOで「ThermoHygroHistory:save」というイベントを、接続中の全てのクライアントに対してemitします。
イベントで処理を連鎖させるので、一つのファイルを肥大化させず、コールバックのネストが深くなる問題まで回避しています。もちろん、I/Oの待ちも最小限に抑えられています。
続いて、図の紫色の部分を説明します。ここも、体感温度と温度・湿度を取得する処理です。
Socket.IOでもクライアントへpushしているのに、なぜこの処理が必要なのでしょうか。それはSocket.IOのクライアントへpushするタイミングがセンサーデータを保存したタイミングだからです。その後にクライアントが接続してきた場合、次にデータが保存されるまで温度・湿度データが送信されず、UIに体感温度が表示されません。仮にセンサーデータの取得間隔が60秒だった場合、クライアントへ送信されるまで最大60秒かかってしまいます。
これを解決するため、ページを読み込んだ直後にクライアントからデータをリクエストさせます。
エアコン制御は先ほどよりもシンプルで、サーバーとクライアントの通信はSocket.IOで完結します。
まず、Webブラウザー上の電源ON/OFFボタンがクリックされると、サーバーへ通知するためにSocket.IOで「AC:toggle」イベントをemitします。
サーバーは、そのイベントハンドラーでArduinoに赤外線発生の命令を送信します。
赤外線発生完了は「string」イベントで拾い、受け取った文字列が「AC:toggled」と一致するとき、クライアントへpush通知するために、Socket.IOで「AC:toggled」イベントをemitさせます。
Copyright © ITmedia, Inc. All Rights Reserved.