2012年3月31日土曜日

オシロスコープをインターネットの向こう側へ

オシロスコープの3つめ。諸事情でRubyをつかっていた部分をすべてNode.jsにしました。決してRubyがキライになったからじゃないですよ。

全力で稼働中

ひとつ前と変わらずNode Ninjaさんで動かしています。→こちら←。仕組みの解説をします。

サーバ側

いままではNode WebSocket Serverをつかい、生のWebSocketをつかってブラウザに送っていましたが、今回からSocket.IOをつかうことにしました。なぜか。生WSのときは、Arduinoで読み取った信号の受け口とブラウザ送信の送り口を同じエンドポイントで運用していました。これだと特にKUFUをしない限り、誰でもサーバにメッセージを投げられて、全てのブラウザに送信されてしまいます。いままではサーバ起動後すぐにArduino側のスクリプトを起動しサーバに接続、IDを保存しておいてそのIDから来たメッセージのみブラウザに送信するとしていたのですが、なんとなくスマートじゃない。これを、Socket.IOの名前空間を利用して解決しています。

名前空間(Namespace)とは

ブラウザからSocket.IOを利用してサーバへつなぐ際に、log.niccol.li/hogehogeのようにサーバ名+文字列を指定することができます。スラッシュ以降が名前空間と呼ばれる機能で、指定した文字列ごとにサーバ内で処理を分岐させることができます(詳しくはこちら)。受信用・送信用に名前空間を分け、ブラウザからは送信専用のエンドポイントを指定することで分離を実現させています。

Arduino中継側

「ArduinoがanalogReadの値をシリアル通信で送信、受けたPCがWebSocketでサーバへ送信」の流れは変わっていません。いままではここをRuby/EventMachineで記述していましたが、サーバでSocket.IOを使い始めたのにあわせてNode.js+Socket.io-clientに置き換えました。前述の名前空間をつかい、Arduinoからの信号受信用のエンドポイントを指定しています。

ここでNode.jsに置き換えたことでちょっと変化が。シリアル通信を読むときに、EventMachineではたまに改行を見落とすため、異常な値が送信される問題がありました。これがNode.jsでは起こらないんですね。モジュール内部も含めて、シリアルポートから読み出したあとの処理に差異はみられないため、読む処理そのものの問題なのかなと。違う点といえば、NodeはTTYも含むI/Oをlibuvでまかなっているため、ノンブロッキング処理になっていますが、Rubyのほうはブロッキング処理のよう。改行で文字列を分割するタイミングとTTY読み出しが当たるととぶのかなと思ってはいますが、どうやって確かめたらいいんですかね。

2012年3月18日日曜日

Arduino用オシロ、実演中

EventMachineとHTML5でつくる、Arduino用なんちゃってオシロスコープの続き。あなたのウェブブラウザでオシロスコープを実際にお楽しみいただけます。とは言っても波形を見られるだけなんだけどね。

デモンストレーションはRealTime - FakeScopeから。東京Node学園祭でいただいたNode Ninjaのサーバをつかっています。

ながれ

  1. Arduinoからシリアル通信でMacへ測定値を送信
  2. シリアル通信をEventMachineで受けたのち、WebSocketでNode Ninja上の中継サーバへ送信
  3. 中継サーバからブラウザへWebSocketで送信
  4. ブラウザ上で、Flotr2を使ってグラフ化
いま(3月18日23時時点)サーバで見えている波形は、iPhoneのスピーカー部分の振動を拾っているところ。何か音楽がかかっていれば揺れます。

それなりの勢いでWebSocketのデータを流しているので、Node Ninjaさんから怒られないか心配しているところではあります。

これから

ArduinoからMacを経由して中継サーバに送っているところ。ここはEthernet Shieldなどを使って、Arduino自信がサーバへ直接送信できるようになればスルーできます。また、信号の発信元もArduinoである必要はありません。WebSocketサーバにCSV形式で値を投げられるならば、何だってよいのです。ゆくゆくはエンドポイントを公開して、センサーネットワークの受け側にしたりとかしたいですね。

おまけ

なんでもグラフ化してみんなでシェアすればソーシャルグラフじゃね?という駄洒落を名古屋でのLTにておみまいしましたが、微妙なウケ具合でした。負けません。

2012年3月13日火曜日

東京と名古屋でLTをやったらすごいウケた話


まさかIT系のLTでトロンボーンを出す日が来るとはね

この週末は東京と名古屋でLTをやってきました。3月10日の土曜日は文京区の情報処理推進機構にて小江戸らぐ。ここでウケたところで調子にのって、12日月曜は名古屋新栄のPiano Bar Club Adriana a.k.a. 名古屋 Geek Barでも。

内容はというと、ひとつ前のArduinoオシロスコープについて。かるく中身を説明したうえで実際に動かしたら大ウケ。名古屋ではたまたまその日に返してもらったトロンボーンをつかって実演。さらにペアプロ合コンのためにつくったとゲスいネタをひきだしてだめ押し。

