0

HTML+JavaScriptをArduinoに直結できるシリアルポートサーバーを作った

SerialPort Serverを使うと、HTMLとJSをArduinoに直結できる。JavaScriptを少し書くだけでArduinoに「カーテン開けろ」とか「部屋の明るさよこせ」とか命令を送れるわけだ。
Web系の技術とハードウェアの技術を同時に使うには、間に「つなぎ」が必要なので、必要な機能を全部入れたサーバーを作ってみた感じです。
(github pagesでプロジェクトページ作ってみたんだけど楽でいいですね)


こういうこと。


シリアルポートサーバーはrubygemsでインストールできる。

gem install serialport-server
which serialport-server
serialport-server --help
serialport-server /dev/tty.デバイス名
–helpでヘルプが出る。デバイス名を引数にして起動できる。
Macならsudo gem installで一発でインストールできるはず。

起動するとHTTPサーバー(http://localhost:8783)、WebSocketサーバー(ws://localhost:8784)、TCP Socketサーバー(localhost:8785)の3つが同時に起動する。

HTTPのサーバーはajaxのクロスドメイン制限を超えてデータのやりとりができるようにしてある。シリアルポートサーバーを動かして、HTMLとJavaScriptを書けばArduinoとJavaScriptが直接通信できるというわけだ。
(response headerにAccess-Control-Allow-Originを付ければ良いとyuisekiのはてブを見ていて知ったのでやってみた。)


■デモ
SerialPort Serverのページにも英語で書いたが、簡単なサンプルプロジェクトを日本語でも解説しておく。


1. Arduinoにプログラムを書き込む

2. ArduinoのDigital13番ピンにLEDを、Analog0番ピンにCdSと10kΩの抵抗を接続する
SerialPort Server sample
SerialPort Server sample

3. SerialPort Serverを起動する

4. Ajax版サンプルを開いて試してみる

5. WebSocket版サンプルを開いて試してみる


WebブラウザからLEDを点灯消灯したり、CdSの明るさの値がリアルタイムにスライダーに反映されているはずだ。
実際にAjaxサンプルのJavaScriptのコードを見てみると簡単さがわかると思う。17行でLEDとCdSを制御できている。



もちろんWebサーバーなので、複数のWebブラウザがSerialPort Serverから同時にデータを読み出そうとしても大丈夫。
増井研で1年以上、遠隔操縦ロボットの制御に使っていたプログラムを元に使っているのでプロセスを起動してたぶん3ヶ月ぐらい放置していても動き続けるぐらいに安定していると思う。

0

skype-socket-gatewayを作った

Skypeのグループチャットにbotを放ったり色々したかったので作った。

Skype APIはSkypeが起動しているマシンの中でSkypeのプログラムと自分のアプリを通信させて使う物で、developer.skype.comにAPIのドキュメントがある。電話をかけたりチャットの中身を取得したり発言したりできる。

Skypeが起動しているマシンでskype socket gatewayを起動しておくと、他のマシンからもtelnetでSkype APIが使える。また、全チャット(含むグループチャット)のデータがJSONでどんどん流れてくるようになっている。

うちではWindows XPのネットブックでbotアカウントでSkypeを起動してあった。Skype chatはP2Pなのでログをそれぞれが持っていて、後で接続してきた人は先に居た人から取得する様になっている。だから、ただ何もしなくてもずっとオンラインの人がいるだけでログの流れが良くなる。
で、そのbotアカウントを真なるbotにするために、Windows用のActiveScriptRuby 1.8.7でskype socket gatewayを動かしてそこに別のLinuxのサーバーから複数のbotが接続して色々処理をしている。

Linux上で動くSkype用のbotを作る方法 – muddy brown thangにXvfbを使ってlinuxで仮想フレームバッファを作ってSkype APIを使う方法が解説されてるけど、なんかLinuxが難しくて挫折したのでwindowsでも動くsocket gatewayを作った。


■ソースコード
githubに全部置いておいた
shokai's skype-socket-gateway at master – GitHub


■インストール

git clone git://github.com/shokai/skype-socket-gateway.git

必要なgemもインストールする
gem install Ruby4Skype json eventmachine

Ruby4Skypeというのを使っているので、Windows、Mac、Linuxで動く。ただしRuby4Skypeが64bit MacOSXでは動かない。Linuxも、Ruby4Skypeのlib/skype/os/linux.rbを修正してinitialize methodのsuperにapp_nameを渡すように修正しないと動かない。

それとWindowsでRuby4Skypeを動かすには色々DLLを入れないとダメ → WinXPでgem install Ruby4Skypeするメモ – yuisekiのいまさらruby厨日記 – Rubyist


■起動する
先にSkypeを起動しておく。

ruby skype-gateway.rb
でport 20000で起動する。


■使う
telnetで接続する。
telnet 192.168.1.37 20000

そしてそのまま
CALL shokaishokai
MESSAGE shokaishokai hello work!!
とか書く。Skype APIがそのまま実行できる。
レスポンスはJSONで来る。

あと、接続しているだけで全てのchatの発言が流れてくる。


■Windowsのスタートアップに登録する
run_skype_gateway.bat というバッチファイルを作る
"C:\Program Files\ruby-1.8\bin\ruby.exe" "C:\Document and Settings/sho/src/ruby/skype-socket-gateway/skype-gateway.rb"
中身はこの1行でいい。これをスタートメニューのスタートアップに入れておけば自動起動できる。


■Skype botを作る
RubyだとTCPSocketクラスを使うと簡単に作れる。

キーワードに反応する単純なbotの例。誰かが「ざんまい」と書くと、即座に「ざんまい行きたい!」と返す。
samples/zanmai_bot.rb at master from shokai's skype-socket-gateway – GitHub
require 'rubygems'
require 'socket'
require 'json'
require 'eventmachine'
$KCODE = 'u'

HOST = "192.168.1.37"
PORT = 20000

begin
s = TCPSocket.open(HOST, PORT)
s.puts "MESSAGE shokaishokai ざんまいbot start"
rescue => e
STDERR.puts e
exit 1
end

EventMachine::run do

EventMachine::defer do
loop do
res = s.gets
exit unless res
res = JSON.parse(res) rescue next
p res
if res['type'] == 'chat_message'
if res['body'] =~ /ざんまい/ # キーワードに反応
s.puts "CHATMESSAGE #{res['chat']} ざんまい行きたい!"
elsif res['body'] =~ /かず(すけ|助)/
s.puts "CHATMESSAGE #{res['chat']} かずにゃんぺろぺろ"
end
end
end
end

EventMachine::defer do
loop do
mes = gets
s.puts mes if mes
end
end

end


これにもう少し語彙を追加すると、だいたい俺の人格と機能を8割方再現したbotが作れる。
shokai_bot.rb at master from shokai's skype-bots – GitHub
6a8dae3e0028f2f870f194cdbec2af75.png


あと、全chatをmongo dbに入れて検索したりできるbotなんかもいる。超便利。
tools/skype_chat_store_mongod.rb at master from shokai's skype-socket-gateway – GitHub
fecba229c251c20a4543e21bd1395929.png


はてなランドに自分のskypeの発言を送るなんて事もできる
hatenaland_proxy.rb at master from shokai's skype-bots – GitHub


■EventMachine
EventMachineというサイバーパンクな名前のライブラリがRubyにあって、これを使うと並行処理が簡単に書けた。
skype socket gatewayでは
  • 標準入力をSkype APIとして実行する
  • 接続してきたTCP Socketクライアントそれぞれからの入力をSkype APIとして実行し、結果をそれぞれに返す
  • 全ソケットクライアントに全chat logを送る
  • クライアントが接続されているかたまに確認する
という処理をやっていて、これらを適当にEventMachine::defer doの中でloopで書いてやればうまく動いた。

不思議だったのは、新しくacceptしたsocket clientオブジェクトそれぞれを個別に処理するためにEventMachine::deferの中でclient毎にそれぞれEventMachine::deferを作ってみたら、ちゃんとスコープが別になってくれた。
  EventMachine::defer do
loop do
s = sock.accept
clients << s
puts "--- new client : #{clients.size}"

# socket -> invoke Skype API
EventMachine::defer do
loop do
cmd = s.gets
next if cmd == nil or cmd.to_s.size < 1
puts "recv => #{cmd}"
begin
p res = {
:type => 'api_response',
:body => skype.invoke(cmd).to_s
}
rescue => e
res = {
:type => 'error',
:body => 'skype api invoke error'
}
STDERR.puts e
end
begin
s.puts "\n"+res.to_json
rescue => e
STDERR.puts e
c.close
break
end
end
end
end
end


それぞれのdeferの中でAPI呼び出しと結果を返す処理が動いている。これをthreadでやる時
Thread::start(s) do |s|
loop do
res = s.gets
# 何か処理
s.puts response.to_json
sleep 0.01
end
end
みたいに引数に渡さなくても勝手にスコープ分けてくれてた。


でも、clientの配列とかchatメッセージのqueueを作って複数のEventMachine::deferからdeleteしたりpushしたりしていじっている部分では、defer間で共有されていた。不思議だ。
EventMachine::run do
# forward all skype chats -> socket
EventMachine::defer do
loop do
if chat_msgs.size > 0
msg = "\n"+chat_msgs.shift.to_json
clients.each{|c|
begin
c.puts msg
rescue => e
STDERR.puts e
end
}
end
end
end

# check clients connection
EventMachine::defer do
loop do
msg = ''
errors = Array.new
clients.each{|c|
begin
c.puts msg
rescue => e
STDERR.puts e
errors << c
end
}
errors.each{|c|
clients.delete(c)
c.close
}
sleep 15
end
end

よくわからないけどEventMachineが凄い。

0

Arduino+XPort+Perl+WebService

ArduinoにXPortでIP持たせて、そこにPerlからデータをプッシュする。

s.h.log: XPortをEthernet-UARTコンバータとして使うの通りにXPortを設定する。

そこに、明日の天気のデータをLivedoorお天気のRSSからパースしてsocketで送ると、

socket通信をXPort越しにArduinoで受け取れるので、晴れ/曇り/雨でそれぞれLEDを光らせる「てるてる坊主」を作ろうというもの。

最初は今日の天気だったが、よく考えたら今日の天気は空見ればわかるから、Date::Simple使って明日の天気になった。

あと、送信データもstringではなくchar一文字でいいやという事になった。Arduinoには文字列比較系の関数が無いし、heepが無いマイコンに無理させる必要も無いし。

以下は暫定版のArduino&Perlコード。

physicalとwebに向いているのをそれぞれで分業させたので、結構短く書けて楽だった。

これでテンプレートができたので、GoogleCalendarから予定引っ張ってきて枕に振動モータ突っ込んで起こすとか1時間あれば作れる。

Read more