Eject Car技術解説ページ

需要は一切ないと思いますが一応作りました。
気になることがありましたら、Eject Car近くの変態までお気軽に声をお掛けください。

[制作者]
レッター :Web操作画面、Ejectカー設計・組み立て
かまぼこ :通信・Eject制御ソフトウェア、Ejectカー、設計・組み立て
いらにか :LED制御

制御ソフトウェア

Ejectカーは見た目は酷いですが、内部では結構面倒なことを色々やってます。

Ejectカー側

Ejectカーは主にJavaで書かれたプログラムで制御されます。
Javaは小型のLinuxボードであるRaspberry Pi上で実行されます。
実際には、C言語で書かれたプログラムをJavaから呼び出すことで制御を行っています。


なぜJavaか

制御ソフトウェアは後述するブラウザとの双方向通信を行うことを念頭に置いて開発がスタートしました。
ブラウザとの双方向通信のために、一度インターネット上に存在するサーバーを介する必要があり、Socket.IOを利用することにしました。
Javaにはオープンソースで開発されているSocket.IOでの通信を可能にするライブラリが存在し、過去に何度か利用経験もあり、開発が容易だと考えました。
JDK8はRaspberry Pi上で動作させるOS(Raspbian)に、最初からインストールされており環境構築の手間を省くこともできます。
また、Javaはマルチスレッドプログラミングが容易であり、様々な処理を非同期で行うことが可能です。
以上の理由から今回は開発言語/環境としてJavaをメインに利用することにしました。

余談ですが実際にハードウェア(光学ドライブ)を操作しているのはJavaではなく、C言語で記述されたプログラムです。
C言語で必要な機能を実装した簡単なAPIを提供し、JavaからAPIを叩くことで、間接的に光学ドライブの操作を行っています。
今回のシステムにおいてのJavaの役割は「Socket.IOでの通信と、その通信の内容を理解し、実際に操作を行うプログラムへの命令の仲介」となります。


実際の処理

処理プログラムでは「Active通知」「Status通知」「Eject処理」など、各機能がそれぞれ独立したスレッドとして動作し、全体としてマルチスレッドで動作しています。
(実際に高専祭で走らせるプログラムではマルチスレッドの恩恵は受けにくい構造になっていますが)
Active通知:Ejectの可否を通知し、またEject命令の受理の承認・拒否の管理を行います。
Status通知:光学ドライブの状態が変化した場合に通知します。(オプションにより変化前に通知が行われる場合もあります)
Eject処理:Eject命令を受信し、Activeにより許可された場合に呼び出され、Ejectを実行します。
その他、細かい処理が色々動いてます。


インターネットへの接続

Raspberry PiはWi-Fiで近隣のアクセスポイントに接続することでインターネットに参加します。
高専祭当日はスマートフォンのテザリング機能により、インターネットに接続される予定です。
テザリングによる接続であるため、グローバルIPが付与されず、またルーティングの設定などは行うことができません。
その条件下でもブラウザとの通信を可能にするために、Socket.IOを利用しています。


VPS側

コンテンツ配信

VPS上ではHTTPサーバー(Apache)が動作しており、Ejectカー操作ページを配信しています。
特にこれといって特別なことでもないので、他に説明はないです。


Node.js + Socket.IO

VPS上ではNode.jsが動作しており、Socket.IOサーバーのサービスを提供しています。
インターネットからアクセス可能なVPSにSocket.IOサーバーを立てることで、外部からリモートアクセス出来ないネットワークからでもクライアントとして接続し、双方向の通信を行うことできます。
ブラウザはクライアントとして接続、Ejectカーもクライアントとして接続させることで、双方向のリアルタイム通信を実現しています。
また、Node.jsで提供するサービスはJavaScriptで記述することができ、簡単にサービスを構築することが可能です。
Node.jsにはSocket.IOというライブラリが存在し、それ利用することで簡単に双方向通信を実現することができます。

Socket.IOによるメッセージ中継

Socket.IOはイベント/メッセージのやりとりをブラウザとサーバで行うことを目的として開発されています。
Eject CarではSocket.IOのサーバを一度経由してメッセージのやりとりを行うことでリアルタイムに双方向通信を可能にしています。
Socket.IOサーバは「さくらのVPS」の上、Node.jsにより構築されており、常に接続を待っています。
そこにユーザーのブラウザからの接続を受け付け、メッセージのやりとりを行います。
今回構築したSocke.IOサーバはイベント・メッセージの中継を行うためのものであり、サーバが能動的にアクションを行うわけではありません。
Eject Carもまた「クライアントとして」Socket.IOサーバに接続されメッセージの中継(受信)を待ちます。
そしてEject命令を受け取った時点でEjectの処理を行ったり、ドライブの状態が変化した場合に通知メッセージをサーバに送信するなどの処理を行います
送信されたメッセージはSocket.IOサーバで中継され、接続されているブラウザにメッセージが届きます。
ブラウザ側はメッセージを受け取るとメッセージに応じた処理を行います。
メッセージはすべてのクライアントに通知されるため、複数台接続されている場合でもすべてのブラウザで同時に動作します。


なぜSocket.IOなのか

