0

Sinatra RocketIOというプラグイン作った、これでWebSocketとCometが使える

nodeのSocket.ioっぽい物のRuby版を作った。

https://github.com/shokai/sinatra-rocketio


依存

EventMachineが有効なWebサーバー(thinとか)と、jQueryが必要。
Rubyは1.8.7〜2.0.0まで動く。


Sinatraで使う


インストール
gem install sinatra-rocketio


ブラウザとか回線に応じてWebSocketとCometの使える方が自動的に選ばれる。
切断されても定期的に再接続を試みるようになってる。


sinatraに読みこむだけで使える。sinatraのプロセス内にwebsocketサーバーも組み込まれる。
require 'sinatra'
require 'sinatra/rocketio'

modular styleではclass内でregisterすればok
class MyApp < Sinatra::Base
register Sinatra::RocketIO
## 略
end


サーバーからブラウザにpushするの簡単にできる


Sinatra側
io = Sinatra::RocketIO

io.push :temperature, 35 # 全てのクライアントに送信
io.push :light, {:value => 150}, {:to => session_id} # 特定のクライアントへ
pushはsinatraのgetとかpostの中に書いてもいい。どこに書いてもpushできる。


クライアント側。JSライブラリを読み込む。
<script src="<%= rocketio_js %>"></script>
hamlの場合はこう
%script{:src => rocketio_js}

そしてJavaScriptに受信イベント書く
var io = new RocketIO().connect(); // WebSocketとCometの適当な方が使われる
io.on("temperature", function(value){
console.log("温度 : " + value);
}); // => "温度 : 35"
io.on("light", function(data){
console.log("明るさ : " + data.value);
}); // => "明るさ : 150"

通信というより、クライアント側でイベント登録しておいてサーバーから呼び出せる感じ。


クライアントからサーバーへ送信


JS側
io.on("connect", function(){
io.push("chat", {name: "shokai", message: "hello"});
});

Ruby側
io.on :chat do |data, client|
puts "#{data['name']} : #{data['message']} <#{client.session}> type:#{client.type}"
io.push :chat, data # 全クライアントに返送
end
逆も同じ要領でできる。”chat”というイベントを登録しておいて受信してみた。
チャットなので全クライアントに返送もする。

サーバーは、クライアントのセッションIDとtype(websocketかcometか)も取れる。


通信方法のまとめ


サーバー側
Sinatra::RocketIO.on “イベント名” でイベント登録して受信。
Sinatra::RocketIO.push “イベント名”, “データ” で送信。

クライアント側
var io = new RocketIO().connect(); で得たio(RocketIOのインスタンス)を使う。
io.on(“イベント名”, コールバック関数) でイベント登録して受信。
io.push(“イベント名”, データ) でサーバーに送信。


だいたいこんな感じで使える。

試しにチャットを作ってみた


http://rocketio-chat.herokuapp.com/
herokuなのでwebsocketが使えなくて全員cometで接続される。

http://dev.shokai.org:4000/
さくらVPSサーバーなのでwebsocket&comet両方とも使える。


ソースコードはここ https://github.com/shokai/rocketio-chat-sample
サーバー側Ruby 25行、クライアント側JS 39行でチャット作れてた。


構成

Comet部分はSinatra CometIOという独立したSinatraプラグインで実装してある。これを一番最初に作った。
WebSocket部分もSinatra::WebSocketIOという、Cometと同じAPIで使えるやつで実装した。
RocketIOはこれらをまとめた。


イベント登録して発火する部分は、event_emitter.jsevent_emitter.rbというJSとRubyそれぞれで使えるイベント管理ライブラリを作った。


性能

2年前に買ったMacMini上のVMWareで動かしてるUbuntuで
WebSocketは1万5千クライアント、Cometは500クライアントぐらい同時接続して普通に動いた。
WebSocketは1万クライアント接続して、そのうち1つがサーバーにpush、サーバーが全クライアントに返送し終わるまで平均0.7秒ぐらい。
CPUが良ければもっといけると思う。

同時接続数を1万以上やるにはOSのファイルディスクリプタの設定とか必要。
https://github.com/shokai/sinatra-websocketio/wiki/C10K

0

Ubuntu/Debianでarduino_firmata動くようになった

これ → 64bit Ubuntu+Arduino UNOでarduino_firmataが動かないらしい

なおった。Raspberry Piでも安定して動くようになった。

serialport gemの read_nonblockとwrite_nonblockを使っていると、DebianやUbuntuで動かなくなっていたのでデフォルトでnonblock使わないようにした。


今まで通りノンブロッキングIOで使いたい人は接続時に

arduino = ArduinoFirmata.connect "/dev/tty.usb-device-name", :nonblock_io => true
で接続すればいい

Arduino Firmata on Ruby

0

im-kayacのrubygemをアップデートした

2年前に作ったim.kayac.comのgemを久しぶりに見たら直したくてしょうがなくなったので直した。
APIを変えたので、対応するの面倒な人はGemfileに

