JenkinsのビルドステータスをArduinoで表現する
いわゆるパトランプというやつですね。最初USB対応の製品を探したのですが、なんせ3万円とかする…これは買えない…orz というわけで、別件で試作したArduinoのシールドにフルカラーLEDを載せたのを思い出し、これを活用してしまおうということにしました。
用意する物とか
- Arduino (今回は手元にあったUno)
- USBケーブル(ArduinoとサーバPCを接続します)
- Jenkinsが動いてるサーバ(Ubuntu 12.04)
- Jenkinsはサーバ上でlocalhost:9090/jenkins/で動いてます
- Node.js + node-serialport
- Node.jsはサーバ上でlocalhost:3000/で動かします
構成
今回悩んだのはシリアルのコントロールでした。Jenkinsのジョブでシェルスクリプトを使って、Arduinoにシリアル経由でコマンドを投げたかったのですが、うまい方法を思いつかず、手っ取り早くNode.js + node-serialportを使って実装することにしました。
Jenkinsにプラグインを導入
Jenkinsのビルドのステータス通知を取得すること。ビルド実行後にシェルスクリプトをジョブから実行する必要があります。 このサイトにズバリ書いてありました。
ステータス通知は
RESULT=`curl http://localhost:9090/jenkins/job/[HogeProject]/lastBuild/api/xml | perl -le '$_=<>;print [/<result>(.+?)</]->[0]'` (HogeProjectの場合)
で取れます。そしてビルド実行後の処理にシェルスクリプトを使うためにPost build taskプラグインを導入しておきます。
Node.jsサーバを立てる
Node.jsサーバをポート3000番で立てます。Expressを使って作成し、node-serialportを組込みます。この辺はRaspberry PiでNode.jsを動かす(※Raspberry Piですが、Ubuntuでも基本は変わりません)とかSerialportを使ってNode.jsでArduinoと通信するを参考にしてください。ちなみに今回はSocket.IOは必要ありません。Expressでサーバだけ作って、シリアルにデータ出力さえできればいいので。さて、UbuntuではArduinoは"/dev/ttyACM0"と認識されるようなので、app.jsにこんな感じでシリアルを設定します。
// Serial Port var portName = '/dev/ttyACM0'; // Ubuntu環境 var sp = new serialport.SerialPort(portName, { baudRate: 9600, dataBits: 8, parity: 'none', stopBits: 1, flowControl: false, parser: serialport.parsers.readline("\n") });
そして、JenkinsのジョブのスクリプトからステータスをNode.jsで受け取るためのWebAPIを作成します。これでhttp ://localhost:3000/status?status=hogeで受け取った、パラメータhogeをspのシリアルに送信することができます。
// Web APIエントリポイント app.get('/status', function(req, res) { res.writeHead(200, {'Content-Type':'text/html; charset=utf-8'}); res.end('OK'); var msg = new Object(); msg.status = req.query.status; console.log('status ' + msg.status); sp.write(msg.status, function(err, bytesWritten) { console.log('bytes written: ', bytesWritten); }); });
ここまで出来ると、とりあえずArduinoをPCとUSBケーブルで接続してNode.jsのサーバを起動すると、シリアルとしてArduinoをオープンできるようになります。ただ、デバイスドライバをオープンするにはデフォルトでsu権限が必要になっていますので、/dev/ttyACM0をchmodで777とか設定して、フルアクセス可能にしてしまいました(^^; とにかくこれで、Node.jsとArduinoがシリアル通信できるようになりました。
Arduinoの仕様
Arduinoの仕様も決めておきましょう。基本的にはフルカラーLEDを使い、以下のようなステータスとコマンドインターフェースとしました。statusが1文字なのは、Arduino側で実装を簡単にするためです。
- 初期化(消灯) : /status?status=r
- ビルド中(青) : /status?status=g
- 成功(緑) : /status?status=s
- 失敗(赤) : /status?status=e
Arduino側のプログラムは簡単で、シリアルの入力を待って比較してLEDを制御するだけです。ちなみにこのプログラムでは点灯しっぱなしになります。実運用では適当な時間が経ったら初期化するとか入れた方がいいでしょう。以下抜粋です(使用したLEDがアノードコモンなので、点灯が0になっています)
void loop() { if (Serial.available() > 0) { // シリアル通信でデータが送られてきたら char c = Serial.read(); // 一文字分データを取り出す。 if (c == 'g') { // Jenkinsビルド開始 Serial.println("START"); analogWrite(LED_RED, 255); analogWrite(LED_BLUE, 0); // 青LEDを点灯 analogWrite(LED_GREEN, 255); } else if (c == 'e') { // エラー Serial.println("ERROR"); analogWrite(LED_RED, 0); // 赤LEDを点灯 analogWrite(LED_BLUE, 255); analogWrite(LED_GREEN, 255); } else if ( c == 's') { // 成功 Serial.println("SUCCESS"); analogWrite(LED_RED, 255); analogWrite(LED_BLUE, 255); analogWrite(LED_GREEN, 0); // 緑LEDを点灯 } else if (c =='r') { // 初期化 Serial.println("RESET"); analogWrite(LED_RED, 255); analogWrite(LED_BLUE, 255); analogWrite(LED_GREEN, 255); } else { Serial.println("etc..."); analogWrite(LED_RED, 255); analogWrite(LED_BLUE, 255); analogWrite(LED_GREEN, 255); } } delay(1000); }
ここまで出来たら動作確認ができます。サーバのコンソールで
$ curl http://localhost:3000/status?status=g
とか打ってみて、LEDが希望通りに点灯すればOKです。あとはこのcurlのコマンドをJenkinsのジョブの中からシェルスクリプトとして実行できるように設定してやります。
Jenkinsのジョブの設定
最後にJenkinsのジョブ設定を行います。
- ビルド開始時に「実行中」
- ビルド終了時に「成功」「失敗」
というようにします。
ビルド開始時
curl http://localhost:3000/status?status=g
実際にはこんな感じ
ビルド終了時スクリプトに追加
RESULT=`curl http://localhost:9090/jenkins/job/[HogeProject]/lastBuild/api/xml | perl -le '$_=<>;print [/<result>(.+?)</]->[0]'` echo $RESULT if [ $RESULT = SUCCESS ] then echo "success" curl http://localhost:3000/status?status=s else echo "failure" curl http://localhost:3000/status?status=e fi
実際にはこんな感じ
運用中のビルド中表示はこんな感じで青く点灯します。 上の2つのデバイスは、別の試作で載せた温度センサーなので今回は関係ありません。
まとめ
何はともあれJenkinsとArduinoを繋げることができました。これでLEDだけではなく、音を慣らしたりもできますね。赤外線リモコンを使って、エラーになるとエアコンを止めてしまって、何が何でも直さないと帰れない・・・とか(むしろ帰りたい気が)なんかテストとかビルドとかが楽しくなる仕掛けを考えると面白いかもしれません。
今回のサンプルコードですが、Githubに置いておきます。環境にも依存するので参考程度に...
※ちなみに、これは某アプリのビルドが40分も掛かるのでステータスが知りたいと思い、休日に構想1時間 + 仮実装1時間、本実装1時間ってとこで作りました\(^o^)/
node-serialportを使ってNode.jsでArduinoと通信する
node-serialportとは
Node.jsでシリアルポートを制御できるようにしたライブラリ。今回はこれを使ってNode.jsとArduinoをシリアル通信で接続します。今回のターゲットは2種類。まずはMac + Arduinoで動作を確認した後、Raspberry Pi + Arduinoで動作させてみます
※2013/3/30 データ受信取りこぼしの件で、修正パッチを頂きました。 @kei_os さんありがとうございます
node-serialportのインストール
まずはMacの方でインストールします。ここを参考にexpressをインストールし、Socket.ioもインストールしておきます。node-serialportのインストールはnpmを使います。
$ npm install serialport
サーバの記述
serialportの使い方だけ抜粋します。
serialportの宣言をしておきます。
var express = require('express') , routes = require('./routes') , path = require('path') , serialport = require('serialport');
MacでArduinoを接続した時のシリアルポートを調べておき、portNameに指定します(接続時に毎回変わる場合もある)通信設定はよくあるパターンですね。パースの単位を改行で行うように設定し、ArduinoからのJSON形式の送信データの末尾に改行を付加するようにします。今回は通信速度を115200にしていますが、なぜか9600だとデータ受信で取りこぼす確率が高く使い物になりませんでした。ただし115200にしてもデータを取りこぼすことが結構あり、どうしてこうなるのか原因調査等した方がいらっしゃいましたら教えて下さい。(正直言って信頼性に欠けるので、プロトタイプ以外には全然ダメ)※2013/3/30修正パッチでbugfixしました
// Serial Port var portName = '/dev/tty.usbmodemfd13431'; // Mac環境 var sp = new serialport.SerialPort(portName, { baudRate: 115200, dataBits: 8, parity: 'none', stopBits: 1, flowControl: false, parser: serialport.parsers.readline("\n") // ※修正:パースの単位を改行で行う });
Arduino → Node.jsの受信の部分です。データ列として受信するので、一旦Bufferで受けておいてJSONでパースしています。データ列が正常に完全な形で受信できなかった場合は、無視するようにしています。
// data from Serial port sp.on('data', function(input) { var buffer = new Buffer(input, 'utf8'); var jsonData; try { jsonData = JSON.parse(buffer); console.log('temp: ' + jsonData.temp); console.log('led: ' + jsonData.led); } catch(e) { // データ受信がおかしい場合無視する return; } // つながっているクライアント全員に送信 io.sockets.json.emit('message', { value: jsonData }); });
Arduino側の実装
Arduinoには温度センサー(LM35Z)とLEDを載っけてあるので、それを制御してみます。loop()を5秒で回して、温度とLEDのON/OFFをJSON文字列としてシリアル送信します。Node.js側では受信した温度の表示を行い、ブラウザを通してArduino上のLEDのON/OFFを行えるようにしました。 コードはここ
Macで動かしてみる
気温が表示され、LEDのON/OFFコントロールがブラウザ上からできるようになっています。ただし、Arduinoのループ周期が5秒に設定してあるので、LEDのコントロールは最大5秒待たされることになります。
Raspberry Piで動かしてみる
同じコードをRaspberry Piで動かしてみます。コードはGithubで管理しているので、Raspberry Piにログインしてから、git cloneしてコードを取ってきます。ここでは、すでにRaspberry Piにはnvm, npmおよびNode.jsがインストールされているものとします。ArduinoをUSBでRaspberry Piに接続してlsするとそれっぽいのが見つかります。ArduinoはttyACM0になりますので、app.jsのportNameを書き換えます。(ttyAMA0はRaspberryPiの内蔵シリアルポート)
$ ls /dev/tty* /dev/ttyACM0 /dev/ttyAMA0
なにはともあれ動かしてみます(えいやっ
$ node app.js /home/pi/work/Node-SerialPort/NodeServer/node_modules/serialport/node_modules/bindings/bindings.js:77 throw e ^ Error: /home/pi/work/Node-SerialPort/NodeServer/node_modules/serialport/build/Release/serialport.node: invalid ELF header at Object.Module._extensions..node (module.js:485:11) at Module.load (module.js:356:32) at Function.Module._load (module.js:312:12) at Module.require (module.js:362:17) at require (module.js:378:17) at bindings (/home/pi/work/Node-SerialPort/NodeServer/node_modules/serialport/node_modules/bindings/bindings.js:72:15) at Object.<anonymous> (/home/pi/work/Node-SerialPort/NodeServer/node_modules/serialport/serialport.js:7:44) at Module._compile (module.js:449:26) at Object.Module._extensions..js (module.js:467:10) at Module.load (module.js:356:32)
さすがにnode-serialportはmacと同じ環境ではないのでダメなようです。一旦serialportをアンインストールして、再インストールします。Raspberry Piではnode-serialportをグローバルインストールしてリンクを作成して使うようにしました(他でも使うかもしれないので)
$ npm uninstall serialport $ npm install -g serialport $ npm link serialport
これでもう一度起動してみます。
先ほどのMacと同じように動きました。
今回のコードはGithubに置いてあります。コードはMacで動作するコードなので注意して下さい。 ※2013/3/30 データ取りこぼしの件bugfixしました
Socket.IO for Android(nginx1.3.13リバースプロキシ対応版)
前回のエントリでnginx(1.3.13)でWebSocketをリバースプロキシしてみたのですが、それでは以前作成したSocket.IO for Androidのサンプルがリバースプロキシを通りませんでした。うーんと(´ε`;)悩んでいた所、 @gtk2kさんに情報を頂きましたのでコードを修正しました。
@tomo_watanabe お疲れのところ申し訳ないけど、一応nginxプロキシ経由で接続できるようになりました。説明が長いためブログで qiita.com/items/15379fe0… twitter.com/gtk2k/status/3…
— gtk2kさん (@gtk2k) 2013年3月3日
修正内容
@gtk2k さんが投稿されたnginx(1.3.13) のリバースプロキシでNode.jsとSocket.IO for Android(weberknecht)をつないでみるのまんまですので、詳細はそちらに譲ります。(細かい違いは"Upgrade"を大文字小文字関係なく比較するようにしています)ただweberknechtの修正が必要になったため、jarライブラリからソースとして取り込むように修正しています。 それと、起動時に入力するIPアドレスはポート番号も含めた形で渡すように修正しました(リバースプロキシを利用すると80になるため)
コード
Android-SocketIO(github)を参照して下さい
nginx(1.3.13)でWebSocketをリバースプロキシしてみる
nginx(1.3.13)でWebSocketのプロキシを試してみました
2013/2/19にnginxが正式にWebSocketに対応したとアナウンスがあったので、試しに使ってみました。
ダウンロード・インストール
ここからnginx-1.3.13をダウンロードしてきて、インストールします。 インストールオプションはあえてデフォルトで
$ wget http://nginx.org/download/nginx-1.3.13.tar.gz $ tar xvf nginx-1.3.13.tar.gz $ cd nginx-1.3.13 $ ./configure $ make $ sudo make install
設定ファイルの書き換え
次にnginx.confを書き換えます。構成は
- リバースプロキシ: 192.168.0.8:80
- バックエンドサーバ: 192.168.0.2:3000
3000ポートの方はNode.jsのサーバを動作させておき、192.168.0.8:80に来たアクセスが、そのまま動けばOKです。別々のサーバにしていますが、リバースプロキシ上で3000ポートで動かしても良かったですね…(^^;
/usr/local/nginx/conf/nginx.conf
を普通に設定してみます。
server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { proxy_pass http://192.168.0.2:3000/; }
Node.jsのサーバで動かすアプリはこれです。これは接続がどうなってるかわかるので。
では起動させてみます。
$ sudo /usr/local/nginx/sbin/nginx
残念…xhr-pollingでしか繋がらないようです。Unexpected response code: 502 と出ています。
あれれ、と思って@nginxorgを眺めてたら、このツイートを発見
Here comes nginx-1.3.13 with websocket proxy nginx.org/en/download.ht… nginx.org/en/CHANGES mailman.nginx.org/pipermail/ngin… trac.nginx.org/nginx/changese…
— nginx web serverさん (@nginxorg) 2013年2月19日
設定ファイルを再度書きかえ
チェンジセットにあるように、丸パクリで書き換えてみました。(正直意味わかってません...)
server { listen 80; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { proxy_pass http://192.168.0.2:3000/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }
さて、これで再度起動して確認します。
おぉ!ちゃんとWebSocketで接続できるようになりました\(^o^)/
Raspberry Piにリバースプロキシを導入する
Raspberry PiにNode.jsを入れて動かしていましたが、Raspberry Piにリバースプロキシを導入して、サブドメイン名でApacheとNode.jsの切り替えをしてみます。リバースプロキシにはWebSocketを透過するnode-http-proxyを導入します。できるだけ手順をそのまま書いたつもりですが、何か不必要な部分があったり、間違ってる箇所があるかもしれません。ネットワーク周りは勉強中なので備忘録代わりに書き残しています。(そもそもリバースプロキシ自体初めて知った...)
Apachをインストールして、デフォルト80でリッスンするのを8080でリッスンするように変更します。リバースプロキシは80をリッスンして、サブドメイン名によってApacheの8080とNode.jsが動いている3000へ振り分けるようにします。
Apacheのインストール
$ sudo apt-get install apache2
きちんと動作しているか、Rasberry Piにアクセスして確認します(ここでは192.168.0.7になっています)
Raspberry Piのデフォルトのランレベルは
$ less /etc/inittab
で見てみると
# The default runlevel. id:2:initdefault:
Apacheをインストールすると自動起動になるようなので確認します。
$ ls /etc/rc2.d/ K01lightdm README S01motd S01triggerhappy S03dbus S03pulseaudio S05plymouth K05nfs-common S01bootlogs S01rsyslog S02apache2 S03dphys-swapfile S03rsync S05rc.local K05rpcbind S01ifplugd S01sudo S03cron S03ntp S03ssh S05rmnologin
"S02apache2"というのがあるので、起動時にapacheが自動起動します。自動起動させたくない場合は
$ sudo update-rc.d apache2 remove
とします。自動起動に再設定するには
$ sudo update-rc.d apache2 defaults
Apacheのリッスンポートを8080に変更
デフォルトでApacheは80をリッスンするので、これを8080に変更します。viやemacsのエディタでルートユーザ権限で編集します。
$ sudo emacs /etc/apache2/apache2.conf // 以下を追記 <VirtualHost *:8080> DocumentRoot /var/www/ ServerName web.raspberry.pi </VirtualHost> $ sudo emacs /etc/apache2/ports.conf // 以下を修正 NameVirtualHost *:8080 <-- 80から8080 Listen 8080 <-- 80から8080
修正したら、Apacheを再起動して8080で接続できるか確認します。
$ sudo service apache2 restart // 80ではエラー $curl http://localhost:80 curl: (7) couldn't connect to host // 8080で接続OK $ curl http://localhost:8080 <html><body><h1>It works!</h1> <p>This is the default web page for this server.</p> <p>The web server software is running but no content has been added, yet.</p> </body></html>
これでApacheは8080で接続できるようになりました。
リバースプロキシのインストール
今回はWebSocketに対応したリバースプロキシを導入したいので、node-http-proxyをインストールします。グローバルでインストールします。参考サイト
$ npm install -g http-proxy
どこかに作業用ディレクトリを作成して、グローバルインストールしたモジュールのリンクを作成します
$ mkdir reverse_proxy $ cd reverse_proxy $ npm link http-proxy /home/pi/work/reverse_proxy/node_modules/http-proxy -> /home/pi/.nvm/v0.8.16/lib/node_modules/http-proxy
proxy.jsを作成します。
/** * node-http-proxy */ var port = 80; var httpProxy = require('http-proxy'); var options = { hostnameOnly: true, router: { 'web.raspberry.pi': '127.0.0.1:8080', 'node.raspberry.pi':'127.0.0.1:3000' } }; var proxyServer = httpProxy.createServer(options); proxyServer.listen(port); console.log('It Works!');
これで、
- web.raspberry.pi → Apache
- node.raspberry.pi → Node.js
へ振り分けできるようになるはずです。
hostsファイルの追記
今回はローカルネットワークで動作させるので、クライアントのブラウザの設定とhostsファイルの書き換えが必要になります。
hostsファイルにはRaspberryPiのIPアドレスと設定したいサブドメインとセットで追記します。ブラウザの設定にも下記のアドレスの場合はプロキシ設定を行わない等の設定が必要になります
# hostsファイルに下記のようにRaspberryPiのIPを追加 192.168.0.7 web.raspberry.pi 192.168.0.7 node.raspberry.pi
リバースプロキシの起動
80ポートをリッスンするにはスーパユーザじゃないとできないので、
$ sudo node proxy.js sudo: node: command not found
nodeコマンドが無いと怒られてしまいます。仕方ないのでちょっと強引に起動させますw
$ sudo su # source /home/pi/.nvm/nvm.sh # nvm use v0.8.16 Now using node v0.8.16 # node proxy.js
この状態でクライアントブラウザから"web.raspberry.pi"へアクセスしてみて"It Works"と出れば、無事リバースプロキシとして動作しています。
Apache(8080)へアクセス
では以前に動かしたNode.jsのプログラムをポート3000で動作させて、最終的にリバースプロキシとしての動作を確認します。
Node.js(3000)へアクセス
まとめ
Raspberry Piでリバースプロキシを動かしてみました。Apache上ではPukiWikiを動かしたり、Node.js側で動作するプログラムの設定を変更したりということができるかもしれません。ルーターとかでは、設定を特定のアドレスで行ったりしていますが、あんな感じでしょうか。Linuxの組込み機器系ではよくありそうな構成なのかもしれません(組込みLinuxの開発経験ナシ)今回のWebSocketを透過するリバースプロキシは、リアルタイムWebデバイスとしての実装には有効な気がします。
Raspberry Piに接続したフルカラーLEDをNode.jsから制御する
Raspberry PiのGPIOの制御方法がわかったので、Node.jsからGPIOをコントロールできるようにします。
Node.jsからGPIOを叩く
前回のGPIO制御のコマンドをそのまま使うような書き方をします。
var exec = require('child_process').exec; exec('gpio -g mode 23 out');
こんな感じで設定をして
exec('gpio -g write 23 1');
とすれば、23がHiになります。
Node.jsからフルカラーLEDを制御する
これだけでは面白く無いので、ブレッドボードにフルカラーLEDを接続してPWM使って色を変えるようにしてみます。PWMはアナログ出力で、Raspberry Piは18番のみがPWMが使えるようになっているので、RGBのBを18に接続して、RとGを普通のGPIOに接続しました。
GPIO Peripheral Full Color LED R - GPIO4 : 23 G - GPIO3 : 24 B - GPIO1 : 18 : PWM
ブラウザクライアントはexpress + Node.js + Socket.IOで作成しました。(以前のエントリを参照)LEDコントロールUIにはjQuery Knobを使っています。ブラウザでノブの値を変化させたら、その値を元にLEDの色を変化させるようになっています。
以下サーバ側(app.js)コード抜粋
// メッセージを受けた時の処理 socket.on('message', function(data) { // 接続しているクライアントに全てに送信 socket.broadcast.emit('message', { value: data.value }); // B exec('gpio -g pwm 18 ' + data.value % 1024); // R if ((1024 <= data.value && data.value < 2047) || (3072 <= data.value && data.value < 4095) ) { exec('gpio -g write 23 1'); } else { exec('gpio -g write 23 0'); } // G if ((2048 <= data.value && data.value < 3071) || (3072 <= data.value && data.value < 4095) ) { exec('gpio -g write 24 1'); } else { exec('gpio -g write 24 0'); } });
回路例は以下(LEDはカソードコモン:GND共通を使用)
これでノブをぐりんぐりんすれば、フルカラーLEDの色が変化します(^^)/
しかし・・・予想してたとはいえ追従性能がかなり悪い...orz
まとめ
リアルタイムWebとリアルデバイスを接続してみるという実験でしたが、Socket.IOのログレベルを下げてるのですが、実際の動作としては変化の追従がかなり遅いです。クリックだとパッパッと変化するのですが、アナログ的に変化させるとキツイです。今回の変化量は 0 〜 4096の間で変化量を都度送信しているのですが、Node.jsの処理が追いついていないのか、GPIOを叩くコードの部分がネックなのか切り分けできていません。いずれにしてもこの程度の処理でログが目で追えるような速度ではハードリアルタイムは無理です。やはりリアルタイムWebといえども、ハードリアルタイムを期待するのはかなり厳しいようです...(^^; 改善方法をご存知の方はぜひ教えて下さい
WebSocket系を使ったリアルタイムWebと、リアルデバイスとの接続させた場合のネックは見えました。ネックを解消し、あくまでハードリアルタイムを求める、あるいはWebSocketの性能レベルとハードリアルタイムを別々に考えシステムを設計する。という2通りの方法があります。普通に考えれば後者で、よくある電源のON/OFFくらいのレベルであれば十分対応可能ですが、もっと違うことを考えて行かないと面白くないですよね...
今回のコードはgithubに置いてあります。
Raspberry Pi + Socket.IOでチャットアプリ
このエントリは前回の続きです。
Socket.IOを使ったチャットアプリの作成
Socket.IOをインストールとして簡易チャットアプリを作成します。といっても、このサイトのサンプルを移植しただけです、はい…
Socket.IOのインストール
まずは作業フォルダを用意します。前回同様、express + EJSでひな形を作成します。
$ express -e socketio create : socketio create : socketio/package.json create : socketio/app.js create : socketio/public create : socketio/public/javascripts create : socketio/public/images create : socketio/public/stylesheets create : socketio/public/stylesheets/style.css create : socketio/routes create : socketio/routes/index.js create : socketio/routes/user.js create : socketio/views create : socketio/views/index.ejs install dependencies: $ cd socketio && npm install run the app: $ node app $ cd socketio && npm install
Socket.IOをインストールします。
$ npm install socket.io
現時点ではSocket.IO ver0.9.13がインストールされました。
アプリの作成
アプリの作成のため、下記のファイルを作成・修正します。
- app.js
- views/index.ejs
- public/javascripts/jquery-1.8.3.min.js (追加)
- public/javascripts/chat.js (追加)
- routes/index.js
サーバサイド(app.js)
/** * Module dependencies. */ var express = require('express') , routes = require('./routes') , http = require('http') , path = require('path'); var app = express() , http = require('http') , server = http.createServer(app) , io = require('socket.io').listen(server); app.configure(function(){ app.set('port', process.env.PORT || 3000); app.set('views', __dirname + '/views'); app.set('view engine', 'ejs'); app.use(express.favicon()); app.use(express.logger('dev')); app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(app.router); app.use(express.static(path.join(__dirname, 'public'))); }); app.configure('development', function(){ app.use(express.errorHandler()); }); app.get('/', routes.index); // ソケットを作る var socketIO = require('socket.io'); // クライアントの接続を待つ server.listen(3000); // クライアントが接続してきたときの処理 io.sockets.on('connection', function(socket) { console.log("connection"); // メッセージを受けた時の処理 socket.on('message', function(data) { // 接続していクライアントに全てに送信 console.log("message"); socket.broadcast.emit('message', { value: data.value }); }); // クライアントが切断した時に処理 socket.on('disconnection', function() { console.log("disconnection"); }); });
クライアントサイド(index.ejs)
クライアントサイドでは、jQueryを使うのでダウンロードしてローカルに配置します。public/javascriptsディレクトリで
$ wget http://code.jquery.com/jquery-1.8.3.min.js
そしてメインのchat.jsをpublic/javascriptsディレクトリに作成します。
var socket = io.connect(); // WebSocketでの接続 socket.on('connect', function(msg) { console.log("connet"); $('#connectId').html('あなたの接続ID::' + socket.socket.transport.sessid); $('#type').html('接続方式::' + socket.socket.transport.name); }); // メッセージを受信 socket.on('message', function(msg) { // メッセージを画面に表示する $('#receiveMsg').prepend(msg.value + '<br>'); }); // メッセージを送信する function SendMsg() { var msg = $('#message').val(); // メッセージを送信 socket.emit('message', { value: msg }); $('#message').val(''); } // 切断する function DisConnect() { var msg = socket.socket.transport.sessid + "は切断しました"; // メッセージを送信する socket.emit('message', { value: msg }); // socketを切断する socket.disconnect(); }
views/index.ejsを修正してjQueryやchat.jsを読み込むように設定し、簡単なUIを作成します。
<!DOCTYPE html> <html> <head> <title><%= title %></title> <script src="/javascripts/jquery-1.8.3.min.js"></script> <script src="/socket.io/socket.io.js"></script> <script src="/javascripts/chat.js"></script> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <h1>socket.ioのサンプル</h1> <div id="connectId"></div> <div id="type"></div> <br> <input type="text" id="message" value=""> <input type="button" value="メッセージを送る" onclick="SendMsg()"> <input type="button" value="切断する" onclick="DisConnect()"> <div id="receiveMsg"></div> </body> </html>
最後にタイトルを修正しておきます。routes/index.jsを修正します。
/* * GET home page. */ exports.index = function(req, res){ res.render('index', { title: 'Socket.IOサンプル' }); };
これでnodeを実行してブラウザを複数起動してチャットできればOKです。
$ node app.js
普通のPCと同じ手順でできちゃいますね...
次はGPIOとかをコントロールしてWebと繋ぎたいですね。