「認めたくないものだな、若さゆえの過ちというものは」
「先程、Socket.IOを使えば簡単にリアルタイムな双方向通信を実現できると言ったが、すまない、あれは嘘だ」
いやまぁ簡単に作れるってのは本当なのですが、それはブラウザとの通信のお話で、Javaでやるには少しつらいところもあったよ。
たぶん、RubyとかPythonで普通にWebSocketとか使ったほうが早かったよ。
というか今回使ったJavaのライブラリ、本格的に使ってみると癖ありすぎだよ。
よくよく考えてみれば、別にJavaじゃなくてもよかった。
そのあたりは宗教上の都合と、あと他の言語でマルチスレッドとかやったことなかったしー。

Socket.IO使わなくてもIRCとか既にチャットのためのシステム/プロトコルが存在するわけで。。
しかもそれならJavaでもRubyでもPythonでもたくさんライブラリが存在するのに。
改めて考えると、自ら茨の道を突き進んでいただけだった気がします。
(そのあたりの選定はかまぼこがやったので、文句はかまぼこ[@kamaboko123]までどうぞ)


ブラウザ側

通信

ブラウザはHTTPによりWebページをWebサーバー(Apache)から受け取り、マークアップを行った後画面上に表示します。
その後、HTML内に記述されているJavaScriptがブラウザによって解釈され実行されます。
Socket.IOサーバーとの通信はJavaScriptにより行われ、またメッセージを受け取った場合の処理もJavaScriptで行われます。


ドライブ描画

ドライブの描画とアニメーションもJavaScriptで行われます。
主にJavaScirptのCanvasと呼ばれる図形描画の機能が使われます。
またCanvasによるアニメーションを実現するために、CreateJSというJavaScriptライブラリを利用しています。
その結果、「FlashっぽいけどHTML5な何か」が出来上がりました。


Ejectカーとの通信

Ejectカーとは、Socket.IOサーバーを通じて通信が行われます。
ブラウザ側で画面をタップすると、Eject命令がSocket.IOサーバーに送信、サーバーはメッセージをEjectカーに中継しEjectが行われます。
また、ドライブの状態が変化した場合には、Ejectカー側からSocket.IOサーバーを通じてメッセージが届き、受信したメッセージに応じて、ブラウザ上にドライブ状態の描画を行います。


ハードウェア

Raspberry Pi

Ejectカーの制御ソフトウェアはLinux OS上で動作しており、そのLinuxはRaspberry Piという小型コンピュータ上で動作しています。
Raspberry Piは小型・軽量・低消費電力が特徴のLinuxボードで、また低価格(3500円程度)で購入することができます。
USBから給電が可能で、モバイルバッテリーでも動作可能。
USB、HDMI、イーサネット、GPIOといった様々なインターフェースを搭載しています。
また、Linxuが動作するため、既存のソフトウェアを使ったり、新しくソフトウェアの開発を行う場合も簡単に開発が可能です。


駆動

Ejectカーは光学ドライブのトレイが開閉する力で動きます。
尺取り虫の動きを参考に、トレイの伸び縮みを利用して前進します。
取り付けられているタイヤは、常に一方向にしか回転しない特殊なものを採用しています。
タイヤに関する詳しいことは「ミニ四駆 ワンウェイホイール」で検索。


電力

Raspberry Pi:5V 2A
光学ドライブ:5V 2A + 12V 350mA(?)
光学ドライブの駆動には制御基板の電源(5V)と、モーター・レーザー動作の電源(12V)が必要。
5V電源はRaspberry Piと一緒にモバイルバッテリーを搭載することで供給。
12V電源は単三電池8本を直列につなぐことで、供給しています。
光学ドライブ本来の用途である読み込み/書き込みには、多くの電力を必要とするため電池の電流ではおそらく不足します。
しかし、今回はスピンアップすらせず、トレイの開閉のみが行えれば良いため、安価かつ手軽に利用できる乾電池を採用しました。
光学ドライブの給電はペリフェラル4ピンで行われ、通常のケーブルを改造してそれぞれをモバイルバッテリーと電池に接続することで、5Vと12Vの供給を行っています。


通信環境

Raspberry PiにUSBの無線LANアダプタを取り付けています。 学校内の無線LANへの接続は避け、スマートフォンのテザリングによりインターネットにアクセスします。
インターネットに接続させない場合、制御のために端末をRaspberry Piが接続されているのと同じアクセスポイントに接続させる必要があります。
高専祭にお越し頂いた皆様にも、Ejectカーの操作を行えるようにするために、Raspberry Piはインターネットに接続させることが必要でした。
しかし、テザリング回線では内向きの通信が遮断されてしまいます。
Raspberry Piでただサーバーを立てても、インターネットからのアクセスができないため、他の方法を考える必要がありました。
そこで、考えだされた仕組みが「インターネットのどこからでもアクセス可能なサーバーにメッセージの中継を行わせる」という方法でした。
具体的には、前述の通りVPS上にSocket.IOサーバーを構築し、ブラウザとRaspberry Piを「クライアントとして」接続させました。
こうすることで、インターネットから直接アクセスすることのできないRaspberry Piに対し、ブラウザからメッセージを送信することを可能にしました。

LED

Ejectカー上部にEject回数をカウントするLEDを搭載しています。
LEDが6個しか用意できなかったため、Eject回数を2進表記したときの下位6ビットを表示しています。
LEDの制御はRaspberry Piに搭載されているGPIOにより行われ、制御プログラムはC言語により記述されています。


使用ライブラリ等

node.js
  Socket.IO Socket.IO

Java
  Socket.IO通信 socket.io-java-client
  JSON関連 org.json

JavaScript
  JSいろいろ jQuery
  Canvasアニメーション CreateJS
  Socket.IO Socket.IO

C
  GPIO制御 WiringPi