gem "im-kayac", "< 0.1.0"
として古いの使ってください。


インストール

gem install im-kayac


gemの使い方

ユーザ名指定してgoogle talkやiphoneにメッセージを送れる。
require 'im-kayac'
ImKayac.to('username').post('hello world')


パスワード認証や秘密鍵認証も使える
ImKayac.to('username').password('your-password').post('hello')
ImKayac.to('username').secret('your-key').post('hello')


im-kayac コマンド

コマンドもつけてみた。gemインストールすれば使える。
% im-kayac --help
% im-kayac -to USERNAME "hello!!"

パスワード認証とかも対応している
% im-kayac -to USERNAME -password PASSWORD "はらへった"
% im-kayac -to USERNAME -secret SECRET_KEY "これは秘密鍵使ってる"

0

Rubygemsとbundlerのダウングレード

gem uninstall rubygems-update  # 全部消す
gem install rubygems-update --version=1.8.24
update_rubygems
これでgem 1.8.24に戻せる


Macのgemコマンドを2.0.4にアップグレードしてみたら、bundler1.3以上を強制されてしまったのだが
どうやってもSSLのエラーがでる

Could not verify the SSL certificate for https://rubygems.org/


のでbundler 1.2.4を使っている

gem uninstall bundler  # 全部アンインストール
gem install bundler --version=1.2.4

あとgem2.0.4はgem searchで検索しても前方一致しかしてないっぽい気がする


きっとそのうち面倒な設定をしなくてもbundlerが使えるようになるのでそれまで待つ

0

RubyからArduinoを操作できるようにFirmataプロトコルを移植した

Javaから移植した
Arduino Firmata on Ruby

インストール

gem install arduino_firmata
Ruby 1.8.7と1.9.2で動作確認してる。serialport gemを使ってるのでJRubyでは動かない。


できること・使い方

RubyからArduinoのデジタル・アナログの入出力が使える。

例えばTwの内部APIと温度センサLM35DZと組み合わせると、9行で温度をtweetできたりして便利。
#usr/bin/env ruby
require 'rubygems'
require 'arduino_firmata'
require 'tw'

arduino = ArduinoFirmata.connect "/dev/tty.usbmodem621"
puts temp = arduino.analog_read(1)*100*5/1024 # 摂氏1度毎に10mV出力なので計算する
client = Tw::Client.new
client.auth "shokai"
client.tweet "現在の温度 #{temp}度"


あとはSinatraと組み合わせて20行ぐらいでブラウザからArduino動かしたりとかもできる



Firmataとは


http://firmata.org
Firmata(ふぁるまーた)はProcessingからArduinoを制御するために5年ぐらい前に作られたプロトコルだ。

Arduinoに Firmataファームウェア を書き込むと、USBシリアル通信経由でパソコンとやりとりしてIOを操作したり色々やってくれる。
プログラムを書く側はArduinoのコードを書いたりパソコンとの通信を意識したりする必要が無くなり、
手元のRubyやProcessingだけを書けば済むのでとても楽になる。
ただし、通信する分だけ遅くなるので超細かくモーターを制御する等には向いていない。


Processing以外にもMax/MSPやopenFrameworksなど色々なプログラミング言語からArduinoを制御する時に使われるようになったり、
また逆に、Arduino以外のボード(Funnel IOなど)をProcessingから制御するのにも使われてたりしている。

いろんな言語とボードを接続するのに便利なプロトコルなのだが、
PhidgetsがあるんだからArduinoはスタンドアロンで使うべきじゃないの?という印象だった。2週間前までは。

八王子の山奥のopenFrameworksセミナーで使ったGrove Systemが拡張パーツの多さ・入手の容易さ・自分で拡張しやすいという点でPhidgetsを食ってしまっていたので、Rubyでも使いたくなった。
つまり Ruby+Firmata+Arduino+GroveSystem 最強なのでは。全部オープンソースだし。

実装はそんなに難しくなくて昨日お風呂にはいる時間を削って作ってみたらすぐ動いたのでgemにした。
Arduino playground – Processing にあったJavaのFirmataライブラリをまるごとRubyで書きなおした。


Rubyっぽい書き方


せっかくRubyで書いてるので使いやすいAPIにしたい。

デバイス名を省略してconnectしても適当に探して接続したりとか、
ADコンバータにcallback登録できるようにしてみた。
arduino = ArduinoFirmata.connect

arduino.on_analog_changed 0 do |value|
puts "analog pin 0 changed #{value}"
arduino.analog_write 11, value
end

ブロックを使うと、arduino.analog_readをanalog_readだけに省略できたりとか
ArduinoFirmata.connect do
puts "firmata version #{version}"

30.times do
an = analog_read 0
analog_write 11, an
sleep 0.01
end
end


簡単なチュートリアルも Arduino Firmata on Ruby に書いたし、
今後も機能追加するので(未実装のon_digital_changed等)見ると良いと思います。