合コンは置いといて、実際にものを動かすLTは強いなと。ハードウェアという飛び道具をあやつってネットとリアルをつなぐ、つないだ様子を間近で見えるプレゼン。こういう方向でのプレゼンを繰り広げるエンジニアはあまりいないと信じて、今年は活動していきたいと思います。

2012年3月5日月曜日

EventMachineとHTML5でつくる、Arduino用なんちゃってオシロスコープ

HTML Fake Oscilloscope
Arduinoのアナログ入力で読んだ電圧の表示用に、HTMLでオシロスコープをつくってみたよ。

ソースはこちら→niccolli/FakeScope · GitHub

仕組み

  1. Arduinoからmillis()とanalogRead()の値をシリアル通信で出力
  2. RubyのEventMachineで読み込み、WebSocketで配信
  3. HTMLでグラフに描画
EventMachineでのシリアル通信にem-serialportを、WebSocketにem-websocketを使っています。HTMLのグラフ描画は直でCanvas……とも思ったのですが、flotr2というちょうキレイなグラフの描けるスクリプトにおまかせしています。

Piezo Mike Shield with LM386
Arduinoのほうは値を出力するならなんでもよいのですが、ただのサイン波ではおもしろくないので、指パッチンのときに使った回路を使っています。ブレッドボードからユニバーサル基板に進化しました。

動かしてみた

赤く見切れているものはトランペットです。マイクでトランペットのベルの振動を拾い、Arduino上のシールドで増幅。それをanalogReadで読んだものを表示させています。そこそこぬるぬるしてるよね。

やり残していること

  • 重い
    flotr2などはiOSでも見られる・使えるもののはずなんだけど、MobileSafariでは最終的に固まります。
  • 波形がとぶ
    これはem-serialportのような。このシリアル通信ライブラリはバッファリングがないので、その部分だけ自前で実装しています。このシリアル通信のデータをみていると、ちょっと信用おけない感じがするのです。
  • サンプリング周期が粗い
    これはArduino。loop()のなかでdelay()などを使ってはいないのですが、loop周期が数msなのでus/ns級の波形はたぶん見られません。ここはもう本物のオシロスコープを買えってことかな。
    追記:loop周期の問題ではなく、シリアル通信のボーレートの問題でした。ボーレートを全開にすれば早くなりますが、Ruby側で落とすぶん(上の問題)が増えているようです。

「Rubyつかってみろ」ってことでEventMachineをつかったおもちゃをつくったつもりだったけど、肝心のRuby分はうっかりすくなめ。なんかこう充実してた気がしてたのは、初めてだったからかな。

2012年3月4日日曜日

em-serialport:EventMachineでシリアルポートをつかう

急遽「おまえ、Rubyやれるのか!やれんのか!」的な話が来たのをいいことに、RubyのEventMachineであそんでいます。おれがイベント駆動。イベント駆動といえばArduino。Arduinoが出すなんらかの値はシリアル通信でうけとるのが楽なので、EventMachineでシリアル入力をトリガーにするためのライブラリem-serialportを使ってみます。

例:シリアルポートでうけた値をターミナルに表示させる

#!/usr/bin/env ruby
require 'em-serialport'

#serial = EventMachine.open_serial('/dev/tty.usbserial-A6008iDi', 9600, 8, 1, 0) 

EM.run do
    serial = EM.open_serial('/dev/tty.usbserial-A6008iDi', 9600, 8, 1, 0)

    serial.on_data do |data|
        print data
    end
end

requireはem-serialportのみ。require 'eventmachine'もつけると、名前の衝突かなにかでえエラーがでます。

open_serialの引数は順にシリアルポート名、ボーレート(もちろんスケッチに書いてあるものを)、データビット長、ストップビット、パリティの有無になっています。特に気にするのは前2つで、後ろは基本このままで大丈夫そうですね。4行目に同じものがコメントアウトになっていますが、ここでメソッドを呼び出すとダメみたいです。ループのなかにあるような感じが微妙に気持ち悪いんですけど、こういうものということで。

serial.on_dataが受信後に実行するコールバック関数。いまはprintのみを入れて、ターミナルに表示させているだけです。

Rubyはほぼ初めてなんですが、いままで書いたコードは命令のあとにセミコロンが必須のものばかりだったので、セミコロンなしインデント至上主義のコードに若干とまどっています。がしかし、やってみると楽しいものね。

注意

on_dataの引数で入ってくる値は、シリアルポートで受けた値がバッファを通すことなく直接入ってきます。上の例のようにそのままターミナルに吐き出す場合はよいのですが、値を拾ってスクリプト内でどうこうとなるとバッファに取り込んだうえで改行(\r\n)をめどに分割する必要があります。